diff --git a/android/build.gradle.kts b/android/build.gradle.kts index dfd33500..8f935e16 100644 --- a/android/build.gradle.kts +++ b/android/build.gradle.kts @@ -121,13 +121,14 @@ dependencies { implementation(MVIKotlin.mvikotlinTimeTravel) // Extras - Extras.Android.apply { + with(Extras.Android) { implementation(Acra.notification) implementation(Acra.http) implementation(appUpdator) implementation(matomo) } + implementation(Extras.kermit) //implementation("com.jakewharton.timber:timber:4.7.1") implementation("dev.icerock.moko:parcelize:0.7.0") implementation("com.github.shabinder:storage-chooser:2.0.4.45") diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 5fa70abf..04d4f636 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -72,6 +72,6 @@ - + \ No newline at end of file diff --git a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt index 1344566c..0bd21b8d 100644 --- a/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt +++ b/android/src/main/java/com/shabinder/spotiflyer/MainActivity.kt @@ -17,15 +17,16 @@ package com.shabinder.spotiflyer import android.annotation.SuppressLint -import android.content.BroadcastReceiver +import android.content.ComponentName import android.content.Context import android.content.Intent -import android.content.IntentFilter +import android.content.ServiceConnection import android.content.pm.PackageManager import android.media.MediaScannerConnection import android.net.Uri import android.os.Build import android.os.Bundle +import android.os.IBinder import android.os.PowerManager import android.util.Log import androidx.activity.ComponentActivity @@ -51,18 +52,17 @@ import com.google.accompanist.insets.navigationBarsPadding import com.google.accompanist.insets.statusBarsHeight import com.google.accompanist.insets.statusBarsPadding import com.shabinder.common.di.* -import com.shabinder.common.di.worker.ForegroundService import com.shabinder.common.models.Actions import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.PlatformActions import com.shabinder.common.models.PlatformActions.Companion.SharedPreferencesKey -import com.shabinder.common.models.Status import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.methods import com.shabinder.common.root.SpotiFlyerRoot import com.shabinder.common.root.SpotiFlyerRoot.Analytics import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks import com.shabinder.common.uikit.* +import com.shabinder.spotiflyer.service.ForegroundService import com.shabinder.spotiflyer.ui.AnalyticsDialog import com.shabinder.spotiflyer.ui.NetworkDialog import com.shabinder.spotiflyer.ui.PermissionDialog @@ -82,12 +82,16 @@ class MainActivity : ComponentActivity() { private val callBacks: SpotiFlyerRootCallBacks get() = root.callBacks private val trackStatusFlow = MutableSharedFlow>(1) private var permissionGranted = mutableStateOf(true) - private lateinit var updateUIReceiver: BroadcastReceiver - private lateinit var queryReceiver: BroadcastReceiver private val internetAvailability by lazy { ConnectionLiveData(applicationContext) } private val tracker get() = (application as App).tracker private val visibleChild get(): SpotiFlyerRoot.Child = root.routerState.value.activeChild.instance + // Variable for storing instance of our service class + var foregroundService: ForegroundService? = null + + // Boolean to check if our activity is bound to service or not + var isServiceBound: Boolean? = null + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) // This app draws behind the system bars, so we want to handle fitting system windows @@ -162,8 +166,62 @@ class MainActivity : ComponentActivity() { TrackHelper.track().download().with(tracker) } handleIntentFromExternalActivity() + + initForegroundService() } + /*START: Foreground Service Handlers*/ + private fun initForegroundService() { + // Start and then Bind to the Service + ContextCompat.startForegroundService( + this@MainActivity, + Intent(this, ForegroundService::class.java) + ) + bindService() + } + + /** + * Interface for getting the instance of binder from our service class + * So client can get instance of our service class and can directly communicate with it. + */ + private val serviceConnection = object : ServiceConnection { + val tag = "Service Connection" + + override fun onServiceConnected(className: ComponentName, iBinder: IBinder) { + Log.d(tag, "connected to service.") + // We've bound to MyService, cast the IBinder and get MyBinder instance + val binder = iBinder as ForegroundService.DownloadServiceBinder + foregroundService = binder.service + isServiceBound = true + lifecycleScope.launch { + foregroundService?.trackStatusFlowMap?.statusFlow?.let { + trackStatusFlow.emitAll(it.conflate()) + } + } + } + + override fun onServiceDisconnected(arg0: ComponentName) { + Log.d(tag, "disconnected from service.") + isServiceBound = false + } + } + + /*Used to bind to our service class*/ + private fun bindService() { + Intent(this, ForegroundService::class.java).also { intent -> + bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) + } + } + + /*Used to unbind from our service class*/ + private fun unbindService() { + Intent(this, ForegroundService::class.java).also { + unbindService(serviceConnection) + } + } + /*END: Foreground Service Handlers*/ + + @Composable private fun isInternetAvailableState(): State { return internetAvailability.observeAsState() @@ -206,12 +264,9 @@ class MainActivity : ComponentActivity() { ) } - override fun sendTracksToService(array: ArrayList) { - for (list in array.chunked(50)) { - val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java) - serviceIntent.putParcelableArrayListExtra("object", list as ArrayList) - ContextCompat.startForegroundService(this@MainActivity, serviceIntent) - } + override fun sendTracksToService(array: List) { + if (foregroundService == null) initForegroundService() + foregroundService?.downloadAllTracks(array) } } @@ -296,10 +351,16 @@ class MainActivity : ComponentActivity() { ) private fun queryActiveTracks() { - val serviceIntent = Intent(this@MainActivity, ForegroundService::class.java).apply { - action = "query" + lifecycleScope.launch { + foregroundService?.trackStatusFlowMap?.let { tracksStatus -> + trackStatusFlow.emit(tracksStatus) + } } - ContextCompat.startForegroundService(this@MainActivity, serviceIntent) + } + + override fun onResume() { + super.onResume() + queryActiveTracks() } @Suppress("DEPRECATION") @@ -357,80 +418,6 @@ class MainActivity : ComponentActivity() { } } - /* - * Broadcast Handlers - * */ - private fun initializeBroadcast(){ - val intentFilter = IntentFilter().apply { - addAction(Status.QUEUED.name) - addAction(Status.FAILED.name) - addAction(Status.DOWNLOADING.name) - addAction(Status.COMPLETED.name) - addAction("Progress") - addAction("Converting") - } - updateUIReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - //Update Flow with latest details - if (intent != null) { - val trackDetails = intent.getParcelableExtra("track") - trackDetails?.let { track -> - lifecycleScope.launch { - val latestMap = trackStatusFlow.replayCache.getOrElse(0 - ) { hashMapOf() }.apply { - this[track.title] = when (intent.action) { - Status.QUEUED.name -> DownloadStatus.Queued - Status.FAILED.name -> DownloadStatus.Failed - Status.DOWNLOADING.name -> DownloadStatus.Downloading() - "Progress" -> DownloadStatus.Downloading(intent.getIntExtra("progress", 0)) - "Converting" -> DownloadStatus.Converting - Status.COMPLETED.name -> DownloadStatus.Downloaded - else -> DownloadStatus.NotDownloaded - } - } - trackStatusFlow.emit(latestMap) - Log.i("Track Update",track.title + track.downloaded.toString()) - } - } - } - } - } - val queryFilter = IntentFilter().apply { addAction("query_result") } - queryReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context?, intent: Intent?) { - //UI update here - if (intent != null){ - @Suppress("UNCHECKED_CAST") - val trackList = intent.getSerializableExtra("tracks") as? HashMap? - trackList?.let { list -> - Log.i("Service Response", "${list.size} Tracks Active") - lifecycleScope.launch { - trackStatusFlow.emit(list) - } - } - } - } - } - registerReceiver(updateUIReceiver, intentFilter) - registerReceiver(queryReceiver, queryFilter) - } - - override fun onResume() { - super.onResume() - initializeBroadcast() - if(visibleChild is SpotiFlyerRoot.Child.List) { - // Update Track List Statuses when Returning to App - queryActiveTracks() - } - } - - override fun onPause() { - super.onPause() - unregisterReceiver(updateUIReceiver) - unregisterReceiver(queryReceiver) - } - - override fun onNewIntent(intent: Intent?) { super.onNewIntent(intent) handleIntentFromExternalActivity(intent) @@ -455,6 +442,11 @@ class MainActivity : ComponentActivity() { } } + override fun onDestroy() { + super.onDestroy() + unbindService() + } + companion object { const val disableDozeCode = 1223 } diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt b/android/src/main/java/com/shabinder/spotiflyer/service/ForegroundService.kt similarity index 76% rename from common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt rename to android/src/main/java/com/shabinder/spotiflyer/service/ForegroundService.kt index f2bf595c..dee184a9 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt +++ b/android/src/main/java/com/shabinder/spotiflyer/service/ForegroundService.kt @@ -14,7 +14,7 @@ * along with this program. If not, see . */ -package com.shabinder.common.di.worker +package com.shabinder.spotiflyer.service import android.annotation.SuppressLint import android.app.DownloadManager @@ -26,6 +26,7 @@ import android.app.PendingIntent.FLAG_CANCEL_CURRENT import android.app.Service import android.content.Context import android.content.Intent +import android.os.Binder import android.os.Build import android.os.IBinder import android.os.PowerManager @@ -40,12 +41,13 @@ import com.shabinder.common.di.downloadFile import com.shabinder.common.di.utils.ParallelExecutor import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadStatus -import com.shabinder.common.models.Status import com.shabinder.common.models.TrackDetails import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.coroutineScope +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.collect import kotlinx.coroutines.launch import org.koin.android.ext.android.inject @@ -68,7 +70,8 @@ class ForegroundService : Service(), CoroutineScope { override val coroutineContext: CoroutineContext get() = serviceJob + Dispatchers.IO - private val allTracksStatus = hashMapOf() + val trackStatusFlowMap = TrackStatusFlowMap(MutableSharedFlow(replay = 1),this) + private var messageList = mutableListOf("", "", "", "", "") private var wakeLock: PowerManager.WakeLock? = null private var isServiceStarted = false @@ -80,7 +83,16 @@ class ForegroundService : Service(), CoroutineScope { private val logger: Kermit by inject() private val dir: Dir by inject() - override fun onBind(intent: Intent): IBinder? = null + + inner class DownloadServiceBinder : Binder() { + // Return this instance of MyService so clients can call public methods + val service: ForegroundService + get() =// Return this instance of Foreground Service so clients can call public methods + this@ForegroundService + } + private val myBinder: IBinder = DownloadServiceBinder() + + override fun onBind(intent: Intent): IBinder = myBinder @SuppressLint("UnspecifiedImmutableFlag") override fun onCreate() { @@ -110,31 +122,13 @@ class ForegroundService : Service(), CoroutineScope { "query" -> { val response = Intent().apply { action = "query_result" - synchronized(allTracksStatus) { - putExtra("tracks", allTracksStatus) + synchronized(trackStatusFlowMap) { + putExtra("tracks", trackStatusFlowMap) } } sendBroadcast(response) } } - - val downloadObjects: ArrayList? = ( - it.getParcelableArrayListExtra("object") ?: it.extras?.getParcelableArrayList( - "object" - ) - ) - - downloadObjects?.let { list -> - downloadObjects.size.let { size -> - total += size - isSingleDownload = (size == 1) - } - list.forEach { track -> - allTracksStatus[track.title] = DownloadStatus.Queued - } - updateNotification() - downloadAllTracks(list) - } } // Wake locks and misc tasks from here : return if (isServiceStarted) { @@ -156,8 +150,16 @@ class ForegroundService : Service(), CoroutineScope { /** * Function To Download All Tracks Available in a List **/ - private fun downloadAllTracks(trackList: List) { + fun downloadAllTracks(trackList: List) { + + trackList.size.also { size -> + total += size + isSingleDownload = (size == 1) + updateNotification() + } + trackList.forEach { + trackStatusFlowMap[it.title] = DownloadStatus.Queued launch(Dispatchers.IO) { downloadService.execute { fetcher.findMp3DownloadLink(it).fold( @@ -165,10 +167,9 @@ class ForegroundService : Service(), CoroutineScope { enqueueDownload(url, it) }, failure = { _ -> - sendTrackBroadcast(Status.FAILED.name, it) failed++ updateNotification() - allTracksStatus[it.title] = DownloadStatus.Failed + trackStatusFlowMap[it.title] = DownloadStatus.Failed } ) } @@ -180,24 +181,20 @@ class ForegroundService : Service(), CoroutineScope { // Initiating Download addToNotification("Downloading ${track.title}") logger.d(tag) { "${track.title} Download Started" } - allTracksStatus[track.title] = DownloadStatus.Downloading() - sendTrackBroadcast(Status.DOWNLOADING.name, track) + trackStatusFlowMap[track.title] = DownloadStatus.Downloading() // Enqueueing Download downloadFile(url).collect { when (it) { is DownloadResult.Error -> { - launch { - logger.d(tag) { it.message } - removeFromNotification("Downloading ${track.title}") - failed++ - updateNotification() - sendTrackBroadcast(Status.FAILED.name, track) - } + logger.d(tag) { it.message } + removeFromNotification("Downloading ${track.title}") + failed++ + updateNotification() } is DownloadResult.Progress -> { - allTracksStatus[track.title] = DownloadStatus.Downloading(it.progress) + trackStatusFlowMap[track.title] = DownloadStatus.Downloading(it.progress) logger.d(tag) { "${track.title} Progress: ${it.progress} %" } val intent = Intent().apply { @@ -209,26 +206,31 @@ class ForegroundService : Service(), CoroutineScope { } is DownloadResult.Success -> { - try { - // Save File and Embed Metadata - val job = launch(Dispatchers.Default) { dir.saveFileWithMetadata(it.byteArray, track) {} } - allTracksStatus[track.title] = DownloadStatus.Converting - sendTrackBroadcast("Converting", track) - addToNotification("Processing ${track.title}") - job.invokeOnCompletion { - converted++ - allTracksStatus[track.title] = DownloadStatus.Downloaded - sendTrackBroadcast(Status.COMPLETED.name, track) - removeFromNotification("Processing ${track.title}") + coroutineScope { + try { + // Save File and Embed Metadata + val job = launch(Dispatchers.Default) { dir.saveFileWithMetadata(it.byteArray, track) {} } + + // Send Converting Status + trackStatusFlowMap[track.title] = DownloadStatus.Converting + addToNotification("Processing ${track.title}") + + // All Processing Completed for this Track + job.invokeOnCompletion { + converted++ + trackStatusFlowMap[track.title] = DownloadStatus.Downloaded + removeFromNotification("Processing ${track.title}") + } + logger.d(tag) { "${track.title} Download Completed" } + downloaded++ + } catch (e: Exception) { + e.printStackTrace() + // Download Failed + logger.d(tag) { "${track.title} Download Failed! Error:Fetch!!!!" } + failed++ } - logger.d(tag) { "${track.title} Download Completed" } - downloaded++ - } catch (e: Exception) { - // Download Failed - logger.d(tag) { "${track.title} Download Failed! Error:Fetch!!!!" } - failed++ + removeFromNotification("Downloading ${track.title}") } - removeFromNotification("Downloading ${track.title}") } } } @@ -270,7 +272,7 @@ class ForegroundService : Service(), CoroutineScope { messageList = mutableListOf("Cleaning And Exiting", "", "", "", "") downloadService.close() updateNotification() - cleanFiles(File(dir.defaultDir()), logger) + cleanFiles(File(dir.defaultDir())) // TODO cleanFiles(File(dir.imageCacheDir())) messageList = mutableListOf("", "", "", "", "") releaseWakeLock() @@ -336,12 +338,4 @@ class ForegroundService : Service(), CoroutineScope { getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager mNotificationManager.notify(notificationId, getNotification()) } - - private fun sendTrackBroadcast(action: String, track: TrackDetails) { - val intent = Intent().apply { - setAction(action) - putExtra("track", track) - } - this@ForegroundService.sendBroadcast(intent) - } } diff --git a/android/src/main/java/com/shabinder/spotiflyer/service/TrackStatusFlowMap.kt b/android/src/main/java/com/shabinder/spotiflyer/service/TrackStatusFlowMap.kt new file mode 100644 index 00000000..be4bcab7 --- /dev/null +++ b/android/src/main/java/com/shabinder/spotiflyer/service/TrackStatusFlowMap.kt @@ -0,0 +1,17 @@ +package com.shabinder.spotiflyer.service + +import com.shabinder.common.models.DownloadStatus +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.launch + +class TrackStatusFlowMap( + val statusFlow: MutableSharedFlow>, + private val scope: CoroutineScope +): HashMap() { + override fun put(key: String, value: DownloadStatus): DownloadStatus? { + val res = super.put(key, value) + scope.launch { statusFlow.emit(this@TrackStatusFlowMap) } + return res + } +} \ No newline at end of file diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/Utils.kt b/android/src/main/java/com/shabinder/spotiflyer/service/Utils.kt similarity index 61% rename from common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/Utils.kt rename to android/src/main/java/com/shabinder/spotiflyer/service/Utils.kt index 06c26c80..309e68fd 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/Utils.kt +++ b/android/src/main/java/com/shabinder/spotiflyer/service/Utils.kt @@ -1,22 +1,22 @@ -package com.shabinder.common.di.worker +package com.shabinder.spotiflyer.service -import co.touchlab.kermit.Kermit +import android.util.Log import java.io.File /** * Cleaning All Residual Files except Mp3 Files **/ -fun cleanFiles(dir: File, logger: Kermit) { +fun cleanFiles(dir: File) { try { - logger.d("File Cleaning") { "Starting Cleaning in ${dir.path} " } + Log.d("File Cleaning","Starting Cleaning in ${dir.path} ") val fList = dir.listFiles() fList?.let { for (file in fList) { if (file.isDirectory) { - cleanFiles(file, logger) + cleanFiles(file) } else if (file.isFile) { if (file.path.toString().substringAfterLast(".") != "mp3") { - logger.d("Files Cleaning") { "Cleaning ${file.path}" } + Log.d("Files Cleaning","Cleaning ${file.path}") file.delete() } } @@ -24,3 +24,4 @@ fun cleanFiles(dir: File, logger: Kermit) { } } catch (e: Exception) { e.printStackTrace() } } + diff --git a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt index af210499..72dce041 100644 --- a/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt +++ b/common/compose/src/commonMain/kotlin/com/shabinder/common/uikit/SpotiFlyerListUi.kt @@ -17,12 +17,32 @@ package com.shabinder.common.uikit import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.itemsIndexed import androidx.compose.foundation.lazy.rememberLazyListState -import androidx.compose.material.* -import androidx.compose.runtime.* +import androidx.compose.material.CircularProgressIndicator +import androidx.compose.material.ExperimentalMaterialApi +import androidx.compose.material.ExtendedFloatingActionButton +import androidx.compose.material.Icon +import androidx.compose.material.MaterialTheme +import androidx.compose.material.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -53,6 +73,7 @@ fun SpotiFlyerListContent( component.onBackPressed() } } + Box(modifier = modifier.fillMaxSize()) { val result = model.queryResult if (result == null) { diff --git a/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt index 6fd52fd8..0d6adde2 100644 --- a/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt +++ b/common/data-models/src/androidMain/kotlin/com.shabinder.common.models/AndroidPlatformActions.kt @@ -14,7 +14,7 @@ actual interface PlatformActions { fun addToLibrary(path: String) - fun sendTracksToService(array: ArrayList) + fun sendTracksToService(array: List) } actual val StubPlatformActions = object : PlatformActions { @@ -24,5 +24,5 @@ actual val StubPlatformActions = object : PlatformActions { override fun addToLibrary(path: String) {} - override fun sendTracksToService(array: ArrayList) {} + override fun sendTracksToService(array: List) {} } diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt index ad7d8bf9..7820eb92 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt @@ -23,11 +23,9 @@ import com.shabinder.common.di.utils.removeIllegalChars import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.TrackDetails import com.shabinder.database.Database -import io.ktor.client.request.HttpRequestBuilder -import io.ktor.client.request.get -import io.ktor.client.statement.HttpStatement -import io.ktor.http.contentLength -import io.ktor.http.isSuccess +import io.ktor.client.request.* +import io.ktor.client.statement.* +import io.ktor.http.* import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow import kotlin.math.roundToInt @@ -105,7 +103,7 @@ suspend fun downloadFile(url: String): Flow { var offset = 0 do { // Set Length optimally, after how many kb you want a progress update, now it 0.25mb - val currentRead = response.content.readAvailable(data, offset, 250000) + val currentRead = response.content.readAvailable(data, offset, 2_50_000) offset += currentRead val progress = (offset * 100f / data.size).roundToInt() emit(DownloadResult.Progress(progress)) diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt index 642a3443..ede4efee 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/integration/SpotiFlyerListImpl.kt @@ -18,6 +18,7 @@ package com.shabinder.common.list.integration import co.touchlab.stately.ensureNeverFrozen import com.arkivanov.decompose.ComponentContext +import com.arkivanov.decompose.lifecycle.doOnResume import com.arkivanov.decompose.value.Value import com.shabinder.common.caching.Cache import com.shabinder.common.di.Picture @@ -38,6 +39,9 @@ internal class SpotiFlyerListImpl( init { instanceKeeper.ensureNeverFrozen() + lifecycle.doOnResume { + onRefreshTracksStatuses() + } } private val store = diff --git a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt index c1459231..5d9e148a 100644 --- a/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt +++ b/common/list/src/commonMain/kotlin/com/shabinder/common/list/store/SpotiFlyerListStoreProvider.kt @@ -60,7 +60,7 @@ internal class SpotiFlyerListStoreProvider( data class UpdateTrackList(val list: List) : Result() data class UpdateTrackItem(val item: TrackDetails) : Result() data class ErrorOccurred(val error: Throwable) : Result() - data class AskForDonation(val isAllowed: Boolean) : Result() + data class AskForSupport(val isAllowed: Boolean) : Result() } private inner class ExecutorImpl : SuspendExecutor() { @@ -73,7 +73,7 @@ internal class SpotiFlyerListStoreProvider( logger.d(message = "Database List Last ID: $it", tag = "Database Last ID") val offset = dir.getDonationOffset dispatch( - Result.AskForDonation( + Result.AskForSupport( // Every 3rd Interval or After some offset isAllowed = offset < 4 && (it % offset == 0L) ) @@ -81,7 +81,7 @@ internal class SpotiFlyerListStoreProvider( } downloadProgressFlow.collect { map -> - logger.d(map.size.toString(), "ListStore: flow Updated") + // logger.d(map.size.toString(), "ListStore: flow Updated") val updatedTrackList = getState().trackList.updateTracksStatuses(map) if (updatedTrackList.isNotEmpty()) dispatch(Result.UpdateTrackList(updatedTrackList)) } @@ -131,7 +131,7 @@ internal class SpotiFlyerListStoreProvider( is Result.UpdateTrackList -> copy(trackList = result.list) is Result.UpdateTrackItem -> updateTrackItem(result.item) is Result.ErrorOccurred -> copy(errorOccurred = result.error) - is Result.AskForDonation -> copy(askForDonation = result.isAllowed) + is Result.AskForSupport -> copy(askForDonation = result.isAllowed) } private fun State.updateTrackItem(item: TrackDetails): State {