/* * Copyright 2008-2013, ETH Zürich, Samuel Welten, Michael Kuhn, Tobias Langner, * Sandro Affentranger, Lukas Bossard, Michael Grob, Rahul Jain, * Dominic Langenegger, Sonia Mayor Alonso, Roger Odermatt, Tobias Schlueter, * Yannick Stucki, Sebastian Wendland, Samuel Zehnder, Samuel Zihlmann, * Samuel Zweifel * * This file is part of Jukefox. * * Jukefox 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 any later version. Jukefox 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 * Jukefox. If not, see <http://www.gnu.org/licenses/>. */ package ch.ethz.dcg.jukefox.controller; import android.app.PendingIntent; import android.appwidget.AppWidgetManager; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.widget.RemoteViews; import ch.ethz.dcg.jukefox.commons.utils.JoinableThread; import ch.ethz.dcg.jukefox.commons.utils.Log; import ch.ethz.dcg.jukefox.controller.player.IOnPlayerStateChangeListener; import ch.ethz.dcg.jukefox.controller.player.IOnPlaylistStateChangeListener; import ch.ethz.dcg.jukefox.model.collection.BaseAlbum; import ch.ethz.dcg.jukefox.model.collection.BaseArtist; import ch.ethz.dcg.jukefox.model.collection.BaseSong; import ch.ethz.dcg.jukefox.model.collection.IReadOnlyPlaylist; import ch.ethz.dcg.jukefox.model.collection.PlaylistSong; import ch.ethz.dcg.jukefox.model.commons.EmptyPlaylistException; import ch.ethz.dcg.jukefox.model.commons.NoAlbumArtException; import ch.ethz.dcg.jukefox.model.player.PlayerState; import ch.ethz.dcg.jukefox.playmode.IPlayMode; import ch.ethz.dcg.pancho3.R; import ch.ethz.dcg.pancho3.controller.player.PlayerService; import ch.ethz.dcg.pancho3.model.JukefoxApplication; import ch.ethz.dcg.pancho3.view.commons.BitmapReflection; import ch.ethz.dcg.pancho3.view.tabs.PlayerActivity; import ch.ethz.dcg.pancho3.view.widgets.JukefoxWidgetLargeSize; import ch.ethz.dcg.pancho3.view.widgets.JukefoxWidgetNormalSize; public class WidgetController { private static final String TAG = WidgetController.class.getSimpleName(); Controller controller; IOnPlaylistStateChangeListener playlistChangeListener = null; IOnPlayerStateChangeListener playstateChangeListener = null; IOnPlaylistStateChangeListener playlistChangeListenerBig = null; IOnPlayerStateChangeListener playstateChangeListenerBig = null; public WidgetController(Controller controller) { this.controller = controller; updateNormalWidget(); updateLargeWidget(); } public synchronized void updateNormalWidget() { JoinableThread updateWidgetThread = new JoinableThread(new Runnable() { @Override public void run() { Log.v(TAG, "initializing widget..."); Context context = controller.getApplicationContext(); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetnormalsize); while (!controller.getPlayerController().isReady()) { try { JoinableThread.sleep(100); } catch (InterruptedException e) { Log.w(TAG, e); } } BaseSong<BaseArtist, BaseAlbum> song; try { song = controller.getPlayerController().getCurrentSong(); } catch (EmptyPlaylistException e1) { song = null; } setSongInfoAndAlbumArt(remoteViews, song, true); setControlButtons(remoteViews); registerButtons(controller.getApplicationContext(), remoteViews); updateNormalWidgetView(context, remoteViews); if (playlistChangeListener == null || playstateChangeListener == null) { playlistChangeListener = getPlaylistChangeListener(R.layout.widgetnormalsize); controller.getPlayerController().addOnPlaylistStateChangeListener(playlistChangeListener); playstateChangeListener = getPlayerStateListener(R.layout.widgetnormalsize); controller.getPlayerController().addOnPlayerStateChangeListener(playstateChangeListener); } Log.v(TAG, "widget initialized."); } }); updateWidgetThread.start(); } public void updateLargeWidget() { JoinableThread updateWidgetThread2 = new JoinableThread(new Runnable() { @Override public void run() { Log.v(TAG, "initializing widget..."); Context context = controller.getApplicationContext(); RemoteViews remoteViews = new RemoteViews(context.getPackageName(), R.layout.widgetbigsize); while (!controller.getPlayerController().isReady()) { try { JoinableThread.sleep(100); } catch (InterruptedException e) { Log.w(TAG, e); } } BaseSong<BaseArtist, BaseAlbum> song; try { song = controller.getPlayerController().getCurrentSong(); } catch (EmptyPlaylistException e1) { song = null; } setSongInfoAndAlbumArt(remoteViews, song, false); setControlButtons(remoteViews); registerButtons(controller.getApplicationContext(), remoteViews); updateLargeWidgetView(context, remoteViews); if (playlistChangeListenerBig == null || playstateChangeListenerBig == null) { playlistChangeListenerBig = getPlaylistChangeListener(R.layout.widgetbigsize); controller.getPlayerController().addOnPlaylistStateChangeListener(playlistChangeListenerBig); playstateChangeListenerBig = getPlayerStateListener(R.layout.widgetbigsize); controller.getPlayerController().addOnPlayerStateChangeListener(playstateChangeListenerBig); } Log.v(TAG, "widget initialized."); } }); updateWidgetThread2.start(); } private void updateNormalWidgetView(Context context, RemoteViews remoteViews) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); ComponentName cn = new ComponentName(context, JukefoxWidgetNormalSize.class); try { Log.v(TAG, "Updating normal widget"); appWidgetManager.updateAppWidget(cn, remoteViews); } catch (Exception e) { // Sometimes things go wrong when updateing the widget (Null pointer // exceptions) Log.w(TAG, e); } } private void updateLargeWidgetView(Context context, RemoteViews remoteViews) { AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context); ComponentName cn = new ComponentName(context, JukefoxWidgetLargeSize.class); try { Log.v(TAG, "Updating large widget"); appWidgetManager.updateAppWidget(cn, remoteViews); } catch (Exception e) { // Sometimes things go wrong when updateing the widget (Null pointer // exceptions) Log.w(TAG, e); } } private void setControlButtons(RemoteViews remoteViews) { if (controller.getPlayerController().getPlayerState() == PlayerState.PLAY) { remoteViews.setImageViewResource(R.id.widgetPlayPauseButton, R.drawable.d016_pause_button); } else { remoteViews.setImageViewResource(R.id.widgetPlayPauseButton, R.drawable.d017_play_button); } } private void setSongInfoAndAlbumArt(RemoteViews remoteViews, BaseSong<BaseArtist, BaseAlbum> song, boolean lowRes) { Log.v(TAG, "Setting Song Info"); String title; if (song != null) { BaseAlbum album = song.getAlbum(); title = song.getArtist().getName() + " - " + song.getName(); Log.v(TAG, "Setting Song Info title: " + title); Bitmap bitmap; try { bitmap = JukefoxApplication.getCollectionModel().getAlbumArtProvider().getAlbumArt(album, lowRes); if (!lowRes) { bitmap = BitmapReflection.getReflection(bitmap); } if (bitmap != null) { remoteViews.setImageViewBitmap(R.id.widgetAlbumArt, bitmap); } else { remoteViews.setImageViewResource(R.id.widgetAlbumArt, R.drawable.d005_empty_cd); } } catch (NoAlbumArtException e) { remoteViews.setImageViewResource(R.id.widgetAlbumArt, R.drawable.d005_empty_cd); } } else { Log.e(TAG, "Setting Song Info3"); title = controller.getApplicationContext().getString(R.string.artist_title_place_holder); remoteViews.setImageViewResource(R.id.widgetAlbumArt, R.drawable.d005_empty_cd); } remoteViews.setTextViewText(R.id.widget_label_track, title); } private IOnPlaylistStateChangeListener getPlaylistChangeListener(final int widgetLayoutId) { return new IOnPlaylistStateChangeListener() { @Override public void onCurrentSongChanged(final PlaylistSong<BaseArtist, BaseAlbum> newSong) { // Workaround by Sämy: this takes sometimes a long time (when no album art is around?) -> do it as a background task JukefoxApplication.getHandler().post(new Runnable() { @Override public void run() { Log.v(TAG, "current song changed: " + newSong); RemoteViews remoteViews = new RemoteViews(controller.getApplicationContext().getPackageName(), widgetLayoutId); if (remoteViews != null) { if (widgetLayoutId == R.layout.widgetnormalsize) { setSongInfoAndAlbumArt(remoteViews, newSong, true); updateNormalWidgetView(controller.getApplicationContext(), remoteViews); } else if (widgetLayoutId == R.layout.widgetbigsize) { setSongInfoAndAlbumArt(remoteViews, newSong, false); updateLargeWidgetView(controller.getApplicationContext(), remoteViews); } } } }); } @Override public void onPlayModeChanged(IPlayMode newPlayMode) { } @Override public void onPlaylistChanged(IReadOnlyPlaylist newPlaylist) { } }; } private IOnPlayerStateChangeListener getPlayerStateListener(final int widgetLayoutId) { return new IOnPlayerStateChangeListener() { @Override public void onPlayerStateChanged(PlayerState newPlayerState) { RemoteViews remoteViews = new RemoteViews(controller.getApplicationContext().getPackageName(), widgetLayoutId); if (remoteViews != null) { setControlButtons(remoteViews); if (widgetLayoutId == R.layout.widgetnormalsize) { updateNormalWidgetView(controller.getApplicationContext(), remoteViews); } else if (widgetLayoutId == R.layout.widgetbigsize) { updateLargeWidgetView(controller.getApplicationContext(), remoteViews); } } } @Override public void onSongCompleted(PlaylistSong<BaseArtist, BaseAlbum> song) { } @Override public void onSongSkipped(PlaylistSong<BaseArtist, BaseAlbum> song, int position) { } @Override public void onSongStarted(PlaylistSong<BaseArtist, BaseAlbum> song) { } }; } public static void registerButtons(Context context, RemoteViews remoteViews) { // Intent albumArtIntent = new Intent(context, PlayerActivity.class); Intent albumArtIntent = new Intent(context, PlayerActivity.class); PendingIntent albumArtPendingIntent = PendingIntent.getActivity(context, 0, albumArtIntent, 0); remoteViews.setOnClickPendingIntent(R.id.widgetAlbumArt, albumArtPendingIntent); Intent playButtonIntent = new Intent(context, PlayerService.class); playButtonIntent.setAction(PlayerService.ACTION_WIDGET_PLAY_PAUSE); PendingIntent playButtonPendingIntent = PendingIntent.getService(context, 0, playButtonIntent, 0); remoteViews.setOnClickPendingIntent(R.id.widgetPlayPauseButton, playButtonPendingIntent); Intent previousButtonIntent = new Intent(context, PlayerService.class); previousButtonIntent.setAction(PlayerService.ACTION_PREVIOUS); PendingIntent previousButtonPendingIntent = PendingIntent.getService(context, 0, previousButtonIntent, 0); remoteViews.setOnClickPendingIntent(R.id.widgetPreviousButton, previousButtonPendingIntent); Intent nextButtonIntent = new Intent(context, PlayerService.class); nextButtonIntent.setAction(PlayerService.ACTION_NEXT); PendingIntent nextButtonPendingIntent = PendingIntent.getService(context, 0, nextButtonIntent, 0); remoteViews.setOnClickPendingIntent(R.id.widgetNextButton, nextButtonPendingIntent); } }