package com.kuxhausen.huemore.net;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Binder;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.InboxStyle;
import android.util.Pair;
import com.kuxhausen.huemore.DecodeErrorActivity;
import com.kuxhausen.huemore.NavigationDrawerActivity;
import com.kuxhausen.huemore.OnActiveMoodsChangedListener;
import com.kuxhausen.huemore.R;
import com.kuxhausen.huemore.automation.FireReceiver;
import com.kuxhausen.huemore.automation.VoiceInputReceiver;
import com.kuxhausen.huemore.persistence.Definitions.InternalArguments;
import com.kuxhausen.huemore.persistence.FutureEncodingException;
import com.kuxhausen.huemore.persistence.HueUrlEncoder;
import com.kuxhausen.huemore.persistence.InvalidEncodingException;
import com.kuxhausen.huemore.persistence.Utils;
import com.kuxhausen.huemore.state.DatabaseGroup;
import com.kuxhausen.huemore.state.Group;
import com.kuxhausen.huemore.state.GroupMoodBrightness;
import com.kuxhausen.huemore.state.Mood;
import com.kuxhausen.huemore.state.NfcGroup;
import com.kuxhausen.huemore.utils.DeferredLog;
import com.kuxhausen.huemore.voice.SpeechParser;
import java.util.List;
public class ConnectivityService extends Service implements OnActiveMoodsChangedListener {
/**
* Class used for the client Binder. Because we know this service always runs in the same process
* as its clients, we don't need to deal with IPC.
*/
public class LocalBinder extends Binder {
public ConnectivityService getService() {
// Return this instance of LocalService so clients can call public methods
return ConnectivityService.this;
}
}
// Binder given to clients
private final IBinder mBinder = new LocalBinder();
private final static int notificationId = 1337;
private LifecycleController mLifecycleController;
@Override
public void onCreate() {
super.onCreate();
mLifecycleController = new LifecycleController(this, this);
}
@Override
/**
* Called after onCreate when service attaching to Activity(s)
*/
public IBinder onBind(Intent intent) {
synchronized (mLifecycleController) {
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.DEAD) {
mLifecycleController.onStartNapping();
}
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.NAPPING) {
mLifecycleController.onStartWorking();
}
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.WORKING) {
mLifecycleController.onStartBound();
}
}
return mBinder;
}
@Override
public boolean onUnbind(Intent intent) {
super.onUnbind(intent);
synchronized (mLifecycleController) {
if (mLifecycleController.getLifecycleState()
== LifecycleController.LifecycleState.BOUND_TO_UI) {
mLifecycleController.onStopBound();
}
}
return true; // ensures onRebind is called
}
@Override
public void onRebind(Intent intent) {
super.onRebind(intent);
synchronized (mLifecycleController) {
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.DEAD) {
mLifecycleController.onStartNapping();
}
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.NAPPING) {
mLifecycleController.onStartWorking();
}
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.WORKING) {
mLifecycleController.onStartBound();
}
}
}
@Override
/**
* Called after onCreate when service (re)started independently
*/
public int onStartCommand(Intent intent, int flags, int startId) {
if (intent != null) {
synchronized (mLifecycleController) {
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.DEAD) {
mLifecycleController.onStartNapping();
}
if (mLifecycleController.getLifecycleState()
== LifecycleController.LifecycleState.NAPPING) {
mLifecycleController.onStartWorking();
}
}
// remove any possible launched wakelocks
ConnectivityServiceLauncher.completeWakefulIntent(intent);
FireReceiver.completeWakefulIntent(intent);
VoiceInputReceiver.completeWakefulIntent(intent);
String encodedMood = null;
String groupName = null;
String moodName = null;
Integer maxBri = null;
Bundle extras = intent.getExtras();
if (extras != null) {
if (extras.getBoolean(InternalArguments.FLAG_CANCEL_PLAYING, false)) {
mLifecycleController.getMoodPlayer().cancelAllMoods();
}
if (extras.containsKey(InternalArguments.VOICE_INPUT) || extras.containsKey(
InternalArguments.VOICE_INPUT_LIST)) {
String best = extras.getString(InternalArguments.VOICE_INPUT);
List<String> candidates = extras.getStringArrayList(InternalArguments.VOICE_INPUT_LIST);
float[]
confidences =
extras.getFloatArray(InternalArguments.VOICE_INPUT_CONFIDENCE_ARRAY);
GroupMoodBrightness parsed = SpeechParser.parse(this, best, candidates, confidences);
groupName = parsed.group;
moodName = parsed.mood;
maxBri = parsed.brightness;
}
if (extras.containsKey(InternalArguments.ENCODED_MOOD)) {
encodedMood = intent.getStringExtra(InternalArguments.ENCODED_MOOD);
}
if (extras.containsKey(InternalArguments.GROUP_NAME)) {
groupName = intent.getStringExtra(InternalArguments.GROUP_NAME);
}
if (extras.containsKey(InternalArguments.MOOD_NAME)) {
moodName = intent.getStringExtra(InternalArguments.MOOD_NAME);
}
if (extras.containsKey(InternalArguments.MAX_BRIGHTNESS)) {
maxBri = intent.getIntExtra(InternalArguments.MAX_BRIGHTNESS, 0);
}
}
if (encodedMood != null) {
try {
Pair<Integer[], Pair<Mood, Integer>> moodPairs = HueUrlEncoder.decode(encodedMood);
if (moodPairs.second.first != null) {
Group g = new NfcGroup(moodPairs.first, groupName, this);
moodName = (moodName == null) ? "Unknown Mood" : moodName;
getMoodPlayer().playMood(g, moodPairs.second.first, moodName, moodPairs.second.second);
}
} catch (InvalidEncodingException e) {
Intent i = new Intent(this, DecodeErrorActivity.class);
i.putExtra(InternalArguments.DECODER_ERROR_UPGRADE, false);
startActivity(i);
} catch (FutureEncodingException e) {
Intent i = new Intent(this, DecodeErrorActivity.class);
i.putExtra(InternalArguments.DECODER_ERROR_UPGRADE, true);
startActivity(i);
}
} else if (moodName != null && groupName != null) {
Group group = DatabaseGroup.load(groupName, this);
Mood mood = Utils.getMoodFromDatabase(moodName, this);
if (group == null) {
DeferredLog.e("ConnectivityService", "Failed to load group: %s", groupName);
} else if (mood == null) {
DeferredLog.e("ConnectivityService", "Failed to load mood: %s", moodName);
} else {
getMoodPlayer().playMood(group, mood, moodName, maxBri);
}
}
}
return super.onStartCommand(intent, flags, startId);
}
public MoodPlayer getMoodPlayer() {
return mLifecycleController.getMoodPlayer();
}
public DeviceManager getDeviceManager() {
return mLifecycleController.getDeviceManager();
}
public void onActiveMoodsChanged() {
if (getMoodPlayer().getPlayingMoods().isEmpty()) {
this.stopForeground(true);
} else {
// Creates an explicit intent for an Activity in your app
Intent openI = new Intent(this, NavigationDrawerActivity.class);
openI.putExtra(InternalArguments.FLAG_SHOW_NAV_DRAWER, true);
PendingIntent
openPI = PendingIntent.getActivity(this, 0, openI, PendingIntent.FLAG_UPDATE_CURRENT);
// create basic compatibility notification
NotificationCompat.Builder mBuilder =
new NotificationCompat.Builder(this).setSmallIcon(R.drawable.ic_notification_whiteshade)
.setContentTitle(this.getResources().getString(R.string.app_name))
.setContentText(getMoodPlayer().getPlayingMoods().get(0).toString())
.setContentIntent(openPI);
// now create rich notification for supported devices
List<PlayingMood> playing = getMoodPlayer().getPlayingMoods();
InboxStyle iStyle = new NotificationCompat.InboxStyle();
for (int i = 0; (i < 5 && i < playing.size()); i++) {
iStyle.addLine(playing.get(i).toString());
}
if (playing.size() > 5) {
iStyle.setSummaryText("+" + (playing.size() - 5) + " "
+ this.getResources().getString(R.string.notification_overflow_more));
}
mBuilder.setStyle(iStyle);
//now add cancel button for supported devices
Intent stopI = new Intent(this, ConnectivityService.class);
stopI.putExtra(InternalArguments.FLAG_CANCEL_PLAYING, true);
PendingIntent
stopPI = PendingIntent.getService(this, 0, stopI, PendingIntent.FLAG_UPDATE_CURRENT);
String
message =
(playing.size() == 1) ? getResources().getString(R.string.action_stop_all)
: getResources().getString(R.string.action_stop);
mBuilder.addAction(R.drawable.ic_action_discard, message, stopPI);
this.startForeground(notificationId, mBuilder.build());
}
}
@Override
public void onDestroy() {
synchronized (mLifecycleController) {
if (mLifecycleController.getLifecycleState()
== LifecycleController.LifecycleState.BOUND_TO_UI) {
mLifecycleController.onStopBound();
}
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.WORKING) {
mLifecycleController.onStopWorking();
}
if (mLifecycleController.getLifecycleState() == LifecycleController.LifecycleState.NAPPING) {
mLifecycleController.onStopNappng();
}
}
super.onDestroy();
}
}