diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreServiceManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreServiceManager.kt index 52d2bdde..596789b2 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreServiceManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/core/CoreServiceManager.kt @@ -333,25 +333,14 @@ object CoreServiceManager { } } + val endpoint = if (time >= 0) SpeedtestManager.getRemoteIPInfo() else null val result = ConnectionTestResult( delayMillis = time, errorMessage = errorStr, + country = endpoint?.country, + ipAddress = endpoint?.ipAddress, ) MessageHelper.sendMsg2UI(service, AppConfig.MSG_MEASURE_DELAY_RESULT, result) - - // Only fetch IP info if the delay test was successful - if (time >= 0) { - SpeedtestManager.getRemoteIPInfo()?.let { ip -> - MessageHelper.sendMsg2UI( - service, - AppConfig.MSG_MEASURE_DELAY_RESULT, - result.copy( - country = ip.country, - ipAddress = ip.ipAddress, - ), - ) - } - } } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainGroupTab.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainGroupTab.kt index 8bff0e17..21183b91 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainGroupTab.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/main/MainGroupTab.kt @@ -1,19 +1,27 @@ package com.v2ray.ang.ui.main +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.widthIn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.foundation.lazy.itemsIndexed +import androidx.compose.foundation.lazy.rememberLazyListState import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.PrimaryScrollableTabRow import androidx.compose.material3.Tab -import androidx.compose.material3.TabRowDefaults import androidx.compose.material3.Text import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.getValue import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.text.style.TextOverflow -import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.v2ray.ang.dto.GroupMapItem @@ -28,32 +36,34 @@ fun GroupTabBar( onTabClick: (Int) -> Unit, modifier: Modifier = Modifier ) { - PrimaryScrollableTabRow( - selectedTabIndex = selectedTabIndex.coerceIn(0, groups.lastIndex), - modifier = modifier.fillMaxWidth(), - containerColor = MaterialTheme.colorScheme.surface.copy(alpha = 0.95f), - contentColor = MaterialTheme.colorScheme.onSurface, - edgePadding = 16.dp, - minTabWidth = 56.dp, - indicator = { - TabRowDefaults.PrimaryIndicator( - modifier = Modifier - .tabIndicatorOffset( - selectedTabIndex = selectedTabIndex.coerceIn(0, groups.lastIndex), - matchContentSize = true - ) - .clip(RoundedCornerShape(3.dp)), - width = Dp.Unspecified, - color = MaterialTheme.colorScheme.secondary - ) - }, - divider = {} + val selectedIndex = selectedTabIndex.coerceIn(0, groups.lastIndex) + val listState = rememberLazyListState(initialFirstVisibleItemIndex = selectedIndex) + + LaunchedEffect(selectedIndex) { + if (listState.layoutInfo.visibleItemsInfo.none { it.index == selectedIndex }) { + listState.animateScrollToItem(selectedIndex) + } + } + + LazyRow( + state = listState, + modifier = modifier + .fillMaxWidth() + .background(MaterialTheme.colorScheme.surface.copy(alpha = 0.95f)), + contentPadding = PaddingValues(horizontal = 16.dp), + verticalAlignment = Alignment.CenterVertically, ) { - groups.forEachIndexed { index, group -> + itemsIndexed( + items = groups, + key = { _, group -> group.id }, + ) { index, group -> + val serverFlow = remember(group.id, mainViewModel) { + mainViewModel.serversForGroup(group.id) + } GroupTabItem( group = group, - selected = index == selectedTabIndex, - serverFlowProvider = { mainViewModel.serversForGroup(group.id) }, + selected = index == selectedIndex, + serverFlow = serverFlow, onClick = { onTabClick(index) } ) } @@ -64,26 +74,39 @@ fun GroupTabBar( private fun GroupTabItem( group: GroupMapItem, selected: Boolean, - serverFlowProvider: () -> StateFlow>, + serverFlow: StateFlow>, onClick: () -> Unit ) { - val serverFlow = remember(group.id) { serverFlowProvider() } val servers by serverFlow.collectAsStateWithLifecycle() - Tab( - selected = selected, - onClick = onClick, - text = { - val text = if (group.id.isEmpty()) { - group.remarks - } else { - "${group.remarks} (${servers.size})" + val text = if (group.id.isEmpty()) { + group.remarks + } else { + "${group.remarks} (${servers.size})" + } + + Box(Modifier.widthIn(min = 56.dp)) { + Tab( + selected = selected, + onClick = onClick, + modifier = Modifier.heightIn(min = 48.dp), + text = { + Text( + text = text, + maxLines = 1, + softWrap = false, + overflow = TextOverflow.Ellipsis + ) } - Text( - text = text, - maxLines = 1, - softWrap = false, - overflow = TextOverflow.Ellipsis + ) + if (selected) { + Box( + Modifier + .align(Alignment.BottomCenter) + .fillMaxWidth() + .height(3.dp) + .clip(RoundedCornerShape(3.dp)) + .background(MaterialTheme.colorScheme.secondary) ) } - ) + } }