How analize preview rectangle camerax android? - android-camerax

I am a beginner developer. I'm sorry for my english.
I am trying to make a barcode reader application. I use MLKit and CameraX.
I want to analyze only the part of the preview that is in the rectangle. Now the preview is being analyzed in full. I only want to analize what is in the rectangle. I tried to use the ViewPort, but it seems I didn't quite understand what it was for, because it could not solve the problem. I looked for solutions on the Internet, but my problem remained relevant. I think that before analysis it is necessary to crop the image and only then analyze it, but is this true?
My layout:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="false"
tools:background="#android:color/white"
tools:context=".ui.BarcodeScanningFragment">
<androidx.camera.view.PreviewView
android:id="#+id/viewFinder"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.t_ovchinnikova.android.scandroid_2.ui.ViewFinderOverlay
android:id="#+id/overlay"
android:layerType="software"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
Camera class:
class CameraSource (private val overlay: ViewFinderOverlay) {
private lateinit var cameraExecutor: ExecutorService
val preview : Preview = Preview.Builder()
.build()
fun startCamera() {
val cameraProviderFuture = ProcessCameraProvider.getInstance(overlay.context)
cameraProviderFuture.addListener(Runnable {
//Используется для привязки жизненного цикла камер к владельцу жизненного цикла
val cameraProvider: ProcessCameraProvider = cameraProviderFuture.get()
bindCameraUseCases(cameraProvider)
}, ContextCompat.getMainExecutor(overlay.context))
}
#SuppressLint("UnsafeOptInUsageError")
private fun bindCameraUseCases(cameraProvider: ProcessCameraProvider) {
val imageAnalysis = ImageAnalysis.Builder()
.setTargetResolution(Size(overlay.width, overlay.height))
.setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST)
.build()
val orientationEventListener = object : OrientationEventListener(overlay.context) {
override fun onOrientationChanged(orientation : Int) {
// Monitors orientation values to determine the target rotation value
val rotation : Int = when (orientation) {
in 45..134 -> Surface.ROTATION_270
in 135..224 -> Surface.ROTATION_180
in 225..314 -> Surface.ROTATION_90
else -> Surface.ROTATION_0
}
imageAnalysis.targetRotation = rotation
}
}
orientationEventListener.enable()
cameraExecutor = Executors.newSingleThreadExecutor()
val analyzer = BarcodeAnalyzer()
imageAnalysis.setAnalyzer(cameraExecutor, analyzer)
var cameraSelector : CameraSelector = CameraSelector.Builder()
.requireLensFacing(CameraSelector.LENS_FACING_BACK)
.build()
val useCaseGroup = UseCaseGroup.Builder()
.addUseCase(preview)
.addUseCase(imageAnalysis)
.build()
cameraProvider.bindToLifecycle(overlay.context as LifecycleOwner, cameraSelector, useCaseGroup)
}
}
class ViewFinderOverlay:
class ViewFinderOverlay(context: Context, attrs: AttributeSet) : View(context, attrs) {
private val boxPaint: Paint = Paint().apply {
color = ContextCompat.getColor(context, R.color.barcode_reticle_stroke)
style = Paint.Style.STROKE
strokeWidth = context.resources.getDimensionPixelOffset(R.dimen.barcode_stroke_width).toFloat()
}
private val scrimPaint: Paint = Paint().apply {
color = ContextCompat.getColor(context, R.color.barcode_reticle_background)
}
private val eraserPaint: Paint = Paint().apply {
strokeWidth = boxPaint.strokeWidth
xfermode = PorterDuffXfermode(PorterDuff.Mode.CLEAR)
}
private val boxCornerRadius: Float =
context.resources.getDimensionPixelOffset(R.dimen.barcode_reticle_corner_radius).toFloat()
var boxRect: RectF? = null
fun setViewFinder() {
val overlayWidth = width.toFloat()
val overlayHeight = height.toFloat()
val boxWidth = overlayWidth * 80 /100
val boxHeight = overlayHeight * 36 / 100
val cx = overlayWidth / 2
val cy = overlayHeight / 2
boxRect = RectF(cx - boxWidth / 2, cy - boxHeight / 2, cx + boxWidth / 2, cy + boxHeight / 2)
invalidate()
}
override fun draw(canvas: Canvas) {
super.draw(canvas)
boxRect?.let {
canvas.drawRect(0f, 0f, canvas.width.toFloat(), canvas.height.toFloat(), scrimPaint)
eraserPaint.style = Paint.Style.FILL
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, eraserPaint)
eraserPaint.style = Paint.Style.STROKE
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, eraserPaint)
// Draws the box.
canvas.drawRoundRect(it, boxCornerRadius, boxCornerRadius, boxPaint)
}
}
}
ViewFinderOverlay - a view that is superimposed on the camera preview.
Screen:
[1]: https://i.stack.imgur.com/cYvFP.jpg
Image analyzer:
#SuppressLint("UnsafeOptInUsageError")
override fun analyze(imageProxy: ImageProxy) {
val mediaImage = imageProxy.image
if (mediaImage != null) {
val image = InputImage.fromMediaImage(mediaImage, imageProxy.imageInfo.rotationDegrees)
val scanner = BarcodeScanning.getClient()
scanner.process(image)
.addOnSuccessListener { barcodes ->
barcodes?.firstOrNull().let { barcode ->
val rawValue = barcode?.rawValue
rawValue?.let {
}
}
}
imageProxy.close()
}
}
Help me please. I would be glad to any advice.

You can take a look at LifecycleCameraController. It takes care of creating use cases as well as configuring ViewPort.
However AFAIK, MLKit doesn't respect the crop rect. What you can do is checking the MLKit result, and discard any barcode whose coordinates are outside of the crop rect.

Related

From Glide.with showing ImageView Android Studio

I am trying to solve this issue but i didn#t find enything until now. So my Image View its a string and i am trying to call it with Glide.with and many more but unfortunately no success until now. It should be a simple Line of code but i am not finding it.
class ImageInformation : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_image_information)
val image = intent.getParcelableExtra<ImageModel>("image")
if (image != null){
val downloadUrl: TextView = findViewById(R.id.downloadUrla)
val previewUrl : ImageView = findViewById(R.id.previewUrls)
val id: TextView = findViewById(R.id.idsa)
val filename: TextView = findViewById(R.id.fileNamea)
val filesize: TextView = findViewById(R.id.filesizea)
val contentType: TextView = findViewById(R.id.contentTypea)
val createdBy: TextView = findViewById(R.id.createdBya)
val createdTimestamp: TextView = findViewById(R.id.createdTimestampa)
val creationSource: TextView = findViewById(R.id.creationSourcea)
val domainIdentityType: TextView = findViewById(R.id.domainIdentityTypea)
val domainIdentityValue: TextView = findViewById(R.id.domainIdentityValuea)
val tags: TextView = findViewById(R.id.tagsa)
val description: TextView = findViewById(R.id.descriptiona)
downloadUrl.text = image.downloadUrl
TO DO, Preview URL
//previewUrl.setImageResource(image.previewUrl)//Required Int not a String.
//Glide.with(this).load( previewUrl).into(previewUrl)
//Glide.with(Activity()).load(previewUrl).centerInside().into(previewUrl)
//previewUrl.setImageResource(image.previewUrl)
//Glide.with(Activity()).load(previewUrl).into(image.previewUrl)
//Glide.with(this).load(previewUrl).centerInside().into(image.previewUrl)
//previewUrl.setImageResource(image.previewUrl)
//Nothing from the above lines doesn#t work.
id.text = image.id
filename.text = image.fileName
filesize.text = image.filesize.toString()
contentType.text = image.contentType
createdBy.text = image.createdBy
createdTimestamp.text = image.createdTimestamp
creationSource.text = image.creationSource
domainIdentityType.text = image.domainIdentityType
domainIdentityValue.text = image.domainIdentityValue
tags.text = image.tags.toString()
description.text = image.description
}
}
}
Try this:
Glide.with(context)
.load(image.downloadUrl) // here we are telling Glide what it needs to load
.into(previewUrl) // here we are telling Glide where to load it into (your imageView)
I must say your naming of you ImageView (previewUrl) does make it very hard to read, it might be better to rename it to something like previewImageView.

Placeholder Photo Not Appearing on Null URL in Search - Android, Kotlin, Glide

I'm using Glide in an Android, Kotlin, Jetpack project. When the photos are loading, the DEFAULT_MOVIE_IMAGE appears. However if one of the movies that is returned has a null value for an image URL, no image appears and the card shrinks down to the size of only the title. I'm trying to set it so the DEFAULT_MOVIE_IMAGE will appear if there is a null value for the movie poster url.
Setting a placeholder or an error image does not appear to be working.
Image part of the MovieCard composable
movie.posterPath?.let { url ->
val image = loadPicture(url = url, defaultImage = DEFAULT_MOVIE_IMAGE).value
image?.let { img ->
Image(
bitmap = img.asImageBitmap(),
contentDescription = "Movie Projector",
modifier = Modifier
.fillMaxWidth()
.height(450.dp),
contentScale = ContentScale.Fit
)
}
ImageUtils.kt
const val DEFAULT_MOVIE_IMAGE = R.drawable.movie_placeholder
const val POSTER_BASE_URL = "https://image.tmdb.org/t/p/w500"
#Composable
fun loadPicture(
url: String,
#DrawableRes defaultImage: Int
): MutableState<Bitmap?> {
val bitmapState: MutableState<Bitmap?> = remember { mutableStateOf(null) }
Glide.with(LocalContext.current)
.asBitmap()
.load(defaultImage)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
bitmapState.value = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
Glide.with(LocalContext.current)
.asBitmap()
.load("https://image.tmdb.org/t/p/w500$url")
.error(R.drawable.space_dog_laika1)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
bitmapState.value = resource
}
override fun onLoadCleared(placeholder: Drawable?) {
}
})
return bitmapState
}
I ended up wrapping the image piece of the movie card in an if/else statement. I'm not sure if this is best practice or the most terse solution, but it worked.
if (movie.posterPath != null) {
movie.posterPath?.let { url ->
val image = loadPicture(url = url, defaultImage = DEFAULT_MOVIE_IMAGE).value
image?.let { img ->
Image(
bitmap = img.asImageBitmap(),
contentDescription = "Movie Poster",
modifier = Modifier
.fillMaxWidth()
.height(450.dp),
contentScale = ContentScale.Fit
)
}
}
} else {
val image: Painter = painterResource(id = DEFAULT_MOVIE_IMAGE)
Image(
painter = image,
contentDescription = "Film Projector",
modifier = Modifier
.fillMaxWidth()
.height(450.dp),
contentScale = ContentScale.Fit
)
}

Android Jetpack Passing Data Between Composables

I'm trying to pass a constantly updating variable "message" across my Jetpack Composables. I have a draggable box that tracks the coordinates of the box but I'm trying to send the real-time data through a TCP connection. However, I noticed that the current coordinate of the draggable box isn't passing through to the other Composable or the socket -only the same value is passed despite message changing continuously due to me dragging the box. Also, the moment dataSendButton() is pressed, the createDragImage() and its draggable box stops animating/running.
var message = "" // global Android send message
class MainActivity : ComponentActivity() {
private var textView: TextView? = null
dataSendButton()
createDragImage()
...
}
}
}
#Composable
fun createDragImage(){
val context = LocalContext.current
...
Box() {
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
.background(Color.Transparent)
.size(150.dp)
.border(BorderStroke(4.dp, SolidColor(Color.Red)))
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consumeAllChanges()
offsetX = someConstantX
offsetY += dragAmount.y
message = offsetY.toString()
...
#Composable
fun dataSendButton() {
val context = LocalContext.current
...
Button(
onClick = {
// **ISSUE: message in this composable is not getting updated with message value from createDragImage()
val b1 = MainActivity.TCPconnector_client(context, message)
b1.execute()
},
{
Text(text = "Send Data", color = Color.White, fontSize = 20.sp)
}
}
}
}
}
It is because that is not how you store state in Compose.
Change the declaration of the variable.
var message by mutableStateOf(...)
Then the changes to it will trigger a recomposition, and so the rest of the code should remain the same. It is always recommended to store the state holders in a viewmodel, and pass the viewmodel around instead.
This is a working code with viewmodel
class MainActivity : ComponentActivity() {
private var textView: TextView? = null
val vm by viewmodels<MViewModel>()
dataSendButton(vm.message, vm:: onMessageChange)
createDragImage(vm.message)
...
}
}
}
#Composable
fun createDragImage(message: String, onMessageChange: (String) -> Unit){
val context = LocalContext.current
...
Box() {
var offsetX by remember { mutableStateOf(0f) }
var offsetY by remember { mutableStateOf(0f) }
Box(
Modifier
.offset { IntOffset(offsetX.roundToInt(), offsetY.roundToInt()) }
.background(Color.Transparent)
.size(150.dp)
.border(BorderStroke(4.dp, SolidColor(Color.Red)))
.pointerInput(Unit) {
detectDragGestures { change, dragAmount ->
change.consumeAllChanges()
offsetX = someConstantX
offsetY += dragAmount.y
onMessageChange (offsetY.toString())
...
#Composable
fun dataSendButton(message: String) {
val context = LocalContext.current
...
Button(
onClick = {
// **ISSUE: message in this composable is not getting updated with message value from createDragImage() // This seems to be an error. Calling a Composable from onClick?
val b1 = MainActivity.TCPconnector_client(context, message)
b1.execute()
},
{
Text(text = "Send Data", color = Color.White, fontSize = 20.sp)
}
}
}
}
}
class MViewModel: ViewModel(){
var message by mutableStateOf("")
private set //do not allow external modifications to ensure consistency
fun onMessageChange (newMessage: String){
message = newMessage
}
}
Note this is the ideal way of doing such implementation. However, for your specific case, if you do not need to access it anywhere else, only changing the declaration as described in the second line of the answer should do
Thanks

App crashes when I click a Button if EditText is empty

I wrote a simple temperature converter app, everything is working fine except when user leaves EditText blank/null but selects one of the radio buttons, The App crashes.
Here is the Kotlin Code:
class MainActivity : AppCompatActivity() {
lateinit var etTemp: EditText
lateinit var radioGroup: RadioGroup
lateinit var btnConverter :Button
lateinit var tempConverted: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
title = "Zeeshan's Temperature Converter"
etTemp = findViewById(R.id.etTemp)
radioGroup = findViewById(R.id.radioGroup)
btnConverter = findViewById(R.id.btnConverter)
tempConverted = findViewById(R.id.tempConverted)
btnConverter.setOnClickListener {
val id = radioGroup.checkedRadioButtonId
val radioButton = findViewById<RadioButton>(id)
if (radioButton == findViewById(R.id.radioC)){
val temp =etTemp.text.toString().toInt()
val result = temp * 9/5 + 32
tempConverted.setText(result.toString())
}
else if (radioButton == findViewById(R.id.radioF)){
val tempy =etTemp.text.toString().toInt()
val resulty = (tempy - 32) / 1.8
tempConverted.setText(resulty.toString())
}
else{
Toast.makeText(this#MainActivity, "Select one conversion scale", Toast.LENGTH_SHORT).show()
}
}
}
}
You should check if etTemp.text.ToString() == "" (that is empty string) if it is, then do not try to convert it into int. The problem appears when you try to convert "null" value to int.
Inside the listener add a check for for EditText to check whether it is empty or not,
btnConverter.setOnClickListener {
// Add the validation check here ... like this -> if(etTemp.length() > 0){
val id = radioGroup.checkedRadioButtonId
val radioButton = findViewById<RadioButton>(id)
if (radioButton == findViewById(R.id.radioC)){
val temp =etTemp.text.toString().toInt()
val result = temp * 9/5 + 32
tempConverted.setText(result.toString())
}
else if (radioButton == findViewById(R.id.radioF)){
val tempy =etTemp.text.toString().toInt()
val resulty = (tempy - 32) / 1.8
tempConverted.setText(resulty.toString())
}
else{
Toast.makeText(this#MainActivity, "Select one conversion scale", Toast.LENGTH_SHORT).show()
}
// Close the check here -> }
else{
// Prompt the user to put some text in the field - this is called form validation before processing
// Toast.makeText(.....).show
}
}
check if the user has entered some input or not, one way is like below (Kotlin):
if(!etTemp.text.isNullOrEmpty())
{
temp =etTemp.text.toString().toInt()
}

Xamarin.Forms Action Bar - Center Aligned Image

Using Xamarin.Forms, how do I get the same effect as the application pictured below, specifically to show a centred image on the Action Bar / page tool bar (the section in a blue box)?
I would like to have a long width image in that section, and the solution must work for Android, iOS, Windows Phone, and Universal Windows (even if it means writing custom renderers or platform specific xamarin code).
I suggest you create your own Xamarin.Forms view and handle the navigation by yourself something similar to this:
public class CustomBackNavigationBar : StackLayout
{
public Image BackIcon;
public Image Icon;
public Label IconTitle;
public StackLayout IconContainer;
public CustomBackNavigationBar(string title, string icon)
{
Padding = new Thickness(15,5);
HeightRequest = 40;
Orientation = StackOrientation.Horizontal;
VerticalOptions = LayoutOptions.Start;
BackgroundColor = StaticData.BlueColor;
Spacing = 15;
BackIcon = new Image
{
Source = StaticData.BackIcon,
HorizontalOptions = LayoutOptions.Start
};
Label Title = new Label
{
Text = title,
TextColor = Color.White,
FontSize = Device.GetNamedSize(NamedSize.Default, typeof(Label)),
FontAttributes = FontAttributes.Bold,
VerticalTextAlignment = TextAlignment.Center
};
Icon = new Image
{
Source = icon
};
IconTitle = new Label
{
Text = StaticData.CallAgent,
TextColor = Color.White,
FontSize = Device.GetNamedSize(NamedSize.Micro, typeof(Label)),
};
IconContainer = new StackLayout
{
HorizontalOptions = LayoutOptions.EndAndExpand,
Spacing = 2,
Children = { Icon, IconTitle }
};
Children.Add(BackIcon);
Children.Add(Title);
Children.Add(IconContainer);
#region Events
BackIcon.GestureRecognizers.Clear();
BackIcon.GestureRecognizers.Add(new TapGestureRecognizer
{
Command = new Command(PopAsync)
});
#endregion
}
async void PopAsync()
{
await App.AppNavigation.PopAsync();
}
}

Resources