diff --git a/.gitignore b/.gitignore index 9582b273..b290bba8 100644 --- a/.gitignore +++ b/.gitignore @@ -21,6 +21,9 @@ V2rayNG/app/build V2rayNG/build V2rayNG/local.properties +# Ignore prebuilt native libraries (built from submodules by compile-hevtun.sh / AndroidLibXrayLite) +V2rayNG/app/libs + # Ignore APK and AAR files *.apk *.aar diff --git a/V2rayNG/app/src/main/AndroidManifest.xml b/V2rayNG/app/src/main/AndroidManifest.xml index 0238a586..e26a81e1 100644 --- a/V2rayNG/app/src/main/AndroidManifest.xml +++ b/V2rayNG/app/src/main/AndroidManifest.xml @@ -209,6 +209,9 @@ + +) + +object TranslatorsParser { + + private val gson = Gson() + + fun parse(json: String): List { + if (json.isBlank()) return emptyList() + + return try { + val type = object : TypeToken>() {}.type + gson.fromJson>(json, type).orEmpty() + .filter { it.language.isNotBlank() && it.contributors.isNotEmpty() } + .distinctBy { it.language } + } catch (e: Exception) { + LogUtil.e(AppConfig.TAG, "Failed to parse translators.json", e) + emptyList() + } + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt index 41db47ea..a31bc6ce 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/AboutActivity.kt @@ -1,5 +1,6 @@ package com.v2ray.ang.ui +import android.content.Intent import android.os.Bundle import android.webkit.WebView import androidx.compose.foundation.layout.Column @@ -46,12 +47,20 @@ class AboutActivity : BaseComponentActivity() { @Composable override fun ScreenContent() { - AboutScreen(onBackClick = { finish() }) + AboutScreen( + onBackClick = { finish() }, + onTranslatorsClick = { + startActivity(Intent(this, TranslatorsActivity::class.java)) + } + ) } } @Composable -fun AboutScreen(onBackClick: () -> Unit) { +fun AboutScreen( + onBackClick: () -> Unit, + onTranslatorsClick: () -> Unit +) { val context = LocalContext.current var showOssDialog by remember { mutableStateOf(false) } @@ -84,6 +93,11 @@ fun AboutScreen(onBackClick: () -> Unit) { title = stringResource(R.string.title_oss_license), onClick = { showOssDialog = true } ) + SettingsMenuItem( + icon = painterResource(R.drawable.ic_translate_24dp), + title = stringResource(R.string.title_translators), + onClick = onTranslatorsClick + ) SettingsMenuItem( icon = painterResource(R.drawable.ic_feedback_24dp), title = stringResource(R.string.title_pref_feedback), diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/TranslatorsActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/TranslatorsActivity.kt new file mode 100644 index 00000000..0f6bf3c9 --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/TranslatorsActivity.kt @@ -0,0 +1,155 @@ +package com.v2ray.ang.ui + +import android.os.Bundle +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.WindowInsets +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.HorizontalDivider +import androidx.compose.material3.Icon +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Scaffold +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.painterResource +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.semantics.Role +import androidx.compose.ui.unit.dp +import com.v2ray.ang.R +import com.v2ray.ang.dto.TranslatorsCredit +import com.v2ray.ang.dto.TranslatorsParser +import com.v2ray.ang.ui.base.BaseComponentActivity +import com.v2ray.ang.ui.compose.AppTopBar +import com.v2ray.ang.ui.compose.NavigationBarsSpacer +import com.v2ray.ang.util.Utils + +class TranslatorsActivity : BaseComponentActivity() { + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + } + + @Composable + override fun ScreenContent() { + TranslatorsScreen(onBackClick = { finish() }) + } +} + +@Composable +fun TranslatorsScreen(onBackClick: () -> Unit) { + val context = LocalContext.current + val credits = remember { + TranslatorsParser.parse(Utils.readTextFromAssets(context, "translators.json")) + } + + Scaffold( + contentWindowInsets = WindowInsets(0), + topBar = { + AppTopBar( + title = stringResource(R.string.title_translators), + onBackClick = onBackClick + ) + } + ) { innerPadding -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(innerPadding), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + items(credits, key = { it.language }) { credit -> + TranslationCreditCard(credit) + } + item { + NavigationBarsSpacer() + } + } + } +} + +@Composable +private fun TranslationCreditCard(credit: TranslatorsCredit) { + Card( + modifier = Modifier.fillMaxWidth(), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceContainerLow + ) + ) { + Column { + Text( + text = credit.language, + style = MaterialTheme.typography.titleMedium, + modifier = Modifier.padding(horizontal = 16.dp, vertical = 12.dp) + ) + credit.contributors.forEachIndexed { index, contributor -> + if (index > 0) { + HorizontalDivider( + modifier = Modifier.padding(horizontal = 16.dp), + color = MaterialTheme.colorScheme.outlineVariant, + thickness = 0.5.dp + ) + } + ContributorRow( + displayName = contributor.displayName ?: contributor.name, + linkUrl = contributor.url + ) + } + } + } +} + +@Composable +private fun ContributorRow(displayName: String, linkUrl: String?) { + val context = LocalContext.current + val isLink = linkUrl != null + + Row( + modifier = Modifier + .fillMaxWidth() + .then( + if (isLink) { + Modifier.clickable(role = Role.Button) { + Utils.openUri(context, linkUrl) + } + } else { + Modifier + } + ) + .padding(horizontal = 16.dp, vertical = 12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + if (isLink) { + Icon( + painter = painterResource(R.drawable.ic_github_24dp), + contentDescription = null, + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(20.dp) + ) + } + Text( + text = displayName, + style = MaterialTheme.typography.bodyLarge, + color = if (isLink) { + MaterialTheme.colorScheme.primary + } else { + MaterialTheme.colorScheme.onSurface + }, + modifier = Modifier.padding(start = if (isLink) 12.dp else 0.dp) + ) + } +} \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable/ic_github_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_github_24dp.xml new file mode 100644 index 00000000..70f18878 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_github_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/drawable/ic_translate_24dp.xml b/V2rayNG/app/src/main/res/drawable/ic_translate_24dp.xml new file mode 100644 index 00000000..bfa3f401 --- /dev/null +++ b/V2rayNG/app/src/main/res/drawable/ic_translate_24dp.xml @@ -0,0 +1,9 @@ + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/values-ar/strings.xml b/V2rayNG/app/src/main/res/values-ar/strings.xml index 3e32f704..378b71ae 100644 --- a/V2rayNG/app/src/main/res/values-ar/strings.xml +++ b/V2rayNG/app/src/main/res/values-ar/strings.xml @@ -283,6 +283,7 @@ حول التطبيق الكود المصدري تراخيص المصادر المفتوحة + المترجمون قناة Telegram ترويج diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index f604d716..78effa02 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -283,6 +283,7 @@ সম্পর্কিত সোর্স কোড ওপেন সোর্স লাইসেন্স + অনুবাদকগণ টেলিগ্রাম চ্যানেল প্রচার diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml index d48b0dbc..f85a9418 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -283,6 +283,7 @@ زبار کود بونچک موجوزا کود بونچک + ترجومه‌گرا تورگه تلگرام تبلیغات diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index bf4b721c..116846f3 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -283,6 +283,7 @@ درباره کد منبع مجوز های منبع باز + مترجمان کانال تلگرام تبلیغات diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 4fadf364..ee93da42 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -283,6 +283,7 @@ О приложении Исходный код Лицензии открытого исходного кода + Переводчики Telegram-канал Предложения diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index fc0af8ef..4e912e42 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -283,6 +283,7 @@ Giới thiệu Mã nguồn Giấy phép mã nguồn mở + Người dịch Kênh Telegram Ưu đãi 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 1c0c6918..0ceb0b88 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -283,6 +283,7 @@ 关于 源代码 开源许可证 + 翻译贡献者 Telegram 频道 推广 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 344253f7..cb7bae55 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -283,6 +283,7 @@ 關於 原始碼 開放原始碼授權 + 翻譯貢獻者 Telegram 頻道 推廣 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 6834e9dd..b96ce5f2 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -283,6 +283,7 @@ About Source code Open-source licenses + Translators Telegram channel Promotion