Optimized UI

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

View File

@@ -20,10 +20,12 @@ import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AlertDialog
import androidx.core.content.ContextCompat
import androidx.core.view.GravityCompat
import androidx.core.view.isVisible
import androidx.lifecycle.lifecycleScope
import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.navigation.NavigationView
import com.google.android.material.tabs.TabLayout
import com.tbruyelle.rxpermissions.RxPermissions
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
@@ -57,6 +59,23 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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
val mainViewModel: MainViewModel by viewModels()
@@ -106,7 +125,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
toggle.syncState()
binding.navView.setNavigationItemSelectedListener(this)
initGroupTab()
setupViewModel()
mainViewModel.copyAssets(assets)
@@ -157,6 +176,29 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
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() {
if (mainStorage?.decodeString(MmkvManager.KEY_SELECTED_SERVER).isNullOrEmpty()) {
return
@@ -309,11 +351,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
mainViewModel.reloadServerList()
true
}
R.id.filter_config -> {
mainViewModel.filterConfig(this)
true
}
else -> super.onOptionsItemSelected(item)
}
@@ -384,18 +421,20 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
.show()
lifecycleScope.launch(Dispatchers.IO) {
val count = AngConfigManager.importBatchConfig(server, mainViewModel.subscriptionId, true)
val (count, countSub) = AngConfigManager.importBatchConfig(server, mainViewModel.subscriptionId, true)
delay(500L)
launch(Dispatchers.Main) {
if (count > 0) {
toast(R.string.toast_success)
mainViewModel.reloadServerList()
} else if (countSub > 0) {
initGroupTab()
} else {
toast(R.string.toast_failure)
}
dialog.dismiss()
}
}
}
}
private fun importConfigCustomClipboard()
@@ -585,12 +624,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
return super.onKeyDown(keyCode, event)
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
// Handle navigation view item clicks here.
when (item.itemId) {
//R.id.server_profile -> activityClass = MainActivity::class.java
R.id.sub_setting -> {
startActivity(Intent(this, SubSettingActivity::class.java))
requestSubSettingActivity.launch(Intent(this,SubSettingActivity::class.java))
}
R.id.settings -> {
startActivity(Intent(this, SettingsActivity::class.java)

View File

@@ -1,13 +1,13 @@
package com.v2ray.ang.ui
import android.Manifest
import android.content.*
import com.tbruyelle.rxpermissions.RxPermissions
import com.v2ray.ang.R
import com.v2ray.ang.util.AngConfigManager
import android.content.Intent
import android.os.Bundle
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.util.AngConfigManager
class ScScannerActivity : BaseActivity() {
@@ -32,8 +32,8 @@ class ScScannerActivity : BaseActivity() {
private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
if (it.resultCode == RESULT_OK) {
val count = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "", false)
if (count > 0) {
val (count, countSub) = AngConfigManager.importBatchConfig(it.data?.getStringExtra("SCAN_RESULT"), "", false)
if (count + countSub > 0) {
toast(R.string.toast_success)
} else {
toast(R.string.toast_failure)

View File

@@ -64,8 +64,8 @@ class UrlSchemeActivity : BaseActivity() {
val decodedUrl = URLDecoder.decode(uriString, "UTF-8")
val uri = Uri.parse(decodedUrl)
if (uri != null) {
val count = AngConfigManager.importBatchConfig(decodedUrl, "", false)
if (count > 0) {
val (count, countSub) = AngConfigManager.importBatchConfig(decodedUrl, "", false)
if (count + countSub > 0) {
toast(R.string.import_subscription_success)
} else {
toast(R.string.import_subscription_failure)

View File

@@ -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)
if (count <= 0) {
count = parseBatchConfig(server, subid, append)
@@ -403,7 +403,7 @@ object AngConfigManager {
updateConfigViaSubAll()
}
return count + countSub
return count to countSub
}
fun parseBatchSubscription(servers: String?): Int {

View File

@@ -8,7 +8,6 @@ import android.content.IntentFilter
import android.content.res.AssetManager
import android.os.Build
import android.util.Log
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
@@ -60,7 +59,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
var serverList = MmkvManager.decodeServerList()
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
val serversCache = mutableListOf<ServersCache>()
val isRunning by lazy { MutableLiveData<Boolean>() }
@@ -146,9 +145,9 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
continue
}
if (keywordFilter.isEmpty() || config.remarks.contains(keywordFilter)) {
// if (keywordFilter.isEmpty() || config.remarks.contains(keywordFilter)) {
serversCache.add(ServersCache(guid, config))
}
// }
}
}
@@ -202,25 +201,30 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
MessageUtil.sendMsg2Service(getApplication(), AppConfig.MSG_MEASURE_DELAY, "")
}
fun filterConfig(context: Context) {
val subscriptions = MmkvManager.decodeSubscriptions()
val listId = subscriptions.map { it.first }.toMutableList()
val listRemarks = subscriptions.map { it.second.remarks }.toMutableList()
listRemarks += context.getString(R.string.filter_config_all)
val checkedItem = listId.indexOf(subscriptionId).takeIf { it >= 0 } ?: listRemarks.count() - 1
fun subscriptionIdChanged(id: String) {
if (subscriptionId != id) {
subscriptionId = id
settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId)
reloadServerList()
}
}
AlertDialog.Builder(context)
.setSingleChoiceItems(listRemarks.toTypedArray(), checkedItem) { dialog, i ->
try {
subscriptionId = if (listRemarks.count() - 1 == i) "" else subscriptions[i].first
settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId)
reloadServerList()
dialog.dismiss()
} catch (e: Exception) {
e.printStackTrace()
}
}
.show()
fun getSubscriptions(context: Context) : Pair<MutableList<String>?, MutableList<String>?> {
val subscriptions = MmkvManager.decodeSubscriptions()
if (subscriptionId.isNotEmpty()
&& !subscriptions.map { it.first }.contains(subscriptionId)
) {
subscriptionIdChanged("")
}
if (subscriptions.isEmpty()) {
return null to null
}
val listId = subscriptions.map { it.first }.toMutableList()
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 {

View File

@@ -37,6 +37,14 @@
android:layout_height="match_parent"
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
android:id="@+id/recycler_view"
android:layout_width="match_parent"

View File

@@ -63,11 +63,6 @@
</item>
</menu>
</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
android:id="@+id/service_restart"
android:title="@string/title_service_restart"

View File

@@ -1,3 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="TabLayoutTextStyle" parent="TextAppearance.Design.Tab">
<item name="textAllCaps">false</item>
</style>
</resources>