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 @@
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+