Last active
January 3, 2022 10:30
-
-
Save kairos34/64e3b41e240a205eb9e7d751393764cd to your computer and use it in GitHub Desktop.
Handle RTC
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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