How can I pass a variable control name to findViewById in Kotlin? - android

I am trying to learn Kotlin and I'm building a simple example as I go. I have 3 image buttons that open the camera and take a photo. The thumbnail is then set into an ImageView. I've used the examples from https://developer.android.com/training/camera/photobasics?hl=en to get the basics working (figuring if I can make it work for one, it'll work for all. It does indeed work for one, but I can't figure out how to make it one function that drops the thumbnail into the correct ImageView.
Inside my onCreate I have the listener for each of the buttons that will invoke the camera:
camRead1.setOnClickListener {dispatchTakePictureIntent() }
camRead2.setOnClickListener {dispatchTakePictureIntent() }
camRead3.setOnClickListener {dispatchTakePictureIntent() }
And I took the sample from the url above:
val REQUEST_IMAGE_CAPTURE = 1
private fun dispatchTakePictureIntent() {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, REQUEST_IMAGE_CAPTURE)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
val thumb: ImageView = findViewById(R.id.thumbRead1)
if (requestCode == REQUEST_IMAGE_CAPTURE && resultCode == RESULT_OK) {
val imageBitmap = data.extras.get("data") as Bitmap
thumb.setImageBitmap(imageBitmap)
}
}
and pasted it into my class MainActivity, and after I replaced imageView in the override function with a variable (thumb) and added the super, it worked perfectly for the first one.
However, I am trying to get 3 photos, read1, read2, and read3 which each need to display the thumb in thumbRead1, thumbRead2 and thumbRead3. I can't figure out how the onActivityResult is executed since the call inside dispatchTakePictureIntent is calling startActivityForResult (especially as Android Studio says that startActivityForResult is deprecated).
Obviously, once onActivityResult executes, I can see that thumb defines R.id.thumbRead1 and receives imageBitmap but I don't understand how I can make it aware of the button that was clicked.
Without understanding how onActivityResult is called, I'm thinking that if I can do something like:
findViewById(R.id("thumbRead" + imgID))
to define the specific ImageView that I want the photo pasted into. Am I on the right track here? If not, what is the recommended way of doing this?

Note they've recently added what's supposed to be a cleaner way of starting other activities for results and getting the results, explained here. But since you're already doing it the traditional way, I'll explain how to get that working.
I think the easiest thing to do in this situation is just make more request codes, so you can check which request it was.
val REQUEST_IMAGE_CAPTURE_SOURCE_1 = 1
val REQUEST_IMAGE_CAPTURE_SOURCE_2 = 2
val REQUEST_IMAGE_CAPTURE_SOURCE_3 = 3
//...
camRead1.setOnClickListener { dispatchTakePictureIntent(REQUEST_IMAGE_CAPTURE_SOURCE_1) }
camRead2.setOnClickListener { dispatchTakePictureIntent(REQUEST_IMAGE_CAPTURE_SOURCE_2) }
camRead3.setOnClickListener { dispatchTakePictureIntent(REQUEST_IMAGE_CAPTURE_SOURCE_3) }
//...
private fun dispatchTakePictureIntent(requestCode: Int) {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, requestCode)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode != RESULT_OK) {
// possibly show message to user
return
}
val imageViewId = when (requestCode) {
REQUEST_IMAGE_CAPTURE_SOURCE_1 -> R.id.thumbRead1
REQUEST_IMAGE_CAPTURE_SOURCE_2 -> R.id.thumbRead2
REQUEST_IMAGE_CAPTURE_SOURCE_3 -> R.id.thumbRead3
}
val imageView = findViewById<ImageView>(imageViewId)
imageView.imageBitmap = data.extras.get("data") as Bitmap
}
By the way, if you want to get an ID for a view using the String like you were showing you were trying, you would do it like this:
val viewId = resources.getIdentifier("thumbRead$imgId", "id", packageName)
val imageView = findViewById<ImageView>(viewId)

You need to pass different request code for each call and pass it to the dispatchTakePictureIntent function. You do not need to get id by findviewbyid. You simply can add the image on the basis of the request code.
val REQUEST_IMAGE_CAPTURE_ONE = 1
val REQUEST_IMAGE_CAPTURE_TWO = 2
val REQUEST_IMAGE_CAPTURE_THREE = 3
private fun dispatchTakePictureIntent(requestCode: Int) {
val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
try {
startActivityForResult(takePictureIntent, requestCode)
} catch (e: ActivityNotFoundException) {
// display error state to the user
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == RESULT_OK) {
val imageBitmap = data.extras.get("data") as Bitmap
if (requestCode == REQUEST_IMAGE_CAPTURE_ONE ) {
thumbRead1.setImageBitmap(imageBitmap)
}else if (requestCode == REQUEST_IMAGE_CAPTURE_TWO ) {
thumbRead2.setImageBitmap(imageBitmap)
}else if (requestCode == REQUEST_IMAGE_CAPTURE_THREE ) {
thumbRead3.setImageBitmap(imageBitmap)
}
}
}

Related

onActivityResults reseting values

I'm testing one functionality of an app in different devices and it happens in one of them:
When I calls onActivityResult, it works normally but in the second time it's called, it reset the previous data registered in the last call. It works normally in most of devices but this one exceptionally cleans the view when comes from activity result.
So basically when I selected the second image either from gallery or for camera intent, it resets the previous selected file.
This is how the data it's being retrieved:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if(resultCode == RESULT_OK) {
val selectedImage: Uri?
if(requestCode == GALLERY_1 || requestCode == GALLERY_2 || requestCode == GALLERY_3) {
selectedImage = data!!.data
configImagePersistentPermission(selectedImage)
} else {
val bitmap = data!!.extras!!.get("data") as Bitmap
selectedImage = configImagePathUri(requireContext(), bitmap)
}
try {
when (requestCode) {
GALLERY_1 -> {
binding.imageDoc1.setImageURI(selectedImage)
configResetImageView(0)
}
CAMERA_1 -> {
binding.imageDoc1.setImageURI(selectedImage)
configResetImageView(0)
}
GALLERY_2 -> {
binding.imageDoc2.setImageURI(selectedImage)
configResetImageView(1)
}
CAMERA_2 -> {
binding.imageDoc2.setImageURI(selectedImage)
configResetImageView(1)
}
GALLERY_3 -> {
binding.imageDoc3.setImageURI(selectedImage)
configResetImageView(2)
}
CAMERA_3 -> {
binding.imageDoc3.setImageURI(selectedImage)
configResetImageView(2)
}
}
imageList.add(selectedImage.toString())
} catch (e: Exception){
e.printStackTrace()
}
}
}
How to prevent losing the previous recorded data in this scenario?
Thanks in advance

Android Open Document Tree with Activity Result Contract

I was recently updating deprecated methods in my App and there is one that I have a few questions about. Did not find any example or explanation describing my use case...
There is a local backup and a restore functions. User has to choose a directory either where to save data to or to restore data from. It was implemented like this:
binding.backupBtn.setOnClikListener{
openDirectory(LOCAL_BACKUP_CODE)
}
binding.restoreBtn.setOnClickListener{
openDirectory(LOCAL_RESTORE_CODE)
}
private fun openDirectory(requestCode: Int){
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
startActivityForResult(intent, requestCode)
}
And then depending on the request code backup or restore:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode = Activity.RESULT_OK) {
val uriTree = data?.data ?: return
if (requestCode == LOCAL_BACKUP_CODE) {
localBackup(uriTree)
}
if (requestCode == LOCAL_RESTORE_CODE) {
localRestore(uriTree)
}
}
}
Now with this Activity Result Contracts how can I specify, and is it possible at all, custom request code. Android documentation for accessing a directory still uses startActivityForResult()
Link
ActivityResultContract.StartActivityForResult() - is a generic contract that takes any Intent but there is no way to provide custom request code.
As for now I simply created 2 launchers for backup and restore:
val backupRequest = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()){
if (it != null){
localBackup(it)
}
}
val restoreRequest = registerForActivityResult(ActivityResultContracts.OpenDocumentTree()){
if (it != null){
localRestore(it)
}
}
binding.backupBtn.setOnClickListener {
backupRequest.launch(null)
}
binding.restoreBtn.setOnClickListener {
restoreRequest.launch(null)
}
Is it the way to do it? I looked into custom Contracts, but can't figure out how I can return Uri AND custom variable (request code in this case).

Get Result - Kotlin startActivityForResult does not deliver any result

Halllo, my BarcodeActivity is called by startActivityForResult through my PCActivity. The value of the scanned barcode should then be returned back to the PCActivity and inserted in a text field there. Unfortunately, I do not get a value back. However, the app does not crash either. Here is my code.
PCActivity:
sn_mb.setDrawableRightTouch {
val intent = Intent(this#PCActivity, BarcodeActivity::class.java)
startActivityForResult(intent, 1)
}
[...]
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 1 && resultCode == Activity.RESULT_OK && data != null) {
val returnedSN = intent.getStringExtra("return_sn")
sn_mb.setText(returnedSN)
} else {
sn_mb.setText("FEHLER!")
}
}
BarcodeActivity
saveBtn.setOnClickListener {
val sn = editTextBarcode.text.toString()
sn.toString()
if (sn!= "") {
val returnIntent:Intent = Intent()
returnIntent.putExtra("return_sn", sn)
setResult(Activity.RESULT_OK, returnIntent)
finish()
} else {
Toast.makeText(
applicationContext,
"Das ist keine gültige Seriennummer",
Toast.LENGTH_SHORT
).show()
}
}
I hope someone can explain or help me with this problem. Thank you very much.
You are extracting your data from the wrong place.
Replace
val returnedSN = intent.getStringExtra("return_sn")
with
val returnedSN = data.getStringExtra("return_sn")
The problem is you are not passing the scanned bar code value (sn) to the intent(returnIntent) in your BarCodeActivity.
First make sure sn is String since you want to pass a StringExtra, therefore:
val sn = editTextBarcode.text.toString()
And then pass sn to your return intent:
returnIntent.putExtra("return_sn", sn)
Notice that in your code you are passing integer 1 instead of sn.
EDIT:
One more minor fix to your code, didn't notice it:
val returnIntent:Intent = getIntent()
Notice that it's getIntent() instead of Intent()

How can I set a request code under different conditions for my activity to bypass onActivityResult?

I have a program that allows me to store data(pictures and metadata with the taken picture) during the execution of a picture being taking with the android system camera activity... but I have code in place to make sure that the user enters data into a popup activity before the camera activity is displayed by using the OnActivityResult function(this way the user's photo has information that is stored as metadata in my firebase database). I was wondering If I can set a request code that wouldn't be equal to the REQUESTCODE2 so that under the condition that my back button is pressed(which will still result in the REQUESTCODE2 being returned for the com.example.myapplication.nameofphoto activity, which then will trigger takepic()) I can purposely make sure that the request code is faulty so that takepic() does not trigger and I don't store null data into my database.
for your information: nameofpersonvar , and nameofphotovar are both in a different class and is the information from the popup activity
private const val REQUESTCODE = 2
private const val REQUESTCODE2 = 3
fun take_pic(){
val takephotoIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE)
if (takephotoIntent.resolveActivity(this.packageManager) != null) {
startActivityForResult(takephotoIntent, REQUESTCODE)
} else {
Toast.makeText(this, "Unable To access Camera... ", Toast.LENGTH_LONG)
.show()
}
}
photoButton.setOnClickListener {
val action3 = Intent(this , com.example.myapplication.nameofphoto::class.java)
startActivityForResult(action3, REQUESTCODE2 )
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (REQUESTCODE == requestCode && resultCode == Activity.RESULT_OK) {
//Compressing the bitmap(image) into a byte[] to match the input of the .putbytes method
val userimage = data?.extras?.get("data") as Bitmap
val byteoutput = ByteArrayOutputStream()
userimage.compress(Bitmap.CompressFormat.JPEG,100 , byteoutput)
val data = byteoutput.toByteArray()
//ref to the firebase "bucket" database
val storageinfo = FirebaseStorage.getInstance().getReference("/Images" )
//extra data that shows who the images belong to (users)
val metadatastoreage = storageMetadata {
setCustomMetadata("Name of person" , nameofpersonvar)
setCustomMetadata("Name of photo" , nameofphotovar)}
storageinfo.putBytes(data, metadatastoreage)
}else if (requestCode ==REQUESTCODE2) {
take_pic()
}
else {
super.onActivityResult(requestCode, resultCode, data)
}
}
Then why don't you send some result code different from the back press method of the current activity opened and check if the result is successful then take pick otherwise do something.
send this as result code from back press method. RESULT_CANCELED
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (REQUESTCODE == requestCode && resultCode == Activity.RESULT_OK) {
//Compressing the bitmap(image) into a byte[] to match the input of the .putbytes method
val userimage = data?.extras?.get("data") as Bitmap
val byteoutput = ByteArrayOutputStream()
userimage.compress(Bitmap.CompressFormat.JPEG,100 , byteoutput)
val data = byteoutput.toByteArray()
//ref to the firebase "bucket" database
val storageinfo = FirebaseStorage.getInstance().getReference("/Images" )
//extra data that shows who the images belong to (users)
val metadatastoreage = storageMetadata {
setCustomMetadata("Name of person" , nameofpersonvar)
setCustomMetadata("Name of photo" , nameofphotovar)}
storageinfo.putBytes(data, metadatastoreage)
return
}
if (requestCode ==REQUESTCODE2 && resultcode == Activity.RESULT_OK) {
take_pic()
} else {
//back pressed do something.
//finish etc
}
}
Edit: You can override the onBackPressed() in the popup activity and send some data using intent to the parent activity. for ex.
Intent resultIntent = new Intent();
// TODO Add extras or a data URI to this intent as appropriate.
resultIntent.putExtra("user_pic_click", "some data");
setResult(Activity.RESULT_OK, resultIntent);
finish();

Update textview using Place Autocomplete's onActivityResult by following MVVM

I am trying to set up a dialogfragment with a textview, and once clicked, use an intent to launch the autocomplete activity. When a place is selected, the textview shows the address.
In onCreateDialog, I have the following code
binding.dialogLocationText.setOnClickListener {
val fields =
listOf(Place.Field.ID, Place.Field.ADDRESS, Place.Field.NAME)
val intent = Autocomplete.IntentBuilder(
AutocompleteActivityMode.OVERLAY, fields
).build(context!!)
startActivityForResult(intent, AUTOCOMPLETE_REQUEST_CODE)
}
Because the textview is accessed through databinding, I can't access it directly in onActivityResult, so created private lateinit var dialogLocationText: TextView and dialogLocationText = binding.dialogLocationText. Then set text in:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == AUTOCOMPLETE_REQUEST_CODE) {
if (resultCode == Activity.RESULT_OK) {
val place = Autocomplete.getPlaceFromIntent(data!!)
dialogLocationText.text = place.address
}
else if (resultCode == AutocompleteActivity.RESULT_ERROR) {
// TODO: Handle the error.
}
}
}
The above does not look like good practice especially with the redundant reference to the textview, and I'm wondering if I can construct differently to follow MVVM.
Thanks for the help.

Categories

Resources