Optimized UI

This commit is contained in:
2dust committed 2024-08-04 19:18:16 +08:00
1 parent 253bd793d7
commit 66e77d50bd
8 files changed
+97 -47

No files matched your search

@@ -20,10 +20,12 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat import androidx.core.view.GravityCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.navigation.NavigationView import com.google.android.material.navigation.NavigationView
import com.google.android.material.tabs.TabLayout
import com.tbruyelle.rxpermissions.RxPermissions import com.tbruyelle.rxpermissions.RxPermissions
import com.tencent.mmkv.MMKV import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig import com.v2ray.ang.AppConfig
@@ -57,6 +59,23 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
startV2Ray() startV2Ray()
} }
} }
private val requestSubSettingActivity = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
initGroupTab()
}
private val tabGroupListener = object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
val selectId = tab?.tag.toString()
if (selectId != mainViewModel.subscriptionId) {
mainViewModel.subscriptionIdChanged(selectId)
}
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
}
override fun onTabReselected(tab: TabLayout.Tab?) {
}
}
private var mItemTouchHelper: ItemTouchHelper? = null private var mItemTouchHelper: ItemTouchHelper? = null
val mainViewModel: MainViewModel by viewModels() val mainViewModel: MainViewModel by viewModels()
@@ -106,7 +125,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toggle.syncState() toggle.syncState()
binding.navView.setNavigationItemSelectedListener(this) binding.navView.setNavigationItemSelectedListener(this)
initGroupTab()
setupViewModel() setupViewModel()
mainViewModel.copyAssets(assets) mainViewModel.copyAssets(assets)
@@ -157,6 +176,29 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
mainViewModel.startListenBroadcast() mainViewModel.startListenBroadcast()
} }
private fun initGroupTab() {
binding.tabGroup.removeOnTabSelectedListener(tabGroupListener)
binding.tabGroup.removeAllTabs()
binding.tabGroup.isVisible = false
val (listId, listRemarks) = mainViewModel.getSubscriptions(this)
if (listId == null || listRemarks == null) {
return
}
for (it in listRemarks.indices) {
val tab = binding.tabGroup.newTab()
tab.text = listRemarks[it]
tab.tag = listId[it]
binding.tabGroup.addTab(tab)
}
val selectIndex =
listId.indexOf(mainViewModel.subscriptionId).takeIf { it >= 0 } ?: (listId.count() - 1)
binding.tabGroup.selectTab(binding.tabGroup.getTabAt(selectIndex))
binding.tabGroup.addOnTabSelectedListener(tabGroupListener)
binding.tabGroup.isVisible = true
}
fun startV2Ray() { fun startV2Ray() {
if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) { if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) {
return return
@@ -309,11 +351,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
mainViewModel.reloadServerList() mainViewModel.reloadServerList()
true true
} }
R.id.filter_config -> {
mainViewModel.filterConfig(this)
true
}
else -> super.onOptionsItemSelected(item) else -> super.onOptionsItemSelected(item)
} }
@@ -384,18 +421,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.show() .show()
lifecycleScope.launch(Dispatchers.IO) { lifecycleScope.launch(Dispatchers.IO) {
val count = AngConfigManager.importBatchConfig(server, mainViewModel.subscriptionId, true) val (count, countSub) = AngConfigManager.importBatchConfig(server, mainViewModel.subscriptionId, true)
delay(500L) delay(500L)
launch(Dispatchers.Main) { launch(Dispatchers.Main) {
if (count > 0) { if (count > 0) {
toast(R.string.toast_success) toast(R.string.toast_success)
mainViewModel.reloadServerList() mainViewModel.reloadServerList()
} else if (countSub > 0) {
initGroupTab()
} else { } else {
toast(R.string.toast_failure) toast(R.string.toast_failure)
} }
dialog.dismiss() dialog.dismiss()
} }
} }
} }
private fun importConfigCustomClipboard() private fun importConfigCustomClipboard()
@@ -585,12 +624,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return super.onKeyDown(keyCode, event) return super.onKeyDown(keyCode, event)
} }
override fun onNavigationItemSelected(item: MenuItem): Boolean { override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here. // Handle navigation view item clicks here.
when (item.itemId) { when (item.itemId) {
//R.id.server_profile -> activityClass = MainActivity::class.java
R.id.sub_setting -> { R.id.sub_setting -> {
startActivity(Intent(this, SubSettingActivity::class.java)) requestSubSettingActivity.launch(Intent(this,SubSettingActivity::class.java))
} }
R.id.settings -> { R.id.settings -> {
startActivity(Intent(this, SettingsActivity::class.java) startActivity(Intent(this, SettingsActivity::class.java)
@@ -1,13 +1,13 @@
package com.v2ray.ang.ui package com.v2ray.ang.ui
import android.Manifest import android.Manifest
import android.content.* import android.content.Intent
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.util.AngConfigManager
import android.os.Bundle import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.extension.toast import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
class ScScannerActivity : BaseActivity() { class ScScannerActivity : BaseActivity() {
@@ -32,8 +32,8 @@ class ScScannerActivity : BaseActivity() {
private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) { if (it.resultCode == RESULT_OK) {
val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "", false) val (count, countSub) = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "", false)
if (count > 0) { if (count + countSub > 0) {
toast(R.string.toast_success) toast(R.string.toast_success)
} else { } else {
toast(R.string.toast_failure) toast(R.string.toast_failure)
@@ -64,8 +64,8 @@ class UrlSchemeActivity : BaseActivity() {
val decodedUrl = URLDecoder.decode(uriString, "UTF-8") val decodedUrl = URLDecoder.decode(uriString, "UTF-8")
val uri = Uri.parse(decodedUrl) val uri = Uri.parse(decodedUrl)
if (uri != null) { if (uri != null) {
val count = AngConfigManager.importBatchConfig(decodedUrl, "", false) val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false)
if (count > 0) { if (count + countSub > 0) {
toast(R.string.import_subscription_success) toast(R.string.import_subscription_success)
} else { } else {
toast(R.string.import_subscription_failure) toast(R.string.import_subscription_failure)
@@ -386,7 +386,7 @@ object AngConfigManager {
// } // }
// } // }
fun importBatchConfig(server: String?, subid: String, append: Boolean): Int { fun importBatchConfig(server: String?, subid: String, append: Boolean): Pair<Int, Int> {
var count = parseBatchConfig(Utils.decode(server), subid, append) var count = parseBatchConfig(Utils.decode(server), subid, append)
if (count <= 0) { if (count <= 0) {
count = parseBatchConfig(server, subid, append) count = parseBatchConfig(server, subid, append)
@@ -403,7 +403,7 @@ object AngConfigManager {
updateConfigViaSubAll() updateConfigViaSubAll()
} }
return count + countSub return count to countSub
} }
fun parseBatchSubscription(servers: String?): Int { fun parseBatchSubscription(servers: String?): Int {
@@ -8,7 +8,6 @@ import android.content.IntentFilter
import android.content.res.AssetManager import android.content.res.AssetManager
import android.os.Build import android.os.Build
import android.util.Log import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.AndroidViewModel import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
@@ -60,7 +59,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
var serverList = MmkvManager.decodeServerList() var serverList = MmkvManager.decodeServerList()
var subscriptionId: String = settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "")?:"" var subscriptionId: String = settingsStorage.decodeString(AppConfig.CACHE_SUBSCRIPTION_ID, "")?:""
var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:"" //var keywordFilter: String = settingsStorage.decodeString(AppConfig.CACHE_KEYWORD_FILTER, "")?:""
private set private set
val serversCache = mutableListOf<ServersCache>() val serversCache = mutableListOf<ServersCache>()
val isRunning by lazy { MutableLiveData<Boolean>() } val isRunning by lazy { MutableLiveData<Boolean>() }
@@ -146,9 +145,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
continue continue
} }
if (keywordFilter.isEmpty() || config.remarks.contains(keywordFilter)) { // if (keywordFilter.isEmpty() || config.remarks.contains(keywordFilter)) {
serversCache.add(ServersCache(guid, config)) serversCache.add(ServersCache(guid, config))
} // }
} }
} }
@@ -202,25 +201,30 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_MEASURE_DELAY, "") MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_MEASURE_DELAY, "")
} }
fun filterConfig(context: Context) { fun subscriptionIdChanged(id: String) {
val subscriptions = MmkvManager.decodeSubscriptions() if (subscriptionId != id) {
val listId = subscriptions.map { it.first }.toMutableList() subscriptionId = id
val listRemarks = subscriptions.map { it.second.remarks }.toMutableList() settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId)
listRemarks += context.getString(R.string.filter_config_all) reloadServerList()
val checkedItem = listId.indexOf(subscriptionId).takeIf { it >= 0 } ?: listRemarks.count() - 1 }
}
AlertDialog.Builder(context) fun getSubscriptions(context: Context) : Pair<MutableList<String>?, MutableList<String>?> {
.setSingleChoiceItems(listRemarks.toTypedArray(), checkedItem) { dialog, i -> val subscriptions = MmkvManager.decodeSubscriptions()
try { if (subscriptionId.isNotEmpty()
subscriptionId = if (listRemarks.count() - 1 == i) "" else subscriptions[i].first && !subscriptions.map { it.first }.contains(subscriptionId)
settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId) ) {
reloadServerList() subscriptionIdChanged("")
dialog.dismiss() }
} catch (e: Exception) { if (subscriptions.isEmpty()) {
e.printStackTrace() return null to null
} }
} val listId = subscriptions.map { it.first }.toMutableList()
.show() listId.add(0, "")
val listRemarks = subscriptions.map { it.second.remarks }.toMutableList()
listRemarks.add(0, context.getString(R.string.filter_config_all))
return listId to listRemarks
} }
fun getPosition(guid: String): Int { fun getPosition(guid: String): Int {
@@ -37,6 +37,14 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<com.google.android.material.tabs.TabLayout
android:id="@+id/tab_group"
app:tabMode="scrollable"
app:tabTextAppearance="@style/TabLayoutTextStyle"
app:tabIndicatorFullWidth="false"
android:layout_height="wrap_content"
android:layout_width="match_parent"/>
<androidx.recyclerview.widget.RecyclerView <androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view" android:id="@+id/recycler_view"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -63,11 +63,6 @@
</item> </item>
</menu> </menu>
</item> </item>
<item
android:id="@+id/filter_config"
android:icon="@drawable/ic_outline_filter_alt_24"
android:title="@string/title_filter_config"
app:showAsAction="ifRoom" />
<item <item
android:id="@+id/service_restart" android:id="@+id/service_restart"
android:title="@string/title_service_restart" android:title="@string/title_service_restart"
@@ -1,3 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<style name="TabLayoutTextStyle" parent="TextAppearance.Design.Tab">
<item name="textAllCaps">false</item>
</style>
</resources> </resources>