blob: 8a7e241d9defa482fb08b2ac558387279c5d8480 [file] [log] [blame]
/*
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.music;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.gadget.GadgetManager;
import android.gadget.GadgetProvider;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.media.MediaFile;
import android.os.SystemClock;
import android.util.Config;
import android.util.Log;
import android.view.View;
import android.widget.RemoteViews;
/**
* Simple gadget to show currently playing album art along
* with play/pause and next track buttons.
*/
public class MediaGadgetProvider extends GadgetProvider {
static final String TAG = "MusicGadgetProvider";
public static final String CMDGADGETUPDATE = "gadgetupdate";
@Override
public void onUpdate(Context context, GadgetManager gadgetManager, int[] gadgetIds) {
defaultGadget(context, gadgetIds);
// Send broadcast intent to any running MediaPlaybackService so it can
// wrap around with an immediate update.
Intent updateIntent = new Intent(MediaPlaybackService.SERVICECMD);
updateIntent.putExtra(MediaPlaybackService.CMDNAME,
MediaGadgetProvider.CMDGADGETUPDATE);
updateIntent.putExtra(GadgetManager.EXTRA_GADGET_IDS, gadgetIds);
updateIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
context.sendBroadcast(updateIntent);
}
/**
* Initialize given gadgets to default state, where we launch Music on default click
* and hide actions if service not running.
*/
static void defaultGadget(Context context, int[] gadgetIds) {
final RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.gadget);
linkButtons(context, views, false /* not from service */);
pushUpdate(context, gadgetIds, views);
}
private static void pushUpdate(Context context, int[] gadgetIds, RemoteViews views) {
// Update specific list of gadgetIds if given, otherwise default to all
final GadgetManager gm = GadgetManager.getInstance(context);
if (gadgetIds != null) {
gm.updateGadget(gadgetIds, views);
} else {
final ComponentName thisGadget = new ComponentName(context,
MediaGadgetProvider.class);
gm.updateGadget(thisGadget, views);
}
}
/**
* Update all active gadget instances by pushing changes
* @param metaChanged
*/
static void updateAllGadgets(MediaPlaybackService service, int[] gadgetIds) {
final Resources res = service.getResources();
final RemoteViews views = new RemoteViews(service.getPackageName(), R.layout.gadget);
final int track = service.getQueuePosition() + 1;
final String titleName = service.getTrackName();
final String artistName = service.getArtistName();
final String albumName = service.getAlbumName();
int albumId = service.getAlbumId();
if (MediaFile.UNKNOWN_STRING.equals(albumName)) {
albumId = -1;
}
// Try loading album artwork and resize if found
Bitmap artwork = MusicUtils.getArtwork(service, albumId, false);
if (artwork != null) {
artwork = scaleArtwork(artwork, service);
views.setImageViewBitmap(R.id.artwork, artwork);
views.setViewVisibility(R.id.artwork, View.VISIBLE);
views.setViewVisibility(R.id.no_artwork, View.GONE);
} else {
views.setViewVisibility(R.id.artwork, View.GONE);
views.setViewVisibility(R.id.no_artwork, View.VISIBLE);
}
// Format title string with track number
final String titleString = res.getString(R.string.gadget_track_num_title, track, titleName);
views.setTextViewText(R.id.title, titleString);
views.setTextViewText(R.id.artist, artistName);
// Set chronometer to correct value
final boolean playing = service.isPlaying();
final long start = SystemClock.elapsedRealtime() - service.position();
final long end = start + service.duration();
views.setChronometer(android.R.id.text1, start, null, playing);
views.setLong(R.id.progress_group, "setDurationBase", end);
// Set correct drawable for pause state
views.setImageViewResource(R.id.control_play, playing ?
R.drawable.gadget_pause : R.drawable.gadget_play);
// Link actions buttons to intents
linkButtons(service, views, true /* not from service */);
pushUpdate(service, gadgetIds, views);
}
/**
* Link up various button actions using {@link PendingIntents}.
*
* @param fromService If false, {@link MediaPlaybackService} isn't running,
* and we should link the play button to start that service.
* Otherwise, we assume the service is awake and send broadcast
* Intents instead.
*/
private static void linkButtons(Context context, RemoteViews views, boolean fromService) {
// Connect up various buttons and touch events
Intent intent;
PendingIntent pendingIntent;
intent = new Intent(context, MediaPlaybackActivity.class);
pendingIntent = PendingIntent.getActivity(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.album_gadget, pendingIntent);
intent = new Intent(MediaPlaybackService.PREVIOUS_ACTION);
pendingIntent = PendingIntent.getBroadcast(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.control_prev, pendingIntent);
// Use broadcast to trigger play/pause, otherwise
if (fromService) {
intent = new Intent(MediaPlaybackService.TOGGLEPAUSE_ACTION);
pendingIntent = PendingIntent.getBroadcast(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
} else {
intent = new Intent(MediaPlaybackService.TOGGLEPAUSE_ACTION);
intent.setComponent(new ComponentName(context, MediaPlaybackService.class));
pendingIntent = PendingIntent.getService(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.control_play, pendingIntent);
}
intent = new Intent(MediaPlaybackService.NEXT_ACTION);
pendingIntent = PendingIntent.getBroadcast(context,
0 /* no requestCode */, intent, 0 /* no flags */);
views.setOnClickPendingIntent(R.id.control_next, pendingIntent);
}
/**
* Scale and chop given album artwork to prepare as gadget background.
*/
private static Bitmap scaleArtwork(Bitmap bitmap, Context context) {
final int cutoutSize = (int) context.getResources().getDimension(R.dimen.gadget_cutout);
final int srcWidth = bitmap.getWidth();
final int srcHeight = bitmap.getHeight();
final int srcDiameter = Math.min(srcWidth, srcHeight);
// Figure out best circle size
final Rect src = new Rect((srcWidth - srcDiameter) / 2,
(srcHeight - srcDiameter) / 2, srcDiameter, srcDiameter);
final Rect dest = new Rect(0, 0, cutoutSize, cutoutSize);
final Bitmap thumb = Bitmap.createBitmap(cutoutSize, cutoutSize,
Bitmap.Config.ARGB_8888);
final Canvas canvas = new Canvas(thumb);
final Paint paint = new Paint();
paint.setAntiAlias(true);
// Draw a mask circle using default paint
final int radius = cutoutSize / 2;
canvas.drawCircle(radius, radius, radius, paint);
paint.setDither(false);
paint.setFilterBitmap(true);
paint.setAlpha(96);
// Draw the actual album art, using the mask circle from earlier. Using
// this approach allows us to alpha-blend the circle edges, which isn't
// possible with Canvas.clipPath()
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, src, dest, paint);
return thumb;
}
}