Add setting to show lockscreen music controls, fix Jukebox menu item, show album art in Album Track view

This commit is contained in:
Joshua Bahnsen 2013-02-02 11:43:52 -07:00
parent c58d1acf93
commit b747bb28f9
10 changed files with 143 additions and 131 deletions

View File

@ -217,6 +217,8 @@
<string name="settings.network_timeout_120000">120 seconds</string>
<string name="settings.show_notification">Show Notification</string>
<string name="settings.show_notification_summary">Show now playing notification in the status bar</string>
<string name="settings.show_lockscreen_controls">Show Lock Screen Controls</string>
<string name="settings.show_lockscreen_controls_summary">Show playback controls on the lock screen</string>
<string name="settings.max_albums">Max Albums</string>
<string name="settings.max_albums_5">5</string>
<string name="settings.max_albums_10">10</string>

View File

@ -261,6 +261,12 @@
a:summary="@string/settings.show_notification_summary"
a:key="showNotification"
a:defaultValue="true"/>
<CheckBoxPreference
a:title="@string/settings.show_lockscreen_controls"
a:summary="@string/settings.show_lockscreen_controls_summary"
a:key="showLockScreen"
a:defaultValue="true"/>
</PreferenceCategory>

View File

@ -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<DownloadFile> {

View File

@ -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);

View File

@ -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);

View File

@ -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"));

View File

@ -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";

View File

@ -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 <http://www.gnu.org/licenses/>.
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<MusicDirectory.Entry> {
private final SubsonicTabActivity activity;
private final ImageLoader imageLoader;
private final boolean checkable;
public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List<MusicDirectory.Entry> 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 <http://www.gnu.org/licenses/>.
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<MusicDirectory.Entry> {
private final SubsonicTabActivity activity;
private final ImageLoader imageLoader;
private final boolean checkable;
public EntryAdapter(SubsonicTabActivity activity, ImageLoader imageLoader, List<MusicDirectory.Entry> 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;
}
}
}

View File

@ -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<Task>(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());

View File

@ -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.