Optimized UI
This commit is contained in:
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>
|
||||||
Reference in new issue
Block a user