Skip to content

Commit

Permalink
Adding firebase storage client
Browse files Browse the repository at this point in the history
  • Loading branch information
cramsan committed Jun 20, 2024
1 parent 45bb2e9 commit 485cf30
Show file tree
Hide file tree
Showing 8 changed files with 333 additions and 13 deletions.
67 changes: 65 additions & 2 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ val jar by tasks.getting(Jar::class) {
it.path.startsWith("${projectDir.path}${File.separator}build${File.separator}jar")
}.map { zipTree(it) }
})
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
}

val sourceSets = project.the<SourceSetContainer>()
Expand Down Expand Up @@ -143,20 +144,82 @@ publishing {
}
}

/**
* List of aar files to include in the jar. Some jars are being omitted because they are not needed for the JVM.
* - lifecycle-*: exclude lifecycle libs due to https://github.com/GitLiveApp/firebase-java-sdk/pull/15 - remove the exclude once the dependencies in the aars are updated to the required version
* - savedstate: Excluded due to this library already being included as part of the compose mutliplatform dependencies. It does not seem to be directly needed by the firebase libraries.
*/
val includeList = listOf(
"activity-*.jar",
"asynclayoutinflater-*.jar",
"coordinatorlayout-*.jar",
"core-*.jar",
"core-runtime-*.jar",
"cursoradapter-*.jar",
"customview-*.jar",
"documentfile-*.jar",
"drawerlayout-*.jar",
"firebase-abt-*.jar",
"firebase-appcheck-*.jar",
"firebase-appcheck-interop-*.jar",
"firebase-auth-interop-*.jar",
"firebase-common-*.jar",
"firebase-common-*.jar",
"firebase-common-ktx-*.jar",
"firebase-common-ktx-*.jar",
"firebase-components-*.jar",
"firebase-components-*.jar",
"firebase-config-*.jar",
"firebase-config-interop-*.jar",
"firebase-database-*.jar",
"firebase-database-collection-*.jar",
"firebase-encoders-json-*.jar",
"firebase-firestore-*.jar",
"firebase-functions-*.jar",
"firebase-iid-*.jar",
"firebase-iid-interop-*.jar",
"firebase-installations-*.jar",
"firebase-installations-interop-*.jar",
"firebase-measurement-connector-*.jar",
"firebase-storage-*.jar",
"fragment-*.jar",
"fragment-*.jar",
"grpc-android-*.jar",
"interpolator-*.jar",
"legacy-support-core-ui-*.jar",
"legacy-support-core-utils-*.jar",
"loader-*.jar",
"localbroadcastmanager-*.jar",
"play-services-base-*.jar",
"play-services-basement-*.jar",
"play-services-basement-*.jar",
"play-services-cloud-messaging-*.jar",
"play-services-stats-*.jar",
"play-services-tasks-*.jar",
"play-services-tasks-*.jar",
"print-*.jar",
"protolite-well-known-types-*.jar",
"slidingpanelayout-*.jar",
"swiperefreshlayout-*.jar",
"versionedparcelable-*.jar",
"viewpager-*.jar",
)

dependencies {
compileOnly("org.robolectric:android-all:12.1-robolectric-8229987")
testImplementation("junit:junit:4.13.2")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.3")
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3")
testImplementation("org.mockito:mockito-core:5.12.0")
// firebase aars
aar("com.google.firebase:firebase-firestore:24.10.0")
aar("com.google.firebase:firebase-functions:20.4.0")
aar("com.google.firebase:firebase-database:20.3.0")
aar("com.google.firebase:firebase-config:21.6.0")
aar("com.google.firebase:firebase-installations:17.2.0")
aar("com.google.firebase:firebase-storage:21.0.0")
// extracted aar dependencies
// exclude lifecycle libs due to https://github.com/GitLiveApp/firebase-java-sdk/pull/15 - remove the exclude once the dependencies in the aars are updated to the required version
api(fileTree(mapOf("dir" to "build/jar", "include" to listOf("*.jar"), "exclude" to listOf("lifecycle-*"))))
api(fileTree(mapOf("dir" to "build/jar", "include" to includeList)))
// polyfill dependencies
implementation("org.jetbrains.kotlin:kotlin-stdlib")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
Expand Down
1 change: 1 addition & 0 deletions src/main/java/android/content/pm/PackageManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public ServiceInfo getServiceInfo(ComponentName component, int flags) throws Nam
data.put("com.google.firebase.components:com.google.firebase.functions.FunctionsRegistrar", "com.google.firebase.components.ComponentRegistrar");
data.put("com.google.firebase.components:com.google.firebase.installations.FirebaseInstallationsRegistrar", "com.google.firebase.components.ComponentRegistrar");
data.put("com.google.firebase.components:com.google.firebase.iid.Registrar", "com.google.firebase.components.ComponentRegistrar");
data.put("com.google.firebase.components:com.google.firebase.storage.StorageRegistrar", "com.google.firebase.components.ComponentRegistrar");
return new ServiceInfo(data);
}
throw new IllegalArgumentException(component.cls);
Expand Down
4 changes: 4 additions & 0 deletions src/main/java/android/net/ConnectivityManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,10 @@ class ConnectivityManager private constructor() {
connected.removeEventListener(networkCallback)
}

fun getActiveNetworkInfo(): NetworkInfo {
return NetworkInfo()
}

open class NetworkCallback : ValueEventListener {
override fun onDataChange(data: DataSnapshot) {
when (data.getValue(Boolean::class.java)) {
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/android/net/NetworkInfo.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package android.net

class NetworkInfo {
var type: Int = 1 // ConnectivityManager.TYPE_WIFI
val isConnectedOrConnecting: Boolean = true
val isConnected: Boolean = true
val isSuspended: Boolean = false
val isAvailable: Boolean = true
}
162 changes: 159 additions & 3 deletions src/main/java/android/net/Uri.kt
Original file line number Diff line number Diff line change
@@ -1,18 +1,45 @@
package android.net

import java.net.URI
import java.net.URLDecoder
import java.net.URLEncoder
import java.nio.charset.StandardCharsets
import java.nio.file.Paths
import java.util.Collections

class Uri(private val uri: URI) {

companion object {
@JvmStatic
fun parse(uriString: String) = Uri(URI.create(uriString))

@JvmStatic
fun encode(s: String?): String? = encode(s, null)

@JvmStatic
fun encode(s: String?, allow: String?): String? {
return URLEncoder.encode(s, StandardCharsets.UTF_8)
}

@JvmStatic
fun decode(s: String?): String? {
return URLDecoder.decode(s, StandardCharsets.UTF_8)
}
}

val scheme get() = uri.scheme
val port get() = uri.port
val host get() = uri.host
fun getScheme(): String = uri.scheme

fun getPort(): Int = uri.port

fun getHost(): String = uri.host

fun getPath(): String = uri.path

fun getAuthority(): String = uri.authority

fun getQuery() = uri.query

fun getFragment() = uri.fragment

fun getQueryParameterNames(): Set<String> {
val query: String = uri.query ?: return emptySet()
Expand Down Expand Up @@ -65,4 +92,133 @@ class Uri(private val uri: URI) {
} while (true)
return null
}

fun buildUpon(): Builder {
return Builder()
.scheme(this.getScheme())
.authority(this.getAuthority())
.path(this.getPath())
.query(this.getQuery())
.fragment(this.getFragment())
}

override fun toString(): String {
return uri.toString()
}

class Builder {
private var scheme: String? = null
private var opaquePart: String? = null
private var authority: String? = null
private var path: String? = null
private var query: String? = null
private var fragment: String? = null

fun scheme(scheme: String?): Builder {
this.scheme = scheme
return this
}

fun opaquePart(opaquePart: String?): Builder {
this.opaquePart = opaquePart
return this
}

fun encodedOpaquePart(opaquePart: String?): Builder {
return opaquePart(URLDecoder.decode(opaquePart, StandardCharsets.UTF_8.toString()))
}

fun authority(authority: String?): Builder {
this.opaquePart = null
this.authority = authority
return this
}

fun encodedAuthority(authority: String?): Builder {
return authority(URLDecoder.decode(authority, StandardCharsets.UTF_8.toString()))
}

fun path(path: String?): Builder {
this.opaquePart = null
this.path = path
return this
}

fun encodedPath(path: String?): Builder {
return this.path(URLDecoder.decode(path, StandardCharsets.UTF_8.toString()))
}

fun appendPath(newSegment: String?): Builder {
val createdPath = Paths.get(this.path.orEmpty(), newSegment).toString()
return this.path(createdPath)
}

fun appendEncodedPath(newSegment: String?): Builder {
val newDecodedSegment = URLDecoder.decode(newSegment, StandardCharsets.UTF_8.toString())
return appendPath(newDecodedSegment)
}

fun query(query: String?): Builder {
this.opaquePart = null
this.query = query
return this
}

fun encodedQuery(query: String?): Builder {
return this.query(URLDecoder.decode(query, StandardCharsets.UTF_8.toString()))
}

fun fragment(fragment: String?): Builder {
this.fragment = fragment
return this
}

fun encodedFragment(fragment: String?): Builder {
return this.query(URLDecoder.decode(fragment, StandardCharsets.UTF_8.toString()))
}

fun appendQueryParameter(key: String?, value: String?): Builder {
this.opaquePart = null
val encodedParameter = encode(key) + "=" + encode(value)
if (this.query == null) {
this.query = decode(encodedParameter)
return this
} else {
val oldQuery: String = encode(query)!!
if (oldQuery.isNotEmpty()) {
this.query = decode("$oldQuery&$encodedParameter")
} else {
this.query = decode(encodedParameter)
}

return this
}
}

fun clearQuery(): Builder {
return this.query(null)
}

fun build(): Uri {
return if (this.opaquePart != null) {
if (this.scheme == null) {
throw UnsupportedOperationException("An opaque URI must have a scheme.")
} else {
Uri(URI(this.scheme, this.opaquePart, this.fragment))
}
} else {
Uri(URI(this.scheme, this.authority, path, this.query, this.fragment))
}
}

override fun toString(): String {
return this.build().toString()
}
}

}

/** Index of a component which was not found. */
private const val NOT_FOUND = -1

private val HEX_DIGITS = "0123456789ABCDEF".toCharArray()
67 changes: 67 additions & 0 deletions src/test/kotlin/FirestoreStorageTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import android.app.Application
import android.content.Context
import android.net.ConnectivityManager
import android.net.Uri
import com.google.firebase.Firebase
import com.google.firebase.FirebaseApp
import com.google.firebase.FirebaseOptions
import com.google.firebase.FirebasePlatform
import com.google.firebase.initialize
import com.google.firebase.storage.internal.Slashes
import com.google.firebase.storage.storage
import fakes.FakeFirebasePlatform
import org.junit.Assert
import org.junit.Before
import org.junit.Test

class FirestoreStorageTest : FirebaseTest() {

private lateinit var app: FirebaseApp

@Before
fun initialize() {
FirebasePlatform.initializeFirebasePlatform(FakeFirebasePlatform())
val options = FirebaseOptions
.Builder()
.setProjectId("my-firebase-project")
.setApplicationId("1:27992087142:android:ce3b6448250083d1")
.setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
.setStorageBucket("fir-kotlin-sdk.appspot.com")
// setDatabaseURL(...)
.build()
app = Firebase.initialize(Application(), options)
}

@Test
fun test_parsing_storage_uri() {
val input = "gs://edifikana-stage.appspot.com"

val normalized = Slashes.normalizeSlashes(input.substring(5))
val fullUri = Slashes.preserveSlashEncode(normalized)
val parsedUri = Uri.parse("gs://$fullUri");

Assert.assertEquals("gs://edifikana-stage.appspot.com", parsedUri.toString())
}

@Test
fun test_loading_default_storage_client() {
Firebase.storage
}

@Test
fun test_getting_root_reference() {
val storage = Firebase.storage
val reference = storage.reference
Assert.assertNotNull(reference)
}

@Test
fun test_getting_child_reference() {
val storage = Firebase.storage
val reference = storage.reference
val downloadRef = reference.child("mountains.jpg")
val downloadUrl = downloadRef.downloadUrl

Assert.assertNotNull(downloadUrl)
}
}
10 changes: 2 additions & 8 deletions src/test/kotlin/FirestoreTest.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.google.firebase.FirebaseOptions
import com.google.firebase.FirebasePlatform
import com.google.firebase.firestore.firestore
import com.google.firebase.initialize
import fakes.FakeFirebasePlatform
import kotlinx.coroutines.runBlocking
import kotlinx.coroutines.tasks.await
import org.junit.Assert.assertEquals
Expand All @@ -14,14 +15,7 @@ import java.io.File
class FirestoreTest : FirebaseTest() {
@Before
fun initialize() {
FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
val storage = mutableMapOf<String, String>()
override fun store(key: String, value: String) = storage.set(key, value)
override fun retrieve(key: String) = storage[key]
override fun clear(key: String) { storage.remove(key) }
override fun log(msg: String) = println(msg)
override fun getDatabasePath(name: String) = File("./build/$name")
})
FirebasePlatform.initializeFirebasePlatform(FakeFirebasePlatform())
val options = FirebaseOptions.Builder()
.setProjectId("my-firebase-project")
.setApplicationId("1:27992087142:android:ce3b6448250083d1")
Expand Down
Loading

0 comments on commit 485cf30

Please sign in to comment.