/* This file is part of the Android Clementine Remote. * Copyright (C) 2013, Andreas Muttscheller <asfa194@gmail.com> * * 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 de.qspool.clementineremote.backend; import android.annotation.SuppressLint; import android.app.Notification; import android.app.NotificationManager; import android.app.Service; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.os.Binder; import android.os.Handler; import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.PowerManager; import android.preference.PreferenceManager; import android.support.v4.app.NotificationCompat; import de.qspool.clementineremote.App; import de.qspool.clementineremote.R; import de.qspool.clementineremote.SharedPreferencesKeys; import de.qspool.clementineremote.backend.downloader.DownloadManager; import de.qspool.clementineremote.backend.globalsearch.GlobalSearchManager; import de.qspool.clementineremote.backend.listener.PlayerConnectionListener; import de.qspool.clementineremote.backend.mediasession.ClementineMediaSessionNotification; import de.qspool.clementineremote.backend.mediasession.MediaSessionController; import de.qspool.clementineremote.backend.pb.ClementineMessage; import de.qspool.clementineremote.backend.pb.ClementineMessageFactory; import de.qspool.clementineremote.backend.pb.ClementineRemoteProtocolBuffer.MsgType; import de.qspool.clementineremote.utils.Utilities; public class ClementineService extends Service { public final static String SERVICE_ID = "de.qspool.clementineremote.service.id"; public final static int SERVICE_START = 1; public final static int SERVICE_DISCONNECTED = 2; public final static String EXTRA_STRING_IP = "EXTRA_IP"; public final static String EXTRA_INT_PORT = "EXTRA_PORT"; public final static String EXTRA_INT_AUTH = "EXTRA_AUTH"; private final String TAG = getClass().getSimpleName(); private NotificationManager mNotificationManager; private Thread mPlayerThread; private boolean mUseWakeLock = false; private PowerManager.WakeLock mWakeLock; private Handler mUiHandler; private ClementineServiceBinder mClementineServiceBinder = new ClementineServiceBinder(); public class ClementineServiceBinder extends Binder { public ClementineService getClementineService() { return ClementineService.this; } } @Override public void onCreate() { super.onCreate(); // Get a Wakelock Object PowerManager pm = (PowerManager) getSystemService( Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "Clementine"); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); mUseWakeLock = prefs.getBoolean(SharedPreferencesKeys.SP_WAKE_LOCK, false); } @Override public IBinder onBind(Intent intent) { return mClementineServiceBinder; } @Override public int onStartCommand(Intent intent, int flags, int startId) { if (intent != null && intent.hasExtra(SERVICE_ID)) { handleServiceAction(intent); } return START_STICKY; } /** * Handle the requests to the service * * @param intent The action to perform */ public void handleServiceAction(final Intent intent) { mNotificationManager = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE); int action = intent.getIntExtra(SERVICE_ID, 0); switch (action) { case SERVICE_START: // Create a new instance if (App.ClementineConnection == null) { App.ClementineConnection = new ClementinePlayerConnection(); App.ClementineConnection.setUiHandler(mUiHandler); MediaSessionController mediaSessionController = new MediaSessionController(this, App.ClementineConnection); mediaSessionController.registerMediaSession(); GlobalSearchManager.getInstance().reset(); App.ClementineConnection.addPlayerConnectionListener( new PlayerConnectionListener() { @Override public void onConnectionStatusChanged( ClementinePlayerConnection.ConnectionStatus status) { switch (status) { case IDLE: sendConnectMessageIfPossible(intent); break; case CONNECTING: break; case NO_CONNECTION: sendDisconnectServiceMessage(); break; case CONNECTED: if (mUseWakeLock) { mWakeLock.acquire(); } break; case LOST_CONNECTION: showKeepAliveDisconnectNotification(); break; case DISCONNECTED: sendDisconnectServiceMessage(); if (mUseWakeLock) { mWakeLock.release(); } break; } } @Override public void onClementineMessageReceived( ClementineMessage clementineMessage) { GlobalSearchManager.getInstance().parseClementineMessage( clementineMessage); } }); mPlayerThread = new Thread(App.ClementineConnection); mPlayerThread.start(); } else { sendConnectMessageIfPossible(intent); } break; case SERVICE_DISCONNECTED: intteruptThread(); App.ClementineConnection = null; stopSelf(); break; default: break; } } @Override public void onDestroy() { if (App.ClementineConnection != null && App.ClementineConnection.isConnected()) { // Move the request to the message Message msg = Message.obtain(); msg.obj = ClementineMessage.getMessage(MsgType.DISCONNECT); // Send the request to the thread App.ClementineConnection.mHandler.sendMessage(msg); } intteruptThread(); App.ClementineConnection = null; } public void setUiHandler(Handler uiHandler) { mUiHandler = uiHandler; } private void sendDisconnectServiceMessage() { Intent mServiceIntent = new Intent( ClementineService.this, ClementineService.class); mServiceIntent .putExtra(SERVICE_ID, SERVICE_DISCONNECTED); startService(mServiceIntent); } private void intteruptThread() { if (mPlayerThread != null) { mPlayerThread.interrupt(); } if (App.ClementineConnection != null && App.ClementineConnection.mHandler != null && mPlayerThread.isAlive()) { App.ClementineConnection.mHandler.post(new Runnable() { @Override public void run() { Looper.myLooper().quit(); } }); } DownloadManager.getInstance().shutdown(); } /** * Create a notification that shows, that we got a keep alive timeout */ @SuppressLint("InlinedApi") private void showKeepAliveDisconnectNotification() { Notification notification = new NotificationCompat.Builder(this) .setSmallIcon(R.drawable.notification) .setContentTitle(getString(R.string.app_name)) .setContentText(getString(R.string.notification_disconnect_keep_alive)) .setAutoCancel(true) .setVisibility(Notification.VISIBILITY_PUBLIC) .setContentIntent(Utilities.getClementineRemotePendingIntent(this)) .build(); mNotificationManager .notify(ClementineMediaSessionNotification.NOTIFIFCATION_ID, notification); } private void sendConnectMessageIfPossible(Intent intent) { if (intent.hasExtra(EXTRA_STRING_IP)) { final String ip = intent.getStringExtra(EXTRA_STRING_IP); final int port = intent.getIntExtra(EXTRA_INT_PORT, 0); final int auth = intent.getIntExtra(EXTRA_INT_AUTH, 0); Message msg = Message.obtain(); msg.obj = ClementineMessageFactory .buildConnectMessage(ip, port, auth, true, false); App.ClementineConnection.mHandler.sendMessage(msg); } } }