diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/helper/QRCodeScannerHelper.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/QRCodeScannerHelper.kt new file mode 100644 index 00000000..5eea4f6e --- /dev/null +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/helper/QRCodeScannerHelper.kt @@ -0,0 +1,38 @@ +package com.v2ray.ang.helper + +import android.content.Intent +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts +import androidx.appcompat.app.AppCompatActivity +import com.v2ray.ang.ui.ScannerActivity + +/** + * Helper for scanning QR codes. + * + * This class encapsulates the logic for launching the QR code scanner activity + * and handling the scan result. + */ +class QRCodeScannerHelper(private val activity: AppCompatActivity) { + private var scanCallback: ((String?) -> Unit)? = null + + private val scanLauncher: ActivityResultLauncher = + activity.registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { result -> + if (result.resultCode == AppCompatActivity.RESULT_OK) { + val scanResult = result.data?.getStringExtra("SCAN_RESULT") + scanCallback?.invoke(scanResult) + } else { + scanCallback?.invoke(null) + } + scanCallback = null + } + + /** + * Launch the QR code scanner activity. + * + * @param onResult Callback invoked with the scan result (null if cancelled or failed) + */ + fun launch(onResult: (String?) -> Unit) { + scanCallback = onResult + scanLauncher.launch(Intent(activity, ScannerActivity::class.java)) + } +} diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BackupActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BackupActivity.kt index f6e40b80..dbacb324 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BackupActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BackupActivity.kt @@ -188,9 +188,7 @@ class BackupActivity : BaseActivity() { } private fun restoreViaLocal() { - checkAndRequestPermission(PermissionType.READ_STORAGE) { - showFileChooser() - } + showFileChooser() } private fun backupViaWebDav() { diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt index 3fd2cae5..2c66f699 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/BaseActivity.kt @@ -23,6 +23,7 @@ import com.v2ray.ang.util.MyContextWrapper import com.v2ray.ang.dto.PermissionType import com.v2ray.ang.helper.FileChooserHelper import com.v2ray.ang.helper.PermissionHelper +import com.v2ray.ang.helper.QRCodeScannerHelper import com.v2ray.ang.util.Utils @@ -42,11 +43,13 @@ abstract class BaseActivity : AppCompatActivity() { private var progressBar: LinearProgressIndicator? = null private lateinit var fileChooser : FileChooserHelper private lateinit var permissionRequester : PermissionHelper + private lateinit var qrCodeScanner : QRCodeScannerHelper override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) fileChooser = FileChooserHelper(this) permissionRequester = PermissionHelper(this) + qrCodeScanner = QRCodeScannerHelper(this) supportActionBar?.setDisplayHomeAsUpEnabled(true) if (!Utils.getDarkModeStatus(this)) { @@ -247,6 +250,20 @@ abstract class BaseActivity : AppCompatActivity() { mimeType: String = "*/*", onResult: (Uri?) -> Unit ) { - fileChooser.launch(mimeType, onResult) + checkAndRequestPermission(PermissionType.READ_STORAGE) { + fileChooser.launch(mimeType, onResult) + } + } + + /** + * Launch QR code scanner with camera permission check. + * Convenience method that delegates to qrCodeScanner helper. + * + * @param onResult Callback invoked with the scan result string (null if cancelled or failed) + */ + protected fun launchQRCodeScanner(onResult: (String?) -> Unit) { + checkAndRequestPermission(PermissionType.CAMERA) { + qrCodeScanner.launch(onResult) + } } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt index acc0b1ce..3b8f5565 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/MainActivity.kt @@ -64,12 +64,6 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList } - private val scanQRCodeForConfig = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode == RESULT_OK) { - importBatchConfig(it.data?.getStringExtra("SCAN_RESULT")) - } - } - override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(binding.root) @@ -370,8 +364,10 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList * import config from qrcode */ private fun importQRcode(): Boolean { - checkAndRequestPermission(PermissionType.CAMERA) { - scanQRCodeForConfig.launch(Intent(this, ScannerActivity::class.java)) + launchQRCodeScanner { scanResult -> + if (scanResult != null) { + importBatchConfig(scanResult) + } } return true } @@ -425,9 +421,7 @@ class MainActivity : BaseActivity(), NavigationView.OnNavigationItemSelectedList */ private fun importConfigLocal(): Boolean { try { - checkAndRequestPermission(PermissionType.READ_STORAGE) { - showFileChooser() - } + showFileChooser() } catch (e: Exception) { Log.e(AppConfig.TAG, "Failed to import config from local file", e) return false diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt index 7f0751e8..fc6a8a14 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/RoutingSettingActivity.kt @@ -6,7 +6,6 @@ import android.os.Bundle import android.util.Log import android.view.Menu import android.view.MenuItem -import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.appcompat.app.AlertDialog import androidx.lifecycle.lifecycleScope @@ -150,8 +149,10 @@ class RoutingSettingActivity : BaseActivity() { } private fun importQRcode(): Boolean { - checkAndRequestPermission(PermissionType.CAMERA) { - scanQRcodeForRulesets.launch(Intent(this, ScannerActivity::class.java)) + launchQRCodeScanner { scanResult -> + if (scanResult != null) { + importRulesetsFromQRcode(scanResult) + } } return true } @@ -166,11 +167,6 @@ class RoutingSettingActivity : BaseActivity() { } } - private val scanQRcodeForRulesets = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode == RESULT_OK) { - importRulesetsFromQRcode(it.data?.getStringExtra("SCAN_RESULT")) - } - } private fun importRulesetsFromQRcode(qrcode: String?): Boolean { AlertDialog.Builder(this).setMessage(R.string.routing_settings_import_rulesets_tip) diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt index 390161af..f9e9cbfa 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScScannerActivity.kt @@ -2,12 +2,10 @@ package com.v2ray.ang.ui import android.content.Intent import android.os.Bundle -import androidx.activity.result.contract.ActivityResultContracts import com.v2ray.ang.R import com.v2ray.ang.extension.toastError import com.v2ray.ang.extension.toastSuccess import com.v2ray.ang.handler.AngConfigManager -import com.v2ray.ang.dto.PermissionType class ScScannerActivity : BaseActivity() { @@ -17,26 +15,20 @@ class ScScannerActivity : BaseActivity() { importQRcode() } - private fun importQRcode(): Boolean { - checkAndRequestPermission(PermissionType.CAMERA) { - scanQRCode.launch(Intent(this, ScannerActivity::class.java)) - } - return true - } + private fun importQRcode() { + launchQRCodeScanner { scanResult -> + if (scanResult != null) { + val (count, countSub) = AngConfigManager.importBatchConfig(scanResult, "", false) - private val scanQRCode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode == RESULT_OK) { - val scanResult = it.data?.getStringExtra("SCAN_RESULT").orEmpty() - val (count, countSub) = AngConfigManager.importBatchConfig(scanResult, "", false) + if (count + countSub > 0) { + toastSuccess(R.string.toast_success) + } else { + toastError(R.string.toast_failure) + } - if (count + countSub > 0) { - toastSuccess(R.string.toast_success) - } else { - toastError(R.string.toast_failure) + startActivity(Intent(this, MainActivity::class.java)) } - - startActivity(Intent(this, MainActivity::class.java)) + finish() } - finish() } } \ No newline at end of file diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt index 698c73f3..c95f912e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ScannerActivity.kt @@ -72,9 +72,7 @@ class ScannerActivity : BaseActivity() { } R.id.select_photo -> { - checkAndRequestPermission(PermissionType.READ_STORAGE) { - showFileChooser() - } + showFileChooser() true } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt index 8dd19d19..7cf66b69 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/UserAssetActivity.kt @@ -9,7 +9,6 @@ import android.provider.OpenableColumns import android.util.Log import android.view.Menu import android.view.MenuItem -import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.viewModels import androidx.lifecycle.lifecycleScope import androidx.recyclerview.widget.LinearLayoutManager @@ -91,12 +90,6 @@ class UserAssetActivity : BaseActivity() { } private fun showFileChooser() { - checkAndRequestPermission(PermissionType.READ_STORAGE) { - showFileChooser2() - } - } - - private fun showFileChooser2() { launchFileChooser { uri -> if (uri == null) { return@launchFileChooser @@ -148,17 +141,14 @@ class UserAssetActivity : BaseActivity() { } private fun importAssetFromQRcode(): Boolean { - checkAndRequestPermission(PermissionType.CAMERA) { - scanQRCodeForAssetURL.launch(Intent(this, ScannerActivity::class.java)) + launchQRCodeScanner { scanResult -> + if (scanResult != null) { + importAsset(scanResult) + } } return true } - private val scanQRCodeForAssetURL = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) { - if (it.resultCode == RESULT_OK) { - importAsset(it.data?.getStringExtra("SCAN_RESULT")) - } - } private fun importAsset(url: String?): Boolean { try {