/***************************************************************************** * AudioServiceController.java ***************************************************************************** * Copyright © 2011-2012 VLC authors and VideoLAN * * 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 2 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, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. *****************************************************************************/ package org.videolan.vlc; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.videolan.vlc.interfaces.IAudioPlayer; import org.videolan.vlc.interfaces.IAudioPlayerControl; import org.videolan.vlc.interfaces.IAudioService; import org.videolan.vlc.interfaces.IAudioServiceCallback; import android.content.ComponentName; import android.content.Context; import android.content.Intent; import android.content.ServiceConnection; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.os.IBinder; import android.os.RemoteException; import android.preference.PreferenceManager; import android.util.Log; public class AudioServiceController implements IAudioPlayerControl { public static final String TAG = "VLC/AudioServiceContoller"; private static AudioServiceController mInstance; private static boolean mIsBound = false; private IAudioService mAudioServiceBinder; private ServiceConnection mAudioServiceConnection; private final ArrayList<IAudioPlayer> mAudioPlayer; private final IAudioServiceCallback mCallback = new IAudioServiceCallback.Stub() { @Override public void update() throws RemoteException { updateAudioPlayer(); } @Override public void updateProgress() throws RemoteException { updateProgressAudioPlayer(); } }; private AudioServiceController() { mAudioPlayer = new ArrayList<IAudioPlayer>(); } public static AudioServiceController getInstance() { if (mInstance == null) { mInstance = new AudioServiceController(); } return mInstance; } /** * Bind to audio service if it is running */ public void bindAudioService(Context context) { if (context == null) { Log.w(TAG, "bindAudioService() with null Context. Ooops" ); return; } context = context.getApplicationContext(); if (!mIsBound) { Intent service = new Intent(context, AudioService.class); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); final boolean enableHS = prefs.getBoolean("enable_headset_detection", true); // Setup audio service connection mAudioServiceConnection = new ServiceConnection() { @Override public void onServiceDisconnected(ComponentName name) { Log.d(TAG, "Service Disconnected"); mAudioServiceBinder = null; mIsBound = false; } @Override public void onServiceConnected(ComponentName name, IBinder service) { if (!mIsBound) // Can happen if unbind is called quickly before this callback return; Log.d(TAG, "Service Connected"); mAudioServiceBinder = IAudioService.Stub.asInterface(service); // Register controller to the service try { mAudioServiceBinder.addAudioCallback(mCallback); mAudioServiceBinder.detectHeadset(enableHS); } catch (RemoteException e) { Log.e(TAG, "remote procedure call failed: addAudioCallback()"); } updateAudioPlayer(); } }; mIsBound = context.bindService(service, mAudioServiceConnection, Context.BIND_AUTO_CREATE); } else { // Register controller to the service try { if (mAudioServiceBinder != null) mAudioServiceBinder.addAudioCallback(mCallback); } catch (RemoteException e) { Log.e(TAG, "remote procedure call failed: addAudioCallback()"); } } } public void unbindAudioService(Context context) { if (context == null) { Log.w(TAG, "unbindAudioService() with null Context. Ooops" ); return; } context = context.getApplicationContext(); if (mIsBound) { mIsBound = false; try { if (mAudioServiceBinder != null) mAudioServiceBinder.removeAudioCallback(mCallback); } catch (RemoteException e) { Log.e(TAG, "remote procedure call failed: removeAudioCallback()"); } context.unbindService(mAudioServiceConnection); mAudioServiceBinder = null; mAudioServiceConnection = null; } } /** * Add a AudioPlayer * @param ap */ public void addAudioPlayer(IAudioPlayer ap) { if (!mAudioPlayer.contains(ap)) mAudioPlayer.add(ap); } /** * Remove AudioPlayer from list * @param ap */ public void removeAudioPlayer(IAudioPlayer ap) { if (mAudioPlayer.contains(ap)) mAudioPlayer.remove(ap); } /** * Update all AudioPlayer */ private void updateAudioPlayer() { for (IAudioPlayer player : mAudioPlayer) player.update(); } /** * Update the progress of all AudioPlayers */ private void updateProgressAudioPlayer() { for (IAudioPlayer player : mAudioPlayer) player.updateProgress(); } /** * This is a handy utility function to call remote procedure calls from mAudioServiceBinder * to reduce code duplication across methods of AudioServiceController. * * @param instance The instance of IAudioService to call, usually mAudioServiceBinder * @param returnType Return type of the method being called * @param defaultValue Default value to return in case of null or exception * @param functionName The function name to call, e.g. "stop" * @param parameterTypes List of parameter types. Pass null if none. * @param parameters List of parameters. Must be in same order as parameterTypes. Pass null if none. * @return The results of the RPC or defaultValue if error */ private <T> T remoteProcedureCall(IAudioService instance, Class<T> returnType, T defaultValue, String functionName, Class<?> parameterTypes[], Object parameters[]) { if(instance == null) { return defaultValue; } try { Method m = IAudioService.class.getMethod(functionName, parameterTypes); @SuppressWarnings("unchecked") T returnVal = (T) m.invoke(instance, parameters); return returnVal; } catch(NoSuchMethodException e) { e.printStackTrace(); return defaultValue; } catch (IllegalArgumentException e) { e.printStackTrace(); return defaultValue; } catch (IllegalAccessException e) { e.printStackTrace(); return defaultValue; } catch (InvocationTargetException e) { if(e.getTargetException() instanceof RemoteException) { Log.e(TAG, "remote procedure call failed: " + functionName + "()"); } return defaultValue; } } public void load(List<String> mediaPathList, int position) { load(mediaPathList, position, false); } public void load(String mediaPath, boolean noVideo) { ArrayList<String> arrayList = new ArrayList<String>(); arrayList.add(mediaPath); load(arrayList, 0, noVideo); } public void load(List<String> mediaPathList, int position, boolean noVideo) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "load", new Class<?>[] { List.class, int.class, boolean.class }, new Object[] { mediaPathList, position, noVideo } ); } public void append(String mediaPath) { ArrayList<String> arrayList = new ArrayList<String>(); arrayList.add(mediaPath); append(arrayList); } public void append(List<String> mediaPathList) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "append", new Class<?>[] { List.class }, new Object[] { mediaPathList } ); } public void moveItem(int positionStart, int positionEnd) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "moveItem", new Class<?>[] { int.class, int.class }, new Object[] { positionStart, positionEnd } ); } public void remove(int position) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "remove", new Class<?>[] { int.class }, new Object[] { position } ); } @SuppressWarnings("unchecked") public List<String> getMediaLocations() { List<String> def = new ArrayList<String>(); return remoteProcedureCall(mAudioServiceBinder, List.class, def, "getMediaLocations", null, null); } public String getCurrentMediaLocation() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getCurrentMediaLocation", null, null); } public void stop() { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "stop", null, null); updateAudioPlayer(); } public void showWithoutParse(int u) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "showWithoutParse", new Class<?>[] { int.class }, new Object[] { u } ); updateAudioPlayer(); } public void playIndex(int i) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "playIndex", new Class<?>[] { int.class }, new Object[] { i } ); updateAudioPlayer(); } @Override public String getAlbum() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getAlbum", null, null); } @Override public String getArtist() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getArtist", null, null); } @Override public String getArtistPrev() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getArtistPrev", null, null); } @Override public String getArtistNext() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getArtistNext", null, null); } @Override public String getTitle() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getTitle", null, null); } @Override public String getTitlePrev() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getTitlePrev", null, null); } @Override public String getTitleNext() { return remoteProcedureCall(mAudioServiceBinder, String.class, (String)null, "getTitleNext", null, null); } @Override public boolean isPlaying() { return hasMedia() && remoteProcedureCall(mAudioServiceBinder, boolean.class, false, "isPlaying", null, null); } @Override public void pause() { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "pause", null, null); updateAudioPlayer(); } @Override public void play() { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "play", null, null); updateAudioPlayer(); } @Override public boolean hasMedia() { return remoteProcedureCall(mAudioServiceBinder, boolean.class, false, "hasMedia", null, null); } @Override public int getLength() { return remoteProcedureCall(mAudioServiceBinder, int.class, 0, "getLength", null, null); } @Override public int getTime() { return remoteProcedureCall(mAudioServiceBinder, int.class, 0, "getTime", null, null); } @Override public Bitmap getCover() { return remoteProcedureCall(mAudioServiceBinder, Bitmap.class, (Bitmap)null, "getCover", null, null); } @Override public Bitmap getCoverPrev() { return remoteProcedureCall(mAudioServiceBinder, Bitmap.class, (Bitmap)null, "getCoverPrev", null, null); } @Override public Bitmap getCoverNext() { return remoteProcedureCall(mAudioServiceBinder, Bitmap.class, (Bitmap)null, "getCoverNext", null, null); } @Override public void next() { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "next", null, null); } @Override public void previous() { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "previous", null, null); } public void setTime(long time) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "setTime", new Class<?>[] { long.class }, new Object[] { time } ); } @Override public boolean hasNext() { return remoteProcedureCall(mAudioServiceBinder, boolean.class, false, "hasNext", null, null); } @Override public boolean hasPrevious() { return remoteProcedureCall(mAudioServiceBinder, boolean.class, false, "hasPrevious", null, null); } @Override public void shuffle() { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "shuffle", null, null); } @Override public void setRepeatType(RepeatType t) { remoteProcedureCall(mAudioServiceBinder, Void.class, (Void)null, "setRepeatType", new Class<?>[] { int.class }, new Object[] { t.ordinal() } ); } @Override public boolean isShuffling() { return remoteProcedureCall(mAudioServiceBinder, boolean.class, false, "isShuffling", null, null); } @Override public RepeatType getRepeatType() { return RepeatType.values()[ remoteProcedureCall(mAudioServiceBinder, int.class, RepeatType.None.ordinal(), "getRepeatType", null, null) ]; } @Override public void detectHeadset(boolean enable) { remoteProcedureCall(mAudioServiceBinder, Void.class, null, "detectHeadset", new Class<?>[] { boolean.class }, new Object[] { enable } ); } @Override public float getRate() { return remoteProcedureCall(mAudioServiceBinder, Float.class, (float) 1.0, "getRate", null, null); } }