From 7e046965661c5c6e4254c2cf607aae0ff07811d8 Mon Sep 17 00:00:00 2001 From: MichaelStH Date: Tue, 21 Sep 2021 19:36:17 +0200 Subject: [PATCH] [feat/038-download-with-flow] : Download feature with Kotlin Flow works --- app/src/main/AndroidManifest.xml | 11 +- .../com/riders/thelab/data/RepositoryImpl.kt | 5 + .../thelab/data/local/model/Download.kt | 11 ++ .../com/riders/thelab/data/remote/ApiImpl.kt | 85 +++++++++++++ .../com/riders/thelab/data/remote/IApi.kt | 3 + .../thelab/ui/download/DownloadActivity.kt | 116 ++++++++++++++++++ .../thelab/ui/download/DownloadViewModel.kt | 66 ++++++++++ .../riders/thelab/utils/AppBuilderUtils.kt | 12 ++ app/src/main/res/drawable/ic_download.xml | 10 ++ app/src/main/res/layout/activity_download.xml | 41 +++++++ app/src/main/res/values/strings.xml | 3 + build.gradle | 2 +- 12 files changed, 362 insertions(+), 3 deletions(-) create mode 100644 app/src/main/java/com/riders/thelab/data/local/model/Download.kt create mode 100644 app/src/main/java/com/riders/thelab/ui/download/DownloadActivity.kt create mode 100644 app/src/main/java/com/riders/thelab/ui/download/DownloadViewModel.kt create mode 100644 app/src/main/res/drawable/ic_download.xml create mode 100644 app/src/main/res/layout/activity_download.xml diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 9c78334f..06c15209 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -365,6 +365,13 @@ android:label="@string/activity_title_google_drive" android:parentActivityName=".ui.mainactivity.MainActivity" android:theme="@style/Theme.TheLab" /> + + @@ -459,8 +466,8 @@ + tools:ignore="MissingClass" + tools:node="remove" /> \ No newline at end of file diff --git a/app/src/main/java/com/riders/thelab/data/RepositoryImpl.kt b/app/src/main/java/com/riders/thelab/data/RepositoryImpl.kt index 34f6ab1e..f2542b5c 100644 --- a/app/src/main/java/com/riders/thelab/data/RepositoryImpl.kt +++ b/app/src/main/java/com/riders/thelab/data/RepositoryImpl.kt @@ -14,6 +14,7 @@ import com.google.firebase.storage.StorageReference import com.riders.thelab.TheLabApplication import com.riders.thelab.data.local.DbImpl import com.riders.thelab.data.local.model.Contact +import com.riders.thelab.data.local.model.Download import com.riders.thelab.data.local.model.Video import com.riders.thelab.data.local.model.app.App import com.riders.thelab.data.local.model.weather.CityModel @@ -215,4 +216,8 @@ class RepositoryImpl @Inject constructor( override suspend fun getBulkWeatherCitiesFile(): ResponseBody { return mApiImpl.getBulkWeatherCitiesFile() } + + override suspend fun getBulkDownload(): Flow { + return mApiImpl.getBulkDownload() + } } \ No newline at end of file diff --git a/app/src/main/java/com/riders/thelab/data/local/model/Download.kt b/app/src/main/java/com/riders/thelab/data/local/model/Download.kt new file mode 100644 index 00000000..8966d457 --- /dev/null +++ b/app/src/main/java/com/riders/thelab/data/local/model/Download.kt @@ -0,0 +1,11 @@ +package com.riders.thelab.data.local.model + +import java.io.File + +sealed class Download { + data class Started(val started: Boolean) : Download() + data class Progress(val percent: Int) : Download() + data class Done(val done: Boolean) : Download() + data class Error(val error: Boolean) : Download() + data class Finished(val file: File) : Download() +} diff --git a/app/src/main/java/com/riders/thelab/data/remote/ApiImpl.kt b/app/src/main/java/com/riders/thelab/data/remote/ApiImpl.kt index 1e17a138..27c6e4a1 100644 --- a/app/src/main/java/com/riders/thelab/data/remote/ApiImpl.kt +++ b/app/src/main/java/com/riders/thelab/data/remote/ApiImpl.kt @@ -6,13 +6,21 @@ import android.widget.Toast import com.google.firebase.auth.FirebaseAuth import com.google.firebase.storage.FirebaseStorage import com.google.firebase.storage.StorageReference +import com.riders.thelab.TheLabApplication +import com.riders.thelab.data.local.model.Download import com.riders.thelab.data.local.model.Video import com.riders.thelab.data.remote.api.* import com.riders.thelab.data.remote.dto.artist.Artist import com.riders.thelab.data.remote.dto.weather.OneCallWeatherResponse +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.Flow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.flow.flow +import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.tasks.await import okhttp3.ResponseBody import timber.log.Timber +import java.io.File import javax.inject.Inject class ApiImpl @Inject constructor( @@ -91,4 +99,81 @@ class ApiImpl @Inject constructor( Timber.e("get cities bulk file()") return mWeatherBulkApiService.getCitiesGZipFile() } + + + override suspend fun getBulkDownload(): Flow { + return mWeatherBulkApiService.getCitiesGZipFile() + .downloadCitiesFile( + TheLabApplication.getInstance().getContext().externalCacheDir!!, + "my_file" + ) + } + + + private suspend fun ResponseBody.downloadCitiesFile( + directory: File, + filename: String + ): Flow = flow { + + emit(Download.Started(true)) + Timber.d("downloadToFileWithProgress()") + emit(Download.Progress(0)) + + // flag to delete file if download errors or is cancelled + var deleteFile = true + val file = File(directory, "${filename}.${contentType()?.subtype}") + + try { + + byteStream().use { inputStream -> + file.outputStream().use { outputStream -> + val totalBytes = contentLength() + val data = ByteArray(8_192) + var progressBytes = 0L + + while (true) { + val bytes = inputStream.read(data) + + if (bytes == -1) { + break + } + + outputStream.channel + outputStream.write(data, 0, bytes) + progressBytes += bytes + + emit(Download.Progress(percent = ((progressBytes * 100) / totalBytes).toInt())) + } + + when { + progressBytes < totalBytes -> { + emit(Download.Done(true)) + throw Exception("missing bytes") + } + progressBytes > totalBytes -> { + emit(Download.Done(true)) + throw Exception("too many bytes") + } + else -> + deleteFile = false + } + } + } + + emit(Download.Finished(file)) + } catch (exception: Exception) { + exception.printStackTrace() + emit(Download.Error(true)) + } finally { + // check if download was successful + emit(Download.Done(true)) + + if (deleteFile) { + file.delete() + } + } + } + .flowOn(Dispatchers.IO) + .distinctUntilChanged() + } \ No newline at end of file diff --git a/app/src/main/java/com/riders/thelab/data/remote/IApi.kt b/app/src/main/java/com/riders/thelab/data/remote/IApi.kt index 69d0ec11..f8491415 100644 --- a/app/src/main/java/com/riders/thelab/data/remote/IApi.kt +++ b/app/src/main/java/com/riders/thelab/data/remote/IApi.kt @@ -3,6 +3,7 @@ package com.riders.thelab.data.remote import android.app.Activity import android.location.Location import com.google.firebase.storage.StorageReference +import com.riders.thelab.data.local.model.Download import com.riders.thelab.data.local.model.Video import com.riders.thelab.data.remote.dto.artist.Artist import com.riders.thelab.data.remote.dto.weather.OneCallWeatherResponse @@ -22,4 +23,6 @@ interface IApi { suspend fun getVideos(): List