Skip to content

Commit

Permalink
style: apply some code styling (edeckers#3)
Browse files Browse the repository at this point in the history
  • Loading branch information
edeckers authored Oct 5, 2020
1 parent aa1714b commit 0cddc0a
Show file tree
Hide file tree
Showing 3 changed files with 268 additions and 230 deletions.
239 changes: 127 additions & 112 deletions android/src/main/java/io/deckers/blob_downloader/BlobDownloaderModule.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,156 +6,171 @@ import android.net.Uri
import android.os.Environment
import android.os.Environment.getExternalStoragePublicDirectory
import com.facebook.common.internal.ImmutableMap
import com.facebook.react.bridge.*
import com.facebook.react.bridge.Promise
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import com.facebook.react.bridge.ReadableMap
import com.facebook.react.modules.network.OkHttpClientProvider

import java.io.File
import java.lang.reflect.Type
import okhttp3.OkHttpClient
import okhttp3.Request
import okio.Okio

class BlobDownloaderModule(val reactContext: ReactApplicationContext) :
ReactContextBaseJavaModule(reactContext) {
val ERROR_MISSING_REQUIRED_PARAM = "ERROR_MISSING_REQUIRED_PARAM"
val ERROR_INVALID_TARGET_PARAM_ENUM = "ERROR_INVALID_TARGET_PARAM_ENUM"
val ERROR_UNEXPECTED_EXCEPTION = "ERROR_UNEXPECTED_EXCEPTION"

class BlobDownloaderModule(val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
val ERROR_MISSING_REQUIRED_PARAM = "ERROR_MISSING_REQUIRED_PARAM"
val ERROR_INVALID_TARGET_PARAM_ENUM = "ERROR_INVALID_TARGET_PARAM_ENUM"
val ERROR_UNEXPECTED_EXCEPTION = "ERROR_UNEXPECTED_EXCEPTION"
val PARAM_FILENAME = "filename"
val PARAM_METHOD = "method"
val PARAM_TARGET = "target"
val PARAM_URL = "url"
val PARAM_USE_DOWNLOAD_MANAGER = "useDownloadManager"

val PARAM_FILENAME = "filename"
val PARAM_METHOD = "method"
val PARAM_TARGET = "target"
val PARAM_URL = "url"
val PARAM_USE_DOWNLOAD_MANAGER = "useDownloadManager"
val TARGET_PARAM_ENUM_PREFIX = "enum://"

val TARGET_PARAM_ENUM_PREFIX = "enum://"
val DEFAULT_METHOD = "GET"

val DEFAULT_METHOD = "GET"
override fun getName(): String {
return "BlobDownloader"
}

override fun getName(): String {
return "BlobDownloader"
}
val predefinedPaths = mapOf(
"DCIM" to getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
"DOCUMENT" to getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
"DOWNLOAD" to getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
)

val predefinedPaths = mapOf(
"DCIM" to getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM),
"DOCUMENT" to getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS),
"DOWNLOAD" to getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)
)
class BlobDownloaderError(open val code: String, message: String) : Throwable(message)

class BlobDownloaderError(open val code: String, message: String) : Throwable(message) {
}
fun stripEnumPrefix(path: String): String = path.replaceFirst(TARGET_PARAM_ENUM_PREFIX, "")

fun stripEnumPrefix(path: String): String = path.replaceFirst(TARGET_PARAM_ENUM_PREFIX, "")
fun assertPathEnum(pathEnum: String) {
val cleanedPathEnum = stripEnumPrefix(pathEnum)

fun assertPathEnum(pathEnum: String) {
val cleanedPathEnum = stripEnumPrefix(pathEnum)

if (!predefinedPaths.containsKey(cleanedPathEnum.toUpperCase())) {
throw BlobDownloaderError(ERROR_INVALID_TARGET_PARAM_ENUM, "Unknown enum `${cleanedPathEnum}`")
}
if (!predefinedPaths.containsKey(cleanedPathEnum.toUpperCase())) {
throw BlobDownloaderError(ERROR_INVALID_TARGET_PARAM_ENUM, "Unknown enum `$cleanedPathEnum`")
}
}

fun isEnum(pathEnum: String) = pathEnum.startsWith(TARGET_PARAM_ENUM_PREFIX, ignoreCase = true)
fun isEnum(pathEnum: String) = pathEnum.startsWith(TARGET_PARAM_ENUM_PREFIX, ignoreCase = true)

fun parsePathEnum(pathEnum: String): File? {
val cleanedPathEnum = stripEnumPrefix(pathEnum);
fun parsePathEnum(pathEnum: String): File? {
val cleanedPathEnum = stripEnumPrefix(pathEnum)

assertPathEnum(cleanedPathEnum)

return predefinedPaths.get(cleanedPathEnum.toUpperCase())
}

fun buildTargetFromPathOrEnum(pathOrEnum: String): String =
if (isEnum(pathOrEnum)) parsePathEnum(pathOrEnum).toString() else pathOrEnum
assertPathEnum(cleanedPathEnum)

return predefinedPaths.get(cleanedPathEnum.toUpperCase())
}

val REQUIRED_PARAMETER_PROCESSOR = ImmutableMap.of(
Boolean::class.java.toString(), { input: ReadableMap, parameterName: String -> input.getBoolean(parameterName) },
String::class.java.toString(), { input: ReadableMap, parameterName: String -> input.getString(parameterName) }
)
fun buildTargetFromPathOrEnum(pathOrEnum: String): String =
if (isEnum(pathOrEnum)) parsePathEnum(pathOrEnum).toString() else pathOrEnum

val REQUIRED_PARAMETER_PROCESSOR = ImmutableMap.of(
Boolean::class.java.toString(),
{ input: ReadableMap, parameterName: String -> input.getBoolean(parameterName) },
String::class.java.toString(),
{ input: ReadableMap, parameterName: String -> input.getString(parameterName) }
)

fun assertTargetParam(target: String) {
if (isEnum(target)) {
assertPathEnum(target)
}
fun assertTargetParam(target: String) {
if (isEnum(target)) {
assertPathEnum(target)
}

fun assertRequiredParameter(input: ReadableMap, type: Type, parameterName: String) {
val maybeValue = REQUIRED_PARAMETER_PROCESSOR.getOrDefault(type.toString()) { _, _ -> throw Exception("No processor defined for type `${type}`, valid options: ${REQUIRED_PARAMETER_PROCESSOR.keys.joinToString(", ")}") }(input, parameterName)

if (maybeValue == null) {
throw BlobDownloaderError(ERROR_MISSING_REQUIRED_PARAM, "`${parameterName}` is a required parameter of type `${type}`")
}
}

fun assertRequiredParameter(input: ReadableMap, type: Type, parameterName: String) {
val availableOptions = REQUIRED_PARAMETER_PROCESSOR.keys.joinToString(", ")
val defaultFallback = "No processor defined for type `$type`, valid options: $availableOptions"
val unknownProcessor = { _: ReadableMap, _: String -> throw Exception(defaultFallback) }

val maybeValue =
REQUIRED_PARAMETER_PROCESSOR.getOrDefault(
type.toString(), unknownProcessor
)(input, parameterName)

if (maybeValue == null) {
throw BlobDownloaderError(
ERROR_MISSING_REQUIRED_PARAM,
"`$parameterName` is a required parameter of type `$type`"
)
}
}

fun fetchBlobUsingDownloadManager(uri: Uri, targetPath: String, filename: String?) {
val downloadManager = reactContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
fun fetchBlobUsingDownloadManager(uri: Uri, targetPath: String, filename: String?) {
val downloadManager = reactContext.getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager

val request = DownloadManager.Request(uri).apply {
setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI or DownloadManager.Request.NETWORK_MOBILE)
.setAllowedOverRoaming(true)
.setVisibleInDownloadsUi(true)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir(targetPath, filename)
}

downloadManager.enqueue(request)
val request = DownloadManager.Request(uri).apply {
setAllowedOverRoaming(true)
.setVisibleInDownloadsUi(true)
.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
.setDestinationInExternalPublicDir(targetPath, filename)
}

fun fetchBlobWithoutDownloadManager(uri: Uri, targetPath: String, filename: String, method: String) {
val fullTargetPath = File(targetPath, filename)
downloadManager.enqueue(request)
}

fun fetchBlobWithoutDownloadManager(
uri: Uri,
targetPath: String,
filename: String,
method: String
) {
val fullTargetPath = File(targetPath, filename)

val okHttpClient = OkHttpClientProvider.getOkHttpClient()
val okHttpClient = OkHttpClientProvider.getOkHttpClient()

val request = Request.Builder().method(method, null).url(uri.toString()).build()
val request = Request.Builder().method(method, null).url(uri.toString()).build()

okHttpClient.newCall(request).execute().use { response ->
response.body()?.source().use { source ->
Okio.buffer(Okio.sink(fullTargetPath)).use { sink ->
sink.writeAll(source)
}
}
okHttpClient.newCall(request).execute().use { response ->
response.body()?.source().use { source ->
Okio.buffer(Okio.sink(fullTargetPath)).use { sink ->
sink.writeAll(source)
}
}
}
}

fun fetchBlobFromValidatedParameters(input: ReadableMap, promise: Promise) {
val filename = input.getString(PARAM_FILENAME) ?: ""
val uri = Uri.parse(input.getString(PARAM_URL))
val targetPathOrEnum = input.getString(PARAM_TARGET) ?: ""
val target = buildTargetFromPathOrEnum(targetPathOrEnum)

val useDownloadManager =
input.hasKey(PARAM_USE_DOWNLOAD_MANAGER) &&
input.getBoolean(PARAM_USE_DOWNLOAD_MANAGER)

if (useDownloadManager) {
fetchBlobUsingDownloadManager(uri, target, filename)
promise.resolve(true)
return
}

fun fetchBlobFromValidatedParameters(input: ReadableMap, promise: Promise) {
val filename = input.getString(PARAM_FILENAME) ?: ""
val uri = Uri.parse(input.getString(PARAM_URL))
val targetPathOrEnum = input.getString(PARAM_TARGET) ?: ""
val target = buildTargetFromPathOrEnum(targetPathOrEnum)
val method = input.getString(PARAM_METHOD) ?: DEFAULT_METHOD

val useDownloadManager =
input.hasKey(PARAM_USE_DOWNLOAD_MANAGER) &&
input.getBoolean(PARAM_USE_DOWNLOAD_MANAGER)
fetchBlobWithoutDownloadManager(uri, target, filename, method)
promise.resolve(true)
}

if (useDownloadManager) {
fetchBlobUsingDownloadManager(uri, target, filename)
promise.resolve(true)
return
}
@ReactMethod
fun fetchBlob(input: ReadableMap, promise: Promise) {
try {
assertRequiredParameter(input, String::class.java, PARAM_FILENAME)
assertRequiredParameter(input, String::class.java, PARAM_TARGET)
assertRequiredParameter(input, String::class.java, PARAM_URL)

val method = input.getString(PARAM_METHOD) ?: DEFAULT_METHOD
assertTargetParam(input.getString(PARAM_TARGET) ?: "")

fetchBlobWithoutDownloadManager(uri, target, filename, method)
promise.resolve(true)
}

@ReactMethod
fun fetchBlob(input: ReadableMap, promise: Promise) {
try {
assertRequiredParameter(input, String::class.java, PARAM_FILENAME)
assertRequiredParameter(input, String::class.java, PARAM_TARGET)
assertRequiredParameter(input, String::class.java, PARAM_URL)

assertTargetParam(input.getString(PARAM_TARGET) ?: "")

fetchBlobFromValidatedParameters(input, promise)
} catch (e: BlobDownloaderError) {
promise.reject(e.code, e.message)
return
} catch (e: Exception) {
promise.reject(ERROR_UNEXPECTED_EXCEPTION, "An unexpected exception occurred: ${e.message}")
return
}
fetchBlobFromValidatedParameters(input, promise)
} catch (e: BlobDownloaderError) {
promise.reject(e.code, e.message)
return
} catch (e: Exception) {
promise.reject(ERROR_UNEXPECTED_EXCEPTION, "An unexpected exception occurred: ${e.message}")
return
}
}
}
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
package io.deckers.blob_downloader

import java.util.Arrays
import java.util.Collections

import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ViewManager
import com.facebook.react.bridge.JavaScriptModule
import java.util.Arrays

class BlobDownloaderPackage : ReactPackage {
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return Arrays.asList<NativeModule>(BlobDownloaderModule(reactContext))
}
override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
return Arrays.asList<NativeModule>(BlobDownloaderModule(reactContext))
}

override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList<ViewManager<*, *>>()
}
override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<*, *>> {
return emptyList<ViewManager<*, *>>()
}
}
Loading

0 comments on commit 0cddc0a

Please sign in to comment.