Как сохранить вибратор на неопределенное время от службы или приемника
Мне нужно предупредить пользователя об определенных событиях с помощью:
- вибрация
- уведомление
Вибрация должна оставаться на неопределенное время, пока пользователь не подтвердит уведомление.
Проблема заключается в остановке вибрации, когда устройство переходит в режим сна. Я прочитал следующие вопросы:
Разрешить вибрации телефона при выключении экрана
Продолжать вибрацию даже после того, как экран переходит в спящий режим в Android
Был ответ на один из вышеупомянутых вопросов, говорящих, что вибрация без образцов сделала успех. Поэтому я попытался назвать версию Vibrator.vibrate
это принимает миллисекунды вместо шаблона с большим числом, но вибрация все равно прекращается.
Другие ответы предлагают зарегистрировать приемник на ACTION_SCREEN_OFF
действие. Это позволило бы мне возобновить вибрацию, если устройство переходит в спящий режим после запуска тревоги, но не будет работать, если устройство уже спало.
Тем не менее, я мог бы заставить это работать, если бы мне сначала удалось включить экран, а затем зарегистрировать приемник для обработки любого события отключения экрана, которое может произойти с этого момента. Поэтому я попытался получить полную блокировку после пробуждения при получении события запуска до запуска звука или вибрации, но он не работает, несмотря на то, что я использую флаги FULL_WAKE_LOCK и ACQUIRE_CAUSES_WAKEUP. Часть wakeup работает, но вскоре после этого устройство снова переходит в режим сна. Я хотел бы думать, что флаг FULL_WAKE_LOCK не работает, потому что он устарел в API 17, но мое устройство - Samsung с 4.1.2, который является API 16!
Похоже, что рекомендуемый подход теперь использует WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, но это должно вызываться из действия, и у меня не появляется никакого экрана, если пользователь не нажимает в уведомлении, и если это происходит, звук и вибрация уже должны иметь был остановлен
Так что это похоже на тупик.
Что еще я мог попробовать?
ОБНОВИТЬ:
Мне не повезло, что экран всегда оставался включенным, но с другой стороны, они позволяют мне включать экран хотя бы на несколько секунд. На самом деле мне не нужно держать экран включенным, поэтому я регистрирую приемник на Intent.ACTION_SCREEN_OFF
действия, и когда экран гаснет, приемник снова возобновляет вибрацию. Это хорошо работало в Samsung, но теперь я перешел на Huawei, чтобы продолжить тестирование, и приемник не работает.
ОБНОВИТЬ:
Вот трассировка стека исключения в устройстве Huawei:
java.util.NoSuchElementException: Death link does not exist
at android.os.BinderProxy.unlinkToDeath(Native Method)
at com.android.server.VibratorService.unlinkVibration(VibratorService.java:294)
at com.android.server.VibratorService.removeVibrationLocked(VibratorService.java:284)
at com.android.server.VibratorService.cancelVibrate(VibratorService.java:213)
at android.os.IVibratorService$Stub.onTransact(IVibratorService.java:83)
at android.os.Binder.execTransact(Binder.java:338)
at dalvik.system.NativeStart.run(Native Method)
2 ответа
После некоторого тестирования мне наконец удалось заставить это работать.
Класс Vibrator имеет два метода:
vibrate (long[] pattern, int repeat)
vibrate (long milliseconds)
Первый - это единственный способ бесконечно вибрировать, используя API (передавая 0 в качестве второго аргумента). Но это было доказано, чтобы сломаться в некоторых устройствах (Huawei), как я разместил в вопросе. Я не говорю о том, что ОС останавливает вибрацию, когда устройство переходит в спящий режим, это было связано с использованием ресивера и блокировки пробуждения, как описано в вопросе. Я говорю об исключениях, вызванных ошибочной реализацией (класс Vibrator является абстрактным).
Второй вариант этого метода не принимает шаблон и не допускает неопределенной вибрации, но мы можем обмануть это, передав в качестве параметра очень большое количество миллисекунд. Это хорошо работает в некоторых устройствах (Huawei), как правильно указал ответ, который я привел в вопросе, но не работает в других (Samsung), где реализация имеет максимальное значение по умолчанию, которое будет использоваться вместо, если значение, переданное как параметр, превышает Это. Это максимальное значение на самом деле меньше минуты, и это означает, что мы не можем полагаться на этот подход.
Таким образом, я приложил все усилия и создал Службу, где я бесконечно вибрирую вручную, вот так:
while(vibrationActive){
Vibrator.vibrate(1000);
Thread.sleep(1000);
}
Уловка приемника, чтобы обнаружить, когда экран гаснет, больше не нужна. Конечно, ОС продолжает выключать вибратор, когда это происходит, но следующая итерация цикла возобновит вибрацию снова. При таком подходе возможно также создать своего рода шаблон, если время ожидания больше, чем время вибрации, но снова этот шаблон будет прерван в любой момент, если экран погаснет.
Специальная услуга только для надежного включения и выключения вибратора. Ты можешь в это поверить? Около 150 строк кода (без юнит-тестов) для чего-то, что должно было быть возможно с помощью нескольких строк.
Вы намерены позволить устройству перейти в спящий режим или нет? Вы можете приобрести Wakelock, который будит экран.
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
WakeLock wl = pm.newWakeLock(PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP | PowerManager.ON_AFTER_RELEASE, "myTAG");
wl.acquire(LOCK_SCREEN_TIME_MINUTES * 60 * 1000);
Это не работает для вас? После этого вы можете показать уведомление, но я не уверен в эффекте, будет ли он удерживать вибрации. Над одним работает на GalaxyTab 2 с Android 4.2.2 и HTC Hero с Android 2.3.4.