perform grails each asynchronously - grails

I would like this to be performed asynchronously
Map someotherMap = [{"10","dance"},{"2","stop"}]
Map resultMap = [:]
someotherMap.each { key, value ->
doSomethingThatsAddsToResultMap(key,value,resultMap)
}
Then I can use resultMap as I wish.
I have tried
Map resultMap = [:]
Map someotherMap = [{"10","dance"},{"2","stop"}]
GParsPool.withPool {
someotherMap.eachParallel { key,value ->
doSomethingThatsAddsToResultMap(key,value,resultMap)
}
}
Any Ideas how I could get this working?

Related

How to properly set DataStore for storing boolean value

I want to set a simple switch that'll save a boolean value and if then block in my function.
Currently I have this in my DataStore:
companion object {
private val Context.dataStore: DataStore<Preferences> by preferencesDataStore("userToken")
private val AutoRestartSystemUI = booleanPreferencesKey("AutoRestartSystemUIValue")
}
var getAutoRestartSystemUIValue: Flow<Boolean> = context.dataStore.data.map { preferences ->
(preferences[AutoRestartSystemUI] ?: "") as Boolean
}
suspend fun setAutoRestartSystemUI(value: Boolean) {
context.dataStore.edit { preferences ->
preferences[AutoRestartSystemUI] = value
}
}
}
and
Button(onClick = {
// if [pro.themed.manager.UserStore(context).getAutoRestartSystemUIValue = true] ()
CoroutineScope(Dispatchers.IO).launch {
UserStore(context).setAutoRestartSystemUI(false)
}
}) {
Text(text = UserStore(context).getAutoRestartSystemUIValue.collectAsState(
initial = ""
).toString())
}
in my activity. I have generally no idea of what I should do next and for some weird reason instead of showing value in a text (temp solution for testing) i have
How do i simplify datastore? How do I properly implement switch that'll make it = !it? How to set default value?

Load next data when on click of button using the Paging 3 library for Compose

Currently Paging library handles when to load the data automatically when a user scrolls down. But what if you want to give the user full authority for when they want the next page of data to be loaded i.e when button is clicked show next page of movies. How can you handle this in Paging library? See below how I've implemented the paging to load data as a user scrolls down
Here below this how I implemented the Paging to load next page when user scrolls down
class MoviesPagingDataSource(
private val repo: MoviesRepository,
) : PagingSource<Int, Movies>() {
override fun getRefreshKey(state: PagingState<Int, Movies>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movies> {
return try {
val nextPageNumber = params.key ?: 0
val response = repo.getMovies(page = nextPageNumber, size = 10)
LoadResult.Page(
data = response.content,
prevKey = null,
nextKey = if (response.content.isNotEmpty()) response.number + 1 else null
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
}
This is how I emit the state in ViewModel for the UI to observe
#HiltViewModel
class MoviesViewModel #Inject constructor(
private val moviesRepository: MoviesRepository
): ViewModel {
....
//emitting the data to the UI to observe
val moviesPagingDataSource = Pager(PagingConfig(pageSize = 10)) {
MoviesPagingDataSource(moviesRepository)
}.flow.cachedIn(viewModelScope)
}
How I'm observing it in the UI
#Composable
fun MoviesList(viewModel: MoviesViewModel) {
val moviesList = viewModel.moviesPagingDataSource.collectAsLazyPagingItems()
LazyColumn {
items(moviesList) { item ->
item?.let { MoviesCard(movie = it) }
}
when (moviesList.loadState.append) {
is LoadState.NotLoading -> Unit
LoadState.Loading -> {
item {
LoadingItem()
}
}
is LoadState.Error -> {
item {
ErrorItem(message = "Some error occurred")
}
}
}
when (moviesList.loadState.refresh) {
is LoadState.NotLoading -> Unit
LoadState.Loading -> {
item {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Center
) {
CircularProgressIndicator()
}
}
}
is LoadState.Error -> TODO()
}
}
}
So currently I'm adding 1 to the previous page every time a user clicks the button to load more movies then saving this movies to the list state. Also making sure the current page is greater than or equal to total pages before loading more data and adding to the list state of previous loaded movies
you can use Channel to block LoadResult returning from load, when user clicks the button, send an element to the Channel. here is the simple
class MoviesPagingDataSource(
private val repo: MoviesRepository
) : PagingSource<Int, Movies>() {
private val channel: Channel<Unit> = Channel(1, BufferOverflow.DROP_LATEST)
override fun getRefreshKey(state: PagingState<Int, Movies>): Int? {
return state.anchorPosition?.let { anchorPosition ->
val anchorPage = state.closestPageToPosition(anchorPosition)
anchorPage?.prevKey?.plus(1) ?: anchorPage?.nextKey?.minus(1)
}
}
override suspend fun load(params: LoadParams<Int>): LoadResult<Int, Movies> {
return try {
val nextPageNumber = params.key ?: 0
// load initial page
if (nextPageNumber == 0) loadNextPage()
val response = repo.getMovies(page = nextPageNumber, size = 10)
/*
* block next page data return, when user click the button,
* call loadNextPage. if you don't want to automatically
* request data as the user scrolls toward the end of the
* loaded data. put this line above the repo.getMovies.
**/
channel.receive()
LoadResult.Page(
data = response.content,
prevKey = null,
nextKey = if (response.content.isNotEmpty()) response.number + 1 else null
)
} catch (e: Exception) {
LoadResult.Error(e)
}
}
suspend fun loadNextPage() {
channel.send(Unit)
}
}

Android dataStore with flow not get update after edit

I'm use DataStore with flow but I cant get any update on the flow when editing DataStore.
Store.kt
private class IStore(private val context: Context): Store {
val eventIDKey = stringPreferencesKey("EventID")
override suspend fun setEventID(eventID: String) {
context.dataStoreSettings.edit { settings ->
settings[eventIDKey] = eventID
}
}
override fun getEventID(): Flow<String> {
return context.dataStoreSettings.data.map { settings -> settings[eventIDKey].orEmpty() }
}
}
and manipulate getEventID() with data from room database in event service
EventService.kt
fun getSelectedEventLive() = store.getEventID()
.onEach { Log.d("EventService", "income new event id $it") }
.flatMapConcat { if(it.isNotBlank()) eventDao.get(it) else flowOf(null) }
onEach called when I collect the data but when updated it's not called again and need to close and open the app to show the latest data
MainViewModel.kt
val selectedEvent = eventService.getSelectedEventLive()
.stateIn(viewModelScope, SharingStarted.Lazily, null)
and use on Compose with this
val currentEvent by mainViewModel.selectedEvent.collectAsState()
Maybe I doing wrong or maybe there is something I miss?
Usually, you want to use flow.collect {...}, since Flow is cold and need to know that it is being collected to start producing new values.
// MainViewModel.kt
private val _selectedEvent = MutableStateFlow<TypeOfYourEvent>()
val selectedEvent: StateFlow<TypeOfYourEvent> = _selectedEvent
init {
viewModelScope.launch {
getSelectedEventLive().collect { it ->
_selectedEvent.value = it
}
}
}
This example should be fine with your composable's code, you still can collect selectedEvent as state.
Yeah i found the solusion its works if i change the flatMapConcat with flatMapLatest in EventService.kt
fun getSelectedEventLive() = store.getEventID()
.filterNot { it.isBlank() }
.flatMapLatest { eventDao.get(it) }

Randomly Rearrange items in a LazyRow - Jetpack Compose

I have a LazyRow. Everything works fine. I just want the items to be randomly rearranged every time this LazyRow is drawn on the screen. Here is my code:
LazyRow(
reverseLayout = true,
contentPadding = PaddingValues(top = twelveDp, end = eightDp)
) {
itemsIndexed(
items = featureUsersLazyPagingItems,
key = { _, featuredPerson ->
featuredPerson.uid
}
) { _, featuredUser ->
featuredUser?.let {
//Daw the age suggested People
DrawSuggestedPerson(featuredUser.toPersonUser(),) {
homeViewModel.deleteFeaturedUserFromLocalDb(featuredUser.uid)
}
}
}
featureUsersLazyPagingItems.apply {
when {
loadState.refresh is LoadState.Loading -> {
item {
ShowLazyColumnIsLoadingProgressBar()
}
}
loadState.append is LoadState.Loading -> {
item {
ShowLazyColumnIsLoadingProgressBar()
}
}
loadState.refresh is LoadState.Error -> {
val e = featureUsersLazyPagingItems.loadState.refresh as LoadState.Error
item {
LazyColumnErrorView(
message = e.error.localizedMessage!!,
onClickRetry = { retry() }
)
}
}
loadState.append is LoadState.Error -> {
val e = featureUsersLazyPagingItems.loadState.append as
LoadState.Error
item {
LazyColumnErrorView(
message = e.error.localizedMessage!!,
onClickRetry = { retry() }
)
}
}
}
}
So the LazyRow displays the same set of 30 or so items but only 3- 4 items are visible on screen, for a bit of variety, I would like the items to be re-arranged so that the user can see different items on the screen. Is there a way to achieve this?
You can shuffle your list inside remember, by doing this you're sure that during one view appearance your order will be the same, and it'll be shuffled on the next view appearance. I'm passing featureUsersLazyPagingItems as a key, so if featureUsersLazyPagingItems changes shuffledItems will be recalculated.
val shuffledItems = remember(featureUsersLazyPagingItems) {
featureUsersLazyPagingItems.shuffled()
}
The only problem of remember is that it'll be reset on screen rotation. Not sure if you need that, and if you wanna save state after rotation, you need to use rememberSaveable. As it can only store simple types, which your class isn't, you can store indices instead, like this:
val shuffledItemIndices = rememberSaveable(featureUsersLazyPagingItems) {
featureUsersLazyPagingItems.indices.shuffled()
}
val shuffledItems = remember(featureUsersLazyPagingItems, shuffledItemIndices) {
featureUsersLazyPagingItems.indices
.map(featureUsersLazyPagingItems::get)
}
I suggest you checkout out documentation for details of how state works in compose.

Some url for parser

I have an url array which I want to parse, how can I get it?
What I want is to see all the url in the same recyclerView.
doAsync {
val array = arrayOf("https://rafelcf.000webhostapp.com/rafel_cf/1.php",
"https://rafelcf.000webhostapp.com/rafel_cf/2.php")
array.forEach
val url = (it)
val stringRequest = StringRequest(Request.Method.GET, url, Response.Listener { response ->
val builder = GsonBuilder()
val mGson = builder.create()
uiThread {
val items: List<ModelJor>
items = (Arrays.asList(*mGson.fromJson(response, Array::class.java)))
val filtro = items.filter { it.estadoPartido == "Pendiente" && it.fecha != "" }
recyclerView!!.layoutManager = GridLayoutManager(activity!!, 1)
val adapter = AdapNJ(activity!!, filtro)
recyclerView!!.adapter = adapter
adapter.notifyDataSetChanged();
}
}, Response.ErrorListener { error -> Log.d(TAG, "Error " + error.message) })
queue.add(stringRequest)
}
See Step by step below.
You can use the following statement to get the response of each url.
val arr = arrayOf("https://rafelcf.000webhostapp.com/rafel_cf/1.php",
"https://rafelcf.000webhostapp.com/rafel_cf/2.php")
arr.forEach {
val jsonText = URL(it).readText()
// parse jsonText to objects
println(jsonText)
}
To parse json text, you can follow the instructions here.
Edit
To implement network connection in android,
you need to do the task in the separate thread, not in UI Thread.
Using Kotlin anko, you can easily achieve this using doAsync.
Just wrap the code to be run in doAsync clause
and wrap the resulting code in uiThread clause.
doAsync {
val arr = arrayOf(...)
arr.forEach {
...
val result = // parsed result
uiThread {
doSomethingWithResult(result)
}
}
}
Step by Step
Okay I'll give you step by step instructions so you can follow along with your project.
1. Add internet permission
We will be using internet connection, so we have to tell that we need permission to use the internet.
Open app/manifests/AndroidManifest.xml.
Add the following tag as a child of root <manifest> tag.
<uses-permission android:name="android.permission.INTERNET"/>
2. Add anko library
We will be using anko library so we can handle async tasks easily without boilerplates.
Open Gradle Scripts/build.gradle (Module: app)
Add following line inside dependencies.
implementation "org.jetbrains.anko:anko:0.10.8"
Click Sync Now.
3. doAsync{} for background tasks
To request in background, we are using doAsync by anko.
Place doAsync clause in where you have to make a request.
In my case, I'll be sending request when user clicks a button.
btnLoad.setOnClickListener {
doAsync {
}
}
This will cause a compile error.
To solve this, simply import doAsync.
4. Loop through urls and fetch response
Now we are in background, we can make requests!
Inside doAsync, request and fetch the response.
val arr = arrayOf("https://rafelcf.000webhostapp.com/rafel_cf/1.php",
"https://rafelcf.000webhostapp.com/rafel_cf/2.php")
arr.forEach {
// request and fetch response
val jsonText = URL(it).readText()
}
Now we have response text in jsonText.
5. Parse response
I'll just use JSONArray and JSONObject to parse jsonText.
But it is your choice which method you will use.
You can use Gson as you described in your question.
As we now that response is in array, we can do as follows:
// we know that result json is an array
val jsonArray = JSONArray(jsonText)
for(i in 0 until jsonArray.length()) {
// get each elements
val jsonObject = jsonArray[i] as JSONObject
// get data of each elements
val idLocal = jsonObject.getString("idLocal")
val idClubLocal = jsonObject.getString("idClubLocal")
val nomLocal = jsonObject.getString("nomLocal")
}
6. Do something with parsed results
Now that we have parsed data, only thing left is to do something with it.
I'll simply log the data to the logcat.
I defined log() to simplify logging codes.
fun log(message: String) = Log.d("MainActivity", message)
Now I logged the data I fetched.
log("[item $i]")
log("idLocal: $idLocal")
log("idClubLocal: $idClubLocal")
log("nomLocal: $nomLocal")
From this point, when you run this code you'll be able to see the result like this in logcat.
MainActivity: [item 0]
MainActivity: idLocal: 0201010551
MainActivity: idClubLocal: 0201010
MainActivity: nomLocal: AAAAA
MainActivity: [item 1]
MainActivity: idLocal: 0201045201
MainActivity: idClubLocal: 0201045
MainActivity: nomLocal: BBBBB
MainActivity: [item 0]
MainActivity: idLocal: 0201010551
MainActivity: idClubLocal: 0201010
MainActivity: nomLocal: CCCCC
MainActivity: [item 1]
MainActivity: idLocal: 0201045201
MainActivity: idClubLocal: 0201045
MainActivity: nomLocal: DDDDD
As you see, these are the data that your urls provide.
7. Let something run in uiThread
Notice that all our code is running in async.
Sometimes, we need to do something with the result in ui thread, such as
Update TextView as request
Add new item to RecyclerView
For now, I will stick on logging data instead of updating uis.
To run code in ui thread, simply wrap the code with uiThread clause.
You might also have to import uiThread.
uiThread {
log("[item $i]")
log("idLocal: $idLocal")
log("idClubLocal: $idClubLocal")
log("nomLocal: $nomLocal")
}
The code works exactly same except the logging now works in ui thread.
FULL CODE
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
btnLoad.setOnClickListener {
doAsync {
val arr = arrayOf("https://rafelcf.000webhostapp.com/rafel_cf/1.php",
"https://rafelcf.000webhostapp.com/rafel_cf/2.php")
arr.forEach {
val jsonText = URL(it).readText()
// we know that result json is an array
val jsonArray = JSONArray(jsonText)
for(i in 0 until jsonArray.length()) {
// get each elements
val jsonObject = jsonArray[i] as JSONObject
// get data of each elements
val idLocal = jsonObject.getString("idLocal")
val idClubLocal = jsonObject.getString("idClubLocal")
val nomLocal = jsonObject.getString("nomLocal")
uiThread {
log("[item $i]")
log("idLocal: $idLocal")
log("idClubLocal: $idClubLocal")
log("nomLocal: $nomLocal")
}
}
}
}
}
}
fun log(message: String) = Log.d("MainActivity", message)
}
The Solution:
fun getPendientes() {
doAsync {
for(num in 1..30) {
val arr = arrayOf(
"http://www.url$num")
arr.forEach {
val jsonText = URL(it).readText()
val jsonArray = JSONArray(jsonText)
for (i in 0 until jsonArray.length()) {
val pendientes = jsonArray.optJSONObject(i)
val nomLocal = pendientes.getString("nomLocal")
val resulLocal = pendientes.getString("resulLocal")
val escudoLocal = pendientes.getString("escudoLocal")
val nomVisitante = pendientes.getString("nomVisitante")
val resulVisitante = pendientes.getString("resulVisitante")
val escudoVisitante = pendientes.getString("escudoVisitante")
val fecha = pendientes.getString("fecha")
val hora = pendientes.getString("hora")
val estadoPartido = pendientes.getString("estadoPartido")
val abreviaturaEstado = pendientes.getString("abreviaturaEstado")
modelPendientes.add(ModelPendientes(nomLocal, resulLocal, escudoLocal,
nomVisitante, resulVisitante, escudoVisitante, fecha, hora, estadoPartido,abreviaturaEstado))
val filtro = modelPendientes
.filter {it ->
it.abreviaturaEstado == "P" ||
it.abreviaturaEstado == "A" ||
it.abreviaturaEstado == "S" &&
it.fecha != ""
}
uiThread {
/*val filtro = modelPendientes.filter { it.abreviaturaEstado == "A" ||
it.abreviaturaEstado == "S" || it.abreviaturaEstado == "P" && it.fecha != ""*/
//}
recyclerView!!.layoutManager = GridLayoutManager(activity!!, 1)
adapter = PendientesAdapter(filtro, activity!!)
recyclerView!!.adapter = adapter
}
}
}
}
}
}

Resources