package com.kuxhausen.huemore.net; import android.app.Service; import android.content.Context; import android.os.PowerManager; import android.os.SystemClock; import com.kuxhausen.huemore.OnActiveMoodsChangedListener; import com.kuxhausen.huemore.R; import alt.android.os.CountDownTimer; public class LifecycleController { /** * How long in milis before the next event the ExecutorService should begin waking back up */ public static final long MILIS_AWAKEN_STARTUP_TIME = 1500l; public static final long TICKS_PER_SECOND = 10l; public static final long EMPTY_CONSECUTIVE_TICKS_TILL_SLEEP = 50; public static final long MINIMUM_NAP_MILLISECONDS = ((EMPTY_CONSECUTIVE_TICKS_TILL_SLEEP * 1000) / TICKS_PER_SECOND) + MILIS_AWAKEN_STARTUP_TIME; private Service mContext; private OnActiveMoodsChangedListener mMoodsListener; private LifecycleState mLifecycleState; private PowerManager.WakeLock mWakeLock; private DeviceManager mDeviceManager; private MoodPlayer mMoodPlayer; private InternalClock mInternalClock; public LifecycleController(Service c, OnActiveMoodsChangedListener moodsListener) { mContext = c; mMoodsListener = moodsListener; mLifecycleState = LifecycleState.NAPPING; } public synchronized DeviceManager getDeviceManager() { if (mLifecycleState == LifecycleState.BOUND_TO_UI || mLifecycleState == LifecycleState.WORKING) { return mDeviceManager; } else { throw new IllegalStateException(); } } public synchronized MoodPlayer getMoodPlayer() { if (mLifecycleState == LifecycleState.BOUND_TO_UI || mLifecycleState == LifecycleState.WORKING) { return mMoodPlayer; } else { throw new IllegalStateException(); } } public synchronized LifecycleState getLifecycleState() { return mLifecycleState; } public synchronized void onStartNapping() { //Log.i("lifecycle", "onStartNapping"); if (mLifecycleState != LifecycleState.DEAD) { throw new IllegalStateException(); } mLifecycleState = LifecycleState.NAPPING; } public synchronized void onStartWorking() { //Log.i("lifecycle", "onStartWorking"); if (mLifecycleState != LifecycleState.NAPPING) { throw new IllegalStateException(); } PowerManager pm = (PowerManager) mContext.getSystemService(Context.POWER_SERVICE); mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, mContext.getString(R.string.app_name)); mWakeLock.acquire(); mDeviceManager = new DeviceManager(mContext); mMoodPlayer = new MoodPlayer(mContext, mDeviceManager); mInternalClock = new InternalClock(); mInternalClock.start(); mLifecycleState = LifecycleState.WORKING; mMoodPlayer.addOnActiveMoodsChangedListener(mMoodsListener); mMoodsListener.onActiveMoodsChanged(); } public synchronized void onStartBound() { //Log.i("lifecycle", "onStartBound"); if (mLifecycleState != LifecycleState.WORKING) { throw new IllegalStateException(); } mLifecycleState = LifecycleState.BOUND_TO_UI; } public synchronized void onStopBound() { //Log.i("lifecycle", "onStopBound"); if (mLifecycleState != LifecycleState.BOUND_TO_UI) { throw new IllegalStateException(); } // Prevent the app from sleeping as the user may be launching into another bound activity. mInternalClock.delaySleep(); mLifecycleState = LifecycleState.WORKING; } public synchronized void onStopWorking() { //Log.i("lifecycle", "onStopWorking"); if (mLifecycleState != LifecycleState.WORKING) { throw new IllegalStateException(); } mInternalClock.cancel(); mInternalClock = null; mMoodPlayer.onDestroy(); mMoodPlayer = null; mDeviceManager.onDestroy(); mDeviceManager = null; mLifecycleState = LifecycleState.NAPPING; mWakeLock.release(); mWakeLock = null; } public synchronized void onStopNappng() { //Log.i("lifecycle", "onStopNapping"); mLifecycleState = LifecycleState.DEAD; } public enum LifecycleState { DEAD, NAPPING, WORKING, BOUND_TO_UI } public class InternalClock extends CountDownTimer { long ticksTillSleep = LifecycleController.EMPTY_CONSECUTIVE_TICKS_TILL_SLEEP; public InternalClock() { super(Long.MAX_VALUE, (1000l / TICKS_PER_SECOND)); } /** * Delay the InternalClock from calling onStopWorking for the next second. */ public void delaySleep() { ticksTillSleep += TICKS_PER_SECOND; } /** * Callback fired on regular interval. * * @param millisUntilFinished The amount of time until finished. */ @Override public void onTick(long millisUntilFinished) { synchronized (LifecycleController.this) { synchronized (mMoodPlayer) { //every tenth of a second, pump mood player (which will in turn pump playing moods) mMoodPlayer.tick(); //Log.i("wtf", "tick"); //also check device manager & mood player to see if can sleep if (mMoodPlayer.nextEventTime() == null || mMoodPlayer.nextEventTime() > ( SystemClock.elapsedRealtime() + LifecycleController.MINIMUM_NAP_MILLISECONDS)) { ticksTillSleep--; //Log.i("wtf", "NextEventTime null or > (elapsedRealtime+MINIMUM_NAP_MILLIS)"); if (ticksTillSleep < 0) { //Log.i("wtf","(ticksTillSleep < LifecycleController.EMPTY_CONSECUTIVE_TICKS_TILL_SLEEP"); if (LifecycleController.this.getLifecycleState() == LifecycleState.WORKING) { //Log.i("wtf", "currently Working"); LifecycleController.this.onStopWorking(); } } } else { ticksTillSleep = LifecycleController.EMPTY_CONSECUTIVE_TICKS_TILL_SLEEP; } } } } /** * Callback fired when the time is up. */ @Override public void onFinish() { } } }