No Asset Audio file is Playing in Exoplayer, Kotlin - java

there is no clear Documentation for playing audio
assets files with the latest Exoplayer
wanted to play multiple audio files simultaneously
and in loop
like Soft Sound
what is the correct way to omit deprecated classes of exoplayer and play audio
build.gradle
// ExoPlayer
api "com.google.android.exoplayer:exoplayer-core:2.18.1"
my
PlayerService.kt file
class PlayerService : Service() {
private val notificationID = 132
private val tag = "Player"
// called when sound is started or stopped
var playerChangeListener: (() -> Unit)? = null
inner class PlayerBinder : Binder() {
fun getService(): PlayerService {
return this#PlayerService
}
}
private val playerBinder = PlayerBinder()
override fun onCreate() {
// load each player into the map
Sound.values().forEach {
exoPlayers[it] = initializeExoPlayer(it.file)
}
}
enum class Sound(val file: String) {
RAIN("rain_sound.ogg"),
...
TABLA("tabla_sound.ogg")
}
private val exoPlayers = mutableMapOf<Sound, ExoPlayer>()
private fun initializeExoPlayer(soundFile: String): ExoPlayer {
// create the player
val trackSelector = DefaultTrackSelector(this)
val exoPlayer = ExoPlayer.Builder(this).setTrackSelector(trackSelector).build()
// load the media source
val dataSource = DefaultDataSourceFactory(this,
Util.getUserAgent(this, this.getString(R.string.app_name)))
val mediaSource = ProgressiveMediaSource.Factory(dataSource)
.createMediaSource(MediaItem.fromUri(Uri.parse("asset:///$soundFile")))
// load the media
Log.d("MAIN", "loading $soundFile")
exoPlayer.setMediaSource(mediaSource)
exoPlayer.prepare()
exoPlayer.play()
// loop indefinitely
exoPlayer.repeatMode = Player.REPEAT_MODE_ALL
return exoPlayer
}
override fun onUnbind(intent: Intent?): Boolean {
// don't continue if we're not playing any sound and the main activity exits
playerChangeListener = null
if (!isPlaying()) {
stopSelf()
Log.d(tag, "stopping service")
}
return super.onUnbind(intent)
}
override fun onBind(intent: Intent?): IBinder {
// return the binding interface
return playerBinder
}
fun startForeground() {
// move to the foreground if we are playing sound
if (isPlaying()) {
....
}
}
fun stopForeground(){...}
fun stopPlaying(){...}
fun isPlaying(): Boolean{...}
fun setVolume(sound: Sound, volume: Float){...}
fun toggleSound(sound: Sound){...}
}
Any help would be highly appreciated.

Related

How to run the app in the background and call API on the broadcast receiver change?

Overview:
Hey there,
I am making an App which is tracking the calls on the phone. The way I want to do this when A call is incoming or outgoing I am making an API request and storing the information of the call. Once the call is ended I am again making API request for marking the call end. So basically I am making 2 API calls per call. I want to perform this operation even the app is closed.
I am using phone_plus package from pub.dev for tracking and getting the call information. When I looked into the package it's using Method Channel Internally for doing this operation. The plugin works perfectly when the app is closed also. It's giving the call status and the details I want
Problem : The problem is when App is closed or phone is locked I am not able to make the API request.
The following error is shown :
W/FlutterJNI: Tried to send a platform message to Flutter, but FlutterJNI was detached from native C++. Could not send. Channel: com.morabaa.phone_plus. Response ID: 15
I have two important questions.
If the app is running in the background do I compulsory need to show the notification that app is running in the background
How can I make API calls in the background. I have tried with flutter_background_services package but the documentation is not clear. I didn't understand how should I pass my arguments to the background.
Can I directly clone the package code and paste my API calling function in native code. If so How can I do that? The package is written in Kotlin.
here is the code of the package :
import android.annotation.SuppressLint
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.telephony.TelephonyManager
import java.net.URI
import java.net.URLEncoder
import androidx.annotation.NonNull
import io.flutter.Log
import io.flutter.embedding.engine.plugins.FlutterPlugin
import io.flutter.plugin.common.MethodCall
import io.flutter.plugin.common.MethodChannel
import io.flutter.plugin.common.MethodChannel.MethodCallHandler
import io.flutter.plugin.common.MethodChannel.Result
import java.util.*
import kotlin.collections.ArrayList
/** PhonePlusPlugin */
class PhonePlusPlugin: FlutterPlugin, MethodCallHandler, BroadcastReceiver() {
private lateinit var context: Context
companion object {
lateinit var methodChannel: MethodChannel
private var lastState = TelephonyManager.EXTRA_STATE_IDLE
private var isIncoming = false
}
private fun setup(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
val plugin = PhonePlusPlugin()
methodChannel = MethodChannel(binding.binaryMessenger, "com.morabaa.phone_plus")
plugin.context = binding.applicationContext
methodChannel.setMethodCallHandler(plugin)
}
override fun onAttachedToEngine(#NonNull flutterPluginBinding: FlutterPlugin.FlutterPluginBinding) {
setup(binding = flutterPluginBinding)
}
override fun onDetachedFromEngine(#NonNull binding: FlutterPlugin.FlutterPluginBinding) {
methodChannel.setMethodCallHandler(null)
}
override fun onMethodCall(call: MethodCall, result: Result) {
if (call.method.equals("phoneTest.incomingCallReceived")) {
Log.d("CallObserver", "phoneIncoming Test implementation")
// TODO: test mode with seconds to wait as parameter
} else {
result.notImplemented()
}
}
private val arguments: HashMap<String, Any> = HashMap()
#SuppressLint("UnsafeProtectedBroadcastReceiver")
override fun onReceive(context: Context?, intent: Intent) {
Log.d("CallObserver", "CallReceiver is starting ....")
var keyList: List<String?> = ArrayList()
val bundle = intent.extras
if (bundle != null) {
keyList = ArrayList(bundle.keySet())
Log.e("CallObserver", "keys : $keyList")
}
if (keyList.contains("incoming_number")) {
val phoneState = intent.getStringExtra(TelephonyManager.EXTRA_STATE)
val phoneIncomingNumber = intent.getStringExtra(TelephonyManager.EXTRA_INCOMING_NUMBER)
val phoneOutgoingNumber = intent.getStringExtra(Intent.EXTRA_PHONE_NUMBER)
val phoneNumber = phoneOutgoingNumber ?: (phoneIncomingNumber
?: "")
if (phoneState != null && phoneNumber != "") {
if (lastState == phoneState) {
//No change, debounce extras
return
}
Log.d("CallObserver", "State Changed in here>>>>>> $phoneState")
arguments["Date"] = System.currentTimeMillis()
if(phoneNumber!="") arguments["Number"] = phoneNumber
if (TelephonyManager.EXTRA_STATE_RINGING == phoneState) {
isIncoming = true
//
lastState = TelephonyManager.EXTRA_STATE_RINGING
onIncomingCallReceived()
} else if (TelephonyManager.EXTRA_STATE_IDLE == phoneState) {
if (lastState == TelephonyManager.EXTRA_STATE_RINGING) {
//
lastState = TelephonyManager.EXTRA_STATE_IDLE
onMissedCall()
} else {
if (isIncoming) {
//
lastState = TelephonyManager.EXTRA_STATE_IDLE
onIncomingCallEnded()
} else {
//
lastState = TelephonyManager.EXTRA_STATE_IDLE
onOutgoingCallEnded()
}
}
} else if (TelephonyManager.EXTRA_STATE_OFFHOOK == phoneState) {
isIncoming = lastState.equals(TelephonyManager.EXTRA_STATE_RINGING)
//
lastState = TelephonyManager.EXTRA_STATE_OFFHOOK
if(isIncoming) {
onIncomingCallAnswered()
} else {
onOutgoingCallStarted()
}
}
}
}
}
private fun onIncomingCallReceived() {
Log.d("CallObserver", "onIncomingCallReceived : number is : ${arguments["Number"]}")
methodChannel.invokeMethod("onIncomingCallReceived", arguments)
}
private fun onIncomingCallAnswered() {
Log.d("CallObserver", "onIncomingCallAnswered : number is : ${arguments["Number"]}")
methodChannel.invokeMethod("onIncomingCallAnswered", arguments)
}
private fun onIncomingCallEnded() {
Log.d("CallObserver", "onIncomingCallEnded : number is : ${arguments["Number"]}")
methodChannel.invokeMethod("onIncomingCallEnded", arguments)
}
private fun onOutgoingCallStarted() {
Log.d("CallObserver", "onOutgoingCallStarted : number is : ${arguments["Number"]}")
methodChannel.invokeMethod("onOutgoingCallStarted", arguments)
}
private fun onOutgoingCallEnded() {
Log.d("CallObserver", "onOutgoingCallEnded : number is : ${arguments["Number"]}")
methodChannel.invokeMethod("onOutgoingCallEnded", arguments)
}
private fun onMissedCall() {
Log.d("CallObserver", "onMissedCall : number is : ${arguments["Number"]}")
methodChannel.invokeMethod("onMissedCall", arguments)
}
}

Keep App Android Running after minimize / block phone [duplicate]

This question already has answers here:
Android - implementing startForeground for a service?
(11 answers)
Closed 11 months ago.
I've createde a simple app (following this link How to Play Sound On Button Click in Android Studio Java 2021 ), and everything works fine, but, i need this app can still playing the sound after minimize then or block the phone (is a white noise app from help on sleep for my baby daughter, and i need that keep playing all night).
Is there any way or setting that allows the sound not to stop after minimizing or blocking the application?
Thank you for enveryone that could help me and my baby :)
If you add the ability to run your application in the background, it will solve your problem. Resources you can review:
https://developer.android.com/guide/components/services
https://developer.android.com/guide/background/threading
Basic Example :
AndroidManifest.xml :
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<application
*
*
*
*
<service android:name=".ui.main.AudioPlayerService"/>
</application>
AudioPlayerService :
private const val PLAYBACK_CHANNEL_ID = "blabla"
private const val PLAYBACK_NOTIFICATION_ID = 1
class AudioPlayerService : Service() {
private var player: SimpleExoPlayer? = null
private var playerNotificationManager: PlayerNotificationManager? = null
private val mediaItem: MediaItem = MediaItem.fromUri(ApiInterface.Channel24LiveURL)
/** Classes to be connected to the service with the
service
*reference providing the link between
*/
private val mBinder = AudioServiceBinder()
/**
* Data that the service will share to other classes. */
inner class AudioServiceBinder : Binder() {
val service
get() = this#AudioPlayerService
val player
get() = this#AudioPlayerService.player
}
override fun onBind(intent: Intent?): IBinder? {
return mBinder
}
/**
* Service start part
*/
override fun onCreate() {
super.onCreate()
// init player
player = SimpleExoPlayer.Builder(this)
.build().apply {
setMediaItem(mediaItem)
playWhenReady = true
prepare()
}
playerNotificationManager = PlayerNotificationManager.createWithNotificationChannel(
applicationContext,
PLAYBACK_CHANNEL_ID,
R.string.playback_channel_name,
R.string.playback_channel_desc,
PLAYBACK_NOTIFICATION_ID,
object : PlayerNotificationManager.MediaDescriptionAdapter {
override fun getCurrentContentTitle(player: Player): CharSequence {
return getString(R.string.def_playback_title)
}
override fun createCurrentContentIntent(player: Player): PendingIntent? {
return PendingIntent.getActivity(
applicationContext,
0,
Intent(applicationContext, MainActivity::class.java),
PendingIntent.FLAG_CANCEL_CURRENT
)
}
override fun getCurrentContentText(player: Player): CharSequence? {
return null
}
override fun getCurrentLargeIcon(
player: Player,
callback: PlayerNotificationManager.BitmapCallback
): Bitmap? {
return getBitmapFromVectorDrawable(applicationContext, R.mipmap.ic_launcher)
}
}, object : PlayerNotificationManager.NotificationListener {
override fun onNotificationCancelled(notificationId: Int, dismissedByUser: Boolean) {
stopSelf()
}
override fun onNotificationPosted(notificationId: Int, notification: Notification, ongoing: Boolean) {
if (ongoing) {
// Make sure the service will not get destroyed while playing media.
startForeground(notificationId, notification)
} else {
// Make notification cancellable.
stopForeground(false)
}
}
}
).apply {
// previous and next actions.
setUseNavigationActions(true)
setPlayer(player)
}
}
#MainThread
private fun getBitmapFromVectorDrawable(
context: Context,
#Suppress("SameParameterValue") #DrawableRes drawableId: Int
): Bitmap? {
return ContextCompat.getDrawable(context, drawableId)?.let {
val drawable = DrawableCompat.wrap(it).mutate()
val bitmap = Bitmap.createBitmap(
drawable.intrinsicWidth,
drawable.intrinsicHeight,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(bitmap)
drawable.setBounds(0, 0, canvas.width, canvas.height)
drawable.draw(canvas)
bitmap
}
}
#MainThread
fun changePlayerVolume(view: ImageView) {
player?.let {
if (it.volume == 1.0f) {
it.volume = 0.0f
view.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_volume_off))
} else {
it.volume = 1.0f
view.setImageDrawable(ContextCompat.getDrawable(this, R.drawable.ic_volume_up))
}
}
}
#MainThread
fun jumpLiveStream() {
player?.let {
it.setMediaItem(mediaItem)
it.playWhenReady = true
}
}
/**
* Triggered when the app is closed. */
override fun onTaskRemoved(rootIntent: Intent?) {// Stop the service when the user closes the application.l
releasePlayer()
stopSelf()
super.onTaskRemoved(rootIntent)
}
override fun onDestroy() {
releasePlayer()
stopSelf()
super.onDestroy()
}
/**
* delete player and playerNotificationManager */
private fun releasePlayer() {
player?.let {
it.release()
player = null
}
playerNotificationManager?.let {
it.setPlayer(null)
playerNotificationManager = null
}
}
}
Good luck!

How to pass already encoded frame to webrtc stream android kotlin

I'm writing and android app in Kotlin to record and live stream a USB camera video concurrently, both in H264 codec format. I use Google Opensource for WebRTC streaming, and android.media package for recording.
The problem is I have to encode every captured frame twice, since I couldn't find a method to pass H264 encoded frame from recording process to WebRTC. This decrease my app performance largely to a frame rate of only 7-8 fps.
Here is the callback to encode a newly arrived frame in recording thread.
private val mediaEncoderCallback = object : MediaCodec.Callback() {
override fun onInputBufferAvailable(p0: MediaCodec, p1: Int) {}
override fun onError(p0: MediaCodec, p1: MediaCodec.CodecException) {}
private var encoderLock = Object()
override fun onOutputBufferAvailable(
p0: MediaCodec,
index: Int,
info: MediaCodec.BufferInfo
) = synchronized(encoderLock) {
if (isStopped.get()) return
val encodedData = try {
requireNotNull(mediaEncoder?.getOutputBuffer(index))
} catch (exception: Exception) {
Timber.e(exception)
return
}
if (info.flags and MediaCodec.BUFFER_FLAG_CODEC_CONFIG != 0) {
info.size = 0
}
if (info.size != 0) {
if (muxerStarted.get()) {
encodedData.position(info.offset)
encodedData.limit(info.offset + info.size)
val trackId = trackIndex.get()
muxer?.writeSampleData(trackId, encodedData, info)
}
}
mediaEncoder?.releaseOutputBuffer(index, false)
Unit
}
override fun onOutputFormatChanged(p0: MediaCodec, p1: MediaFormat) {
if (isStopped.get()) return
timer.schedule(object : TimerTask() {
override fun run() {
forceStopRecording()
}
}, captureTimeInMillis)
val currentMuxer = muxer ?: return
if (trackIndex.get() == -1) {
currentMuxer.addTrack(mediaEncoder?.outputFormat!!).also {
trackIndex.set(it)
}
}
if (!muxerStarted.get() && trackIndex.get() >= 0) {
muxerStarted.set(true)
currentMuxer.start()
}
}
}
override fun startRecording(captureFile: File, captureTimeInMillis: Long) {
this.captureTimeInMillis = captureTimeInMillis
if (!CodecProvider.checkIfCodecSupported(VIDEO_MIME_TYPE)) return
muxer = MediaMuxer(captureFile.path, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4)
val codec = MediaCodec.createEncoderByType(VIDEO_MIME_TYPE).also {
mediaEncoder = it
}
codec.configure(getVideoFormat(), null, null, MediaCodec.CONFIGURE_FLAG_ENCODE)
inputSurface = codec.createInputSurface()
codec.setCallback(mediaEncoderCallback)
codec.start()
codecStarted.set(true)
inputSurface?.let { onSurfaceUpdated(it) }
}
Here is the callback to stream new raw frame in webrtc thread.
override fun handleNewBuffer(cameraKey: String, byteBuffer: ByteBuffer) {
if (!isStreamingStarted) return
val imageArray = ByteArray(byteBuffer.remaining())
byteBuffer.get(imageArray)
byteBuffer.rewind()
val buffer = NV21Buffer(
imageArray,
CameraConfig.WIDTH_SIZE,
CameraConfig.HEIGHT_SIZE,
null
)
val timestamp = TimeUnit.MILLISECONDS.toNanos(SystemClock.elapsedRealtime())
val videoFrame = VideoFrame(buffer, 0, timestamp)
localVideoSource.capturerObserver.onFrameCaptured(videoFrame)
videoFrame.release()
}
Is there any workaround to encode one time for both streaming and recording (or more specific, to pass *encodedData from recording thread to webrtc thread, instead of a raw byteBuffer).
Thank you!

Android: Open and read another app location

I need to develop an app which monitors a list of apps and, if the system is rebooted, return to the last open page of the monitored apps. So, say that android was on the youtube app playing lofi hip hop radio - beats to relax/study to. How could my app send android back to this given video? Also, is there a way for me to know where in the app the user is? For example, can I know that the user was on youtube playing lofi and open it back again for them?
I already know how to open another app as well as detect whichever app is currently open, but I need to know the its uri as well. I can use adb and shell script if it is needed.
I have the following code to launch another app:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
val intent = Intent(this, BackAppListenerService::class.java)
startService(intent)
openApp(this, "com.google.android.youtube")
}
/** Open another app.
* #param context current Context, like Activity, App, or Service
* #param packageName the full package name of the app to open
* #return true if likely successful, false if unsuccessful
*/
fun openApp(context: Context, packageName: String?): Boolean {
val manager = context.packageManager
return try {
val i = manager.getLaunchIntentForPackage(packageName!!)
if (i == null) {
println("Activity not found")
return false;
//throw new ActivityNotFoundException();
}
//throw new ActivityNotFoundException();
i.addCategory(Intent.CATEGORY_LAUNCHER)
context.startActivity(i)
true
} catch (e: ActivityNotFoundException) {
println(e)
false
}
}
}
And with this I get the current foreground app
class BackAppListenerService : Service() {
private var isRunning = false
private var lastApp = ""
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
override fun onCreate() {
isRunning = true
Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS)
}
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
//Creating new thread for my service
//Always write your long running tasks in a separate thread, to avoid ANR
Thread(Runnable {
while (true) {
try {
Thread.sleep(10)
} catch (e: Exception) {
}
val currentForegroundApp = getForegroundApp()
val currentApp = currentForegroundApp.first
if (currentApp != lastApp) {
// New app on front
lastApp = currentApp
println("Current App $lastApp")
}
}
}).start()
return START_STICKY
}
// Must Have Usage Access Permission
fun getForegroundApp(): Pair<String, UsageStats> {
var currentApp = "NULL"
var currentAppInfo: UsageStats? = null
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val usm = this.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
val time = System.currentTimeMillis()
val appList =
usm.queryUsageStats(UsageStatsManager.INTERVAL_DAILY, time - 1000 * 1000, time)
if (appList != null && appList.size > 0) {
val mySortedMap: SortedMap<Long, UsageStats> =
TreeMap<Long, UsageStats>()
for (usageStats in appList) {
mySortedMap.put(usageStats.lastTimeUsed, usageStats)
}
if (mySortedMap != null && !mySortedMap.isEmpty()) {
currentAppInfo = mySortedMap[mySortedMap.lastKey()]!!
currentApp = mySortedMap[mySortedMap.lastKey()]!!.packageName
}
}
} else {
val am = this.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
val tasks = am.runningAppProcesses
currentApp = tasks[0].processName
}
return Pair(currentApp.split(".").last(), currentAppInfo!!)
}
[...]
}
Any help is much appreciated.

Remote data received in composite disposable but not getting to the repository from callback.onResult

I am using RxJava and PagedList for the first time. I followed through the tutorial by Riad as it is explanatory (Android Movie App MVVM). The only difference is that I had to create my viewmodel factory differently as ViewModelProviders.of() that he used is deprecated.
The main issue is that I can see that the response from the Api is collected but does not get to my repository and viewmodel. I have tried to debug and find reason why this happening but could not figure it out.
Perhaps there is something am missing that I could helped with
Datasource
class MovieDataSource(private val apiService:TheMovieDbInterface, private val compositeDisposable: CompositeDisposable):PageKeyedDataSource<Int, Movies>() {
var page = FIRST_PAGE
val networkState = MutableLiveData<NetworkState>()
override fun loadInitial(
params: LoadInitialParams<Int>,
callback: LoadInitialCallback<Int, Movies>
) {
networkState.postValue(NetworkState.LOADING)
try {
compositeDisposable.add(
apiService.getPopularMovies(page)
.subscribeOn(Schedulers.io())
.subscribe({it->
callback.onResult(it.results, null, page+1)
Log.i("MovieDetailsResponse", "${it.results}")
networkState.postValue(NetworkState.LOADED)
},{
networkState.postValue(NetworkState.ERROR)
Log.e("MovieDetailsResponse", it.message)
})
)
}
catch (e:Exception){
Log.e("MovieDetailsResponse", e.message)
}
}
override fun loadAfter(params: LoadParams<Int>, callback: LoadCallback<Int, Movies>) {
networkState.postValue(NetworkState.LOADING)
try {
compositeDisposable.add(
apiService.getPopularMovies(params.key)
.subscribeOn(Schedulers.io())
.subscribe({it->
if(it.totalPages >= params.key){
callback.onResult(it.results, params.key+1)
networkState.postValue(NetworkState.LOADED)
}
else{
networkState.postValue(NetworkState.END_OF_LIST)
}
},{
networkState.postValue(NetworkState.ERROR)
Log.e("MovieDetailsResponse", it.message)
})
)
}
catch (e:Exception){
Log.e("MovieDetailsResponse", e.message)
}
}
override fun loadBefore(params: LoadParams<Int>, callback: LoadCallback<Int, Movies>) {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
}
DataSourceFactory
class MovieDataSourceFactory(private val apiService: TheMovieDbInterface, private val compositeDisposable: CompositeDisposable):DataSource.Factory<Int, Movies>() {
val movieLiveDataSource = MutableLiveData<MovieDataSource>()
override fun create(): DataSource<Int, Movies> {
val movieDataSource = MovieDataSource(apiService, compositeDisposable)
movieLiveDataSource.postValue(movieDataSource)
return movieDataSource
}
}
PagedListRepository
lass MoviePagedListRepository(private val apiService:TheMovieDbInterface) {
lateinit var moviePagedList:LiveData<PagedList<Movies>>
lateinit var movieDataSourceFactory: MovieDataSourceFactory
fun fetchLiveMoviePagedList(compositeDisposable: CompositeDisposable):LiveData<PagedList<Movies>>{
movieDataSourceFactory = MovieDataSourceFactory(apiService, compositeDisposable)
val config = PagedList.Config.Builder()
.setEnablePlaceholders(false)
.setPageSize(POST_PER_PAGE)
.build()
moviePagedList = LivePagedListBuilder(movieDataSourceFactory, config).build()
Log.i("moviePagedList", "$moviePagedList")
return moviePagedList
}
fun getNetworkState():LiveData<NetworkState>{
return Transformations.switchMap<MovieDataSource, NetworkState>(
movieDataSourceFactory.movieLiveDataSource, MovieDataSource::networkState
)
}
}
ViewModel
class MainViewModel(private val movieRepository:MoviePagedListRepository): ViewModel() {
private val compositeDisposable = CompositeDisposable()
val moviePagedList : LiveData<PagedList<Movies>> = movieRepository.fetchLiveMoviePagedList(compositeDisposable)
val networkState:LiveData<NetworkState> by lazy {
movieRepository.getNetworkState()
}
fun isListEmpty():Boolean{
return moviePagedList.value?.isEmpty()?: true
}
override fun onCleared() {
super.onCleared()
compositeDisposable.dispose()
}
}

Categories

Resources