Реалізація Shake-to-Report (відправка бага встряхуванням) у мобільному додатку
Shake-to-Report — це паттерн, при якому користувач або тестувальник встряхує пристрій й отримує форму відправки бага з автоматично захопленим скриншотом і діагностичною інформацією. Всередині команди — зручніше TestFlight feedback. Для beta-тестерів — набагато нижче поріг входу, ніж заповнювати форму вручну.
Детектування встряхування
iOS
// UIWindow subclass для перехвату shake
class FeedbackWindow: UIWindow {
override func motionEnded(_ motion: UIEvent.EventSubtype, with event: UIEvent?) {
if motion == .motionShake {
FeedbackManager.shared.presentFeedbackForm()
}
super.motionEnded(motion, with: event)
}
}
// Використання в SceneDelegate
func scene(_ scene: UIScene, willConnectTo session: UISceneSession,
options connectionOptions: UIScene.ConnectionOptions) {
if let windowScene = scene as? UIWindowScene {
window = FeedbackWindow(windowScene: windowScene)
window?.rootViewController = UIHostingController(rootView: ContentView())
window?.makeKeyAndVisible()
}
}
Переопалення UIWindow — стандартний підхід, не вимагає SwiftUI-специфічного коду й працює з будь-якою архітектурою.
Android — акселерометр
На Android немає вбудованої eventi «shake» — детектуємо через акселерометр:
class ShakeDetector(private val onShake: () -> Unit) : SensorEventListener {
private val SHAKE_THRESHOLD_GRAVITY = 2.7f
private val SHAKE_SLOP_TIME_MS = 500
private var lastShakeMs: Long = 0
override fun onSensorChanged(event: SensorEvent) {
val gX = event.values[0] / SensorManager.GRAVITY_EARTH
val gY = event.values[1] / SensorManager.GRAVITY_EARTH
val gZ = event.values[2] / SensorManager.GRAVITY_EARTH
val gForce = sqrt(gX * gX + gY * gY + gZ * gZ.toDouble()).toFloat()
if (gForce > SHAKE_THRESHOLD_GRAVITY) {
val now = System.currentTimeMillis()
if (lastShakeMs + SHAKE_SLOP_TIME_MS > now) return
lastShakeMs = now
onShake()
}
}
override fun onAccuracyChanged(sensor: Sensor, accuracy: Int) {}
}
// Реєстрація в Activity/Fragment
val sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
val shakeDetector = ShakeDetector { showFeedbackDialog() }
sensorManager.registerListener(
shakeDetector,
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER),
SensorManager.SENSOR_DELAY_UI
)
SHAKE_SLOP_TIME_MS = 500 запобігає кількократному вызову від одного встряхування. Поріг 2.7g — компроміс між чутливістю й хибними спрацюваннями при ходьбі.
Контекст, який збирається автоматично
При встряхуванні до показу UI:
data class BugReport(
val screenshot: Bitmap,
val appVersion: String = BuildConfig.VERSION_NAME,
val buildNumber: String = BuildConfig.VERSION_CODE.toString(),
val osVersion: String = "Android ${Build.VERSION.RELEASE}",
val device: String = "${Build.MANUFACTURER} ${Build.MODEL}",
val currentScreen: String = screenTracker.currentScreenName,
val recentLogs: List<String> = LogBuffer.getLast(50), // останні 50 рядків логу
val memoryInfo: String = getMemoryInfo(),
val networkType: String = getNetworkType()
)
recentLogs — якщо у додатку налаштований in-memory log buffer (Timber tree, що записує в кільцевий буфер), баг-звіт відразу містить останні подієі. Розробник бачить все, що відбувалося за 30 секунд до встряхування.
Режими доступності
Shake неудобний для користувачів з тремором або тих, хто тримає пристрій на столі. Для QA й beta-програм додаємо альтернативні тригери:
- Довге натискання на логотип або версію в «Про додаток»
- Приховане меню через потрійний тап по порожній області екрана
- Жест двома пальцями (3 пальці, 3 тапи)
Shake зазвичай відключають у production або роблять опціональним через developer settings — щоб звичайний користувач не відкривав форму випадково.
Готові інструменти
| Інструмент | Платформи | Особливості |
|---|---|---|
| Instabug | iOS, Android, Flutter, RN | Shake + screen recording, Jira/Slack інтеграції |
| Shake.io | iOS, Android | Вбудована модель обговорення потоків |
| BugShaker | iOS (open source) | Простий, тільки email |
| Власна реалізація | Будь-які | Повний контроль, без зовнішніх залежностей |
Instabug — промисловий стандарт для мобільних QA-команд. Власна реалізація виправдана, якщо потрібно контролювати, які дані покидають пристрій.
Орієнтири по строкам
Кастомна реалізація shake-детектора з захопленням скриншота й відправкою в Jira — 3–5 днів. Інтеграція Instabug з кастомною темою й налаштуванням маршрутизації звітів — 1–2 дні.







