Handle scan recovery phrase QR

This commit is contained in:
Andrew 2024-03-01 18:46:24 +10:30
parent 2555b34d8e
commit 9e270c7ac0
2 changed files with 58 additions and 13 deletions

View File

@ -7,6 +7,8 @@ import android.os.Bundle
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import androidx.activity.viewModels
import androidx.camera.core.CameraSelector
import androidx.camera.core.ExperimentalGetImage
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.Preview
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
@ -59,19 +61,28 @@ import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.isGranted
import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale
import com.google.mlkit.vision.barcode.BarcodeScanner
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import network.loki.messenger.R
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.BaseActionBarActivity
import org.thoughtcrime.securesms.ui.AppTheme
import org.thoughtcrime.securesms.ui.OutlineButton
import org.thoughtcrime.securesms.ui.baseBold
import org.thoughtcrime.securesms.ui.colorDestructive
import java.util.concurrent.Executors
import javax.inject.Inject
private const val TAG = "LinkDeviceActivity"
@AndroidEntryPoint
@androidx.annotation.OptIn(ExperimentalGetImage::class)
class LinkDeviceActivity : BaseActionBarActivity() {
@Inject
@ -134,16 +145,20 @@ class LinkDeviceActivity : BaseActionBarActivity() {
val localContext = LocalContext.current
val cameraProvider = remember { ProcessCameraProvider.getInstance(localContext) }
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()
val scanner = BarcodeScanning.getClient(options)
runCatching {
when (title) {
R.string.activity_link_device_scan_qr_code -> {
cameraProvider.get().bindToLifecycle(LocalLifecycleOwner.current, selector, preview)
LocalSoftwareKeyboardController.current?.hide()
cameraProvider.get().bindToLifecycle(LocalLifecycleOwner.current, selector, preview, buildAnalysisUseCase(scanner, viewModel::onQrPhrase))
}
else -> cameraProvider.get().unbind(preview)
}
}
}.onFailure { Log.e(TAG, "error binding camera", it) }
when (title) {
R.string.activity_recovery_password -> RecoveryPassword(state, onChange, onContinue)
R.string.activity_link_device_scan_qr_code -> MaybeScanQrCode()
@ -196,11 +211,7 @@ fun ScanQrCode(preview: Preview) {
Box {
AndroidView(
modifier = Modifier.fillMaxSize(),
factory = { context ->
PreviewView(context).apply {
preview.setSurfaceProvider(surfaceProvider)
}
}
factory = { PreviewView(it).apply { preview.setSurfaceProvider(surfaceProvider) } }
)
Box(
@ -270,3 +281,26 @@ fun RecoveryPassword(state: LinkDeviceState, onChange: (String) -> Unit = {}, on
fun Context.startLinkDeviceActivity() {
Intent(this, LinkDeviceActivity::class.java).let(::startActivity)
}
private fun buildAnalysisUseCase(
scanner: BarcodeScanner,
onBarcodeScanned: (String) -> Unit
): ImageAnalysis = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build().apply {
setAnalyzer(Executors.newSingleThreadExecutor()) { imageProxy ->
InputImage.fromMediaImage(
imageProxy.image!!,
imageProxy.imageInfo.rotationDegrees
).let(scanner::process).apply {
addOnSuccessListener { barcodes ->
barcodes.forEach {
it.takeIf { it.valueType == Barcode.TYPE_TEXT }?.rawValue?.let(onBarcodeScanned)
}
}
addOnCompleteListener {
imageProxy.close()
}
}
}
}

View File

@ -30,7 +30,14 @@ class LinkDeviceViewModel @Inject constructor(
fun onRecoveryPhrase() {
val mnemonic = state.value.recoveryPhrase
tryPhrase(mnemonic)
}
fun onQrPhrase(string: String) {
tryPhrase(string)
}
private fun tryPhrase(mnemonic: String) {
viewModelScope.launch(Dispatchers.IO) {
try {
MnemonicCodec { MnemonicUtilities.loadFileContents(getApplication(), it) }
@ -39,10 +46,14 @@ class LinkDeviceViewModel @Inject constructor(
.let(::LinkDeviceEvent)
.let { event.send(it) }
} catch (exception: Exception) {
when (exception) {
state.update {
it.copy(
error = when (exception) {
is MnemonicCodec.DecodingError -> exception.description
else -> "An error occurred."
}.let { error -> state.update { it.copy(error = error) } }
}
)
}
}
}
}