android random keeps generating the same int sequence - android

i'm having a well known problem in android which is generating random numbers. the problem is that i need to generate 20 random numbers between 1 and 905 every time a screen is loaded. and if i use kotlin random it keeps generating the same sequence even if i'm closing and reopen the app. and java random and secure random generates different sequence only if i'm closing and reopening the app. but i want to have different sequences even if i dont close the app and open it again.
i know it has been asked before but my problem is that i tried every solution and its the same results.
i would appreciate the help if anyone knows what the hell is wrong.
here are the solutions i used:
1.
val randomIndex: List<Int> = List(20){
java.util.Random().nextInt(905) + 1
}
val randomIndex: MutableSet<Int> = remember{ mutableSetOf() }
while (randomIndex.size < 20){
randomIndex.add(java.util.Random().nextInt(905) + 1)
}
val randomIndex: List<Int> = (0..905).toList.shuffled(Random).subList(0,20)
i'm using jetpack compose and kotlin so ignore all the mutable state and remembers. also where ever you see Random i used all java Random, SecureRandom and kotlin Random and the problem i discussed persists.
i also tried initializing Random out of loop on the second code i put here and still it does the same thing.
EDIT:
thought i mentioned these here aswell. i'm using kolin 1.6.10 and my compile sdk for android is 33. i'm also trying the app on 2 different phones with android 8 and 11.
EDIT2:
#Tenfout04 suggested to put my indexes outside of my composable so this is what i did inside of my viewmodel:
var pokemonIndex: List<Int> = mutableStateListOf()
init {
pokemonIndex = List(20){
java.util.Random().nextInt(905)+1
}
}
and in my composable:
val viewModel: PokemonGameViewModel = hiltViewModel()
val randomIndex = viewModel.pokemonIndex
another thing i tried in my viewmodel is:
val pokemonIndex: MutableState<List<Int>>
get() = mutableStateOf(
List(20){
java.util.Random().nextInt(905)+1
}
)
results are the same :(

Related

I Can't figure out how to write a Test to pass a Composable Test in Android

I took a Codelab Lunch-tray App it had no Tests so I tried to create these tests to practice. I tried to create testcases for it based on another codelab Codelab Cupcake
The way these 2 projects differ is that on the second codelab(Lunch-tray) the "Next" button is in uppercase.
Which I can not figure out how to write a test to make it pass.
val myNextButton = composeTestRule.activity.getString(R.string.btn_next).uppercase()
composeTestRule.onNodeWithText(myNextButton).performClick()
navController.assertCurrentRouteName(LixoPlayScreen.SideDishScreen.name)
This seems to work, but on mouse over activity I see this message
Avoid calling often as it can involve synchronization and can be slow.
In the onNodeWithText method you can set the ignoreCase to true.
Something like:
composeTestRule.onNodeWithText(
text = composeTestRule.activity.getString(R.string.next),
ignoreCase = true)
.performClick()
In the codelab, to use the onNodeWithStringId method just change:
fun <A : ComponentActivity> AndroidComposeTestRule<ActivityScenarioRule<A>, A>.onNodeWithStringId(
#StringRes id: Int
): SemanticsNodeInteraction = onNodeWithText(activity.getString(id),ignoreCase = true)

Unit test can not be achieved in android studio using kotlin

In android studio using kotlin, I created a class with the code below:
class Dice(val numSides:Int){
fun roll():Int{
return (1..numSides).random()
}
}
in ExampleUnitTest.kt file I have created the following code to test:
class ExampleUnitTest {
#Test
fun generates_number() {
val dice = Dice(4)
val rollResult = dice.roll()
assertTrue("The value of rollResult was not between 1 and 6", rollResult in 1..6)
}
}
The test should be passed only if Dice(6) as the result should be any number between 1 to 6 but as shown above when with Dice(4) or any number, the test passed. that why? thanks
Your test checks if the rolled number is between 1 and 6. With Dice(4) the result will be between 1 and 4, which means it's always going to be between 1 and 6 as well. Or to put it another way, it's impossible for you to get a value from Dice(4).roll() that isn't somewhere between 1 and 6 - it's never going to be 5 or 6, but that doesn't contradict anything!
What you probably want to test is whether Dice(4).roll()'s possible range of outputs is from 1 to 6. But there's no way to absolutely prove that from the outside, not the way the class is written anyway. All you can do is provide a value for numSides and then call roll() and check the output. That's all the class offers to you in terms of interactivity.
A better question is why you want to test this? Your test seems to be written to fail when your code is working - you should be testing that the behaviour is what you'd expect, so all your tests pass. So really, what you'd want to test is that Dice(4).roll() only produces values within the expected range, i.e. between 1 and 4.
Since it's random the only way to really do this is run the same test lots of times and make sure a bad number never comes up (or you eventually see a number you want), so you can say with a high degree of confidence that it's probably correct. Something like
#Test
fun generates_number() {
val dice = Dice(4)
val allGood = generateSequence { dice.roll() }.take(9999).all { it in 1..4 }
assertTrue("At least one dice roll was not between 1 and 4", allGood)
}
(or you could use a for loop with break to exit early if one of the values isn't valid, instead of the sequence + all)
I just picked 9999 at random but you could use statistics to pick a more suitable number. But the problem here is the random behaviour - because it's not predictable, you can't do a simple state in -> result out test with an expected result.

Cannot use == with livedata<int> and int, how else can i check?

ok so I have an android app and have followed androids room with a view tutorial. I have managed to get it working as expected with my recyclerview to show a history of all games played. I am now working on an achievements style page and want to check for specific scores achieved.
In my DAO file I have the following;
#Query("SELECT COUNT(*) from SavedScores WHERE difficulty = :Difficulty AND questioncount = :QuestionCount AND answeredcorrectly =:QuestionCount")
fun CheckRecordsForTrophy(Difficulty: String,QuestionCount:Int):Flow<Int>
Then in my room repository I have this;
val easy5: Flow<Int> = savedScoresDao.CheckRecordsForTrophy("Easy",5)
In my view model;
val easy5: LiveData<Int> = repository.easy5.asLiveData()
and then in an activity I have the following;
Before the oncreate method:
private val savedScoresViewModel: SavedScoresViewModel by viewModels {
SavedScoresViewModelFactory((application as ScoreApplication).repository)
}
Within the oncreate method:
var easy5var = savedScoresViewModel.easy5
savedScoresViewModel.easy5.observe(this) {
if(easy5var==0){}
}
I am not 100% sure if I should be following all these steps like I did to get all data into my recycler view but I have effectively followed the same steps with exception of adapters etc as I am simply trying to understand if they have met the criteria for a given achievement.
I have a hard coded elements in my repo at the moment for the function i.e CheckRecordsForTrophy("Easy",5) which I will figure how to set from the activity later
The issue I appear to be facing is with:
if(easy5var==0){}
The error I get is Operator '==' cannot be applied to 'LiveData' and 'Int'.
Goal: Check if within my score table is there a record where the score is equal to the number of questions asked, if so I will mark an achievement as complete. I have read that using count* in the query returns the number of records found so I can use that to work out if they should get the achievement or not. In other words, if no records, no achievement.
You are comparing an Int to a LiveData, when you probably wanted to compare the int to the emitted value of the live data.
var easy5var = savedScoresViewModel.easy5
savedScoresViewModel.easy5.observe(this) { newValue -> // this is what you've missed
// if(easy5var==0) {} <-- you've made the wrong equality check here
if (newValue == 0) {} // <-- probably this is what you've meant to do.
}

Cannot access 'split': it is private in file

I develop an app with Kotlin and got this weird error today in Android Studio, so I tried the same code in InteliJ Idea as well, where I get the same behaviour. I've used the split method so far in my Android project and it worked always like a charm.
This is the code where this behaviour appears:
val rawString = "OK;ABC;34"
val delimited = rawString.split(";",true,0).last()
So today, the compiler says that the split method cannot be reached, because it is private in file. Go figure!
In the code above, I'm trying to get the String "34" into my delimited variable. Are there any restrictions that I'm missing or are there any changes made on this method?
Thanks in advance.
Use like the below, split that you have used is a private function in Strings.kt class
val rawString = "OK;ABC;34"
val delimited = rawString.split(";", ignoreCase = true, limit = 0).last()

DataBinding ViewModel vs RxJava 2

So basically up until now I have been using rxjava2 extensively in the applications, but decided to check out data binding, view models and live data. And Im not sure I've got all of this right, because apart from saving state during rotation of device I do not see any other clear benefits of switching, I could even say that I see downsides of introducing data binding with view model between view and rx java powered requests.
Lets see example of some registration form. It would contain:
2 inputs - name and surname
Field with 3 choices
Button with progress
In the reactive world I would have two observables with name and surname, one observable that would merge 3 choices clicks and map them to the right enum, then I could combine all the data together, communicate directly with my single responsible for sending the data in between I would have state with progress or error and tada Im done.
And here is the thing that I came up with using data binding and view models:
class LiveDataViewModel : ViewModel() {
enum class Choice {
NONE, FIRST, SECOND, THIRD
}
private val _progressVisibilityLiveData = MutableLiveData<Boolean>()
private val _errorLiveData = MutableLiveData<GlobalError>()
val progressVisibilityLiveData: LiveData<Boolean> = _progressVisibilityLiveData.apply { value = false }
val errorLiveData: LiveData<GlobalError> = _errorLiveData
val data = LiveDataData()
val observableData = ObservableField(LiveDataData())
fun actionContinue() {
_progressVisibilityLiveData.postValue(true)
if (observableData.get()?.isValid() == false) _errorLiveData.postValue(GlobalError.AllFieldsRequired)
else sendToApi()
}
private fun sendToApi() {
// TODO there would be still an rx java call to single, when we would handle error in the same way we are doing
// it in actionContinue
}
data class LiveDataData(val firstName: ObservableField<String> = ObservableField(""),
val secondName: ObservableField<String> = ObservableField(""),
val choice: ObservableField<Choice> = ObservableField(Choice.NONE)) {
fun changeChoice(newChoice: Choice) {
choice.set(newChoice)
}
fun isValid(): Boolean = !firstName.get().isNullOrEmpty() && !secondName.get().isNullOrEmpty() && choice.get() != Choice.NONE
fun toRequest(): Request = Request(firstName.get()!!, secondName.get()!!, choice.get()!!)
}
}
So I would change fields of my LiveDataData directly from xml using bindData, also I would change state of my selection box depending on this binding too, progress would have to be done manually and then it would trigger the visibility using data binding. But is it really a good way of handling such cases?
The disadvantages I see are that the whole logic in actionContinue would be manually changing values, the values from ObservableProperties could be null, so we either have to handle nullable values everywhere of we have to use !! and to be honest Im not feeling that this is the right direction.
Maybe any of you guys have thought about something similar and could eventually point me if I made some wrong assumptions or if I shouldn't use for example ObservableProperty at all. Obviously there are tons of articles about data binding and live data etc, but I haven't found any that would satisfy my curiosity. Oh and create MutableLiveData for each property from form is not an option.
RxJava is a completely different concept than DataBinding. It's more of a way of handling concurrency than it is about binding data. I 100% think it's worth learning. The Android community has embraced it with open arms.
Shameless plug: I compiled a list of RxJava resources awhile back - http://gregloesch.com/dev/2014/10/20/resources-for-learning-rxjava-android.html

Categories

Resources