1792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project/* 2792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Copyright (C) 2009 The Android Open Source Project 3792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 4792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License"); 5792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * you may not use this file except in compliance with the License. 6792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * You may obtain a copy of the License at 7792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 8792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 10792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * See the License for the specific language governing permissions and 14792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * limitations under the License. 15792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 16792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 17792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectpackage com.android.music; 18792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 19792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.app.PendingIntent; 20490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Projectimport android.appwidget.AppWidgetManager; 21490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Projectimport android.appwidget.AppWidgetProvider; 22792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.ComponentName; 23792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.Context; 24792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.Intent; 25792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.content.res.Resources; 26792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.os.Environment; 2732949d74d37e04691180035263ae63c68e84971aJeffrey Sharkeyimport android.view.View; 28792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Projectimport android.widget.RemoteViews; 29792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 30792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project/** 31490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project * Simple widget to show currently playing album art along 32792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * with play/pause and next track buttons. 33792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 34490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Projectpublic class MediaAppWidgetProvider extends AppWidgetProvider { 35490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project static final String TAG = "MusicAppWidgetProvider"; 36792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 37490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project public static final String CMDAPPWIDGETUPDATE = "appwidgetupdate"; 38d9a3cdee419e8e6373f38d6d44612cc35f8fe61cMarco Nelissen 39490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project private static MediaAppWidgetProvider sInstance; 40792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 41490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project static synchronized MediaAppWidgetProvider getInstance() { 42792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project if (sInstance == null) { 43490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project sInstance = new MediaAppWidgetProvider(); 44792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 45792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project return sInstance; 46792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 47792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 48792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project @Override 49490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { 50490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project defaultAppWidget(context, appWidgetIds); 51792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 52792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project // Send broadcast intent to any running MediaPlaybackService so it can 53792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project // wrap around with an immediate update. 54792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project Intent updateIntent = new Intent(MediaPlaybackService.SERVICECMD); 55792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project updateIntent.putExtra(MediaPlaybackService.CMDNAME, 56490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project MediaAppWidgetProvider.CMDAPPWIDGETUPDATE); 57490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project updateIntent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds); 58792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY); 59792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project context.sendBroadcast(updateIntent); 60792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 61792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 62792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 63490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project * Initialize given widgets to default state, where we launch Music on default click 64792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * and hide actions if service not running. 65792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 66490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project private void defaultAppWidget(Context context, int[] appWidgetIds) { 67792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project final Resources res = context.getResources(); 68490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.album_appwidget); 69792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 7032949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey views.setViewVisibility(R.id.title, View.GONE); 711901e2f909925cb3f9af315b264907ded4acc664Daniel Sandler views.setTextViewText(R.id.artist, res.getText(R.string.widget_initial_text)); 72792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 73792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project linkButtons(context, views, false /* not playing */); 74490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project pushUpdate(context, appWidgetIds, views); 75792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 76792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 77490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project private void pushUpdate(Context context, int[] appWidgetIds, RemoteViews views) { 78490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project // Update specific list of appWidgetIds if given, otherwise default to all 79490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project final AppWidgetManager gm = AppWidgetManager.getInstance(context); 80490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project if (appWidgetIds != null) { 81490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project gm.updateAppWidget(appWidgetIds, views); 82792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } else { 83d9a3cdee419e8e6373f38d6d44612cc35f8fe61cMarco Nelissen gm.updateAppWidget(new ComponentName(context, this.getClass()), views); 84792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 85792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 86792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 87792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 88490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project * Check against {@link AppWidgetManager} if there are any instances of this widget. 89792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 90792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project private boolean hasInstances(Context context) { 91490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); 92d9a3cdee419e8e6373f38d6d44612cc35f8fe61cMarco Nelissen int[] appWidgetIds = appWidgetManager.getAppWidgetIds( 93d9a3cdee419e8e6373f38d6d44612cc35f8fe61cMarco Nelissen new ComponentName(context, this.getClass())); 94490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project return (appWidgetIds.length > 0); 95792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 96792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 97792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 98792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Handle a change notification coming over from {@link MediaPlaybackService} 99792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 100792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project void notifyChange(MediaPlaybackService service, String what) { 101792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project if (hasInstances(service)) { 1026b507de7beda7f4a8b969b437dfbf81f5ecd5e5eMarco Nelissen if (MediaPlaybackService.META_CHANGED.equals(what) || 103792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project MediaPlaybackService.PLAYSTATE_CHANGED.equals(what)) { 104792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project performUpdate(service, null); 105792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 106792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 107792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 108792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 109792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 110490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project * Update all active widget instances by pushing changes 111792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 112490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project void performUpdate(MediaPlaybackService service, int[] appWidgetIds) { 113792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project final Resources res = service.getResources(); 114490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project final RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.album_appwidget); 115792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 116349c020e7f44674c305f3035275a79cde987c605Jeffrey Sharkey CharSequence titleName = service.getTrackName(); 117349c020e7f44674c305f3035275a79cde987c605Jeffrey Sharkey CharSequence artistName = service.getArtistName(); 11832949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey CharSequence errorState = null; 119792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 120792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project // Format title string with track number, or show SD card message 121792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project String status = Environment.getExternalStorageState(); 122349c020e7f44674c305f3035275a79cde987c605Jeffrey Sharkey if (status.equals(Environment.MEDIA_SHARED) || 123792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project status.equals(Environment.MEDIA_UNMOUNTED)) { 124ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer if (android.os.Environment.isExternalStorageRemovable()) { 125ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer errorState = res.getText(R.string.sdcard_busy_title); 126ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer } else { 127ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer errorState = res.getText(R.string.sdcard_busy_title_nosdcard); 128ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer } 129792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } else if (status.equals(Environment.MEDIA_REMOVED)) { 130ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer if (android.os.Environment.isExternalStorageRemovable()) { 131ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer errorState = res.getText(R.string.sdcard_missing_title); 132ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer } else { 133ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer errorState = res.getText(R.string.sdcard_missing_title_nosdcard); 134ecf5b6577ed52056fa8ae6035805bfed9f50c2f3Eric Fischer } 135349c020e7f44674c305f3035275a79cde987c605Jeffrey Sharkey } else if (titleName == null) { 13632949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey errorState = res.getText(R.string.emptyplaylist); 137792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 138792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 13932949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey if (errorState != null) { 14032949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey // Show error state to user 14132949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey views.setViewVisibility(R.id.title, View.GONE); 14232949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey views.setTextViewText(R.id.artist, errorState); 14332949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey 14432949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey } else { 14532949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey // No error, so show normal titles 14632949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey views.setViewVisibility(R.id.title, View.VISIBLE); 14732949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey views.setTextViewText(R.id.title, titleName); 14832949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey views.setTextViewText(R.id.artist, artistName); 14932949d74d37e04691180035263ae63c68e84971aJeffrey Sharkey } 150792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 151792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project // Set correct drawable for pause state 152792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project final boolean playing = service.isPlaying(); 153349c020e7f44674c305f3035275a79cde987c605Jeffrey Sharkey if (playing) { 154a0fcf0f56a388b7b36dd4c12bbf655569bdec8f6Mike Cleron views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_pause); 155349c020e7f44674c305f3035275a79cde987c605Jeffrey Sharkey } else { 156a0fcf0f56a388b7b36dd4c12bbf655569bdec8f6Mike Cleron views.setImageViewResource(R.id.control_play, R.drawable.ic_appwidget_music_play); 157349c020e7f44674c305f3035275a79cde987c605Jeffrey Sharkey } 158792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 159792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project // Link actions buttons to intents 160792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project linkButtons(service, views, playing); 161792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 162490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project pushUpdate(service, appWidgetIds, views); 163792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 164792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 165792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project /** 166792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * Link up various button actions using {@link PendingIntents}. 167792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * 168792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * @param playerActive True if player is active in background, which means 16938bcd927f46d6477e6bae88795c38bfe92d7a9f9Marco Nelissen * widget click will launch {@link MediaPlaybackActivity}, 170792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project * otherwise we launch {@link MusicBrowserActivity}. 171792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project */ 172792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project private void linkButtons(Context context, RemoteViews views, boolean playerActive) { 173792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project // Connect up various buttons and touch events 174792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project Intent intent; 175792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project PendingIntent pendingIntent; 176792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 177792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project final ComponentName serviceName = new ComponentName(context, MediaPlaybackService.class); 178792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 179792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project if (playerActive) { 18038bcd927f46d6477e6bae88795c38bfe92d7a9f9Marco Nelissen intent = new Intent(context, MediaPlaybackActivity.class); 181792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project pendingIntent = PendingIntent.getActivity(context, 182792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 0 /* no requestCode */, intent, 0 /* no flags */); 183490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent); 184792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } else { 185792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project intent = new Intent(context, MusicBrowserActivity.class); 186792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project pendingIntent = PendingIntent.getActivity(context, 187792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 0 /* no requestCode */, intent, 0 /* no flags */); 188490384bf1b81cc703fd42de8674e593b216e3435The Android Open Source Project views.setOnClickPendingIntent(R.id.album_appwidget, pendingIntent); 189792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 190792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 191792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project intent = new Intent(MediaPlaybackService.TOGGLEPAUSE_ACTION); 192792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project intent.setComponent(serviceName); 193792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project pendingIntent = PendingIntent.getService(context, 194792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 0 /* no requestCode */, intent, 0 /* no flags */); 195792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project views.setOnClickPendingIntent(R.id.control_play, pendingIntent); 196792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 197792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project intent = new Intent(MediaPlaybackService.NEXT_ACTION); 198792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project intent.setComponent(serviceName); 199792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project pendingIntent = PendingIntent.getService(context, 200792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project 0 /* no requestCode */, intent, 0 /* no flags */); 201792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project views.setOnClickPendingIntent(R.id.control_next, pendingIntent); 202792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project } 203792a2206a4f05f6bd13fce902d3663892d2947afThe Android Open Source Project} 204