Avatar ·

Как заставить Android устройство вибрировать с разной частотой и длительностью в Kotlin?

Всем привет! Я начинающий Android-разработчик на Kotlin. Пишу простое приложение-будильник, и мне нужно добавить вибрацию. Проблема в том, что я хочу, чтобы вибрация была не просто одинаковой, а менялась: сначала короткие импульсы, потом длинные, с разной частотой. Как это сделать?

Сейчас у меня есть такой код, но он работает очень примитивно — просто включает вибрацию на фиксированное время:

class MainActivity : AppCompatActivity() {
    private lateinit var vibrator: Vibrator

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
        vibrator.vibrate(1000) // просто вибрирует 1 секунду
    }
}

Я читал про VibrationEffect и VibratorManager в новых API, но запутался. Есть ли способ создать сложный паттерн вибрации? Например, 200 мс вибрация, 100 мс пауза, 500 мс вибрация, 200 мс пауза, и так несколько раз. И можно ли управлять амплитудой (силой) вибрации?

У меня API 29+ (Android 10). Заранее спасибо за помощь!

<<>>

Использование VibrationEffect для сложных паттернов вибрации

Да, вы на правильном пути! Начиная с Android 8.0 (API 26), Google представила VibrationEffect, который позволяет создавать сложные паттерны вибрации с контролем длительности, пауз и амплитуды. Для API 29+ (Android 10) доступен ещё более продвинутый VibratorManager.

1. Базовый паттерн (массив длительностей)

Самый простой способ — использовать VibrationEffect.createWaveform() с массивом длительностей в миллисекундах. Нечётные элементы — вибрация, чётные — паузы:

class MainActivity : AppCompatActivity() {
    private lateinit var vibrator: Vibrator

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
        
        // Паттерн: 200мс вибро, 100мс пауза, 500мс вибро, 200мс пауза
        val pattern = longArrayOf(200, 100, 500, 200)
        
        // -1 означает "не повторять"
        vibrator.vibrate(VibrationEffect.createWaveform(pattern, -1))
    }
}

2. Паттерн с контролем амплитуды (силы вибрации)

Чтобы управлять силой вибрации (от 1 до 255, где 255 — максимальная), используйте версию createWaveform() с двумя массивами:

fun vibrateWithAmplitude() {
    val timings = longArrayOf(200, 100, 500, 200) // длительности
    val amplitudes = intArrayOf(255, 0, 128, 0)   // амплитуды: 0 = пауза
    
    val effect = VibrationEffect.createWaveform(timings, amplitudes, -1)
    vibrator.vibrate(effect)
}

Важно: амплитуда 0 означает отсутствие вибрации (паузу), поэтому массив amplitudes должен совпадать по длине с timings. Для пауз ставьте 0.

3. Повторяющийся паттерн

Чтобы паттерн повторялся, укажите индекс, с которого начинать повтор (обычно 0):

fun repeatingPattern() {
    val pattern = longArrayOf(100, 50, 100, 50, 200, 100, 200, 100)
    // Повторять с начала (индекс 0)
    val effect = VibrationEffect.createWaveform(pattern, 0)
    vibrator.vibrate(effect)
}

// Чтобы остановить вибрацию:
fun stopVibration() {
    vibrator.cancel()
}

4. Использование VibratorManager (API 29+)

На Android 10+ рекомендуется использовать VibratorManager для лучшей совместимости:

class MainActivity : AppCompatActivity() {
    private lateinit var vibratorManager: VibratorManager
    private lateinit var vibrator: Vibrator

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        
        vibratorManager = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
        vibrator = vibratorManager.defaultVibrator
        
        // Пример: сложный паттерн для будильника
        val alarmPattern = longArrayOf(
            500, 200,  // длинная вибрация + пауза
            200, 100,  // короткая вибрация + пауза
            500, 200,
            200, 100,
            1000, 500  // очень длинная + пауза
        )
        
        val effect = VibrationEffect.createWaveform(alarmPattern, 0)
        vibrator.vibrate(effect)
    }
}

5. Проверка наличия вибратора

Всегда проверяйте, поддерживает ли устройство вибрацию:

fun safeVibrate(effect: VibrationEffect) {
    if (vibrator.hasVibrator()) {
        vibrator.vibrate(effect)
    } else {
        Toast.makeText(this, "Вибрация не поддерживается", Toast.LENGTH_SHORT).show()
    }
}

6. Важные замечания

  • Разрешение: В манифесте укажите <uses-permission android:name="android.permission.VIBRATE" />
  • API уровни: Для API < 26 используйте старый метод vibrate(long[] pattern, int repeat)
  • Амплитуда: Работает не на всех устройствах; на некоторых она игнорируется
  • Энергопотребление: Длительная вибрация быстро садит батарею

Теперь ваш будильник может вибрировать как настоящий! 🎉

<<>>

Альтернативный подход: использование AudioAttributes для тактильной обратной связи

Если вам нужно не просто "вибрировать", а создать тактильную обратную связь (например, для нажатий кнопок), можно использовать AudioAttributes с типом USAGE_ALARM или USAGE_NOTIFICATION. Это даёт системе понять контекст вибрации и может повлиять на её интенсивность:

fun alarmVibration() {
    val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
    
    val attributes = AudioAttributes.Builder()
        .setUsage(AudioAttributes.USAGE_ALARM)
        .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
        .build()
    
    val pattern = longArrayOf(100, 50, 200, 100, 300, 150)
    val effect = VibrationEffect.createWaveform(pattern, 0)
    
    vibrator.vibrate(effect, attributes)
}

Этот метод особенно полезен, если вы используете NotificationChannel с вибрацией — можно задать паттерн прямо в канале уведомлений:

val channel = NotificationChannel(
    "alarm_channel",
    "Будильник",
    NotificationManager.IMPORTANCE_HIGH
).apply {
    vibrationPattern = longArrayOf(500, 200, 500, 200)
    enableVibration(true)
}

Также не забывайте, что на некоторых устройствах (например, с тактильными двигателями X-axis) амплитуда может не поддерживаться, но паттерны работают всегда. Для отладки используйте vibrator.areAllEffectsSupported() (API 30+).