/* * Copyright (C) 2009 Teleca Poland Sp. z o.o. <android@teleca.com> * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.teleca.jamendo.service; import com.teleca.jamendo.MyApplication; import com.teleca.jamendo.activity.playview.PlayView; import com.teleca.jamendo.api.IPlayEngine; import com.teleca.jamendo.api.IPlayEngineListener; import com.teleca.jamendo.api_impl.PlayerEngineImpl; import com.teleca.jamendo.model.Playlist; import com.teleca.jamendo.R; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.app.Service; import android.content.Context; import android.content.Intent; import android.net.wifi.WifiManager; import android.net.wifi.WifiManager.WifiLock; import android.os.IBinder; import android.preference.PreferenceManager; import android.telephony.PhoneStateListener; import android.telephony.TelephonyManager; import android.util.Log; /** * Background player * * @author Lukasz Wisniewski */ public class PlayerService extends Service{ public static final String ACTION_PLAY = "play"; public static final String ACTION_NEXT = "next"; public static final String ACTION_PREV = "prev"; public static final String ACTION_STOP = "stop"; public static final String ACTION_BIND_LISTENER = "bind_listener"; private WifiManager mWifiManager; private WifiLock mWifiLock; private IPlayEngine mPlayerEngine; private TelephonyManager mTelephonyManager; private PhoneStateListener mPhoneStateListener; private NotificationManager mNotificationManager = null; private static final int PLAYING_NOTIFY_ID = 667667; @Override public IBinder onBind(Intent intent) { return null; } @Override public void onCreate() { Log.i(MyApplication.TAG, "Player Service onCreate"); // All necessary Application <-> Service pre-setup goes in here mPlayerEngine = new PlayerEngineImpl(); mPlayerEngine.setListener(mLocalEngineListener); mTelephonyManager = (TelephonyManager)this.getSystemService(Context.TELEPHONY_SERVICE); mPhoneStateListener = new PhoneStateListener(){ @Override public void onCallStateChanged(int state, String incomingNumber) { Log.e(MyApplication.TAG, "onCallStateChanged"); if (state == TelephonyManager.CALL_STATE_IDLE) { // resume playback } else { if(mPlayerEngine != null){ mPlayerEngine.pause(); } } } }; mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_CALL_STATE); mNotificationManager = ( NotificationManager ) getSystemService( NOTIFICATION_SERVICE ); mWifiManager = (WifiManager) getSystemService(Context.WIFI_SERVICE); mWifiLock = mWifiManager.createWifiLock(MyApplication.TAG); mWifiLock.setReferenceCounted(false); MyApplication.getInstance().setConcretePlayerEngine(mPlayerEngine); mRemoteEngineListener = MyApplication.getInstance().fetchPlayerEngineListener(); } @Override public void onStart(Intent intent, int startId) { super.onStart(intent, startId); if(intent == null){ return; } String action = intent.getAction(); Log.i(MyApplication.TAG, "Player Service onStart - "+action); if(action.equals(ACTION_STOP)){ stopSelfResult(startId); return; } if(action.equals(ACTION_BIND_LISTENER)){ mRemoteEngineListener = MyApplication.getInstance().fetchPlayerEngineListener(); return; } // we need to have up-to-date playlist if any of play,next,prev buttons is pressed updatePlaylist(); if(action.equals(ACTION_PLAY)){ mPlayerEngine.play(); return; } if(action.equals(ACTION_NEXT)){ mPlayerEngine.next(); return; } if(action.equals(ACTION_PREV)){ mPlayerEngine.prev(); return; } } /** * Fetches a new playlist if its reference address differs from the current one */ private void updatePlaylist(){ if(mPlayerEngine.getPlaylist() != MyApplication.getInstance().fetchPlaylist()){ mPlayerEngine.openPlaylist(MyApplication.getInstance().fetchPlaylist()); } } @Override public void onDestroy() { Log.i(MyApplication.TAG, "Player Service onDestroy"); MyApplication.getInstance().setConcretePlayerEngine(null); mPlayerEngine.stop(); mPlayerEngine = null; // unregister listener mTelephonyManager.listen(mPhoneStateListener, PhoneStateListener.LISTEN_NONE); super.onDestroy(); } /** * Hint: if necessary this can be extended to ArrayList of listeners in the future, though * I do not expect that it will be necessary */ private IPlayEngineListener mRemoteEngineListener; /** * Sends notification to the status bar + passes other notifications to remote listeners */ private IPlayEngineListener mLocalEngineListener = new IPlayEngineListener(){ @Override public void onTrackBuffering(int percent) { if(mRemoteEngineListener != null){ mRemoteEngineListener.onTrackBuffering(percent); } } @Override public void onTrackChanged(Playlist playlistEntry) { displayNotifcation(playlistEntry); if(mRemoteEngineListener != null){ mRemoteEngineListener.onTrackChanged(playlistEntry); } // last.fm scrobbler boolean lastFm = PreferenceManager.getDefaultSharedPreferences(PlayerService.this).getBoolean("lastfm_scrobble", false); if(lastFm){ scrobblerMetaChanged(); } } @Override public void onTrackProgress(int seconds) { if(mRemoteEngineListener != null){ mRemoteEngineListener.onTrackProgress(seconds); } } @Override public void onTrackStop() { // allow killing this service // NO-OP setForeground(false); mWifiLock.release(); mNotificationManager.cancel(PLAYING_NOTIFY_ID); if(mRemoteEngineListener != null){ mRemoteEngineListener.onTrackStop(); } } @Override public boolean onTrackStart() { // prevent killing this service // NO-OP setForeground(true); mWifiLock.acquire(); if(mRemoteEngineListener != null){ if( !mRemoteEngineListener.onTrackStart() ) return false; } boolean wifiOnlyMode = PreferenceManager.getDefaultSharedPreferences(PlayerService.this).getBoolean("wifi_only", false); // wifi only mode if(wifiOnlyMode && !mWifiManager.isWifiEnabled()){ return false; } // roaming protection boolean roamingProtection = PreferenceManager.getDefaultSharedPreferences(PlayerService.this).getBoolean("roaming_protection", true); if(!mWifiManager.isWifiEnabled()){ if(roamingProtection && mTelephonyManager.isNetworkRoaming()) return false; } return true; } @Override public void onTrackPause() { if(mRemoteEngineListener != null){ mRemoteEngineListener.onTrackPause(); } } @Override public void onTrackStreamError() { if(mRemoteEngineListener != null){ mRemoteEngineListener.onTrackStreamError(); } } }; /** * This snippet of code scrobbles music to last.fm servers using the official * last.fm client */ private void scrobblerMetaChanged(){ Playlist entry = mPlayerEngine.getPlaylist().getSelectedTrack(); if(entry != null){ Intent i = new Intent("fm.last.android.metachanged"); i.putExtra("artist", entry.getAlbum().getArtistName()); i.putExtra("album", entry.getAlbum().getName()); i.putExtra("track", entry.getTrack().getName()); i.putExtra("duration", entry.getTrack().getDuration()*1000); // duration in milliseconds sendBroadcast(i); } } private void displayNotifcation(Playlist playlistEntry) { String notificationMessage = playlistEntry.getTrack().getName() + " - " + playlistEntry.getAlbum().getArtistName(); Notification notification = new Notification( R.drawable.stat_notify, notificationMessage, System.currentTimeMillis() ); PendingIntent contentIntent = PendingIntent.getActivity( this, 0, new Intent( this, PlayView.class ), 0); notification.setLatestEventInfo( this, "Jamendo Player", notificationMessage, contentIntent ); notification.flags |= Notification.FLAG_ONGOING_EVENT; mNotificationManager.notify( PLAYING_NOTIFY_ID, notification ); } }