diff --git a/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/fetch.kt b/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/fetch.kt index 7f3e86fd..4a40da40 100644 --- a/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/fetch.kt +++ b/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/fetch.kt @@ -46,6 +46,8 @@ interface FetchResult { val responseCodeMessage: String val rawResponse: InputStream + fun getHeader(key: String): String + fun close() } diff --git a/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/http/httpFetch.kt b/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/http/httpFetch.kt index 2204e20d..faef886a 100644 --- a/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/http/httpFetch.kt +++ b/api-base/src/jvmMain/kotlin/com/lhwdev/fetch/http/httpFetch.kt @@ -3,6 +3,7 @@ package com.lhwdev.fetch.http import com.lhwdev.fetch.* import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.runInterruptible import kotlinx.coroutines.withContext import java.io.* import java.net.HttpURLConnection @@ -34,7 +35,7 @@ fun interface HttpInterceptor : FetchInterceptor { } val HttpInterceptorImpl: HttpInterceptor = HttpInterceptor { url, method, headers, session, body -> - if(sDebugFetch) { + if(sDebugFetch) runInterruptible { println("") val lastCookieManager = threadLocalCookieHandler @@ -141,7 +142,7 @@ val HttpInterceptorImpl: HttpInterceptor = HttpInterceptor { url, method, header } finally { if(session != null) setThreadLocalCookieHandler(lastCookieManager) } - } else { // without debug logging + } else runInterruptible { // without debug logging val lastCookieManager = threadLocalCookieHandler if(session != null) setThreadLocalCookieHandler(session.cookieManager) @@ -201,6 +202,8 @@ private class HttpResultImpl(val connection: HttpURLConnection) : return connection.inputStream } + override fun getHeader(key: String): String = connection.getHeaderField(key) + override fun close() { connection.disconnect() } diff --git a/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/getUserGroup.kt b/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/getUserGroup.kt index abc7de87..dd459e6b 100644 --- a/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/getUserGroup.kt +++ b/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/getUserGroup.kt @@ -21,10 +21,18 @@ data class User( @SerialName("token") val token: UserToken ) +// a temporary hack to pass clientVersion where proper api design is not done +class UserGroup(val users: List, val clientVersion: String) : List by users -suspend fun Session.getUserGroup(institute: InstituteInfo, token: UsersToken): List = fetch( + +suspend fun Session.getUserGroup(institute: InstituteInfo, token: UsersToken): UserGroup = fetch( institute.requestUrl["selectUserGroup"], method = HttpMethod.post, headers = sDefaultFakeHeader + mapOf("Content-Type" to ContentTypes.json, "Authorization" to token.token), body = "{}" -).toJsonLoose(ListSerializer(User.serializer())) +).let { + UserGroup( + users = it.toJsonLoose(ListSerializer(User.serializer())), + clientVersion = it.getHeader("X-Client-Version") + ) +} diff --git a/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt b/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt index bb623630..94daec8a 100644 --- a/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt +++ b/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt @@ -110,7 +110,7 @@ class MainActivity : AppCompatActivity() { } else { time.text = "시간 예약 안 됨" } - updateTime(intent, reset = true) + updateTime(intent) } fun pickTime() { diff --git a/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt b/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt index 1c03d20b..b101aa06 100644 --- a/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt +++ b/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt @@ -15,7 +15,6 @@ import net.gotev.cookiestore.InMemoryCookieStore import java.net.CookieManager import java.net.CookiePolicy import java.util.Calendar -import java.util.Date import kotlin.random.Random @@ -40,7 +39,7 @@ suspend fun Context.singleOfUserGroup(list: List) = if(list.size == 1) lis null } -suspend fun Context.surveyData(user: User, usersIdentifier: UserIdentifier): SurveyData { +suspend fun Context.surveyData(user: User, usersIdentifier: UserIdentifier, clientVersion: String): SurveyData { val pref = preferenceState val quickTestNegative = pref.quickTest?.let { @@ -53,8 +52,6 @@ suspend fun Context.surveyData(user: User, usersIdentifier: UserIdentifier): Sur } ?: false val isIsolated = pref.isIsolated - val clientVersion = pref.appMeta().hcsVersion - return SurveyData( userToken = user.token, upperUserName = usersIdentifier.mainUserName, @@ -93,7 +90,7 @@ suspend fun Context.submitSuspend(session: Session, notification: Boolean = true val users = session.getUserGroup(institute, usersToken) val user = singleOfUserGroup(users) ?: return@trial - val surveyData = surveyData(user, usersIdentifier) + val surveyData = surveyData(user, usersIdentifier, users.clientVersion) val result = session.registerSurvey( pref.institute!!, @@ -118,7 +115,7 @@ suspend fun Context.submitSuspend(session: Session, notification: Boolean = true } } -fun Context.updateTime(intent: PendingIntent, reset: Boolean = false) { +fun Context.updateTime(intent: PendingIntent) { val preferenceState = preferenceState val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager if(Build.VERSION.SDK_INT >= 21) @@ -132,13 +129,9 @@ fun Context.updateTime(intent: PendingIntent, reset: Boolean = false) { isRandom = preferenceState.isRandomEnabled, nextDay = false ) - - if(reset) preferenceState.lastSubmit = -1 } } -private val random = Random - fun millisToDaysCumulative(millis: Long) = // ms sec min hour day millis / 1000 / 60 / 60 / 24 @@ -152,67 +145,61 @@ fun Context.scheduleNextAlarm( nextDay: Boolean ) { val pref = preferenceState - val currentTime: Long + val now = Calendar.getInstance() - var newTime = Calendar.getInstance().run { - currentTime = timeInMillis - - val new = clone() as Calendar - - // Submitted today - val last = pref.lastSubmit - val lastDay = millisToDaysCumulative(last) - - val targetMin = hour * 60 + min - val currentMin = this[Calendar.HOUR_OF_DAY] * 60 + this[Calendar.MINUTE] - - // update date - val quick = pref.quickTest - - if(quick?.behavior == QuickTestInfo.Behavior.doNotSubmit && quick.days.size >= 7) { - // refuse to schedule - return - } - - val newDay = millisToDaysCumulative(new.timeInMillis) - if(nextDay || lastDay == newDay) { - new.add(Calendar.DAY_OF_YEAR, 1) - } + val new = now.clone() as Calendar + + // Submitted today + val last = pref.lastSubmit + val lastDay = millisToDaysCumulative(last) + + // update date + val quick = pref.quickTest + + if(quick?.behavior == QuickTestInfo.Behavior.doNotSubmit && quick.days.size >= 7) { + // refuse to schedule + return + } + + if(nextDay || lastDay == millisToDaysCumulative(new.timeInMillis)) { + new.add(Calendar.DATE, 1) + } + + var iteration = 0 + while(iteration < 10) { + val days = millisToDaysCumulative(new.timeInMillis) + val day = new[Calendar.DAY_OF_WEEK] - var iteration = 0 - while(iteration < 10) { - val days = millisToDaysCumulative(new.timeInMillis) - val day = new[Calendar.DAY_OF_WEEK] - - when { - !pref.includeWeekend && (day == Calendar.SATURDAY || day == Calendar.SUNDAY) -> Unit - quick != null && quick.behavior == QuickTestInfo.Behavior.doNotSubmit && day in quick.days -> Unit - else -> break - } - new.add(Calendar.DAY_OF_YEAR, 1) - iteration++ - } - if(iteration == 10) { - return + when { + !pref.includeWeekend && (day == Calendar.SATURDAY || day == Calendar.SUNDAY) -> Unit + quick != null && quick.behavior == QuickTestInfo.Behavior.doNotSubmit && day in quick.days -> Unit + else -> break } - - new[Calendar.HOUR_OF_DAY] = hour - new[Calendar.MINUTE] = min - new[Calendar.SECOND] = 0 - new[Calendar.MILLISECOND] = 0 - selfLog("schedule time selection (nextDay=$nextDay lastDay=$lastDay, current=$newDay)") - new - }.timeInMillis + new.add(Calendar.DAY_OF_WEEK, 1) + iteration++ + } + if(iteration == 10) { // guard against looping forever + return + } + + new[Calendar.HOUR_OF_DAY] = hour + new[Calendar.MINUTE] = min + new[Calendar.SECOND] = 0 + new[Calendar.MILLISECOND] = 0 + var newTime = new.timeInMillis + + selfLog("schedule time selection (nextDay=$nextDay lastDay=$lastDay, newDay=${millisToDaysCumulative(newTime)})") if(isRandom) { newTime += 1000 * 60 * (Random.nextFloat() * 10 - 5).toInt() - if(newTime - currentTime < 10000) { - selfLog("scheduling: coerced time from $newTime") - newTime = currentTime + 10000 - } } - selfLog("scheduling next alarm at ${Date(newTime)}", force = true) + if(newTime - now.timeInMillis < 10000) { + selfLog("scheduling: coerced time from $newTime (diff = ${newTime - now.timeInMillis})") + newTime = now.timeInMillis + 10000 + } + + selfLog("scheduling next alarm at ${new.time}", force = true) val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager if(Build.VERSION.SDK_INT < 21) { diff --git a/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt b/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt index 1138644d..89bd13d1 100644 --- a/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt +++ b/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt @@ -20,7 +20,6 @@ import androidx.core.view.doOnPreDraw import androidx.core.view.setPadding import com.google.android.material.snackbar.Snackbar import com.lhwdev.fetch.http.Session -import com.lhwdev.fetch.http.fetch import com.lhwdev.selfTestMacro.api.* import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.suspendCancellableCoroutine @@ -29,7 +28,6 @@ import kotlinx.serialization.ExperimentalSerializationApi import kotlinx.serialization.KSerializer import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json -import java.net.URL import java.util.WeakHashMap import kotlin.coroutines.resume import kotlin.properties.ReadWriteProperty @@ -155,7 +153,7 @@ class PreferenceState(val pref: SharedPreferences) { putLong("lastSubmit", value) } - var appMeta: AppMeta? by pref.preferenceSerialized("appMeta", AppMeta.serializer()) + // var appMeta: AppMeta? by pref.preferenceSerialized("appMeta", AppMeta.serializer()) var lastQuestion: String? by pref.preferenceString("lastQuestion") @@ -171,24 +169,24 @@ class PreferenceState(val pref: SharedPreferences) { } } -suspend fun PreferenceState.appMeta(): AppMeta.Data { - val day = millisToDaysCumulative(System.currentTimeMillis()) - val last = appMeta - if(last != null && last.at == day) return last.data - - val data = fetch(URL("https://raw.githubusercontent.com/wiki/lhwdev/covid-selftest-macro/app_meta.json")) - .toJsonLoose(AppMeta.Data.serializer()) - appMeta = AppMeta(data, at = day) - return data -} - -@Serializable -class AppMeta(val data: Data, val at: Long) { - @Serializable - class Data( - val hcsVersion: String - ) -} +// suspend fun PreferenceState.appMeta(): AppMeta.Data { +// val day = millisToDaysCumulative(System.currentTimeMillis()) +// val last = appMeta +// if(last != null && last.at == day) return last.data +// +// val data = fetch(URL("https://raw.githubusercontent.com/wiki/lhwdev/covid-selftest-macro/app_meta.json")) +// .toJsonLoose(AppMeta.Data.serializer()) +// appMeta = AppMeta(data, at = day) +// return data +// } + +// @Serializable +// class AppMeta(val data: Data, val at: Long) { +// @Serializable +// class Data( +// val hcsVersion: String +// ) +// } @Serializable data class QuickTestInfo(