현재 보드게임 관련앱을 제작중인데.
타이머 기능이있고, 타이머가 끝나면 진동을 주게하고싶었다.
관련 라이브러를 찾아봤지만 구현되어있는게 없어서 직접 구현해보기로했다.
안드로이드에서는 간단히 Vibrator를 통해 진동을 발생 시킬수있다.
S 부터 버전 분기가 되었으므로 나누어서 init 해준다.
private val vibrator: Vibrator
init {
vibrator = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
val vibratorManager =
context.getSystemService(Context.VIBRATOR_MANAGER_SERVICE) as android.os.VibratorManager
vibratorManager.defaultVibrator
} else {
context.getSystemService(VIBRATOR_SERVICE) as Vibrator
}
}
vibrate를 통해 발생시킬 수 있다.
O버전 부터는 시간외에 amplitude를 받아 진동세기를 조절할수 있는데 여기서는 단순히 발생이 목적이기 때문에 100으로 고정하였다.
fun vibrate(time: Long) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createOneShot(time, 100))
} else {
vibrator.vibrate(time)
}
}
timings을 받아 발생시킬 수 있다.
예를 들어 timings가 [100,500,100,500] 이라면
0.1초 대기 0.5초 발생 0.1초 대기 0.5초 발생 이런식이다.
repeat으로 특정 index구간을 반복 할 수있는데 여기서는 무시한다.(1회만)
fun vibratePattern(timings: LongArray) {
val repeat = -1
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
vibrator.vibrate(VibrationEffect.createWaveform(timings, repeat))
} else {
vibrator.vibrate(timings, repeat)
}
}
iOS는 진동 발생 시켜본적이 처음이라 찾아보다가 corehaptics을 이용하여 커스텀 진동을 구현 할 수 있는 것을 확인 하였다.
CHHapticEngine을 통해 CHHapticEvent을 받아와서 원하는 진동을 구현 하도록 만들었다.
internal fun playHaptic(
eventPattern: List<CHHapticEvent>
) {
if (engine == null) {
resetEngine()
}
engine?.let { engine ->
engine?.stopWithCompletionHandler {
try {
val pattern = CHHapticPattern(
events = eventPattern,
parameters = emptyList<CHHapticDynamicParameter>(),
error = null
)
val player = engine.createPlayerWithPattern(pattern = pattern, error = null)
engine.notifyWhenPlayersFinished {
CHHapticEngineFinishedActionStopEngine
}
engine.startWithCompletionHandler {
player?.startAtTime(0.0, error = null)
}
} catch (e: Exception) {
println("playHaptic error")
e.printStackTrace()
}
}
}
}
가져온 시간을 CHHapticEvent 리스트로 변환하여 발생시킨다.
fun vibrate(time: Long) {
try {
customHaptic.playHaptic(
listOf(
CHHapticEvent(
eventType = CHHapticEventTypeHapticContinuous,
parameters = emptyList<CHHapticEventParameter>(),
relativeTime = 0.1,
duration = time.toIosDuration()
)
)
)
} catch (e: Exception) {
println("vibrate error")
e.printStackTrace()
}
}
가져온 패턴을 CHHapticEvent 리스트로 변환하여 발생시킨다.
안드로이드처럼 자동으로 off/on 타이밍이 아니라 손수 변환해주었다.
CHHapticEvent에는 relativeTime/duration이 있는데
즉 [100,500,100,500]의 값이 왔다면 아래와 같이 두개의 CHHapticEvent를 가진다.
fun vibratePattern(timings: LongArray) {
try {
val convertPattern = mutableListOf<CHHapticEvent>()
var prevTime: Double? = null
timings.forEachIndexed { index, time ->
val convertDuration = time.toIosDuration()
if (index % 2 == 0) {
prevTime = if (prevTime == null) {
convertDuration
} else {
prevTime!! + convertDuration
}
} else {
CHHapticEvent(
eventType = CHHapticEventTypeHapticContinuous,
parameters = emptyList<CHHapticEventParameter>(),
relativeTime = prevTime!!,
duration = convertDuration
).also {
convertPattern.add(it)
}
prevTime = prevTime!! + convertDuration
}
}
customHaptic.playHaptic(convertPattern)
} catch (e: Exception) {
e.printStackTrace()
}
}
코드는 여기에서 git