/* * 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.framework; import android.os.Handler; import android.os.HandlerThread; import android.os.Looper; import android.os.Message; import android.os.RemoteException; import android.util.Log; import com.fastbootmobile.encore.model.Album; import com.fastbootmobile.encore.model.Playlist; import com.fastbootmobile.encore.model.Song; import com.fastbootmobile.encore.providers.ProviderIdentifier; import com.fastbootmobile.encore.service.IPlaybackCallback; import com.fastbootmobile.encore.service.IPlaybackService; import com.fastbootmobile.encore.service.PlaybackService; import java.util.ArrayList; import java.util.List; /** * Proxy class for Playback service calls */ public class PlaybackProxy { private static final String TAG = "PlaybackProxy"; private static Handler sHandler; private static final List<IPlaybackCallback> sPendingCallbacks = new ArrayList<>(); private static final int MSG_PLAY = 1; private static final int MSG_PAUSE = 2; private static final int MSG_STOP = 3; private static final int MSG_PLAY_SONG = 4; private static final int MSG_PLAY_AT_INDEX = 5; private static final int MSG_CLEAR_QUEUE = 6; private static final int MSG_QUEUE_SONG = 7; private static final int MSG_QUEUE_ALBUM = 8; private static final int MSG_ADD_CALLBACK = 9; private static final int MSG_REMOVE_CALLBACK = 10; private static final int MSG_PLAY_ALBUM = 11; private static final int MSG_SEEK = 12; private static final int MSG_SET_DSP_CHAIN = 13; private static final int MSG_NEXT = 14; private static final int MSG_PREVIOUS = 15; private static final int MSG_SET_REPEAT_MODE = 16; private static final int MSG_PLAY_PLAYLIST = 17; private static final int MSG_QUEUE_PLAYLIST = 18; private static final int MSG_SET_SHUFFLE_MODE = 19; private static final int MSG_PLAY_NEXT = 20; private static final int MSG_SLEEP_TIMER = 21; private static final int MSG_SET_PLAYER_MUTED = 22; private static class PlaybackProxyHandler extends Handler { public PlaybackProxyHandler(Looper looper) { super(looper); } @Override public void handleMessage(Message msg) { try { switch (msg.what) { case MSG_PLAY: getPlayback().play(); break; case MSG_PAUSE: getPlayback().pause(); break; case MSG_STOP: getPlayback().stop(); break; case MSG_PLAY_SONG: getPlayback().playSong((Song) msg.obj); break; case MSG_PLAY_AT_INDEX: getPlayback().playAtQueueIndex(msg.arg1); break; case MSG_CLEAR_QUEUE: getPlayback().clearPlaybackQueue(); break; case MSG_QUEUE_SONG: getPlayback().queueSong((Song) msg.obj, msg.arg1 == 1); break; case MSG_QUEUE_ALBUM: getPlayback().queueAlbum((Album) msg.obj, msg.arg1 == 1); break; case MSG_PLAY_ALBUM: getPlayback().playAlbum((Album) msg.obj); break; case MSG_PLAY_NEXT: getPlayback().playNext((Song) msg.obj); break; case MSG_SEEK: getPlayback().seek((Long) msg.obj); break; case MSG_SET_DSP_CHAIN: getPlayback().setDSPChain((List<ProviderIdentifier>) msg.obj); break; case MSG_NEXT: getPlayback().next(); break; case MSG_PREVIOUS: getPlayback().previous(); break; case MSG_SET_REPEAT_MODE: getPlayback().setRepeatMode((Boolean) msg.obj); break; case MSG_SET_SHUFFLE_MODE: getPlayback().setShuffleMode((Boolean) msg.obj); break; case MSG_PLAY_PLAYLIST: getPlayback().playPlaylist((Playlist) msg.obj); break; case MSG_QUEUE_PLAYLIST: getPlayback().queuePlaylist((Playlist) msg.obj, msg.arg1 == 1); break; case MSG_ADD_CALLBACK: try { getPlayback().addCallback((IPlaybackCallback) msg.obj); } catch (RemoteException e) { // Service likely isn't bound: queue callback so that it's added when // the service is ready. synchronized (sPendingCallbacks) { sPendingCallbacks.add((IPlaybackCallback) msg.obj); } } break; case MSG_REMOVE_CALLBACK: IPlaybackService service = getPlayback(false); if (service != null) { service.removeCallback((IPlaybackCallback) msg.obj); } break; case MSG_SLEEP_TIMER: getPlayback().setSleepTimer((Long) msg.obj); break; case MSG_SET_PLAYER_MUTED: getPlayback().setPlayerMuted((Boolean) msg.obj); break; } } catch (Exception e) { Log.e(TAG, "Cannot run remote method", e); } } } static { HandlerThread thread = new HandlerThread("PlaybackProxy"); thread.start(); sHandler = new PlaybackProxyHandler(thread.getLooper()); } private static IPlaybackService getPlayback(boolean connectIfNull) throws RemoteException { IPlaybackService service = PluginsLookup.getDefault().getPlaybackService(connectIfNull); if (service == null && connectIfNull) { throw new RemoteException("Playback service is null"); } return service; } private static IPlaybackService getPlayback() throws RemoteException { return getPlayback(true); } static void notifyPlaybackConnected() { synchronized (sPendingCallbacks) { for (IPlaybackCallback callback : sPendingCallbacks) { addCallback(callback); } sPendingCallbacks.clear(); } } public static boolean isServiceConnected() { try { return (getPlayback(false) != null); } catch (RemoteException e) { return false; } } public static void play() { Message.obtain(sHandler, MSG_PLAY).sendToTarget(); } public static void playSong(Song song) { Message.obtain(sHandler, MSG_PLAY_SONG, song).sendToTarget(); } public static void playAlbum(Album album) { Message.obtain(sHandler, MSG_PLAY_ALBUM, album).sendToTarget(); } public static void playAtIndex(int index) { Message.obtain(sHandler, MSG_PLAY_AT_INDEX, index, 0).sendToTarget(); } public static void pause() { Message.obtain(sHandler, MSG_PAUSE).sendToTarget(); } public static void stop() { Message.obtain(sHandler, MSG_STOP).sendToTarget(); } public static void clearQueue() { Message.obtain(sHandler, MSG_CLEAR_QUEUE).sendToTarget(); } public static void queueSong(Song song, boolean top) { Message.obtain(sHandler, MSG_QUEUE_SONG, top ? 1 : 0, 0, song).sendToTarget(); } public static void queueAlbum(Album album, boolean top) { Message.obtain(sHandler, MSG_QUEUE_ALBUM, top ? 1 : 0, 0, album).sendToTarget(); } public static void playNext(Song song) { Message.obtain(sHandler, MSG_PLAY_NEXT, song).sendToTarget(); } public static int getState() { try { return getPlayback().getState(); } catch (RemoteException e) { return PlaybackService.STATE_STOPPED; } } public static Song getCurrentTrack() { try { return getPlayback().getCurrentTrack(); } catch (RemoteException e) { return null; } } public static void addCallback(IPlaybackCallback callback) { Message.obtain(sHandler, MSG_ADD_CALLBACK, callback).sendToTarget(); } public static void removeCallback(IPlaybackCallback callback) { Message.obtain(sHandler, MSG_REMOVE_CALLBACK, callback).sendToTarget(); } public static List<Song> getCurrentPlaybackQueue() { try { return getPlayback().getCurrentPlaybackQueue(); } catch (RemoteException e) { return new ArrayList<>(); } } public static int getCurrentTrackPosition() { try { return getPlayback().getCurrentTrackPosition(); } catch (RemoteException e) { return 0; } } public static int getCurrentTrackLength() { try { return getPlayback().getCurrentTrackLength(); } catch (RemoteException e) { return 0; } } public static int getCurrentTrackIndex() { try { return getPlayback().getCurrentTrackIndex(); } catch (RemoteException e) { return 0; } } public static void seek(long timeMs) { Message.obtain(sHandler, MSG_SEEK, timeMs).sendToTarget(); } public static void next() { Message.obtain(sHandler, MSG_NEXT).sendToTarget(); } public static void previous() { Message.obtain(sHandler, MSG_PREVIOUS).sendToTarget(); } public static List<ProviderIdentifier> getDSPChain() { try { return getPlayback().getDSPChain(); } catch (RemoteException e) { return new ArrayList<>(); } } public static void setDSPChain(List<ProviderIdentifier> chain) { Message.obtain(sHandler, MSG_SET_DSP_CHAIN, chain).sendToTarget(); } public static boolean isRepeatMode() { try { return getPlayback().isRepeatMode(); } catch (RemoteException e) { return false; } } public static void setRepeatMode(boolean repeat) { Message.obtain(sHandler, MSG_SET_REPEAT_MODE, repeat).sendToTarget(); } public static boolean isShuffleMode() { try { return getPlayback().isShuffleMode(); } catch (RemoteException e) { return false; } } public static void setShuffleMode(boolean shuffle) { Message.obtain(sHandler, MSG_SET_SHUFFLE_MODE, shuffle).sendToTarget(); } public static void playPlaylist(Playlist p) { Message.obtain(sHandler, MSG_PLAY_PLAYLIST, p).sendToTarget(); } public static void queuePlaylist(Playlist p, boolean top) { Message.obtain(sHandler, MSG_QUEUE_PLAYLIST, top ? 1 : 0, 0, p).sendToTarget(); } public static void setSleepTimer(long uptime) { Message.obtain(sHandler, MSG_SLEEP_TIMER, uptime).sendToTarget(); } public static long getSleepTimerEndTime() { try { return getPlayback().getSleepTimerEndTime(); } catch (RemoteException e) { return -1; } } public static void setPhonePlayerMuted(boolean muted) { Message.obtain(sHandler, MSG_SET_PLAYER_MUTED, muted).sendToTarget(); } }