MediaAppWidgetProvider.java revision 490384bf1b81cc703fd42de8674e593b216e3435
1/* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 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 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 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. 15 */ 16 17package com.android.music; 18 19import android.app.PendingIntent; 20import android.appwidget.AppWidgetManager; 21import android.appwidget.AppWidgetProvider; 22import android.content.BroadcastReceiver; 23import android.content.ComponentName; 24import android.content.Context; 25import android.content.Intent; 26import android.content.res.Resources; 27import android.graphics.Bitmap; 28import android.graphics.BitmapFactory; 29import android.graphics.Canvas; 30import android.graphics.Paint; 31import android.graphics.PorterDuff; 32import android.graphics.PorterDuffXfermode; 33import android.graphics.Rect; 34import android.media.MediaFile; 35import android.os.Environment; 36import android.os.SystemClock; 37import android.util.Config; 38import android.util.Log; 39import android.view.View; 40import android.widget.RemoteViews; 41 42/** 43 * Simple widget to show currently playing album art along 44 * with play/pause and next track buttons. 45 */ 46public class MediaAppWidgetProvider extends AppWidgetProvider { 47 static final String TAG = "MusicAppWidgetProvider"; 48 49 public static final String CMDAPPWIDGETUPDATE = "appwidgetupdate"; 50 51 static final ComponentName THIS_APPWIDGET = 52 new ComponentName("com.android.music", 53 "com.android.music.MediaAppWidgetProvider"); 54 55 private static MediaAppWidgetProvider sInstance; 56 57 static synchronized MediaAppWidgetProvider getInstance() { 58 if (sInstance == null) { 59 sInstance = new MediaAppWidgetProvider(); 60 } 61 return sInstance; 62 } 63 64 @Override 65 public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 66 defaultAppWidget(context, appWidgetIds); 67 68 // Send broadcast intent to any running MediaPlaybackService so it can 69 // wrap around with an immediate update. 70 Intent updateIntent = new Intent(MediaPlaybackService.SERVICECMD); 71 updateIntent.putExtra(MediaPlaybackService.CMDNAME, 72 MediaAppWidgetProvider.CMDAPPWIDGETUPDATE); 73 updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 74 updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 75 context.sendBroadcast(updateIntent); 76 } 77 78 /** 79 * Initialize given widgets to default state, where we launch Music on default click 80 * and hide actions if service not running. 81 */ 82 private void defaultAppWidget(Context context, int[] appWidgetIds) { 83 final Resources res = context.getResources(); 84 final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.album_appwidget); 85 86 views.setTextViewText(R.id.title, res.getText(R.string.emptyplaylist)); 87 88 linkButtons(context, views, false /* not playing */); 89 pushUpdate(context, appWidgetIds, views); 90 } 91 92 private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) { 93 // Update specific list of appWidgetIds if given, otherwise default to all 94 final AppWidgetManager gm = AppWidgetManager.getInstance(context); 95 if (appWidgetIds != null) { 96 gm.updateAppWidget(appWidgetIds, views); 97 } else { 98 gm.updateAppWidget(THIS_APPWIDGET, views); 99 } 100 } 101 102 /** 103 * Check against {@link AppWidgetManager} if there are any instances of this widget. 104 */ 105 private boolean hasInstances(Context context) { 106 AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 107 int[] appWidgetIds = appWidgetManager.getAppWidgetIds(THIS_APPWIDGET); 108 return (appWidgetIds.length > 0); 109 } 110 111 /** 112 * Handle a change notification coming over from {@link MediaPlaybackService} 113 */ 114 void notifyChange(MediaPlaybackService service, String what) { 115 if (hasInstances(service)) { 116 if (MediaPlaybackService.PLAYBACK_COMPLETE.equals(what) || 117 MediaPlaybackService.META_CHANGED.equals(what) || 118 MediaPlaybackService.PLAYSTATE_CHANGED.equals(what)) { 119 performUpdate(service, null); 120 } 121 } 122 } 123 124 /** 125 * Update all active widget instances by pushing changes 126 */ 127 void performUpdate(MediaPlaybackService service, int[] appWidgetIds) { 128 final Resources res = service.getResources(); 129 final RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.album_appwidget); 130 131 final int track = service.getQueuePosition() + 1; 132 final String titleName = service.getTrackName(); 133 final String artistName = service.getArtistName(); 134 135 // Format title string with track number, or show SD card message 136 CharSequence titleString = ""; 137 String status = Environment.getExternalStorageState(); 138 if (titleName != null) { 139 titleString = res.getString(R.string.gadget_track_num_title, track, titleName); 140 } else if (status.equals(Environment.MEDIA_SHARED) || 141 status.equals(Environment.MEDIA_UNMOUNTED)) { 142 titleString = res.getText(R.string.sdcard_busy_title); 143 } else if (status.equals(Environment.MEDIA_REMOVED)) { 144 titleString = res.getText(R.string.sdcard_missing_title); 145 } else { 146 titleString = res.getText(R.string.emptyplaylist); 147 } 148 149 views.setTextViewText(R.id.title, titleString); 150 views.setTextViewText(R.id.artist, artistName); 151 152 // Set correct drawable for pause state 153 final boolean playing = service.isPlaying(); 154 views.setImageViewResource(R.id.control_play, playing ? 155 R.drawable.appwidget_pause : R.drawable.appwidget_play); 156 157 // Link actions buttons to intents 158 linkButtons(service, views, playing); 159 160 pushUpdate(service, appWidgetIds, views); 161 } 162 163 /** 164 * Link up various button actions using {@link PendingIntents}. 165 * 166 * @param playerActive True if player is active in background, which means 167 * widget click will launch {@link MediaPlaybackActivity}, 168 * otherwise we launch {@link MusicBrowserActivity}. 169 */ 170 private void linkButtons(Context context, RemoteViews views, boolean playerActive) { 171 // Connect up various buttons and touch events 172 Intent intent; 173 PendingIntent pendingIntent; 174 175 final ComponentName serviceName = new ComponentName(context, MediaPlaybackService.class); 176 177 if (playerActive) { 178 intent = new Intent(context, MediaPlaybackActivity.class); 179 pendingIntent = PendingIntent.getActivity(context, 180 0 /* no requestCode */, intent, 0 /* no flags */); 181 views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent); 182 } else { 183 intent = new Intent(context, MusicBrowserActivity.class); 184 pendingIntent = PendingIntent.getActivity(context, 185 0 /* no requestCode */, intent, 0 /* no flags */); 186 views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent); 187 } 188 189 intent = new Intent(MediaPlaybackService.TOGGLEPAUSE_ACTION); 190 intent.setComponent(serviceName); 191 pendingIntent = PendingIntent.getService(context, 192 0 /* no requestCode */, intent, 0 /* no flags */); 193 views.setOnClickPendingIntent(R.id.control_play, pendingIntent); 194 195 intent = new Intent(MediaPlaybackService.NEXT_ACTION); 196 intent.setComponent(serviceName); 197 pendingIntent = PendingIntent.getService(context, 198 0 /* no requestCode */, intent, 0 /* no flags */); 199 views.setOnClickPendingIntent(R.id.control_next, pendingIntent); 200 } 201} 202