Android Room database - when all rows are loaded - android

I am trying to use Android Room described there: https://codelabs.developers.google.com/codelabs/android-room-with-a-view-kotlin/#13
source codes: https://github.com/googlecodelabs/android-room-with-a-view/tree/kotlin/app/src/main/java/com/example/android/roomwordssample
I would like to choose one record when all records will be loaded.
I tried in the end of "onCreate(savedInstanceState: Bundle?)" function add this code:
var rnd : Random = Random(System.currentTimeMillis())
var words : List<Word>? = wordViewModel.allWords.value
// now wordViewModel.allWords.value is NULL
if (words != null) {
var textViewRandomWord: TextView = findViewById<TextView>(R.id.textView)
textViewRandomWord.text = words.get(rnd.nextInt(words.size)).word
}
But in this time is variable wordViewModel.allWords.value is still null.
Can you help me, where to add this code (or any similar code) to get random record from all saved records and show it when activity is created?
Thank you.

wordViewModel.allWords is [LiveData] : https://developer.android.com/topic/libraries/architecture/livedata object, so its good to get value by observer
wordViewModel.allWords.observe(this, Observer { words ->
// Do your implementation
})

Related

Retrieving a shared preferences item from another activity. Android Studio, Kotlin, API 29

I am a complete Novice writing my first app.
I created an Activity called SettingsInput.kt and it works fine saving settings as sharedPreferences, displaying them on press of buttons.
Now I want to use one of the settings as part of a calculation in a function to cull old data from a couple of tables in the database The function is in the databaseHandler class.
I get an error "Unresolved reference: getSharedPreferences" None of the methods I have tried have helped so far. Can anyone help, bearing in mind that the target API level is API 29?
var sharedPrefFile = "greenbandbasicpreference"
val sharedPreferences:SharedPreferences = getSharedPreferences(sharedPrefFile,Context.MODE_PRIVATE)//doesnt recognise getSharedPreferences
fun cullData(){
var noow = ZonedDateTime.now(Clock.systemUTC())
var noowSecs:Long = noow.toEpochSecond()
var noowMins:Long = (noowSecs)/60
//var bolusLifeMins:Long = 220// this has to come from a database or store of "preferences"
//var bolusLifeMins:Long = sharedPreferences.getLong("insLife_key",0)
val bolusLifeMins = getSharedPreferences("greenbandbasicpreference",Context.MODE_PRIVATE)
var minTimeMins = noowMins - bolusLifeMins - 2*(24*60)
val db = this.writableDatabase
db.delete(
"BolusTable",
"btime <"+ minTimeMins ,
null
)
db.delete(//this is a part of the function for cull CarbsTable
"CarbsTable",
"carbTime <"+ minTimeMins ,
null
)
db.close()
}
The method getSharedPreferences is a method from Context class, so you will need a context to request it, like:
context.getSharedPreferences(sharedPrefFile,Context.MODE_PRIVATE)
https://developer.android.com/reference/android/content/Context#getSharedPreferences(java.lang.String,%20int)

Firestore query StartAt(DocumentReference) not giving proper result

In my firestore database,there are 12+ documents.I am getting the first 3 documents correctly by calling the below function on button click. But on the secondclick, though the documentReference is passed correctly, its not retrieving any data.The querySnapshot size is coming 0. What could be the problem.
Given below is the declaration
private val db: FirebaseFirestore = FirebaseFirestore.getInstance()
private val colRef: CollectionReference = db.collection("Notebook")
private var lastResult: DocumentReference? = null
private lateinit var query: Query
and below is the onButtonClick code :
private fun loadNoteNew() {
#Suppress("SENSELESS_COMPARISON", "LiftReturnOrAssignment")
if (lastResult == null) {
query = colRef.orderBy("priority")
.limit(3)
} else {
Log.i(TAG, "Start ${lastResult!!.id}")
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
}
Log.i(TAG, "before get")
query.get()
.addOnSuccessListener { querySnapshot ->
var data = ""
Log.i(TAG, "querySnapshot Size : ${querySnapshot.size()}")
if (lastResult != null) {
Log.i(TAG, "querySnapshot ID : ${lastResult!!.id}")
}
for (snapshot in querySnapshot) {
val note = snapshot.toObject(Note::class.java)
note.id = snapshot.id
val title = note.title
val desc = note.description
val priority = note.priority
data += "${note.id} \nTitle =$title \nDescription = $desc\nPriority : $priority\n\n"
}
if (querySnapshot.size() > 0) {
data += "---------------\n\n"
textView_loadData.append(data)
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
Log.i(TAG, lastResult!!.id)
}
}
}
Given below is the logcat for first click
I/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 3
I/FireStoreExample: P9hIw4Ai7w4IHP6H3ew3
and given below is the logcat of second click
I/FireStoreExample: Start P9hIw4Ai7w4IHP6H3ew3
I/FireStoreExample: before get
I/FireStoreExample: querySnapshot Size : 0
I/FireStoreExample: querySnapshot ID : P9hIw4Ai7w4IHP6H3ew3
Please help me find out,where i am getting it wrong.
Thanks
The second query result is empty because of a misunderstanding on the semantics of query pagination using startAt and startAfter methods.
Let's say the Notebook collection contains N documents. When you make the first query you're asking for the first 3 documents ordered by the priority field so the query is returning documents 1..3. Then upon the second click you're expecting the query to return the next 3 results so indeed you're expecting documents 4..6. The keypoint here is that both startAt and startAfter paginate based on the value of the ordered field rather than with the last document retrieved. Overall the semantics of startAt and startAfter are roughly as follows.
orderby(X).startAt(Y) => Return documents whose X field is greater than or equal Y
orderby(X).startAfter(Y) => Return documents whose X field is strictly greater than Y
With that in mind, let's examine what the code is actually doing when you make the second query:
// At the end of the first query...
lastResult = querySnapshot.documents[querySnapshot.size() - 1].reference
// Second query
query = colRef.orderBy("priority")
.startAfter(lastResult)
.limit(3)
In the code above you're asking for the documents whose "priority" field is greater than document reference "P9hIw4Ai7w4IHP6H3ew3" and indeed there are no documents greater than that, therefore the result set is empty. Here is api reference for both.
There is yet another thing to note. Because these methods filter upon the fields value the position of the cursor could be ambiguous. For instance, if you have 4 documents with priority 3 and already retrieved the leading three if you set startAfter(3) you'll be missing a document. Similarly, if startAt(3) were to be made you'll get back the same three documents. This is also pointed out in the documentation. All in all you have a couple of options to make this work as intended:
Add another orderby in another field so that documents are uniquely identified by the combination so to prevent any cursor ambiguity and be able to use startAfter with guarantees. Next snippet build upon the doc samples and your code.
// first query
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.limit(3)
// Save last document
lastResult = querySnapshot.documents[querySnapshot.size() - 1]
// Second and next queries
query = colRef.orderBy("priority")
.orderBy("AnotherField")
.startAfter(lastResult)
.limit(3)
Lastly remember that it might be simpler to just query all the documents if they're not many and delay optimizations until they become a performance issue.

add/overwrite field of type array in Firestore

I want to add a field of type array inside a collection.
if the field doesn't exist create it. if it exists overwrite it with the new array value.
the field should be called macAddress and it's of type array of String
I have tried the following:
val macInput = setting_mac_text.text.toString()
val macArray = macInput.split(",")
val macList = Arrays.asList(macArray)
val data =
hashMapOf(Pair(FirebaseConstants.USER_MAC_ADDRESS, macArray))
//save it in firebase
db.collection(FirebaseConstants.ORGANIZATION)
.document(orgID + ".${FirebaseConstants.USER_MAC_ADDRESS}")
.set(FieldValue.arrayUnion(macList))
.addOnCompleteListener { task ->
if (task.isSuccessful) {
Log.d(TAG, "successfully inserted")
} else {
Log.d(TAG, " failed ${task.exception}")
}
}
also tried to insert the list itself and hash map like this
val data = hashMapOf(Pair(FirebaseConstants.USER_MAC_ADDRESS, macArray))
db.collection(FirebaseConstants.ORGANIZATION)
.document(orgID)
.set(data))
but it keeps giving me java.lang.IllegalArgumentException: Invalid data. Nested arrays are not supported
what am I doing wrong here?
You're doing three things wrong here:
FieldValue.arrayUnion() is only meant to be used as the value of a field to add elements to that field. The way you are using it now in the first sample, it's being taken as the entire contents of the document.
set() with one parameter is only intended to create or overwrite an entire document. It can't be used to update an existing document. You would have to pass in SetOptions to tell it to merge if you want an update. Or, you would simply use update() to modify an existing document.
Your code that deals with macArray and macList isn't working the way you expect. You are creating a list with one element, which is itself an array. The error message is telling you that you can't have nested arrays like this.
I suggest taking a step back and simplifying your code, removing all the moving parts that don't have to do with Firestore. Just hard code values in your Firestore update until the update works the way you want, then add in the code that works with actual values. Get one simple thing to work, then add to it. If you get an error, you will know that the code you just added was incorrect.
To overwrite an array, you would simply call the set method and have the merge option set to true:
try {
const query = await DatabaseService.queryBuilder({
collection: CollectionName,
});
return await query
.doc(insuranceId)
.set(
{ DOCUMENT_PROPERTY_HERE: ARRAY_HERE },
{ merge: true }
);
} catch (exception) {
return Promise.reject(exception);
}

Unable to fetch the list column of AWS DynamoDB in Android Kotlin

I am developing an Android application using Kotlin and AWS DynamoDB. I am new to both technologies. What I am doing now is I am trying to scan data from a table of DynamoDB. I know how to scan it. But the problem is that one of the column has List data type.
I have a table called item with the following columns.
Note in particular the Images field.
In Kotlin Android, I scan the table like this.
val dynamoDBClient = AmazonDynamoDBClient(AWSMobileClient.getInstance().credentialsProvider)
val fetchedItems: ArrayList<Any> = ArrayList();
val scanRequest = ScanRequest().withTableName(MainApplication.DB_TABLE_ITEMS);
scanRequest.exclusiveStartKey = lastEvaluatedKey
val scanResult = dynamoDBClient.scan(scanRequest)
scanResult.items.forEach { item ->
Log.i("ITEM_NAME", item.get("Name")?.s)
val viewItem = ItemDO()
viewItem.id = item.get("Id")?.s
viewItem.description = item.get("Description")?.s
viewItem.name = item.get("Name")?.s
viewItem.userId = item.get("UserId")?.s
viewItem.images = item.get("Images")?.ns
fetchedItems.add(viewItem)
Log.i("IMAGES_COUNT", item.get("Images")?.ns?.size.toString())
}
But this
item.get("Images")?.ns
always return null even if the data exists in the column as in the screenshot below.
Why my code is not fetching the list data type but others?
The code looks good and should be returning data for all the attributes irrespective of their type. I have equivalent piece of code in java that works as expected. Can you try inspecting the value returned by item.get("Images") before making the null-safe call. Type of the value returned by item.get("Images") is AttributeValue and so there is a possibility that the value gets lost in the course of implicit type conversion.

Sorting Secondary Collection using Primary Sorted Collection's field

I have an unsorted List of Users and a sorted list of Users id. Id is a string.
I want to sort first list by second. How to do that in Kotlin?
data class User(val name : String, val id : String)
val unsorted = listOf<User>(
User("Max", "b12s11"),
User("Joe", "dj1232"),
User("Sam", "23d112"),
User("Tom", "k213i1")
)
val sorted = listOf<String>(
"dj1232",
"b12s11",
"k213i1",
"23d112"
)
// what I need
val result = listOf<User>(
User("Joe", "dj1232"),
User("Max", "b12s11"),
User("Tom", "k213i1"),
User("Sam", "23d112")
)
Shorter solution:
val result = unsorted.sortedBy { sorted.indexOf(it.id) }
Although the other answers show a solution to your problem, it seems to me that a Map<String, User> might better fit the purpose, e.g.:
val usersByid = unsorted.associateBy { it.id }
val result = sorted.mapNotNull {
usersById[it]
}
I assume that every id is only once in the list, therefore I used associateBy. Otherwise it wouldn't be an id for me ;-)
The main difference between this solution and others is that this solution only returns the entries that are also in the sorted-list. Note that if you have users for which you have no id in the sorted-list, this solution omits them, whereas other solutions put those entries at the front of the list. Depends on what you really want.
It could be that this solution is more efficient than the others. Accessing the Map should be much faster then reiterating all the entries over and over again (which both indexOf and first basically do).
I don't know of any Kotlin syntax for doing this, sorting one list by another, but this solution should work for you (the way I understood this question, was that you want to sort according to the Id's in sorted):
val correctList = arrayListOf<User>()
sorted.forEach { sortedId ->
correctList.add(unsorted.first {
it.id == sortedId
})
}
It iterates over your sorted list of Id's and takes the item in the first list (unsorted) which matches that ID and adds it to correctList
Edit: see answer from #Andrei Tanana for a better kotlin answer than mine : sort unsorted collection with another sorted collection's field it's pretty cool :D
Edit2: Thanks to #Roland for pointing out, I can simplify my answer even further with :
val correctList = sorted.map { sortedId -> unsorted.first { it.id == sortedId } }

Categories

Resources