1
1
package io.github.saeeddev94.xray.activity
2
2
3
3
import android.os.Bundle
4
+ import android.util.Log
4
5
import android.view.LayoutInflater
5
6
import android.view.Menu
6
7
import android.view.MenuItem
7
8
import android.widget.EditText
8
9
import android.widget.LinearLayout
9
10
import android.widget.RadioButton
10
11
import android.widget.RadioGroup
12
+ import android.widget.Toast
11
13
import androidx.activity.viewModels
12
14
import androidx.appcompat.app.AppCompatActivity
13
15
import androidx.lifecycle.Lifecycle
@@ -20,19 +22,28 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder
20
22
import com.google.android.material.materialswitch.MaterialSwitch
21
23
import com.google.android.material.radiobutton.MaterialRadioButton
22
24
import io.github.saeeddev94.xray.R
25
+ import io.github.saeeddev94.xray.Settings
23
26
import io.github.saeeddev94.xray.adapter.LinkAdapter
24
27
import io.github.saeeddev94.xray.database.Link
28
+ import io.github.saeeddev94.xray.database.Profile
25
29
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
26
33
import io.github.saeeddev94.xray.viewmodel.LinkViewModel
34
+ import io.github.saeeddev94.xray.viewmodel.ProfileViewModel
27
35
import kotlinx.coroutines.Dispatchers
28
36
import kotlinx.coroutines.flow.collectLatest
29
37
import kotlinx.coroutines.launch
30
38
import kotlinx.coroutines.withContext
39
+ import org.json.JSONObject
40
+ import java.net.URI
31
41
import kotlin.reflect.cast
32
42
33
43
class LinksActivity : AppCompatActivity () {
34
44
35
45
private val linkViewModel: LinkViewModel by viewModels()
46
+ private val profileViewModel: ProfileViewModel by viewModels()
36
47
private val adapter by lazy { LinkAdapter () }
37
48
private val linksRecyclerView by lazy { findViewById<RecyclerView >(R .id.linksRecyclerView) }
38
49
private var links: List <Link > = listOf ()
@@ -52,10 +63,8 @@ class LinksActivity : AppCompatActivity() {
52
63
lifecycleScope.launch {
53
64
lifecycle.repeatOnLifecycle(Lifecycle .State .STARTED ) {
54
65
linkViewModel.links.collectLatest {
55
- withContext(Dispatchers .Main ) {
56
- links = it
57
- adapter.submitList(it)
58
- }
66
+ links = it
67
+ adapter.submitList(it)
59
68
}
60
69
}
61
70
}
@@ -76,6 +85,112 @@ class LinksActivity : AppCompatActivity() {
76
85
}
77
86
78
87
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
+ }
79
194
}
80
195
81
196
private fun openLink (index : Int = -1, link : Link = Link ()) {
@@ -137,7 +252,14 @@ class LinksActivity : AppCompatActivity() {
137
252
138
253
private fun deleteLink (link : Link ) {
139
254
lifecycleScope.launch {
255
+ profileViewModel.linkProfiles(link.id)
256
+ .forEach { linkProfile ->
257
+ deleteProfile(linkProfile)
258
+ }
140
259
linkViewModel.delete(link)
260
+ withContext(Dispatchers .Main ) {
261
+ setResult(RESULT_OK )
262
+ }
141
263
}
142
264
}
143
265
}
0 commit comments