mirror of
https://github.com/openlibrecommunity/olcng.git
synced 2026-07-03 14:05:17 +02:00
feat(subscriptions): add subscription grouping by category and multi-subscription support
This commit is contained in:
@@ -3,4 +3,5 @@ package com.v2ray.ang.dto
|
||||
data class GroupMapItem(
|
||||
var id: String,
|
||||
var remarks: String,
|
||||
var subIds: List<String> = listOf(id)
|
||||
)
|
||||
@@ -75,6 +75,24 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
fun reloadServerList() {
|
||||
serverList = if (subscriptionId.isEmpty()) {
|
||||
MmkvManager.decodeAllServerList()
|
||||
} else if (subscriptionId.startsWith("group_")) {
|
||||
val allSubs = MmkvManager.decodeSubscriptions()
|
||||
val groupSubs = when (subscriptionId) {
|
||||
"group_white" -> allSubs.filter {
|
||||
it.subscription.remarks.startsWith("БЕЛЫЕ", ignoreCase = true) ||
|
||||
it.subscription.remarks.startsWith("WHITE", ignoreCase = true)
|
||||
}
|
||||
"group_black" -> allSubs.filter {
|
||||
it.subscription.remarks.startsWith("ЧЕРНЫЕ", ignoreCase = true) ||
|
||||
it.subscription.remarks.startsWith("BLACK", ignoreCase = true)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
val combined = mutableListOf<String>()
|
||||
groupSubs.forEach { sub ->
|
||||
combined.addAll(MmkvManager.decodeServerList(sub.guid))
|
||||
}
|
||||
combined
|
||||
} else {
|
||||
MmkvManager.decodeServerList(subscriptionId)
|
||||
}
|
||||
@@ -102,7 +120,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
* @param toPosition The target position of the server.
|
||||
*/
|
||||
fun swapServer(fromPosition: Int, toPosition: Int) {
|
||||
if (subscriptionId.isEmpty()) {
|
||||
if (subscriptionId.isEmpty() || subscriptionId.startsWith("group_")) {
|
||||
return
|
||||
}
|
||||
|
||||
@@ -152,6 +170,30 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
fun updateConfigViaSubAll(): SubscriptionUpdateResult {
|
||||
if (subscriptionId.isEmpty()) {
|
||||
return AngConfigManager.updateConfigViaSubAll()
|
||||
} else if (subscriptionId.startsWith("group_")) {
|
||||
val allSubs = MmkvManager.decodeSubscriptions()
|
||||
val groupSubs = when (subscriptionId) {
|
||||
"group_white" -> allSubs.filter {
|
||||
it.subscription.remarks.startsWith("БЕЛЫЕ", ignoreCase = true) ||
|
||||
it.subscription.remarks.startsWith("WHITE", ignoreCase = true)
|
||||
}
|
||||
"group_black" -> allSubs.filter {
|
||||
it.subscription.remarks.startsWith("ЧЕРНЫЕ", ignoreCase = true) ||
|
||||
it.subscription.remarks.startsWith("BLACK", ignoreCase = true)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
var totalResult = SubscriptionUpdateResult()
|
||||
groupSubs.forEach { sub ->
|
||||
val result = AngConfigManager.updateConfigViaSub(SubscriptionCache(sub.guid, sub.subscription))
|
||||
totalResult = SubscriptionUpdateResult(
|
||||
configCount = totalResult.configCount + result.configCount,
|
||||
successCount = totalResult.successCount + result.successCount,
|
||||
failureCount = totalResult.failureCount + result.failureCount,
|
||||
skipCount = totalResult.skipCount + result.skipCount
|
||||
)
|
||||
}
|
||||
return totalResult
|
||||
} else {
|
||||
val subItem = MmkvManager.decodeSubscription(subscriptionId) ?: return SubscriptionUpdateResult()
|
||||
return AngConfigManager.updateConfigViaSub(SubscriptionCache(subscriptionId, subItem))
|
||||
@@ -218,12 +260,23 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
if (serversCache.isEmpty()) {
|
||||
return@launch
|
||||
}
|
||||
|
||||
val actualSubId = if (subscriptionId.startsWith("group_")) {
|
||||
""
|
||||
} else {
|
||||
subscriptionId
|
||||
}
|
||||
|
||||
MessageUtil.sendMsg2TestService(
|
||||
getApplication(),
|
||||
TestServiceMessage(
|
||||
key = AppConfig.MSG_MEASURE_CONFIG,
|
||||
subscriptionId = subscriptionId,
|
||||
serverGuids = if (keywordFilter.isNotEmpty()) serversCache.map { it.guid } else emptyList()
|
||||
subscriptionId = actualSubId,
|
||||
serverGuids = if (keywordFilter.isNotEmpty() || subscriptionId.startsWith("group_")) {
|
||||
serversCache.map { it.guid }
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -266,18 +319,61 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
groups.add(
|
||||
GroupMapItem(
|
||||
id = "",
|
||||
remarks = context.getString(R.string.filter_config_all)
|
||||
remarks = context.getString(R.string.filter_config_all),
|
||||
subIds = emptyList()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
val whiteList = mutableListOf<String>()
|
||||
val blackList = mutableListOf<String>()
|
||||
val otherGroups = mutableListOf<GroupMapItem>()
|
||||
|
||||
subscriptions.forEach { sub ->
|
||||
val remarks = sub.subscription.remarks
|
||||
when {
|
||||
remarks.startsWith("БЕЛЫЕ", ignoreCase = true) ||
|
||||
remarks.startsWith("WHITE", ignoreCase = true) -> {
|
||||
whiteList.add(sub.guid)
|
||||
}
|
||||
remarks.startsWith("ЧЕРНЫЕ", ignoreCase = true) ||
|
||||
remarks.startsWith("BLACK", ignoreCase = true) -> {
|
||||
blackList.add(sub.guid)
|
||||
}
|
||||
else -> {
|
||||
otherGroups.add(
|
||||
GroupMapItem(
|
||||
id = sub.guid,
|
||||
remarks = remarks,
|
||||
subIds = listOf(sub.guid)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (whiteList.isNotEmpty()) {
|
||||
groups.add(
|
||||
GroupMapItem(
|
||||
id = sub.guid,
|
||||
remarks = sub.subscription.remarks
|
||||
id = "group_white",
|
||||
remarks = "БЕЛЫЕ СПИСКИ",
|
||||
subIds = whiteList
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
if (blackList.isNotEmpty()) {
|
||||
groups.add(
|
||||
GroupMapItem(
|
||||
id = "group_black",
|
||||
remarks = "ЧЕРНЫЕ СПИСКИ",
|
||||
subIds = blackList
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
groups.addAll(otherGroups)
|
||||
|
||||
return groups
|
||||
}
|
||||
|
||||
@@ -362,6 +458,22 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
MmkvManager.decodeSubsList().forEach { guid ->
|
||||
sortByTestResultsForSub(guid)
|
||||
}
|
||||
} else if (subscriptionId.startsWith("group_")) {
|
||||
val allSubs = MmkvManager.decodeSubscriptions()
|
||||
val groupSubs = when (subscriptionId) {
|
||||
"group_white" -> allSubs.filter {
|
||||
it.subscription.remarks.startsWith("БЕЛЫЕ", ignoreCase = true) ||
|
||||
it.subscription.remarks.startsWith("WHITE", ignoreCase = true)
|
||||
}
|
||||
"group_black" -> allSubs.filter {
|
||||
it.subscription.remarks.startsWith("ЧЕРНЫЕ", ignoreCase = true) ||
|
||||
it.subscription.remarks.startsWith("BLACK", ignoreCase = true)
|
||||
}
|
||||
else -> emptyList()
|
||||
}
|
||||
groupSubs.forEach { sub ->
|
||||
sortByTestResultsForSub(sub.guid)
|
||||
}
|
||||
} else {
|
||||
sortByTestResultsForSub(subscriptionId)
|
||||
}
|
||||
@@ -413,14 +525,24 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||
}
|
||||
|
||||
fun findSubscriptionIdBySelect(): String? {
|
||||
// Get the selected server GUID
|
||||
val selectedGuid = MmkvManager.getSelectServer()
|
||||
if (selectedGuid.isNullOrEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
val config = MmkvManager.decodeServerConfig(selectedGuid)
|
||||
return config?.subscriptionId
|
||||
val subId = config?.subscriptionId ?: return null
|
||||
|
||||
val subscription = MmkvManager.decodeSubscription(subId)
|
||||
val remarks = subscription?.remarks ?: return subId
|
||||
|
||||
return when {
|
||||
remarks.startsWith("БЕЛЫЕ", ignoreCase = true) ||
|
||||
remarks.startsWith("WHITE", ignoreCase = true) -> "group_white"
|
||||
remarks.startsWith("ЧЕРНЫЕ", ignoreCase = true) ||
|
||||
remarks.startsWith("BLACK", ignoreCase = true) -> "group_black"
|
||||
else -> subId
|
||||
}
|
||||
}
|
||||
|
||||
fun onTestsFinished() {
|
||||
|
||||
@@ -48,8 +48,10 @@
|
||||
android:id="@+id/tab_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:tabIndicatorFullWidth="false"
|
||||
app:tabMode="scrollable"
|
||||
app:tabIndicatorFullWidth="true"
|
||||
app:tabMode="fixed"
|
||||
app:tabGravity="fill"
|
||||
app:tabMaxWidth="0dp"
|
||||
app:tabTextAppearance="@style/TabLayoutTextStyle" />
|
||||
|
||||
<androidx.viewpager2.widget.ViewPager2
|
||||
@@ -71,58 +73,72 @@
|
||||
android:background="@color/divider_color_light" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_test"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/view_height_dp64"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/connection_test_pending"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@+id/view_pager"
|
||||
android:nextFocusRight="@+id/fab"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:gravity="center_vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_test_state"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="start|center_vertical"
|
||||
android:maxLines="2"
|
||||
android:minLines="1"
|
||||
android:paddingStart="@dimen/padding_spacing_dp16"
|
||||
android:text="@string/connection_test_pending"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
android:gravity="center"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/btn_summary_lite"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:src="@drawable/ic_lite_bolt"
|
||||
app:tint="@color/colorWhite"
|
||||
app:backgroundTint="@color/color_fab_inactive"
|
||||
app:fabSize="mini"
|
||||
app:maxImageSize="24dp"
|
||||
app:elevation="0dp"
|
||||
app:pressedTranslationZ="0dp"
|
||||
app:hoveredFocusedTranslationZ="0dp" />
|
||||
app:fabSize="normal"
|
||||
app:maxImageSize="28dp"
|
||||
app:elevation="4dp"
|
||||
app:pressedTranslationZ="8dp"
|
||||
app:hoveredFocusedTranslationZ="6dp" />
|
||||
|
||||
<com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
android:id="@+id/fab"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginEnd="@dimen/padding_spacing_dp16"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/tasker_start_service"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@+id/layout_test"
|
||||
android:src="@drawable/ic_play_24dp"
|
||||
app:tint="@color/colorWhite"
|
||||
app:fabSize="mini"
|
||||
app:elevation="0dp"
|
||||
app:pressedTranslationZ="0dp"
|
||||
app:hoveredFocusedTranslationZ="0dp" />
|
||||
app:fabSize="normal"
|
||||
app:maxImageSize="28dp"
|
||||
app:elevation="4dp"
|
||||
app:pressedTranslationZ="8dp"
|
||||
app:hoveredFocusedTranslationZ="6dp" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/layout_test"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:clickable="true"
|
||||
android:contentDescription="@string/connection_test_pending"
|
||||
android:focusable="true"
|
||||
android:nextFocusLeft="@+id/view_pager"
|
||||
android:nextFocusRight="@+id/fab"
|
||||
android:orientation="vertical"
|
||||
android:gravity="center"
|
||||
android:paddingTop="4dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_test_state"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:maxLines="2"
|
||||
android:minLines="1"
|
||||
android:text="@string/connection_test_pending"
|
||||
android:textAppearance="@style/TextAppearance.AppCompat.Small" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
Reference in New Issue
Block a user