diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt index f8f5e45bb..05fffbbdc 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainActivity.kt @@ -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) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt index da1cb7cde..95361b90d 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ScScannerActivity.kt @@ -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) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt index 83b674590..3668fa716 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UrlSchemeActivity.kt @@ -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) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index f6825317f..0b60190a3 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -386,7 +386,7 @@ object AngConfigManager { // } // } - fun importBatchConfig(server: String?, subid: String, append: Boolean): Int { + fun importBatchConfig(server: String?, subid: String, append: Boolean): Pair { 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 { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt index e3d9d25e7..5f7c99138 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/MainViewModel.kt @@ -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() val isRunning by lazy { MutableLiveData() } @@ -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) { + fun subscriptionIdChanged(id: String) { + if (subscriptionId != id) { + subscriptionId = id + settingsStorage.encode(AppConfig.CACHE_SUBSCRIPTION_ID, subscriptionId) + reloadServerList() + } + } + + fun getSubscriptions(context: Context) : Pair?, MutableList?> { 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 += context.getString(R.string.filter_config_all) - val checkedItem = listId.indexOf(subscriptionId).takeIf { it >= 0 } ?: listRemarks.count() - 1 + listRemarks.add(0, context.getString(R.string.filter_config_all)) - 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() + return listId to listRemarks } fun getPosition(guid: String): Int { diff --git a/V2rayNG/app/src/main/res/layout/activity_main.xml b/V2rayNG/app/src/main/res/layout/activity_main.xml index 5d8218f0d..77934e7b4 100644 --- a/V2rayNG/app/src/main/res/layout/activity_main.xml +++ b/V2rayNG/app/src/main/res/layout/activity_main.xml @@ -37,6 +37,14 @@ android:layout_height="match_parent" android:orientation="vertical"> + + - + \ No newline at end of file