@ -7,6 +7,7 @@ import android.net.Uri
import android.provider.Settings
import android.provider.Settings
import androidx.camera.core.CameraSelector
import androidx.camera.core.CameraSelector
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageAnalysis
import androidx.camera.core.ImageProxy
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.lifecycle.ProcessCameraProvider
import androidx.camera.view.PreviewView
import androidx.camera.view.PreviewView
import androidx.compose.foundation.background
import androidx.compose.foundation.background
@ -47,12 +48,18 @@ import com.google.mlkit.vision.barcode.BarcodeScanner
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScannerOptions
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.BarcodeScanning
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.barcode.common.Barcode
import com.google.mlkit.vision.common.InputImage
import kotlinx.coroutines.FlowPreview
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.debounce
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.emptyFlow
import kotlinx.coroutines.flow.filter
import network.loki.messenger.R
import network.loki.messenger.R
import org.session.libsignal.utilities.Log
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.onboarding.Analyzer
import java.util.concurrent.Executors
import java.util.concurrent.Executors
import kotlin.time.Duration.Companion.seconds
typealias CameraPreview = androidx . camera . core . Preview
typealias CameraPreview = androidx . camera . core . Preview
@ -61,7 +68,7 @@ private const val TAG = "NewMessageFragment"
@OptIn ( ExperimentalPermissionsApi :: class )
@OptIn ( ExperimentalPermissionsApi :: class )
@Composable
@Composable
fun MaybeScanQrCode (
fun MaybeScanQrCode (
errors : Flow < String > = emptyFlow ( ) ,
errors : Flow < String > ,
onClickSettings : ( ) -> Unit = LocalContext . current . run { {
onClickSettings : ( ) -> Unit = LocalContext . current . run { {
Intent ( Settings . ACTION _APPLICATION _DETAILS _SETTINGS ) . apply {
Intent ( Settings . ACTION _APPLICATION _DETAILS _SETTINGS ) . apply {
data = Uri . fromParts ( " package " , packageName , null )
data = Uri . fromParts ( " package " , packageName , null )
@ -75,7 +82,10 @@ fun MaybeScanQrCode(
val cameraPermissionState = rememberPermissionState ( Manifest . permission . CAMERA )
val cameraPermissionState = rememberPermissionState ( Manifest . permission . CAMERA )
if ( cameraPermissionState . status . isGranted ) {
if ( cameraPermissionState . status . isGranted ) {
ScanQrCode ( errors , onScan )
ScanQrCode ( errors ) {
Log . d ( " QR " , " scan: $it " )
onScan ( it )
}
} else if ( cameraPermissionState . status . shouldShowRationale ) {
} else if ( cameraPermissionState . status . shouldShowRationale ) {
Column (
Column (
modifier = Modifier
modifier = Modifier
@ -105,6 +115,7 @@ fun MaybeScanQrCode(
}
}
}
}
@OptIn ( FlowPreview :: class )
@Composable
@Composable
fun ScanQrCode ( errors : Flow < String > , onScan : ( String ) -> Unit ) {
fun ScanQrCode ( errors : Flow < String > , onScan : ( String ) -> Unit ) {
val localContext = LocalContext . current
val localContext = LocalContext . current
@ -140,9 +151,11 @@ fun ScanQrCode(errors: Flow<String>, onScan: (String) -> Unit) {
val scaffoldState = rememberScaffoldState ( )
val scaffoldState = rememberScaffoldState ( )
LaunchedEffect ( Unit ) {
LaunchedEffect ( Unit ) {
errors . collect { error ->
errors . filter { scaffoldState . snackbarHostState . currentSnackbarData == null }
scaffoldState . snackbarHostState . showSnackbar ( message = error )
. buffer ( 0 , BufferOverflow . DROP _OLDEST )
}
. collect { error ->
scaffoldState . snackbarHostState . showSnackbar ( message = error )
}
}
}
Scaffold (
Scaffold (
@ -185,4 +198,26 @@ private fun buildAnalysisUseCase(
. setBackpressureStrategy ( ImageAnalysis . STRATEGY _KEEP _ONLY _LATEST )
. setBackpressureStrategy ( ImageAnalysis . STRATEGY _KEEP _ONLY _LATEST )
. build ( ) . apply {
. build ( ) . apply {
setAnalyzer ( Executors . newSingleThreadExecutor ( ) , Analyzer ( scanner , onBarcodeScanned ) )
setAnalyzer ( Executors . newSingleThreadExecutor ( ) , Analyzer ( scanner , onBarcodeScanned ) )
}
}
class Analyzer (
private val scanner : BarcodeScanner ,
private val onBarcodeScanned : ( String ) -> Unit
) : ImageAnalysis . Analyzer {
@SuppressLint ( " UnsafeOptInUsageError " )
override fun analyze ( image : ImageProxy ) {
InputImage . fromMediaImage (
image . image !! ,
image . imageInfo . rotationDegrees
) . let ( scanner :: process ) . apply {
addOnSuccessListener { barcodes ->
barcodes . forEach {
it . rawValue ?. let ( onBarcodeScanned )
}
}
addOnCompleteListener {
image . close ( )
}
}
}
}