-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMainActivity.kt
More file actions
204 lines (179 loc) · 7.48 KB
/
MainActivity.kt
File metadata and controls
204 lines (179 loc) · 7.48 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
package com.xpeho.xpeapp
import android.app.AlertDialog
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Build
import android.os.Bundle
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts
import androidx.annotation.RequiresApi
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import androidx.core.content.ContextCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.xpeho.xpeapp.enums.Screens
import com.xpeho.xpeapp.ui.Home
import com.xpeho.xpeapp.ui.notifications.AlarmScheduler
import com.xpeho.xpeapp.ui.theme.XpeAppTheme
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.withContext
import okhttp3.OkHttpClient
import okhttp3.Request
import org.json.JSONObject
import java.io.IOException
import com.xpeho.xpeho_ui_android.foundations.Colors as XpehoColors
import androidx.core.net.toUri
class MainActivity : ComponentActivity() {
companion object {
private const val TOKEN_CHECK_INTERVAL_HOURS = 8
private const val HOURS_TO_MILLISECONDS = 60 * 60 * 1000L
}
private val requestPermissionLauncher = registerForActivityResult(
ActivityResultContracts.RequestPermission()
) { isGranted: Boolean ->
if (isGranted) {
// Schedule the alarm if the permission is granted
scheduleNotificationAlarm()
} else {
Log.d("Permission", "Permission denied")
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
installSplashScreen()
// Check if the app has the necessary notifications permissions.
checkPermissions()
// Check for app update
checkForUpdate()
// This is done to skip to the Home screen faster,
// thus not forcing the user to wait for authentication.
val connectedLastTime = runBlocking {
XpeApp.appModule.datastorePref.getWasConnectedLastTime()
}
val startScreenFlow: MutableStateFlow<Screens> =
MutableStateFlow(if (connectedLastTime) Screens.Home else Screens.Login)
// Periodic check for token expiration (every 8 hours)
CoroutineScope(Dispatchers.IO).launch {
while (true) {
kotlinx.coroutines.delay(TOKEN_CHECK_INTERVAL_HOURS * HOURS_TO_MILLISECONDS)
// Check if we are connected and if the token has expired
val authState = XpeApp.appModule.authenticationManager.authState.value
if (authState is com.xpeho.xpeapp.domain.AuthState.Authenticated) {
// isAuthValid() automatically logs out if the token has expired
XpeApp.appModule.authenticationManager.isAuthValid()
}
}
}
// If the user was connected last time, try to restore the authentication state.
if (connectedLastTime) {
CoroutineScope(Dispatchers.IO).launch {
XpeApp.appModule.authenticationManager.restoreAuthStateFromStorage()
// Check validity after restoration (automatic logout if expired)
XpeApp.appModule.authenticationManager.isAuthValid()
}
}
setContent {
XpeAppTheme {
// A surface container using the 'background' color from the theme
Surface(
modifier = Modifier
.fillMaxSize(),
color = XpehoColors.BACKGROUND_COLOR
) {
val startScreen = startScreenFlow.collectAsStateWithLifecycle()
Home(startScreen.value)
}
}
}
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun checkPermissions() {
when {
ContextCompat.checkSelfPermission(
this@MainActivity,
android.Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_GRANTED -> {
Log.d("Permission", "Permission already granted")
scheduleNotificationAlarm()
}
else -> {
requestPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
}
}
}
private fun scheduleNotificationAlarm() {
val alarmScheduler = AlarmScheduler()
alarmScheduler.scheduleAlarm(this)
}
@RequiresApi(Build.VERSION_CODES.TIRAMISU)
private fun checkForUpdate() {
// Check for updates only in release mode
if (!BuildConfig.DEBUG) {
CoroutineScope(Dispatchers.IO).launch {
val latestVersion = getLatestReleaseTag()
val currentVersion = getCurrentAppVersion()
// If the latest version is not null and is greater than the current version,
if (latestVersion != null && isVersionLessThan(currentVersion, latestVersion)) {
withContext(Dispatchers.Main) {
showUpdateDialog(latestVersion)
}
}
}
}
}
private fun showUpdateDialog(version: String) {
AlertDialog.Builder(this)
.setTitle(getString(R.string.force_update_popup_title_label))
.setMessage(
getString(
R.string.force_update_popup_message_label,
version
)
)
.setCancelable(false)
.setPositiveButton(getString(R.string.force_update_popup_button_label)) { _, _ ->
val intent = Intent(Intent.ACTION_VIEW).apply {
data = "market://details?id=$packageName".toUri()
setPackage("com.android.vending")
}
startActivity(intent)
}
.show()
}
private fun getCurrentAppVersion(): String {
return BuildConfig.VERSION_NAME
}
private suspend fun getLatestReleaseTag(): String? {
return withContext(Dispatchers.IO) {
val client = OkHttpClient()
val request = Request.Builder()
.url("https://api.github.com/repos/XPEHO/xpeapp_android/releases/latest")
.build()
client.newCall(request).execute().use { response ->
if (!response.isSuccessful) throw IOException("Unexpected code $response")
val jsonResponse = JSONObject(response.body.string())
jsonResponse.getString("tag_name")
}
}
}
private fun isVersionLessThan(currentVersion: String, latestVersion: String): Boolean {
val currentParts = currentVersion.split(".")
val latestParts = latestVersion.split(".")
for (i in 0 until maxOf(currentParts.size, latestParts.size)) {
val currentPart = currentParts.getOrNull(i)?.toIntOrNull() ?: 0
val latestPart = latestParts.getOrNull(i)?.toIntOrNull() ?: 0
if (currentPart < latestPart) return true
if (currentPart > latestPart) return false
}
return false
}
}