12

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.

Community
  • 1
  • 1
Persson
  • 422
  • 4
  • 21
  • Which device are you testing on? – Natig Babayev Dec 31 '19 at 09:45
  • 1
    What about using a notification with a High priority channel? – Saurabh Jan 03 '20 at 06:08
  • You should not poll your website for updates all the time. This will use a lot of data on the device, drains the battery and also creates a lot of load on your website. Better solution would be to send a notification from server to your app when there is an update available. – Ridcully Jan 07 '20 at 15:24
  • @Persson Has any of the posts answered your question? – Anatolii Jan 11 '20 at 14:21

4 Answers4

3

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();
                }
            }
        }
    });
}
2

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.

Anatolii
  • 14,139
  • 4
  • 35
  • 65
1

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/

Roberto Gonçalves
  • 3,186
  • 4
  • 13
  • 27
  • I believe you're answering a different question here. The OP states that his update service doesn't cause his device to vibrate when the screen is off. However, what you're answering is how to initiate a vibration if Intent.ACTION_SCREEN_OFF is received. – Anatolii Jan 07 '20 at 09:28
0

From your explanation

I have a service that checks for updates on my website

you check for updates to your website at some frequency. Make sure you acquire wake lock before you try connecting to the server as your application won't otherwise your application will not be able to connect to internet.

You can also try programmatically turning on screen by displaying notification or by following this answer in case you are not able to acquire wake lock when screen is off.

I believe that problem may be caused because you might be trying to acquire wake lock after you detect an update.

Mayank Kumar Chaudhari
  • 16,027
  • 10
  • 55
  • 122