/*
* 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 );
}
}