Error Handling

This commit is contained in:
shabinder 2021-04-28 22:52:33 +05:30
parent a0797bd891
commit 318c673cea
9 changed files with 65 additions and 34 deletions

View File

@ -33,7 +33,7 @@ class App: Application(), KoinComponent {
val loggingEnabled = true
initKoin(loggingEnabled) {
androidLogger(Level.NONE)
androidLogger(Level.NONE) // No virtual method elapsedNow
androidContext(this@App)
modules(appModule(loggingEnabled))
}

View File

@ -35,6 +35,7 @@ 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.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
@ -49,6 +50,7 @@ import com.shabinder.common.di.Picture
import com.shabinder.common.list.SpotiFlyerList
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.TrackDetails
import kotlinx.coroutines.delay
@Composable
fun SpotiFlyerListContent(
@ -61,11 +63,20 @@ fun SpotiFlyerListContent(
// TODO Better Null Handling
val result = model.queryResult
if (result == null) {
/* Loading Bar */
Column(Modifier.fillMaxSize(), verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally) {
CircularProgressIndicator()
Spacer(modifier.padding(8.dp))
Text("Loading..", style = appNameStyle, color = colorPrimary)
}
LaunchedEffect(Unit) {
delay(350)
/*Handle if Any Exception Occurred*/
model.errorOccurred?.let {
showPopUpMessage(it.message ?: "An Error Occurred, Check your Link / Connection")
component.onBackPressed()
}
}
} else {
LazyColumn(
verticalArrangement = Arrangement.spacedBy(12.dp),

View File

@ -150,8 +150,9 @@ actual class Dir actual constructor(
}
}catch (e:Exception){
withContext(Dispatchers.Main){
Toast.makeText(appContext,"Could Not Create File:\n${songFile.absolutePath}",Toast.LENGTH_SHORT).show()
//Toast.makeText(appContext,"Could Not Create File:\n${songFile.absolutePath}",Toast.LENGTH_SHORT).show()
}
if(songFile.exists()) songFile.delete()
logger.e { "${songFile.absolutePath} could not be created" }
}
}

View File

@ -43,7 +43,6 @@ import com.shabinder.common.di.utils.ParallelExecutor
import com.shabinder.common.models.DownloadResult
import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.TrackDetails
import com.shabinder.downloader.YoutubeDownloader
import com.shabinder.downloader.models.formats.Format
import com.shabinder.common.models.Status
import kotlinx.coroutines.CoroutineScope
@ -57,6 +56,7 @@ import java.io.File
import kotlin.coroutines.CoroutineContext
class ForegroundService : Service(), CoroutineScope {
private val tag: String = "Foreground Service"
private val channelId = "ForegroundDownloaderService"
private val notificationId = 101
@ -64,26 +64,25 @@ class ForegroundService : Service(), CoroutineScope {
private var converted = 0 // Total Files Converted
private var downloaded = 0 // Total Files downloaded
private var failed = 0 // Total Files failed
private val isFinished: Boolean
get() = converted + failed == total
private var isSingleDownload: Boolean = false
private val isFinished get() = converted + failed == total
private var isSingleDownload = false
private lateinit var serviceJob: Job
override val coroutineContext: CoroutineContext
get() = serviceJob + Dispatchers.IO
private val allTracksStatus = hashMapOf<String, DownloadStatus>()
private var messageList = mutableListOf("", "", "", "", "")
private var wakeLock: PowerManager.WakeLock? = null
private var isServiceStarted = false
private var messageList = mutableListOf("", "", "", "", "")
private lateinit var cancelIntent: PendingIntent
private lateinit var downloadManager: DownloadManager
private lateinit var downloadService: ParallelExecutor
private val ytDownloader get() = fetcher.youtubeProvider.ytDownloader
private val fetcher: FetchPlatformQueryResult by inject()
private val logger: Kermit by inject()
private val dir: Dir by inject()
private val ytDownloader: YoutubeDownloader
get() = fetcher.youtubeProvider.ytDownloader
override fun onBind(intent: Intent): IBinder? = null

View File

@ -24,6 +24,7 @@ import com.shabinder.common.di.providers.SpotifyProvider
import com.shabinder.common.di.providers.YoutubeMp3
import com.shabinder.common.di.providers.YoutubeMusic
import io.ktor.client.HttpClient
import io.ktor.client.features.HttpTimeout
import io.ktor.client.features.json.JsonFeature
import io.ktor.client.features.json.serializer.KotlinxSerializer
import io.ktor.client.features.logging.DEFAULT
@ -65,6 +66,12 @@ fun createHttpClient(enableNetworkLogs: Boolean = false, serializer: KotlinxSeri
install(JsonFeature) {
this.serializer = serializer
}
// Timeout
install(HttpTimeout) {
requestTimeoutMillis = 15000L
connectTimeoutMillis = 15000L
socketTimeoutMillis = 15000L
}
if (enableNetworkLogs) {
install(Logging) {
logger = Logger.DEFAULT
@ -72,4 +79,5 @@ fun createHttpClient(enableNetworkLogs: Boolean = false, serializer: KotlinxSeri
}
}
}
/*Client Active Throughout App's Lifetime*/
val ktorHttpClient = HttpClient {}

View File

@ -53,23 +53,27 @@ expect class Dir (
suspend fun downloadFile(url: String): Flow<DownloadResult> {
return flow {
val client = createHttpClient()
val response = client.get<HttpStatement>(url).execute()
val data = ByteArray(response.contentLength()!!.toInt())
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)
offset += currentRead
val progress = (offset * 100f / data.size).roundToInt()
emit(DownloadResult.Progress(progress))
} while (currentRead > 0)
if (response.status.isSuccess()) {
emit(DownloadResult.Success(data))
} else {
emit(DownloadResult.Error("File not downloaded"))
try {
val client = createHttpClient()
val response = client.get<HttpStatement>(url).execute()
val data = ByteArray(response.contentLength()!!.toInt())
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)
offset += currentRead
val progress = (offset * 100f / data.size).roundToInt()
emit(DownloadResult.Progress(progress))
} while (currentRead > 0)
if (response.status.isSuccess()) {
emit(DownloadResult.Success(data))
} else {
emit(DownloadResult.Error("File not downloaded"))
}
client.close()
} catch (e:Exception) {
emit(DownloadResult.Error(e.message ?: "File not downloaded"))
}
client.close()
}
}

View File

@ -63,10 +63,6 @@ private suspend fun isInternetAvailable(): Boolean {
actual val isInternetAvailable: Boolean
get() {
return true
/*var result = false
val job = GlobalScope.launch { result = isInternetAvailable() }
while(job.isActive){}
return result*/
}
val DownloadProgressFlow: MutableSharedFlow<HashMap<String, DownloadStatus>> = MutableSharedFlow(1)

View File

@ -73,7 +73,8 @@ interface SpotiFlyerList {
data class State(
val queryResult: PlatformQueryResult? = null,
val link: String = "",
val trackList: List<TrackDetails> = emptyList()
val trackList: List<TrackDetails> = emptyList(),
val errorOccurred: Exception? = null
)
}

View File

@ -58,6 +58,7 @@ internal class SpotiFlyerListStoreProvider(
data class ResultFetched(val result: PlatformQueryResult, val trackList: List<TrackDetails>) : Result()
data class UpdateTrackList(val list: List<TrackDetails>) : Result()
data class UpdateTrackItem(val item: TrackDetails) : Result()
data class ErrorOccurred(val error: Exception) : Result()
}
private inner class ExecutorImpl : SuspendExecutor<Intent, Unit, State, Result, Nothing>() {
@ -74,10 +75,19 @@ internal class SpotiFlyerListStoreProvider(
override suspend fun executeIntent(intent: Intent, getState: () -> State) {
when (intent) {
is Intent.SearchLink -> fetchQuery.query(link)?.let { result ->
result.trackList = result.trackList.toMutableList()
dispatch((Result.ResultFetched(result, result.trackList.updateTracksStatuses(downloadProgressFlow.replayCache.getOrElse(0) { hashMapOf() }))))
executeIntent(Intent.RefreshTracksStatuses, getState)
is Intent.SearchLink -> {
try {
val result = fetchQuery.query(link)
if( result != null) {
result.trackList = result.trackList.toMutableList()
dispatch((Result.ResultFetched(result, result.trackList.updateTracksStatuses(downloadProgressFlow.replayCache.getOrElse(0) { hashMapOf() }))))
executeIntent(Intent.RefreshTracksStatuses, getState)
} else {
throw Exception("An Error Occurred, Check your Link / Connection")
}
} catch (e:Exception) {
dispatch(Result.ErrorOccurred(e))
}
}
is Intent.StartDownloadAll -> {
@ -107,6 +117,7 @@ internal class SpotiFlyerListStoreProvider(
is Result.ResultFetched -> copy(queryResult = result.result, trackList = result.trackList, link = link)
is Result.UpdateTrackList -> copy(trackList = result.list)
is Result.UpdateTrackItem -> updateTrackItem(result.item)
is Result.ErrorOccurred -> copy(errorOccurred = result.error)
}
private fun State.updateTrackItem(item: TrackDetails): State {