How i can update document in Firestore kotlin? - kotlin

This image show my firestore documents which that auto generated.Which i want to do
//I want to update or merge Firestore document without adding new document. So i want to image url to firestore existing document field with merge method. But it does not overwrite, it is going to create a new document with auto-generated ID. How i can fix it?
val uuid = UUID.randomUUID()
val imageName = "$uuid.jpg"
val reference = storage.reference
val imagereference = reference.child("images").child(imageName)
if (selectedPicture != null) {
imagereference.putFile(selectedPicture!!).addOnSuccessListener {
val uid = FirebaseAuth.getInstance().currentUser!!.uid
val uploadPictureReference = storage.reference.child("images").child(imageName)
uploadPictureReference.downloadUrl.addOnSuccessListener {
val downloadUrl = it.toString()
if (uid != null) {
val postMap = hashMapOf<String, Any>()
postMap.put("downloadUs", downloadUrl)
postMap.put("Email", auth.currentUser!!.email!!)
postMap.put("date", Timestamp.now())
val new = firestore.collection("ProfPic").document(uid)
new.set(postMap, SetOptions.merge()).addOnSuccessListener { }.
addOnFailureListener { Toast.makeText(this#MainActivity, it.localizedMessage, Toast
.LENGTH_LONG).show() }

Related

How can I grab specific text from this html file? Using Jsoup functionality in Kotlin

Code:
import org.jsoup.Jsoup
fun main(){
val url = "https://www.google.com/maps/place/?q=place_id:"
//val placeID:String = "ChIJRVY_etDX3IARGYLVpoq7f68";
val placeID:String = "ChIJ1zoUGxYE9YgRL399jlxWp6Q";
val doc = Jsoup.connect(url+placeID).get()
val text = doc.html()
//println(text)
when (text)
{
is String -> println("yay");
}
//val pID: Collection<String>? = placeID as? String
//val here: Pair<Int, String>? = text.findLastAnyOf(placeID,0,false);
var tempLine:String = ""
tempLine = text.substringAfterLast(placeID)
val newLength = tempLine.length
var tempLine2 = tempLine.substringBefore("window.APP_FLAGS")
println(tempLine2)
}
Output.jpg
The text I want to specifically grab and return
I am working on a personal side project and I have hit a wall, where I cannot grab the busy time information from Google's page, this location is Trader Joes.

kotlin how to parse text into a list of data class

I have a kotlin data class described as:
data class Credentials(val p: String, val id: String, val key: String, val pass: String, val out: String)
I am trying to read from 2 text files located in a directory, and put them into this data class.
How the data looks:
config file
[user1]
out = specialk
id = mike
[user2]
out = specialk
id = mike
[user3]
out = specialk
id = mike
credentials file
[user1]
key = qwer1
pass = 3452
[user2]
key = qwer3
pass = 345232
[user3]
key = qwer5
pass = 3452gfd
Setting it up:
val homepath = System.getProperty("user.home")
val config = "$homepath/foobar"
val cred= "$homepath/credbar"
val configStream: InputStream = File(config).inputStream()
val credStream: InputStream = File(cred).inputStream()
This next part is something I am unsure of. What I think is that I should be reading each stream and putting it into a list of data class grouped by the user. However, I'm not sure how that should be accomplished.
configStream.bufferedReader().forEachLine {
// put to data class here.
}
I can't write single comment cause i am new at Stackowerflow. Sorry about that. If this file holds your app backup you can take backup as JSON file. This is much easier than this.
Firstly you need to add Gson dependency in your project
implementation 'com.google.code.gson:gson:2.8.6'
Secondly you need to two parser object that is for make parse to data class to JSON and JSON to data class.
You need to declare your export and import type as below
val exportType = object : TypeToken<List<Credentials>>() {}.type
And after that for convert your data to JSON String you can use this
private fun List<Credentials>.toJson() = Gson().toJson(this, exportType)
this code returns String.
and if you want to convert JSON to String you can use this code
private fun fromJson(str: String): List<Credentials> {
return try {
Gson().fromJson(str, exportType)
} catch (e: Exception) {
Log.e("From Json Exception", "$e")
emptyList()
}
}
this code returns list of your data class.
I hope this can help you. I did not ask is this you want to do because my Stacowerflow account is new.
Basically I would try to convert your streams into two strings
val configInputFileString = convertStreamToString(configStream)
val credentialsInputFileString = convertStreamToString(credStream)
You can easily convert a stream by using this function:
fun convertStreamToString(stream: InputStream?): String? {
val reader = BufferedReader(InputStreamReader(stream))
val sb = StringBuilder()
var line: String? = null
while (reader.readLine().also { line = it } != null) {
sb.append(line).append("\n")
}
reader.close()
return sb.toString()
}
Once you have the two streams, I would define to different data classes to get the elements of the streams:
data class UserConfig(val placeholder: String, val out: String, val id: String)
data class UserCredentials(val placeholder: String, val key: String, val pass: String)
for each stream you need to get each row, splitting by new line character \n and clearing unuseful parts:
private fun getUserConfigs(elements: List<String>): ArrayList<UserConfig> {
val configs = arrayListOf<UserConfig>()
for (element in elements) {
val splittedConfig = element
.replace("out = ", "")
.replace("id = ", "")
.split("\n")
val config = UserConfig(splittedConfig[0], splittedConfig[1], splittedConfig[2])
configs.add(config)
}
return configs
}
private fun getUserCredentials(elements: List<String>): ArrayList<UserCredentials> {
val credentials = arrayListOf<UserCredentials>()
for (element in elements) {
val splittedCredentials = element
.replace("key = ", "")
.replace("pass = ", "")
.split("\n")
val config = UserCredentials(splittedCredentials[0], splittedCredentials[1], splittedCredentials[2])
credentials.add(config)
}
return credentials
}
Now you can map userConfig and credentialConfigs in a usersMap
val userConfigs = getUserConfigs(configInputFileString!!.split("\n\n"))
val credentialConfigs = getUserCredentials(credentialsInputFileString!!.split("\n\n"))
val usersMap = userConfigs.map { userConfig ->
userConfig to credentialConfigs.find { it.placeholder == userConfig.placeholder }
}
Finally you can transform the usersMap to match your data class by doing:
val credentials = usersMap.map { Credentials(it.first.placeholder, it.first.id, it.second!!.key, it.second!!.pass, it.first.out) }
As long as your files seem to be of Windows INI format you can use third-party library Ini4J to parse them.
Parse both files and merge two Inis into list of Credentials
val configs = Ini(File(config))
val credentials = Ini(File(cred))
val result: List<Credentials> = configs.keySet().map { user ->
Credentials(p = user, // or whatever 'p' is
id = configs.get(user, "id")!!,
key = credentials.get(user, "key")!!,
pass = credentials.get(user, "pass")!!,
out = configs.get(user, "out")!!)
}

Why is the variable dowloadUrl returning null in the saveProfile method?

The following code shows the firebase storage upload task which I am using to get the variable downloadUrl, downloadUrl isn't null as it prints in the log but when I call the function saveProfile it returns null, why?
var downloadUrl: String? = null
fun upload(bytes: ByteArray) {
val storageReference = FirebaseStorage.getInstance().reference
.child(
"images/users/${FirebaseAuth.getInstance().currentUser!!.uid}/profile_image"
)
val metadata = StorageMetadata.Builder()
.setContentType("image/jpg")
.setContentLanguage("en")
.build()
storageReference.putBytes(bytes, metadata).addOnSuccessListener {
model.listener!!.progressBarGone()
model.listener!!.toast("Uploaded Successfully")
val urlTask = it.storage.downloadUrl
while (!urlTask.isSuccessful);
this.downloadUrl = urlTask.result.toString()
Log.d("Upload", "DownloadUrl $downloadUrl")
And this is the saveProfile function.
fun saveProfile() {
val user = User()
if (model.name.isNullOrEmpty() || model.phone.isNullOrEmpty()) {
model.listener!!.toast("Fields cannot be empty")
return
}
if (downloadUrl.isNullorEmpty()) {
log.d(TAG, "URL empty")
}
user.name = model.name
user.phone = model.phone
user.profile_image = downloadUrl
When you call
this.downloadUrl = urlTask.result.toString()
You assign downloadUrl to the object that is bound to the lambda you pass to addOnSuccessListener, not the global downloadUrl. For quick fix, I'd suggest you renaming global downloadUrl to some other name that wouldn't be shadowed.

Recycler View recycle issue

I have a recyclerView. When I do the pull to refresh, if the new data is just one list item, then the recycler view loads the item perfectly. But if the updated data contains 2 or more, then I think the view is not recycled properly. In the actionContainer, there should only one item to be added for each of the updated list item. But during pull to refresh, ONLY WHEN there are 2 or more list items to be updated, the actionContainer shows 2 data where it should be only one. Can someone help me to fix this?
override fun onBindViewHolder(holder: HistoryListAdapter.ViewHolder?, position: Int) {
info("onBindViewHolder =>"+listAssets.size)
info("onBindViewHolder itemCount =>"+itemCount)
info("onBindViewHolder position =>"+position)
val notesButton = holder?.notesButton
val notesView = holder?.notesTextView
val dateTime = listAssets[position].date
val location = listAssets[position].location
val sessionId = listAssets[position].id
holder?.sessionID = sessionId
holder?.portraitImageView?.setImageDrawable(listAssets[position].image)
holder?.titleTextView?.text = DateTimeFormatter.getFormattedDate(context, dateTime)
val timeString = DateTimeFormatter.getFormattedTime(context, dateTime)
if (location.length != 0) {
holder?.subtitleTextView?.text = "$timeString # $location"
} else {
holder?.subtitleTextView?.text = "$timeString"
}
val data = listAssets[position].data
for (actionData in data) {
val inflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as LayoutInflater
val parent = inflater.inflate(R.layout.history_card_action, null)
val icon = parent?.findViewById(R.id.historyActionIcon) as ImageView
val title = parent?.findViewById(R.id.historyActionTitle) as TextView
val subtitle = parent?.findViewById(R.id.historyActionSubtitle) as TextView
var iconDrawable: Drawable? = null
when(actionData.type) {
ActionType.HEART -> {
iconDrawable = ContextCompat.getDrawable(context, R.drawable.heart)
}
ActionType.LUNGS -> {
iconDrawable = ContextCompat.getDrawable(context, R.drawable.lungs)
}
ActionType.TEMPERATURE -> {
iconDrawable = ContextCompat.getDrawable(context, R.drawable.temperature)
}
}
icon.setImageDrawable(iconDrawable)
val titleString = actionData.title
titleString?.let {
title.text = titleString
}
val subtitleString = actionData.subtitle
subtitleString?.let {
subtitle.text = subtitleString
}
holder?.actionContainer?.addView(parent)
}
val notes = listAssets[position].notes
notesView?.text = notes
if (notes.length == 0) {
notesButton?.layoutParams?.width = 0
} else {
notesButton?.layoutParams?.width = toggleButtonWidth
}
if (expandedNotes.contains(sessionId)) {
notesView?.expandWithoutAnimation()
} else {
notesView?.collapseWithoutAnimation()
}
notesButton?.onClick {
notesView?.toggleExpansion()
}
}
data class ListAssets(val id: String,
val date: Date,
val location: String,
val notes: String,
val image: Drawable,
val data: ArrayList<ListData>)
data class ListData(val type: ActionType,
val title: String?,
val subtitle: String?)
override fun onViewRecycled(holder: HistoryListAdapter.ViewHolder?) {
super.onViewRecycled(holder)
if (holder != null) {
holder.actionContainer.removeAllViewsInLayout()
holder.actionContainer.removeAllViews()
val notesTextView = holder.notesTextView
if (notesTextView != null) {
if (notesTextView.expandedState) {
val sessionID = holder.sessionID
sessionID?.let {
val sessionSearch = expandedNotes.firstOrNull {
it.contentEquals(sessionID)
}
if (sessionSearch == null) {
expandedNotes.add(sessionID)
}
}
} else {
val sessionID = holder.sessionID
sessionID?.let {
val sessionSearch = expandedNotes.firstOrNull {
it.contentEquals(sessionID)
}
if (sessionSearch != null) {
expandedNotes.remove(sessionSearch)
}
}
}
}
}
}
First, you should probably not override onViewRecycled() unless you have to perform some very particular resources cleanup.
The place where you want to setup your views before display is onBindViewHolder().
Second, you don't need not add or remove views dynamically in a RecyclerView item, it's simpler and more efficient to only switch the visibility of the view between VISIBLE and GONE. In cases where this is not enough because views are too different, you should declare different view types, which will create separate ViewHolders for each view type.
You should not remove or add any view while overriding onBindViewHoder() method of RecyclerView Adapter because next time when a recycled layout is used, the removed views will not be found. Instead of this you can use show/hide on a view.
If you add any view to the layout dynamically, later on when this layout is recycled, it also contains the extra view which you have added before.
Similarly, if you remove any view from the layout dynamically, later on when this layout is recycled, it does not contain the view which you have removed earlier.
I have implemented a RecyclerView and Retrofit,it has the SwipeView layout (Pull to Refresh).Here is the link to the repisitory.
https://github.com/frankodoom/Retrofit-RecyclerVew

Accessing an attribute of a map Entry (Kotlin)

I have the following class:
class Entry1(var type:String, var kind:String, var index:Int)
And a map:
var map1 = mutableMapOf<String, Entry>()
How would I access the attributes of Entry1 of a given map entry?
Example: Say I have:
map1["ex1"] = Entry("ex2","ex3",4)
Now I want to get the indexfrom the Entry I've added.
How can that be done?
val entry = map1["ex1"]
if (entry != null) {
val index = entry.index;
...
}
or
val entry = map1["ex1"]
val index = entry?.let{
entry.index
} ?: -1 //optional