Home > database >  Application Crashes Accepting Photo In Landscape Mode
Application Crashes Accepting Photo In Landscape Mode

Time:06-02

I've got an activity which uses camera intent. If I take a picture vertically and accept to display it in an imageview vertically there is no problem. If i take a picture horizontally and accept it while having the mobile horizontally it crashes, but if I turn it to vertical it works. Does anybody know why this might be happening? It does not have much sense cause when the app crashes the only thing it says is that the photoFile is null, it seems as if there was no picture but in fact there is a picture. Here is the code from the activity:

private const val FILE_NAME = "photo.jpg"

class TextCameraActivity : AppCompatActivity(), TextToSpeech.OnInitListener {

private lateinit var binding: ActivityTextCameraBinding

private lateinit var bitmap : Bitmap

private lateinit var photoFile: File

private var tts: TextToSpeech? = null
private var locale : Locale = Locale("es", "ES")

private var progressBar : ProgressBar? = null
private var i = 0
private val handler = Handler()


override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    binding = ActivityTextCameraBinding.inflate(layoutInflater)
    setContentView(binding.root)

    binding.btn7.setOnClickListener {
        val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
        photoFile = getPhotoFile(FILE_NAME)

        val fileProvider = FileProvider.getUriForFile(this, "com.example.fileprovider", photoFile)
        takePictureIntent.putExtra(MediaStore.EXTRA_OUTPUT, fileProvider)

        if(takePictureIntent.resolveActivity(this.packageManager) != null) {
            startActivityForResult(takePictureIntent, 42)

        } else {
            Toast.makeText(this, "Unable to open camera", Toast.LENGTH_SHORT).show()
            Log.d("mensaje", "?????Unable to open camera")
        }
    }

    tts = TextToSpeech(this, this)

    progressBar = binding.progressBar

    binding.btnReconocerImagen.setOnClickListener {

        progressBar!!.visibility = View.VISIBLE
        i = progressBar!!.progress

        Thread(Runnable {
            while (i < 10) {
                i  = 1
                handler.post(Runnable {
                    progressBar!!.progress = i

                })
                try {
                    Thread.sleep(100)
                } catch (e: InterruptedException) {
                    e.printStackTrace()
                }
            }
            progressBar!!.visibility = View.INVISIBLE

        }).start()
        recognizeText()
    }

    val actionBar = supportActionBar
    actionBar?.setDisplayHomeAsUpEnabled(true)
}

private fun getPhotoFile(fileName:String):File {
    val storageDirectory = getExternalFilesDir(Environment.DIRECTORY_PICTURES)
    return File.createTempFile(fileName, ".jpg", storageDirectory)
}

override fun onOptionsItemSelected(item: MenuItem): Boolean {
    when (item.getItemId()) {
        android.R.id.home -> {
            finish()
            return true
        }
    }
    return super.onOptionsItemSelected(item)
}

override fun onCreateOptionsMenu(menu: Menu?): Boolean {
    return true
}

override fun onInit(status: Int) {

    if (status == TextToSpeech.SUCCESS) {
        // set US English as language for tts
        val result = tts!!.setLanguage(locale)

        if (result == TextToSpeech.LANG_MISSING_DATA || result == TextToSpeech.LANG_NOT_SUPPORTED) {
            Log.e("TTS","The Language specified is not supported!")
        } else {
            binding.btnReconocerImagen.isEnabled = true

        }

    } else {
        Log.e("TTS", "Initilization Failed!")
    }

}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if(requestCode == 42 && resultCode == Activity.RESULT_OK) {
        bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
        binding.imageView.setImageBitmap(bitmap)
    } else {
        super.onActivityResult(requestCode, resultCode, data)
    }

}

private fun recognizeText() {

    try {
        val image = InputImage.fromBitmap(bitmap, 0)

        val recognizer = TextRecognition.getClient(TextRecognizerOptions.DEFAULT_OPTIONS)

        val result = recognizer.process(image)
            .addOnSuccessListener { visionText ->

                for (block in visionText.textBlocks) {
                    val boundingBox = block.boundingBox
                    val cornerPoints = block.cornerPoints
                    val text = block.text

                    Log.d("mensaje", "he encontrado $text")
                    binding.tvTextoReconocido.text = "El texto reconocido es: $text"
                    tts!!.speak("El texto reconocido es $text", TextToSpeech.QUEUE_FLUSH, null, "")

                    for (line in block.lines) {
                        // ...
                        for (element in line.elements) {
                            // ...
                        }
                    }
                }
            }
            .addOnFailureListener { e ->

            }


    } catch (e : Exception) {
        Log.d("mensaje", "NO HAY IMAGEN SELECCIONADA")
        Toast.makeText(this,"NO HAS SELECCIONADO NINGUNA IMAGEN PARA RECONOCER", Toast.LENGTH_SHORT).show()
    }


}

}

Error:

E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.example.appdigitalinkrecognition, PID: 30407
    java.lang.RuntimeException: Unable to resume activity {com.example.appdigitalinkrecognition/com.example.appdigitalinkrecognition.TextCameraActivity}: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=42, result=-1, data=null} to activity {com.example.appdigitalinkrecognition/com.example.appdigitalinkrecognition.TextCameraActivity}: kotlin.UninitializedPropertyAccessException: lateinit property photoFile has not been initialized
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4918)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4955)
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:246)
        at android.app.ActivityThread.main(ActivityThread.java:8653)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602)
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
     Caused by: java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=42, result=-1, data=null} to activity {com.example.appdigitalinkrecognition/com.example.appdigitalinkrecognition.TextCameraActivity}: kotlin.UninitializedPropertyAccessException: lateinit property photoFile has not been initialized
        at android.app.ActivityThread.deliverResults(ActivityThread.java:5590)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4905)
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4955) 
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) 
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:246) 
        at android.app.ActivityThread.main(ActivityThread.java:8653) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130) 
     Caused by: kotlin.UninitializedPropertyAccessException: lateinit property photoFile has not been initialized
        at com.example.appdigitalinkrecognition.TextCameraActivity.onActivityResult(TextCameraActivity.kt:143)
        at android.app.Activity.dispatchActivityResult(Activity.java:8550)
        at android.app.ActivityThread.deliverResults(ActivityThread.java:5583)
        at android.app.ActivityThread.performResumeActivity(ActivityThread.java:4905) 
        at android.app.ActivityThread.handleResumeActivity(ActivityThread.java:4955) 
        at android.app.servertransaction.ResumeActivityItem.execute(ResumeActivityItem.java:52) 
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:176) 
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97) 
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2336) 
        at android.os.Handler.dispatchMessage(Handler.java:106) 
        at android.os.Looper.loop(Looper.java:246) 
        at android.app.ActivityThread.main(ActivityThread.java:8653) 
        at java.lang.reflect.Method.invoke(Native Method) 
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:602) 
        at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1130)
```` 

CodePudding user response:

If I had to guess, you're getting different results because changing the orientation destroys an Activity, so a new one can be created to match the new orientation. So maybe that's happening in the background - but if you take a photo vertically, there's no orientation change so the Activity isn't destroyed

That's an issue because like the docs say:

Note: Since your process and activity can be destroyed between when you call launch() and when the onActivityResult() callback is triggered, any additional state needed to handle the result must be saved and restored separately from these APIs.

This is the closest thing I can find to them explicitly saying "onActivityResult can run before onCreate", but if that's what's happening to you, then that's why you're getting that lateinit property photoFile has not been initialized error.

It might also be because you're not actually initialising it in onCreate, you're doing it in a click listener - so even if onCreate does run first, the button would need to be clicked again, and onActivityResult definitely runs before that. But just moving it into onCreate might not be enough


I think to handle this properly, you'll need to think about your "show the photo" task having two parts - creating the path (in onCreate) and getting a result (through onActivityResult). You can't guarantee the order of those two things, so when you do one, you'll have to check if the other part has already been completed - if it has, you can show the image.

You could do something like this:

private var pathInitialised = false
private var photoTaken = false

override fun onCreate(savedInstanceState: Bundle?) {
    ...
    // don't do this in the click listener - it needs to be available when the activity starts
    photoFile = getPhotoFile(FILE_NAME)
    // set the flag, and try to show the pic if ready
    pathInitialised = true
    tryDisplayPhoto()
}

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if(requestCode == 42 && resultCode == Activity.RESULT_OK) {
        // set the flag, and try to show the pic if ready
        photoTaken = true
        tryDisplayPhoto()
    } else {
        super.onActivityResult(requestCode, resultCode, data)
    }
}

private fun tryDisplayPhoto() {
    if (pathInitialised && photoTaken) {
        // now you know that the path is available and a photo needs to be shown
        bitmap = BitmapFactory.decodeFile(photoFile.absolutePath)
        binding.imageView.setImageBitmap(bitmap)
    }
}

So now, no matter what order those two methods are called, they both try to display a pic - and that will only happen when the last of those methods is called, and all the pieces are in place

CodePudding user response:

Declaring photoFile as optional should solve the problem:

private var photoFile: File? = null 

You will need to add a null-check in your onActivityResult:

override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
    if(requestCode == 42 && resultCode == Activity.RESULT_OK) {
        photoFile?.let { photo -> 
            bitmap = BitmapFactory.decodeFile(photo.absolutePath)
            binding.imageView.setImageBitmap(bitmap)
        } ?: run {
            // Photo is not defined...
        }
    } else {
        super.onActivityResult(requestCode, resultCode, data)
    }
}
  • Related