Controls in notification

This commit is contained in:
Christopher Eby 2012-02-18 01:46:38 -06:00
parent 48a4819392
commit 8d32bcf2ae
8 changed files with 156 additions and 24 deletions

View File

@ -1,14 +1,12 @@
#!/bin/sh #!/bin/sh
force=$1
gen() { gen() {
name=`basename "$1" .svgz` name=`basename "$1" .svgz`
png="res/drawable-$2/$name.png" png="res/drawable-$2/$name.png"
if [[ "$1" -nt "$png" || $force ]]; then if [ "$1" -nt "$png" -o ! -e "$png" ]; then
inkscape --without-gui --export-area-page --export-dpi=$3 --export-png="$png.out" $1 inkscape --without-gui --export-area-page --export-dpi=$3 --export-png="$png" $1
pngcrush -q -brute "$png.out" "$png" convert -strip "$png" "$png"
rm "$png.out" optipng -quiet -o7 "$png"
echo echo
fi fi
} }

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 251 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

View File

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
Copyright (C) 2012 Christopher Eby <kreed@kreed.org>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_gravity="center_vertical"
android:orientation="horizontal">
<ImageView
android:id="@+id/cover"
android:scaleType="fitCenter"
android:maxWidth="80dip"
android:adjustViewBounds="true"
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:layout_marginRight="5dip" />
<LinearLayout
android:layout_width="0px"
android:layout_height="wrap_content"
android:layout_weight="1"
android:layout_gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent.Title"
android:singleLine="true"
android:ellipsize="marquee" />
<TextView
android:id="@+id/artist"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textAppearance="@android:style/TextAppearance.StatusBar.EventContent"
android:singleLine="true"
android:ellipsize="marquee" />
</LinearLayout>
<ImageButton
android:id="@+id/play_pause"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:layout_height="42dip"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:background="?android:attr/selectableItemBackground"
android:scaleType="fitCenter"
android:src="@drawable/play_normal"
android:contentDescription="@string/play_pause" />
<ImageButton
android:id="@+id/next"
android:paddingTop="5dip"
android:paddingBottom="5dip"
android:layout_height="42dip"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:background="?android:attr/selectableItemBackground"
android:scaleType="fitCenter"
android:src="@drawable/next_normal"
android:contentDescription="@string/next_song" />
<ImageButton
android:id="@+id/close"
android:padding="5dip"
android:layout_height="42dip"
android:layout_width="wrap_content"
android:layout_gravity="center"
android:background="?android:attr/selectableItemBackground"
android:scaleType="fitCenter"
android:src="@drawable/notification_close" />
</LinearLayout>

View File

@ -26,7 +26,7 @@ THE SOFTWARE.
android:layout_gravity="center_vertical" android:layout_gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:id="@+id/icon" android:id="@+id/cover"
android:scaleType="fitCenter" android:scaleType="fitCenter"
android:maxWidth="80dip" android:maxWidth="80dip"
android:adjustViewBounds="true" android:adjustViewBounds="true"

View File

@ -28,12 +28,12 @@ import android.app.PendingIntent;
import android.app.Service; import android.app.Service;
import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetManager;
import android.content.BroadcastReceiver; import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.Context; import android.content.Context;
import android.content.Intent; import android.content.Intent;
import android.content.IntentFilter; import android.content.IntentFilter;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.database.ContentObserver; import android.database.ContentObserver;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
@ -113,6 +113,13 @@ public final class PlaybackService extends Service
* instead. * instead.
*/ */
public static final String ACTION_TOGGLE_PLAYBACK_DELAYED = "org.kreed.vanilla.action.TOGGLE_PLAYBACK_DELAYED"; public static final String ACTION_TOGGLE_PLAYBACK_DELAYED = "org.kreed.vanilla.action.TOGGLE_PLAYBACK_DELAYED";
/**
* Action for startService: toggle playback on/off.
*
* This works the same way as ACTION_PLAY_PAUSE but prevents the notification
* from being hidden regardless of notification visibility settings.
*/
public static final String ACTION_TOGGLE_PLAYBACK_NOTIFICATION = "org.kreed.vanilla.action.TOGGLE_PLAYBACK_NOTIFICATION";
/** /**
* Action for startService: advance to the next song. * Action for startService: advance to the next song.
*/ */
@ -144,16 +151,18 @@ public final class PlaybackService extends Service
* when this is called. * when this is called.
*/ */
public static final String ACTION_PREVIOUS_SONG_AUTOPLAY = "org.kreed.vanilla.action.PREVIOUS_SONG_AUTOPLAY"; public static final String ACTION_PREVIOUS_SONG_AUTOPLAY = "org.kreed.vanilla.action.PREVIOUS_SONG_AUTOPLAY";
/** /**
* Change the shuffle mode. * Change the shuffle mode.
*/ */
public static final String ACTION_CYCLE_SHUFFLE = "org.kreed.vanilla.CYCLE_SHUFFLE"; public static final String ACTION_CYCLE_SHUFFLE = "org.kreed.vanilla.CYCLE_SHUFFLE";
/** /**
* Change the repeat mode. * Change the repeat mode.
*/ */
public static final String ACTION_CYCLE_REPEAT = "org.kreed.vanilla.CYCLE_REPEAT"; public static final String ACTION_CYCLE_REPEAT = "org.kreed.vanilla.CYCLE_REPEAT";
/**
* Pause music and hide the notifcation.
*/
public static final String ACTION_CLOSE_NOTIFICATION = "org.kreed.vanilla.CLOSE_NOTIFICATION";
public static final int NEVER = 0; public static final int NEVER = 0;
public static final int WHEN_PLAYING = 1; public static final int WHEN_PLAYING = 1;
@ -353,6 +362,11 @@ public final class PlaybackService extends Service
* What to do when an accelerometer shake is detected. * What to do when an accelerometer shake is detected.
*/ */
private Action mShakeAction; private Action mShakeAction;
/**
* If true, the notification should not be hidden when pausing regardless
* of user settings.
*/
private boolean mForceNotificationVisible;
@Override @Override
public void onCreate() public void onCreate()
@ -453,6 +467,14 @@ public final class PlaybackService extends Service
if (ACTION_TOGGLE_PLAYBACK.equals(action)) { if (ACTION_TOGGLE_PLAYBACK.equals(action)) {
playPause(); playPause();
} else if (ACTION_TOGGLE_PLAYBACK_NOTIFICATION.equals(action)) {
mForceNotificationVisible = true;
synchronized (mStateLock) {
if ((mState & FLAG_PLAYING) != 0)
pause();
else
play();
}
} else if (ACTION_TOGGLE_PLAYBACK_DELAYED.equals(action)) { } else if (ACTION_TOGGLE_PLAYBACK_DELAYED.equals(action)) {
if (mHandler.hasMessages(CALL_GO, Integer.valueOf(0))) { if (mHandler.hasMessages(CALL_GO, Integer.valueOf(0))) {
mHandler.removeMessages(CALL_GO, Integer.valueOf(0)); mHandler.removeMessages(CALL_GO, Integer.valueOf(0));
@ -493,6 +515,11 @@ public final class PlaybackService extends Service
cycleFinishAction(); cycleFinishAction();
} else if (ACTION_CYCLE_SHUFFLE.equals(action)) { } else if (ACTION_CYCLE_SHUFFLE.equals(action)) {
cycleShuffle(); cycleShuffle();
} else if (ACTION_CLOSE_NOTIFICATION.equals(action)) {
mForceNotificationVisible = false;
pause();
stopForeground(true); // sometimes required to clear notification
mNotificationManager.cancel(NOTIFICATION_ID);
} }
MediaButtonReceiver.registerMediaButton(this); MediaButtonReceiver.registerMediaButton(this);
@ -708,7 +735,7 @@ public final class PlaybackService extends Service
if (mMediaPlayerInitialized) if (mMediaPlayerInitialized)
mMediaPlayer.pause(); mMediaPlayer.pause();
if (mNotificationMode == ALWAYS) { if (mNotificationMode == ALWAYS || mForceNotificationVisible) {
stopForeground(false); stopForeground(false);
mNotificationManager.notify(NOTIFICATION_ID, createNotification(mCurrentSong, mState)); mNotificationManager.notify(NOTIFICATION_ID, createNotification(mCurrentSong, mState));
} else { } else {
@ -818,7 +845,7 @@ public final class PlaybackService extends Service
private void updateNotification() private void updateNotification()
{ {
if ((mNotificationMode == ALWAYS || mNotificationMode == WHEN_PLAYING && (mState & FLAG_PLAYING) != 0) && mCurrentSong != null) if ((mForceNotificationVisible || mNotificationMode == ALWAYS || mNotificationMode == WHEN_PLAYING && (mState & FLAG_PLAYING) != 0) && mCurrentSong != null)
mNotificationManager.notify(NOTIFICATION_ID, createNotification(mCurrentSong, mState)); mNotificationManager.notify(NOTIFICATION_ID, createNotification(mCurrentSong, mState));
else else
mNotificationManager.cancel(NOTIFICATION_ID); mNotificationManager.cancel(NOTIFICATION_ID);
@ -865,6 +892,7 @@ public final class PlaybackService extends Service
*/ */
public int playPause() public int playPause()
{ {
mForceNotificationVisible = false;
synchronized (mStateLock) { synchronized (mStateLock) {
if ((mState & FLAG_PLAYING) != 0) if ((mState & FLAG_PLAYING) != 0)
return pause(); return pause();
@ -1621,23 +1649,40 @@ public final class PlaybackService extends Service
Bitmap cover = song.getCover(this); Bitmap cover = song.getCover(this);
if (cover == null) { if (cover == null) {
views.setImageViewResource(R.id.icon, R.drawable.icon); views.setImageViewResource(R.id.cover, R.drawable.albumart_mp_unknown_list);
} else { } else {
views.setImageViewBitmap(R.id.icon, cover); views.setImageViewBitmap(R.id.cover, cover);
} }
if (playing) {
views.setTextViewText(R.id.title, song.title); String title = song.title;
} else {
views.setTextViewText(R.id.title, getResources().getString(R.string.notification_title_paused, song.title)); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
int playButton = playing ? R.drawable.pause_normal : R.drawable.play_normal;
views.setImageViewResource(R.id.play_pause, playButton);
ComponentName service = new ComponentName(this, PlaybackService.class);
Intent playPause = new Intent(PlaybackService.ACTION_TOGGLE_PLAYBACK_NOTIFICATION);
playPause.setComponent(service);
views.setOnClickPendingIntent(R.id.play_pause, PendingIntent.getService(this, 0, playPause, 0));
Intent next = new Intent(PlaybackService.ACTION_NEXT_SONG);
next.setComponent(service);
views.setOnClickPendingIntent(R.id.next, PendingIntent.getService(this, 0, next, 0));
Intent close = new Intent(PlaybackService.ACTION_CLOSE_NOTIFICATION);
close.setComponent(service);
views.setOnClickPendingIntent(R.id.close, PendingIntent.getService(this, 0, close, 0));
} else if (!playing) {
title = getResources().getString(R.string.notification_title_paused, song.title);
} }
views.setTextViewText(R.id.title, title);
views.setTextViewText(R.id.artist, song.artist); views.setTextViewText(R.id.artist, song.artist);
if (mInvertNotification) { if (mInvertNotification) {
TypedArray array = getTheme().obtainStyledAttributes(new int[] { android.R.attr.textColorPrimary }); views.setTextColor(R.id.title, 0xffffffff);
int color = array.getColor(0, 0xFF00FF); views.setTextColor(R.id.artist, 0xffffffff);
array.recycle();
views.setTextColor(R.id.title, color);
views.setTextColor(R.id.artist, color);
} }
Notification notification = new Notification(); Notification notification = new Notification();
@ -1659,6 +1704,7 @@ public final class PlaybackService extends Service
case AudioManager.AUDIOFOCUS_LOSS: case AudioManager.AUDIOFOCUS_LOSS:
case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT: case AudioManager.AUDIOFOCUS_LOSS_TRANSIENT:
mDuckedLoss = false; mDuckedLoss = false;
mForceNotificationVisible = true;
unsetFlag(FLAG_PLAYING); unsetFlag(FLAG_PLAYING);
break; break;
case AudioManager.AUDIOFOCUS_GAIN: case AudioManager.AUDIOFOCUS_GAIN: