How can MediaPlayer be stopped when the App is backgrounded? I see that the MediaPlayer stops when screen rotates. But the MediaPlayed keeps playing while it is backgrounded and a different App is run.
val current = LocalContext.current
val player1: MediaPlayer = remember {MediaPlayer.create(current, R.raw.music1)}
DisposableEffect(Unit) {
player1.isLooping = true
player1.start()
onDispose {
player1.stop()
}
}
I tried adding MediaPlayer in a class which was life cycle aware. This can be controlled from compose. But the functions "onPause" and "onStop" do not stop the MediaPlayer.
class ExampleClass(application: Application) : AndroidViewModel(application),
DefaultLifecycleObserver {
private var player: MediaPlayer? = null
fun toggleOn(application: Application) {
if (player == null) {
player = MediaPlayer.create(application, R.raw.music1)
}
player!!.start()
}
fun toggleOff() {
player!!.pause()
}
override fun onPause(owner: LifecycleOwner) {
super.onPause(owner)
toggleOff()
}
override fun onStop(owner: LifecycleOwner) {
super.onStop(owner)
toggleOff()
}
}
Use LifecycleOwner and LauchedEffect
val lifecycleOwner = LocalLifecycleOwner.current
DisposableEffect(key1 = lifecycleOwner) {
val observer = LifecycleEventObserver { _, event ->
if (event == Lifecycle.Event.ON_RESUME) {
player.start()
} else if (event == Lifecycle.Event.ON_STOP) {
player.stop()
}
}
// Add the observer to the lifecycle
lifecycleOwner.lifecycle.addObserver(observer)
// When the effect leaves the Composition, remove the observer
onDispose {
lifecycleOwner.lifecycle.removeObserver(observer)
}
}
A similar sample: here
Related
I don't understand to force recompose is actually a good idea or not. I cannot find a way to solve the problem. I have value of mutableStateOf it's type of Boolean and I am calling a function onResume to check that Boolean value in my viewmodel.
BloodPressurePairViewModel.kt
class BloodPressurePairViewModel : BaseViewModel() {
var isBluetoothEnabled by mutableStateOf(false)
private set
fun setBluetoothEnable(newValue: Boolean) {
isBluetoothEnabled = newValue
}
fun handleBluetoothScanState(bluetoothAdapter: BluetoothAdapter) {
if (isBluetoothEnabled) {
getPairedDevice(bluetoothAdapter)
startScan(bluetoothAdapter)
} else {
cancelTimeWarning()
stopScan(bluetoothAdapter)
}
}
fun cancelTimeWarning() {
// more logic here
}
// more function here I am not adding.
}
BluetoothConnectionContentStateful
fun BluetoothConnectionContentStateful(
context: Context = LocalContext.current,
viewModel: BloodPressurePairViewModel = getViewModel(),
) {
val activity = context as ComponentActivity
val rememberPairScreenState = rememberConnectionScreenState(context, viewModel)
DisposableEffect(key1 = lifecycleOwner) {
val lifeCycleEventObserver = LifecycleEventObserver { _, event ->
when (event) {
Lifecycle.Event.ON_RESUME -> {
viewModel.handleBluetoothScanState(rememberPairScreenState.bluetoothAdapter)
rememberPairScreenState.bluetoothStatusRegister.apply {
context.registerReceiver(this,IntentFilter(BluetoothAdapter.ACTION_STATE_CHANGED))
},
Lifecycle.Event.ON_PAUSE -> {
rememberPairScreenState.unRegisterBluetoothStatusReceiver()
}
else -> {}
}
}
lifecycleOwner.lifecycle.addObserver(lifeCycleEventObserver)
onDispose {
lifecycleOwner.lifecycle.removeObserver(lifeCycleEventObserver)
}
}
rememberConnectionScreenState
#Composable
fun rememberConnectionScreenState(context: Context, viewModel: BloodPressurePairViewModel) = remember {
ConnectionScreenState(context, viewModel)
}
class ConnectionScreenState(context: Context, private val viewModel: BloodPressurePairViewModel) {
private val bluetoothManager: BluetoothManager by lazy { activity.getSystemService(BluetoothManager::class.java) }
val bluetoothAdapter: BluetoothAdapter by lazy { bluetoothManager.adapter }
var bluetoothStatusRegister = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == BluetoothAdapter.ACTION_STATE_CHANGED) {
when (intent.getIntExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.ERROR)) {
BluetoothAdapter.STATE_OFF -> viewModel.setBluetoothEnable(false)
BluetoothAdapter.STATE_ON -> viewModel.setBluetoothEnable(true)
}
}
}
}
fun unRegisterBluetoothStatusReceiver() {
activity.unregisterReceiver(bluetoothStatusRegister)
}
}
When bluetoothStatusRegister is trigger and BluetoothAdapter.STATE_OFF is true and then I called viewModel.setBluetoothEnable(false) the isBluetoothEnabled is false. Then I don't understand how to recall the function of handleBluetoothScanState(bluetoothAdapter). So is this good idea to recompose the function? Thanks
Generally it is not what you want to do. Especially because your recomposition can lead to recompositions of another composables which you don't need. In addition, as z.y. mentioned, this can lead to exception.
What you should do is to tie your recomposition to bluetooth state, which you will save in your ViewModel. Then you can use LaunchedEffect to invoke some actions on state change.
val bluetoothState by remember { mutableStateOf (viewModel.bluetoothState) }
// Please note that design of type you want your state to be is up to you
LaunchedEffect(bluetoothState) {
// Perform any manipulation you want on bluetooth state change
}
I have the following:
interface CartRepository {
fun getCart(): Flow<CartState>
}
interface ProductRepository {
fun getProductByEan(ean: String): Flow<Either<ServerError, Product?>>
}
class ScanViewModel(
private val productRepository: ProductRepository,
private val cartRepository: CartRepository
) :
BaseViewModel<ScanUiState>(Initial) {
fun fetchProduct(ean: String) = viewModelScope.launch {
setState(Loading)
productRepository
.getProductByEan(ean)
.combine(cartRepository.getCart(), combineToGridItem())
.collect { result ->
when (result) {
is Either.Left -> {
sendEvent(Error(R.string.error_barcode_product_not_found, null))
setState(Initial)
}
is Either.Right -> {
setState(ProductUpdated(result.right))
}
}
}
}
}
When a user scans a barcode fetchProduct is being called. Every time a new coroutine is being set up. And after a while, there are many running in the background and the combine is triggered when the cart state is updated on all of them, which can cause errors.
I want to cancel all old coroutines and only have the latest call running and update on cart change.
I know I can do the following by saving the job and canceling it before starting a new one. But is this really the way to go? Seems like I'm missing something.
var searchJob: Job? = null
private fun processImage(frame: Frame) {
barcodeScanner.process(frame.toInputImage(this))
.addOnSuccessListener { barcodes ->
barcodes.firstOrNull()?.rawValue?.let { ean ->
searchJob?.cancel()
searchJob = viewModel.fetchProduct(ean)
}
}
.addOnFailureListener {
Timber.e(it)
messageMaker.showError(
binding.root,
getString(R.string.unknown_error)
)
}
}
I could also have a MutableSharedFlow in my ViewModel to make sure the UI only react to the last product the user has been fetching:
private val productFlow = MutableSharedFlow<Either<ServerError, Product?>>(replay = 1)
init {
viewModelScope.launch {
productFlow.combine(
mycroftRepository.getCart(),
combineToGridItem()
).collect { result ->
when (result) {
is Either.Right -> {
setState(ProductUpdated(result.right))
}
else -> {
sendEvent(Error(R.string.error_barcode_product_not_found, null))
setState(Initial)
}
}
}
}
}
fun fetchProduct(ean: String) = viewModelScope.launch {
setState(Loading)
repository.getProductByEan(ean).collect { result ->
productFlow.emit(result)
}
}
What's considered best practice handling this scenario?
I can't think of a simpler pattern for cancelling any previous Job when starting a new one.
If you're concerned about losing your stored job reference on screen rotation (you probably won't since Fragment instances are typically reused on rotation), you can move Job storage and cancellation into the ViewModel:
private var fetchProductJob: Job? = null
fun fetchProduct(ean: String) {
fetchProductJob?.cancel()
fetchProductJob = viewModelScope.launch {
//...
}
}
If you're repeatedly using this pattern, you could create a helper class like this. Not sure if there's a better way.
class SingleJobPipe(val scope: CoroutineScope) {
private var job: Job? = null
fun launch(
context: CoroutineContext = EmptyCoroutineContext,
start: CoroutineStart = CoroutineStart.DEFAULT,
block: suspend CoroutineScope.() -> Unit
): Job = synchronized(this) {
job?.cancel()
scope.launch(context, start, block).also { job = it }
}
}
// ...
private val fetchProductPipe = SingleJobPipe(viewModelScope)
fun fetchProduct(ean: String) = fetchProductPipe.launch {
//...
}
I'm on creating a simple music player with Exoplayer in Android Kotlin. (Playing local MP3 in storage)
The problem is that the playback music is stopped outside the app and if the mobile turns on sleep mode.
So, I tried to implement the Foreground service, but it didn't work.
Below is my code without the part I tried to implement the Foreground service.
Please, let me know how to resolve this issue or how to correctly implement foreground service.
class AudioviewActivity : AppCompatActivity() {
private var player: SimpleExoPlayer? = null
private var playbackPosition = 0L
private var currentWindow = 0
private var playWhenReady = false
override fun onBackPressed() {
super.onBackPressed()
player!!.stop()
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_audioview)
}
private fun initializePlayer() {
if (player == null) {
player = ExoPlayerFactory.newSimpleInstance(this)
val userAgent = Util.getUserAgent(this, "Mymusicplayer")
val mediaSource = ExtractorMediaSource(
Uri.parse("asset:///trackone.mp3"),
DefaultDataSourceFactory(this, userAgent), DefaultExtractorsFactory(),
null, null)
player!!.prepare(mediaSource)
player!!.seekTo(currentWindow, playbackPosition)
player!!.playWhenReady = playWhenReady
player!!.repeatMode = SimpleExoPlayer.REPEAT_MODE_ONE
}
}
private fun releasePlayer() {
player?.let {
playbackPosition = it.currentPosition
currentWindow = it.currentWindowIndex
playWhenReady = it.playWhenReady
it.release()
player = null
}
}
override fun onResume() {
super.onResume()
initializePlayer()
}
override fun onRestart() {
super.onRestart()
initializePlayer()
}
override fun onStop() {
super.onStop()
releasePlayer()
}
}
Since you are releasing player in Onstop , So when your app goes in background then Onstop is being called and you are releasing player , So dont release player in OnStop(), remove that part of the code.
I want to implement the new In-App Update library in my app, but I've noticed that it trigger a memory leak in my activity when it's recreated/rotated.
Here's the only detail I have from LeakCanary:
Obviously, I've nothing if I remove the code from the In-App Update lib especially the addOnSuccessListener :
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
updateInfo.value = appUpdateInfo
updateAvailable.value = true
}else{
updateInfo.value = null
updateAvailable.value = false
}
}
According to this post, I have first used some LiveData, but the problem was the same, so I used a full class to handle the callback, with LiveData :
My Service class :
class AppUpdateService {
val updateAvailable: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
val updateDownloaded: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
val updateInfo: MutableLiveData<AppUpdateInfo> by lazy { MutableLiveData<AppUpdateInfo>() }
fun checkForUpdate(appUpdateManager: AppUpdateManager){
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
updateInfo.value = appUpdateInfo
updateAvailable.value = true
}else{
updateInfo.value = null
updateAvailable.value = false
}
}
}
fun checkUpdateOnResume(appUpdateManager: AppUpdateManager){
appUpdateManager.appUpdateInfo.addOnSuccessListener {
updateDownloaded.value = (it.installStatus() == InstallStatus.DOWNLOADED)
}
}
}
My Activity simplified :
class MainActivity : BaseActivity(), InstallStateUpdatedListener {
override fun contentViewID(): Int { return R.layout.activity_main }
private val UPDATE_REQUEST_CODE = 8000
private lateinit var appUpdateManager : AppUpdateManager
private val appUpdateService = AppUpdateService()
override fun onStateUpdate(state: InstallState?) {
if(state?.installStatus() == InstallStatus.DOWNLOADED){ notifyUser() }
}
// Called in the onCreate()
override fun setupView(){
appUpdateManager = AppUpdateManagerFactory.create(this)
appUpdateManager.registerListener(this)
setupAppUpdateServiceObservers()
// Check for Update
appUpdateService.checkForUpdate(appUpdateManager)
}
private fun setupAppUpdateServiceObservers(){
appUpdateService.updateAvailable.observe(this, Observer {
if (it)
requestUpdate(appUpdateService.updateInfo.value)
})
appUpdateService.updateDownloaded.observe(this, Observer {
if (it)
notifyUser()
})
}
private fun requestUpdate(appUpdateInfo: AppUpdateInfo?){
appUpdateManager.startUpdateFlowForResult(appUpdateInfo, AppUpdateType.FLEXIBLE, this, UPDATE_REQUEST_CODE)
}
private fun notifyUser(){
showSnackbar(getString(R.string.updated_downloaded), getString(R.string.restart)) {
appUpdateManager.completeUpdate()
appUpdateManager.unregisterListener(this)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == UPDATE_REQUEST_CODE) {
if (resultCode != RESULT_OK) {
Timber.d("Update flow failed! Result code: $resultCode")
}
}
}
override fun onDestroy() {
appUpdateManager.unregisterListener(this)
super.onDestroy()
}
override fun onResume() {
super.onResume()
appUpdateService.checkUpdateOnResume(appUpdateManager)
}
}
I don't really understand how to avoid the memory leak as the appUpdateManager has to be created with the context of the activity, and it looks to be the thing that causes the memory leak with the callback.
Does someone already implement it without having this issue?
Using weak reference to the context will probably solve your memory leak problem. Write this in your activity:
WeakReference<Context> contextWeakReference = new WeakReference<Context>(this);
Context context = contextWeakReference.get();
if (context != null) {
// Register using context here
}
There are lots of good articles on WeakReference, Garbage Collection and Memory Leaks to read more on the subject.
Also, onDestroy() is not guaranteed to be called. When you start another Activity, onPause() and onStop() method called instead of onDestroy().
The onDestroy() calls when you hit back button or call finish() method. So, unregister Listener in onPause() or onStop(). If you unregister in onDestroy() method, it might cause a memory leak.
Another idea is that since AppUpdateService class in not a subclass of ViewModel, it is not lifecycle aware. I'm not sure, but, you might need to remove observers in onstop/onDestroy of the activity and add them in onResume. (observers has a strong reference to the LifecycleOwner, here the activiy) To do that you need to define observers to be able to remove them later. Something like:
MutableLiveData<Boolean> someData = new MutableLiveData<>;
and then in onResume:
someData = appUpdateService.updateAvailable;
someData.observe()
and in onStop:
someData.removeObservers()
It's just a guess, but, I hope it would help somehow.
Thanks to #Sina Farahzadi I searched and try a lot of things and figured that the problem was the appUpdateManager.appUdateInfo call with the Task object.
The way I found to solve the memory leak is to use the applicationContext instead of the context of the activity. I'm not sure it's the best solution, but it's the one I've found for now. I've exported all in my service class so here's my code :
AppUpdateService.kt :
class AppUpdateService : InstallStateUpdatedListener {
val updateAvailable: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
val updateDownloaded: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
val notifyUser: MutableLiveData<Boolean> by lazy { MutableLiveData<Boolean>() }
val updateInfo: MutableLiveData<AppUpdateInfo> by lazy { MutableLiveData<AppUpdateInfo>() }
private var appUpdateManager : AppUpdateManager? = null
private var appUpdateInfoTask: Task<AppUpdateInfo>? = null
override fun onStateUpdate(state: InstallState?) {
notifyUser.value = (state?.installStatus() == InstallStatus.DOWNLOADED)
}
fun setupAppUpdateManager(context: Context){
appUpdateManager = AppUpdateManagerFactory.create(context)
appUpdateManager?.registerListener(this)
checkForUpdate()
}
fun onStopCalled(){
appUpdateManager?.unregisterListener(this)
appUpdateInfoTask = null
appUpdateManager = null
}
fun checkForUpdate(){
appUpdateInfoTask = appUpdateManager?.appUpdateInfo
appUpdateInfoTask?.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)){
updateInfo.value = appUpdateInfo
updateAvailable.value = true
}else{
updateInfo.value = null
updateAvailable.value = false
}
}
}
fun startUpdate(activity: Activity, code: Int){
appUpdateManager?.startUpdateFlowForResult(updateInfo.value, AppUpdateType.FLEXIBLE, activity, code)
}
fun updateComplete(){
appUpdateManager?.completeUpdate()
appUpdateManager?.unregisterListener(this)
}
fun checkUpdateOnResume(){
appUpdateManager?.appUpdateInfo?.addOnSuccessListener {
updateDownloaded.value = (it.installStatus() == InstallStatus.DOWNLOADED)
}
}
}
MainActivity simplified :
class MainActivity : BaseActivity(){
override fun contentViewID(): Int { return R.layout.activity_main }
private val UPDATE_REQUEST_CODE = 8000
private var appUpdateService: AppUpdateService? = AppUpdateService()
/**
* Setup the view of the activity (navigation and menus)
*/
override fun setupView(){
val contextWeakReference = WeakReference<Context>(applicationContext)
contextWeakReference.get()?.let {weakContext ->
appUpdateService?.setupAppUpdateManager(weakContext)
}
}
private fun setupAppUpdateServiceObservers(){
appUpdateService?.updateAvailable?.observe(this, Observer {
if (it)
requestUpdate()
})
appUpdateService?.updateDownloaded?.observe(this, Observer {
if (it)
notifyUser()
})
appUpdateService?.notifyUser?.observe(this, Observer {
if (it)
notifyUser()
})
}
private fun removeAppUpdateServiceObservers(){
appUpdateService?.updateAvailable?.removeObservers(this)
appUpdateService?.updateDownloaded?.removeObservers(this)
appUpdateService?.notifyUser?.removeObservers(this)
}
private fun requestUpdate(){
appUpdateService?.startUpdate(this, UPDATE_REQUEST_CODE)
}
private fun notifyUser(){
showSnackbar(getString(R.string.updated_downloaded), getString(R.string.restart)) {
appUpdateService?.updateComplete()
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == UPDATE_REQUEST_CODE) {
if (resultCode != RESULT_OK) {
Timber.d("Update flow failed! Result code: $resultCode")
}
}
}
override fun onStop() {
appUpdateService?.onStopCalled()
removeAppUpdateServiceObservers()
appUpdateService = null
super.onStop()
}
override fun onResume() {
super.onResume()
setupAppUpdateServiceObservers()
appUpdateService?.checkUpdateOnResume()
}
}
For now, I will keep it that way and continue to search for another way to do it.
Let me know if someone has a better way to do it.
Use this helper class:
class GoogleUpdater(activity: FragmentActivity) : LifecycleObserver {
private val appUpdateManager = AppUpdateManagerFactory.create(activity)
private var installStateUpdatedListener: InstallStateUpdatedListener? = null
private var wra = WeakReference(activity)
private val activity get() = wra.get()
init {
activity.lifecycle.addObserver(this)
}
fun checkUpdate() {
fun showCompleteUpdateDialog() {
activity?.let { activity ->
if (!activity.isFinishing)
AlertDialog.Builder(activity)
.setTitle(R.string.notification)
.setMessage(R.string.restart_to_complete_update)
.setIcon(ContextCompat.getDrawable(activity, R.drawable.ic_notification)
?.apply {
mutate()
alpha = 127
})
.setPositiveButton(R.string.yes) { _: DialogInterface?, _: Int -> appUpdateManager.completeUpdate() }
.setNegativeButton(R.string.no, null)
.create()
.apply { setCanceledOnTouchOutside(false) }
.show()
}
}
installStateUpdatedListener = object : InstallStateUpdatedListener {
override fun onStateUpdate(state: InstallState) {
if (state.installStatus() == InstallStatus.DOWNLOADED)
showCompleteUpdateDialog()
else if (state.installStatus() == InstallStatus.INSTALLED)
appUpdateManager.unregisterListener(this)
}
}.also { appUpdateManager.registerListener(it) }
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
val clientVersionStalenessDays = appUpdateInfo.clientVersionStalenessDays()
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(AppUpdateType.FLEXIBLE)
&& clientVersionStalenessDays != null
&& clientVersionStalenessDays >= DAYS_FOR_FLEXIBLE_UPDATE) {
try {
activity?.let { activity ->
if (!activity.isFinishing)
appUpdateManager.startUpdateFlowForResult(
appUpdateInfo,
AppUpdateType.FLEXIBLE,
activity,
REQUEST_CODE_APP_UPDATE)
}
} catch (e: SendIntentException) {
FirebaseCrashlytics.getInstance().recordException(e)
}
} else if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED)
showCompleteUpdateDialog()
}
}
#OnLifecycleEvent(Lifecycle.Event.ON_STOP)
private fun onStop() {
installStateUpdatedListener?.let { appUpdateManager.unregisterListener(it) }
}
companion object {
const val REQUEST_CODE_APP_UPDATE = 11
const val DAYS_FOR_FLEXIBLE_UPDATE = 1
}
}
In Activity:
GoogleUpdater(this).apply { checkUpdate() }
I am using 'RxJava binding APIs for Android UI widgets' to trigger click events on buttons or textview.
PFB code(Edited) that using to trigger the event
class BookAgentActivity : BaseActivity(), BookAgentView {
#Inject
#field:Named("activity")
lateinit var compositeDisposable: CompositeDisposable
#Inject
lateinit var bookAgentViewModelFactory: BookAgentViewModelFactory
private lateinit var bookAgentViewModel: BookAgentViewModel
private lateinit var cityLocalityJson: CityLocalitiesMO
override fun getLayoutId(): Int {
return R.layout.activity_book_agent
}
override fun initializeDagger() {
IleApplication.getRoomComponent().inject(this)
}
override fun initializeViewModel() {
bookAgentViewModel = ViewModelProviders.of(this, bookAgentViewModelFactory).get(BookAgentViewModel::class.java)
bookAgentViewModel.setView(this)
}
override fun setUpUi() {
gradientStatusBar()
cityLocalityJson = appPreferences.cityLocalitiesJson
compositeDisposable.add(bookAgentCityLocationTV.clicks().observeOn(AndroidSchedulers.mainThread()).subscribe {
startActivity(Intent(this, AreaLocalitiesActivity::class.java)
.putExtra(AppConstants.COMING_FROM_AGENT_KEY, true))
})
compositeDisposable.add(filtersButton.clicks().observeOn(AndroidSchedulers.mainThread()).subscribe {
startActivity(Intent(this, FiltersMainActivity::class.java)
.putExtra(AppConstants.FILTER_TYPE_KEY, AppConstants.AGENT_FILTER))
})
compositeDisposable.add(searchAgentsButton.clicks()
.subscribe { startActivity(Intent(this#BookAgentActivity, SearchAgentActivity::class.java)) })
}
override fun onSuccess(response: Any) {
if (response is AgentsDetailAPIResponse) {
response.let {
val agentDetailsList = it.data
if (agentDetailsList != null && agentDetailsList.size > 0) {
updateAgentPinsOnMap(agentDetailsList)
}
}
}
}
override fun onDestroy() {
super.onDestroy()
compositeDisposable.clear()
}
}
:) Above code works fine for the first time
:( But after coming back from BookAgentActivity (onBackPressed())
Click events are not working for searchAgentsButton as well as for other views too.
Have tried including combinations of other lines of code like below:
.subscribeOn(Schedulers.newThread())
.observeOn(AndroidSchedulers.mainThread())
.share()
But none of the above things are working.