diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/profiles/SelectionMode.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/profiles/SelectionMode.kt index e2f7c7ef..3933e973 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/profiles/SelectionMode.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/profiles/SelectionMode.kt @@ -10,11 +10,14 @@ import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.GroupRepo import io.nekohasekai.sagernet.database.ProfileManager import io.nekohasekai.sagernet.database.ProxyEntity +import io.nekohasekai.sagernet.group.SubscriptionClient +import io.nekohasekai.sagernet.ktx.Logs import io.nekohasekai.sagernet.ktx.confirmAction import io.nekohasekai.sagernet.ktx.nameList import io.nekohasekai.sagernet.ktx.showAllowingStateLoss import io.nekohasekai.sagernet.ktx.snackbar import io.nekohasekai.sagernet.ui.ConfigurationFragment +import io.nekohasekai.sagernet.ui.MainActivity import io.nekohasekai.sagernet.widget.QRCodeDialog /** @@ -123,6 +126,16 @@ class SelectionMode(private val host: ConfigurationFragment) { refreshPinTitle() } + private fun invertSelection() { + val shown = host.listFor(groupId)?.adapter?.displayedIds ?: return + for (id in shown) { + if (!checked.remove(id)) checked.add(id) + } + if (checked.isEmpty()) return finish() + host.onSelectionChanged(null) + refreshPinTitle() + } + /** Every checked profile is pinned, so the action unpins (refresh_pin_action). */ private fun allPinned(): Boolean { val adapter = host.listFor(groupId)?.adapter ?: return false @@ -155,9 +168,12 @@ class SelectionMode(private val host: ConfigurationFragment) { } R.id.action_selection_all -> selectAll() + R.id.action_selection_invert -> invertSelection() R.id.action_selection_delete -> delete(ids) R.id.action_selection_pin -> pin(ids) R.id.action_selection_clear_results -> clearResults(ids) + R.id.action_selection_resolve_domain -> resolve(ids) + R.id.action_selection_clear_traffic -> clearTraffic(ids) R.id.action_selection_move -> moveToGroup(ids) R.id.action_selection_copy_links -> copyLinks(ids, throne = false) R.id.action_selection_copy_throne -> copyLinks(ids, throne = true) @@ -199,6 +215,35 @@ class SelectionMode(private val host: ConfigurationFragment) { } } + private fun resolve(ids: List) { + if (ids.isEmpty()) return + val gid = groupId + finish() + host.launchIo { + val report = SubscriptionClient.resolveProfiles(gid, ids) + if (report.isNotBlank()) host.onUi { snackbar(report).show() } + } + } + + private fun clearTraffic(ids: List) { + val gid = groupId + confirm(ids, host.getString(R.string.confirm_clear_traffic), R.string.confirm_clear) { + val service = (host.activity as? MainActivity)?.connection?.service + val adapter = host.listFor(gid)?.adapter + finish() + ids.forEach { adapter?.clearTraffic(it) } + host.launchIo { + val array = ids.toLongArray() + try { + service?.resetTraffic(array) + } catch (e: Exception) { + Logs.w(e) + } + ProfileManager.resetTraffic(array) + } + } + } + /** [title] above the names of [ids] in list order. */ private fun confirm(ids: List, title: String, @StringRes action: Int, run: () -> Unit) { host.launchIo { diff --git a/app/src/main/res/menu/profile_selection_menu.xml b/app/src/main/res/menu/profile_selection_menu.xml index 74e99f3f..7980aec2 100644 --- a/app/src/main/res/menu/profile_selection_menu.xml +++ b/app/src/main/res/menu/profile_selection_menu.xml @@ -33,6 +33,10 @@ android:id="@+id/action_selection_all" android:title="@string/profiles_select_all" app:showAsAction="never" /> + + + Разрешить домен Работающий профиль сохранён. Выбрать все + Инвертировать выбор Показать Внешний IP Скорость diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8b6872e7..6a43e30e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1302,6 +1302,7 @@ %d selected Select all + Invert selection Move to group There is no other basic group to move to.