There is no error and no problem with clicking the button, but the voice file does not upload to the Firestore.
Audio selection is possible, but the message "Successfully Uploaded:" is not output, and the voice file is not uploaded to the Fire Store.
I think the cancellation line in onActivityResult and startActivityForResult is the problem.
How do I get rid of the cancellation line? And is there any other reason why the file doesn't go up on the fire store?
++I modified the Firestore rules.
MainActivity.kt
import ...
class MainActivity : AppCompatActivity() {
val AUDIO : Int = 0
lateinit var uri: Uri
lateinit var mStorage: StorageReference
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val musicBtn = findViewById<View>(R.id.musicBtn) as Button
mStorage = FirebaseStorage.getInstance().getReference("Uploads")
musicBtn.setOnClickListener(View.OnClickListener {
view -> val intent = Intent()
intent.setType("audio/*")
intent.setAction(Intent.ACTION_GET_CONTENT)
startActivityForResult(Intent.createChooser(intent, "Select MP3"), AUDIO)
})
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val uriTxt = findViewById<View>(R.id.uriTxt)as TextView
if (requestCode== RESULT_OK){
if (requestCode == AUDIO) {
uri = data!!.data!!
uriTxt.text = uri.toString()
upload()
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun upload() {
var mReference = mStorage.child(uri.lastPathSegment!!)
try {
mReference.putFile(uri).addOnSuccessListener {
taskSnapshot: UploadTask.TaskSnapshot? -> var url = taskSnapshot!!
val dwnTxt = findViewById<View>(R.id.dwnTxt) as TextView
dwnTxt.text = url.toString()
Toast.makeText(this, "Successfully Uploaded :)", Toast.LENGTH_LONG).show()
}
}catch (e: Exception) {
Toast.makeText(this, e.toString(), Toast.LENGTH_LONG).show()
}
}
}
enter image description here
A cancellation line is created as shown in the image. How do I solve it?
startActivityResult and onActivityResult have been deprecated. You need to migrate to registerForActivityResult and ActivityResultContracts methods.
A few of the examples are given below:
https://developer.android.com/codelabs/android-app-permissions
Predefined Contracts - https://developer.android.com/reference/kotlin/androidx/activity/result/contract/ActivityResultContracts
https://developer.android.com/reference/kotlin/androidx/activity/result/contract/ActivityResultContract
https://developer.android.com/training/basics/intents/result
Another easier way you can do this is by ignoring the error and using #Deprecated("Deprecated in Java") annotation but it is recommended that you migrate.
The cancellation line means those functions are deprecated, but in this case, that is not your problem because they still behave as they used to.
This might be your issue. You have a typo here:
if (requestCode== RESULT_OK){
if (requestCode == AUDIO) {
You should be checking if the resultCode is RESULT_OK, not the requestCode. So currently your if statement will not run unless your AUDIO request code happens to be the same as Activity.RESULT_OK.
If that still doesn't solve it, you should add an OnFailureListener to find out what's happening. You can put a debug breakpoint in your OnFailureListener so you can inspect the error object for what the problem is.
Related
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).
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)
}
}
}
I'm using Kotlin. I have beent trying to choose a folder to create a file in it and export Data from my Database into said file. But now it showed me, that startActivityForResult is deprecated
I have read the Question:
OnActivityResult method is deprecated, what is the alternative?
, but sadly, I couldn't see how you would implement that in a Optionsmenu, to open a Action_Create_Document for a Data-Export.
As a non-native Speaker, i also had quite a bit of trouble to understand the basic training: https://developer.android.com/training/basics/intents/result .
So my question is: How do you implement a call to choose a location and create a txt-file and the take said filelocation to fill it with text, with the registerForActivityResult without moving to another Activity/with staying on the Activity you are.
override fun onOptionsItemSelected(item: MenuItem): Boolean {
return when (item.itemId) {
R.id.Export -> {
val intent = Intent(Intent.ACTION_CREATE_DOCUMENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.type = "text/plain"
intent.putExtra(Intent.EXTRA_TITLE, "Spells.txt")
startActivityForResult(intent, 112)
return true
}
else -> super.onOptionsItemSelected(item)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, resultData: Intent?) {
super.onActivityResult(requestCode, resultCode, resultData)
if (requestCode == 112 && resultCode == RESULT_OK) {
Toast.makeText(this, "Created", Toast.LENGTH_LONG).show()
val path = resultData?.data?.path
val myfile: File
if (path != null) {
myfile = File(path)
....
doing stuff()
}
}
}
I have found the Problem in my thinking. I just had to use the Intent I created before to launch the resultLauncher, that was shown on the previous question, instead of the Activity-changing Intent.
Also, I found, that the Value val resultLauncher, that was shown, had to be declared inside the class but outside the other functions, which was the part, where I got confused. Guess I got routine-blinded and should take more Breaks
Here some code about how you can use the new registerForActivityResult approach, in this case It replaces the Intent.ACTION_CREATE_DOCUMENT intent.
class YourActivity {
// This is the launcher ...
// CreateDocument() -> Intent.ACTION_CREATE_DOCUMENT
private val getCreateFileUriContent = registerForActivityResult(ActivityResultContracts.CreateDocument()) { uri: Uri? ->
// Handle the returned Uri
uri?.let { onCreateFileSelected(it) }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//....
someButton.setOnClickListener {
//launch for results
getCreateFileUriContent.launch("test.json")
}
}
}
In general, my task is to get base64 from chosen file. In order to open File Browser, I call following function:
private fun showFileBrowser() {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.setType("*/*")
startActivityForResult(intent, FILE_CHOOSE_REQUEST_CODE)
}
It is successfully opened. When some file is chosen, onActivityResult is called. Here it is:
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == FILE_CHOOSE_REQUEST_CODE) {
// not that data.data is Uri
if(data != null && data.data != null) {
val encodedBase64 = getBase64FromPath(data.data.path)
print(encodedBase64)
}
}
}
Here is how I convert File to base64:
private fun getBase64FromPath(path: String): String {
try {
val bytes = File(path).readBytes()
return Base64.encodeToString(bytes, Base64.DEFAULT)
} catch (error: IOException) {
error.printStackTrace() // This exception always occurs
}
}
Seems like I do everything right, but I get FileNotFoundException. I don't know what is the reason for this. I didn't add any permission, because I don't to write anything to scared, I just want a user to choose a file, I will convert it to base64 and send it to the server. So, what is the problem this my code?
my task is to get base64 from chosen file
Your code has little to do with files. ACTION_GET_CONTENT is not limited to files on the device, let alone files on the filesystem that you can access.
When some file is chosen, onActivityResult is called
You get a Uri via onActivityResult(). A Uri is not a file, and getPath() on a Uri only has meaning if the scheme of that Uri is file. Most Uri values will have a scheme of content.
Replace your function with:
private fun getBase64ForUriAndPossiblyCrash(uri: Uri): String {
try {
val bytes = contentResolver.openInputStream(uri).readBytes()
return Base64.encodeToString(bytes, Base64.DEFAULT)
} catch (error: IOException) {
error.printStackTrace() // This exception always occurs
}
The AndPossiblyCrash portion of the function name is because you are going to run out of memory if the content is too large. Also note that you are doing this work on the main application thread, so your UI will be frozen while you are reading this in.
I'm developing an android app using kotlin to upload pdf files to firebase storage
I followed a tutorial and when it comes to running I can only brows my files but I cannot choose any file to upload here is a screenshot screenshot
here is the code from mainActivity.kt
under class MainActivity : AppCompatActivity()
val pdf: Int=0
lateinit var uri:Uri
lateinit var mStorage: StorageReference
under onCreate()
val pdfBtn=findViewById<Button>(R.id.pdfBtn)
mStorage=FirebaseStorage.getInstance().getReference("Uploads")
pdfBtn.setOnClickListener(View.OnClickListener {
view: View-> val intent = Intent()
intent.setType("pdf/*")
intent.setAction(Intent.ACTION_GET_CONTENT)
startActivityForResult(Intent.createChooser(intent,"Select PDF"),pdf)
})
The rest of the method related to the upload
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
val uriTxt=findViewById<TextView>(R.id.uriTxt)
if(resultCode== Activity.RESULT_OK){
if(requestCode==pdf){
uri=data!!.data
uriTxt.text=uri.toString()
upload()
}
}
super.onActivityResult(requestCode, resultCode, data)
}
private fun upload(){
var mRefrence= mStorage.child(uri.lastPathSegment)
try{
mRefrence.putFile(uri).addOnSuccessListener {
taskSnapshot: UploadTask.TaskSnapshot? -> var url =taskSnapshot!!.downloadUrl
val dwnTxt=findViewById<TextView>(R.id.dwnTxt)
dwnTxt.text=url.toString()
Toast.makeText(this,"Successfully uploaded",Toast.LENGTH_LONG).show()
}
}
catch (e: Exception){
Toast.makeText(this,e.toString(),Toast.LENGTH_LONG).show()
}
}
Can some one corrects my code please and tell me what's wrong with it.
thank you very much
in case if anyone was wondering the problem was in the type
intent.setType("pdf/*")
the correction is
intent.type="application/pdf"
This utility class I create it to upload multiple images to firebase storage using kotlin with help of coroutine. if you have any enhancement please tell me.
you need to add these dependencies firstly.
implementation 'com.google.firebase:firebase-storage-ktx:19.1.1'
//Firebase adds support to Coroutines through the kotlinx-coroutines-play-serviceslibrary
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.3.1"
for more info check link, github link
private suspend fun uploadPhoto(file: File): Uri {
val storageRef = Firebase.storage.reference
val fileName = UUID.randomUUID().toString()
val fileUri = Uri.fromFile(file)
return storageRef.child(fileName)
.putFile(fileUri)
.await()
.storage
.downloadUrl
.await()
.
}
}