2 * Copyright (C) 2009 The Android Open Source Project
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
17 package com.android.music;
19 import android.app.PendingIntent;
20 import android.appwidget.AppWidgetManager;
21 import android.appwidget.AppWidgetProvider;
22 import android.content.ComponentName;
23 import android.content.Context;
24 import android.content.Intent;
25 import android.content.res.Resources;
26 import android.os.Environment;
27 import android.view.View;
28 import android.widget.RemoteViews;
31 * Simple widget to show currently playing album art along
32 * with play/pause and next track buttons.
34 public class MediaAppWidgetProvider extends AppWidgetProvider {
35 static final String TAG = "MusicAppWidgetProvider";
37 public static final String CMDAPPWIDGETUPDATE = "appwidgetupdate";
39 private static MediaAppWidgetProvider sInstance;
41 static synchronized MediaAppWidgetProvider getInstance() {
42 if (sInstance == null) {
43 sInstance = new MediaAppWidgetProvider();
49 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
50 defaultAppWidget(context, appWidgetIds);
52 // Send broadcast intent to any running MediaPlaybackService so it can
53 // wrap around with an immediate update.
54 Intent updateIntent = new Intent(MediaPlaybackService.SERVICECMD);
55 updateIntent.putExtra(MediaPlaybackService.CMDNAME,
56 MediaAppWidgetProvider.CMDAPPWIDGETUPDATE);
57 updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
58 updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
59 context.sendBroadcast(updateIntent);
63 * Initialize given widgets to default state, where we launch Music on default click
64 * and hide actions if service not running.
66 private void defaultAppWidget(Context context, int[] appWidgetIds) {
67 final Resources res = context.getResources();
68 final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.album_appwidget);
70 views.setViewVisibility(R.id.title, View.GONE);
71 views.setTextViewText(R.id.artist, res.getText(R.string.emptyplaylist));
73 linkButtons(context, views, false /* not playing */);
74 pushUpdate(context, appWidgetIds, views);
77 private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) {
78 // Update specific list of appWidgetIds if given, otherwise default to all
79 final AppWidgetManager gm = AppWidgetManager.getInstance(context);
80 if (appWidgetIds != null) {
81 gm.updateAppWidget(appWidgetIds, views);
83 gm.updateAppWidget(new ComponentName(context, this.getClass()), views);
88 * Check against {@link AppWidgetManager} if there are any instances of this widget.
90 private boolean hasInstances(Context context) {
91 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
92 int[] appWidgetIds = appWidgetManager.getAppWidgetIds(
93 new ComponentName(context, this.getClass()));
94 return (appWidgetIds.length > 0);
98 * Handle a change notification coming over from {@link MediaPlaybackService}
100 void notifyChange(MediaPlaybackService service, String what) {
101 if (hasInstances(service)) {
102 if (MediaPlaybackService.PLAYBACK_COMPLETE.equals(what) ||
103 MediaPlaybackService.META_CHANGED.equals(what) ||
104 MediaPlaybackService.PLAYSTATE_CHANGED.equals(what)) {
105 performUpdate(service, null);
111 * Update all active widget instances by pushing changes
113 void performUpdate(MediaPlaybackService service, int[] appWidgetIds) {
114 final Resources res = service.getResources();
115 final RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.album_appwidget);
117 CharSequence titleName = service.getTrackName();
118 CharSequence artistName = service.getArtistName();
119 CharSequence errorState = null;
121 // Format title string with track number, or show SD card message
122 String status = Environment.getExternalStorageState();
123 if (status.equals(Environment.MEDIA_SHARED) ||
124 status.equals(Environment.MEDIA_UNMOUNTED)) {
125 errorState = res.getText(R.string.sdcard_busy_title);
126 } else if (status.equals(Environment.MEDIA_REMOVED)) {
127 errorState = res.getText(R.string.sdcard_missing_title);
128 } else if (titleName == null) {
129 errorState = res.getText(R.string.emptyplaylist);
132 if (errorState != null) {
133 // Show error state to user
134 views.setViewVisibility(R.id.title, View.GONE);
135 views.setTextViewText(R.id.artist, errorState);
138 // No error, so show normal titles
139 views.setViewVisibility(R.id.title, View.VISIBLE);
140 views.setTextViewText(R.id.title, titleName);
141 views.setTextViewText(R.id.artist, artistName);
144 // Set correct drawable for pause state
145 final boolean playing = service.isPlaying();
147 views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_pause);
149 views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_play);
152 // Link actions buttons to intents
153 linkButtons(service, views, playing);
155 pushUpdate(service, appWidgetIds, views);
159 * Link up various button actions using {@link PendingIntents}.
161 * @param playerActive True if player is active in background, which means
162 * widget click will launch {@link MediaPlaybackActivity},
163 * otherwise we launch {@link MusicBrowserActivity}.
165 private void linkButtons(Context context, RemoteViews views, boolean playerActive) {
166 // Connect up various buttons and touch events
168 PendingIntent pendingIntent;
170 final ComponentName serviceName = new ComponentName(context, MediaPlaybackService.class);
173 intent = new Intent(context, MediaPlaybackActivity.class);
174 pendingIntent = PendingIntent.getActivity(context,
175 0 /* no requestCode */, intent, 0 /* no flags */);
176 views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
178 intent = new Intent(context, MusicBrowserActivity.class);
179 pendingIntent = PendingIntent.getActivity(context,
180 0 /* no requestCode */, intent, 0 /* no flags */);
181 views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent);
184 intent = new Intent(MediaPlaybackService.TOGGLEPAUSE_ACTION);
185 intent.setComponent(serviceName);
186 pendingIntent = PendingIntent.getService(context,
187 0 /* no requestCode */, intent, 0 /* no flags */);
188 views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
190 intent = new Intent(MediaPlaybackService.NEXT_ACTION);
191 intent.setComponent(serviceName);
192 pendingIntent = PendingIntent.getService(context,
193 0 /* no requestCode */, intent, 0 /* no flags */);
194 views.setOnClickPendingIntent(R.id.control_next, pendingIntent);