Some url for parser - parsing

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
}
}
}
}
}
}

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) }

how to use liveData coroutine block

how to use liveData coroutine block
in offical doc
https://developer.android.google.cn/topic/libraries/architecture/coroutines#livedata
now can use livedata with coroutine in liveData block
val user: LiveData<User> = liveData {
val data = database.loadUser() // loadUser is a suspend function.
emit(data)
}
when i try to use like this
fun onLogin(v: View) {
liveData(context = Dispatchers.IO) {
val reqLogin = ReqLogin(account = account.value?:"", password = MD5(password.value?:""))
val data = HttpManager.service(MobileApi::class.java).loginSuspend(reqLogin)
emit(data.data!!)
}
}
codes in block not executed
search and found that liveData block always use for assignment
https://medium.com/androiddevelopers/viewmodels-and-livedata-patterns-antipatterns-21efaef74a54
if want to refresh the livedata value, can use Transformations like
LiveData<Repo> repo = Transformations.switchMap(repoIdLiveData, repoId -> {
if (repoId.isEmpty()) {
return AbsentLiveData.create();
}
return repository.loadRepo(repoId);
});
but how can i use it when
1. activity onResume and refresh the data from server
2. some click event trigger the request and get some new data to show
in my login scenes, use viewModelScope seems more useful
fun onLogin(v: View) {
val reqLogin = ReqLogin(account = account.value ?: "", password = MD5(password.value ?: ""))
viewModelScope.launch {
val data = withContext(Dispatchers.IO) {
HttpManager.service(MobileApi::class.java).loginSuspend(reqLogin)
}
_userInfo.value = data.data!!
_isLogin.value = true
}
}
fun onLogin(v: View) {
liveData(context = Dispatchers.IO) {
val reqLogin = ReqLogin(account = account.value?:"", password = MD5(password.value?:""))
val data = HttpManager.service(MobileApi::class.java).loginSuspend(reqLogin)
emit(data.data!!)
}
}
Code block is not executed because the documentation says that
The code block starts executing when LiveData becomes active and is
automatically canceled after a configurable timeout when the LiveData
becomes inactive.
You should make it active by observing it.

perform grails each asynchronously

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?

Resources