Skip to content

Commit

Permalink
Use androidx.startup to find default DatabasesDir for Android Run…
Browse files Browse the repository at this point in the history
…time (#95)
  • Loading branch information
05nelsonm authored Dec 1, 2023
1 parent 4172e95 commit 0ddc477
Show file tree
Hide file tree
Showing 10 changed files with 174 additions and 51 deletions.
2 changes: 2 additions & 0 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
[versions]
androidx-startup = "1.1.1"
androidx-test-core = "1.5.0"
androidx-test-runner = "1.5.2"

Expand All @@ -22,6 +23,7 @@ sql-jdbc-xerial = "3.43.2.2"
slf4j = "1.7.36"

[libraries]
androidx-startup-runtime = { module = "androidx.startup:startup-runtime", version.ref = "androidx-startup" }
androidx-test-core = { module = "androidx.test:core", version.ref = "androidx-test-core" }
androidx-test-runner = { module = "androidx.test:runner", version.ref = "androidx-test-runner" }

Expand Down
1 change: 1 addition & 0 deletions library/driver/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ kmpConfiguration {
sourceSetMain {
dependencies {
implementation(files(jdbcRepack.jarSQLiteJDBCAndroid))
implementation(libs.androidx.startup.runtime)
}
}
}
Expand Down
17 changes: 16 additions & 1 deletion library/driver/src/androidMain/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,19 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*-->
<manifest />
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">

<application>
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="io.toxicity.sqlite.mc.driver.internal.SqliteMCInitializer"
android:value="androidx.startup" />
</provider>
</application>

</manifest>
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,6 @@
package io.toxicity.sqlite.mc.driver.config

import android.content.Context
import java.io.File
import io.toxicity.sqlite.mc.driver.internal.SqliteMCInitializer.Impl.Companion.databasesDirFile

public fun Context.databasesDir(): DatabasesDir {
val path = applicationContext.getDatabasePath("d")
val parent = path.parentFile ?: File(path.path.dropLast(1))

return DatabasesDir(parent)
}
public fun Context.databasesDir(): DatabasesDir = DatabasesDir(databasesDirFile())
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 Toxicity
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package io.toxicity.sqlite.mc.driver.internal

import java.io.File

internal actual val DEFAULT_DATABASES_DIR: File? by lazy {
SqliteMCInitializer.Impl.INSTANCE.databasesDir ?: run {

if (System.getProperty("java.runtime.name")?.contains("android", ignoreCase = true) == true) {
// It's android runtime and androidx.startup was not initialized... something
// is very wrong. Set to null so exception is lazily thrown.
null
} else {
// Android unit tests. Default to temporary directory for the host machine
System.getProperty("java.io.tmpdir")
?.ifBlank { null }
?.let { tmp -> File(tmp).resolve("sqlite_mc").resolve(".databases") }
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
/*
* Copyright (c) 2023 Toxicity
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package io.toxicity.sqlite.mc.driver.internal

import android.content.Context
import androidx.startup.AppInitializer
import androidx.startup.Initializer
import java.io.File

internal class SqliteMCInitializer: Initializer<SqliteMCInitializer.Impl> {

internal class Impl private constructor() {

internal companion object {

@JvmField
internal val INSTANCE: Impl = Impl()

@JvmStatic
internal fun Context.databasesDirFile(): File {
val path = applicationContext.getDatabasePath("d")
return path.parentFile ?: File(path.path.dropLast(1))
}
}

@get:JvmName("databasesDir")
internal var databasesDir: File? = null
private set

@get:JvmName("isInitialized")
internal val isInitialized: Boolean get() = databasesDir != null

@JvmSynthetic
internal fun init(context: Context) {
databasesDir = context.databasesDirFile()
}
}

override fun create(context: Context): Impl {
val appInitializer = AppInitializer.getInstance(context)
check(appInitializer.isEagerlyInitialized(javaClass)) {
"""
SqliteMCInitializer cannot be initialized lazily.
Please ensure that you have:
<meta-data
android:name='io.toxicity.sqlite.mc.driver.internal.SqliteMCInitializer'
android:value='androidx.startup' />
under InitializationProvider in your AndroidManifest.xml
""".trimIndent()
}
Impl.INSTANCE.init(context)
return Impl.INSTANCE
}

override fun dependencies(): List<Class<out Initializer<*>>> = emptyList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ import kotlin.jvm.JvmField
* to not throw exception when [DatabasesDir] is instantiated.
*
* Default Locations:
* - Android: `/data/user/{userid}/{your.application.id}/databases/`
* - Android Runtime: `/data/user/{userid}/{your.application.id}/databases/`
* - Android Unit Tests: `{tmp dir}/sqlite_mc/.databases`
* - Jvm - Unix: `~/.databases/`
* - Jvm - Mingw: `{drive}:\Users\{username}\.databases\`
* - Native - Apple: `~/Documents/databases/`
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
package io.toxicity.sqlite.mc.driver.config

import io.toxicity.sqlite.mc.driver.SQLiteMCDriver
import io.toxicity.sqlite.mc.driver.internal.DEFAULT_DATABASES_DIR
import java.io.File
import java.nio.file.Path
import kotlin.io.path.pathString
Expand All @@ -34,7 +35,8 @@ import kotlin.io.path.pathString
* to not throw exception when [DatabasesDir] is instantiated.
*
* Default Locations:
* - Android: `/data/user/{userid}/{your.application.id}/databases/`
* - Android Runtime: `/data/user/{userid}/{your.application.id}/databases/`
* - Android Unit Tests: `{tmp dir}/sqlite_mc/.databases`
* - Jvm - Unix: `~/.databases/`
* - Jvm - Mingw: `{drive}:\Users\{username}\.databases\`
*
Expand All @@ -50,50 +52,10 @@ public actual class DatabasesDir public actual constructor(path: String?) {
public actual val path: String? = if (!path.isNullOrBlank()) {
File(path).absolutePath
} else {
DEFAULT_DIR
DEFAULT_DATABASES_DIR?.absolutePath
}

public actual override fun equals(other: Any?): Boolean = other is DatabasesDir && other.path == path
public actual override fun hashCode(): Int = 17 * 31 + path.hashCode()
public actual override fun toString(): String = path ?: ""

private companion object {

private val DEFAULT_DIR: String? by lazy {
var defaultDir: File? = null

if (
System.getProperty("os.name")?.contains("Linux", ignoreCase = true) == true
&& System.getProperty("java.vendor")?.equals("The Android Project", ignoreCase = true) == true
) {
val cache: String? = System.getProperty("java.io.tmpdir")

defaultDir = if (
cache?.startsWith("/data/user/") == true
&& cache.endsWith("/cache")
) {
File(cache)
} else {
val dexcache: String? = System.getProperty("dexmaker.dexcache")

if (
dexcache?.startsWith("/data/user/") == true
&& dexcache.endsWith("/app_dxmaker_cache")
) {
File(dexcache)
} else {
null
}
}?.resolveSibling("databases")
}

if (defaultDir == null) {
defaultDir = System.getProperty("user.home")
?.ifBlank { null }
?.let { home -> File(home).resolve(".databases") }
}

defaultDir?.absolutePath
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/*
* Copyright (c) 2023 Toxicity
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package io.toxicity.sqlite.mc.driver.internal

import java.io.File

internal expect val DEFAULT_DATABASES_DIR: File?
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/*
* Copyright (c) 2023 Toxicity
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* https://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
**/
package io.toxicity.sqlite.mc.driver.internal

import java.io.File

internal actual val DEFAULT_DATABASES_DIR: File? by lazy {
System.getProperty("user.home")
?.ifBlank { null }
?.let { home -> File(home).resolve(".databases") }
}

0 comments on commit 0ddc477

Please sign in to comment.