Android music player widget - android

I'm working on a music player-widget (home screen widget)..
It only needs to play 1 song, (Preferably with the MediaPlayer class). But I'm not sure how to implement it. I'm kind of inexperienced with Android development, just so that's mentioned.
The class I have so far extends AppWidgetProvider, and I guess it is not a good idea to let this class handle the music-playing part, but rather a Service. And if so, how?
Furthermore, I have 3 buttons: play, pause and stop, and I can distinguish which one has been pressed in onReceive(...).
Thanks in advance!
Here is the class.
public class MusicManager extends AppWidgetProvider {
private final String ACTION_WIDGET_PLAY = "PlaySong";
private final String ACTION_WIDGET_PAUSE = "PauseSong";
private final String ACTION_WIDGET_STOP = "StopSong";
private final int INTENT_FLAGS = 0;
private final int REQUEST_CODE = 0;
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
RemoteViews controlButtons = new RemoteViews(context.getPackageName(),
R.layout.main);
Intent playIntent = new Intent(context, MusicService.class);
Intent pauseIntent = new Intent(context, MusicService.class);
Intent stopIntent = new Intent(context, MusicService.class);
PendingIntent playPendingIntent = PendingIntent.getService(
context, REQUEST_CODE, playIntent, INTENT_FLAGS);
PendingIntent pausePendingIntent = PendingIntent.getService(
context, REQUEST_CODE, pauseIntent, INTENT_FLAGS);
PendingIntent stopPendingIntent = PendingIntent.getService(
context, REQUEST_CODE, stopIntent, INTENT_FLAGS);
controlButtons.setOnClickPendingIntent(
R.id.btnPlay, playPendingIntent);
controlButtons.setOnClickPendingIntent(
R.id.btnPause, pausePendingIntent);
controlButtons.setOnClickPendingIntent(
R.id.btnStop, stopPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, controlButtons);
}
}

Added <service android:name=".MusicService" android:enabled="true" />
to the manifest!

In the onUpdate(...) method of your AppWidgetProvider, use something like this to start a service (this example associates the service to a button click event):
Intent intent = new Intent(context, MusicService.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
// Get the layout for the App Widget and attach an on-click listener to the button
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.appwidget_provider_layout);
views.setOnClickPendingIntent(R.id.button, pendingIntent);
For more info look here

Related

Android Problems with Widget and Multiple Pending Intents

I have a widget I am working on that controls a media player service. I have 4 buttons on my widget:
Previous
Play/Pause
Stop
Forward
The four buttons are assigned a pending intent which sends a Broadcast to the player service with the required action contained in it as an extra.
The problem I am having is that when I assign the pending intents to the buttons as below, they all seem to have the same broadcast value. For example, with the code below all 4 buttons seem to send Globals.PlaybackActions.SKIP_FORWARD. However, if I comment out the code that assigns the pending intents except for the "Play" button then pressing "Play" broadcasts the correct value.
Is there something I'm not aware of with using multiple pending intents as broadcasts on a widget or have I made a silly mistake in my code that I cannot spot? Or secret option C?
I'm hopelessly lost and confused and would be very grateful for any help. Thanks.
public class PlayerWidget extends AppWidgetProvider {
private boolean serviceIsPlaying = false;
private PendingIntent pendingIntentPrev = null;
private PendingIntent pendingIntentNext = null;
private PendingIntent pendingIntentStop = null;
private PendingIntent pendingIntentPlay = null;
public void onReceive(Context context, Intent intent) {
if (!intent.getAction().equals(Globals.BROADCAST_WIDGET_UPDATE)) {
return;
}
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
ComponentName thisAppWidget = new ComponentName(context.getPackageName(), PlayerWidget.class.getName());
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisAppWidget);
this.updateViews(context, appWidgetManager, appWidgetIds);
}
private void updateViews(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_player);
this.setClickIntents(context);
if (this.serviceIsPlaying) {
remoteViews.setImageViewResource(R.id.btnPlay, R.drawable.ic_action_pause);
}
else {
remoteViews.setImageViewResource(R.id.btnPlay, R.drawable.ic_action_play);
}
remoteViews.setTextViewText(R.id.txtArtistName, currentTrack.getArtistName());
remoteViews.setTextViewText(R.id.txtSongName, currentTrack.getSongTitle());
remoteViews.setTextViewText(R.id.txtAlbumName, currentTrack.getAlbumName());
remoteViews.setImageViewBitmap(R.id.imgAlbumArt, BitmapFactory.decodeFile(currentTrack.getAlbumArtPath()));
remoteViews.setOnClickPendingIntent(R.id.btnPrev, pendingIntentPrev);
remoteViews.setOnClickPendingIntent(R.id.btnNext, pendingIntentNext);
remoteViews.setOnClickPendingIntent(R.id.btnStop, pendingIntentStop);
remoteViews.setOnClickPendingIntent(R.id.btnPlay, pendingIntentPlay);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
private void setClickIntents(Context context) {
Intent intentPrev = new Intent(Globals.BROADCAST_PLAYBACK_ACTION);
Intent intentNext = new Intent(Globals.BROADCAST_PLAYBACK_ACTION);
Intent intentStop = new Intent(Globals.BROADCAST_PLAYBACK_ACTION);
Intent intentPlay = new Intent(Globals.BROADCAST_PLAYBACK_ACTION);
intentPrev.putExtra(Globals.EXTRA_PLAYBACK_ACTION, Globals.PlaybackActions.SKIP_BACKWARD);
intentNext.putExtra(Globals.EXTRA_PLAYBACK_ACTION, Globals.PlaybackActions.SKIP_FORWARD);
intentStop.putExtra(Globals.EXTRA_PLAYBACK_ACTION, Globals.PlaybackActions.STOP);
if (this.serviceIsPlaying) {
intentPlay.putExtra(Globals.EXTRA_PLAYBACK_ACTION, Globals.PlaybackActions.PAUSE);
}
else {
intentPlay.putExtra(Globals.EXTRA_PLAYBACK_ACTION, Globals.PlaybackActions.PLAY);
}
pendingIntentPrev = PendingIntent.getBroadcast(context, 0, intentPrev, 0);
pendingIntentNext = PendingIntent.getBroadcast(context, 0, intentNext, 0);
pendingIntentStop = PendingIntent.getBroadcast(context, 0, intentStop, 0);
pendingIntentPlay = PendingIntent.getBroadcast(context, 0, intentPlay, 0);
}
}
I think the problem is a second parameter in PendingIntent.getBroadcast() method. The second parameter is requestCode and it should be different for all 4 PendingIntent-s you are using.For example:
pendingIntentPrev = PendingIntent.getBroadcast(context, 0, intentPrev, 0);
pendingIntentNext = PendingIntent.getBroadcast(context, 1, intentNext, 0);
pendingIntentStop = PendingIntent.getBroadcast(context, 2, intentStop, 0);
pendingIntentPlay = PendingIntent.getBroadcast(context, 3, intentPlay, 0);
I have the same problem few days ago and this helped me. Hope it will help you too.

Widget stops receiving data after app crashes or user kills the app?

I have an app which plays music in a Service. There is also a Widget to control the music player. Everything works fine.
Now after the user closes the app or if it crashes or if I use System.exit(); or getActivity().finish(); to close it there are some problems. Everything seems to work fine, but somehow the widget doesn't receive broadcasts anymore even though another Activity still receives them.
The widget is still able to send broadcasts, it is only unable to receive them.
Could somebody please help me out?
Code from my widget:
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
ma = (GlobalSongList)context.getApplicationContext();
context.getApplicationContext().registerReceiver(broadcastCoverReceiver, new IntentFilter(Music_service.BROADCAST_COVER));
context.getApplicationContext().registerReceiver(broadcastPlayPause, new IntentFilter(BROADCAST_PLAYPAUSE));
context.getApplicationContext().registerReceiver(checkagain, new IntentFilter("xyz"));
context.getApplicationContext().registerReceiver(intentshuttingdown, new IntentFilter("shutdown"));
RemoteViews controlButtons = new RemoteViews(context.getPackageName(),R.layout.widget);
Intent playIntent = new Intent(BROADCAST_PLAYPAUSE);
PendingIntent playPendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, playIntent, PendingIntent.FLAG_UPDATE_CURRENT);
controlButtons.setOnClickPendingIntent(R.id.bPlay, playPendingIntent);
Intent previousIntent = new Intent(why);
PendingIntent previousPendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, previousIntent, PendingIntent.FLAG_UPDATE_CURRENT);
controlButtons.setOnClickPendingIntent(R.id.bprev, previousPendingIntent);
if(ma.NP_List.size()>0) {
controlButtons.setTextViewText(R.id.song, ma.NP_List.get(ma.position).song);
controlButtons.setTextViewText(R.id.artist, ma.NP_List.get(ma.position).getArtist());
Intent open_activityIntent = new Intent(BROADCAST_open_activity);
PendingIntent openactivityPendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, open_activityIntent, PendingIntent.FLAG_UPDATE_CURRENT);
controlButtons.setOnClickPendingIntent(R.id.parent, openactivityPendingIntent);
if(ma.boolMusicPlaying1 == true) {
controlButtons.setImageViewResource(R.id.bPlay, R.drawable.pause2);
} else {
controlButtons.setImageViewResource(R.id.bPlay, R.drawable.play);
}
} else if(ma.NP_List.size() == 0) {
controlButtons.setTextViewText(R.id.song, "");
controlButtons.setTextViewText(R.id.artist, "");
}
Intent nextIntent = new Intent(BROADCAST_SWAP);
nextIntent.putExtra("nextprev", 1);
PendingIntent nextPendingIntent = PendingIntent.getBroadcast(context, 0, nextIntent, PendingIntent.FLAG_UPDATE_CURRENT);
controlButtons.setOnClickPendingIntent(R.id.bNext, nextPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, controlButtons);
}
public BroadcastReceiver broadcastCoverReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent serviceIntent) {
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context.getApplicationContext());
ComponentName thisWidget = new ComponentName(context.getApplicationContext(), MyWidget.class);
int[] appWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
if (appWidgetIds != null && appWidgetIds.length > 0) {
onUpdate(context, appWidgetManager, appWidgetIds);
}
}
};
You should use android app widget and not use broadcasts since it is the most conventional way of doing it....which is also mentioned in the docs
You should bind and unbind background service on onResume and onPause respectively in Activity.
Visit Android Activity and Service relationship - after Pause, after Stop to get to know relationship between Service and Activity

Widget sending multiple types of intents

I am building a widget that has multiple buttons, each one sending off its own intent to a broadcast receiver. The broadcast receiver is suppose to display a Toast message based on which button was pushed. The code currently looks like this:
public class WidgetProvider extends AppWidgetProvider {
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds){
ComponentName thisWidget = new ComponentName(context, WidgetProvider.class);
int[] allWidgetIds = appWidgetManager.getAppWidgetIds(thisWidget);
for (int widgetId : allWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widget_layout);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
// Set the text of the buttons
remoteViews.setTextViewText(R.id.widgetPreset1Button, prefs.getString("widget1", "Not set"));
remoteViews.setTextViewText(R.id.widgetPreset2Button, prefs.getString("widget2", "Not set"));
remoteViews.setTextViewText(R.id.widgetPreset3Button, prefs.getString("widget3", "Not set"));
remoteViews.setTextViewText(R.id.widgetPreset4Button, prefs.getString("widget4", "Not set"));
// Register the buttons with an OnClick event
Intent intent1 = new Intent("myapp.SendWidgetPreset");
intent1.putExtra("Widget", 1);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.widgetPreset1Button, pendingIntent1);
Intent intent2 = new Intent("myapp.SendWidgetPreset");
intent2.putExtra("Widget", 2);
PendingIntent pendingIntent2 = PendingIntent.getBroadcast(context, 0, intent2, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.widgetPreset2Button, pendingIntent2);
Intent intent3 = new Intent("myapp.SendWidgetPreset");
intent3.putExtra("Widget", 3);
PendingIntent pendingIntent3 = PendingIntent.getBroadcast(context, 0, intent3, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.widgetPreset3Button, pendingIntent3);
Intent intent4 = new Intent("myapp.SendWidgetPreset");
intent4.putExtra("Widget", 4);
PendingIntent pendingIntent4 = PendingIntent.getBroadcast(context, 0, intent4, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.widgetPreset4Button, pendingIntent4);
new WidgetBroadcastReceiver();
appWidgetManager.updateAppWidget(widgetId, remoteViews);
}
}
}
and the BroadcastReceiver:
public class WidgetBroadcastReceiver extends BroadcastReceiver{
public WidgetBroadcastReceiver(){
}
#Override
public void onReceive(Context context, Intent arg1) {
int widget = arg1.getIntExtra("Widget", -1);
Toast.makeText(context, "Widget pressed: " + widget, Toast.LENGTH_SHORT).show();
}
}
My problem is it always displays Widget pressed: 4 regardless of which button is pressed. If I put the four lines intent4, intent4.putExtra(), pendingIntent4, and remoteViews.setOnClickPendingIntent() above all of the other intents, it will then always say Widget pressed: 3. In other words, whatever the last intent registration is, that is the widget displayed in the Toast message.
Anyone know why this isn't working how I want it?
you need to provide seperate request code for each pendingintent ex:
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 1/*request code*/, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
Your PendingIntents are being overwritten by the next one. THis is because they compare the Intent being encapsulated, and extras are not considered when comparing Intents. Do this for each intent:
Intent intent1 = new Intent("myapp.SendWidgetPreset");
intent1.putExtra("Widget", 1);
// This line makes your intents different
intent1.setData(Uri.parse(intent1.toUri(Intent.URI_INTENT_SCHEME)));
PendingIntent pendingIntent1 = PendingIntent.getBroadcast(context, 0, intent1, PendingIntent.FLAG_UPDATE_CURRENT);
remoteViews.setOnClickPendingIntent(R.id.widgetPreset1Button, pendingIntent1);
Seems like PendingIntent.getBroadcast() will ignore requestCode (unlike PendingIntent.getActivity).
Thus, to make unique PendingIntents you could supply Data for the Intent.
Example:
public static Intent makeUniqueIntent(String action, int requestCode) {
Intent intent = new Intent(action);
intent.setData(Uri.parse("http://"+ String.valueOf(requestCode)));
return intent;
}
Then make your Pending Intent as per usual, including the requestCode.
PendingIntent.getBroadcast(ctx, request_code,
makeUniqueIntent(NotificationReceiver.INTENT, request_code),
PendingIntent.FLAG_CANCEL_CURRENT);
With a Data element in an Intent, a matching Intent Filter within AndroidManifest.xml must also have a Data element:
<receiver android:name=".service.NotificationReceiver">
<intent-filter>
<action android:name="my.package.my_action_string"/>
<data android:scheme="http"/>
</intent-filter>
</receiver>
The above intent-filter works as only a scheme (i.e. "http") was identified. So any Uri with that scheme will match this filter's "data" element and the corresponding Receiver class will be called.
Notes:
NotificationReceiver is my class, extending BroadcastReceiver
NotificationReceiver.INTENT is a String constant I declared in NotificationReceiver. In this example it would equal "my.package.my_action_string";
request_code can be anything. Make it unique & save it if you want to reference this same Pending Intent in the future (say for canceling an alarm that uses it).
More info on Data test with Intent Filters:
http://developer.android.com/guide/components/intents-filters.html#DataTest

Start activity by clicking on widget

I newbie at programming Android and I try to do a widget which has be able get some data from ISP about my account. There are a lot of unknown things how to do it, but I have did a few things - I've got a widget with configure activity, where user should type login and password. Widget stores the data in SharedPerferences, and when it's time to update widget I use a Service to start an AsyncTask to getting it from ISP an account data. Now I want to do start an activity by click on widget. I've tried all advice which I found on this site and widget can't start activity. My widget based on another widget which placed here https://github.com/Arturus/MetrikaWidget. I dont understand what and where I should change to start activity by clicking on my widget. Thanks.
UPDATE:
My update function, where I suggest I should place PendingIntent
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds)
{
Log.d(TAG, "onUpdate");
for (int appWidgetId : appWidgetIds)
{
updateAppWidget(context, appWidgetManager, appWidgetId);
}
/* An updateAppWidget functions looks like as:
Intent intent = new Intent(context, UpdateWidgetService.class);
intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
context.startService(intent);
*/
Intent intent = new Intent(context, DetailedStatActivity.class);
PendingIntent pendingIntent = PendingIntent.getService(context, 0, intent, 0);
RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.layout);
views.setOnClickPendingIntent(R.id.layout, pendingIntent);
ComponentName componentName = new ComponentName(context.getPackageName(), WidgetProvider.class.getName());
appWidgetManager.updateAppWidget(componentName, views);
}
Use this snippet in onUpdate() method of your widget AppWidgetProvider class:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetlayout);
Intent configIntent = new Intent(context, Activity.class);
PendingIntent configPendingIntent = PendingIntent.getActivity(context, 0, configIntent, 0);
remoteViews.setOnClickPendingIntent(R.id.widget, configPendingIntent);
appWidgetManager.updateAppWidget(appWidgetIds, remoteViews);
}
Here widgetlayout is name of your widget layout and R.id.widget is it's parent layout id.
Edit:
Now,I see your code that you added to your question.You would to do:
PendingIntent.getActivity(context, 0, configIntent, 0);
(that start's activity) instead of
PendingIntent.getService(...);
that attempt to starts service.Good luck.
References:
doityourselfandroid.com
helloandroid.com
Intent inet = new Intent(your_action);
inet.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
PendingIntent pIntentNetworkInfo = PendingIntent.getActivity(context, 2,
inet, Intent.FLAG_ACTIVITY_NEW_TASK);
remoteViews.setOnClickPendingIntent(you_component_when_the_user_pressing_this_activity_should_start, pIntentNetworkInfo);
I don't know about "Creating widget from another widget". This is out of my knowledge but I suggest you to build your own widget.
Apart from that, calling activity from widget should be using PendingIntent
Here is simple example to do it
Intent iSetting = new Intent(this, MyConfig.class);
PendingIntent piSetting = PendingIntent.getActivity(this, 0, iSetting, 0);
views.setOnClickPendingIntent(R.id.IdComponent, piSetting);
Or you might need to see this link and this link
In order to initiate the launcher activity, I have integrated the addCategory() and setAction() methods into the onUpdate() method.
Intent configIntent = new Intent(context, MainActivity.class);
configIntent.addCategory("android.intent.category.LAUNCHER");
configIntent.setAction("android.intent.action.MAIN");
PendingIntent configPendingIntent = PendingIntent.getActivity(context, Values.REQ_CODE, configIntent, PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
views.setOnClickPendingIntent(R.id.btn_widget, configPendingIntent);

Updating extras in an intent in app Widget in Android

My code:
#Override
public void onUpdate(Context context, AppWidgetManager appWidgetManager,
int[] appWidgetIds) {
Intent active = new Intent(context, DialerWidget.class);
active.setAction(ACTION_WIDGET_RECEIVER);
active.putExtra("com.anirudha.android.appwidget.Number", currentNumber);
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context,
0, active, 0);
views.setOnClickPendingIntent(R.id.one,
actionPendingIntent);
views.setTextViewText(R.id.number, currentNumber);
appWidgetManager.updateAppWidget(appWidgetIds, views);
}
#Override
public void onReceive(Context context, Intent intent) {
if (intent.getAction().equals(ACTION_WIDGET_RECEIVER)) {
currentNumber = intent.getStringExtra("Number");
currentNumber += "1";
Intent active = new Intent(context, DialerWidget.class);
active.setAction(ACTION_WIDGET_RECEIVER);
active.putExtra("com.anirudha.android.appwidget.Number", CurrentNumber);
active.putExtra("com.anirudha.android.appwidget.Key", "1");
PendingIntent actionPendingIntent = PendingIntent.getBroadcast(context,
0, active, 0);
views.setOnClickPendingIntent(R.id.one,
actionPendingIntent);
views.setTextViewText(R.id.number, currentNumber);
ComponentName cn = new ComponentName(context.getPackageName(),
DialerWidget.class.getName());
AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
appWidgetManager.updateAppWidget(cn, views);
Toast.makeText(context, currentNumber, Toast.LENGTH_SHORT).show();
}
super.onReceive(context, intent);
}
So, basically i want to change the extras i should receive with the Intent. But whenever the button is pressed i receive the same value with the Intent. I'm new to android development. Any help would be appreciated. Thanks
If you want to update an existing pending intent, you have to use the FLAG_UPDATE_CURRENT flag when you create it.
PendingIntent actionPendingIntent =
PendingIntent.getBroadcast(context, 0, active, PendingIntent.FLAG_UPDATE_CURRENT);
What type is currentNumber? It's not shown but from the code I guess it's an int.
You are putting it into the extra as an int, but you are reading it out as a string. The string will always we empty because there is no string extra with that name so currentnumber will also be 0, then increased by 1, so always be 1.
Also you are writing it with "com.anirudha.android.appwidget.Number" but reading with "Number". This must be the same key for writing and reading.
Solution: use getIntExtra()

Categories

Resources