/
MainViewModel.kt
147 lines (128 loc) · 4.44 KB
/
MainViewModel.kt
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
package com.kieronquinn.app.simnumbersetter.ui.screens.main
import android.content.ActivityNotFoundException
import android.content.Context
import android.content.Intent
import android.net.Uri
import androidx.annotation.StringRes
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.kieronquinn.app.simnumbersetter.R
import com.kieronquinn.app.simnumbersetter.repositories.PermissionRepository
import com.kieronquinn.app.simnumbersetter.repositories.RootRepository
import com.kieronquinn.app.simnumbersetter.repositories.ServiceRepository
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.*
import kotlinx.coroutines.launch
import java.lang.Exception
abstract class MainViewModel : ViewModel() {
abstract val state: StateFlow<State>
abstract val number: StateFlow<String?>
abstract fun onNumberChanged(newNumber: String)
abstract fun onSaveClicked()
abstract fun onMenuItemClicked(context: Context, id: Int)
sealed class State {
data class Loading(val loadType: LoadType) : State()
data class Loaded(internal val number: String) : State()
data class Error(val errorType: ErrorType) : State()
}
enum class ErrorType(@StringRes val messageRes: Int) {
NO_ROOT(R.string.error_no_root),
NO_PERMISSION(R.string.error_no_permission),
NO_XPOSED(R.string.error_no_xposed),
SAVE_FAILED(R.string.error_save_failed)
}
enum class LoadType(@StringRes val contentRes: Int) {
LOADING(R.string.loading_loading),
SAVING(R.string.loading_saving)
}
}
class MainViewModelImpl(
private val rootRepository: RootRepository,
private val permissionRepository: PermissionRepository,
private val serviceRepository: ServiceRepository
) : MainViewModel() {
companion object {
private const val URL_GITHUB = "https://kieronquinn.co.uk/redirect/SNS/github"
private const val URL_DONATE = "https://kieronquinn.co.uk/redirect/SNS/donate"
}
private val customNumber = MutableStateFlow<String?>(null)
override val state = MutableStateFlow<State>(State.Loading(LoadType.LOADING))
override val number = combine(customNumber, state) { n, s ->
when {
n != null -> n
s is State.Loaded -> s.number
else -> null
}
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
override fun onNumberChanged(newNumber: String) {
viewModelScope.launch {
customNumber.emit(newNumber)
}
}
override fun onSaveClicked() {
save(number.value ?: "")
}
override fun onMenuItemClicked(context: Context, id: Int) {
val url = when(id) {
R.id.menu_github -> URL_GITHUB
R.id.menu_donate -> URL_DONATE
else -> return
}
try {
context.startActivity(Intent(Intent.ACTION_VIEW).apply {
data = Uri.parse(url)
})
}catch (e: ActivityNotFoundException) {
//Nothing can be done
}
}
private fun load() = viewModelScope.launch {
state.emit(State.Loading(LoadType.LOADING))
if (!rootRepository.isRooted()) {
state.emit(State.Error(ErrorType.NO_ROOT))
return@launch
}
if (!permissionRepository.grantDumpPermission()) {
state.emit(State.Error(ErrorType.NO_PERMISSION))
return@launch
}
val number = try {
serviceRepository.runWithService {
it.line1Number
}
} catch (e: Exception) {
null
} ?: run {
state.emit(State.Error(ErrorType.NO_XPOSED))
return@launch
}
state.emit(State.Loaded(number))
}
private fun save(number: String) = viewModelScope.launch {
state.emit(State.Loading(LoadType.SAVING))
val result = try {
serviceRepository.runWithService {
it.setLine1Number(number, null)
}
} catch (e: Exception) {
null
} ?: run {
state.emit(State.Error(ErrorType.NO_XPOSED))
return@launch
}
customNumber.emit(null)
delay(1000L)
if (result) {
load()
} else {
state.emit(State.Error(ErrorType.SAVE_FAILED))
}
}
init {
load()
}
override fun onCleared() {
serviceRepository.unbindServiceIfNeeded()
super.onCleared()
}
}