Skip to content

Instantly share code, notes, and snippets.

@kairos34
Last active January 3, 2022 10:30
Show Gist options
  • Save kairos34/64e3b41e240a205eb9e7d751393764cd to your computer and use it in GitHub Desktop.
Save kairos34/64e3b41e240a205eb9e7d751393764cd to your computer and use it in GitHub Desktop.
Handle RTC
import android.app.AlarmManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.SystemClock
import android.util.Log
import com.zk.android.constant.SdkException
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.launch
import java.util.*
private const val RTC_UPDATE_ACTION = "com.zkteco.android.rtc.UPDATE"
private val RTC_CALENDAR: Calendar
get() = Calendar.getInstance(TimeZone.getTimeZone("UTC"))
private const val IGNORE_INTERVAL = 5000L
private const val INDEX_YEAR = 6
private const val INDEX_MONTH = 5
private const val INDEX_DAY = 4
private const val INDEX_HOUR = 2
private const val INDEX_MINUTE = 1
private const val INDEX_SECOND = 0
class RTCManager(private val context: Context) {
private val TAG = javaClass.simpleName
private data class Time(
val year: Int,
val month: Int,
val day: Int,
val hour: Int,
val minute: Int,
val second: Int
)
// Update system time each 12 hours
private val SCHEDULEUPDATE_INTERVAL = 12 * AlarmManager.INTERVAL_HOUR
private val INITIAL_INTERVAL = 2 * AlarmManager.INTERVAL_HOUR
private val alarmIntent = Intent(RTC_UPDATE_ACTION).run {
PendingIntent.getBroadcast(context, 0, this, 0)
}
private val clockUpdater = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
GlobalScope.launch {
syncTime()
}
}
}
private val androidClockListener = object : BroadcastReceiver() {
private var lastTimeSetTimestamp = SystemClock.elapsedRealtime()
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == Intent.ACTION_TIME_CHANGED) {
Log.d(TAG, "Time changed event onReceive")
syncTimeToRTC()
} else if (intent?.action == Intent.ACTION_TIMEZONE_CHANGED) {
Log.d(TAG, "Timezone changed event onReceive")
syncTimeToRTC()
}
}
private fun syncTimeToRTC() {
Log.d(TAG, "RTC > onReceive")
if (SystemClock.elapsedRealtime() - lastTimeSetTimestamp > IGNORE_INTERVAL) {
Log.d(TAG, "RTC > syncTime")
lastTimeSetTimestamp = SystemClock.elapsedRealtime()
val calendar = Calendar.getInstance()
calendar.timeZone = TimeZone.getTimeZone("UTC")
calendar.time = Date()
setTime(calendar.timeInMillis)
}
}
}
init {
// Register listener to override Android time
androidClockListener.run {
context.registerReceiver(this, IntentFilter(Intent.ACTION_TIME_CHANGED))
context.registerReceiver(this, IntentFilter(Intent.ACTION_TIMEZONE_CHANGED))
}
context.registerReceiver(clockUpdater, IntentFilter(RTC_UPDATE_ACTION))
scheduleUpdate()
}
fun getTime() = run {
getRTCTime()?.toCalendar()?.timeInMillis?.apply {
Log.d(TAG, "getTime ${Date(this)}")
}
}
fun setTime(time: Long) {
Log.d(TAG, "setTime ${Date(time)} $time")
setRTCTime(time.toTime())
}
private fun getRTCTime() =
try {
val time = getRTC()
with(time) {
val year = get(INDEX_YEAR) + 2000
val month = get(INDEX_MONTH)
val day = get(INDEX_DAY)
val hour = get(INDEX_HOUR)
val minute = get(INDEX_MINUTE)
val second = get(INDEX_SECOND)
Log.d(TAG, "getRTCTime at $year-$month-$day $hour:$minute:$second")
Time(
get(INDEX_YEAR) + 2000,
get(INDEX_MONTH),
get(INDEX_DAY),
get(INDEX_HOUR),
get(INDEX_MINUTE),
get(INDEX_SECOND)
)
}
} catch (e: SdkException) {
Log.e(TAG, e.message)
null
}
private fun setRTCTime(time: Time) {
try {
with(time) {
Log.d(TAG, "setRTCTime at $year-$month-$day $hour:$minute:$second")
setRTC(year, month, day, hour, minute, second)
}
} catch (e: SdkException) {
Log.e(TAG, e.message)
}
}
private fun Long.toTime() = RTC_CALENDAR.run {
time = Date(this@toTime)
val year = get(Calendar.YEAR)
val month = get(Calendar.MONTH) + 1 // Java API months go from 0 to 11
val day = get(Calendar.DAY_OF_MONTH)
val hour = get(Calendar.HOUR_OF_DAY)
val minute = get(Calendar.MINUTE)
val second = get(Calendar.SECOND)
Time(year, month, day, hour, minute, second)
}
private fun Time.toCalendar() = RTC_CALENDAR.apply {
set(Calendar.YEAR, year)
set(Calendar.MONTH, month - 1) // Java API months go from 0 to 11
set(Calendar.DAY_OF_MONTH, day)
set(Calendar.HOUR_OF_DAY, hour)
set(Calendar.MINUTE, minute)
set(Calendar.SECOND, second)
}
private fun syncTime() {
Log.d(TAG, "syncTime from hardware RTC")
(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager).run {
getTime()?.let {
setTime(it)
}
}
}
private fun scheduleUpdate() {
(context.getSystemService(Context.ALARM_SERVICE) as AlarmManager).run {
setInexactRepeating(
AlarmManager.ELAPSED_REALTIME,
SystemClock.elapsedRealtime() + INITIAL_INTERVAL,
SCHEDULEUPDATE_INTERVAL,
alarmIntent
)
}
}
// Below two functions taken from DeviceHelper class...
// device instance in the functions is the same with rfid module device instance
@Throws(com.zkteco.android.constant.SdkException::class)
private fun getRTC(): IntArray = device.getRTC()
@Throws(com.zkteco.android.constant.SdkException::class)
private fun setRTC(
year: Int,
month: Int,
day: Int,
hour: Int,
minute: Int,
second: Int
): Boolean =
device.setRTC(year, month, day, hour, minute, second)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment