mirror of
https://github.com/oxen-io/session-android.git
synced 2024-11-27 12:05:22 +00:00
Add QR errors
This commit is contained in:
parent
8fc83213ba
commit
ca0206409c
@ -12,7 +12,6 @@ import androidx.camera.core.ExperimentalGetImage
|
||||
import androidx.camera.core.ImageAnalysis
|
||||
import androidx.camera.core.ImageAnalysis.Analyzer
|
||||
import androidx.camera.core.ImageProxy
|
||||
import androidx.camera.core.Preview
|
||||
import androidx.camera.lifecycle.ProcessCameraProvider
|
||||
import androidx.camera.view.PreviewView
|
||||
import androidx.compose.foundation.ExperimentalFoundationApi
|
||||
@ -36,9 +35,14 @@ import androidx.compose.material.LocalContentAlpha
|
||||
import androidx.compose.material.LocalContentColor
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.OutlinedTextField
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Snackbar
|
||||
import androidx.compose.material.SnackbarHost
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TextFieldDefaults
|
||||
import androidx.compose.material.rememberScaffoldState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
@ -53,6 +57,7 @@ import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
@ -66,7 +71,7 @@ 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.flow.first
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.launch
|
||||
import network.loki.messenger.R
|
||||
import org.session.libsession.utilities.TextSecurePreferences
|
||||
@ -79,7 +84,6 @@ import org.thoughtcrime.securesms.ui.colorDestructive
|
||||
import org.thoughtcrime.securesms.ui.components.SessionTabRow
|
||||
import java.util.concurrent.Executors
|
||||
import javax.inject.Inject
|
||||
import kotlin.coroutines.CoroutineContext
|
||||
|
||||
private const val TAG = "LinkDeviceActivity"
|
||||
|
||||
@ -94,7 +98,7 @@ class LinkDeviceActivity : BaseActionBarActivity() {
|
||||
|
||||
val viewModel: LinkDeviceViewModel by viewModels()
|
||||
|
||||
val preview = Preview.Builder().build()
|
||||
val preview = androidx.camera.core.Preview.Builder().build()
|
||||
val selector = CameraSelector.Builder()
|
||||
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
|
||||
.build()
|
||||
@ -147,15 +151,15 @@ class LinkDeviceActivity : BaseActionBarActivity() {
|
||||
val scanner = BarcodeScanning.getClient(options)
|
||||
|
||||
runCatching {
|
||||
when (title) {
|
||||
R.string.activity_link_device_scan_qr_code -> {
|
||||
LocalSoftwareKeyboardController.current?.hide()
|
||||
cameraProvider.get().apply {
|
||||
unbindAll()
|
||||
bindToLifecycle(LocalLifecycleOwner.current, selector, preview, buildAnalysisUseCase(scanner, viewModel::scan))
|
||||
}
|
||||
}
|
||||
else -> cameraProvider.get().unbindAll()
|
||||
cameraProvider.get().unbindAll()
|
||||
if (title == R.string.activity_link_device_scan_qr_code) {
|
||||
LocalSoftwareKeyboardController.current?.hide()
|
||||
cameraProvider.get().bindToLifecycle(
|
||||
LocalLifecycleOwner.current,
|
||||
selector,
|
||||
preview,
|
||||
buildAnalysisUseCase(scanner, viewModel::scan)
|
||||
)
|
||||
}
|
||||
}.onFailure { Log.e(TAG, "error binding camera", it) }
|
||||
when (title) {
|
||||
@ -166,6 +170,7 @@ class LinkDeviceActivity : BaseActionBarActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
@Composable
|
||||
fun MaybeScanQrCode() {
|
||||
@ -173,7 +178,7 @@ class LinkDeviceActivity : BaseActionBarActivity() {
|
||||
val cameraPermissionState = rememberPermissionState(android.Manifest.permission.CAMERA)
|
||||
|
||||
if (cameraPermissionState.status.isGranted) {
|
||||
ScanQrCode(preview)
|
||||
ScanQrCode(preview, viewModel.qrErrorsFlow)
|
||||
} else if (cameraPermissionState.status.shouldShowRationale) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
@ -204,27 +209,59 @@ class LinkDeviceActivity : BaseActionBarActivity() {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ScanQrCode(preview: Preview) {
|
||||
Box {
|
||||
AndroidView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
factory = { PreviewView(it).apply { preview.setSurfaceProvider(surfaceProvider) } }
|
||||
)
|
||||
@Composable
|
||||
fun ScanQrCode(preview: androidx.camera.core.Preview, errors: Flow<String>) {
|
||||
val scaffoldState = rememberScaffoldState()
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.aspectRatio(1f)
|
||||
.padding(20.dp)
|
||||
.clip(shape = RoundedCornerShape(20.dp))
|
||||
.background(Color(0x33ffffff))
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
LaunchedEffect(Unit) {
|
||||
errors.collect { error ->
|
||||
lifecycleScope.launch {
|
||||
scaffoldState.snackbarHostState.showSnackbar(
|
||||
message = error,
|
||||
actionLabel = "Dismiss"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
scaffoldState = scaffoldState,
|
||||
snackbarHost = {
|
||||
SnackbarHost(
|
||||
hostState = scaffoldState.snackbarHostState,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
) { data ->
|
||||
Snackbar(
|
||||
snackbarData = data,
|
||||
modifier = Modifier.padding(16.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
) { padding ->
|
||||
Box(modifier = Modifier.padding(padding)) {
|
||||
AndroidView(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
factory = { PreviewView(it).apply { preview.setSurfaceProvider(surfaceProvider) } }
|
||||
)
|
||||
|
||||
Box(
|
||||
Modifier
|
||||
.aspectRatio(1f)
|
||||
.padding(20.dp)
|
||||
.clip(shape = RoundedCornerShape(20.dp))
|
||||
.background(Color(0x33ffffff))
|
||||
.align(Alignment.Center)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewRecoveryPassword() = RecoveryPassword(state = LinkDeviceState())
|
||||
|
||||
@Composable
|
||||
fun RecoveryPassword(state: LinkDeviceState, onChange: (String) -> Unit = {}, onContinue: () -> Unit = {}) {
|
||||
Column(
|
||||
@ -271,9 +308,9 @@ fun RecoveryPassword(state: LinkDeviceState, onChange: (String) -> Unit = {}, on
|
||||
OutlineButton(
|
||||
text = stringResource(id = R.string.continue_2),
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(horizontal = 64.dp, vertical = 20.dp)
|
||||
.width(200.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.padding(horizontal = 64.dp, vertical = 20.dp)
|
||||
.width(200.dp)
|
||||
) { onContinue() }
|
||||
}
|
||||
}
|
||||
|
@ -11,12 +11,15 @@ import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.consumeAsFlow
|
||||
import kotlinx.coroutines.flow.debounce
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.mapNotNull
|
||||
import kotlinx.coroutines.flow.receiveAsFlow
|
||||
import kotlinx.coroutines.flow.take
|
||||
import kotlinx.coroutines.flow.takeWhile
|
||||
import kotlinx.coroutines.flow.update
|
||||
import kotlinx.coroutines.launch
|
||||
import org.session.libsignal.crypto.MnemonicCodec
|
||||
import org.session.libsignal.crypto.MnemonicCodec.DecodingError.InputTooShort
|
||||
import org.session.libsignal.crypto.MnemonicCodec.DecodingError.InvalidWord
|
||||
import org.session.libsignal.utilities.Hex
|
||||
import org.thoughtcrime.securesms.crypto.MnemonicUtilities
|
||||
import javax.inject.Inject
|
||||
@ -35,7 +38,9 @@ class LinkDeviceViewModel @Inject constructor(
|
||||
private val event = Channel<LinkDeviceEvent>()
|
||||
val eventFlow = event.receiveAsFlow().take(1)
|
||||
private val qrErrors = Channel<Throwable>()
|
||||
private val qrErrorsFlow = qrErrors.receiveAsFlow().debounce(QR_ERROR_TIME).takeWhile { event.isEmpty }
|
||||
val qrErrorsFlow = qrErrors.receiveAsFlow().debounce(QR_ERROR_TIME).takeWhile { event.isEmpty }.mapNotNull {
|
||||
"This QR code does not contain a Recovery Password."
|
||||
}
|
||||
|
||||
private val codec by lazy { MnemonicCodec { MnemonicUtilities.loadFileContents(getApplication(), it) } }
|
||||
|
||||
@ -64,11 +69,19 @@ class LinkDeviceViewModel @Inject constructor(
|
||||
}
|
||||
|
||||
private fun onFailure(error: Throwable) {
|
||||
state.update { it.copy(error = error.message) }
|
||||
state.update {
|
||||
it.copy(
|
||||
error = when (error) {
|
||||
is InputTooShort -> "The Recovery Password you entered is not long enough. Please check and try again."
|
||||
is InvalidWord -> "Some of the words in your Recovery Password are incorrect. Please check and try again."
|
||||
else -> "Please check your Recovery Password and try again."
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun onScanFailure(error: Throwable) {
|
||||
state.update { it.copy(error = error.message) }
|
||||
viewModelScope.launch { qrErrors.send(error) }
|
||||
}
|
||||
|
||||
private fun runDecodeCatching(mnemonic: String) = runCatching {
|
||||
|
Loading…
Reference in New Issue
Block a user