package com.kuxhausen.huemore.net; import com.google.gson.Gson; import android.content.ContentValues; import android.content.Context; import android.database.Cursor; import android.os.SystemClock; import android.support.annotation.NonNull; import android.util.Pair; import com.kuxhausen.huemore.OnActiveMoodsChangedListener; import com.kuxhausen.huemore.persistence.Definitions; 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.BulbState; import com.kuxhausen.huemore.state.Group; import com.kuxhausen.huemore.state.Mood; import com.kuxhausen.huemore.state.SyntheticGroup; import java.util.ArrayList; import java.util.List; import alt.android.os.CountDownTimer; public class MoodPlayer { private Gson gson = new Gson(); private Context mContext; private DeviceManager mDeviceManager; private ArrayList<OnActiveMoodsChangedListener> moodsChangedListeners = new ArrayList<OnActiveMoodsChangedListener>(); private ArrayList<PlayingMood> mPlayingMoods = new ArrayList<PlayingMood>(); private CountDownTimer countDownTimer; public MoodPlayer(Context c, DeviceManager m) { mContext = c; mDeviceManager = m; restoreNappingMoods(); } public void playMood(@NonNull Group g, Mood m, String mName, Integer maxBri) { playMood(g, m, mName, maxBri, SystemClock.elapsedRealtime(), null); } public synchronized void playMood(@NonNull Group g, Mood m, String mName, Integer maxBri, Long savedStartTime, Long savedProgress) { if (g == null) { throw new IllegalArgumentException(); } PlayingMood pm = new PlayingMood(m, mName, g, savedStartTime, Utils.getDayStartElapsedRealTimeMillis(), savedProgress); for (int i = 0; i < mPlayingMoods.size(); i++) { if (mPlayingMoods.get(i).getGroup().conflictsWith(pm.getGroup())) { //unschedule any conflicting moods mPlayingMoods.remove(i); i--; } } BrightnessManager briManager = mDeviceManager.obtainBrightnessManager(g); briManager.setPolicy(BrightnessManager.BrightnessPolicy.VOLUME_BRI); if (maxBri != null) { briManager.setVolumeWithoutUpdate(maxBri); } mPlayingMoods.add(pm); // update notifications onActiveMoodsChanged(); mDeviceManager.onStateChanged(); } public synchronized void cancelMood(Group g) { if (g == null) { throw new IllegalArgumentException(); } for (int i = 0; i < mPlayingMoods.size(); i++) { if (mPlayingMoods.get(i).getGroup().equals(g)) { //unschedule mPlayingMoods.remove(i); i--; } } mDeviceManager.obtainBrightnessManager(g) .setPolicy(BrightnessManager.BrightnessPolicy.DIRECT_BRI); // update notifications onActiveMoodsChanged(); mDeviceManager.onStateChanged(); } public synchronized void cancelAllMoods() { for (int i = mPlayingMoods.size() - 1; i >= 0; i--) { //unschedule mPlayingMoods.remove(i); } } public void addOnActiveMoodsChangedListener(OnActiveMoodsChangedListener l) { moodsChangedListeners.add(l); } public void removeOnActiveMoodsChangedListener(OnActiveMoodsChangedListener l) { moodsChangedListeners.remove(l); } public void onActiveMoodsChanged() { for (OnActiveMoodsChangedListener l : moodsChangedListeners) { l.onActiveMoodsChanged(); } } public void onDestroy() { if (nextEventTime() != null) { saveNappingMoods(); } if (countDownTimer != null) { countDownTimer.cancel(); } } public synchronized void tick() { for (PlayingMood pm : getPlayingMoods()) { List<Pair<List<Long>, BulbState>> toDo = pm.tick(SystemClock.elapsedRealtime()); for (Pair<List<Long>, BulbState> eachBatch : toDo) { BulbState toSend = eachBatch.second; for (Long id : eachBatch.first) { mDeviceManager.obtainBrightnessManager(pm.getGroup()) .setState(mDeviceManager.getNetworkBulb(id), toSend); } } } boolean activeMoodsChanged = false; for (int i = 0; i < mPlayingMoods.size(); i++) { if (!mPlayingMoods.get(i).hasFutureEvents()) { PlayingMood pm = mPlayingMoods.get(i); mPlayingMoods.remove(i); i--; mDeviceManager.obtainBrightnessManager(pm.getGroup()) .setPolicy(BrightnessManager.BrightnessPolicy.DIRECT_BRI); // update notifications activeMoodsChanged = true; } } if (activeMoodsChanged) { onActiveMoodsChanged(); } } /** * @return next event time in milliseconds measured in SystemClock.getRealtime. Will return null * if no future events */ public synchronized Long nextEventTime() { Long result = Long.MAX_VALUE; for (PlayingMood pm : getPlayingMoods()) { if (pm.hasFutureEvents()) { if (pm.getNextEventInCurrentMillis() < result) { result = pm.getNextEventInCurrentMillis(); } } } if (result == Long.MAX_VALUE) { return null; } else { return result; } } /** * to save power, service is shutting down so ongoing moods should be schedule to be restarted in * time for their next events */ private synchronized void saveNappingMoods() { long awakenTime = nextEventTime(); awakenTime -= LifecycleController.MILIS_AWAKEN_STARTUP_TIME; mContext.getContentResolver().delete(Definitions.PlayingMood.URI, null, null); for (PlayingMood pm : mPlayingMoods) { ContentValues cv = new ContentValues(); cv.put(Definitions.PlayingMood.COL_GROUP_VALUE, gson.toJson(SyntheticGroup.asSynthetic( pm.getGroup()))); cv.put(Definitions.PlayingMood.COL_MOOD_NAME, pm.getMoodName()); cv.put(Definitions.PlayingMood.COL_MOOD_VALUE, HueUrlEncoder.encode(pm.getMood())); cv.put(Definitions.PlayingMood.COL_MOOD_BRI, mDeviceManager.obtainBrightnessManager(pm.getGroup()).getBrightness()); cv.put(Definitions.PlayingMood.COL_MILI_TIME_STARTED, pm.getStartTime()); cv.put(Definitions.PlayingMood.COL_INTERNAL_PROGRESS, pm.getInternalProgress()); mContext.getContentResolver().insert(Definitions.PlayingMood.URI, cv); } ConnectivityServiceLauncher.scheduleInternalAlarm(mContext, awakenTime); } private synchronized void restoreNappingMoods() { String[] projectionColumns = {Definitions.PlayingMood.COL_GROUP_VALUE, Definitions.PlayingMood.COL_MOOD_NAME, Definitions.PlayingMood.COL_MOOD_VALUE, Definitions.PlayingMood.COL_MOOD_BRI, Definitions.PlayingMood.COL_MILI_TIME_STARTED, Definitions.PlayingMood.COL_INTERNAL_PROGRESS }; Cursor cursor = mContext.getContentResolver().query(Definitions.PlayingMood.URI, projectionColumns, null, null, null); cursor.moveToPosition(-1);// not the same as move to first! while (cursor.moveToNext()) { SyntheticGroup g = gson.fromJson(cursor.getString(0), SyntheticGroup.class); String mName = cursor.getString(1); Mood m = null; try { m = HueUrlEncoder.decode(cursor.getString(2)).second.first; } catch (InvalidEncodingException e) { } catch (FutureEncodingException e) { } Integer moodBri = cursor.getInt(3); Long miliTimeStarted = cursor.getLong(4); Long colInternalProgress = cursor.getLong(5); this.playMood(g, m, mName, moodBri, miliTimeStarted, colInternalProgress); } cursor.close(); mContext.getContentResolver().delete(Definitions.PlayingMood.URI, null, null); } public synchronized List<PlayingMood> getPlayingMoods() { return mPlayingMoods; } }