From 38089ca949affcf10fb23b9601de365f0b939a81 Mon Sep 17 00:00:00 2001 From: lhwdev Date: Mon, 28 Feb 2022 22:36:04 +0900 Subject: [PATCH] Add feature --- .idea/deploymentTargetDropDown.xml | 4 +- .idea/misc.xml | 8 + .idea/runConfigurations.xml | 10 - .../selfTestMacro/api/registerServey.kt | 26 +-- app/build.gradle | 4 +- .../com/lhwdev/selfTestMacro/MainActivity.kt | 116 +++++++++- .../com/lhwdev/selfTestMacro/selfTestUtils.kt | 67 ++++-- .../java/com/lhwdev/selfTestMacro/utils.kt | 13 ++ app/src/main/res/layout/content_main.xml | 212 ++++++++++++++---- 9 files changed, 350 insertions(+), 110 deletions(-) delete mode 100644 .idea/runConfigurations.xml diff --git a/.idea/deploymentTargetDropDown.xml b/.idea/deploymentTargetDropDown.xml index 49c0df8d..c17d57e2 100644 --- a/.idea/deploymentTargetDropDown.xml +++ b/.idea/deploymentTargetDropDown.xml @@ -7,11 +7,11 @@ - + - + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml index 4c2e5259..d85dd906 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,5 +1,13 @@ + + + \ No newline at end of file diff --git a/.idea/runConfigurations.xml b/.idea/runConfigurations.xml deleted file mode 100644 index 797acea5..00000000 --- a/.idea/runConfigurations.xml +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/registerServey.kt b/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/registerServey.kt index 84e37306..30269407 100644 --- a/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/registerServey.kt +++ b/api/src/main/kotlin/com/lhwdev/selfTestMacro/api/registerServey.kt @@ -18,30 +18,6 @@ import kotlinx.serialization.encoding.Decoder import kotlinx.serialization.encoding.Encoder -/* - * deviceUuid: "" - * rspns00: "Y" - * rspns01: "1" - * rspns02: "1" - * rspns03: null - * rspns04: null - * rspns05: null - * rspns06: null - * rspns07: null - * rspns08: null - * rspns09: "0" - * rspns10: null - * rspns11: null - * rspns12: null - * rspns13: null - * rspns14: null - * rspns15: null - * upperToken: "Bearer ey..." - * upperUserNameEncpt: "홍길동" - */ - - - /** * Can be used only once. */ @@ -62,7 +38,7 @@ data class SurveyData( @Serializable(with = YesNoSerializer::class) val rspns00: Boolean = true, val rspns01: String = "1", val rspns02: String = "1", - val rspns03: String = "1", + val rspns03: String? = "1", val rspns04: String? = null, val rspns05: String? = null, val rspns06: String? = null, diff --git a/app/build.gradle b/app/build.gradle index 9159f235..01f1a404 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -14,8 +14,8 @@ android { applicationId "com.lhwdev.selfTestMacro" minSdkVersion 19 targetSdkVersion 31 - versionCode 1017 - versionName "2.17" + versionCode 1019 + versionName "2.19" multiDexEnabled true diff --git a/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt b/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt index ed576e4f..0508afaa 100644 --- a/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt +++ b/app/src/main/java/com/lhwdev/selfTestMacro/MainActivity.kt @@ -17,6 +17,7 @@ import android.widget.* import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AppCompatActivity import androidx.core.text.HtmlCompat +import androidx.core.view.isVisible import androidx.core.view.setPadding import androidx.lifecycle.lifecycleScope import com.lhwdev.selfTestMacro.api.* @@ -28,9 +29,14 @@ import kotlinx.coroutines.withContext import kotlinx.serialization.Serializable import kotlinx.serialization.json.Json import java.net.URL +import java.util.Calendar const val IGNORE_BATTERY_OPTIMIZATION_REQUEST = 1001 +private val defaultQuickTestInfo = QuickTestInfo( + days = emptySet(), + behavior = QuickTestInfo.Behavior.negative +) @Suppress("SpellCheckingInspection") @@ -63,7 +69,7 @@ class MainActivity : AppCompatActivity() { @SuppressLint("SetTextI18n") - suspend fun updateCurrentState() = withContext(Dispatchers.IO) main@ { + suspend fun updateCurrentState() = withContext(Dispatchers.IO) main@{ val institute = pref.institute!! val user = pref.user!! // note: may change @@ -136,19 +142,38 @@ class MainActivity : AppCompatActivity() { } + switch_weekend.isChecked = pref.includeWeekend + + switch_weekend.setOnCheckedChangeListener { _, isChecked -> + pref.includeWeekend = isChecked + updateTime(intent) + } + + switch_isolation.isChecked = pref.isIsolated switch_isolation.setOnCheckedChangeListener { _, isChecked -> - pref.isIsolated = isChecked - if(isChecked) AlertDialog.Builder(this).apply { - setTitle("자가격리자 옵션") - setMessage(""" - |가정에서 자가격리를 하고 있으신 분은 이 옵션을 선택해주세요. 즉, 자가진단 설문에서 3번 문항에 '예'를 답해야 하는 경우입니다. - |(참고) 해당 문항: - |3. 학생 본인 또는 동거인이 방역당국에 의해 현재 자가격리가 이루어지고 있나요? - |※ 동거인이 자가격리중인 경우, ① 매 등교 희망일로부터 2일 이내 진단검사 결과가 음성인 경우 또는 ② 격리 통지를 받은 ‘즉시’ 자가격리된 동거인과 접촉이 없었던 경우는 ‘아니오’ 선택 - """.trimMargin()) - }.show() + if(isChecked) { + AlertDialog.Builder(this).apply { + setTitle("자가격리자 옵션") + setMessage( + """ + |가정에서 자가격리를 하고 있으신 분은 이 옵션을 선택해주세요. 즉, 자가진단 설문에서 3번 문항에 '예'를 답해야 하는 경우입니다. + |(참고) 해당 문항: + |3. 학생 본인 또는 동거인이 방역당국에 의해 현재 자가격리가 이루어지고 있나요? + |※ 동거인이 자가격리중인 경우, ① 매 등교 희망일로부터 2일 이내 진단검사 결과가 음성인 경우 또는 ② 격리 통지를 받은 ‘즉시’ 자가격리된 동거인과 접촉이 없었던 경우는 ‘아니오’ 선택 + """.trimMargin() + ) + setPositiveButton("설정") { _, _ -> + pref.isIsolated = true + } + setNegativeButton("취소") { _, _ -> + switch_isolation.isChecked = false + } + }.show() + } else { + pref.isIsolated = false + } } @@ -163,6 +188,73 @@ class MainActivity : AppCompatActivity() { true } + var quickTest = pref.quickTest ?: defaultQuickTestInfo + var quickTestEnabled = pref.quickTest != null + + quick_test_enable.isChecked = quickTestEnabled + quick_test_result.isVisible = quickTestEnabled + quick_test_enable.setOnCheckedChangeListener { _, isChecked -> + quick_test_result.isVisible = isChecked + quickTestEnabled = isChecked + + pref.quickTest = if(isChecked) { + quickTest + } else { + null + } + } + + fun quickTestDay(button: Button, day: Int) { + fun up(enabled: Boolean) { + val color = if(enabled) { + 0x55229cff + } else { + 0x00000000 + } + button.setBackgroundColor(color) + } + up(day in quickTest.days) + button.setOnClickListener { + val lastEnabled = day in quickTest.days + + val days = if(lastEnabled) { + quickTest.days - day + } else { + quickTest.days + day + } + quickTest = quickTest.copy(days = days) + pref.quickTest = quickTest + up(!lastEnabled) + updateTime(intent) + } + } + + quickTestDay(monday, Calendar.MONDAY) + quickTestDay(tuesday, Calendar.TUESDAY) + quickTestDay(wednesday, Calendar.WEDNESDAY) + quickTestDay(thursday, Calendar.THURSDAY) + quickTestDay(friday, Calendar.FRIDAY) + quickTestDay(saturday, Calendar.SATURDAY) + quickTestDay(sunday, Calendar.SUNDAY) + + quick_test_result_behavior.check( + when(quickTest.behavior) { + QuickTestInfo.Behavior.negative -> R.id.result_negative + QuickTestInfo.Behavior.doNotSubmit -> R.id.disable_macro + } + ) + + quick_test_result_behavior.setOnCheckedChangeListener { _, checkedId -> + val behavior = when(checkedId) { + R.id.result_negative -> QuickTestInfo.Behavior.negative + R.id.disable_macro -> QuickTestInfo.Behavior.doNotSubmit + else -> error("oh no") + } + quickTest = quickTest.copy(behavior = behavior) + pref.quickTest = quickTest + updateTime(intent) + } + submit.setOnClickListener { lifecycleScope.launch { submitSuspend(session, false) @@ -194,7 +286,7 @@ class MainActivity : AppCompatActivity() { content = URL("https://raw.githubusercontent.com/wiki/lhwdev/covid-selftest-macro/notice_v4.json").readText() - val notificationObject = Json { + val notificationObject: NotificationObject = Json { ignoreUnknownKeys = true /* loose */ }.decodeFromString(NotificationObject.serializer(), content) diff --git a/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt b/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt index b8b020f0..ac6a83d5 100644 --- a/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt +++ b/app/src/main/java/com/lhwdev/selfTestMacro/selfTestUtils.kt @@ -40,16 +40,40 @@ suspend fun Context.singleOfUserGroup(list: List) = if(list.size == 1) lis null } +fun Context.surveyData(user: User, usersIdentifier: UserIdentifier): SurveyData { + val pref = preferenceState + + val quickTestNegative = pref.quickTest?.let { + if(it.behavior != QuickTestInfo.Behavior.negative) { + false + } else { + val day = Calendar.getInstance().get(Calendar.DAY_OF_WEEK) + day in it.days + } + } ?: false + val isIsolated = pref.isIsolated + + + return SurveyData( + userToken = user.token, + upperUserName = usersIdentifier.mainUserName, + rspns03 = if(quickTestNegative) null else "1", + rspns07 = if(quickTestNegative) "0" else null, + rspns09 = if(isIsolated) "1" else "0", + rspns00 = !(isIsolated) // true = okay, false = problem + ) +} + suspend fun Context.submitSuspend(session: Session, notification: Boolean = true) { val pref = preferenceState selfLog("submitSuspend ${pref.user?.identifier?.mainUserName}") + try { tryAtMost(maxTrial = 3) trial@{ val institute = pref.institute!! val loginInfo: UserLoginInfo = - pref.user!! // (not valid ->) // note: `preferenceStte.user` may change after val user = ... + pref.user!! // (not valid ->) // note: `preferenceState.user` may change after val user = ... - val isIsolated = pref.isIsolated // val user = loginInfo.ensureTokenValid( // session, institute, @@ -65,17 +89,13 @@ suspend fun Context.submitSuspend(session: Session, notification: Boolean = true ).token val users = session.getUserGroup(institute, usersToken) - val user = singleOfUserGroup(users) ?: return@trial + val surveyData = surveyData(user, usersIdentifier) val result = session.registerSurvey( pref.institute!!, user, - SurveyData( - userToken = user.token, - upperUserName = usersIdentifier.mainUserName, - rspns09 = if(isIsolated) "1" else "0" - ) + surveyData ) pref.lastSubmit = Date().time @@ -98,6 +118,7 @@ fun Context.updateTime(intent: PendingIntent) { val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager if(Build.VERSION.SDK_INT >= 21) selfLog("updateTime: lastAlarm=${alarmManager.nextAlarmClock}") + alarmManager.cancel(intent) if(preferenceState.isSchedulingEnabled) scheduleNextAlarm(intent, preferenceState.hour, preferenceState.min, isRandom = preferenceState.isRandomEnabled) @@ -106,7 +127,7 @@ fun Context.updateTime(intent: PendingIntent) { private val random = Random private fun millisToDaysCumulative(millis: Long) = - // ms s min hour day + // ms sec min hour day millis / 1000 / 60 / 60 / 24 @SuppressLint("NewApi") @@ -132,12 +153,30 @@ fun Context.scheduleNextAlarm( val targetMin = hour * 60 + min val currentMin = this[Calendar.HOUR_OF_DAY] * 60 + this[Calendar.MINUTE] - if( - nextDay || - lastDay == millisToDaysCumulative(currentTime) || - targetMin < currentMin - 5 - ) { + // update date + val quick = pref.quickTest + + if(quick?.behavior == QuickTestInfo.Behavior.doNotSubmit && quick.days.size >= 7) { + // refuse to schedule + return + } + + 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 + nextDay || lastDay == days || targetMin < currentMin - 5 -> 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 } new[Calendar.HOUR_OF_DAY] = hour diff --git a/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt b/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt index a3abe51f..95b53273 100644 --- a/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt +++ b/app/src/main/java/com/lhwdev/selfTestMacro/utils.kt @@ -131,6 +131,7 @@ class PreferenceState(val pref: SharedPreferences) { var firstState by pref.preferenceInt("first", 0) var isSchedulingEnabled by pref.preferenceBoolean("isSchedulingEnabled", false) var isRandomEnabled by pref.preferenceBoolean("isRandomEnabled", false) + var includeWeekend by pref.preferenceBoolean("includeWeekend", false) var isIsolated by pref.preferenceBoolean("isIsolated", false) var hour by pref.preferenceInt("hour", -1) @@ -143,6 +144,9 @@ class PreferenceState(val pref: SharedPreferences) { var setting: UserSetting? by pref.preferenceSerialized("userSetting", UserSetting.serializer()) + var quickTest: QuickTestInfo? + by pref.preferenceSerialized("quickTest", QuickTestInfo.serializer()) + var lastSubmit: Long get() = pref.getLong("lastSubmit", Long.MIN_VALUE) set(value) = pref.edit { @@ -163,6 +167,14 @@ class PreferenceState(val pref: SharedPreferences) { } } +@Serializable +data class QuickTestInfo( + val days: Set, + val behavior: Behavior +) { + enum class Behavior { negative, doNotSubmit } +} + // I knew that global things are bad in android(i waz lazy), but didn't know would be by far worst. // Only one line below caused TWO bugs; I WON'T do like this in the future @@ -238,6 +250,7 @@ fun SharedPreferences.preferenceSerialized( if(!updated) { val string = getString(key, null) cache = if(string == null) null else formatter.decodeFromString(serializer, string) + updated = true } return cache } diff --git a/app/src/main/res/layout/content_main.xml b/app/src/main/res/layout/content_main.xml index e031766f..0c38d473 100644 --- a/app/src/main/res/layout/content_main.xml +++ b/app/src/main/res/layout/content_main.xml @@ -1,58 +1,180 @@ - - - - + + + + - - - - - -