Skip to content

Commit 826d65e

Browse files
committed
Replace thread with CoroutineScope
1 parent 7b86912 commit 826d65e

File tree

15 files changed

+561
-201
lines changed

15 files changed

+561
-201
lines changed

app/src/main/java/io/github/saeeddev94/xray/Xray.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,11 @@ package io.github.saeeddev94.xray
33
import android.app.Application
44
import io.github.saeeddev94.xray.database.XrayDatabase
55
import io.github.saeeddev94.xray.repository.LinkRepository
6+
import io.github.saeeddev94.xray.repository.ProfileRepository
67

78
class Xray : Application() {
89

910
private val xrayDatabase by lazy { XrayDatabase.ref(this) }
1011
val linkRepository by lazy { LinkRepository(xrayDatabase.linkDao()) }
12+
val profileRepository by lazy { ProfileRepository(xrayDatabase.profileDao()) }
1113
}

app/src/main/java/io/github/saeeddev94/xray/activity/AssetsActivity.kt

Lines changed: 28 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@ import android.widget.ProgressBar
99
import android.widget.Toast
1010
import androidx.activity.result.contract.ActivityResultContracts
1111
import androidx.appcompat.app.AppCompatActivity
12+
import androidx.lifecycle.lifecycleScope
1213
import io.github.saeeddev94.xray.R
1314
import io.github.saeeddev94.xray.Settings
1415
import io.github.saeeddev94.xray.databinding.ActivityAssetsBinding
1516
import io.github.saeeddev94.xray.helper.DownloadHelper
17+
import kotlinx.coroutines.Dispatchers
18+
import kotlinx.coroutines.launch
19+
import kotlinx.coroutines.withContext
1620
import java.io.File
1721
import java.io.FileOutputStream
1822
import java.lang.Exception
@@ -92,47 +96,45 @@ class AssetsActivity : AppCompatActivity() {
9296
progressBar.progress = 0
9397

9498
downloading = true
95-
Thread {
96-
DownloadHelper(url, file, object : DownloadHelper.DownloadListener {
97-
override fun onProgress(progress: Int) {
98-
runOnUiThread { progressBar.progress = progress }
99-
}
99+
DownloadHelper(lifecycleScope, url, file, object : DownloadHelper.DownloadListener {
100+
override fun onProgress(progress: Int) {
101+
progressBar.progress = progress
102+
}
100103

101-
override fun onError(exception: Exception) {
102-
runOnUiThread {
103-
downloading = false
104-
Toast.makeText(applicationContext, exception.message, Toast.LENGTH_SHORT).show()
105-
setAssetStatus()
106-
}
107-
}
104+
override fun onError(exception: Exception) {
105+
downloading = false
106+
Toast.makeText(applicationContext, exception.message, Toast.LENGTH_SHORT).show()
107+
setAssetStatus()
108+
}
108109

109-
override fun onComplete() {
110-
runOnUiThread {
111-
downloading = false
112-
setAssetStatus()
113-
}
114-
}
115-
}).start()
116-
}.start()
110+
override fun onComplete() {
111+
downloading = false
112+
setAssetStatus()
113+
}
114+
}).start()
117115
}
118116

119117
private fun writeToFile(uri: Uri?, file: File) {
120118
if (uri == null) return
121-
Thread {
119+
lifecycleScope.launch {
122120
contentResolver.openInputStream(uri).use { input ->
123121
FileOutputStream(file).use { output ->
124122
input?.copyTo(output)
125123
}
126124
}
127-
runOnUiThread { setAssetStatus() }
128-
}.start()
125+
withContext(Dispatchers.Main) {
126+
setAssetStatus()
127+
}
128+
}
129129
}
130130

131131
private fun delete(file: File) {
132-
Thread {
132+
lifecycleScope.launch {
133133
file.delete()
134-
runOnUiThread { setAssetStatus() }
135-
}.start()
134+
withContext(Dispatchers.Main) {
135+
setAssetStatus()
136+
}
137+
}
136138
}
137139

138140
}

app/src/main/java/io/github/saeeddev94/xray/activity/ExcludeActivity.kt

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,17 @@ import android.view.View
1010
import android.widget.ImageView
1111
import androidx.appcompat.app.AppCompatActivity
1212
import androidx.appcompat.widget.SearchView
13+
import androidx.lifecycle.lifecycleScope
1314
import androidx.recyclerview.widget.LinearLayoutManager
1415
import androidx.recyclerview.widget.RecyclerView
1516
import io.github.saeeddev94.xray.R
1617
import io.github.saeeddev94.xray.Settings
1718
import io.github.saeeddev94.xray.adapter.ExcludeAdapter
1819
import io.github.saeeddev94.xray.databinding.ActivityExcludeBinding
1920
import io.github.saeeddev94.xray.dto.AppList
21+
import kotlinx.coroutines.Dispatchers
22+
import kotlinx.coroutines.launch
23+
import kotlinx.coroutines.withContext
2024

2125
class ExcludeActivity : AppCompatActivity() {
2226

@@ -93,7 +97,7 @@ class ExcludeActivity : AppCompatActivity() {
9397
}
9498

9599
private fun getApps() {
96-
Thread {
100+
lifecycleScope.launch {
97101
val selected = ArrayList<AppList>()
98102
val unselected = ArrayList<AppList>()
99103
packageManager.getInstalledPackages(PackageManager.GET_PERMISSIONS).forEach {
@@ -106,7 +110,7 @@ class ExcludeActivity : AppCompatActivity() {
106110
val isSelected = Settings.excludedApps.contains(packageName)
107111
if (isSelected) selected.add(app) else unselected.add(app)
108112
}
109-
runOnUiThread {
113+
withContext(Dispatchers.Main) {
110114
apps = ArrayList(selected + unselected)
111115
filtered = apps.toMutableList()
112116
excludedApps = Settings.excludedApps.split("\n").toMutableSet()
@@ -115,7 +119,7 @@ class ExcludeActivity : AppCompatActivity() {
115119
appsList.adapter = excludeAdapter
116120
appsList.layoutManager = LinearLayoutManager(applicationContext)
117121
}
118-
}.start()
122+
}
119123
}
120124

121125
private fun saveExcludedApps() {

app/src/main/java/io/github/saeeddev94/xray/activity/LinksActivity.kt

Lines changed: 126 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
package io.github.saeeddev94.xray.activity
22

33
import android.os.Bundle
4+
import android.util.Log
45
import android.view.LayoutInflater
56
import android.view.Menu
67
import android.view.MenuItem
78
import android.widget.EditText
89
import android.widget.LinearLayout
910
import android.widget.RadioButton
1011
import android.widget.RadioGroup
12+
import android.widget.Toast
1113
import androidx.activity.viewModels
1214
import androidx.appcompat.app.AppCompatActivity
1315
import androidx.lifecycle.Lifecycle
@@ -20,19 +22,28 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
2022
import com.google.android.material.materialswitch.MaterialSwitch
2123
import com.google.android.material.radiobutton.MaterialRadioButton
2224
import io.github.saeeddev94.xray.R
25+
import io.github.saeeddev94.xray.Settings
2326
import io.github.saeeddev94.xray.adapter.LinkAdapter
2427
import io.github.saeeddev94.xray.database.Link
28+
import io.github.saeeddev94.xray.database.Profile
2529
import io.github.saeeddev94.xray.databinding.ActivityLinksBinding
30+
import io.github.saeeddev94.xray.helper.ConfigHelper
31+
import io.github.saeeddev94.xray.helper.HttpHelper
32+
import io.github.saeeddev94.xray.helper.LinkHelper
2633
import io.github.saeeddev94.xray.viewmodel.LinkViewModel
34+
import io.github.saeeddev94.xray.viewmodel.ProfileViewModel
2735
import kotlinx.coroutines.Dispatchers
2836
import kotlinx.coroutines.flow.collectLatest
2937
import kotlinx.coroutines.launch
3038
import kotlinx.coroutines.withContext
39+
import org.json.JSONObject
40+
import java.net.URI
3141
import kotlin.reflect.cast
3242

3343
class LinksActivity : AppCompatActivity() {
3444

3545
private val linkViewModel: LinkViewModel by viewModels()
46+
private val profileViewModel: ProfileViewModel by viewModels()
3647
private val adapter by lazy { LinkAdapter() }
3748
private val linksRecyclerView by lazy { findViewById<RecyclerView>(R.id.linksRecyclerView) }
3849
private var links: List<Link> = listOf()
@@ -52,10 +63,8 @@ class LinksActivity : AppCompatActivity() {
5263
lifecycleScope.launch {
5364
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
5465
linkViewModel.links.collectLatest {
55-
withContext(Dispatchers.Main) {
56-
links = it
57-
adapter.submitList(it)
58-
}
66+
links = it
67+
adapter.submitList(it)
5968
}
6069
}
6170
}
@@ -76,6 +85,112 @@ class LinksActivity : AppCompatActivity() {
7685
}
7786

7887
private fun refreshLinks() {
88+
Toast.makeText(applicationContext, "Getting update", Toast.LENGTH_SHORT).show()
89+
lifecycleScope.launch {
90+
val profiles = profileViewModel.activeLinks()
91+
links.filter { it.isActive }.forEach { link ->
92+
runCatching {
93+
val content = HttpHelper.get(link.address).trim()
94+
val newProfiles = if (link.type == Link.Type.Json) {
95+
jsonProfile(link, content)
96+
} else {
97+
subscriptionProfiles(link, content)
98+
}
99+
if (newProfiles.isNotEmpty()) {
100+
val linkProfiles = profiles.filter { it.linkId == link.id }
101+
manageProfiles(link, linkProfiles, newProfiles)
102+
}
103+
}
104+
}
105+
withContext(Dispatchers.Main) {
106+
setResult(RESULT_OK)
107+
Toast.makeText(applicationContext, "Done", Toast.LENGTH_SHORT).show()
108+
}
109+
}
110+
}
111+
112+
private suspend fun jsonProfile(link: Link, value: String): List<Profile> {
113+
val list = arrayListOf<Profile>()
114+
runCatching {
115+
val error = ConfigHelper.isValid(applicationContext, value)
116+
if (error.isEmpty()) {
117+
val name = LinkHelper.remark(URI(link.address))
118+
val config = JSONObject(value).toString(2)
119+
val profile = Profile()
120+
profile.linkId = link.id
121+
profile.name = name
122+
profile.config = config
123+
list.add(profile)
124+
}
125+
}
126+
return list.toList()
127+
}
128+
129+
private suspend fun subscriptionProfiles(link: Link, value: String): List<Profile> {
130+
return runCatching {
131+
val decoded = LinkHelper.decodeBase64(value).trim()
132+
decoded.split("\n")
133+
.reversed()
134+
.map { LinkHelper(it) }
135+
.filter { it.isValid() }
136+
.map { linkHelper ->
137+
val profile = Profile()
138+
profile.linkId = link.id
139+
profile.config = linkHelper.json()
140+
profile.name = linkHelper.remark()
141+
profile
142+
}.filter {
143+
val error = ConfigHelper.isValid(applicationContext, it.config)
144+
error.isEmpty()
145+
}
146+
}.getOrNull() ?: listOf()
147+
}
148+
149+
private suspend fun manageProfiles(link: Link, linkProfiles: List<Profile>, newProfiles: List<Profile>) {
150+
if (newProfiles.size >= linkProfiles.size) {
151+
newProfiles.forEachIndexed { index, newProfile ->
152+
if (index >= linkProfiles.size) {
153+
newProfile.linkId = link.id
154+
insertProfile(newProfile)
155+
} else {
156+
val linkProfile = linkProfiles[index]
157+
updateProfile(linkProfile, newProfile)
158+
}
159+
}
160+
return
161+
}
162+
linkProfiles.forEachIndexed { index, linkProfile ->
163+
if (index >= newProfiles.size) {
164+
deleteProfile(linkProfile)
165+
} else {
166+
val newProfile = newProfiles[index]
167+
updateProfile(linkProfile, newProfile)
168+
}
169+
}
170+
}
171+
172+
private suspend fun insertProfile(newProfile: Profile) {
173+
profileViewModel.insert(newProfile)
174+
profileViewModel.fixInsertIndex()
175+
}
176+
177+
private suspend fun updateProfile(linkProfile: Profile, newProfile: Profile) {
178+
Log.e("INJA", "updateProfile: ${linkProfile.name}, ${newProfile.name}")
179+
linkProfile.name = newProfile.name
180+
linkProfile.config = newProfile.config
181+
profileViewModel.update(linkProfile)
182+
}
183+
184+
private suspend fun deleteProfile(linkProfile: Profile) {
185+
profileViewModel.delete(linkProfile)
186+
profileViewModel.fixDeleteIndex(linkProfile.index)
187+
withContext(Dispatchers.Main) {
188+
val selectedProfile = Settings.selectedProfile
189+
if (selectedProfile == linkProfile.id) {
190+
Settings.selectedProfile = 0L
191+
Settings.save(applicationContext)
192+
}
193+
}
79194
}
80195

81196
private fun openLink(index: Int = -1, link: Link = Link()) {
@@ -137,7 +252,14 @@ class LinksActivity : AppCompatActivity() {
137252

138253
private fun deleteLink(link: Link) {
139254
lifecycleScope.launch {
255+
profileViewModel.linkProfiles(link.id)
256+
.forEach { linkProfile ->
257+
deleteProfile(linkProfile)
258+
}
140259
linkViewModel.delete(link)
260+
withContext(Dispatchers.Main) {
261+
setResult(RESULT_OK)
262+
}
141263
}
142264
}
143265
}

0 commit comments

Comments
 (0)