diff --git a/AndroidManifest.xml b/AndroidManifest.xml index b496505d..81382f7d 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -2,8 +2,8 @@ + a:versionCode="36" + a:versionName="1.2.0.9" > diff --git a/src/com/thejoshwa/ultrasonic/androidapp/activity/DownloadActivity.java b/src/com/thejoshwa/ultrasonic/androidapp/activity/DownloadActivity.java index 08fffa3b..1ad489e0 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/activity/DownloadActivity.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/activity/DownloadActivity.java @@ -95,6 +95,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi private VisualizerView visualizerView; private boolean visualizerAvailable; private boolean equalizerAvailable; + private boolean jukeboxAvailable; private SilentBackgroundTask onProgressChangedTask; LinearLayout visualizerViewLayout; @@ -595,6 +596,23 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi visualizerAvailable = (downloadService != null) && (downloadService.getVisualizerController() != null); equalizerAvailable = (downloadService != null) && (downloadService.getEqualizerController() != null); + new Thread(new Runnable() + { + @Override + public void run() + { + try + { + DownloadService downloadService = getDownloadService(); + jukeboxAvailable = (downloadService != null) && (downloadService.isJukeboxAvailable()); + } + catch (Exception e) + { + Log.e(TAG, e.getMessage(), e); + } + } + }).start(); + final View nowPlayingMenuItem = findViewById(R.id.menu_now_playing); menuDrawer.setActiveView(nowPlayingMenuItem); @@ -841,18 +859,25 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi } } - if (downloadService.isJukeboxEnabled()) + + if (jukeboxOption != null) { - if (jukeboxOption != null) + jukeboxOption.setEnabled(jukeboxAvailable); + jukeboxOption.setVisible(jukeboxAvailable); + + if (downloadService.isJukeboxEnabled()) { - jukeboxOption.setTitle(R.string.download_menu_jukebox_off); + if (jukeboxOption != null) + { + jukeboxOption.setTitle(R.string.download_menu_jukebox_off); + } } - } - else - { - if (jukeboxOption != null) + else { - jukeboxOption.setTitle(R.string.download_menu_jukebox_on); + if (jukeboxOption != null) + { + jukeboxOption.setTitle(R.string.download_menu_jukebox_on); + } } } } @@ -1321,13 +1346,20 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi final DownloadService downloadService = getDownloadService(); - if (downloadService == null) + if (downloadService == null || e1 == null || e2 == null) { return false; } + float e1X = e1.getX(); + float e2X = e2.getX(); + float e1Y = e1.getY(); + float e2Y = e2.getY(); + float absX = Math.abs(velocityX); + float absY = Math.abs(velocityY); + // Right to Left swipe - if (e1.getX() - e2.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) + if (e1X - e2X > swipeDistance && absX > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); if (downloadService.getCurrentPlayingIndex() < downloadService.size() - 1) @@ -1340,7 +1372,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi } // Left to Right swipe - if (e2.getX() - e1.getX() > swipeDistance && Math.abs(velocityX) > swipeVelocity) + if (e2X - e1X > swipeDistance && absX > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); downloadService.previous(); @@ -1350,7 +1382,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi } // Top to Bottom swipe - if (e2.getY() - e1.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) + if (e2Y - e1Y > swipeDistance && absY > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); downloadService.seekTo(downloadService.getPlayerPosition() + 30000); @@ -1359,7 +1391,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi } // Bottom to Top swipe - if (e1.getY() - e2.getY() > swipeDistance && Math.abs(velocityY) > swipeVelocity) + if (e1Y - e2Y > swipeDistance && absY > swipeVelocity) { warnIfNetworkOrStorageUnavailable(); downloadService.seekTo(downloadService.getPlayerPosition() - 8000); diff --git a/src/com/thejoshwa/ultrasonic/androidapp/activity/EqualizerActivity.java b/src/com/thejoshwa/ultrasonic/androidapp/activity/EqualizerActivity.java index 4cf157ff..007a2ab6 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/activity/EqualizerActivity.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/activity/EqualizerActivity.java @@ -18,9 +18,6 @@ */ package com.thejoshwa.ultrasonic.androidapp.activity; -import java.util.HashMap; -import java.util.Map; - import android.app.Activity; import android.media.audiofx.Equalizer; import android.os.Bundle; @@ -38,6 +35,9 @@ import com.thejoshwa.ultrasonic.androidapp.R; import com.thejoshwa.ultrasonic.androidapp.audiofx.EqualizerController; import com.thejoshwa.ultrasonic.androidapp.service.DownloadServiceImpl; +import java.util.HashMap; +import java.util.Map; + /** * Equalizer controls. * @@ -136,14 +136,20 @@ public class EqualizerActivity extends Activity private void updateBars() { - - for (Map.Entry entry : bars.entrySet()) + try { - short band = entry.getKey(); - SeekBar bar = entry.getValue(); - bar.setEnabled(equalizer.getEnabled()); - short minEQLevel = equalizer.getBandLevelRange()[0]; - bar.setProgress(equalizer.getBandLevel(band) - minEQLevel); + for (Map.Entry entry : bars.entrySet()) + { + short band = entry.getKey(); + SeekBar bar = entry.getValue(); + bar.setEnabled(equalizer.getEnabled()); + short minEQLevel = equalizer.getBandLevelRange()[0]; + bar.setProgress(equalizer.getBandLevel(band) - minEQLevel); + } + } + catch (Exception ex) + { + } } @@ -189,7 +195,13 @@ public class EqualizerActivity extends Activity short level = (short) (progress + minEQLevel); if (fromUser) { - equalizer.setBandLevel(band, level); + try + { + equalizer.setBandLevel(band, level); + } + catch (Exception ex) + { + } } updateLevelText(levelTextView, level); } @@ -204,6 +216,7 @@ public class EqualizerActivity extends Activity { } }); + layout.addView(bandBar); } } @@ -216,7 +229,9 @@ public class EqualizerActivity extends Activity private static void updateLevelText(TextView levelTextView, short level) { - levelTextView.setText((level > 0 ? "+" : "") + level / 100 + " dB"); + if (levelTextView != null) + { + levelTextView.setText(String.format("%s%d dB", level > 0 ? "+" : "", level / 100)); + } } - } diff --git a/src/com/thejoshwa/ultrasonic/androidapp/activity/MainActivity.java b/src/com/thejoshwa/ultrasonic/androidapp/activity/MainActivity.java index 989b65bb..ec354613 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/activity/MainActivity.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/activity/MainActivity.java @@ -321,7 +321,6 @@ public class MainActivity extends SubsonicTabActivity private boolean getActiveServerEnabled() { - final int activeServer = Util.getActiveServer(this); boolean activeServerEnabled = false; diff --git a/src/com/thejoshwa/ultrasonic/androidapp/activity/SubsonicTabActivity.java b/src/com/thejoshwa/ultrasonic/androidapp/activity/SubsonicTabActivity.java index c7ea500f..de610292 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/activity/SubsonicTabActivity.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/activity/SubsonicTabActivity.java @@ -454,7 +454,10 @@ public class SubsonicTabActivity extends Activity implements OnClickListener return; } - nowPlayingView = findViewById(R.id.now_playing); + if (nowPlayingView == null) + { + nowPlayingView = findViewById(R.id.now_playing); + } if (nowPlayingView != null) { @@ -549,8 +552,22 @@ public class SubsonicTabActivity extends Activity implements OnClickListener public void hideNowPlaying() { - nowPlayingView = findViewById(R.id.now_playing); - setVisibilityOnUiThread(nowPlayingView, View.GONE); + try + { + if (nowPlayingView == null) + { + nowPlayingView = findViewById(R.id.now_playing); + } + + if (nowPlayingView != null) + { + setVisibilityOnUiThread(nowPlayingView, View.GONE); + } + } + catch (Exception ex) + { + Log.w(String.format("Exception in hideNowPlaying: %s", ex), ex); + } } public void setTextViewTextOnUiThread(final RemoteViews view, final int id, final String text) diff --git a/src/com/thejoshwa/ultrasonic/androidapp/service/CachedMusicService.java b/src/com/thejoshwa/ultrasonic/androidapp/service/CachedMusicService.java index b45eb572..a878280a 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/service/CachedMusicService.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/service/CachedMusicService.java @@ -33,6 +33,7 @@ import com.thejoshwa.ultrasonic.androidapp.domain.Playlist; import com.thejoshwa.ultrasonic.androidapp.domain.SearchCriteria; import com.thejoshwa.ultrasonic.androidapp.domain.SearchResult; import com.thejoshwa.ultrasonic.androidapp.domain.Share; +import com.thejoshwa.ultrasonic.androidapp.domain.UserInfo; import com.thejoshwa.ultrasonic.androidapp.domain.Version; import com.thejoshwa.ultrasonic.androidapp.util.CancellableTask; import com.thejoshwa.ultrasonic.androidapp.util.Constants; @@ -58,6 +59,7 @@ public class CachedMusicService implements MusicService private final LRUCache> cachedMusicDirectories; private final LRUCache> cachedArtist; private final LRUCache> cachedAlbum; + private final LRUCache> cachedUserInfo; private final TimeLimitedCache cachedLicenseValid = new TimeLimitedCache(120, TimeUnit.SECONDS); private final TimeLimitedCache cachedIndexes = new TimeLimitedCache(60 * 60, TimeUnit.SECONDS); private final TimeLimitedCache cachedArtists = new TimeLimitedCache(60 * 60, TimeUnit.SECONDS); @@ -73,6 +75,7 @@ public class CachedMusicService implements MusicService cachedMusicDirectories = new LRUCache>(MUSIC_DIR_CACHE_SIZE); cachedArtist = new LRUCache>(MUSIC_DIR_CACHE_SIZE); cachedAlbum = new LRUCache>(MUSIC_DIR_CACHE_SIZE); + cachedUserInfo = new LRUCache>(MUSIC_DIR_CACHE_SIZE); } @Override @@ -378,6 +381,10 @@ public class CachedMusicService implements MusicService cachedLicenseValid.clear(); cachedIndexes.clear(); cachedPlaylists.clear(); + cachedGenres.clear(); + cachedAlbum.clear(); + cachedArtist.clear(); + cachedUserInfo.clear(); restUrl = newUrl; } } @@ -470,4 +477,24 @@ public class CachedMusicService implements MusicService return dir; } + + @Override + public UserInfo getUser(String username, Context context, ProgressListener progressListener) throws Exception + { + checkSettingsChanged(context); + + TimeLimitedCache cache = cachedUserInfo.get(username); + + UserInfo userInfo = cache == null ? null : cache.get(); + + if (userInfo == null) + { + userInfo = musicService.getUser(username, context, progressListener); + cache = new TimeLimitedCache(Util.getDirectoryCacheTime(context), TimeUnit.SECONDS); + cache.set(userInfo); + cachedUserInfo.put(username, cache); + } + + return userInfo; + } } diff --git a/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadService.java b/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadService.java index d59a183f..26bfb8c3 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadService.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadService.java @@ -125,6 +125,8 @@ public interface DownloadService boolean isJukeboxEnabled(); + boolean isJukeboxAvailable(); + void setJukeboxEnabled(boolean b); void adjustJukeboxVolume(boolean up); diff --git a/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java b/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java index 710ee1de..ecddaacd 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/service/DownloadServiceImpl.java @@ -49,6 +49,7 @@ import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory; import com.thejoshwa.ultrasonic.androidapp.domain.MusicDirectory.Entry; import com.thejoshwa.ultrasonic.androidapp.domain.PlayerState; import com.thejoshwa.ultrasonic.androidapp.domain.RepeatMode; +import com.thejoshwa.ultrasonic.androidapp.domain.UserInfo; import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x1; import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x2; import com.thejoshwa.ultrasonic.androidapp.provider.UltraSonicAppWidgetProvider4x3; @@ -896,18 +897,8 @@ public class DownloadServiceImpl extends Service implements DownloadService lifecycleSupport.serializeDownloadQueue(); } - private synchronized void playNext(boolean start) + private synchronized void playNext() { - // Swap the media players since nextMediaPlayer is ready to play - if (start) - { - //nextMediaPlayer.start(); - } - else - { - Log.i(TAG, "nextMediaPlayer already playing"); - } - MediaPlayer tmp = mediaPlayer; mediaPlayer = nextMediaPlayer; nextMediaPlayer = tmp; @@ -1362,6 +1353,25 @@ public class DownloadServiceImpl extends Service implements DownloadService return jukeboxEnabled; } + @Override + public boolean isJukeboxAvailable() + { + MusicService musicService = MusicServiceFactory.getMusicService(DownloadServiceImpl.this); + + try + { + String username = Util.getUserName(DownloadServiceImpl.this, Util.getActiveServer(DownloadServiceImpl.this)); + UserInfo user = musicService.getUser(username, DownloadServiceImpl.this, null); + return user.getJukeboxRole(); + } + catch (Exception e) + { + Log.w("Error getting user information", e); + } + + return false; + } + @Override public void setJukeboxEnabled(boolean jukeboxEnabled) { @@ -1689,21 +1699,13 @@ public class DownloadServiceImpl extends Service implements DownloadService setPlayerStateCompleted(); int pos = cachedPosition; - Log.i(TAG, "Ending position " + pos + " of " + duration); + Log.i(TAG, String.format("Ending position %d of %d", pos, duration)); if (!isPartial || (downloadFile.isWorkDone() && (Math.abs(duration - pos) < 10000))) { if (nextPlaying != null && nextPlayerState == PlayerState.PREPARED) { - if (!nextSetup) - { - playNext(true); - } - else - { - nextSetup = false; - playNext(false); - } + playNext(); } else { @@ -1777,14 +1779,23 @@ public class DownloadServiceImpl extends Service implements DownloadService private void handleError(Exception x) { - Log.w(TAG, "Media player error: " + x, x); - mediaPlayer.reset(); + Log.w(TAG, String.format("Media player error: %s", x), x); + + try + { + mediaPlayer.reset(); + } + catch (Exception ex) + { + Log.w(TAG, String.format("Exception encountered when resetting media player: %s", ex), ex); + } + setPlayerState(IDLE); } private void handleErrorNext(Exception x) { - Log.w(TAG, "Next Media player error: " + x, x); + Log.w(TAG, String.format("Next Media player error: %s", x), x); nextMediaPlayer.reset(); setNextPlayerState(IDLE); } diff --git a/src/com/thejoshwa/ultrasonic/androidapp/service/MusicService.java b/src/com/thejoshwa/ultrasonic/androidapp/service/MusicService.java index 92fde281..24495d5e 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/service/MusicService.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/service/MusicService.java @@ -33,6 +33,7 @@ import com.thejoshwa.ultrasonic.androidapp.domain.Playlist; import com.thejoshwa.ultrasonic.androidapp.domain.SearchCriteria; import com.thejoshwa.ultrasonic.androidapp.domain.SearchResult; import com.thejoshwa.ultrasonic.androidapp.domain.Share; +import com.thejoshwa.ultrasonic.androidapp.domain.UserInfo; import com.thejoshwa.ultrasonic.androidapp.domain.Version; import com.thejoshwa.ultrasonic.androidapp.util.CancellableTask; import com.thejoshwa.ultrasonic.androidapp.util.ProgressListener; @@ -138,4 +139,6 @@ public interface MusicService void createBookmark(String id, int position, Context context, ProgressListener progressListener) throws Exception; MusicDirectory getVideos(boolean refresh, Context context, ProgressListener progressListener) throws Exception; + + UserInfo getUser(String username, Context context, ProgressListener progressListener) throws Exception; } \ No newline at end of file diff --git a/src/com/thejoshwa/ultrasonic/androidapp/service/OfflineMusicService.java b/src/com/thejoshwa/ultrasonic/androidapp/service/OfflineMusicService.java index 78c5d781..fc373f5f 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/service/OfflineMusicService.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/service/OfflineMusicService.java @@ -33,6 +33,7 @@ import com.thejoshwa.ultrasonic.androidapp.domain.MusicFolder; import com.thejoshwa.ultrasonic.androidapp.domain.Playlist; import com.thejoshwa.ultrasonic.androidapp.domain.SearchCriteria; import com.thejoshwa.ultrasonic.androidapp.domain.SearchResult; +import com.thejoshwa.ultrasonic.androidapp.domain.UserInfo; import com.thejoshwa.ultrasonic.androidapp.util.Constants; import com.thejoshwa.ultrasonic.androidapp.util.FileUtil; import com.thejoshwa.ultrasonic.androidapp.util.ProgressListener; @@ -742,6 +743,12 @@ public class OfflineMusicService extends RESTMusicService throw new OfflineException("Getting Genres not available in offline mode"); } + @Override + public UserInfo getUser(String username, Context context, ProgressListener progressListener) throws Exception + { + throw new OfflineException("Getting user info not available in offline mode"); + } + private static void listFilesRecursively(File parent, List children) { for (File file : FileUtil.listMediaFiles(parent)) diff --git a/src/com/thejoshwa/ultrasonic/androidapp/service/RESTMusicService.java b/src/com/thejoshwa/ultrasonic/androidapp/service/RESTMusicService.java index 331f92cd..c0a2d0ab 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/service/RESTMusicService.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/service/RESTMusicService.java @@ -39,6 +39,7 @@ import com.thejoshwa.ultrasonic.androidapp.domain.SearchCriteria; import com.thejoshwa.ultrasonic.androidapp.domain.SearchResult; import com.thejoshwa.ultrasonic.androidapp.domain.ServerInfo; import com.thejoshwa.ultrasonic.androidapp.domain.Share; +import com.thejoshwa.ultrasonic.androidapp.domain.UserInfo; import com.thejoshwa.ultrasonic.androidapp.domain.Version; import com.thejoshwa.ultrasonic.androidapp.service.parser.AlbumListParser; import com.thejoshwa.ultrasonic.androidapp.service.parser.BookmarkParser; @@ -57,6 +58,7 @@ import com.thejoshwa.ultrasonic.androidapp.service.parser.RandomSongsParser; import com.thejoshwa.ultrasonic.androidapp.service.parser.SearchResult2Parser; import com.thejoshwa.ultrasonic.androidapp.service.parser.SearchResultParser; import com.thejoshwa.ultrasonic.androidapp.service.parser.ShareParser; +import com.thejoshwa.ultrasonic.androidapp.service.parser.UserInfoParser; import com.thejoshwa.ultrasonic.androidapp.service.parser.VersionParser; import com.thejoshwa.ultrasonic.androidapp.service.ssl.SSLSocketFactory; import com.thejoshwa.ultrasonic.androidapp.service.ssl.TrustSelfSignedStrategy; @@ -1411,6 +1413,32 @@ public class RESTMusicService implements MusicService } } + @Override + public UserInfo getUser(String username, Context context, ProgressListener progressListener) throws Exception + { + checkServerVersion(context, "1.3", "getUser not supported."); + + HttpParams params = new BasicHttpParams(); + HttpConnectionParams.setSoTimeout(params, SOCKET_READ_TIMEOUT_GET_RANDOM_SONGS); + + List parameterNames = new ArrayList(); + List parameterValues = new ArrayList(); + + parameterNames.add("username"); + parameterValues.add(username); + + Reader reader = getReader(context, progressListener, "getUser", params, parameterNames, parameterValues); + + try + { + return new UserInfoParser(context).parse(reader, progressListener); + } + finally + { + Util.close(reader); + } + } + @Override public List getChatMessages(Long since, Context context, ProgressListener progressListener) throws Exception { diff --git a/src/com/thejoshwa/ultrasonic/androidapp/util/ModalBackgroundTask.java b/src/com/thejoshwa/ultrasonic/androidapp/util/ModalBackgroundTask.java index cca30a2f..cb7e7f7e 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/util/ModalBackgroundTask.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/util/ModalBackgroundTask.java @@ -102,7 +102,15 @@ public abstract class ModalBackgroundTask extends BackgroundTask @Override public void run() { - progressDialog.dismiss(); + try + { + progressDialog.dismiss(); + } + catch (Exception e) + { + // nothing + } + done(result); } }); diff --git a/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java b/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java index 6c05f2b2..c5cc1427 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/util/Util.java @@ -750,7 +750,7 @@ public class Util extends DownloadActivity { int slashIndex = path.indexOf('/'); - if (slashIndex != 0) + if (slashIndex > 0) { return path.substring(0, slashIndex); } diff --git a/src/com/thejoshwa/ultrasonic/androidapp/view/SongView.java b/src/com/thejoshwa/ultrasonic/androidapp/view/SongView.java index ffd8c31f..874ff8d3 100644 --- a/src/com/thejoshwa/ultrasonic/androidapp/view/SongView.java +++ b/src/com/thejoshwa/ultrasonic/androidapp/view/SongView.java @@ -129,7 +129,11 @@ public class SongView extends UpdateView implements Checkable updateBackground(); this.song = song; - this.downloadFile = downloadService.forSong(song); + + if (downloadService != null) + { + this.downloadFile = downloadService.forSong(song); + } inflater.inflate(song.isVideo() ? R.layout.video_list_item : R.layout.song_list_item, this, true); @@ -307,11 +311,14 @@ public class SongView extends UpdateView implements Checkable this.rightImageType = ImageType.none; this.rightImage = null; - CharSequence statusText = this.statusTextView.getText(); - - if (statusText != "" || statusText != null) + if (this.statusTextView != null) { - this.statusTextView.setText(null); + CharSequence statusText = this.statusTextView.getText(); + + if (statusText != "" || statusText != null) + { + this.statusTextView.setText(null); + } } }