I am creating an sleep timer app that gradually lowers the system volume as time goes by. If the user sets the time to 30 minutes then the volume will decrease at 15 minutes then 7.5 minutes, etc.
I currently have the volume decay being sent to a JobService which works just fine when it's up on my phone screen, but once I lock my phone and it's left in the background it will work for a few minutes tops. I have also tried using Service and IntentService, both yielding similar results.
I wonder if these threads think they are finished even though they are not, since I am using a handle.postDelayed which calls itself.
Why doesn't it run in the background for more than a few minutes? Do I need to have a checker inside of onStartJob to see if startVolumeDecrement is finished?
Below is relevant code (full code at very bottom of post):
MainActivity.java
package com.example.dreamquiet;
//imports
/.../
public class MainActivity extends AppCompatActivity {
//Methods:
@Override
protected void onCreate(Bundle savedInstanceState) { //method that does it all!
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
startDecay()
}
public void startDecay(){
//this is used to pass the time to the job.
PersistableBundle bundle = new PersistableBundle();
bundle.putInt("totsleep", totalSleepTime);
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
JobInfo jobInfo = new JobInfo.Builder(11, new ComponentName(this, StartDecayJob.class))
// only add if network access is required
.setRequiredNetworkType(JobInfo.NETWORK_TYPE_ANY)
.setExtras(bundle) //this is used to pass the time to the job.
.build();
jobScheduler.schedule(jobInfo);
}
public void stopDecay(){
JobScheduler jobScheduler = (JobScheduler) getSystemService(Context.JOB_SCHEDULER_SERVICE);
jobScheduler.cancelAll();
}
StartDecayJob.java
package com.example.dreamquiet;
//bunch of imports
/..../
public class StartDecayJob extends JobService {
protected int counter;
protected int totalSleepTime;
protected boolean isDone;
final Handler handleDecay = new Handler();
@Override
public boolean onStartJob(JobParameters jobParameters) {
totalSleepTime = jobParameters.getExtras().getInt("totsleep");
startVolumeDecrement();
return true;
}
@Override
public boolean onStopJob(JobParameters jobParameters) {
handleDecay.removeCallbacksAndMessages(null); //wipe out the current startVolumeDecrement
super.onDestroy();
stopSelf();
Toast.makeText(this, "Goodnight :)", Toast.LENGTH_LONG).show();
// return true to restart the job
return false;
}
//the method that lowers the volume over time.
protected void startVolumeDecrement() {
//get the audio
final AudioManager phoneVolume = (AudioManager) getApplicationContext().getSystemService(Context.AUDIO_SERVICE);
int currentVolume = phoneVolume.getStreamVolume(AudioManager.STREAM_MUSIC);
//get the vector. Yes it's ugly. enjoy it.
Vector decayVector = new Vector();
decayVector = populateDecay(totalSleepTime, decayVector, currentVolume);
final Vector decay = decayVector;
counter = 0; //Reset the counter --> safe :P
final Runnable volDecay = new Runnable(){
public void run(){
if(counter >= decay.size()-1 || phoneVolume.getStreamVolume(AudioManager.STREAM_MUSIC) == 0){
//if current volume isn't muted then MUTE IT!
if(phoneVolume.getStreamVolume(AudioManager.STREAM_MUSIC) > 0){
counter--;
handleDecay.postDelayed(this, ((int)decay.get(decay.size()-1)));
}
//If using spotify, it will pause it! :)
Intent pauseSpotify = new Intent("com.spotify.mobile.android.ui.widget.PLAY");
pauseSpotify.setPackage("com.spotify.music");
sendBroadcast(pauseSpotify);
return; //return is redundant but why not...
}
//decrease volume then recall function
phoneVolume.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, AudioManager.FLAG_SHOW_UI);
handleDecay.postDelayed(this, ((int) decay.get(counter++)));
}
};
handleDecay.postDelayed(volDecay, ((int) decay.get(counter++))); //start the initial counter
}
}
My manifest file
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.dreamquiet" >
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"></uses-permission> <!--preventitive measures-->
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme" >
<activity android:name=".MainActivity" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
<!-- intent defined in this service tag below -->
<service android:name=".StartDecayJob"
android:label="decay"
android:permission="android.permission.BIND_JOB_SERVICE"/> <!--Gotta see if process will work -->
</application>
</manifest>