diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt index e4fe27933..039be42df 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetActivity.kt @@ -21,6 +21,7 @@ import com.v2ray.ang.AppConfig import com.v2ray.ang.R import com.v2ray.ang.databinding.ActivitySubSettingBinding import com.v2ray.ang.databinding.ItemRecyclerUserAssetBinding +import com.v2ray.ang.dto.AssetUrlItem import com.v2ray.ang.extension.toTrafficString import com.v2ray.ang.extension.toast import com.v2ray.ang.util.MmkvManager @@ -39,9 +40,11 @@ import java.util.* class UserAssetActivity : BaseActivity() { private lateinit var binding: ActivitySubSettingBinding private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) } + private val assetStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_ASSET, MMKV.MULTI_PROCESS_MODE) } val extDir by lazy { File(Utils.userAssetPath(this)) } - val geofiles = arrayOf("geosite.dat", "geoip.dat") + val builtInGeoFiles = arrayOf("geosite.dat", "geoip.dat") + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -55,6 +58,11 @@ class UserAssetActivity : BaseActivity() { binding.recyclerView.adapter = UserAssetAdapter() } + override fun onResume() { + super.onResume() + binding.recyclerView.adapter?.notifyDataSetChanged() + } + override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.menu_asset, menu) return super.onCreateOptionsMenu(menu) @@ -148,31 +156,33 @@ class UserAssetActivity : BaseActivity() { val httpPort = Utils.parseInt(settingsStorage?.decodeString(AppConfig.PREF_HTTP_PORT), AppConfig.PORT_HTTP.toInt()) toast(R.string.msg_downloading_content) - geofiles.forEach { + var assets = MmkvManager.decodeAssetUrls() + assets = addBuiltInGeoItems(assets) + + assets.forEach { //toast(getString(R.string.msg_downloading_content) + it) lifecycleScope.launch(Dispatchers.IO) { - val result = downloadGeo(it, 60000, httpPort) + val result = downloadGeo(it.second, 60000, httpPort) launch(Dispatchers.Main) { if (result) { - toast(getString(R.string.toast_success) + " " + it) + toast(getString(R.string.toast_success) + " " + it.second.remarks) binding.recyclerView.adapter?.notifyDataSetChanged() } else { - toast(getString(R.string.toast_failure) + " " + it) + toast(getString(R.string.toast_failure) + " " + it.second.remarks) } } } } } - private fun downloadGeo(name: String, timeout: Int, httpPort: Int): Boolean { - val url = AppConfig.geoUrl + name - val targetTemp = File(extDir, name + "_temp") - val target = File(extDir, name) + private fun downloadGeo(item: AssetUrlItem, timeout: Int, httpPort: Int): Boolean { + val targetTemp = File(extDir, item.remarks + "_temp") + val target = File(extDir, item.remarks) var conn: HttpURLConnection? = null //Log.d(AppConfig.ANG_PACKAGE, url) try { - conn = URL(url).openConnection( + conn = URL(item.url).openConnection( Proxy( Proxy.Type.HTTP, InetSocketAddress("127.0.0.1", httpPort) @@ -197,33 +207,74 @@ class UserAssetActivity : BaseActivity() { conn?.disconnect() } } + private fun addBuiltInGeoItems(assets: List>): List> { + val list = mutableListOf>() + builtInGeoFiles.forEach { + list.add(Utils.getUuid() to AssetUrlItem( + it, + AppConfig.geoUrl + it + ) + ) + } + + return list + assets + } inner class UserAssetAdapter : RecyclerView.Adapter() { override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): UserAssetViewHolder { - return UserAssetViewHolder(ItemRecyclerUserAssetBinding.inflate(LayoutInflater.from(parent.context), parent, false)) + return UserAssetViewHolder( + ItemRecyclerUserAssetBinding.inflate( + LayoutInflater.from(parent.context), + parent, + false) + ) } @SuppressLint("SetTextI18n") override fun onBindViewHolder(holder: UserAssetViewHolder, position: Int) { - val file = extDir.listFiles()?.getOrNull(position) ?: return - holder.itemUserAssetBinding.assetName.text = file.name - val dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) - holder.itemUserAssetBinding.assetProperties.text = "${file.length().toTrafficString()} • ${dateFormat.format(Date(file.lastModified()))}" - if (file.name in geofiles) { + var assets = MmkvManager.decodeAssetUrls(); + assets = addBuiltInGeoItems(assets); + val item = assets.getOrNull(position) ?: return +// file with name == item.second.remarks + val file = extDir.listFiles()?.find { it.name == item.second.remarks } + + holder.itemUserAssetBinding.assetName.text = item.second.remarks + + if (file != null) { + val dateFormat = DateFormat.getDateTimeInstance(DateFormat.MEDIUM, DateFormat.MEDIUM) + holder.itemUserAssetBinding.assetProperties.text = + "${file.length().toTrafficString()} • ${dateFormat.format(Date(file.lastModified()))}" + } else { + holder.itemUserAssetBinding.assetProperties.text = getString(R.string.msg_file_not_found) + } + + if (item.second.remarks in builtInGeoFiles) { + holder.itemUserAssetBinding.layoutEdit.visibility = GONE holder.itemUserAssetBinding.layoutRemove.visibility = GONE } else { + holder.itemUserAssetBinding.layoutEdit.visibility = VISIBLE holder.itemUserAssetBinding.layoutRemove.visibility = VISIBLE } + + holder.itemUserAssetBinding.layoutEdit.setOnClickListener { + val intent = Intent(this@UserAssetActivity, UserAssetUrlActivity::class.java) + intent.putExtra("assetId", item.first) + startActivity(intent) + } holder.itemUserAssetBinding.layoutRemove.setOnClickListener { - file.delete() +// file.delete() + MmkvManager.removeAssetUrl(item.first) binding.recyclerView.adapter?.notifyItemRemoved(position) } } override fun getItemCount(): Int { - return extDir.listFiles()?.size ?: 0 + var assets = MmkvManager.decodeAssetUrls(); + assets = addBuiltInGeoItems(assets); + return assets.size } } - class UserAssetViewHolder(val itemUserAssetBinding: ItemRecyclerUserAssetBinding) : RecyclerView.ViewHolder(itemUserAssetBinding.root) + class UserAssetViewHolder(val itemUserAssetBinding: ItemRecyclerUserAssetBinding) : + RecyclerView.ViewHolder(itemUserAssetBinding.root) } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt index 3199227dd..749c30ccc 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/UserAssetUrlActivity.kt @@ -21,7 +21,8 @@ class UserAssetUrlActivity : BaseActivity() { var save_config: MenuItem? = null private val assetStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_ASSET, MMKV.MULTI_PROCESS_MODE) } - private val editAssetId by lazy { intent.getStringExtra("assetUrlId").orEmpty() } + private val editAssetId by lazy { intent.getStringExtra("assetId").orEmpty() } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) binding = ActivityUserAssetUrlBinding.inflate(layoutInflater) @@ -94,7 +95,7 @@ class UserAssetUrlActivity : BaseActivity() { if (editAssetId.isNotEmpty()) { AlertDialog.Builder(this).setMessage(R.string.del_config_comfirm) .setPositiveButton(android.R.string.ok) { _, _ -> - MmkvManager.removeSubscription(editAssetId) + MmkvManager.removeAssetUrl(editAssetId) finish() } .show() 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 9683c6d38..f00ab0e84 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 @@ -2,6 +2,7 @@ package com.v2ray.ang.util import com.google.gson.Gson import com.tencent.mmkv.MMKV +import com.v2ray.ang.dto.AssetUrlItem import com.v2ray.ang.dto.ServerAffiliationInfo import com.v2ray.ang.dto.ServerConfig import com.v2ray.ang.dto.SubscriptionItem @@ -21,6 +22,7 @@ object MmkvManager { private val serverStorage by lazy { MMKV.mmkvWithID(ID_SERVER_CONFIG, MMKV.MULTI_PROCESS_MODE) } private val serverAffStorage by lazy { MMKV.mmkvWithID(ID_SERVER_AFF, MMKV.MULTI_PROCESS_MODE) } private val subStorage by lazy { MMKV.mmkvWithID(ID_SUB, MMKV.MULTI_PROCESS_MODE) } + private val assetStorage by lazy { MMKV.mmkvWithID(ID_ASSET, MMKV.MULTI_PROCESS_MODE) } fun decodeServerList(): MutableList { val json = mainStorage?.decodeString(KEY_ANG_CONFIGS) @@ -143,6 +145,22 @@ object MmkvManager { removeServerViaSubid(subid) } + fun decodeAssetUrls(): List> { + val assetUrlItems = mutableListOf>() + assetStorage?.allKeys()?.forEach { key -> + val json = assetStorage?.decodeString(key) + if (!json.isNullOrBlank()) { + assetUrlItems.add(Pair(key, Gson().fromJson(json, AssetUrlItem::class.java))) + } + } + assetUrlItems.sortedBy { (_, value) -> value.addedTime } + return assetUrlItems + } + + fun removeAssetUrl(assetid: String) { + assetStorage?.remove(assetid) + } + fun removeAllServer() { mainStorage?.clearAll() serverStorage?.clearAll() diff --git a/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml b/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml index 7ae979126..1f1e1533a 100644 --- a/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml +++ b/V2rayNG/app/src/main/res/layout/item_recycler_user_asset.xml @@ -42,6 +42,22 @@ tools:text="1MB . 2020.01.01" /> + + + + + المستخدم (اختياري) التشفير التدفق - مفتاح عام - ShortId - SpiderX Reserved (اختياري) العنوان المحلي IPv4(اختياري) Mtu(optional, default 1420) @@ -82,6 +79,8 @@ إضافة ملفات تحميل الملفات + أضف عنوان URL للأصل + لم يتم العثور على الملف جار التحميل بحث تحديد الكل diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index beff2ba2d..e0a9d4c09 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -82,6 +82,8 @@ دانلود فایل‌ها + URL را اضافه کنید + فایل پیدا نشد بارگذاری جستجو انتخاب همه diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 560c2c884..932537081 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -49,8 +49,6 @@ Запрос узла (WS/H2) / Шифрование QUIC Путь (WS/H2) / Ключ QUIC / Сид KCP / Сервис gRPC TLS - Fingerprint - Alpn Разрешать небезопасные SNI Адрес @@ -84,6 +82,8 @@ Загрузить файлы + Добавить URL ресурса + Файл не найден Загрузка… Поиск Выбрать все diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index b7bb7e71d..bf2ad2802 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -82,6 +82,8 @@ Tải xuống tệp tin + Thêm URL nội dung + Không tìm thấy tập tin Đang tải... Tìm kiếm Chọn tất cả diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index ba70b6523..887c25a8c 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -83,6 +83,8 @@ + 添加资产网址 + 文件未找到 正在加载 搜索 全选 diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index ef18d37c3..3bc7cdd6f 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -82,6 +82,8 @@ 下載檔案 + 新增資產網址 + 文件未找到 載入 搜尋 全選 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index d358d43d9..91616d3d5 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -90,7 +90,8 @@ Add URL URL Download files - Add asset URL + Add asset URL + File not found