Avatar ·

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

Всем привет! Разрабатываю приложение для Android на Kotlin и столкнулся с проблемой управления вибрацией. Нужно, чтобы устройство вибрировало не просто "вкл/выкл", а с разной частотой и силой (амплитудой). Например, для уведомлений — короткая слабая вибрация, для будильника — длинная и сильная, а для игрового события — пульсирующая с нарастанием. Пробовал использовать старый `Vibrator` через `getSystemService`, но там только `vibrate(long milliseconds)` — просто вибрирует заданное время без возможности менять частоту. Слышал, что с API 26 (Android 8) появился `VibratorManager` и `VibrationEffect`, но не могу разобраться, как правильно создать сложные паттерны. Вот мой текущий код, который работает, но не дает нужной гибкости: ```kotlin val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator if (vibrator.hasVibrator()) { vibrator.vibrate(500) // просто вибрирует 0.5 секунды } ``` Мне нужно: 1. Вибрировать с определенной частотой (например, 100 Гц). 2. Создать последовательность вибраций с паузами (шаблон). 3. Менять амплитуду (силу вибрации) в процентах от 0 до 255. Подскажите, как это сделать правильно на Kotlin, учитывая современные API. Буду благодарен за примеры кода! Устройство тестирую на Pixel 6 с Android 14. <<>>

Современное управление вибрацией в Android на Kotlin

Вы абсолютно правы — старый метод vibrate(long) устарел и не дает контроля над частотой и амплитудой. Начиная с Android 8 (API 26) и особенно в Android 12+ появился мощный API VibrationEffect и VibratorManager. Вот как решить все три ваши задачи.

1. Базовая настройка и проверка

Сначала получите доступ к вибратору через VibratorManager (рекомендуемый способ для API 31+):

// Для Android 12+ (API 31+)
val vibratorManager = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
val vibrator = vibratorManager.defaultVibrator

// Для всех версий (обратная совместимость)
val vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
    val manager = getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
    manager.defaultVibrator
} else {
    @Suppress("DEPRECATION")
    getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
}

// Всегда проверяйте наличие вибратора
if (!vibrator.hasVibrator()) return

2. Вибрация с заданной частотой (Гц)

Частота вибрации задается через VibrationEffect.Prebaked или через кастомные эффекты. Для точной частоты (например, 100 Гц) используйте VibrationEffect.Composition:

// Создаем эффект вибрации с частотой 100 Гц на 1 секунду
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { // API 33+
    val effect = VibrationEffect.startComposition()
        .addEffect(
            VibrationEffect.createWaveform(
                longArrayOf(0, 1000), // сразу начать, длиться 1 сек
                intArrayOf(128),      // амплитуда 50% (0-255)
                -1                    // не повторять
            ),
            VibrationEffect.Composition.PRIMITIVE_QUICK_RISE // быстрый старт
        )
        .compose()
    vibrator.vibrate(effect)
} else {
    // Для старых версий — просто вибрация с амплитудой
    val effect = VibrationEffect.createWaveform(
        longArrayOf(0, 1000),
        intArrayOf(128),
        -1
    )
    vibrator.vibrate(effect)
}

3. Шаблон с паузами (последовательность вибраций)

Создайте паттерн из чередующихся "вибрация/тишина":

// Паттерн: вибрация 200мс, пауза 100мс, вибрация 400мс, пауза 200мс, вибрация 200мс
val timings = longArrayOf(0, 200, 100, 400, 200, 200) // [задержка, вибрация, пауза, вибрация...]
val amplitudes = intArrayOf(0, 255, 0, 128, 0, 64)    // амплитуды для каждого отрезка

val effect = VibrationEffect.createWaveform(timings, amplitudes, -1) // -1 = не повторять
vibrator.vibrate(effect)

// Для бесконечного повторения (например, для будильника):
val repeatingEffect = VibrationEffect.createWaveform(timings, amplitudes, 0) // повторять с индекса 0
vibrator.vibrate(repeatingEffect)

4. Пульсирующая вибрация с нарастанием

Для эффекта "пульсации" комбинируйте примитивы:

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    val composition = VibrationEffect.startComposition()
        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.5f, 0)  // 50% силы
        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_TICK, 0.3f, 200)      // через 200мс
        .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 1.0f, 400) // еще через 400мс
        .compose()
    vibrator.vibrate(composition)
}

5. Полный пример для вашего случая

class VibrationHelper(private val context: Context) {
    private val vibrator: Vibrator by lazy {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
            val manager = context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as VibratorManager
            manager.defaultVibrator
        } else {
            @Suppress("DEPRECATION")
            context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
        }
    }
    
    fun vibrateNotification() {
        // Короткая слабая вибрация (50% амплитуды, 100мс)
        val effect = VibrationEffect.createWaveform(
            longArrayOf(0, 100),
            intArrayOf(128),
            -1
        )
        vibrator.vibrate(effect)
    }
    
    fun vibrateAlarm() {
        // Длинная сильная с повторением
        val effect = VibrationEffect.createWaveform(
            longArrayOf(0, 1000, 500, 1000),
            intArrayOf(255, 0, 255),
            0 // повторять
        )
        vibrator.vibrate(effect)
    }
    
    fun vibrateGamePulse() {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
            val effect = VibrationEffect.startComposition()
                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_QUICK_RISE, 0.8f, 0)
                .addPrimitive(VibrationEffect.Composition.PRIMITIVE_SLOW_RISE, 1.0f, 300)
                .compose()
            vibrator.vibrate(effect)
        }
    }
}

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

  • Разрешения: Для API 26+ не требуется дополнительных разрешений в манифесте, но для Android 13+ (API 33) нужно добавить <uses-permission android:name="android.permission.VIBRATE" />.
  • Амплитуда: Работает только на устройствах с поддержкой амплитудной модуляции (большинство современных смартфонов). Значения от 0 (нет вибрации) до 255 (максимум).
  • Частота: Точная частота (в Гц) доступна только через VibrationEffect.Composition на API 33+, на старых устройствах используйте длительность и амплитуду для имитации.
  • Тестирование: На эмуляторе вибрация может не работать — проверяйте на реальном устройстве.
<<>>

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

Если вам нужна очень точная частота вибрации (например, для тактильной обратной связи в играх) или вы работаете со старыми устройствами без поддержки VibrationEffect, можно использовать хак с AudioTrack и генерацией низкочастотного звука. Однако этот метод работает не на всех устройствах и требует разрешения RECORD_AUDIO.

fun vibrateWithFrequency(context: Context, frequencyHz: Int, durationMs: Int) {
    val vibrator = context.getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
    if (!vibrator.hasVibrator()) return
    
    // Генерируем синусоиду нужной частоты
    val sampleRate = 44100
    val