Compare commits

...

26 Commits

Author SHA1 Message Date
2dust bdbd0b154e up 1.8.5 2023-05-07 15:35:42 +08:00
2dust 653137ec58 bug fixes 2023-05-07 15:34:11 +08:00
2dust 39f65850be Merge pull request #2258 from Amir-yazdanmanesh/master
Fix persian translation
2023-05-07 15:19:33 +08:00
2dust 2d8db97417 Merge branch 'master' into master 2023-05-07 15:19:25 +08:00
2dust 17a6f80798 Merge pull request #2255 from hadi-norouzi/feature/share_sub
Feature/share sub
2023-05-07 15:18:28 +08:00
2dust 381fe859ff Merge pull request #2254 from hadi-norouzi/master
Fix status bar and navigation bar, remove FooterView TouchHelper
2023-05-07 15:14:04 +08:00
am.yazdanmanesh e2bdf17b82 Fix persian translation 2023-05-05 21:54:18 +03:30
Hadi Norouzi d20afc6801 Share subscription implemented 2023-05-04 18:03:50 +03:30
Hadi Norouzi 54046a27e6 Fix fa strings and test layout direction 2023-05-04 17:04:35 +03:30
Hadi Norouzi 36c000d18a Fix status bar and navigation bar, remove FooterView TouchHelper 2023-05-04 16:34:41 +03:30
2dust 2f3c2cf4d5 up 1.8.4 2023-04-29 20:46:28 +08:00
2dust 892358d5d8 Merge pull request #2227 from AlirezaIvaz/master
Add themed icon support
2023-04-27 09:40:08 +08:00
Alireza Ivaz 6dced903cd Add themed icon support 2023-04-26 17:05:11 +03:30
2dust f8a98a426e Merge pull request #2213 from solokot/master
Updated Russian translation
2023-04-22 14:40:57 +08:00
solokot 011506e99f Updated Russian translation 2023-04-21 21:33:22 +03:00
2dust 966151d3fe bug fixes 2023-04-19 19:45:11 +08:00
2dust 247f4db77e up 1.8.3 2023-04-18 09:51:49 +08:00
2dust 9a661bc401 Use Google code scanner 2023-04-17 10:28:34 +08:00
2dust f5f1b3816c Refactor createQRCode 2023-04-16 19:58:51 +08:00
2dust 3971c9badc use Exception 2023-04-16 19:56:44 +08:00
2dust 390fbf046b Merge pull request #2130 from pouriaksrvi/master
[New Feature] UX enhancement - Reversed the order of server list
2023-04-06 08:28:54 +08:00
Programmer ec7ba59528 Bug Fix - Fixed the order of subscription import
Subscription server list should not be reversed when it is being imported. Fixed this problem.
2023-04-03 04:12:01 +03:30
Programmer 838941c6a4 Merge branch '2dust:master' into master 2023-04-03 04:00:52 +03:30
2dust a0ec764b64 Merge pull request #2142 from solokot/master
Updated Russian translation
2023-04-02 09:32:29 +08:00
solokot 4cfe3394b1 Updated Russian translation 2023-03-29 09:35:28 +03:00
Programmer ae9aa75ba0 New Feature
Fixed incorrect default order of server list as described in the issue https://github.com/2dust/v2rayNG/issues/2116
The order was problematic for users who add new servers more frequently
2023-03-26 06:27:52 +00:00
30 changed files with 264 additions and 98 deletions
+4 -4
View File
@@ -17,8 +17,8 @@ android {
minSdkVersion 21
targetSdkVersion Integer.parseInt("$targetSdkVer")
multiDexEnabled true
versionCode 510
versionName "1.8.2"
versionCode 516
versionName "1.8.5"
}
buildTypes {
@@ -109,11 +109,11 @@ dependencies {
implementation 'io.reactivex:rxjava:1.3.8'
implementation 'io.reactivex:rxandroid:1.2.1'
implementation 'com.tbruyelle.rxpermissions:rxpermissions:0.9.4@aar'
implementation 'me.dm7.barcodescanner:core:1.9.8'
implementation 'me.dm7.barcodescanner:zxing:1.9.8'
implementation 'com.github.jorgecastilloprz:fabprogresscircle:1.01@aar'
implementation 'me.drakeet.support:toastcompat:1.1.0'
implementation 'com.blacksquircle.ui:editorkit:2.1.1'
implementation 'com.blacksquircle.ui:language-base:2.1.1'
implementation 'com.blacksquircle.ui:language-json:2.1.1'
implementation 'io.github.g00fy2.quickie:quickie-bundled:1.6.0'
implementation 'com.google.zxing:core:3.5.1'
}
@@ -39,6 +39,7 @@ object AppConfig {
const val PREF_PER_APP_PROXY_SET = "pref_per_app_proxy_set"
const val PREF_BYPASS_APPS = "pref_bypass_apps"
const val PREF_CONFIRM_REMOVE = "pref_confirm_remove"
const val PREF_START_SCAN_IMMEDIATE = "pref_start_scan_immediate"
const val HTTP_PROTOCOL: String = "http://"
const val HTTPS_PROTOCOL: String = "https://"
@@ -4,7 +4,6 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.text.TextUtils
import com.google.zxing.WriterException
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.service.V2RayServiceManager
@@ -34,7 +33,7 @@ class TaskerReceiver : BroadcastReceiver() {
} else {
Utils.stopVService(context)
}
} catch (e: WriterException) {
} catch (e: Exception) {
e.printStackTrace()
}
}
@@ -558,8 +558,13 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList
* read content from uri
*/
private fun readContentFromUri(uri: Uri) {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
RxPermissions(this)
.request(Manifest.permission.READ_EXTERNAL_STORAGE)
.request(permission)
.subscribe {
if (it) {
try {
@@ -228,7 +228,7 @@ class MainRecyclerAdapter(val activity: MainActivity) : RecyclerView.Adapter<Mai
BaseViewHolder(itemMainBinding.root), ItemTouchHelperViewHolder
class FooterViewHolder(val itemFooterBinding: ItemRecyclerFooterBinding) :
BaseViewHolder(itemFooterBinding.root), ItemTouchHelperViewHolder
BaseViewHolder(itemFooterBinding.root)
override fun onItemDismiss(position: Int) {
val guid = mActivity.mainViewModel.serversCache.getOrNull(position)?.guid ?: return
@@ -3,58 +3,62 @@ package com.v2ray.ang.ui
import android.Manifest
import android.app.Activity
import android.os.Bundle
import com.google.zxing.Result
import me.dm7.barcodescanner.zxing.ZXingScannerView
import android.content.Intent
import android.graphics.BitmapFactory
import android.os.Build
import android.view.Menu
import android.view.MenuItem
import androidx.activity.result.contract.ActivityResultContracts
import com.google.zxing.BarcodeFormat
import com.tbruyelle.rxpermissions.RxPermissions
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.R
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.QRCodeDecoder
import io.github.g00fy2.quickie.QRResult
import io.github.g00fy2.quickie.ScanCustomCode
import io.github.g00fy2.quickie.config.ScannerConfig
class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
class ScannerActivity : BaseActivity(){
private var mScannerView: ZXingScannerView? = null
private val scanQrCode = registerForActivityResult(ScanCustomCode(), ::handleResult)
private val settingsStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SETTING, MMKV.MULTI_PROCESS_MODE) }
public override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mScannerView = ZXingScannerView(this) // Programmatically initialize the scanner view
mScannerView?.setAutoFocus(true)
val formats = ArrayList<BarcodeFormat>()
formats.add(BarcodeFormat.QR_CODE)
mScannerView?.setFormats(formats)
setContentView(mScannerView) // Set the scanner view as the content view
if (settingsStorage?.decodeBool(AppConfig.PREF_START_SCAN_IMMEDIATE) == true) {
launchScan()
}
supportActionBar?.setDisplayHomeAsUpEnabled(true)
}
public override fun onResume() {
super.onResume()
mScannerView!!.setResultHandler(this) // Register ourselves as a handler for scan results.
mScannerView!!.startCamera() // Start camera on resume
}
public override fun onPause() {
super.onPause()
mScannerView!!.stopCamera() // Stop camera on pause
}
override fun handleResult(rawResult: Result) {
// Do something with the result here
// Log.v(FragmentActivity.TAG, rawResult.text) // Prints scan results
// Log.v(FragmentActivity.TAG, rawResult.barcodeFormat.toString()) // Prints the scan format (qrcode, pdf417 etc.)
private fun launchScan(){
scanQrCode.launch(
ScannerConfig.build {
setHapticSuccessFeedback(true) // enable (default) or disable haptic feedback when a barcode was detected
setShowTorchToggle(true) // show or hide (default) torch/flashlight toggle button
setShowCloseButton(true) // show or hide (default) close button
}
)
}
finished(rawResult.text)
// If you would like to resume scanning, call this method below:
// mScannerView!!.resumeCameraPreview(this)
private fun handleResult(result: QRResult) {
if (result is QRResult.QRSuccess ) {
finished(result.content.rawValue)
} else {
finish()
}
}
private fun finished(text: String) {
@@ -70,6 +74,10 @@ class ScannerActivity : BaseActivity(), ZXingScannerView.ResultHandler {
}
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
R.id.scan_code -> {
launchScan()
true
}
R.id.select_photo -> {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
@@ -2,20 +2,34 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.graphics.Color
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import android.view.ViewGroup
import androidx.appcompat.app.AlertDialog
import com.google.gson.Gson
import com.tencent.mmkv.MMKV
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ItemQrcodeBinding
import com.v2ray.ang.databinding.ItemRecyclerSubSettingBinding
import com.v2ray.ang.dto.EConfigType
import com.v2ray.ang.extension.toast
import com.v2ray.ang.util.AngConfigManager
import com.v2ray.ang.util.MmkvManager
import com.v2ray.ang.util.QRCodeDecoder
import com.v2ray.ang.util.Utils
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>() {
class SubSettingRecyclerAdapter(val activity: SubSettingActivity) :
RecyclerView.Adapter<SubSettingRecyclerAdapter.MainViewHolder>() {
private var mActivity: SubSettingActivity = activity
private val subStorage by lazy { MMKV.mmkvWithID(MmkvManager.ID_SUB, MMKV.MULTI_PROCESS_MODE) }
private val share_method: Array<out String> by lazy {
mActivity.resources.getStringArray(R.array.share_sub_method)
}
override fun getItemCount() = mActivity.subscriptions.size
override fun onBindViewHolder(holder: MainViewHolder, position: Int) {
@@ -31,7 +45,8 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
holder.itemView.setBackgroundColor(Color.TRANSPARENT)
holder.itemSubSettingBinding.layoutEdit.setOnClickListener {
mActivity.startActivity(Intent(mActivity, SubEditActivity::class.java)
mActivity.startActivity(
Intent(mActivity, SubEditActivity::class.java)
.putExtra("subId", subId)
)
}
@@ -40,11 +55,51 @@ class SubSettingRecyclerAdapter(val activity: SubSettingActivity) : RecyclerView
subStorage?.encode(subId, Gson().toJson(subItem))
notifyItemChanged(position)
}
if (TextUtils.isEmpty(subItem.url)) {
holder.itemSubSettingBinding.layoutShare.visibility = View.INVISIBLE
} else {
holder.itemSubSettingBinding.layoutShare.setOnClickListener {
AlertDialog.Builder(mActivity)
.setItems(share_method.asList().toTypedArray()) { _, i ->
try {
when (i) {
0 -> {
val ivBinding =
ItemQrcodeBinding.inflate(LayoutInflater.from(mActivity))
ivBinding.ivQcode.setImageBitmap(
QRCodeDecoder.createQRCode(
subItem.url
)
)
AlertDialog.Builder(mActivity).setView(ivBinding.root).show()
}
1 -> {
Utils.setClipboard(mActivity, subItem.url)
}
else -> mActivity.toast("else")
}
} catch (e: Exception) {
e.printStackTrace()
}
}.show()
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): MainViewHolder {
return MainViewHolder(ItemRecyclerSubSettingBinding.inflate(LayoutInflater.from(parent.context), parent, false))
return MainViewHolder(
ItemRecyclerSubSettingBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
)
}
class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) : RecyclerView.ViewHolder(itemSubSettingBinding.root)
class MainViewHolder(val itemSubSettingBinding: ItemRecyclerSubSettingBinding) :
RecyclerView.ViewHolder(itemSubSettingBinding.root)
}
@@ -11,7 +11,6 @@ import android.content.Intent
import android.text.TextUtils
import android.view.Menu
import android.view.MenuItem
import com.google.zxing.WriterException
import com.tencent.mmkv.MMKV
import com.v2ray.ang.AppConfig
import com.v2ray.ang.databinding.ActivityTaskerBinding
@@ -65,7 +64,7 @@ class TaskerActivity : BaseActivity() {
listview?.setItemChecked(pos, true)
}
}
} catch (e: WriterException) {
} catch (e: Exception) {
e.printStackTrace()
}
@@ -3,7 +3,6 @@ package com.v2ray.ang.ui
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import com.google.zxing.WriterException
import com.v2ray.ang.R
import com.v2ray.ang.databinding.ActivityLogcatBinding
import com.v2ray.ang.extension.toast
@@ -44,7 +43,7 @@ class UrlSchemeActivity : BaseActivity() {
}
startActivity(Intent(this, MainActivity::class.java))
finish()
} catch (e: WriterException) {
} catch (e: Exception) {
e.printStackTrace()
}
}
@@ -4,6 +4,7 @@ import android.Manifest
import android.annotation.SuppressLint
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.provider.OpenableColumns
import android.util.Log
@@ -75,7 +76,14 @@ class UserAssetActivity : BaseActivity() {
}
private fun showFileChooser() {
RxPermissions(this).request(Manifest.permission.READ_EXTERNAL_STORAGE).subscribe {
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
Manifest.permission.READ_MEDIA_IMAGES
} else {
Manifest.permission.READ_EXTERNAL_STORAGE
}
RxPermissions(this)
.request(permission)
.subscribe {
if (it) {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.type = "*/*"
@@ -91,7 +99,8 @@ class UserAssetActivity : BaseActivity() {
} catch (ex: android.content.ActivityNotFoundException) {
toast(R.string.toast_require_file_manager)
}
}
} else
toast(R.string.toast_permission_denied)
}
}
@@ -670,7 +670,7 @@ object AngConfigManager {
if (TextUtils.isEmpty(conf)) {
return null
}
return Utils.createQRCode(conf)
return QRCodeDecoder.createQRCode(conf)
} catch (e: Exception) {
e.printStackTrace()
@@ -756,6 +756,7 @@ object AngConfigManager {
var count = 0
servers.lines()
.reversed()
.forEach {
val resId = importConfig(it, subid, removedSelectedServer)
if (resId == 0) {
@@ -46,7 +46,7 @@ object MmkvManager {
serverStorage?.encode(key, Gson().toJson(config))
val serverList = decodeServerList()
if (!serverList.contains(key)) {
serverList.add(key)
serverList.add(0, key)
mainStorage?.encode(KEY_ANG_CONFIGS, Gson().toJson(serverList))
if (mainStorage?.decodeString(KEY_SELECTED_SERVER).isNullOrBlank()) {
mainStorage?.encode(KEY_SELECTED_SERVER, key)
@@ -5,6 +5,7 @@ import android.graphics.BitmapFactory
import com.google.zxing.*
import com.google.zxing.common.GlobalHistogramBinarizer
import com.google.zxing.common.HybridBinarizer
import com.google.zxing.qrcode.QRCodeWriter
import java.util.*
/**
@@ -13,6 +14,36 @@ import java.util.*
object QRCodeDecoder {
val HINTS: MutableMap<DecodeHintType, Any?> = EnumMap(DecodeHintType::class.java)
/**
* create qrcode using zxing
*/
fun createQRCode(text: String, size: Int = 800): Bitmap? {
try {
val hints = HashMap<EncodeHintType, String>()
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
val bitMatrix = QRCodeWriter().encode(text,
BarcodeFormat.QR_CODE, size, size, hints)
val pixels = IntArray(size * size)
for (y in 0 until size) {
for (x in 0 until size) {
if (bitMatrix.get(x, y)) {
pixels[y * size + x] = 0xff000000.toInt()
} else {
pixels[y * size + x] = 0xffffffff.toInt()
}
}
}
val bitmap = Bitmap.createBitmap(size, size,
Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, size, 0, 0, size, size)
return bitmap
} catch (e: Exception) {
e.printStackTrace()
return null
}
}
/**
* 同步解析本地图片二维码。该方法是耗时操作,请在子线程中调用。
*
@@ -4,13 +4,7 @@ import android.content.ClipboardManager
import android.content.Context
import android.text.Editable
import android.util.Base64
import com.google.zxing.WriterException
import android.graphics.Bitmap
import com.google.zxing.BarcodeFormat
import com.google.zxing.qrcode.QRCodeWriter
import com.google.zxing.EncodeHintType
import java.util.*
import kotlin.collections.HashMap
import android.content.ClipData
import android.content.Intent
import android.content.res.Configuration.UI_MODE_NIGHT_MASK
@@ -171,36 +165,6 @@ object Utils {
return ret
}
/**
* create qrcode using zxing
*/
fun createQRCode(text: String, size: Int = 800): Bitmap? {
try {
val hints = HashMap<EncodeHintType, String>()
hints[EncodeHintType.CHARACTER_SET] = "utf-8"
val bitMatrix = QRCodeWriter().encode(text,
BarcodeFormat.QR_CODE, size, size, hints)
val pixels = IntArray(size * size)
for (y in 0 until size) {
for (x in 0 until size) {
if (bitMatrix.get(x, y)) {
pixels[y * size + x] = 0xff000000.toInt()
} else {
pixels[y * size + x] = 0xffffffff.toInt()
}
}
}
val bitmap = Bitmap.createBitmap(size, size,
Bitmap.Config.ARGB_8888)
bitmap.setPixels(pixels, 0, size, 0, 0, size, size)
return bitmap
} catch (e: WriterException) {
e.printStackTrace()
return null
}
}
/**
* is ip address
*/
@@ -274,7 +238,7 @@ object Utils {
if (value != null && Patterns.WEB_URL.matcher(value).matches() || URLUtil.isValidUrl(value)) {
return true
}
} catch (e: WriterException) {
} catch (e: Exception) {
e.printStackTrace()
return false
}
@@ -74,8 +74,8 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
config.fullConfig = Gson().fromJson(server, V2rayConfig::class.java)
val key = MmkvManager.encodeServerConfig("", config)
serverRawStorage?.encode(key, server)
serverList.add(key)
serversCache.add(ServersCache(key,config))
serverList.add(0, key)
serversCache.add(0, ServersCache(key,config))
}
fun swapServer(fromPosition: Int, toPosition: Int) {
@@ -50,7 +50,8 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application
AppConfig.PREF_PREFER_IPV6,
AppConfig.PREF_PER_APP_PROXY,
AppConfig.PREF_BYPASS_APPS,
AppConfig.PREF_CONFIRM_REMOVE, -> {
AppConfig.PREF_CONFIRM_REMOVE,
AppConfig.PREF_START_SCAN_IMMEDIATE, -> {
settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false))
}
AppConfig.PREF_SNIFFING_ENABLED -> {
@@ -50,7 +50,7 @@
android:layout_width="match_parent"
android:layout_height="@dimen/connection_test_height"
android:background="?attr/colorPrimary"
android:gravity="center|left"
android:gravity="center|start"
android:nextFocusRight="@+id/fab"
android:clickable="true"
android:focusable="true"
@@ -16,7 +16,8 @@
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
android:layout_height="match_parent"
tools:listitem="@layout/item_recycler_sub_setting"/>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
</RelativeLayout>
@@ -73,12 +73,13 @@
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/layout_margin_spacing"
android:visibility="invisible">
>
<ImageView
android:layout_width="@dimen/png_height"
android:layout_height="@dimen/png_height"
android:src="@drawable/ic_share_black_24dp" />
android:src="@drawable/ic_share_black_24dp"
app:tint="?attr/colorMainText"/>
</LinearLayout>
@@ -1,6 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/scan_code"
android:icon="@drawable/ic_scan_black_24dp"
android:title=""
app:showAsAction="always" />
<item
android:id="@+id/select_photo"
android:icon="@drawable/ic_image_photo"
@@ -2,4 +2,5 @@
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@color/ic_launcher_background"/>
<foreground android:drawable="@mipmap/ic_launcher_foreground"/>
<monochrome android:drawable="@mipmap/ic_launcher_foreground" />
</adaptive-icon>
+13 -4
View File
@@ -147,25 +147,29 @@
<string name="title_pref_confirm_remove">تایید حذف پرونده پیکربندی</string>
<string name="summary_pref_confirm_remove">آیا برای حذف پرونده پیکربندی نیاز به تایید دوم توسط کاربر است</string>
<string name="title_pref_start_scan_immediate">فورا اسکن را شروع کن</string>
<string name="summary_pref_start_scan_immediate">دوربین را برای اسکن بلافاصله در هنگام راه اندازی باز کنید، در غیر این صورت می توانید کد را اسکن کنید یا عکسی را در نوار ابزار انتخاب کنید.</string>
<string name="title_pref_feedback">بازخورد</string>
<string name="summary_pref_feedback">بهبودهای بازخورد یا اشکالات در گیت‌هاب</string>
<string name="summary_pref_tg_group">عضویت در گروه تلگرام</string>
<string name="toast_tg_app_not_found">برنامه تلگرام پیدا نشد</string>
<string name="title_pref_promotion">تبلیغات،</string>
<string name="title_pref_promotion">تبلیغات</string>
<string name="summary_pref_promotion">تبلیغات، برای جزئیات بیشتر کلیک کنید (کمک مالی کنید تا حذف شود)</string>
<string name="title_core_loglevel">سطح گزارشات</string>
<string name="title_mode">حالت</string>
<string name="title_mode_help">برای راهنمایی بیشتر روی این متن، کلیک کنید</string>
<string name="title_language">زبان</string>
<string name="title_ui_settings">تنظیمات رابط کاربری</string>
<string name="title_logcat">گزارشات</string>
<string name="logcat_copy">کپی</string>
<string name="logcat_clear">پاک کردن</string>
<string name="title_service_restart">راه‌اندازی مجدد خدمات</string>
<string name="title_del_all_config">حذف تمام پیکربندی</string>
<string name="title_del_duplicate_config">Delete duplicate config</string>
<string name="title_del_duplicate_config">حذف کانفیگ های تکراری</string>
<string name="title_del_invalid_config">تنظیمات نامعتبر را حذف کنید (ابتدا آزمایش کنید)</string>
<string name="title_export_all">خروجی گرفتن پیکربندی‌های غیرسفارشی در کلیپ‌بورد</string>
<string name="title_sub_setting">تنظیمات گروه‌ی اشتراک</string>
@@ -177,9 +181,9 @@
<string name="title_real_ping_all_server">تاخیر واقعی همه پیکربندی</string>
<string name="title_user_asset_setting">پرونده‌های دارایی جغرافیا</string>
<string name="title_sort_by_test_results">مرتب‌سازی بر اساس نتایج آزمایش</string>
<string name="title_filter_config">فیلتر پرونده پیکربندی</string>
<string name="title_filter_config">فیلتر پرونده پیکربندی ها</string>
<string name="filter_config_all">همه گروه‌های اشتراک</string>
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
<string name="title_del_duplicate_config_count">حذف %d کانفیگ تکراری</string>
<string name="tasker_start_service">شروع خدمات</string>
<string name="tasker_setting_confirm">تایید</string>
@@ -207,6 +211,11 @@
<item>خروجی گرفتن پیکربندی کامل در کلیپ‌بورد</item>
</string-array>
<string-array name="share_sub_method">
<item>QRcode</item>
<item>خروجی گرفتن در کلیپ‌بورد</item>
</string-array>
<string-array name="routing_tag">
<item>نشانی اینترنتی یا آی‌پی پروکسی</item>
<item>نشانی اینترنتی یا آی‌پی مستقیم</item>
@@ -0,0 +1,15 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="AppThemeDayNight" parent="Theme.AppCompat.DayNight.DarkActionBar">
<item name="colorPrimary">@color/colorPrimary</item>
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
<item name="colorAccent">@color/colorAccent</item>
<item name="colorMainBg">@color/colorBg</item>
<item name="colorMainText">@color/colorText</item>
<item name="android:statusBarColor">@color/colorPrimary</item>
<item name="android:navigationBarColor">@color/colorPrimary</item>
</style>
</resources>
+16 -2
View File
@@ -49,6 +49,8 @@
<string name="server_lab_request_host">Запрос узла (WS/H2) / Шифрование QUIC</string>
<string name="server_lab_path">Путь (WS/H2) / Ключ QUIC / Сид KCP / Сервис gRPC</string>
<string name="server_lab_stream_security">TLS</string>
<string name="server_lab_stream_fingerprint" translatable="false">Fingerprint</string>
<string name="server_lab_stream_alpn" translatable="false">Alpn</string>
<string name="server_lab_allow_insecure">Разрешать небезопасные</string>
<string name="server_lab_sni">SNI</string>
<string name="server_lab_address3">Адрес</string>
@@ -59,6 +61,9 @@
<string name="server_lab_security4">Пользователь (необязательно)</string>
<string name="server_lab_encryption">Шифрование</string>
<string name="server_lab_flow">Поток</string>
<string name="server_lab_public_key" translatable="false">PublicKey</string>
<string name="server_lab_short_id" translatable="false">ShortId</string>
<string name="server_lab_spider_x" translatable="false">SpiderX</string>
<string name="toast_success">Успешно</string>
<string name="toast_failure">Ошибка</string>
<string name="toast_none_data">Ничего нет</string>
@@ -147,6 +152,9 @@
<string name="title_pref_confirm_remove">Подтверждение удаления профиля</string>
<string name="summary_pref_confirm_remove">Требовать двойное подтверждение удаления профиля</string>
<string name="title_pref_start_scan_immediate">Сканирование при запуске</string>
<string name="summary_pref_start_scan_immediate">Начинать сканирование сразу при запуске приложения или запускать функцию сканирования камерой или из изображения через панель инструментов</string>
<string name="title_pref_feedback">Обратная связь</string>
<string name="summary_pref_feedback">Предложить улучшение или сообщить об ошибке на GitHub</string>
<string name="summary_pref_tg_group">Присоединиться к группе в Telegram</string>
@@ -159,13 +167,14 @@
<string name="title_mode">Режим</string>
<string name="title_mode_help">Нажмите для получения дополнительной информации</string>
<string name="title_language">Язык</string>
<string name="title_ui_settings">Настройки интерфейса</string>
<string name="title_logcat">Системный журнал</string>
<string name="logcat_copy">Копировать</string>
<string name="logcat_clear">Очистить</string>
<string name="title_service_restart">Перезапуск службы</string>
<string name="title_del_all_config">Удалить все профили</string>
<string name="title_del_duplicate_config">Delete duplicate config</string>
<string name="title_del_duplicate_config">Удалить дубликаты профилей</string>
<string name="title_del_invalid_config">Удалить сбойные профили (после проверки)</string>
<string name="title_export_all">Экспорт всех профилей в буфер обмена</string>
<string name="title_sub_setting">Группы</string>
@@ -179,7 +188,7 @@
<string name="title_sort_by_test_results">Сортировка по результатам теста</string>
<string name="title_filter_config">Фильтр профилей</string>
<string name="filter_config_all">Все группы</string>
<string name="title_del_duplicate_config_count">Delete %d duplicate configurations</string>
<string name="title_del_duplicate_config_count">Удалено дубликатов профилей: %d</string>
<string name="tasker_start_service">Запуск службы</string>
<string name="tasker_setting_confirm">Подтвердить</string>
@@ -207,6 +216,11 @@
<item>Экспорт всего профиля в буфер обмена</item>
</string-array>
<string-array name="share_sub_method">
<item>QR-код</item>
<item>Экспорт в буфер обмена</item>
</string-array>
<string-array name="routing_tag">
<item>Проксируемые</item>
<item>Прямые</item>
@@ -146,6 +146,9 @@
<string name="title_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình</string>
<string name="summary_pref_confirm_remove">Hiển thị thông báo xác nhận xoá cấu hình khi bạn xoá một cấu hình.</string>
<string name="title_pref_start_scan_immediate">Start scanning immediately</string>
<string name="summary_pref_start_scan_immediate">Open the camera to scan immediately at startup, otherwise you can choose to scan the code or select a photo in the toolbar</string>
<string name="title_pref_feedback">Phản hồi lỗi</string>
<string name="summary_pref_feedback">Phản hồi cải tiến hoặc lỗi lên GitHub</string>
<string name="summary_pref_tg_group">Tham gia nhóm Telegram</string>
@@ -158,6 +161,7 @@
<string name="title_mode">Chế độ kết nối</string>
<string name="title_mode_help">Nhấn vào đây nếu bạn cần trợ giúp!</string>
<string name="title_language">Ngôn ngữ</string>
<string name="title_ui_settings">UI settings</string>
<string name="title_logcat">Nhật ký hoạt động</string>
<string name="logcat_copy">Sao chép nhật ký</string>
@@ -206,6 +210,11 @@
<item>Sao chép thành cấu hình tùy chỉnh</item>
</string-array>
<string-array name="share_sub_method">
<item>Xuất ra mã QR (Chụp màn hình để lưu)</item>
<item>Sao chép cấu hình này</item>
</string-array>
<string-array name="routing_tag">
<item>Proxy URL hoặc IP</item>
<item>Direct URL hoặc IP</item>
@@ -146,6 +146,9 @@
<string name="title_pref_confirm_remove">删除配置文件确认</string>
<string name="summary_pref_confirm_remove">删除配置文件是否需要用户二次确认</string>
<string name="title_pref_start_scan_immediate">立即启动扫码</string>
<string name="summary_pref_start_scan_immediate">启动时立即打开相机扫描,否则可在工具栏选择扫码或选照片</string>
<string name="title_pref_feedback">反馈</string>
<string name="summary_pref_feedback">反馈改进或漏洞至 GitHub</string>
<string name="summary_pref_tg_group">加入Telegram Group</string>
@@ -158,6 +161,7 @@
<string name="title_mode">模式</string>
<string name="title_mode_help">点此查看更多帮助</string>
<string name="title_language">语言</string>
<string name="title_ui_settings">用户界面设置</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">复制</string>
@@ -205,6 +209,12 @@
<item>导出至剪贴板</item>
<item>导出完整配置至剪贴板</item>
</string-array>
<string-array name="share_sub_method">
<item>二维码</item>
<item>导出至剪贴板</item>
</string-array>
share_method
<string-array name="routing_tag">
<item>代理的网址或IP</item>
@@ -146,6 +146,9 @@
<string name="title_pref_confirm_remove">刪除配置文件確認</string>
<string name="summary_pref_confirm_remove">刪除配置文件是否需要用戶二次確認</string>
<string name="title_pref_start_scan_immediate">立即啟動掃碼</string>
<string name="summary_pref_start_scan_immediate">啟動時立即打開相機掃描,否則可在工具欄選擇掃碼或選照片</string>
<string name="title_pref_feedback">意見回饋</string>
<string name="summary_pref_feedback">前往 GitHub 回報錯誤</string>
<string name="summary_pref_tg_group">加入 Telegram 群組</string>
@@ -158,6 +161,7 @@
<string name="title_mode">模式</string>
<string name="title_mode_help">輕觸以檢視說明</string>
<string name="title_language">語言</string>
<string name="title_ui_settings">用戶界面設置</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">複製</string>
@@ -206,6 +210,11 @@
<item>匯出完整組態至剪貼簿</item>
</string-array>
<string-array name="share_sub_method">
<item>QR Code</item>
<item>匯出至剪貼簿</item>
</string-array>
<string-array name="routing_tag">
<item>Proxy URL 或 IP</item>
<item>直接連線 URL 或 IP</item>
@@ -154,6 +154,9 @@
<string name="title_pref_confirm_remove">Delete configuration file confirmation</string>
<string name="summary_pref_confirm_remove">Whether to delete the configuration file requires a second confirmation by the user</string>
<string name="title_pref_start_scan_immediate">Start scanning immediately</string>
<string name="summary_pref_start_scan_immediate">Open the camera to scan immediately at startup, otherwise you can choose to scan the code or select a photo in the toolbar</string>
<string name="title_pref_feedback">Feedback</string>
<string name="summary_pref_feedback">Feedback enhancements or bugs to GitHub</string>
<string name="summary_pref_tg_group">Join Telegram Group</string>
@@ -166,6 +169,7 @@
<string name="title_mode">Mode</string>
<string name="title_mode_help">Click me for more help</string>
<string name="title_language">Language</string>
<string name="title_ui_settings">UI settings</string>
<string name="title_logcat">Logcat</string>
<string name="logcat_copy">Copy</string>
@@ -214,6 +218,11 @@
<item>Export full configuration to clipboard</item>
</string-array>
<string-array name="share_sub_method">
<item>QRcode</item>
<item>Export to clipboard</item>
</string-array>
<string-array name="routing_tag">
<item>proxy URL or IP</item>
<item>direct URL or IP</item>
@@ -6,6 +6,8 @@
<item name="colorAccent">@color/colorAccent</item>
<item name="colorMainBg">@color/colorBg</item>
<item name="colorMainText">@color/colorText</item>
<item name="android:statusBarColor">@color/colorPrimary</item>
<item name="android:navigationBarColor">@color/colorPrimary</item>
</style>
<style name="AppThemeDayNight.NoActionBar" parent="AppThemeDayNight">
+14 -5
View File
@@ -124,6 +124,20 @@
android:summary="%s"
android:title="@string/title_mode" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/title_ui_settings">
<CheckBoxPreference
android:key="pref_confirm_remove"
android:summary="@string/summary_pref_confirm_remove"
android:title="@string/title_pref_confirm_remove" />
<CheckBoxPreference
android:key="pref_start_scan_immediate"
android:summary="@string/summary_pref_start_scan_immediate"
android:title="@string/title_pref_start_scan_immediate" />
<ListPreference
android:defaultValue="auto"
android:entries="@array/language_select"
@@ -132,10 +146,5 @@
android:summary="%s"
android:title="@string/title_language" />
<CheckBoxPreference
android:key="pref_confirm_remove"
android:summary="@string/summary_pref_confirm_remove"
android:title="@string/title_pref_confirm_remove" />
</PreferenceCategory>
</PreferenceScreen>