Skip to content

Commit 58bc2c2

Browse files
author
cramsan
committed
Adding JVM support for Firebase Storage
1 parent 45bb2e9 commit 58bc2c2

File tree

7 files changed

+206
-10
lines changed

7 files changed

+206
-10
lines changed

build.gradle.kts

+9-1
Original file line numberDiff line numberDiff line change
@@ -81,6 +81,7 @@ val jar by tasks.getting(Jar::class) {
8181
it.path.startsWith("${projectDir.path}${File.separator}build${File.separator}jar")
8282
}.map { zipTree(it) }
8383
})
84+
duplicatesStrategy = DuplicatesStrategy.EXCLUDE
8485
}
8586

8687
val sourceSets = project.the<SourceSetContainer>()
@@ -143,20 +144,27 @@ publishing {
143144
}
144145
}
145146

147+
val exclusionList = listOf(
148+
"lifecycle-*",
149+
"savedstate*",
150+
)
151+
146152
dependencies {
147153
compileOnly("org.robolectric:android-all:12.1-robolectric-8229987")
148154
testImplementation("junit:junit:4.13.2")
149155
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.3")
150156
testImplementation("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:1.7.3")
157+
testImplementation("org.mockito:mockito-core:5.12.0")
151158
// firebase aars
152159
aar("com.google.firebase:firebase-firestore:24.10.0")
153160
aar("com.google.firebase:firebase-functions:20.4.0")
154161
aar("com.google.firebase:firebase-database:20.3.0")
155162
aar("com.google.firebase:firebase-config:21.6.0")
156163
aar("com.google.firebase:firebase-installations:17.2.0")
164+
aar("com.google.firebase:firebase-storage:21.0.0")
157165
// extracted aar dependencies
158166
// 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
159-
api(fileTree(mapOf("dir" to "build/jar", "include" to listOf("*.jar"), "exclude" to listOf("lifecycle-*"))))
167+
implementation(fileTree(mapOf("dir" to "build/jar", "include" to listOf("*.jar"), "exclude" to exclusionList)))
160168
// polyfill dependencies
161169
implementation("org.jetbrains.kotlin:kotlin-stdlib")
162170
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")

gradle.properties

+1-1
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
version=0.4.3
1+
version=0.4.5

src/main/java/android/content/pm/PackageManager.java

+16
Original file line numberDiff line numberDiff line change
@@ -33,12 +33,28 @@ public ServiceInfo getServiceInfo(ComponentName component, int flags) throws Nam
3333
case "com.google.firebase.components.ComponentDiscoveryService":
3434
Map<String, Object> data = new HashMap<>();
3535
data.put("com.google.firebase.components.ComponentRegistrar", Boolean.TRUE);
36+
data.put("com.google.firebase.components:com.google.firebase.FirebaseCommonKtxRegistrar", "com.google.firebase.components.ComponentRegistrar");
37+
data.put("com.google.firebase.components:com.google.firebase.abt.component.AbtRegistrar", "com.google.firebase.components.ComponentRegistrar");
38+
data.put("com.google.firebase.components:com.google.firebase.analytics.connector.internal.AnalyticsConnectorRegistrar", "com.google.firebase.components.ComponentRegistrar");
39+
data.put("com.google.firebase.components:com.google.firebase.appcheck.FirebaseAppCheckKtxRegistrar", "com.google.firebase.components.ComponentRegistrar");
40+
data.put("com.google.firebase.components:com.google.firebase.appcheck.FirebaseAppCheckRegistrar", "com.google.firebase.components.ComponentRegistrar");
41+
data.put("com.google.firebase.components:com.google.firebase.crashlytics.CrashlyticsRegistrar", "com.google.firebase.components.ComponentRegistrar");
42+
data.put("com.google.firebase.components:com.google.firebase.crashlytics.FirebaseCrashlyticsKtxRegistrar", "com.google.firebase.components.ComponentRegistrar");
3643
data.put("com.google.firebase.components:com.google.firebase.database.DatabaseRegistrar", "com.google.firebase.components.ComponentRegistrar");
44+
data.put("com.google.firebase.components:com.google.firebase.datatransport.TransportRegistrar", "com.google.firebase.components.ComponentRegistrar");
45+
data.put("com.google.firebase.components:com.google.firebase.firestore.FirebaseFirestoreKtxRegistrar", "com.google.firebase.components.ComponentRegistrar");
3746
data.put("com.google.firebase.components:com.google.firebase.firestore.FirestoreRegistrar", "com.google.firebase.components.ComponentRegistrar");
3847
data.put("com.google.firebase.components:com.google.firebase.auth.FirebaseAuthRegistrar", "com.google.firebase.components.ComponentRegistrar");
3948
data.put("com.google.firebase.components:com.google.firebase.functions.FunctionsRegistrar", "com.google.firebase.components.ComponentRegistrar");
49+
data.put("com.google.firebase.components:com.google.firebase.installations.FirebaseInstallationsKtxRegistrar", "com.google.firebase.components.ComponentRegistrar");
4050
data.put("com.google.firebase.components:com.google.firebase.installations.FirebaseInstallationsRegistrar", "com.google.firebase.components.ComponentRegistrar");
4151
data.put("com.google.firebase.components:com.google.firebase.iid.Registrar", "com.google.firebase.components.ComponentRegistrar");
52+
data.put("com.google.firebase.components:com.google.firebase.ktx.FirebaseCommonLegacyRegistrar", "com.google.firebase.components.ComponentRegistrar");
53+
data.put("com.google.firebase.components:com.google.firebase.remoteconfig.FirebaseRemoteConfigKtxRegistrar", "com.google.firebase.components.ComponentRegistrar");
54+
data.put("com.google.firebase.components:com.google.firebase.remoteconfig.RemoteConfigRegistrar", "com.google.firebase.components.ComponentRegistrar");
55+
data.put("com.google.firebase.components:com.google.firebase.sessions.FirebaseSessionsRegistrar", "com.google.firebase.components.ComponentRegistrar");
56+
data.put("com.google.firebase.components:com.google.firebase.storage.FirebaseStorageKtxRegistrar", "com.google.firebase.components.ComponentRegistrar");
57+
data.put("com.google.firebase.components:com.google.firebase.storage.StorageRegistrar", "com.google.firebase.components.ComponentRegistrar");
4258
return new ServiceInfo(data);
4359
}
4460
throw new IllegalArgumentException(component.cls);

src/main/java/android/net/Uri.kt

+101
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
package android.net
22

3+
import java.io.File
4+
import java.io.UnsupportedEncodingException
35
import java.net.URI
46
import java.util.Collections
57

@@ -8,11 +10,105 @@ class Uri(private val uri: URI) {
810
companion object {
911
@JvmStatic
1012
fun parse(uriString: String) = Uri(URI.create(uriString))
13+
14+
@JvmStatic
15+
fun encode(s: String?): String? = encode(s, null)
16+
17+
@JvmStatic
18+
fun encode(s: String?, allow: String?): String? {
19+
if (s == null) {
20+
return null
21+
} else {
22+
var encoded: StringBuilder? = null
23+
val oldLength = s.length
24+
25+
var nextAllowed: Int
26+
var current = 0
27+
while (current < oldLength) {
28+
var nextToEncode = current
29+
while (nextToEncode < oldLength && isAllowed(s[nextToEncode], allow)) {
30+
++nextToEncode
31+
}
32+
33+
if (nextToEncode == oldLength) {
34+
if (current == 0) {
35+
return s
36+
}
37+
38+
encoded!!.append(s, current, oldLength)
39+
return encoded.toString()
40+
}
41+
42+
if (encoded == null) {
43+
encoded = StringBuilder()
44+
}
45+
46+
if (nextToEncode > current) {
47+
encoded.append(s, current, nextToEncode)
48+
}
49+
50+
current = nextToEncode
51+
52+
nextAllowed = current + 1
53+
while (nextAllowed < oldLength && !isAllowed(s[nextAllowed], allow)) {
54+
++nextAllowed
55+
}
56+
57+
val toEncode = s.substring(current, nextAllowed)
58+
59+
try {
60+
val bytes = toEncode.toByteArray(charset("UTF-8"))
61+
val bytesLength = bytes.size
62+
63+
for (i in 0 until bytesLength) {
64+
encoded.append('%')
65+
encoded.append(HEX_DIGITS[(bytes[i].toInt() and 240) shr 4])
66+
encoded.append(HEX_DIGITS[bytes[i].toInt() and 15])
67+
}
68+
} catch (var11: UnsupportedEncodingException) {
69+
val e = var11
70+
throw AssertionError(e)
71+
}
72+
current = nextAllowed
73+
}
74+
75+
return encoded?.toString() ?: s
76+
}
77+
}
78+
79+
@JvmStatic
80+
fun fromFile(file: File?): Uri {
81+
TODO()
82+
}
83+
84+
@JvmStatic
85+
fun fromParts(scheme: String?, ssp: String?, fragment: String?): Uri {
86+
TODO()
87+
}
88+
89+
@JvmStatic
90+
fun decode(s: String?): String? {
91+
TODO()
92+
}
93+
94+
@JvmStatic
95+
fun withAppendedPath(baseUri: Uri, pathSegment: String?): Uri {
96+
TODO()
97+
}
98+
99+
private fun isAllowed(c: Char, allow: String?): Boolean {
100+
return ((c >= 'A' && c <= 'Z')
101+
|| (c >= 'a' && c <= 'z')
102+
|| (c >= '0' && c <= '9')
103+
|| ("_-!.~'()*".indexOf(c) != NOT_FOUND
104+
) || (allow != null && allow.indexOf(c) != NOT_FOUND))
105+
}
11106
}
12107

13108
val scheme get() = uri.scheme
14109
val port get() = uri.port
15110
val host get() = uri.host
111+
val path get() = uri.path
16112

17113
fun getQueryParameterNames(): Set<String> {
18114
val query: String = uri.query ?: return emptySet()
@@ -66,3 +162,8 @@ class Uri(private val uri: URI) {
66162
return null
67163
}
68164
}
165+
166+
/** Index of a component which was not found. */
167+
private const val NOT_FOUND = -1
168+
169+
private val HEX_DIGITS = "0123456789ABCDEF".toCharArray()
+51
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
import android.app.Application
2+
import android.net.Uri
3+
import com.google.firebase.Firebase
4+
import com.google.firebase.FirebaseApp
5+
import com.google.firebase.FirebaseOptions
6+
import com.google.firebase.FirebasePlatform
7+
import com.google.firebase.initialize
8+
import com.google.firebase.storage.internal.Slashes
9+
import com.google.firebase.storage.storage
10+
import fakes.FakeFirebasePlatform
11+
import org.junit.After
12+
import org.junit.Assert
13+
import org.junit.Before
14+
import org.junit.Test
15+
import org.mockito.Mockito.mock
16+
import java.io.File
17+
18+
class FirestoreStorageTest : FirebaseTest() {
19+
20+
private lateinit var app: FirebaseApp
21+
22+
@Before
23+
fun initialize() {
24+
FirebasePlatform.initializeFirebasePlatform(FakeFirebasePlatform())
25+
val options = FirebaseOptions
26+
.Builder()
27+
.setProjectId("my-firebase-project")
28+
.setApplicationId("1:27992087142:android:ce3b6448250083d1")
29+
.setApiKey("AIzaSyADUe90ULnQDuGShD9W23RDP0xmeDc6Mvw")
30+
.setStorageBucket("fir-kotlin-sdk.appspot.com")
31+
// setDatabaseURL(...)
32+
.build()
33+
app = Firebase.initialize(Application(), options)
34+
}
35+
36+
@Test
37+
fun test_something() {
38+
val input = "gs://edifikana-stage.appspot.com"
39+
40+
val normalized = Slashes.normalizeSlashes(input.substring(5))
41+
val fullUri = Slashes.preserveSlashEncode(normalized)
42+
val parsedUri = Uri.parse("gs://" + fullUri);
43+
44+
Assert.assertNotNull(parsedUri)
45+
}
46+
47+
@Test
48+
fun test_loading_storage() {
49+
Firebase.storage
50+
}
51+
}

src/test/kotlin/FirestoreTest.kt

+2-8
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import com.google.firebase.FirebaseOptions
44
import com.google.firebase.FirebasePlatform
55
import com.google.firebase.firestore.firestore
66
import com.google.firebase.initialize
7+
import fakes.FakeFirebasePlatform
78
import kotlinx.coroutines.runBlocking
89
import kotlinx.coroutines.tasks.await
910
import org.junit.Assert.assertEquals
@@ -14,14 +15,7 @@ import java.io.File
1415
class FirestoreTest : FirebaseTest() {
1516
@Before
1617
fun initialize() {
17-
FirebasePlatform.initializeFirebasePlatform(object : FirebasePlatform() {
18-
val storage = mutableMapOf<String, String>()
19-
override fun store(key: String, value: String) = storage.set(key, value)
20-
override fun retrieve(key: String) = storage[key]
21-
override fun clear(key: String) { storage.remove(key) }
22-
override fun log(msg: String) = println(msg)
23-
override fun getDatabasePath(name: String) = File("./build/$name")
24-
})
18+
FirebasePlatform.initializeFirebasePlatform(FakeFirebasePlatform())
2519
val options = FirebaseOptions.Builder()
2620
.setProjectId("my-firebase-project")
2721
.setApplicationId("1:27992087142:android:ce3b6448250083d1")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package fakes
2+
3+
import com.google.firebase.FirebasePlatform
4+
import java.io.File
5+
6+
/**
7+
* Fake used to store firebase data during testing. The [storage] is made purposefully public to allow for direct
8+
* access and modification if needed.
9+
*/
10+
class FakeFirebasePlatform(
11+
val storage: MutableMap<String, String> = mutableMapOf(),
12+
databaseFolderPath: String = "./build/database/"
13+
) : FirebasePlatform() {
14+
15+
private val databaseFolder = File(databaseFolderPath)
16+
17+
override fun store(key: String, value: String) { storage[key] = value }
18+
19+
override fun retrieve(key: String) = storage[key]
20+
21+
override fun clear(key: String) { storage.remove(key) }
22+
23+
override fun log(msg: String) = println(msg)
24+
25+
override fun getDatabasePath(name: String) = File(databaseFolder, name)
26+
}

0 commit comments

Comments
 (0)