1

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>

Full code here

Austin
  • 11
  • 1
  • I don't see any problem with your code though I can certainly say that the JobService would only run for 10 minutes then stopped self irrespective of whether you are foreground or background. – anshul Dec 31 '19 at 10:05

1 Answers1

0

Because when you lock your phone os will pause the running service if many services running in different application and same issue i face when i was build quiz app.So solution is you can use WAKE LOCK it solved the problem.

In activity :

    private var wakeLock: PowerManager.WakeLock? = null

To start the wake lock :

    var powerManager = getSystemService(POWER_SERVICE) as PowerManager
    wakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
            "ExampleApp:Wakelock Started")
    wakeLock?.acquire()

Most important step to release the wake lock :

  if (wakeLock?.isHeld!!) {
            L.e("ExampleApp", "Wakelock Released")
            wakeLock?.release();
        }

At last add permission in manifest :

  <uses-permission android:name="android.permission.WAKE_LOCK" />
haresh
  • 1,424
  • 2
  • 12
  • 18