From 3e186a230780a39f474a1677487778070fdc29f5 Mon Sep 17 00:00:00 2001 From: Christopher Eby Date: Wed, 26 May 2010 22:25:00 -0500 Subject: [PATCH] Add a 1x4 widget --- AndroidManifest.xml | 10 ++ res/drawable/appwidget_bg.9.png | Bin 0 -> 2909 bytes res/drawable/appwidget_divider.9.png | Bin 0 -> 2806 bytes res/drawable/button_background.xml | 27 ++++ res/drawable/button_background_focused.xml | 25 +++ res/layout/four_long_widget.xml | 112 +++++++++++++ res/xml/four_long_widget.xml | 24 +++ src/org/kreed/vanilla/ContextApplication.java | 1 + src/org/kreed/vanilla/FourLongWidget.java | 147 ++++++++++++++++++ src/org/kreed/vanilla/OneCellWidget.java | 15 +- 10 files changed, 356 insertions(+), 5 deletions(-) create mode 100644 res/drawable/appwidget_bg.9.png create mode 100644 res/drawable/appwidget_divider.9.png create mode 100644 res/drawable/button_background.xml create mode 100644 res/drawable/button_background_focused.xml create mode 100644 res/layout/four_long_widget.xml create mode 100644 res/xml/four_long_widget.xml create mode 100644 src/org/kreed/vanilla/FourLongWidget.java diff --git a/AndroidManifest.xml b/AndroidManifest.xml index d0bb3b9e..0e8ca92f 100644 --- a/AndroidManifest.xml +++ b/AndroidManifest.xml @@ -63,6 +63,16 @@ + + + + + + diff --git a/res/drawable/appwidget_bg.9.png b/res/drawable/appwidget_bg.9.png new file mode 100644 index 0000000000000000000000000000000000000000..3b29eae77be9d42e4fafd518822c4807266ed59b GIT binary patch literal 2909 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 + + + + + + \ No newline at end of file diff --git a/res/drawable/button_background_focused.xml b/res/drawable/button_background_focused.xml new file mode 100644 index 00000000..bf37741b --- /dev/null +++ b/res/drawable/button_background_focused.xml @@ -0,0 +1,25 @@ + + + + + \ No newline at end of file diff --git a/res/layout/four_long_widget.xml b/res/layout/four_long_widget.xml new file mode 100644 index 00000000..ff825667 --- /dev/null +++ b/res/layout/four_long_widget.xml @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/res/xml/four_long_widget.xml b/res/xml/four_long_widget.xml new file mode 100644 index 00000000..4bfd650a --- /dev/null +++ b/res/xml/four_long_widget.xml @@ -0,0 +1,24 @@ + + + \ No newline at end of file diff --git a/src/org/kreed/vanilla/ContextApplication.java b/src/org/kreed/vanilla/ContextApplication.java index 415689a8..dbe12013 100644 --- a/src/org/kreed/vanilla/ContextApplication.java +++ b/src/org/kreed/vanilla/ContextApplication.java @@ -128,6 +128,7 @@ public class ContextApplication extends Application { public static void broadcast(Intent intent) { OneCellWidget.receive(intent); + FourLongWidget.receive(intent); ArrayList list = mActivities; if (list != null) { diff --git a/src/org/kreed/vanilla/FourLongWidget.java b/src/org/kreed/vanilla/FourLongWidget.java new file mode 100644 index 00000000..5987bd1d --- /dev/null +++ b/src/org/kreed/vanilla/FourLongWidget.java @@ -0,0 +1,147 @@ +/* + * Copyright (C) 2010 Christopher Eby + * + * This file is part of Vanilla Music Player. + * + * Vanilla Music Player is free software; you can redistribute it and/or modify + * it under the terms of the GNU Library General Public License as published by + * the Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Vanilla Music Player 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. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +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.content.res.Resources; +import android.graphics.Bitmap; +import android.view.View; +import android.widget.RemoteViews; + +/** + * Simple 1x4 widget to show currently playing song, album art, and play/pause, + * next, and previous buttons. Uses artwork from the default Android music + * widget. + */ +public class FourLongWidget extends AppWidgetProvider { + @Override + public void onUpdate(Context context, AppWidgetManager manager, int[] ids) + { + Song song; + int state; + + if (ContextApplication.hasService()) { + PlaybackService service = ContextApplication.getService(); + song = service.getSong(0); + state = service.getState(); + } else { + SongTimeline timeline = new SongTimeline(); + timeline.loadState(context); + song = timeline.getSong(0); + state = 0; + + // If we generated a new current song (because the PlaybackService has + // never been started), then we need to save the state. + timeline.saveState(context, 0); + } + + updateWidget(context, manager, ids, song, state); + } + + /** + * Receive a broadcast sent by the PlaybackService and update the widget + * accordingly. + * + * @param intent The intent that was broadcast. + */ + public static void receive(Intent intent) + { + String action = intent.getAction(); + if (PlaybackService.EVENT_CHANGED.equals(action) || PlaybackService.EVENT_REPLACE_SONG.equals(action)) { + Context context = ContextApplication.getContext(); + Song song = intent.getParcelableExtra("song"); + int state = intent.getIntExtra("state", -1); + + AppWidgetManager manager = AppWidgetManager.getInstance(context); + int[] ids = manager.getAppWidgetIds(new ComponentName(context, FourLongWidget.class)); + updateWidget(context, manager, ids, song, state); + } + } + + /** + * Populate the widgets with the given ids with the given info. + * + * @param context A Context to use. + * @param manager The AppWidgetManager that will be used to update the + * widget. + * @param ids An array containing the ids of all the widgets to update. + * @param song The current Song in PlaybackService. + * @param state The current PlaybackService state. + */ + public static void updateWidget(Context context, AppWidgetManager manager, int[] ids, Song song, int state) + { + if (ids == null || ids.length == 0) + return; + + Resources res = context.getResources(); + RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.four_long_widget); + + Bitmap cover = null; + + if (song == null) { + views.setViewVisibility(R.id.title, View.GONE); + views.setTextViewText(R.id.artist, res.getText(R.string.no_songs)); + } else { + views.setViewVisibility(R.id.title, View.VISIBLE); + views.setTextViewText(R.id.title, song.title); + views.setTextViewText(R.id.artist, song.artist); + cover = song.getCover(); + } + + if (cover == null) { + views.setViewVisibility(R.id.cover, View.GONE); + } else { + views.setViewVisibility(R.id.cover, View.VISIBLE); + views.setImageViewBitmap(R.id.cover, cover); + } + + if (state != -1) { + boolean playing = (state & PlaybackService.FLAG_PLAYING) != 0; + views.setImageViewResource(R.id.play, playing ? R.drawable.pause : R.drawable.play); + } + + Intent intent; + PendingIntent pendingIntent; + + ComponentName service = new ComponentName(context, PlaybackService.class); + + intent = new Intent(context, LaunchActivity.class); + pendingIntent = PendingIntent.getActivity(context, 0, intent, 0); + views.setOnClickPendingIntent(R.id.layout, pendingIntent); + + intent = new Intent(PlaybackService.ACTION_PREVIOUS_SONG).setComponent(service); + pendingIntent = PendingIntent.getService(context, 0, intent, 0); + views.setOnClickPendingIntent(R.id.previous, pendingIntent); + + intent = new Intent(PlaybackService.ACTION_TOGGLE_PLAYBACK).setComponent(service); + pendingIntent = PendingIntent.getService(context, 0, intent, 0); + views.setOnClickPendingIntent(R.id.play, pendingIntent); + + intent = new Intent(PlaybackService.ACTION_NEXT_SONG).setComponent(service); + pendingIntent = PendingIntent.getService(context, 0, intent, 0); + views.setOnClickPendingIntent(R.id.next, pendingIntent); + + manager.updateAppWidget(ids, views); + } +} diff --git a/src/org/kreed/vanilla/OneCellWidget.java b/src/org/kreed/vanilla/OneCellWidget.java index 4dce4353..e6546e13 100644 --- a/src/org/kreed/vanilla/OneCellWidget.java +++ b/src/org/kreed/vanilla/OneCellWidget.java @@ -90,7 +90,10 @@ public class OneCellWidget extends AppWidgetProvider { */ public static void updateWidget(Context context, AppWidgetManager manager, int[] ids, Song song, int state) { - for (int i = 0; i != ids.length; ++i) { + if (ids == null || ids.length == 0) + return; + + for (int i = ids.length; --i != -1; ) { SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context); boolean doubleTap = settings.getBoolean("double_tap_" + ids[i], false); @@ -101,12 +104,14 @@ public class OneCellWidget extends AppWidgetProvider { views.setImageViewResource(R.id.play_pause, playing ? R.drawable.hidden_pause : R.drawable.hidden_play); } - Intent playPause = new Intent(context, PlaybackService.class); - playPause.setAction(doubleTap ? PlaybackService.ACTION_TOGGLE_PLAYBACK_DELAYED : PlaybackService.ACTION_TOGGLE_PLAYBACK); + ComponentName service = new ComponentName(context, PlaybackService.class); + + Intent playPause = new Intent(doubleTap ? PlaybackService.ACTION_TOGGLE_PLAYBACK_DELAYED : PlaybackService.ACTION_TOGGLE_PLAYBACK); + playPause.setComponent(service); views.setOnClickPendingIntent(R.id.play_pause, PendingIntent.getService(context, 0, playPause, 0)); - Intent next = new Intent(context, PlaybackService.class); - next.setAction(doubleTap ? PlaybackService.ACTION_NEXT_SONG_DELAYED : PlaybackService.ACTION_NEXT_SONG); + Intent next = new Intent(doubleTap ? PlaybackService.ACTION_NEXT_SONG_DELAYED : PlaybackService.ACTION_NEXT_SONG); + next.setComponent(service); views.setOnClickPendingIntent(R.id.next, PendingIntent.getService(context, 0, next, 0)); if (song == null) {