Android Kotlin - Firestore image upload and display - android

At the moment as a sample I'm recreating instagram-like post, where user can post a description and image (along with username since I'm still not configuring authentication). So far as follows I can only post username and description. Along side of that I can load new image as a source into the imageview on the screen.
My data class is:
data class Post(val username: String,
val timestamp: Date,
val postTxt: String,
val numLikes: Int,
val numComments: Int,
val documentId: String)
I put data in a hashmap as follows:
val data = hashMapOf(
NUM_LIKES to 0,
NUM_COMMENTS to 0,
POST_TXT to addPostTxt.text.toString().trim(),
TIMESTAMP to FieldValue.serverTimestamp(),
USERNAME to addUsernameTxt.text.toString().trim()
)
and then pass it to upload to firestore:
FirebaseFirestore.getInstance().collection(POST_REF)
.add(data)
.addOnSuccessListener { finish() }
.addOnFailureListener {
Log.e("Exception", "Could not add post: $it")
toast("Could not add post")
}
And to get the data I created a function which takes snapshot of type QuerySnapshot like so:
// Clear all the posts before relaod
posts.clear()
snapshot.documents.forEach { document ->
val data = document.data
val name = data?.get(USERNAME) as String
val timestamp = data[TIMESTAMP] as Date
val postTxt = data[POST_TXT] as String
val numLikes = data[NUM_LIKES] as Long
val numComments = data[NUM_COMMENTS] as Long
val documentId = document.id
val newPost = Post(name, timestamp, postTxt, numLikes.toInt(), numComments.toInt(), documentId)
posts.add(newPost)
}
postsAdapter.notifyDataSetChanged()
So far uploading text, current time of upload works fine for both upload and reading it from the database; however my question here is:
- What is the best way (following current codebase) to upload image to firestore and storage and display it accordingly in the main window (preferably using Glide or picasso)?

It's generally not a good idea to store binary data like images in Firestore. You could very easily exceed the limit for how much data you can store in a document, which is 1MB. Large documents also take more time to load on the client, which is even worse if you don't even need to use the image at the time of the read.
Instead, consider uploading the image to Cloud Storage and saving a path to that location in a field in your document. Then, when it's time to display the image, load it from there.

Related

Firebase Storage. Is it possible to parallelize queries when upload an image?

I want to upload user avatar into Firebase Storage. When I do this, I remove all previous avatars.
Also I need to keep user updated with my local storage and Realtime Database.
Is it possible to do it in faster way?
override suspend fun uploadImage(uri: Uri) = withContext(dispatchers.io) {
val user = remoteRepository.getUser() // I do several parallel queries when save edited user info
clearPreviousImages(user.id) // so, I guess, I need remote user below
val fileName = "${user.id}/${uri.lastPathSegment}"
val uploadReference = referencesRepository.getImagesStorageReference().child(fileName)
uploadReference.putFile(uri).await()
val link = uploadReference.downloadUrl.await().toString()
val updatedUser = user.copy(avatar = link) // save image url in Realtime Database
remoteRepository.updateUser(updatedUser) // and return updated user
}
private suspend fun clearPreviousImages(userId: String) {
referencesRepository.getImagesStorageReference().child(userId).listAll().await()
.items
.forEach { it.delete().await() }
}
If I try to put clearPreviousImages in async block and remove await() inside forEach, I get the following error
Task is not yet complete
Can I speed up the upload?

Get an Object from Firebase Firestore in Kotlin

I alredy searched here in the forum but i didn't find nothing like that. I want to get an Object from Firebase Firestore, but I can't manipulate the object that I am receiving. How can I separate the imageLink from the imageTitle?
The database structure is:
The code I am using is:
firebaseFirestore.collection("image").get()
.addOnSuccessListener { documentSnapshot ->
val imageData = documentSnapshot.documents[0].data?.getValue("imageData")
}
But, when I do that, I receive something like that:
How can I get the imageLink from the imageTitle separately?
You can try casting imageData as a Map as shown below:
db.collection("images").limit(1).get().addOnSuccessListener { querySnapshot ->
val imageData =
querySnapshot.documents[0].data!!.getValue("imageData")?.let { it as Map<*, *> }
val imageLink = imageData?.get("imageLink")
val imageTitle = imageData?.get("imageTitle")
Log.d("MainActivity", "imageLink: $imageLink")
Log.d("MainActivity", "imageTitle: $imageTitle")
}
Checkout the documentation for let for more information.
You are calling get() on a CollectionReference that will fetch all documents from the collection (if you add any). If you only want to fetch 1st document from the collection, you can add limit(1) that'll save any additional charges.

How to store multiple images in one document(Firestore) Android

User should be able to upload multiple images in one post.
I tried to add all urls to a ArrayList and then to store them to firestore.
All I get are errors.
"imageList" returns always null
Here is my code:
fun uploadPost(images:ArrayList<Uri>, imageCount: Int, description:String){
images.forEach { image->
val imageRef = storageReferenence.child(System.currentTimeMillis().toString()
+"."+ image.lastPathSegment)
val uploadTask = imageRef.putFile((image))
uploadTask.addOnSuccessListener {
val downloadUrl = imageRef.downloadUrl
downloadUrl.addOnSuccessListener {uri->
Log.d("IMAGES", uri.toString())
imageList!!.add(uri.toString())
count++
}
}
}
//firebaseRepository.firestoreDB.collection("post").document(firebaseRepository.userid.toString()).update(post)
}
You can see that the Log has the url but when I try to add it to the imageList
it fails to do so.
THIS CAUSES THE ERROR: imageList!!.add(uri.toString())
ERROR MSG: AddViewModel$uploadPost$$inlined$forEach$lambda$1$1.onSuccess
I don`t really know what is better to store the images as an array or to store each image like this: image1:url... , image2: url..
I need them to be part of the same document.

Reading custom metadata from Firebase storage using Kotlin (Android)

I'm newish to Kotlin, and new to StackOverflow. This is my first question.
I'm trying to retrieve and parse metadata stored in Google Firebase storage, using Kotlin. I have successfully retrieved the file and displayed it, and am able to get a reference to the metadata using val valName = referenceName.metadata. At this point I would like to retrieve the custom metadata that is stored in that val and parse it to a string. Printing the contents of the metadata using toString() returns com.google.android.gms.tasks.taskId to the console.
I've visited the docs and used them for a lot of my project so far, they are located at:
https://firebase.google.com/docs/storage/android/file-metadata, but am stuck on what to do next.
Thanks for your help!
My code:
// create an instance of the firebase storage
val storage = FirebaseStorage.getInstance()
// create a reference to storage
val storageRef = storage.reference
// create a reference to the featured content image
val filmRef = storageRef.child("featured/film.jpg")
// place the image metadata in a val - this appears to be working
val filmMeta = filmRef.metadata
// parse metadata to a string
// ****** what to do next? ********
val filmId = filmMeta.customMetadata("id") // <--- this does not work
You should add your customMetedata in storage ref
val metadata = storageMetadata {
setCustomMetadata("id", "filmId")
}
filmRef.updateMetadata(metadata).addOnSuccessListener {
// Updated metadata is in storageMetadata
val filmId = it.getCustomMetadata("id")
}.addOnFailureListener {
}
Then you can use like this:
filmRef.metadata.addOnSuccessListener {
val filmId = it.getCustomMetadata("id")
}.addOnFailureListener {
}

Simply fetching data from Cloud Firestore but Firestore + ViewModel + LiveData

I'm building an app with use of Cloud Firestore. I've already implemented RecyclerView with FirestoreRecyclerAdapter and set to it data from collection. Before I process further I need to understand something:
1. Inversely to local database (sqlite3, without LiveData) Firestore does send a request to a device when any change in the database occurs, am I right?
2. Assuming I was right in the "question" above. When I fetch data to the adapter like this:
In my activity onCreate:
val questions = FirebaseFirestore.getInstance().collection("DatabaseStructure")
val query = questions.orderBy("timestamp", Query.Direction.DESCENDING)
val options = FirestoreRecyclerOptions.Builder<DatabaseStructure>()
.setQuery(query, DatabaseStructure::class.java)
.build()
firestoreAdapter = FirestoreAdapter(options)
recyclerView.setHasFixedSize(true)
recyclerView.layoutManager = LinearLayoutManager(context)
recyclerView.adapter = firestoreAdapter
My data class:
data class DatabaseStructure(
val question: String = "",
val description: String? = "",
val image: String? = "",
val leftAnswers: Int = 0,
val rightAnswers: Int = 0,
val timestamp: com.google.firebase.Timestamp = com.google.firebase.Timestamp(Date()),
val userName: String? = "Anonymous")
Every time activity is recreated, e.g users rotates device, reopens activity, data is fetched from the Cloud Firestore one more time, even if no changes appeared, am I right? To avoid this should I use ViewModel then?
Assuming question above was also true and I've implemented ViewModel to my application, that stores data from Cloud Firestore. Now if any change in the database appears, Firestore sends a request to a device to fetch data. Is there still any sense to observe data using LiveData?
To sum up, what is the best model to implement Firestore without unnecessary fetching data from database, to make it in the most efficient way and the least power consuming?
Thanks in advance!

Categories

Resources