Created
April 22, 2021 10:21
-
-
Save ivan200/9cb4bcf3cb2b02e8d58db39166c34329 to your computer and use it in GitHub Desktop.
Managing correct time in android app
This file contains 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
object AppLocalTime : AppLocalTimeBase() { | |
override fun saveServerDateTimeMls(millis: Long) = Prefs::serverDateTimeMls.set(millis) | |
override fun savePhoneDateTimeMls(millis: Long) = Prefs::phoneDateTimeMls.set(millis) | |
override fun savePhoneBootMillis(millis: Long) = Prefs::phonePhoneBootMls.set(millis) | |
override fun loadServerDateTimeMls(): Long = Prefs.serverDateTimeMls | |
override fun loadPhoneDateTimeMls(): Long = Prefs.phoneDateTimeMls | |
override fun loadPhoneBootMillis(): Long = Prefs.phonePhoneBootMls | |
override fun getServerDateFormat(): String = "yyyy-MM-dd HH:mm:ss ZZZZ" // sample 2017-04-24 15:14:39 +0000 | |
override fun getServerDateString(ondate: (String?) -> Unit, onError: (Throwable?) -> Unit) { | |
//ClientServer.Requests.getServerTime() | |
} | |
} |
This file contains 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.content.Context | |
import android.os.Build | |
import android.os.SystemClock | |
import android.provider.Settings | |
import java.text.SimpleDateFormat | |
import java.util.* | |
import java.util.concurrent.TimeUnit | |
import java.util.logging.Level | |
import java.util.logging.Logger | |
import kotlin.math.abs | |
inline val <T : Any> T.TAG: String get() = this::class.java.simpleName | |
inline val <T : Any> T.logger: Logger get() = Logger.getLogger(this.TAG) | |
abstract class AppLocalTimeBase { | |
private var lastServerTimeMillis: Long = 0 | |
private var lastPhoneTimeMillis: Long = 0 | |
private var lastPhoneBootMillis: Long = 0 | |
private var needToUpdateTime = true | |
private var alreadyUpdating = false | |
/** | |
* Если получаем данные первый раз, пытаемся восстановить их из телефона | |
* Если при сравнении времени, которое было с последней загрузки, и времени в телефоне разница больше 5 минут | |
* то нам потребуется срочно обновить время | |
* Если не получилось восстановить данные о последней загрузке, то просто используем текущее время | |
* Высчитываем время сервера при помощи таймеров телефона | |
*/ | |
val currentDateTimeMls: Long | |
get() { | |
if (lastServerTimeMillis <= 0 || lastPhoneTimeMillis <= 0 || lastPhoneBootMillis <= 0) { | |
restoreTimesFromPrefs() | |
} | |
val phoneBootMillis = SystemClock.elapsedRealtime() | |
val phoneTimeMillis = System.currentTimeMillis() | |
val phoneBootDiff = phoneBootMillis - lastPhoneBootMillis | |
val phoneTimeDiff = phoneTimeMillis - lastPhoneTimeMillis | |
val diffMillis = abs(phoneBootDiff - phoneTimeDiff) | |
val diffMinutes = TimeUnit.MILLISECONDS.toMinutes(diffMillis) | |
if (diffMinutes > 5) { | |
needToUpdateTime = true | |
updateTime() | |
logger.log(Level.INFO, "time needToUpdateTime") | |
} else { | |
needToUpdateTime = false | |
} | |
if (lastPhoneBootMillis <= 0) { | |
return phoneTimeMillis | |
} | |
val countedServerTimeMillis: Long | |
if (phoneBootMillis > lastPhoneBootMillis) { | |
countedServerTimeMillis = lastServerTimeMillis + phoneBootDiff | |
logger.log(Level.FINEST, "time fromBoot: $countedServerTimeMillis") | |
} else { | |
countedServerTimeMillis = lastServerTimeMillis + phoneTimeDiff | |
logger.log(Level.FINEST, "time fromPhone: $countedServerTimeMillis") | |
} | |
return countedServerTimeMillis | |
} | |
val currentDate: Date | |
get() { | |
return Date(currentDateTimeMls) | |
} | |
val isTimeCorrect: Boolean get() = !needToUpdateTime | |
val currentCalendar: Calendar | |
get() { | |
val calendar = Calendar.getInstance() | |
calendar.time = currentDate | |
return calendar | |
} | |
val currentDayOfYear: Int | |
get() { | |
return currentCalendar.get(Calendar.DAY_OF_YEAR) | |
} | |
val currentDateString: String | |
get() { | |
return dateToString(currentDate, getServerDateFormat()) | |
} | |
//При любом получении данных с сервера, записываем данные о времени в настройки телефона | |
fun saveOurServerDate(serverTime: String) { | |
if(serverTime.isEmpty()){ | |
return | |
} | |
val serverDateTime = dateFromString(serverTime, getServerDateFormat()) | |
if (serverDateTime == null) { | |
logger.log(Level.WARNING, "Ошибка обновления времени: $serverTime") | |
return | |
} | |
logger.log(Level.FINEST, "time saveTime") | |
this.lastServerTimeMillis = serverDateTime.time | |
this.lastPhoneTimeMillis = System.currentTimeMillis() | |
this.lastPhoneBootMillis = SystemClock.elapsedRealtime() | |
if (lastPhoneBootMillis > 0 && lastPhoneTimeMillis > 0 && lastPhoneBootMillis > 0) { | |
saveServerDateTimeMls(lastServerTimeMillis) | |
savePhoneDateTimeMls(lastPhoneTimeMillis) | |
savePhoneBootMillis(lastPhoneBootMillis) | |
needToUpdateTime = false | |
} | |
} | |
abstract fun saveServerDateTimeMls(millis: Long) | |
abstract fun savePhoneDateTimeMls(millis: Long) | |
abstract fun savePhoneBootMillis(millis: Long) | |
abstract fun loadServerDateTimeMls(): Long | |
abstract fun loadPhoneDateTimeMls(): Long | |
abstract fun loadPhoneBootMillis(): Long | |
abstract fun getServerDateFormat(): String | |
private fun updateTime() { | |
if (needToUpdateTime && !alreadyUpdating) { | |
alreadyUpdating = true | |
getServerDateString(ondate = { | |
alreadyUpdating = false | |
if (it.isNullOrEmpty()) { | |
logger.log(Level.WARNING, "Ошибка обновления времени") | |
} else { | |
saveOurServerDate(it) | |
} | |
}, onError = { | |
alreadyUpdating = false | |
logger.log(Level.WARNING, "Ошибка обновления времени, ${it?.message}") | |
}) | |
} | |
} | |
abstract fun getServerDateString(ondate: (String?)->Unit, onError: (Throwable?) -> Unit) | |
//Восстановление данных о времени из настроек | |
private fun restoreTimesFromPrefs() { | |
lastServerTimeMillis = loadServerDateTimeMls() | |
lastPhoneTimeMillis = loadPhoneDateTimeMls() | |
lastPhoneBootMillis = loadPhoneBootMillis() | |
} | |
companion object { | |
fun dateFromString(str: String?, pattern: String, timeZone: String? = null): Date? { | |
if(str == null) return null | |
logger.log(Level.FINEST, "dateTimeFromString: $str, zone:$timeZone") | |
val sdf = SimpleDateFormat(pattern, Locale.getDefault()) | |
if(!timeZone.isNullOrEmpty()) { | |
sdf.timeZone = TimeZone.getTimeZone(timeZone) | |
} | |
var d: Date? = null | |
try { | |
d = sdf.parse(str) | |
} catch (throwable: Throwable){ | |
logger.log(Level.WARNING, throwable.getStackTraceText()) | |
} | |
return d | |
} | |
fun millisToString(millis: Long?, pattern: String): String? { | |
if(millis == null || millis <= 0) return null | |
val sdf = SimpleDateFormat(pattern, Locale.ENGLISH) | |
// sdf.timeZone = instance.currentCalendar.timeZone | |
return sdf.format(millis) | |
} | |
private fun dateToString(date: Date, pattern: String): String { | |
val sdf = SimpleDateFormat(pattern, Locale.ENGLISH) | |
// sdf.timeZone = instance.currentCalendar.timeZone | |
return sdf.format(date) | |
} | |
@Suppress("DEPRECATION") | |
fun isTimeAutomatic(c: Context): Boolean { | |
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { | |
Settings.Global.getInt(c.contentResolver, Settings.Global.AUTO_TIME, 0) == 1 | |
} else { | |
Settings.System.getInt(c.contentResolver, Settings.System.AUTO_TIME, 0) == 1 | |
} | |
} | |
@Suppress("DEPRECATION") | |
fun isTimeZoneAutomatic(c: Context): Boolean { | |
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) { | |
Settings.Global.getInt(c.contentResolver, Settings.Global.AUTO_TIME_ZONE, 0) == 1 | |
} else { | |
Settings.System.getInt(c.contentResolver, Settings.System.AUTO_TIME_ZONE, 0) == 1 | |
} | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment