/* * Copyright (C) 2017 Team Gateship-One * (Hendrik Borghorst & Frederik Luetkes) * * The AUTHORS.md file contains a detailed contributors list: * <https://github.com/gateship-one/odyssey/blob/master/AUTHORS.md> * * 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 org.gateshipone.odyssey.widget; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.appwidget.AppWidgetProvider; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.widget.RemoteViews; import org.gateshipone.odyssey.activities.OdysseyMainActivity; import org.gateshipone.odyssey.activities.OdysseySplashActivity; import org.gateshipone.odyssey.R; import org.gateshipone.odyssey.artworkdatabase.ArtworkManager; import org.gateshipone.odyssey.models.TrackModel; import org.gateshipone.odyssey.playbackservice.NowPlayingInformation; import org.gateshipone.odyssey.playbackservice.PlaybackService; import org.gateshipone.odyssey.playbackservice.managers.PlaybackServiceStatusHelper; import org.gateshipone.odyssey.utils.CoverBitmapLoader; public class OdysseyWidgetProvider extends AppWidgetProvider { private static final String TAG = OdysseyWidgetProvider.class.getSimpleName(); private RemoteViews mViews; private AppWidgetManager mAppWidgetManager; private Context mContext; private static TrackModel mLastTrack = null; private static NowPlayingInformation mLastInfo; private static Bitmap mLastCover = null; private static boolean mHideArtwork; private final static int INTENT_OPENGUI = 0; private final static int INTENT_PREVIOUS = 1; private final static int INTENT_PLAYPAUSE = 2; private final static int INTENT_NEXT = 3; @Override public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) { super.onUpdate(context, appWidgetManager, appWidgetIds); mContext = context; // Perform this loop procedure for each App Widget that belongs to this // provider for (int appWidgetId : appWidgetIds) { // Tell the AppWidgetManager to perform an update on the current app widget mAppWidgetManager = appWidgetManager; if (null != mViews) { appWidgetManager.updateAppWidget(appWidgetId, mViews); } else { setWidgetContent(null); } } } public void onDeleted(Context context, int[] appWidgetIds) { super.onDeleted(context, appWidgetIds); mLastTrack = null; } /** * This is the broadcast receiver for NowPlayingInformation objects sent by the PBS * * @param context Context used for this receiver * @param intent Intent containing the NowPlayingInformation as a payload. */ @Override public void onReceive(Context context, Intent intent) { super.onReceive(context, intent); // Save the context to use later mContext = context; // Type checks if (intent.getAction().equals(PlaybackServiceStatusHelper.MESSAGE_NEWTRACKINFORMATION)) { // Extract the payload from the intent NowPlayingInformation info = intent.getParcelableExtra(PlaybackServiceStatusHelper.INTENT_NOWPLAYINGNAME); // Check if a payload was sent if (null != info) { // Refresh the widget with the new information setWidgetContent(info); // Save the information for later usage (when the asynchronous bitmap loader finishes) mLastInfo = info; } } else if (intent.getAction().equals(PlaybackServiceStatusHelper.MESSAGE_HIDE_ARTWORK_CHANGED)) { mHideArtwork = intent.getBooleanExtra(PlaybackServiceStatusHelper.MESSAGE_EXTRA_HIDE_ARTWORK_CHANGED_VALUE, context.getResources().getBoolean(R.bool.pref_hide_artwork_default)); mLastCover = null; mLastTrack = null; setWidgetContent(mLastInfo); } else if ( intent.getAction().equals(ArtworkManager.ACTION_NEW_ARTWORK_READY)) { // Check if the new artwork matches the currently playing track. If so reload the artwork because it is now available. String albumKey = intent.getStringExtra(ArtworkManager.INTENT_EXTRA_KEY_ALBUM_KEY); if ( mLastTrack != null && mLastTrack.getTrackAlbumKey().equals(albumKey)) { CoverBitmapLoader coverLoader = new CoverBitmapLoader(mContext, new CoverReceiver()); if (!mHideArtwork) { coverLoader.getImage(mLastTrack); } mLastCover = null; } } } /** * Updates the widget by creating a new RemoteViews object and setting all the intents for the * buttons and the TextViews correctly. * * @param info */ private void setWidgetContent(NowPlayingInformation info) { boolean nowPlaying = false; // Create a new RemoteViews object containing the default widget layout mViews = new RemoteViews(mContext.getPackageName(), R.layout.widget_odyssey_big); // Check if valid object if (info != null) { TrackModel item = info.getCurrentTrack(); if (item != null) { mViews.setTextViewText(R.id.widget_big_trackName, item.getTrackName()); mViews.setTextViewText(R.id.widget_big_ArtistAlbum, item.getTrackArtistName()); // Check if the tracks album changed if (mLastTrack == null || !mLastTrack.getTrackAlbumKey().equals(item.getTrackAlbumKey())) { // If the albumKey changed, then it is necessary to start the image loader mViews.setImageViewResource(R.id.widget_big_cover, R.drawable.odyssey_notification); CoverBitmapLoader coverLoader = new CoverBitmapLoader(mContext, new CoverReceiver()); if (!mHideArtwork) { coverLoader.getImage(item); } mLastCover = null; } else if (mLastTrack.getTrackAlbumKey().equals(item.getTrackAlbumKey()) && mLastCover != null) { // Reuse the image from last calls if the album is the same mViews.setImageViewBitmap(R.id.widget_big_cover, mLastCover); } } // Set the images of the play button dependent on the playback state. PlaybackService.PLAYSTATE playState = info.getPlayState(); if (playState == PlaybackService.PLAYSTATE.PLAYING) { // Show pause icon nowPlaying = true; mViews.setImageViewResource(R.id.widget_big_play, R.drawable.ic_pause_48dp); } else { // Show play icon mViews.setImageViewResource(R.id.widget_big_play, R.drawable.ic_play_arrow_48dp); } // Save the last track information to check if the album key changed (see above). mLastTrack = item; } // set button actions // Main action Intent mainIntent = new Intent(mContext, OdysseySplashActivity.class); mainIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_CLEAR_TASK | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION); if (nowPlaying) { // add intent only if playing is active mainIntent.putExtra(OdysseyMainActivity.MAINACTIVITY_INTENT_EXTRA_REQUESTEDVIEW, OdysseyMainActivity.MAINACTIVITY_INTENT_EXTRA_REQUESTEDVIEW_NOWPLAYINGVIEW); } PendingIntent mainPendingIntent = PendingIntent.getActivity(mContext, INTENT_OPENGUI, mainIntent, PendingIntent.FLAG_UPDATE_CURRENT); mViews.setOnClickPendingIntent(R.id.widget_big_cover, mainPendingIntent); // Play/Pause action Intent playPauseIntent = new Intent(mContext, PlaybackService.class); playPauseIntent.putExtra("action", PlaybackService.ACTION_TOGGLEPAUSE); PendingIntent playPausePendingIntent = PendingIntent.getService(mContext, INTENT_PLAYPAUSE, playPauseIntent, PendingIntent.FLAG_CANCEL_CURRENT); mViews.setOnClickPendingIntent(R.id.widget_big_play, playPausePendingIntent); // Previous song action Intent prevIntent = new Intent(mContext, PlaybackService.class); prevIntent.putExtra("action", PlaybackService.ACTION_PREVIOUS); PendingIntent prevPendingIntent = PendingIntent.getService(mContext, INTENT_PREVIOUS, prevIntent, PendingIntent.FLAG_CANCEL_CURRENT); mViews.setOnClickPendingIntent(R.id.widget_big_previous, prevPendingIntent); // Next song action Intent nextIntent = new Intent(mContext, PlaybackService.class); nextIntent.putExtra("action", PlaybackService.ACTION_NEXT); PendingIntent nextPendingIntent = PendingIntent.getService(mContext, INTENT_NEXT, nextIntent, PendingIntent.FLAG_CANCEL_CURRENT); mViews.setOnClickPendingIntent(R.id.widget_big_next, nextPendingIntent); // Send the widget to the launcher by transfering the remote view mAppWidgetManager = AppWidgetManager.getInstance(mContext); mAppWidgetManager.updateAppWidget(new ComponentName(mContext, OdysseyWidgetProvider.class), mViews); } private class CoverReceiver implements CoverBitmapLoader.CoverBitmapListener { public CoverReceiver() { } /** * Sets the global image variable for this track and recall the update method to refresh * the views. * * @param bm Bitmap fetched for the currently running track. */ @Override public void receiveBitmap(Bitmap bm, final CoverBitmapLoader.IMAGE_TYPE type) { // Check if a valid image was found. if (type == CoverBitmapLoader.IMAGE_TYPE.ALBUM_IMAGE && bm != null) { // Set the globally used variable mLastCover = bm; // Call the update method to refresh the view setWidgetContent(mLastInfo); } } } }