Last active
June 22, 2018 19:44
-
-
Save fredy-mederos/e8a3da3abc8793384327d0867ff9696c to your computer and use it in GitHub Desktop.
RemoteStorageManager with Firebase-Storage
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.remotestorage | |
import com.google.firebase.storage.FirebaseStorage | |
import org.koin.dsl.module.applicationContext | |
/** | |
* @author @fredy-mederos | |
*/ | |
val RemoteStorageModule = applicationContext { | |
// bean { RemoteStorageDemoManagerImpl(true) as RemoteStorageManager } | |
bean { RemoteStorageManagerImpl(FirebaseStorage.getInstance()) as RemoteStorageManager } | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.remotestorage | |
import android.net.Uri | |
import com.google.firebase.storage.FirebaseStorage | |
import kotlinx.coroutines.experimental.delay | |
import kotlinx.coroutines.experimental.suspendCancellableCoroutine | |
import java.io.File | |
/** | |
* @author @fredy-mederos | |
*/ | |
/** | |
* Operation progress [percent], [part] and [total] | |
*/ | |
typealias OperationProgress = (percent: Int, part: Long, total: Long) -> Unit | |
interface RemoteStorageManager { | |
/** | |
* Upload a [file] to a [remotePath] in remote storage and get notified with [uploadProgress]. | |
* @return an [UploadResult] | |
*/ | |
suspend fun uploadFile( | |
remotePath: String, | |
file: File, | |
uploadProgress: OperationProgress? = null | |
): UploadResult | |
/** | |
* Download a file from a [remotePath] and save it into given [file]. Also notified with [downloadProgress] | |
* @return a [DownloadResult] | |
*/ | |
suspend fun downloadFile( | |
remotePath: RemotePath, | |
file: File, | |
downloadProgress: OperationProgress? = null | |
): DownloadResult | |
/** | |
* Result from [uploadFile] operation | |
*/ | |
sealed class UploadResult { | |
/** | |
* Success response with the [remoteUrl] | |
*/ | |
data class Success(val remoteUrl: Uri?) : UploadResult() | |
/** | |
* Error response with the [ex] Exception | |
*/ | |
data class Error(val ex: Exception?) : UploadResult() | |
} | |
/** | |
* Result from [downloadFile] operation | |
*/ | |
sealed class DownloadResult { | |
/** | |
* Success response | |
*/ | |
class Success : DownloadResult() | |
/** | |
* Error response with the [ex] Exception | |
*/ | |
data class Error(val ex: Exception?) : DownloadResult() | |
} | |
/** | |
* Remote [path] param used in [downloadFile] operation | |
*/ | |
sealed class RemotePath(open val path: String) { | |
/** | |
* The remote path is from a child | |
*/ | |
data class RemoteChild(override val path: String) : RemotePath(path) | |
/** | |
* The remote path is from an url | |
*/ | |
data class RemoteUrl(override val path: String) : RemotePath(path) | |
} | |
} | |
/** | |
* RemoteStorage implementation using Firebase-Storage | |
*/ | |
class RemoteStorageManagerImpl( | |
private val firebaseStorage: FirebaseStorage, | |
maxUploadRetryTimeMillis: Long = 20 * 1_000 //20 secs for retry upload | |
) : RemoteStorageManager { | |
init { | |
firebaseStorage.maxUploadRetryTimeMillis = maxUploadRetryTimeMillis | |
} | |
override suspend fun uploadFile( | |
remotePath: String, | |
file: File, | |
uploadProgress: OperationProgress? | |
): RemoteStorageManager.UploadResult = suspendCancellableCoroutine { continuation -> | |
val ref = firebaseStorage.getReference(remotePath) | |
ref.putFile(Uri.fromFile(file)).addOnSuccessListener { result -> | |
continuation.resume(RemoteStorageManager.UploadResult.Success(result.downloadUrl)) | |
}.addOnFailureListener { ex -> | |
continuation.resume(RemoteStorageManager.UploadResult.Error(ex)) | |
}.addOnProgressListener { task -> | |
uploadProgress?.let { | |
val percent = ((100 * task.bytesTransferred) / task.totalByteCount).toInt() | |
it.invoke(percent, task.bytesTransferred, task.totalByteCount) | |
} | |
} | |
} | |
override suspend fun downloadFile( | |
remotePath: RemoteStorageManager.RemotePath, | |
file: File, | |
downloadProgress: OperationProgress? | |
): RemoteStorageManager.DownloadResult = suspendCancellableCoroutine { continuation -> | |
val ref = when (remotePath) { | |
is RemoteStorageManager.RemotePath.RemoteChild -> firebaseStorage.getReference( | |
remotePath.path | |
) | |
is RemoteStorageManager.RemotePath.RemoteUrl -> firebaseStorage.getReferenceFromUrl( | |
remotePath.path | |
) | |
} | |
ref.getFile(file).addOnSuccessListener { | |
continuation.resume(RemoteStorageManager.DownloadResult.Success()) | |
}.addOnFailureListener { ex -> | |
continuation.resume(RemoteStorageManager.DownloadResult.Error(ex)) | |
}.addOnProgressListener { task -> | |
downloadProgress?.let { | |
val percent = ((100f * task.bytesTransferred) / task.totalByteCount).toInt() | |
it.invoke(percent, task.bytesTransferred, task.totalByteCount) | |
} | |
} | |
} | |
} | |
/** | |
* RemoteStorage implementation for fake demo purposes | |
*/ | |
class RemoteStorageDemoManagerImpl(private val forceSuccess: Boolean) : RemoteStorageManager { | |
override suspend fun uploadFile( | |
remotePath: String, | |
file: File, | |
uploadProgress: OperationProgress? | |
): RemoteStorageManager.UploadResult { | |
return if (!forceSuccess) { | |
delay(2_000) | |
RemoteStorageManager.UploadResult.Error(Exception("Error")) | |
} else { | |
(0..10).forEach { | |
delay(500) | |
uploadProgress?.invoke(it * 10, it.toLong(), 10L) | |
} | |
RemoteStorageManager.UploadResult.Success(Uri.parse("http://fake.external.api/$remotePath")) | |
} | |
} | |
override suspend fun downloadFile( | |
remotePath: RemoteStorageManager.RemotePath, | |
file: File, | |
downloadProgress: OperationProgress? | |
): RemoteStorageManager.DownloadResult { | |
return if (!forceSuccess) { | |
delay(2_000) | |
RemoteStorageManager.DownloadResult.Error(Exception("Error")) | |
} else { | |
(0..10).forEach { | |
delay(500) | |
downloadProgress?.invoke(it * 10, it.toLong(), 10L) | |
} | |
RemoteStorageManager.DownloadResult.Success() | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment