diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/OnStartDragListener.java b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/OnStartDragListener.java deleted file mode 100644 index a6407b6f..00000000 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/OnStartDragListener.java +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright (C) 2015 Paul Burke - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.v2ray.ang.helper; - -import androidx.recyclerview.widget.RecyclerView; - -/** - * Listener for manual initiation of a drag. - */ -public interface OnStartDragListener { - - /** - * Called when a view is requesting a start of a drag. - * - * @param viewHolder The holder of the view to drag. - */ - void onStartDrag(RecyclerView.ViewHolder viewHolder); - -} 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 ffef8f4a..cc7d9d31 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 @@ -112,11 +112,9 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = adapter - val callback = SimpleItemTouchHelperCallback(adapter) - mItemTouchHelper = ItemTouchHelper(callback) + mItemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback(adapter)) mItemTouchHelper?.attachToRecyclerView(binding.recyclerView) - val toggle = ActionBarDrawerToggle( this, binding.drawerLayout, binding.toolbar, R.string.navigation_drawer_open, R.string.navigation_drawer_close ) diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt index 205ff11e..d163a5f0 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/MainRecyclerAdapter.kt @@ -230,31 +230,16 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter fromPosition) - notifyItemRangeChanged(fromPosition, toPosition - fromPosition + 1) - else - notifyItemRangeChanged(toPosition, fromPosition - toPosition + 1) return true } override fun onItemMoveCompleted() { // do nothing } + + override fun onItemDismiss(position: Int) { + } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingEditActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingEditActivity.kt index f7f20761..9e94ecd3 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingEditActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingEditActivity.kt @@ -73,7 +73,6 @@ class RoutingEditActivity : BaseActivity() { return false } - Log.d("====", Gson().toJson(rulesetItem)) SettingsManager.saveRoutingRuleset(position, rulesetItem) toast(R.string.toast_success) finish() diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingActivity.kt index 13d0cb71..b67915c6 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingActivity.kt @@ -8,12 +8,14 @@ import android.view.View import android.widget.AdapterView import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivityRoutingSettingBinding import com.v2ray.ang.dto.RulesetItem import com.v2ray.ang.extension.toast +import com.v2ray.ang.helper.SimpleItemTouchHelperCallback import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.MmkvManager.settingsStorage import com.v2ray.ang.util.SettingsManager @@ -26,6 +28,7 @@ class RoutingSettingActivity : BaseActivity() { var rulesets: MutableList = mutableListOf() private val adapter by lazy { RoutingSettingRecyclerAdapter(this) } + private var mItemTouchHelper: ItemTouchHelper? = null private val routing_domain_strategy: Array by lazy { resources.getStringArray(R.array.routing_domain_strategy) } @@ -43,6 +46,9 @@ class RoutingSettingActivity : BaseActivity() { binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = adapter + mItemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback(adapter)) + mItemTouchHelper?.attachToRecyclerView(binding.recyclerView) + val found = Utils.arrayFind(routing_domain_strategy, settingsStorage?.decodeString(AppConfig.PREF_ROUTING_DOMAIN_STRATEGY) ?: "") found.let { binding.spDomainStrategy.setSelection(if (it >= 0) it else 0) } binding.spDomainStrategy.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { @@ -105,7 +111,7 @@ class RoutingSettingActivity : BaseActivity() { else -> super.onOptionsItemSelected(item) } - private fun refreshData() { + fun refreshData() { rulesets = MmkvManager.decodeRoutingRulesets() ?: mutableListOf() adapter.notifyDataSetChanged() } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt index aecf7c5a..a95b7510 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/RoutingSettingRecyclerAdapter.kt @@ -3,13 +3,17 @@ package com.v2ray.ang.ui import android.content.Intent import android.graphics.Color import android.view.LayoutInflater +import android.view.View import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView import com.v2ray.ang.databinding.ItemRecyclerRoutingSettingBinding +import com.v2ray.ang.helper.ItemTouchHelperAdapter +import com.v2ray.ang.helper.ItemTouchHelperViewHolder +import com.v2ray.ang.ui.MainRecyclerAdapter.BaseViewHolder import com.v2ray.ang.util.SettingsManager -class RoutingSettingRecyclerAdapter(val activity: RoutingSettingActivity) : - RecyclerView.Adapter() { +class RoutingSettingRecyclerAdapter(val activity: RoutingSettingActivity) : RecyclerView.Adapter(), + ItemTouchHelperAdapter { private var mActivity: RoutingSettingActivity = activity override fun getItemCount() = mActivity.rulesets.size @@ -47,5 +51,28 @@ class RoutingSettingRecyclerAdapter(val activity: RoutingSettingActivity) : } class MainViewHolder(val itemRoutingSettingBinding: ItemRecyclerRoutingSettingBinding) : - RecyclerView.ViewHolder(itemRoutingSettingBinding.root) + BaseViewHolder(itemRoutingSettingBinding.root), ItemTouchHelperViewHolder + + open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun onItemSelected() { + itemView.setBackgroundColor(Color.LTGRAY) + } + + fun onItemClear() { + itemView.setBackgroundColor(0) + } + } + + override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { + SettingsManager.swapRoutingRuleset(fromPosition, toPosition) + notifyItemMoved(fromPosition, toPosition) + return true + } + + override fun onItemMoveCompleted() { + mActivity.refreshData() + } + + override fun onItemDismiss(position: Int) { + } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt index 7cf048f7..8c5fbb13 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubEditActivity.kt @@ -69,11 +69,9 @@ class SubEditActivity : BaseActivity() { private fun saveServer(): Boolean { val subItem: SubscriptionItem val json = subStorage?.decodeString(editSubId) - var subId = editSubId if (!json.isNullOrBlank()) { subItem = Gson().fromJson(json, SubscriptionItem::class.java) } else { - subId = Utils.getUuid() subItem = SubscriptionItem() } @@ -93,7 +91,7 @@ class SubEditActivity : BaseActivity() { // return false // } - subStorage?.encode(subId, Gson().toJson(subItem)) + MmkvManager.encodeSubscription(editSubId, subItem) toast(R.string.toast_success) finish() return true diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt index 29d98900..be637eb9 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingActivity.kt @@ -6,12 +6,14 @@ import android.view.Menu import android.view.MenuItem import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope +import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.LinearLayoutManager import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivitySubSettingBinding import com.v2ray.ang.databinding.LayoutProgressBinding import com.v2ray.ang.dto.SubscriptionItem import com.v2ray.ang.extension.toast +import com.v2ray.ang.helper.SimpleItemTouchHelperCallback import com.v2ray.ang.util.AngConfigManager import com.v2ray.ang.util.MmkvManager import kotlinx.coroutines.Dispatchers @@ -23,6 +25,7 @@ class SubSettingActivity : BaseActivity() { var subscriptions: List> = listOf() private val adapter by lazy { SubSettingRecyclerAdapter(this) } + private var mItemTouchHelper: ItemTouchHelper? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -33,12 +36,14 @@ class SubSettingActivity : BaseActivity() { binding.recyclerView.setHasFixedSize(true) binding.recyclerView.layoutManager = LinearLayoutManager(this) binding.recyclerView.adapter = adapter + + mItemTouchHelper = ItemTouchHelper(SimpleItemTouchHelperCallback(adapter)) + mItemTouchHelper?.attachToRecyclerView(binding.recyclerView) } override fun onResume() { super.onResume() - subscriptions = MmkvManager.decodeSubscriptions() - adapter.notifyDataSetChanged() + refreshData() } override fun onCreateOptionsMenu(menu: Menu): Boolean { @@ -77,4 +82,9 @@ class SubSettingActivity : BaseActivity() { else -> super.onOptionsItemSelected(item) } + + fun refreshData() { + subscriptions = MmkvManager.decodeSubscriptions() + adapter.notifyDataSetChanged() + } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt index e43951c7..ac73d78b 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SubSettingRecyclerAdapter.kt @@ -8,17 +8,18 @@ import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog import androidx.recyclerview.widget.RecyclerView -import com.google.gson.Gson import com.v2ray.ang.R import com.v2ray.ang.databinding.ItemQrcodeBinding import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding import com.v2ray.ang.extension.toast -import com.v2ray.ang.util.MmkvManager.subStorage +import com.v2ray.ang.helper.ItemTouchHelperAdapter +import com.v2ray.ang.helper.ItemTouchHelperViewHolder +import com.v2ray.ang.util.MmkvManager import com.v2ray.ang.util.QRCodeDecoder +import com.v2ray.ang.util.SettingsManager import com.v2ray.ang.util.Utils -class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : - RecyclerView.Adapter() { +class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter(), ItemTouchHelperAdapter { private var mActivity: SubSettingActivity = activity @@ -45,7 +46,8 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : holder.itemSubSettingBinding.chkEnable.setOnCheckedChangeListener { _, isChecked -> subItem.enabled = isChecked - subStorage?.encode(subId, Gson().toJson(subItem)) + MmkvManager.encodeSubscription(subId, subItem) + } if (TextUtils.isEmpty(subItem.url)) { @@ -93,5 +95,28 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : } class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) : - RecyclerView.ViewHolder(itemSubSettingBinding.root) + BaseViewHolder(itemSubSettingBinding.root), ItemTouchHelperViewHolder + + open class BaseViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) { + fun onItemSelected() { + itemView.setBackgroundColor(Color.LTGRAY) + } + + fun onItemClear() { + itemView.setBackgroundColor(0) + } + } + + override fun onItemMove(fromPosition: Int, toPosition: Int): Boolean { + SettingsManager.swapSubscriptions(fromPosition, toPosition) + notifyItemMoved(fromPosition, toPosition) + return true + } + + override fun onItemMoveCompleted() { + mActivity.refreshData() + } + + override fun onItemDismiss(position: Int) { + } } 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 2286405d..5301df10 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 @@ -15,7 +15,6 @@ import com.v2ray.ang.R import com.v2ray.ang.dto.* import com.v2ray.ang.util.MmkvManager.serverRawStorage import com.v2ray.ang.util.MmkvManager.settingsStorage -import com.v2ray.ang.util.MmkvManager.subStorage import com.v2ray.ang.util.fmt.ShadowsocksFmt import com.v2ray.ang.util.fmt.SocksFmt import com.v2ray.ang.util.fmt.TrojanFmt @@ -27,162 +26,6 @@ import java.net.URI import java.util.* object AngConfigManager { - - /** - * Legacy loading config - */ -// fun migrateLegacyConfig(c: Context): Boolean? { -// try { -// val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(c) -// val context = defaultSharedPreferences.getString(ANG_CONFIG, "") -// if (context.isNullOrBlank()) { -// return null -// } -// val angConfig = Gson().fromJson(context, AngConfig::class.java) -// for (i in angConfig.vmess.indices) { -// upgradeServerVersion(angConfig.vmess[i]) -// } -// -// copyLegacySettings(defaultSharedPreferences) -// migrateVmessBean(angConfig, defaultSharedPreferences) -// migrateSubItemBean(angConfig) -// -// defaultSharedPreferences.edit().remove(ANG_CONFIG).apply() -// return true -// } catch (e: Exception) { -// e.printStackTrace() -// } -// return false -// } -// -// private fun copyLegacySettings(sharedPreferences: SharedPreferences) { -// listOf( -// AppConfig.PREF_MODE, -// AppConfig.PREF_REMOTE_DNS, -// AppConfig.PREF_DOMESTIC_DNS, -// AppConfig.PREF_LOCAL_DNS_PORT, -// AppConfig.PREF_SOCKS_PORT, -// AppConfig.PREF_HTTP_PORT, -// AppConfig.PREF_LOGLEVEL, -// AppConfig.PREF_ROUTING_DOMAIN_STRATEGY, -// AppConfig.PREF_V2RAY_ROUTING_AGENT, -// AppConfig.PREF_V2RAY_ROUTING_BLOCKED, -// AppConfig.PREF_V2RAY_ROUTING_DIRECT, -// ).forEach { key -> -// settingsStorage?.encode(key, sharedPreferences.getString(key, null)) -// } -// listOf( -// AppConfig.PREF_SPEED_ENABLED, -// AppConfig.PREF_PROXY_SHARING, -// AppConfig.PREF_LOCAL_DNS_ENABLED, -// AppConfig.PREF_ALLOW_INSECURE, -// AppConfig.PREF_PREFER_IPV6, -// AppConfig.PREF_PER_APP_PROXY, -// AppConfig.PREF_BYPASS_APPS, -// ).forEach { key -> -// settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) -// } -// settingsStorage?.encode( -// AppConfig.PREF_SNIFFING_ENABLED, -// sharedPreferences.getBoolean(AppConfig.PREF_SNIFFING_ENABLED, true) -// ) -// settingsStorage?.encode( -// AppConfig.PREF_PER_APP_PROXY_SET, -// sharedPreferences.getStringSet(AppConfig.PREF_PER_APP_PROXY_SET, setOf()) -// ) -// } -// -// private fun migrateVmessBean(angConfig: AngConfig, sharedPreferences: SharedPreferences) { -// angConfig.vmess.forEachIndexed { index, vmessBean -> -// val type = EConfigType.fromInt(vmessBean.configType) ?: return@forEachIndexed -// val config = ServerConfig.create(type) -// config.remarks = vmessBean.remarks -// config.subscriptionId = vmessBean.subid -// if (type == EConfigType.CUSTOM) { -// val jsonConfig = sharedPreferences.getString(ANG_CONFIG + vmessBean.guid, "") -// val v2rayConfig = try { -// Gson().fromJson(jsonConfig, V2rayConfig::class.java) -// } catch (e: Exception) { -// e.printStackTrace() -// return@forEachIndexed -// } -// config.fullConfig = v2rayConfig -// serverRawStorage?.encode(vmessBean.guid, jsonConfig) -// } else { -// config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> -// vnext.address = vmessBean.address -// vnext.port = vmessBean.port -// vnext.users[0].id = vmessBean.id -// if (config.configType == EConfigType.VMESS) { -// vnext.users[0].alterId = vmessBean.alterId -// vnext.users[0].security = vmessBean.security -// } else if (config.configType == EConfigType.VLESS) { -// vnext.users[0].encryption = vmessBean.security -// vnext.users[0].flow = vmessBean.flow -// } -// } -// config.outboundBean?.settings?.servers?.get(0)?.let { server -> -// server.address = vmessBean.address -// server.port = vmessBean.port -// if (config.configType == EConfigType.SHADOWSOCKS) { -// server.password = vmessBean.id -// server.method = vmessBean.security -// } else if (config.configType == EConfigType.SOCKS) { -// if (TextUtils.isEmpty(vmessBean.security) && TextUtils.isEmpty(vmessBean.id)) { -// server.users = null -// } else { -// val socksUsersBean = -// V2rayConfig.OutboundBean.OutSettingsBean.ServersBean.SocksUsersBean() -// socksUsersBean.user = vmessBean.security -// socksUsersBean.pass = vmessBean.id -// server.users = listOf(socksUsersBean) -// } -// } else if (config.configType == EConfigType.TROJAN) { -// server.password = vmessBean.id -// } -// } -// config.outboundBean?.streamSettings?.let { streamSetting -> -// val sni = streamSetting.populateTransportSettings( -// vmessBean.network, -// vmessBean.headerType, -// vmessBean.requestHost, -// vmessBean.path, -// vmessBean.path, -// vmessBean.requestHost, -// vmessBean.path, -// vmessBean.headerType, -// vmessBean.path, -// vmessBean.requestHost, -// ) -// val allowInsecure = if (vmessBean.allowInsecure.isBlank()) { -// settingsStorage?.decodeBool(AppConfig.PREF_ALLOW_INSECURE) ?: false -// } else { -// vmessBean.allowInsecure.toBoolean() -// } -// var fingerprint = streamSetting.tlsSettings?.fingerprint -// streamSetting.populateTlsSettings( -// vmessBean.streamSecurity, allowInsecure, -// vmessBean.sni.ifBlank { sni }, fingerprint, null, null, null, null -// ) -// } -// } -// val key = MmkvManager.encodeServerConfig(vmessBean.guid, config) -// if (index == angConfig.index) { -// mainStorage.encode(KEY_SELECTED_SERVER, key) -// } -// } -// } -// -// private fun migrateSubItemBean(angConfig: AngConfig) { -// angConfig.subItem.forEach { -// val subItem = SubscriptionItem() -// subItem.remarks = it.remarks -// subItem.url = it.url -// subItem.enabled = it.enabled -// subStorage?.encode(it.id, Gson().toJson(subItem)) -// } -// } - /** * parse config form qrcode or... */ @@ -590,7 +433,7 @@ object AngConfigManager { val subItem = SubscriptionItem() subItem.remarks = uri.fragment ?: "import sub" subItem.url = url - subStorage.encode(Utils.getUuid(), Gson().toJson(subItem)) + MmkvManager.encodeSubscription("", subItem) return 1 } } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt index 5d7d5156..b634d87f 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/MmkvManager.kt @@ -21,6 +21,7 @@ object MmkvManager { private const val ID_SETTING = "SETTING" private const val KEY_SELECTED_SERVER = "SELECTED_SERVER" private const val KEY_ANG_CONFIGS = "ANG_CONFIGS" + private const val KEY_SUB_IDS = "SUB_IDS" private val mainStorage by lazy { MMKV.mmkvWithID(ID_MAIN, MMKV.MULTI_PROCESS_MODE) } val settingsStorage by lazy { MMKV.mmkvWithID(ID_SETTING, MMKV.MULTI_PROCESS_MODE) } @@ -153,27 +154,68 @@ object MmkvManager { } } - fun decodeSubscriptions(): List> { - val subscriptions = mutableListOf>() + fun initSubsList() { + val subsList = decodeSubsList() + if (subsList.isNotEmpty()) { + return + } subStorage.allKeys()?.forEach { key -> + subsList.add(key) + } + encodeSubsList(subsList) + } + + fun decodeSubscriptions(): List> { + initSubsList() + + val subscriptions = mutableListOf>() + decodeSubsList().forEach { key -> val json = subStorage.decodeString(key) if (!json.isNullOrBlank()) { subscriptions.add(Pair(key, Gson().fromJson(json, SubscriptionItem::class.java))) } } - return subscriptions.sortedBy { (_, value) -> value.addedTime } + return subscriptions } fun removeSubscription(subid: String) { subStorage.remove(subid) + val subsList = decodeSubsList() + subsList.remove(subid) + encodeSubsList(subsList) + removeServerViaSubid(subid) } + fun encodeSubscription(guid: String, subItem: SubscriptionItem) { + val key = guid.ifBlank { Utils.getUuid() } + subStorage.encode(key, Gson().toJson(subItem)) + + val subsList = decodeSubsList() + if (!subsList.contains(key)) { + subsList.add(key) + encodeSubsList(subsList) + } + } + fun decodeSubscription(subscriptionId: String): SubscriptionItem? { val json = subStorage.decodeString(subscriptionId) ?: return null return Gson().fromJson(json, SubscriptionItem::class.java) } + fun encodeSubsList(subsList: MutableList) { + mainStorage.encode(KEY_SUB_IDS, Gson().toJson(subsList)) + } + + fun decodeSubsList(): MutableList { + val json = mainStorage.decodeString(KEY_SUB_IDS) + return if (json.isNullOrBlank()) { + mutableListOf() + } else { + Gson().fromJson(json, Array::class.java).toMutableList() + } + } + fun decodeAssetUrls(): List> { val assetUrlItems = mutableListOf>() assetStorage.allKeys()?.forEach { key -> diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SettingsManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SettingsManager.kt index c6489db4..de86e58e 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SettingsManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/SettingsManager.kt @@ -2,8 +2,11 @@ package com.v2ray.ang.util import android.content.Context import android.text.TextUtils +import android.util.Log import com.google.gson.Gson import com.v2ray.ang.dto.RulesetItem +import com.v2ray.ang.util.MmkvManager.subStorage +import java.util.Collections object SettingsManager { @@ -71,5 +74,21 @@ object SettingsManager { return exist == true } + fun swapRoutingRuleset(fromPosition: Int, toPosition: Int) { + val rulesetList = MmkvManager.decodeRoutingRulesets() + if (rulesetList.isNullOrEmpty()) return + + Collections.swap(rulesetList, fromPosition, toPosition) + MmkvManager.encodeRoutingRulesets(rulesetList) + } + + fun swapSubscriptions(fromPosition: Int, toPosition: Int) { + val subsList = MmkvManager.decodeSubsList() + if (subsList.isNullOrEmpty()) return + + Collections.swap(subsList, fromPosition, toPosition) + MmkvManager.encodeSubsList(subsList) + } + }