Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Phone vibration stops working after screen is off / locked

I have a service that checks for updates on my website and I would like it to be able to cause a vibration when a update is found even if the screen is off or locked.

The vibration currently only works if the screen is not off / locked. All other functions does work even if the screen is off / locked.

Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
   long[] pattern = new long[]{0, 400, 200, 400};
   if (Build.VERSION.SDK_INT >= 26) { // New API
     vibrator.vibrate(VibrationEffect.createWaveform(pattern,0));
   } else { // Old API
     vibrator.vibrate(pattern, 0);
}

How can I vibrate a phone even if its screen is off? I've tried to use a WakeLock but that doesn't seem to be the problem?

I have all the permissions set, as its working when the screen is on.

like image 743
Persson Avatar asked Nov 10 '19 15:11

Persson


3 Answers

Either you use a service or life cycle methods, you will need create a loop cycle so that your vibrator restarts when finished the pattern you defined.

I did it as follows using a handler.

Handler handlerVibrate;
Vibrator vibrator;
private void vibrate(){
    // this handler will keep vibrator running even with screen off (vibrator stops when the phone has the screen off)
    vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
    handlerVibrate = new Handler();
    handlerVibrate.post(new Runnable() {
        @Override
        public void run() {
            if(vibrator != null) {
                Log.d("mainActivity", "handler");
                long[] pattern = {0, 500, 500};
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
                    vibrator.vibrate(VibrationEffect.createWaveform(pattern, 0)); // repeat at index 0
                } else {
                    //deprecated in API 26
                    vibrator.vibrate(pattern, 0); // repeat at index 0
                }
            }
            if(CONDITIONS TO STOP/CONTINUE){
                // keep launching so that it keeps vibrating even with the screen off
                handlerVibrate.postDelayed(this, 1000);
            } else {
                if(vibrator != null) {
                    vibrator.cancel();
                }
            }
        }
    });
}
like image 163
Javier Castellanos Cruz Avatar answered Oct 13 '22 16:10

Javier Castellanos Cruz


Given this:

I have a service that checks for updates on my website and I would like it to be able to cause a vibration when a update is found even if the screen is off or locked.

And this:

How can I vibrate a phone even if its screen is off? I've tried to use a WakeLock but that doesn't seem to be the problem?

I draw a conclusion that your service works without problems when your app is in the foreground but not when it's in the background. So, it's most likely your target Android API is 26 and higher.

In this case, your update service stops working after some time due to the background limitations imposed by the OS. As the update service no longer runs, obviously, it cannot initiate any vibration.

Background Execution Limits for API >= 26

Starting from Android 8.0 there are limitations on what apps can run when they're in the background. So, your service will work for a certain time and then it will be killed by the system as stated in the documentation (please read more here):

When an app goes into the background, it has a window of several minutes in which it is still allowed to create and use services. At the end of that window, the app is considered to be idle. At this time, the system stops the app's background services, just as if the app had called the services' Service.stopSelf() methods.

Hence, if you want your service to continue operating while the app is in the background you still have options. Some of them are the following

WorkManager

The WorkManager API makes it easy to schedule deferrable, asynchronous tasks that are expected to run even if the app exits or device restarts.

Replace your service with a WorkManager, which is run periodically to query your server.

JobScheduler

Well, you don't really need it if you use WorkManager. It will be used by WorkManager internally if your device API is 23 and later.

Foreground Service

Convert your service to a foreground service so that the app is considered in foreground during its execution. But in this case, it has to be active the whole time you're performing your update checks, so maybe it's not the best solution. Please read more here.

Conclusion

Keep in mind that background services should be replaced with WorkManager or Foreground Service starting from API 26. Otherwise, they may not perform as intended as the system kills them after a certain amount of time.

like image 42
Anatolii Avatar answered Oct 13 '22 16:10

Anatolii


You can try to add a receiver to control the screen logic on/off:

public class ScreenReceiver extends BroadcastReceiver {

    public static boolean wasScreenOn = true;

    @Override
    public void onReceive(Context context, Intent intent) {
        if (intent.getAction().equals(Intent.ACTION_SCREEN_OFF)) {
            // DO WHATEVER YOU NEED TO DO HERE
            Vibrator v = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
            long[] pattern = new long[]{0, 400, 200, 400};
            // Only perform this pattern one time (-1 means "do not repeat")
            v.vibrate(pattern, -1);
            wasScreenOn = false;
        } else if (intent.getAction().equals(Intent.ACTION_SCREEN_ON)) {
            // AND DO WHATEVER YOU NEED TO DO HERE
            wasScreenOn = true;
        }
    }

}

public class ExampleActivity extends Activity {

   @Override
    protected void onCreate() {
        // INITIALIZE RECEIVER
        IntentFilter filter = new IntentFilter(Intent.ACTION_SCREEN_ON);
        filter.addAction(Intent.ACTION_SCREEN_OFF);
        BroadcastReceiver mReceiver = new ScreenReceiver();
        registerReceiver(mReceiver, filter);
        // YOUR CODE
    }

    @Override
    protected void onPause() {
        // WHEN THE SCREEN IS ABOUT TO TURN OFF
        if (ScreenReceiver.wasScreenOn) {
            // THIS IS THE CASE WHEN ONPAUSE() IS CALLED BY THE SYSTEM DUE TO A SCREEN STATE CHANGE
            System.out.println("SCREEN TURNED OFF");
        } else {
            // THIS IS WHEN ONPAUSE() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
        }
        super.onPause();
    }

    @Override
    protected void onResume() {
        // ONLY WHEN SCREEN TURNS ON
        if (!ScreenReceiver.wasScreenOn) {
            // THIS IS WHEN ONRESUME() IS CALLED DUE TO A SCREEN STATE CHANGE
            System.out.println("SCREEN TURNED ON");
        } else {
            // THIS IS WHEN ONRESUME() IS CALLED WHEN THE SCREEN STATE HAS NOT CHANGED
        }
        super.onResume();
    }

}

Here is an activity lifecycle: Activity Lifecycle

Reference:

https://thinkandroid.wordpress.com/2010/01/24/handling-screen-off-and-screen-on-intents/

like image 1
Roberto Gonçalves Avatar answered Oct 13 '22 16:10

Roberto Gonçalves