/* * Copyright (C) 2014 Fastboot Mobile, LLC. * * This program is free software; you can redistribute it and/or modify it under the terms of the * GNU General Public License as published by the Free Software Foundation; either version 3 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See * the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with this program; * if not, see <http://www.gnu.org/licenses>. */ package com.fastbootmobile.encore.service; import android.annotation.TargetApi; import android.app.Notification; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.drawable.BitmapDrawable; import android.os.Build; import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationManagerCompat; import android.view.View; import android.widget.RemoteViews; import com.fastbootmobile.encore.app.MainActivity; import com.fastbootmobile.encore.app.R; import com.fastbootmobile.encore.art.AlbumArtTask; import com.fastbootmobile.encore.utils.Utils; import com.fastbootmobile.encore.art.AlbumArtHelper; import com.fastbootmobile.encore.art.RecyclingBitmapDrawable; import com.fastbootmobile.encore.model.Album; import com.fastbootmobile.encore.model.Artist; import com.fastbootmobile.encore.model.BoundEntity; import com.fastbootmobile.encore.model.Song; import com.fastbootmobile.encore.providers.ProviderAggregator; /** * Class handling the playback service notification */ public class ServiceNotification implements AlbumArtHelper.AlbumArtListener { private static final String TAG = "ServiceNotification"; private Context mContext; private Resources mResources; private final Bitmap mDefaultArt; private BitmapDrawable mCurrentArt; private NotificationChangedListener mListener; private Song mCurrentSong; private RemoteViews mBaseTemplate; private RemoteViews mExpandedTemplate; private boolean mHasNext; private boolean mShowPlayAction; private AlbumArtTask mArtTask; private NotificationCompat.Builder mBuilder; private Notification mNotification; private static final int NOTIFICATION_ID = 1; public ServiceNotification(Context ctx) { mContext = ctx; mResources = ctx.getResources(); // Get the default album art BitmapDrawable bdDefault = (BitmapDrawable) mResources.getDrawable(R.drawable.album_placeholder); mDefaultArt = bdDefault.getBitmap().copy(bdDefault.getBitmap().getConfig(), false); mCurrentArt = null; // Build the static notification actions setPlayPauseActionImpl(false, false); } public String getString(int resId) { return mResources.getString(resId); } private void buildBaseNotification() { Intent notificationIntent = new Intent(mContext, MainActivity.class); PendingIntent pendingIntent = PendingIntent.getActivity(mContext, 0, notificationIntent, 0); mBuilder = new NotificationCompat.Builder(mContext); // Build the core notification mBuilder.setSmallIcon(R.drawable.ic_notification) .setContentIntent(pendingIntent) .setAutoCancel(false) .setOngoing(true) .setContent(mBaseTemplate) .setPriority(NotificationCompat.PRIORITY_HIGH); } private void buildRemoteViews() { mBaseTemplate = new RemoteViews(mContext.getPackageName(), R.layout.notification_base); mExpandedTemplate = new RemoteViews(mContext.getPackageName(), R.layout.notification_expanded); // Setup pending intents PendingIntent piNext = PendingIntent.getService(mContext, 0, NotifActionService.getIntentNext(mContext), 0); PendingIntent piPrevious = PendingIntent.getService(mContext, 0, NotifActionService.getIntentPrevious(mContext), 0); PendingIntent piPause = PendingIntent.getService(mContext, 0, NotifActionService.getIntentTogglePause(mContext), 0); PendingIntent piStop = PendingIntent.getService(mContext, 0, NotifActionService.getIntentStop(mContext), 0); mBaseTemplate.setOnClickPendingIntent(R.id.btnNotifNext, piNext); mExpandedTemplate.setOnClickPendingIntent(R.id.btnNotifNext, piNext); mBaseTemplate.setOnClickPendingIntent(R.id.btnNotifPlayPause, piPause); mExpandedTemplate.setOnClickPendingIntent(R.id.btnNotifPlayPause, piPause); mBaseTemplate.setOnClickPendingIntent(R.id.btnNotifPrevious, piPrevious); mExpandedTemplate.setOnClickPendingIntent(R.id.btnNotifPrevious, piPrevious); mBaseTemplate.setOnClickPendingIntent(R.id.btnNotifClose, piStop); mExpandedTemplate.setOnClickPendingIntent(R.id.btnNotifClose, piStop); } @TargetApi(Build.VERSION_CODES.LOLLIPOP) private void buildNotification() { if (mCurrentSong == null) { return; } buildRemoteViews(); buildBaseNotification(); // And get the notification object mNotification = mBuilder.build(); // Add the expanded controls mNotification.bigContentView = mExpandedTemplate; if (Utils.hasLollipop()) { mNotification.category = Notification.CATEGORY_SERVICE; mNotification.visibility = Notification.VISIBILITY_PUBLIC; } // Update fields final ProviderAggregator aggregator = ProviderAggregator.getDefault(); if (mCurrentSong != null) { if (mCurrentSong.isLoaded()) { final Artist artist = mCurrentSong.getArtist() != null ? aggregator.retrieveArtist(mCurrentSong.getArtist(), mCurrentSong.getProvider()) : null; final Album album = mCurrentSong.getAlbum() != null ? aggregator.retrieveAlbum(mCurrentSong.getAlbum(), mCurrentSong.getProvider()) : null; // Song title mBaseTemplate.setTextViewText(R.id.tvNotifLineOne, mCurrentSong.getTitle()); mExpandedTemplate.setTextViewText(R.id.tvNotifLineOne, mCurrentSong.getTitle()); if (artist != null && artist.getName() != null && !artist.getName().isEmpty()) { // Artist name mBaseTemplate.setTextViewText(R.id.tvNotifLineTwo, artist.getName()); mExpandedTemplate.setTextViewText(R.id.tvNotifLineTwo, artist.getName()); } else { mBaseTemplate.setTextViewText(R.id.tvNotifLineTwo, null); mExpandedTemplate.setTextViewText(R.id.tvNotifLineTwo, null); } if (album != null && album.getName() != null && !album.getName().isEmpty()) { // Album name mExpandedTemplate.setTextViewText(R.id.tvNotifLineThree, album.getName()); } else { mExpandedTemplate.setTextViewText(R.id.tvNotifLineThree, null); } } else { mBuilder.setContentTitle(getString(R.string.loading)); } } if (mShowPlayAction) { mBaseTemplate.setImageViewResource(R.id.btnNotifPlayPause, R.drawable.ic_notif_play); mExpandedTemplate.setImageViewResource(R.id.btnNotifPlayPause, R.drawable.ic_notif_play); } else { mBaseTemplate.setImageViewResource(R.id.btnNotifPlayPause, R.drawable.ic_notif_pause); mExpandedTemplate.setImageViewResource(R.id.btnNotifPlayPause, R.drawable.ic_notif_pause); } if (mHasNext) { mBaseTemplate.setViewVisibility(R.id.btnNotifNext, View.VISIBLE); mExpandedTemplate.setViewVisibility(R.id.btnNotifNext, View.VISIBLE); } else { mBaseTemplate.setViewVisibility(R.id.btnNotifNext, View.GONE); mExpandedTemplate.setViewVisibility(R.id.btnNotifNext, View.GONE); } if (mCurrentArt != null && !mCurrentArt.getBitmap().isRecycled()) { mBaseTemplate.setImageViewBitmap(R.id.ivAlbumArt, mCurrentArt.getBitmap()); mExpandedTemplate.setImageViewBitmap(R.id.ivAlbumArt, mCurrentArt.getBitmap()); } else { mBaseTemplate.setImageViewResource(R.id.ivAlbumArt, R.drawable.album_placeholder); mExpandedTemplate.setImageViewResource(R.id.ivAlbumArt, R.drawable.album_placeholder); } // Post update if (mListener != null) { mListener.onNotificationChanged(this); } } /** * Posts or updates the notification to the system's notification manager * * @param managerCompat The notification manager */ public void notify(NotificationManagerCompat managerCompat) { managerCompat.notify(NOTIFICATION_ID, mNotification); } /** * Posts or updates the notification as a foreground notification * * @param service The service instance */ public void notify(Service service) { service.startForeground(NOTIFICATION_ID, mNotification); } /** * Sets the status of the Play/Pause action button within the notification. * * @param play If true, the "Play" action will be shown. If false, the "Pause" action will be * shown. */ public void setPlayPauseAction(boolean play) { setPlayPauseActionImpl(play, true); } private void setPlayPauseActionImpl(boolean play, boolean callBuild) { mShowPlayAction = play; if (callBuild) { buildNotification(); } } /** * Sets the current song to be displayed. This will update the notification text and icon * * @param s The song to be displayed */ public void setCurrentSong(Song s) { if (!s.equals(mCurrentSong)) { mCurrentSong = s; updateAlbumArt(); buildNotification(); } } /** * Sets whether or not the "Next" action should be enabled or not */ public void setHasNext(boolean hasNext) { if (mHasNext != hasNext) { mHasNext = hasNext; buildNotification(); } } /** * Sets the listener to be called when the notification contents changes and notify should be * called on the NotificationManager. * * @param listener The listener */ public void setOnNotificationChangedListener(NotificationChangedListener listener) { mListener = listener; } /** * @return The current active album art */ public BitmapDrawable getAlbumArt() { return mCurrentArt; } private void updateAlbumArt() { if (mCurrentSong != null) { if (mArtTask != null) { mArtTask.cancel(true); } final int artSize = mContext.getResources().getDimensionPixelSize(R.dimen.notification_expanded_art_size); mArtTask = AlbumArtHelper.retrieveAlbumArt(mContext.getApplicationContext().getResources(), this, mCurrentSong, artSize, true); } } @Override public void onArtLoaded(RecyclingBitmapDrawable output, BoundEntity request) { if (request.equals(mCurrentSong)) { mCurrentArt = output; buildNotification(); } mArtTask = null; } public interface NotificationChangedListener { void onNotificationChanged(ServiceNotification notification); } }