Add a 1x1 cell widget

Larger sizes are to come
This commit is contained in:
Christopher Eby 2010-02-26 23:21:46 -06:00
parent 21d5fafa74
commit dc0639784e
10 changed files with 301 additions and 87 deletions

View File

@ -15,6 +15,12 @@
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<receiver android:name=".OneCellWidget" android:label="Vanilla Music 1x1">
<intent-filter>
<action android:name="android.appwidget.action.APPWIDGET_UPDATE" />
</intent-filter>
<meta-data android:name="android.appwidget.provider" android:resource="@xml/one_cell_widget" />
</receiver>
<activity
android:name="RemoteActivity"
android:theme="@android:style/Theme.Dialog"

BIN
res/drawable/empty.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@drawable/next_normal" />
<item
android:state_focused="true"
android:drawable="@drawable/next_normal" />
<item android:drawable="@drawable/empty" />
</selector>

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_pressed="true"
android:drawable="@drawable/pause_normal" />
<item
android:state_focused="true"
android:drawable="@drawable/pause_normal" />
<item android:drawable="@drawable/empty" />
</selector>

View File

@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/stopped_text"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:gravity="center"
android:text="Service stopped." />

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<ImageView
android:id="@+id/cover_view"
android:layout_height="fill_parent"
android:layout_width="fill_parent" />
<LinearLayout
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:orientation="vertical" >
<ImageButton
android:id="@+id/play_pause"
android:background="@drawable/hidden_play_pause"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1" />
<ImageButton
android:id="@+id/next"
android:background="@drawable/hidden_next"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:layout_weight="1" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,6 @@
<appwidget-provider
xmlns:android="http://schemas.android.com/apk/res/android"
android:minWidth="72dip"
android:minHeight="72dip"
android:initialLayout="@layout/default_widget"
/>

View File

@ -72,14 +72,14 @@ public class CoverView extends View {
}
}
private void drawText(Canvas canvas, String text, float left, float top, float width, float maxWidth, Paint paint)
private static void drawText(Canvas canvas, String text, float left, float top, float width, float maxWidth, Paint paint)
{
float offset = Math.max(0, maxWidth - width) / 2;
canvas.clipRect(left, top, left + maxWidth, top + paint.getTextSize() * 2, Region.Op.REPLACE);
canvas.drawText(text, left + offset, top - paint.ascent(), paint);
}
private RectF centerRect(float maxWidth, float maxHeight, float width, float height)
private static RectF centerRect(float maxWidth, float maxHeight, float width, float height)
{
RectF rect = new RectF();
rect.left = (maxWidth - width) / 2;
@ -89,94 +89,146 @@ public class CoverView extends View {
return rect;
}
private static RectF bottomStretchRect(float maxWidth, float maxHeight, float width, float height)
{
RectF rect = new RectF();
rect.left = 0;
rect.top = maxHeight - height;
rect.right = maxWidth;
rect.bottom = maxHeight;
return rect;
}
public static Bitmap createMiniBitmap(Song song, int width, int height)
{
if (song == null || width < 1 || height < 1)
return null;
Paint paint = new Paint();
paint.setAntiAlias(true);
String title = song.title == null ? "" : song.title;
Bitmap cover = song.coverPath == null ? null : BitmapFactory.decodeFile(song.coverPath);
float titleSize = 12;
float padding = 2;
paint.setTextSize(titleSize);
float titleWidth = paint.measureText(title);
float boxWidth = width;
float boxHeight = Math.min(height, titleSize + padding * 2);
Bitmap bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
if (cover != null) {
canvas.drawBitmap(cover, null, new Rect(0, 0, width, height), paint);
cover.recycle();
cover = null;
}
RectF boxRect = bottomStretchRect(width, height, boxWidth, boxHeight);
paint.setARGB(150, 0, 0, 0);
canvas.drawRect(boxRect, paint);
float maxWidth = boxWidth - padding * 2;
paint.setARGB(255, 255, 255, 255);
boxRect.top += padding;
boxRect.left += padding;
paint.setTextSize(titleSize);
drawText(canvas, title, boxRect.left, boxRect.top, titleWidth, maxWidth, paint);
return bitmap;
}
public static Bitmap createBitmap(Song song, int width, int height)
{
if (song == null || width < 1 || height < 1)
return null;
Paint paint = new Paint();
paint.setAntiAlias(true);
String title = song.title == null ? "" : song.title;
String album = song.album == null ? "" : song.album;
String artist = song.artist == null ? "" : song.artist;
Bitmap cover = song.coverPath == null ? null : BitmapFactory.decodeFile(song.coverPath);
float titleSize = 20;
float subSize = 14;
float padding = 10;
paint.setTextSize(titleSize);
float titleWidth = paint.measureText(title);
paint.setTextSize(subSize);
float albumWidth = paint.measureText(album);
float artistWidth = paint.measureText(artist);
float boxWidth = Math.min(width, Math.max(titleWidth, Math.max(artistWidth, albumWidth)) + padding * 2);
float boxHeight = Math.min(height, titleSize + subSize * 2 + padding * 4);
float coverWidth;
float coverHeight;
if (cover == null) {
coverWidth = 0;
coverHeight = 0;
} else {
coverWidth = cover.getWidth();
coverHeight = cover.getHeight();
float drawableAspectRatio = coverHeight / coverWidth;
float viewAspectRatio = (float) height / width;
float scale = drawableAspectRatio > viewAspectRatio ? height / coverWidth
: width / coverHeight;
coverWidth *= scale;
coverHeight *= scale;
}
int bitmapWidth = (int)Math.max(coverWidth, boxWidth);
int bitmapHeight = (int)Math.max(coverHeight, boxHeight);
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
if (cover != null) {
RectF rect = centerRect(bitmapWidth, bitmapHeight, coverWidth, coverHeight);
canvas.drawBitmap(cover, null, rect, paint);
cover.recycle();
cover = null;
}
RectF boxRect = centerRect(bitmapWidth, bitmapHeight, boxWidth, boxHeight);
paint.setARGB(150, 0, 0, 0);
canvas.drawRect(boxRect, paint);
float maxWidth = boxWidth - padding * 2;
paint.setARGB(255, 255, 255, 255);
boxRect.top += padding;
boxRect.left += padding;
paint.setTextSize(titleSize);
drawText(canvas, title, boxRect.left, boxRect.top, titleWidth, maxWidth, paint);
boxRect.top += titleSize + padding;
paint.setTextSize(subSize);
drawText(canvas, album, boxRect.left, boxRect.top, albumWidth, maxWidth, paint);
boxRect.top += subSize + padding;
drawText(canvas, artist, boxRect.left, boxRect.top, artistWidth, maxWidth, paint);
return bitmap;
}
private void createBitmap(int i)
{
Song song = mSongs[i];
Bitmap bitmap = null;
if (song != null) {
int width = getWidth();
int height = getHeight();
if (width == 0 || height == 0)
return;
Paint paint = new Paint();
paint.setAntiAlias(true);
String title = song.title == null ? "" : song.title;
String album = song.album == null ? "" : song.album;
String artist = song.artist == null ? "" : song.artist;
Bitmap cover = song.coverPath == null ? null : BitmapFactory.decodeFile(song.coverPath);
float titleSize = 20;
float subSize = 14;
float padding = 10;
paint.setTextSize(titleSize);
float titleWidth = paint.measureText(title);
paint.setTextSize(subSize);
float albumWidth = paint.measureText(album);
float artistWidth = paint.measureText(artist);
float boxWidth = Math.min(width, Math.max(titleWidth, Math.max(artistWidth, albumWidth)) + padding * 2);
float boxHeight = Math.min(height, titleSize + subSize * 2 + padding * 4);
float coverWidth;
float coverHeight;
if (cover == null) {
coverWidth = 0;
coverHeight = 0;
} else {
coverWidth = cover.getWidth();
coverHeight = cover.getHeight();
float drawableAspectRatio = coverHeight / coverWidth;
float viewAspectRatio = (float) height / width;
float scale = drawableAspectRatio > viewAspectRatio ? height / coverWidth
: width / coverHeight;
coverWidth *= scale;
coverHeight *= scale;
}
int bitmapWidth = (int)Math.max(coverWidth, boxWidth);
int bitmapHeight = (int)Math.max(coverHeight, boxHeight);
bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.RGB_565);
Canvas canvas = new Canvas(bitmap);
if (cover != null) {
RectF rect = centerRect(bitmapWidth, bitmapHeight, coverWidth, coverHeight);
canvas.drawBitmap(cover, null, rect, paint);
cover.recycle();
cover = null;
}
RectF boxRect = centerRect(bitmapWidth, bitmapHeight, boxWidth, boxHeight);
paint.setARGB(150, 0, 0, 0);
canvas.drawRect(boxRect, paint);
float maxWidth = boxWidth - padding * 2;
paint.setARGB(255, 255, 255, 255);
boxRect.top += padding;
boxRect.left += padding;
paint.setTextSize(titleSize);
drawText(canvas, title, boxRect.left, boxRect.top, titleWidth, maxWidth, paint);
boxRect.top += titleSize + padding;
paint.setTextSize(subSize);
drawText(canvas, album, boxRect.left, boxRect.top, albumWidth, maxWidth, paint);
boxRect.top += subSize + padding;
drawText(canvas, artist, boxRect.left, boxRect.top, artistWidth, maxWidth, paint);
}
Bitmap oldBitmap = mBitmaps[i];
mBitmaps[i] = bitmap;
mBitmaps[i] = createBitmap(mSongs[i], getWidth(), getHeight());
if (oldBitmap != null)
oldBitmap.recycle();
}

View File

@ -0,0 +1,41 @@
package org.kreed.vanilla;
import android.app.PendingIntent;
import android.appwidget.AppWidgetManager;
import android.appwidget.AppWidgetProvider;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.widget.RemoteViews;
public class OneCellWidget extends AppWidgetProvider {
@Override
public void onUpdate(Context context, AppWidgetManager manager, int[] ids)
{
context.sendBroadcast(new Intent(PlaybackService.APPWIDGET_SMALL_UPDATE));
}
private static void sendUpdate(Context context, RemoteViews views)
{
AppWidgetManager manager = AppWidgetManager.getInstance(context);
ComponentName widget = new ComponentName(context, OneCellWidget.class);
manager.updateAppWidget(widget, views);
}
public static void update(Context context, Song song)
{
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.one_cell_widget);
views.setOnClickPendingIntent(R.id.play_pause, PendingIntent.getBroadcast(context, 0, new Intent(PlaybackService.TOGGLE_PLAYBACK), 0));
views.setOnClickPendingIntent(R.id.next, PendingIntent.getBroadcast(context, 0, new Intent(PlaybackService.NEXT_SONG), 0));
if (song != null)
views.setImageViewBitmap(R.id.cover_view, CoverView.createMiniBitmap(song, 72, 72));
sendUpdate(context, views);
}
public static void reset(Context context)
{
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.default_widget);
sendUpdate(context, views);
}
}

View File

@ -56,6 +56,10 @@ import android.widget.Toast;
public class PlaybackService extends Service implements Runnable, MediaPlayer.OnCompletionListener, MediaPlayer.OnErrorListener, SharedPreferences.OnSharedPreferenceChangeListener {
private static final int NOTIFICATION_ID = 2;
public static final String APPWIDGET_SMALL_UPDATE = "org.kreed.vanilla.action.APPWIDGET_SMALL_UPDATE";
public static final String TOGGLE_PLAYBACK = "org.kreed.vanilla.action.TOGGLE_PLAYBACK";
public static final String NEXT_SONG = "org.kreed.vanilla.action.NEXT_SONG";
public static final int STATE_NORMAL = 0;
public static final int STATE_NO_MEDIA = 1;
public static final int STATE_PLAYING = 2;
@ -187,6 +191,8 @@ public class PlaybackService extends Service implements Runnable, MediaPlayer.On
unregisterReceiver(mReceiver);
mNotificationManager.cancel(NOTIFICATION_ID);
resetWidgets();
if (mWakeLock != null && mWakeLock.isHeld())
mWakeLock.release();
}
@ -235,6 +241,27 @@ public class PlaybackService extends Service implements Runnable, MediaPlayer.On
} else if (Intent.ACTION_MEDIA_SCANNER_FINISHED.equals(action)
|| Intent.ACTION_MEDIA_SCANNER_SCAN_FILE.equals(action)) {
mHandler.sendEmptyMessage(RETRIEVE_SONGS);
} else if (TOGGLE_PLAYBACK.equals(action)) {
if (mHandler.hasMessages(DO)) {
mHandler.removeMessages(DO);
Intent launcher = new Intent(PlaybackService.this, NowPlayingActivity.class);
launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(launcher);
} else {
mHandler.sendMessageDelayed(mHandler.obtainMessage(DO, PLAY_PAUSE, 0), 250);
}
} else if (NEXT_SONG.equals(action)) {
if (mHandler.hasMessages(DO)) {
mHandler.removeMessages(DO);
Intent launcher = new Intent(PlaybackService.this, NowPlayingActivity.class);
launcher.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(launcher);
} else {
mHandler.sendMessageDelayed(mHandler.obtainMessage(DO, SET_SONG, 1), 250);
}
} else if (APPWIDGET_SMALL_UPDATE.equals(intent.getAction())) {
OneCellWidget.update(PlaybackService.this, getSong(0));
return;
}
}
};
@ -297,6 +324,7 @@ public class PlaybackService extends Service implements Runnable, MediaPlayer.On
private static final int HANDLE_PAUSE = 8;
private static final int RETRIEVE_SONGS = 9;
private static final int CALL = 10;
private static final int DO = 11;
public void run()
{
@ -376,6 +404,16 @@ public class PlaybackService extends Service implements Runnable, MediaPlayer.On
}
}
break;
case DO:
if (message.arg1 == DO) {
Log.w("VanillaMusic", "handleMessage.DO should not be called with arg1 = DO");
return;
}
message.what = message.arg1;
message.arg1 = message.arg2;
handleMessage(message);
break;
}
}
};
@ -384,6 +422,9 @@ public class PlaybackService extends Service implements Runnable, MediaPlayer.On
filter.addAction(Intent.ACTION_HEADSET_PLUG);
filter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED);
filter.addAction(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
filter.addAction(TOGGLE_PLAYBACK);
filter.addAction(NEXT_SONG);
filter.addAction(APPWIDGET_SMALL_UPDATE);
registerReceiver(mReceiver, filter);
PowerManager powerManager = (PowerManager)getSystemService(POWER_SERVICE);
@ -554,6 +595,8 @@ public class PlaybackService extends Service implements Runnable, MediaPlayer.On
mSongTimeline.remove(0);
--mCurrentSong;
}
updateWidgets();
}
public void onCompletion(MediaPlayer player)
@ -598,4 +641,15 @@ public class PlaybackService extends Service implements Runnable, MediaPlayer.On
return mSongTimeline.get(pos);
}
private void updateWidgets()
{
Song song = getSong(0);
OneCellWidget.update(this, song);
}
private void resetWidgets()
{
OneCellWidget.reset(this);
}
}