Kotlin Variable changes are not applied in method and observer - kotlin

private var enableBottomSheet = false
bottomSheetStack.observe(requireActivity()) { stack ->
logd("========= enableBottomSheet: ${enableBottomSheet}")
showBottomSheet(stack)
}
override fun setMenuVisibility(menuVisible: Boolean) {
super.setMenuVisibility(menuVisible)
enableBottomSheet = menuVisible
lifecycleScope.launch{
repeat(100){
logd("==== TIME($it): enableBottomSheet $enableBottomSheet")
delay(100L)
}
}
if (menuVisible) {
viewModel.apiCall()
}else{
bottomSheetJob?.cancel()
}
}
private fun showBottomSheet(stack: Stack<BottomSheetStatusModel>) {
logd("====== http showBottomSheet enableBottomSheet: ${enableBottomSheet}")
if(!enableBottomSheet) return
if (stack.size > 0) {
try {
bottomSheetJob = lifecycleScope.launch {
delay(100L)
try {
findNavController().navigate(R.id.action_mainContainerFragment_to_statusBottomSheet, bundle, null)
}catch (e: java.lang.Exception){
e.printStackTrace()
}
}
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
}
}
And I got this result:
================ http setMenuVisibility enableBottomSheet: true ================
==== TIME(0): enableBottomSheet true
...
========= http enableBottomSheet: false
==== TIME(13): enableBottomSheet true
...
==== TIME(22): enableBottomSheet true
====== http showBottomSheet enableBottomSheet: false
...
==== TIME(99): enableBottomSheet true
There is no part that assigns false value to the enableBottomSheet.
I don't know why enableBottomSheet keeps getting false.
It happens when I go like A_Fragment(A1_Fragment) -> B_Fragment -> A_Fragment(A1_Fragment) -> A_Fragment(A2_Fragment uses the code above).
(A_Fragment is the parent fragment that contains A1~A2_Fragment as ViewPager2. B_Fragment is a fragment that can go from A1_Fragment.)
Why is it happened?

Related

How can I listen the recyclerView changes and save

Im trying to do start a radio and the radioPlayButton will change to pause button and I need how to do notifyData and save changes.
This is my adapter;
class RadioListAdapters(private var radioList: ArrayList<RadioListModel>, private val listener: Listener,private var radioFilterList : List<RadioListModel>) :
RecyclerView.Adapter<RadioListAdapters.AdapterHolder>() , Filterable {
interface Listener {
fun startRadio(radio: RadioListModel) {
}
fun stopRadio(radio: RadioListModel) {
}
}
class AdapterHolder(val binding: RecyclerRowBinding) : RecyclerView.ViewHolder(binding.root) {
private var player = MediaPlayerSingleton.player
fun bind(radio: RadioListModel, listener: Listener) {
player = MediaPlayer()
binding.radioPlayButton.setOnClickListener {
if (player != null && !player!!.isPlaying) {
itemView.setOnClickListener {
listener.startRadio(radio)
if (player!!.isPlaying) {
binding.radioPlayButton.setBackgroundResource(R.drawable.pause)
}
}
}else if (player!!.isPlaying) {
listener.stopRadio(radio)
if (!player!!.isPlaying) {
binding.radioPlayButton.setBackgroundResource(R.drawable.play)
}
}
}
}
This is my fragment: listener method;
override fun startRadio(radio: RadioListModel) {
super.startRadio(radio)
var player = MediaPlayerSingleton.player
binding.recyclerProgres.visibility = View.VISIBLE
val name = radio.name
val streamUrl = radio.streamLink
val imageUrl = radio.imageLink
val position = radio.id
if (player != null && player!!.isPlaying) {
player!!.pause()
player!!.release()
Shared.Constant.clearPlay(requireContext())
}
try {
if (player != null) {
player!!.release()
Shared.Constant.clearPlay(requireContext())
}
player = MediaPlayer()
player!!.setAudioStreamType(AudioManager.STREAM_MUSIC)
player!!.setDataSource(Uri.parse(streamUrl).toString())
player!!.setOnPreparedListener(MediaPlayer.OnPreparedListener {
player!!.start()
if (player!!.isPlaying) {
if (name != null && streamUrl != null) {
Shared.Constant.setPlaying(requireContext(), name, streamUrl, imageUrl!!, position!!
)
Toast.makeText(activity, "Playing ${radio.name}", Toast.LENGTH_SHORT).show()
binding.recyclerProgres.visibility = View.GONE
radioListAdapters!!.notifyData(radioFilterList)
}
}
})
player!!.setOnCompletionListener(MediaPlayer.OnCompletionListener {
if (player!!.release().run { true }) return#OnCompletionListener
})
player!!.setOnErrorListener(MediaPlayer.OnErrorListener { mp, what, extra ->
player!!.reset()
Shared.Constant.clearPlay(requireContext())
true
})
player?.prepareAsync()
} catch (e: IllegalArgumentException) {
e.printStackTrace()
} catch (e: SecurityException) {
e.printStackTrace()
} catch (e: IllegalStateException) {
e.printStackTrace()
} catch (e: IOException) {
e.printStackTrace()
}
}
Except this, I'm trying save radio properties to a shared preferences singleton and need to compare with get method in playing
Example => when play button clicked; if -> setData == getData dont do anything
else if -> setData != getData pause radio and start new,
if you help me for do this I will be glad! Thank you in advance.

Remote Mediator loads only the first page

I'm trying to add offline capabilities to my TMDB app. I've tried doing it with Room but RemoteMediator only loads the first page.
This is how I implemented the RemoteMediator class
#OptIn(ExperimentalPagingApi::class)
class MoviesPopularMediator(
private val service: ApiService,
private val database: PopularMoviesDatabase
) : RemoteMediator<Int, MoviesModel>() {
override suspend fun load(
loadType: LoadType,
state: PagingState<Int, MoviesModel>
): MediatorResult {
return try {
val loadKey = when(loadType){
LoadType.REFRESH -> {
1
}
LoadType.PREPEND -> return MediatorResult.Success(endOfPaginationReached = true)
LoadType.APPEND ->{
state.lastItemOrNull()
?: return MediatorResult.Success(endOfPaginationReached = true)
getMoviesPage()
}
}
val response = service.getPopular(
page = state.config.pageSize,
)
val listing = response.body()
val results = listing?.results
if (listing != null) {
database.withTransaction {
if (loadKey != null) {
database.popularMoviesPageDao().savePopularMoviesPage(MoviesPage(page = listing.page, results = listing.results, total_pages = listing.total_pages))
}
if (results != null) {
database.popularMoviesDao().savePopularMovies(results)
}
}
}
MediatorResult.Success(endOfPaginationReached = response.body()?.page == response.body()?.total_pages)
} catch (exception: IOException) {
MediatorResult.Error(exception)
} catch (exception: HttpException) {
MediatorResult.Error(exception)
}
}
private suspend fun getMoviesPage(): MoviesPage? {
return database.popularMoviesPageDao().getPopularMoviesPage().firstOrNull()
}
}
I get the data from this api: https://api.themoviedb.org/3/.
Any ideas on how I should change this RemoteMediator so that it will load all pages?
If you need more details please feel free to ask

ProgressDialog shows after the method is complete

I am trying to make a ProgressDialog show up while the application is looking for an IP Address in the network. In my present codes, even though the initialization of the ProgressDialog is at the beginning, it shows after what I am waiting for finishes.
Here is my code:
val clickListener = View.OnClickListener { view ->
when(view.id) {
R.id.button_upload -> {
progressDialog = ProgressDialog(activity)
progressDialog!!.setMessage("Looking for the server. Please wait...")
progressDialog!!.setCancelable(false)
progressDialog!!.show()
if(findServer()) {
Log.i("TAG", "FOUND")
} else {
Log.i("TAG", "NOT FOUND")
}
}
}
}
private fun findServer(): Boolean {
if(canPingServer()) {
Toast.makeText(context, "We are connected to the server server", Toast.LENGTH_LONG).show()
gView.button_upload.setText("Upload")
gView.button_upload.isEnabled = true
progressDialog!!.dismiss()
return true
} else {
Toast.makeText(context, "We cannot connect to the server.", Toast.LENGTH_LONG).show()
gView.button_upload.setText("Server not found")
gView.button_upload.isEnabled = false
progressDialog!!.dismiss()
return false
}
}
private fun canPingServer(): Boolean {
val runtime = Runtime.getRuntime()
try {
val mIpAddrProcess = runtime.exec("/system/bin/ping -c 1 192.168.1.4")
val mExitValue = mIpAddrProcess.waitFor()
Log.i("TAG","mExitValue $mExitValue")
return mExitValue == 0
} catch (ignore: InterruptedException) {
ignore.printStackTrace()
Log.i("TAG"," Exception:$ignore")
} catch (e: IOException) {
e.printStackTrace()
Log.i("TAG"," Exception:$e")
}
return false
}
I believe that I have to create the AsyncTask<Void, Void, String> for this, but the thing is, this fragment have inherited from another class already like so
class UploadFragment : BaseFragment() {.....}
It's showing because you findServer() function needs to execute on a different thread.
val clickListener = View.OnClickListener { view ->
when(view.id) {
R.id.button_upload -> {
progressDialog = ProgressDialog(activity)
progressDialog!!.setMessage("Looking for the server. Please wait...")
progressDialog!!.setCancelable(false)
progressDialog!!.show()
Thread(Runnable {
if(findServer()) {
Log.i("TAG", "FOUND")
} else {
Log.i("TAG", "NOT FOUND")
}
}).start()
}
}
}
AsyncTask<Void, Void, String> is another way to multi thread in java but I believe the way I showed above would suit your needs better. You need to be careful though because anything that has to run on the main thread I.e. your toasts or where you are setting the text of your elements still needs to happen on the main thread. You can accomplish this by surrounding anything that requires being run on the main thread with
activity.runOnUiThread(java.lang.Runnable {
//put code here that needs to be run on the ui thread
})
In you case an example would be
private fun findServer(): Boolean {
if(canPingServer()) {
activity.runOnUiThread(java.lang.Runnable {
Toast.makeText(context, "We are connected to the server server", Toast.LENGTH_LONG).show()
gView.button_upload.setText("Upload")
gView.button_upload.isEnabled = true
progressDialog!!.dismiss()
})
return true
} else {
activity.runOnUiThread(java.lang.Runnable {
Toast.makeText(context, "We cannot connect to the server.", Toast.LENGTH_LONG).show()
gView.button_upload.setText("Server not found")
gView.button_upload.isEnabled = false
progressDialog!!.dismiss()
})
return false
}
}

Kotlin -Coroutine request won't wait till first request finish

I have 2 requests: SIGNUP and SIGNUP_UPLOAD_AVATAR
#POST(SIGNUP)
fun registerUser(#QueryMap(encoded = true) userCredentials: HashMap<String, Any>): Deferred<Response<UserResponse>>
#Multipart
#POST(SIGNUP_UPLOAD_AVATAR) //SHOULD BE A PUT, DUE TO ONLY SEND FILE.
fun uploadAvatar(#Part file: MultipartBody.Part): Deferred<Response<ResponseBody>>
currently i decided to change for the use of COROUTINES however im having an issue, that is when the first post SIGNUP_UPLOAD_AVATAR start his request, i need to wait till it finish to go with the SIGNUP process. However the second coroutine start immediately, without asking if the first request finish or is still working.
This is my function:
fun getImageUrlCoRoutine(){
val requestFile = RequestBody.create(MediaType.parse("multipart/form-data"), profilePicture)
val body = MultipartBody.Part.createFormData("file", "android.${getFileExtension(profilePicture)}", requestFile)
val service = APIService.create()
val request = service.uploadAvatar(body)
try {
GlobalScope.launch(Dispatchers.Main) {
val response = CoroutineUtil().retryIO (times = 3){ request.await() }
val responseCode = StatusCode(response.code()).description
when(responseCode){
StatusCode.Status.OK -> {
response.body()?.let {
it.let {
println("RESULT: " + it.string())
avatarUrl = it.string()
println("Avatar: " + avatarUrl)
registrationCoroutine(this) <- here goes for the second request(registration)
}
}
}
else -> {}
}
}
}catch (e: HttpException){
val responseCode = StatusCode(e.code()).description
when(responseCode){
StatusCode.Status.NotAcceptable -> {
}
else -> {
}
}
}catch (e: Throwable){
println("Houston we got a Coroutine Problem")
println(e)
}
}
fun registrationCoroutine(suspended: CoroutineScope) {
val service = APIService.create()
val data = HashMap<String, Any>()
data["email"] = email
data["phone"] = phoneNumber
data["username"] = email
data["password"] = password
data["fullName"] = fullname
data["token"] = ""
data["appName"] = BuildConfig.APP_NAME
data["avatarUrl"] = avatarUrl
data["deviceType"] = BuildConfig.PLATFORM
data["needsVerify"] = false
suspended.launch {
val request = service.registerUser(data)
try {
val response = CoroutineUtil().retryIO(times = 3) { request.await() }
val responseCode = StatusCode(response.code()).description
when(responseCode){
StatusCode.Status.OK -> {
response.body()?.let {
it.let {
println(it)
}
}
}
else -> {}
}
} catch (e: HttpException) {
val responseCode = StatusCode(e.code()).description
when(responseCode){
StatusCode.Status.NotAcceptable -> {}
else -> {}
}
} catch (e: Throwable) {
println("Houston we have a coroutine problem")
println(e)
}
}
}
and the responses that i get...
2019-06-25 08:41:28.858 19886-19886/com.multirequest.development
I/System.out: RESULT:
2019-06-25 08:41:28.859 19886-19886/com.multirequest.development
I/System.out: Avatar:
2019-06-25 08:41:28.880 19886-20735/com.multirequest.development
D/OkHttp: --> POST
http://myCustomURL.com/signup?deviceType=ANDROID&password=demdemdem&needsVerify=false&phone=+1123456789&avatarUrl=&appName=DEMO&fullName=demmmm&email=demm#gmail.com&username=demm#gmail.com&token=
201
And i need that when i get the AvatarURL the signup process begin...
Thanks :)

Problem with callback and coroutines Kotlin

i have some problem.
here , i launch a callback with the function getRoute() to get the route for the navigation
override fun onViewCreated(view: View, savedInstanceState: Bundle?)
{
Mapbox.getInstance(this.context!!, getString(R.string.token_mapbox))
mapView = view.findViewById(R.id.mapView)
mapView?.onCreate(savedInstanceState)
Log.d("test", "test1")
val autocomplete : GeocoderAutoCompleteView = view.findViewById(R.id.address_search)
autocomplete.setAccessToken(Mapbox.getAccessToken())
autocomplete.setType(GeocodingCriteria.TYPE_POI)
autocomplete.setCountry("CA")
autocomplete.setOnFeatureListener {
fun onFeatureClick(feature : CarmenFeature) {
hideOnScreenKeyboard()
val position : Position = feature.asPosition()
updateMap(position.latitude, position.longitude)
}
}
mapView?.getMapAsync { mapboxMap ->
map = mapboxMap
Log.d("test", "test4")
enableLocationPlugin()
Log.d("test", "test3")
originCoord = LatLng(originLocation?.latitude!!, originLocation?.longitude!!)
Log.d("test", "test")
Log.d("test", "test8")
startButton.setOnClickListener(View.OnClickListener {v ->
Log.d("test","enter here")
val mapboxGeocoding : MapboxGeocoding = MapboxGeocoding.builder().accessToken(Mapbox.getAccessToken()!!).query(address_search.text.toString()).build()
Log.d("test","1")
if (destinationMarker != null) {
mapboxMap.removeMarker(destinationMarker!!)
}
Log.d("test","2")
mapboxGeocoding.enqueueCall(object : Callback<GeocodingResponse>
{
override fun onResponse(call : Call<GeocodingResponse>, response : Response<GeocodingResponse>) {
Log.d("test","3")
val results = response.body()!!.features()
if (results.size > 0) {
// Log the first results Point.
val firstResultPoint : Point = results[0].center()!!
Log.d("test", "onResponse: " + firstResultPoint.toString());
val test = LatLng(firstResultPoint.latitude(), firstResultPoint.longitude())
destinationCoord = test
destinationMarker = mapboxMap.addMarker(MarkerOptions().position(destinationCoord))
destinationPosition = Point.fromLngLat(destinationCoord!!.longitude, destinationCoord!!.latitude)
originPosition = Point.fromLngLat(originCoord!!.longitude, originCoord!!.latitude)
getRoute(originPosition!!, destinationPosition!!)
startButton.isEnabled = true
startButton.isClickable = true
var simulateRoute: Boolean = true
var options: NavigationLauncherOptions = NavigationLauncherOptions.builder().directionsRoute(currentRoute).shouldSimulateRoute(simulateRoute).build()
NavigationLauncher.startNavigation(activity, options)
}
else {
// No result for your request were found.
Log.d(TAG, "onResponse: No result found");
}
}
override fun onFailure(call : Call<GeocodingResponse>, throwable: Throwable) {
throwable.printStackTrace()
}
})
})
}
}
But when i want to use the variable currentroute who is set in getRoute function in NavigationLauncherOptions , he say to me that the variable is null.
So i think the callback don't end and the function Navagation launch before it end.
here is the function getRoute
private fun getRoute(origin : Point, destination : Point) {
Log.d("test", "testgetroute")
NavigationRoute.builder(this.context)
.accessToken(Mapbox.getAccessToken()!!)
.origin(origin)
.destination(destination)
.build()
.getRoute( object: Callback<DirectionsResponse> {
override fun onResponse(call : Call<DirectionsResponse>, response : Response<DirectionsResponse>) {
// You can get the generic HTTP info about the response
Log.d("test", "Response code: " + response.code())
if (response.body() == null) {
Log.e("test", "No routes found, make sure you set the right user and access token.")
return
} else if (response.body()!!.routes().size < 1) {
Log.e("test", "No routes found")
return
}
currentRoute = response.body()!!.routes()[0]
Log.d("test", currentRoute.toString() + " current route is set")
// Draw the route on the map
if (navigationMapRoute != null) {
navigationMapRoute!!.removeRoute()
} else {
navigationMapRoute = NavigationMapRoute(null, mapView!!, map!!, R.style.NavigationMapRoute)
}
navigationMapRoute!!.addRoute(currentRoute)
}
override fun onFailure(call : Call<DirectionsResponse>, throwable: Throwable) {
Log.e("test", "Error: " + throwable.message)
}
})
}
i search a lot and i'm new in kotlin , but did someone figure it out the problem ?
because i can't find the solutions
Thanks for your answer and your time
08-31 00:15:58.965 25377-25377/com.example.parky.parky_android D/test: enter here
08-31 00:15:58.971 25377-25377/com.example.parky.parky_android D/test: 1
2
08-31 00:15:59.129 25377-25377/com.example.parky.parky_android D/test: 3
08-31 00:15:59.131 25377-25377/com.example.parky.parky_android D/test: onResponse: Point{type=Point, bbox=null, coordinates=[-71.269566, 46.779652]}
08-31 00:15:59.179 25377-25377/com.example.parky.parky_android D/test: testgetroute
08-31 00:15:59.230 25377-25377/com.example.parky.parky_android D/AndroidRuntime: Shutting down VM
08-31 00:15:59.235 25377-25377/com.example.parky.parky_android E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.parky.parky_android, PID: 25377
java.lang.NullPointerException: Null directionsRoute
at com.mapbox.services.android.navigation.ui.v5.AutoValue_NavigationLauncherOptions$Builder.directionsRoute(AutoValue_NavigationLauncherOptions.java:151)
at ItemFourFragment$onViewCreated$2$1$1.onResponse(ItemFourFragment.kt:320)
at retrofit2.ExecutorCallAdapterFactory$ExecutorCallbackCall$1$1.run(ExecutorCallAdapterFactory.java:70)
at android.os.Handler.handleCallback(Handler.java:751)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:154)
at android.app.ActivityThread.main(ActivityThread.java:6682)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1520)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1410)
08-31 00:15:59.454 25377-25382/com.example.parky.parky_android I/art: Compiler allocated 8MB to compile retrofit2.ParameterHandler retrofit2.ServiceMethod$Builder.parseParameterAnnotation(int, java.lang.reflect.Type, java.lang.annotation.Annotation[], java.lang.annotation.Annotation)