From b747bb28f97fb87cbf1e51f245be70403383ad2c Mon Sep 17 00:00:00 2001 From: Joshua Bahnsen Date: Sat, 2 Feb 2013 11:43:52 -0700 Subject: [PATCH] Add setting to show lockscreen music controls, fix Jukebox menu item, show album art in Album Track view --- res/values/strings.xml | 2 + res/xml/settings.xml | 6 + .../androidapp/activity/DownloadActivity.java | 8 +- .../activity/SelectAlbumActivity.java | 8 + .../service/DownloadServiceImpl.java | 2 +- .../parser/MusicDirectoryEntryParser.java | 2 +- .../subsonic/androidapp/util/Constants.java | 1 + .../androidapp/util/EntryAdapter.java | 145 +++++++++--------- .../subsonic/androidapp/util/ImageLoader.java | 93 ++++++----- .../subsonic/androidapp/util/Util.java | 7 +- 10 files changed, 143 insertions(+), 131 deletions(-) diff --git a/res/values/strings.xml b/res/values/strings.xml index 6adab2d4..3cde49dc 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -217,6 +217,8 @@ 120 seconds Show Notification Show now playing notification in the status bar + Show Lock Screen Controls + Show playback controls on the lock screen Max Albums 5 10 diff --git a/res/xml/settings.xml b/res/xml/settings.xml index 9d489551..f35df746 100644 --- a/res/xml/settings.xml +++ b/res/xml/settings.xml @@ -261,6 +261,12 @@ a:summary="@string/settings.show_notification_summary" a:key="showNotification" a:defaultValue="true"/> + + diff --git a/src/net/sourceforge/subsonic/androidapp/activity/DownloadActivity.java b/src/net/sourceforge/subsonic/androidapp/activity/DownloadActivity.java index a5aef724..6a724f91 100644 --- a/src/net/sourceforge/subsonic/androidapp/activity/DownloadActivity.java +++ b/src/net/sourceforge/subsonic/androidapp/activity/DownloadActivity.java @@ -98,7 +98,6 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi private ImageButton repeatButton; private MenuItem equalizerMenuItem; private MenuItem visualizerMenuItem; - private MenuItem jukeboxMenuItem; private View toggleListButton; private ScheduledExecutorService executorService; private DownloadFile currentPlaying; @@ -276,6 +275,7 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi onProgressChanged(); } }); + playlistView.setOnTouchListener(gestureListener); registerForContextMenu(playlistView); @@ -439,7 +439,6 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi MenuItem screenOption = menu.findItem(R.id.menu_screen_on_off); equalizerMenuItem = menu.findItem(R.id.download_equalizer); visualizerMenuItem = menu.findItem(R.id.download_visualizer); - jukeboxMenuItem = menu.findItem(R.id.download_jukebox); equalizerMenuItem.setEnabled(equalizerAvailable); equalizerMenuItem.setVisible(equalizerAvailable); @@ -536,13 +535,11 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi boolean active = !visualizerView.isActive(); visualizerView.setActive(active); getDownloadService().setShowVisualization(visualizerView.isActive()); - //updateButtons(); Util.toast(DownloadActivity.this, active ? R.string.download_visualizer_on : R.string.download_visualizer_off); return true; case R.id.download_jukebox: boolean jukeboxEnabled = !getDownloadService().isJukeboxEnabled(); getDownloadService().setJukeboxEnabled(jukeboxEnabled); - //updateButtons(); Util.toast(DownloadActivity.this, jukeboxEnabled ? R.string.download_jukebox_on : R.string.download_jukebox_off, false); return true; default: @@ -742,9 +739,6 @@ public class DownloadActivity extends SubsonicTabActivity implements OnGestureLi startButton.setVisibility(View.VISIBLE); break; } - - if (jukeboxMenuItem != null) - jukeboxMenuItem.setEnabled(getDownloadService().isJukeboxEnabled()); } private class SongListAdapter extends ArrayAdapter { diff --git a/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java b/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java index b6d8b585..41c6b55b 100644 --- a/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java +++ b/src/net/sourceforge/subsonic/androidapp/activity/SelectAlbumActivity.java @@ -18,9 +18,11 @@ */ package net.sourceforge.subsonic.androidapp.activity; +import android.app.ActionBar; import android.app.AlertDialog; import android.content.DialogInterface; import android.content.Intent; +import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.util.Log; @@ -606,6 +608,12 @@ public class SelectAlbumActivity extends SubsonicTabActivity { } if (songCount > 0) { + ActionBar actionBar = getActionBar(); + + if (actionBar != null) { + getImageLoader().setActionBarArtwork(selectButton, entries.get(0), actionBar); + } + entryList.addFooterView(footer); selectButton.setVisibility(View.VISIBLE); playNowButton.setVisibility(View.VISIBLE); diff --git a/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java b/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java index 5d6b8637..a63c7a16 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java +++ b/src/net/sourceforge/subsonic/androidapp/service/DownloadServiceImpl.java @@ -741,7 +741,7 @@ public class DownloadServiceImpl extends Service implements DownloadService { } private void setRemoteControl() { - if (Util.getMediaButtonsPreference(this)) { + if (Util.isLockScreenEnabled(this)) { AudioManager audioManager = (AudioManager) this.getSystemService(Context.AUDIO_SERVICE); audioManager.requestAudioFocus(_afChangeListener, AudioManager.STREAM_MUSIC, AudioManager.AUDIOFOCUS_GAIN); diff --git a/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java b/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java index 72f091c5..5a775066 100644 --- a/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java +++ b/src/net/sourceforge/subsonic/androidapp/service/parser/MusicDirectoryEntryParser.java @@ -38,12 +38,12 @@ public class MusicDirectoryEntryParser extends AbstractParser { entry.setDirectory(getBoolean("isDir")); entry.setCoverArt(get("coverArt")); entry.setArtist(get("artist")); + entry.setYear(getInteger("year")); entry.setStarred(getValueExists("starred")); if (!entry.isDirectory()) { entry.setAlbum(get("album")); entry.setTrack(getInteger("track")); - entry.setYear(getInteger("year")); entry.setGenre(get("genre")); entry.setContentType(get("contentType")); entry.setSuffix(get("suffix")); diff --git a/src/net/sourceforge/subsonic/androidapp/util/Constants.java b/src/net/sourceforge/subsonic/androidapp/util/Constants.java index 2730cf9e..7cb8e523 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/Constants.java +++ b/src/net/sourceforge/subsonic/androidapp/util/Constants.java @@ -78,6 +78,7 @@ public final class Constants { public static final String PREFERENCES_KEY_BUFFER_LENGTH = "bufferLength"; public static final String PREFERENCES_KEY_NETWORK_TIMEOUT = "networkTimeout"; public static final String PREFERENCES_KEY_SHOW_NOTIFICATION = "showNotification"; + public static final String PREFERENCES_KEY_SHOW_LOCK_SCREEN_CONTROLS = "showLockScreen"; public static final String PREFERENCES_KEY_MAX_ALBUMS = "maxAlbums"; public static final String PREFERENCES_KEY_MAX_SONGS = "maxSongs"; public static final String PREFERENCES_KEY_MAX_ARTISTS = "maxArtists"; diff --git a/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java b/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java index 1b4d72cf..c1918ce9 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java +++ b/src/net/sourceforge/subsonic/androidapp/util/EntryAdapter.java @@ -1,71 +1,74 @@ -/* - This file is part of Subsonic. - - Subsonic is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Subsonic is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Subsonic. If not, see . - - Copyright 2010 (C) Sindre Mehus - */ -package net.sourceforge.subsonic.androidapp.util; - -import java.util.List; - -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import net.sourceforge.subsonic.androidapp.activity.SubsonicTabActivity; -import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; - -/** - * @author Sindre Mehus - */ -public class EntryAdapter extends ArrayAdapter { - - private final SubsonicTabActivity activity; - private final ImageLoader imageLoader; - private final boolean checkable; - - public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List entries, boolean checkable) { - super(activity, android.R.layout.simple_list_item_1, entries); - this.activity = activity; - this.imageLoader = imageLoader; - this.checkable = checkable; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - MusicDirectory.Entry entry = getItem(position); - - if (entry.isDirectory()) { - AlbumView view; - // TODO: Reuse AlbumView objects once cover art loading is working. -// if (convertView != null && convertView instanceof AlbumView) { -// view = (AlbumView) convertView; -// } else { - view = new AlbumView(activity); -// } - view.setAlbum(entry, imageLoader); - return view; - - } else { - SongView view; - if (convertView != null && convertView instanceof SongView) { - view = (SongView) convertView; - } else { - view = new SongView(activity); - } - view.setSong(entry, checkable); - return view; - } - } -} +/* + This file is part of Subsonic. + + Subsonic is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + Subsonic is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Subsonic. If not, see . + + Copyright 2010 (C) Sindre Mehus + */ +package net.sourceforge.subsonic.androidapp.util; + +import java.util.List; + +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import net.sourceforge.subsonic.androidapp.activity.SubsonicTabActivity; +import net.sourceforge.subsonic.androidapp.domain.MusicDirectory; + +/** + * @author Sindre Mehus + */ +public class EntryAdapter extends ArrayAdapter { + + private final SubsonicTabActivity activity; + private final ImageLoader imageLoader; + private final boolean checkable; + + public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List entries, boolean checkable) { + super(activity, android.R.layout.simple_list_item_1, entries); + this.activity = activity; + this.imageLoader = imageLoader; + this.checkable = checkable; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + MusicDirectory.Entry entry = getItem(position); + + if (entry.isDirectory()) { + AlbumView view; + + if (convertView != null && convertView instanceof AlbumView) { + view = (AlbumView) convertView; + } else { + view = new AlbumView(activity); + } + + view.setAlbum(entry, imageLoader); + return view; + + } else { + SongView view; + + if (convertView != null && convertView instanceof SongView) { + view = (SongView) convertView; + } else { + view = new SongView(activity); + } + + view.setSong(entry, checkable); + return view; + } + } +} diff --git a/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java b/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java index 5ae21b38..302b5033 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java +++ b/src/net/sourceforge/subsonic/androidapp/util/ImageLoader.java @@ -18,6 +18,7 @@ */ package net.sourceforge.subsonic.androidapp.util; +import android.app.ActionBar; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Canvas; @@ -29,6 +30,7 @@ import android.graphics.drawable.BitmapDrawable; import android.graphics.drawable.Drawable; import android.graphics.drawable.TransitionDrawable; import android.os.Handler; +import android.os.Message; import android.util.DisplayMetrics; import android.util.Log; import android.view.View; @@ -60,6 +62,7 @@ public class ImageLoader implements Runnable { private final int imageSizeDefault; private final int imageSizeLarge; private Drawable largeUnknownImage; + private Drawable drawable; public ImageLoader(Context context) { queue = new LinkedBlockingQueue(500); @@ -67,7 +70,6 @@ public class ImageLoader implements Runnable { // Determine the density-dependent image sizes. imageSizeDefault = context.getResources().getDrawable(R.drawable.unknown_album).getIntrinsicHeight(); DisplayMetrics metrics = context.getResources().getDisplayMetrics(); - //imageSizeLarge = (int) Math.round(Math.min(metrics.widthPixels, metrics.heightPixels) * 0.6); imageSizeLarge = (int) Math.round(Math.min(metrics.widthPixels, metrics.heightPixels)); for (int i = 0; i < CONCURRENCY; i++) { @@ -102,7 +104,46 @@ public class ImageLoader implements Runnable { } queue.offer(new Task(view, entry, size, large, large, crossfade)); } + + public void setActionBarArtwork(final View view, final MusicDirectory.Entry entry, final ActionBar ab) { + if (entry == null || entry.getCoverArt() == null) { + ab.setLogo(largeUnknownImage); + } + final int size = imageSizeLarge; + drawable = cache.get(getKey(entry.getCoverArt(), size)); + + if (drawable != null) { + ab.setLogo(drawable); + } + + final Handler handler = new Handler(){ + @Override + public void handleMessage(Message msg) { + drawable = (Drawable) msg.obj; + ab.setLogo(drawable); + } + }; + + new Thread(new Runnable() { + public void run() { + MusicService musicService = MusicServiceFactory.getMusicService(view.getContext()); + + try + { + Bitmap bitmap = musicService.getCoverArt(view.getContext(), entry, size, true, null); + drawable = Util.createDrawableFromBitmap(view.getContext(), bitmap); + Message msg = Message.obtain(); + msg.obj = drawable; + handler.sendMessage(msg); + cache.put(getKey(entry.getCoverArt(), size), drawable); + } catch (Throwable x) { + Log.e(TAG, "Failed to download album art.", x); + } + } + }).start(); + } + private String getKey(String coverArtId, int size) { return coverArtId + size; } @@ -161,54 +202,6 @@ public class ImageLoader implements Runnable { } } - private Bitmap createReflection(Bitmap originalImage) { - - int width = originalImage.getWidth(); - int height = originalImage.getHeight(); - - // The gap we want between the reflection and the original image - final int reflectionGap = 4; - - // This will not scale but will flip on the Y axis - Matrix matrix = new Matrix(); - matrix.preScale(1, -1); - - // Create a Bitmap with the flip matix applied to it. - // We only want the bottom half of the image - Bitmap reflectionImage = Bitmap.createBitmap(originalImage, 0, height / 2, width, height / 2, matrix, false); - - // Create a new bitmap with same width but taller to fit reflection - Bitmap bitmapWithReflection = Bitmap.createBitmap(width, (height + height / 2), Bitmap.Config.ARGB_8888); - - // Create a new Canvas with the bitmap that's big enough for - // the image plus gap plus reflection - Canvas canvas = new Canvas(bitmapWithReflection); - - // Draw in the original image - canvas.drawBitmap(originalImage, 0, 0, null); - - // Draw in the gap - Paint defaultPaint = new Paint(); - canvas.drawRect(0, height, width, height + reflectionGap, defaultPaint); - - // Draw in the reflection - canvas.drawBitmap(reflectionImage, 0, height + reflectionGap, null); - - // Create a shader that is a linear gradient that covers the reflection - Paint paint = new Paint(); - LinearGradient shader = new LinearGradient(0, originalImage.getHeight(), 0, - bitmapWithReflection.getHeight() + reflectionGap, 0x70000000, 0xff000000, - Shader.TileMode.CLAMP); - - // Set the paint to use this shader (linear gradient) - paint.setShader(shader); - - // Draw a rectangle using the paint with our linear gradient - canvas.drawRect(0, height, width, bitmapWithReflection.getHeight() + reflectionGap, paint); - - return bitmapWithReflection; - } - private class Task { private final View view; private final MusicDirectory.Entry entry; @@ -227,7 +220,7 @@ public class ImageLoader implements Runnable { this.crossfade = crossfade; handler = new Handler(); } - + public void execute() { try { MusicService musicService = MusicServiceFactory.getMusicService(view.getContext()); diff --git a/src/net/sourceforge/subsonic/androidapp/util/Util.java b/src/net/sourceforge/subsonic/androidapp/util/Util.java index fe8f48c6..40313823 100644 --- a/src/net/sourceforge/subsonic/androidapp/util/Util.java +++ b/src/net/sourceforge/subsonic/androidapp/util/Util.java @@ -139,6 +139,11 @@ public class Util extends DownloadActivity { SharedPreferences prefs = getPreferences(context); return prefs.getBoolean(Constants.PREFERENCES_KEY_SHOW_NOTIFICATION, false); } + + public static boolean isLockScreenEnabled(Context context) { + SharedPreferences prefs = getPreferences(context); + return prefs.getBoolean(Constants.PREFERENCES_KEY_SHOW_LOCK_SCREEN_CONTROLS, false); + } public static void setActiveServer(Context context, int instance) { SharedPreferences prefs = getPreferences(context); @@ -701,7 +706,7 @@ public class Util extends DownloadActivity { // Ignored. } } - + private static void startForeground(Service service, int notificationId, Notification notification) { // Service.startForeground() was introduced in Android 2.0. // Use reflection to maintain compatibility with 1.5.