Anonymous
Android Kotlin - разрешение запуска запускается слишком быстро, когда еще нет вида поверхности
Сообщение
Anonymous » 06 июн 2025, 14:38
Я не могу запустить свой сканер с RegisterForctivityResult. Мне кажется, что запуск запускается слишком быстро, когда еще нет представления поверхности.
Код: Выделить всё
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
previewView = binding.scannerPreviewView
scannerProgressBar = binding.scannerProgressBar
scannerTextView = binding.scannerTextView
previewView.previewStreamState.observe(viewLifecycleOwner) { state ->
when (state) {
PreviewView.StreamState.IDLE -> {
scannerProgressBar.visibility = View.VISIBLE
scannerTextView.visibility = View.GONE
}
PreviewView.StreamState.STREAMING -> {
scannerProgressBar.visibility = View.GONE
scannerTextView.visibility = View.VISIBLE
}
}
}
startCamera()
}
< /code>
и навигация на сам сканер, и, к сожалению, он также просто отображает черный экран. < /p>
Только когда я перевел разрешения с MainActivty в фрагмент, что камера запускается, когда я ввожу его во второй раз. Сканер начинается только после второго вызова. < /P>
abstract class QrScannerGenericFragment : Fragment() {
// UI
private lateinit var previewView: PreviewView
private lateinit var scannerProgressBar: ProgressBar
private lateinit var scannerTextView: TextView
// CAMERA
private val requestCameraPermissionLauncher =
registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted ->
if (isGranted) {
// Permission was granted – proceed to open camera
startCamera()
} else {
// Permission denied – show a message or disable camera functionality
}
}
private lateinit var cameraProvider: ProcessCameraProvider
private lateinit var analysisUseCase: ImageAnalysis
private var rgbaBitmap: Bitmap? = null
private val cameraExecutor by lazy { Executors.newSingleThreadExecutor() }
private val scanner by lazy {
val options = BarcodeScannerOptions.Builder()
.setBarcodeFormats(Barcode.FORMAT_QR_CODE)
.build()
BarcodeScanning.getClient(options)
}
// HELPERS
private var scanned = false
// private var cameraPermissionHelper: CameraPermissionHelper? = null
private var infoListener: InfoListener? = null
// BINDING
private var _binding: FragmentQrScannerBinding? = null
private val binding get() = _binding!!
override fun onAttach(context: Context) {
super.onAttach(context)
if (context is InfoListener) {
infoListener = context
} else {
throw RuntimeException("$context must implement InfoListener")
}
}
override fun onCreateView(
inflater: LayoutInflater,
parent: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentQrScannerBinding.inflate(inflater, parent, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
previewView = binding.scannerPreviewView
scannerProgressBar = binding.scannerProgressBar
scannerTextView = binding.scannerTextView
previewView.previewStreamState.observe(viewLifecycleOwner) { state ->
when (state) {
PreviewView.StreamState.IDLE -> {
scannerProgressBar.visibility = View.VISIBLE
scannerTextView.visibility = View.GONE
}
PreviewView.StreamState.STREAMING -> {
scannerProgressBar.visibility = View.GONE
scannerTextView.visibility = View.VISIBLE
}
}
}
view.post { checkAndRequestCameraPermission() }
}
private fun checkAndRequestCameraPermission() {
context?.let { ctx ->
when {
// 2a. If permission is already granted, just open the camera
ContextCompat.checkSelfPermission(ctx, Manifest.permission.CAMERA) ==
PackageManager.PERMISSION_GRANTED -> {
startCamera()
}
// 2c. Otherwise, directly request the permission
else -> {
requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA)
}
}
}
}
private fun startCamera() {
scannerProgressBar.visibility = View.VISIBLE
val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext())
cameraProviderFuture.addListener({
cameraProvider = cameraProviderFuture.get()
val previewUseCase = Preview.Builder()
.build()
.also { it.setSurfaceProvider(previewView.surfaceProvider) }
analysisUseCase = ImageAnalysis.Builder()
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888)
.build()
.also { useCase ->
useCase.setAnalyzer(cameraExecutor) { imageProxy ->
processImageProxy(imageProxy)
}
}
val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
cameraProvider.unbindAll()
cameraProvider.bindToLifecycle(
viewLifecycleOwner,
cameraSelector,
previewUseCase,
analysisUseCase
)
}, ContextCompat.getMainExecutor(requireContext()))
}
private fun processImageProxy(imageProxy: ImageProxy) {
try {
val width = imageProxy.width
val height = imageProxy.height
if (rgbaBitmap == null
|| rgbaBitmap?.width != width
|| rgbaBitmap?.height != height
) {
rgbaBitmap?.recycle()
rgbaBitmap = createBitmap(width, height)
}
val buffer = imageProxy.planes[0].buffer
buffer.rewind()
rgbaBitmap!!.copyPixelsFromBuffer(buffer)
val inputImage =
InputImage.fromBitmap(rgbaBitmap!!, imageProxy.imageInfo.rotationDegrees)
scanner.process(inputImage)
.addOnSuccessListener { barcodes ->
if (!scanned) {
barcodes.firstOrNull()?.rawValue?.let { value ->
scanned = true
requireActivity().runOnUiThread {
onQrcodeDetected(value)
}
}
}
}
.addOnFailureListener { e ->
Log.e(LOGCAT_TAG, "Błąd skanowania", e)
}
} catch (e: Exception) {
Log.e(LOGCAT_TAG, "Error processing imageProxy", e)
} finally {
imageProxy.close()
}
}
override fun onResume() {
super.onResume()
scanned = false
}
override fun onStop() {
if (::cameraProvider.isInitialized) {
cameraProvider.unbindAll()
}
super.onStop()
}
override fun onDestroyView() {
super.onDestroyView()
if (::cameraProvider.isInitialized) {
cameraProvider.unbindAll()
}
cameraExecutor.shutdown()
scanner.close()
rgbaBitmap?.run {
if (!isRecycled) recycle()
}
rgbaBitmap = null
_binding = null
}
protected abstract fun onQrcodeDetected(value: String)
companion object {
private const val LOGCAT_TAG = "TagDebug"
}
}
Все работает на Android 11, но не для API 34
Подробнее здесь:
https://stackoverflow.com/questions/796 ... s-no-surfa
1749209931
Anonymous
Я не могу запустить свой сканер с RegisterForctivityResult. Мне кажется, что запуск запускается слишком быстро, когда еще нет представления поверхности.[code]override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) previewView = binding.scannerPreviewView scannerProgressBar = binding.scannerProgressBar scannerTextView = binding.scannerTextView previewView.previewStreamState.observe(viewLifecycleOwner) { state -> when (state) { PreviewView.StreamState.IDLE -> { scannerProgressBar.visibility = View.VISIBLE scannerTextView.visibility = View.GONE } PreviewView.StreamState.STREAMING -> { scannerProgressBar.visibility = View.GONE scannerTextView.visibility = View.VISIBLE } } } startCamera() } < /code> и навигация на сам сканер, и, к сожалению, он также просто отображает черный экран. < /p> Только когда я перевел разрешения с MainActivty в фрагмент, что камера запускается, когда я ввожу его во второй раз. Сканер начинается только после второго вызова. < /P> abstract class QrScannerGenericFragment : Fragment() { // UI private lateinit var previewView: PreviewView private lateinit var scannerProgressBar: ProgressBar private lateinit var scannerTextView: TextView // CAMERA private val requestCameraPermissionLauncher = registerForActivityResult(ActivityResultContracts.RequestPermission()) { isGranted -> if (isGranted) { // Permission was granted – proceed to open camera startCamera() } else { // Permission denied – show a message or disable camera functionality } } private lateinit var cameraProvider: ProcessCameraProvider private lateinit var analysisUseCase: ImageAnalysis private var rgbaBitmap: Bitmap? = null private val cameraExecutor by lazy { Executors.newSingleThreadExecutor() } private val scanner by lazy { val options = BarcodeScannerOptions.Builder() .setBarcodeFormats(Barcode.FORMAT_QR_CODE) .build() BarcodeScanning.getClient(options) } // HELPERS private var scanned = false // private var cameraPermissionHelper: CameraPermissionHelper? = null private var infoListener: InfoListener? = null // BINDING private var _binding: FragmentQrScannerBinding? = null private val binding get() = _binding!! override fun onAttach(context: Context) { super.onAttach(context) if (context is InfoListener) { infoListener = context } else { throw RuntimeException("$context must implement InfoListener") } } override fun onCreateView( inflater: LayoutInflater, parent: ViewGroup?, savedInstanceState: Bundle? ): View? { _binding = FragmentQrScannerBinding.inflate(inflater, parent, false) return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) previewView = binding.scannerPreviewView scannerProgressBar = binding.scannerProgressBar scannerTextView = binding.scannerTextView previewView.previewStreamState.observe(viewLifecycleOwner) { state -> when (state) { PreviewView.StreamState.IDLE -> { scannerProgressBar.visibility = View.VISIBLE scannerTextView.visibility = View.GONE } PreviewView.StreamState.STREAMING -> { scannerProgressBar.visibility = View.GONE scannerTextView.visibility = View.VISIBLE } } } view.post { checkAndRequestCameraPermission() } } private fun checkAndRequestCameraPermission() { context?.let { ctx -> when { // 2a. If permission is already granted, just open the camera ContextCompat.checkSelfPermission(ctx, Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED -> { startCamera() } // 2c. Otherwise, directly request the permission else -> { requestCameraPermissionLauncher.launch(Manifest.permission.CAMERA) } } } } private fun startCamera() { scannerProgressBar.visibility = View.VISIBLE val cameraProviderFuture = ProcessCameraProvider.getInstance(requireContext()) cameraProviderFuture.addListener({ cameraProvider = cameraProviderFuture.get() val previewUseCase = Preview.Builder() .build() .also { it.setSurfaceProvider(previewView.surfaceProvider) } analysisUseCase = ImageAnalysis.Builder() .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) .setOutputImageFormat(ImageAnalysis.OUTPUT_IMAGE_FORMAT_RGBA_8888) .build() .also { useCase -> useCase.setAnalyzer(cameraExecutor) { imageProxy -> processImageProxy(imageProxy) } } val cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA cameraProvider.unbindAll() cameraProvider.bindToLifecycle( viewLifecycleOwner, cameraSelector, previewUseCase, analysisUseCase ) }, ContextCompat.getMainExecutor(requireContext())) } private fun processImageProxy(imageProxy: ImageProxy) { try { val width = imageProxy.width val height = imageProxy.height if (rgbaBitmap == null || rgbaBitmap?.width != width || rgbaBitmap?.height != height ) { rgbaBitmap?.recycle() rgbaBitmap = createBitmap(width, height) } val buffer = imageProxy.planes[0].buffer buffer.rewind() rgbaBitmap!!.copyPixelsFromBuffer(buffer) val inputImage = InputImage.fromBitmap(rgbaBitmap!!, imageProxy.imageInfo.rotationDegrees) scanner.process(inputImage) .addOnSuccessListener { barcodes -> if (!scanned) { barcodes.firstOrNull()?.rawValue?.let { value -> scanned = true requireActivity().runOnUiThread { onQrcodeDetected(value) } } } } .addOnFailureListener { e -> Log.e(LOGCAT_TAG, "Błąd skanowania", e) } } catch (e: Exception) { Log.e(LOGCAT_TAG, "Error processing imageProxy", e) } finally { imageProxy.close() } } override fun onResume() { super.onResume() scanned = false } override fun onStop() { if (::cameraProvider.isInitialized) { cameraProvider.unbindAll() } super.onStop() } override fun onDestroyView() { super.onDestroyView() if (::cameraProvider.isInitialized) { cameraProvider.unbindAll() } cameraExecutor.shutdown() scanner.close() rgbaBitmap?.run { if (!isRecycled) recycle() } rgbaBitmap = null _binding = null } protected abstract fun onQrcodeDetected(value: String) companion object { private const val LOGCAT_TAG = "TagDebug" } } [/code] Все работает на Android 11, но не для API 34 Подробнее здесь: [url]https://stackoverflow.com/questions/79655863/android-kotlin-permission-launcher-launches-too-quickly-when-there-is-no-surfa[/url]