Add a 1x1 cell widget
Larger sizes are to come
This commit is contained in:
parent
21d5fafa74
commit
dc0639784e
@ -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
BIN
res/drawable/empty.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 163 B |
10
res/drawable/hidden_next.xml
Normal file
10
res/drawable/hidden_next.xml
Normal 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>
|
10
res/drawable/hidden_play_pause.xml
Normal file
10
res/drawable/hidden_play_pause.xml
Normal 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>
|
8
res/layout/default_widget.xml
Normal file
8
res/layout/default_widget.xml
Normal 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." />
|
27
res/layout/one_cell_widget.xml
Normal file
27
res/layout/one_cell_widget.xml
Normal 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>
|
6
res/xml/one_cell_widget.xml
Normal file
6
res/xml/one_cell_widget.xml
Normal 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"
|
||||
/>
|
@ -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();
|
||||
}
|
||||
|
41
src/org/kreed/vanilla/OneCellWidget.java
Normal file
41
src/org/kreed/vanilla/OneCellWidget.java
Normal 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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user