Skip to content

Commit

Permalink
Feature/native storage.shared.preferences (#261)
Browse files Browse the repository at this point in the history
* Prevent NativeStorage to be an object. But allow to get it from views: Views.storage
Make android to use SharedPreferences instead of files
  • Loading branch information
soywiz authored Jul 10, 2020
1 parent 2fd4a16 commit d102a4c
Show file tree
Hide file tree
Showing 12 changed files with 76 additions and 76 deletions.
Original file line number Diff line number Diff line change
@@ -1,51 +1,31 @@
package com.soywiz.korge.service.storage

import java.io.*
import java.util.*
import android.content.*
import com.soywiz.korge.view.*
import com.soywiz.korgw.*

actual object NativeStorage : IStorage {
val props = Properties()
actual class NativeStorage actual constructor(val views: Views) : IStorage {
private val context = (views.gameWindow as AndroidGameWindow).androidContext
private val preferences = context.getSharedPreferences("KorgeNativeStorage", Context.MODE_PRIVATE)

init {
load()
}

private fun load() {
try {
FileInputStream(File("game.storage")).use { fis ->
props.load(fis)
}
} catch (e: IOException) {
e.printStackTrace()
}
}

private fun save() {
try {
FileOutputStream(File("game.storage")).use { fout ->
props.store(fout, "")
}
} catch (e: IOException) {
e.printStackTrace()
}
}
private inline fun edit(block: SharedPreferences.Editor.() -> Unit) {
preferences.edit().apply {
block()
apply()
}
}

actual override fun set(key: String, value: String) {
props[key] = value
save()
edit { putString(key, value) }
}

actual override fun getOrNull(key: String): String? {
return props[key]?.toString()
}
actual override fun getOrNull(key: String): String? = preferences.getString(key, null)

actual override fun remove(key: String) {
props.remove(key)
save()
edit { remove(key) }
}

actual override fun removeAll() {
props.clear()
save()
edit { removeAll() }
}
}
1 change: 1 addition & 0 deletions korge/src/commonMain/kotlin/com/soywiz/korge/Korge.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import com.soywiz.korge.internal.*
import com.soywiz.korge.logger.*
import com.soywiz.korge.resources.*
import com.soywiz.korge.scene.*
import com.soywiz.korge.service.storage.*
import com.soywiz.korge.stat.*
import com.soywiz.korge.time.*
import com.soywiz.korge.view.*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,15 @@
package com.soywiz.korge.service.storage

import com.soywiz.kds.*
import com.soywiz.korge.view.*

/** Cross-platform way of synchronously storing small data */
expect object NativeStorage : IStorage {
expect class NativeStorage(views: Views) : IStorage {
override fun set(key: String, value: String)
override fun getOrNull(key: String): String?
override fun remove(key: String)
override fun removeAll()
}

val Views.storage: NativeStorage by Extra.PropertyThis<Views, NativeStorage> { NativeStorage(this) }
val ViewsContainer.storage: NativeStorage get() = this.views.storage
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.soywiz.korge.service.storage

import com.soywiz.korge.view.*
import com.soywiz.korinject.*

//@Singleton
open class Storage : IStorage by NativeStorage
open class Storage(views: Views) : IStorage by NativeStorage(views)
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,25 @@ import kotlin.reflect.*

class StorageItem<T : Any>(val storage: IStorage, val clazz: KClass<T>, val key: String, val mapper: ObjectMapper, val gen: (() -> T)?) {
val isDefined: Boolean get() = key in storage
@Suppress("UNCHECKED_CAST")
val realGen: (() -> T)? = when {
gen != null -> gen
else -> when (clazz) {
Boolean::class -> ({ false } as (() -> T))
Int::class -> ({ 0 } as (() -> T))
Long::class -> ({ 0L } as (() -> T))
Float::class -> ({ 0f } as (() -> T))
Double::class -> ({ 0.0 } as (() -> T))
String::class -> ({ "" } as (() -> T))
else -> null
}
}
var value: T
set(value) { storage[key] = Json.stringify(mapper.toUntyped(clazz, value)) }
get () {
if (!isDefined) storage[key] = Json.stringify(mapper.toUntyped(clazz,
gen?.invoke() ?: error("Can't find '$key' and no default generator was defined")
realGen?.invoke()
?: error("Can't find '$key' and no default generator was defined")
))
return Json.parseTyped(clazz, storage[key], mapper)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ open class ViewsForTesting @JvmOverloads constructor(
}

// @TODO: Run a faster eventLoop where timers happen much faster
fun viewsTest(timeout: TimeSpan? = DEFAULT_SUSPEND_TEST_TIMEOUT, block: suspend Stage.() -> Unit): Unit = suspendTest(timeout = timeout, cond = { !OS.isNative }) {
fun viewsTest(timeout: TimeSpan? = DEFAULT_SUSPEND_TEST_TIMEOUT, block: suspend Stage.() -> Unit): Unit = suspendTest(timeout = timeout, cond = { !OS.isNative && !OS.isAndroid }) {
Korge.prepareViewsBase(views, gameWindow, fixedSizeStep = frameTime)

injector.mapInstance<Module>(object : Module() {
Expand Down
2 changes: 1 addition & 1 deletion korge/src/commonMain/kotlin/com/soywiz/korge/view/Stage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ class Stage(override val views: Views) : Container()
, View.Reference
, CoroutineScope by views
, EventDispatcher by EventDispatcher.Mixin()
, ViewsScope
, ViewsScope, ViewsContainer
{
val injector get() = views.injector
val ag get() = views.ag
Expand Down
14 changes: 8 additions & 6 deletions korge/src/commonMain/kotlin/com/soywiz/korge/view/Views.kt
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import com.soywiz.korge.annotations.*
import com.soywiz.korge.input.*
import com.soywiz.korge.internal.*
import com.soywiz.korge.render.*
import com.soywiz.korge.service.storage.*
import com.soywiz.korge.stat.*
import com.soywiz.korgw.*
import com.soywiz.korim.bitmap.*
Expand Down Expand Up @@ -42,13 +43,13 @@ class Views constructor(
val timeProvider: HRTimeProvider,
val stats: Stats,
val gameWindow: GameWindow
) : Extra by Extra.Mixin(), EventDispatcher by EventDispatcher.Mixin(), CoroutineScope, ViewsScope,
) : Extra by Extra.Mixin(), EventDispatcher by EventDispatcher.Mixin(), CoroutineScope, ViewsScope, ViewsContainer,
BoundsProvider, DialogInterface by gameWindow, AsyncCloseable {
override val views = this

val keys get() = input.keys

var imageFormats = RegisteredImageFormats
var imageFormats = RegisteredImageFormats
val renderContext = RenderContext(ag, this, stats, coroutineContext)
val agBitmapTextureManager = renderContext.agBitmapTextureManager
var clearEachFrame = true
Expand Down Expand Up @@ -375,6 +376,11 @@ fun Views.texture(width: Int, height: Int, mipmaps: Boolean = false) =
suspend fun Views.texture(bmp: ByteArray, mipmaps: Boolean = false): Texture =
texture(nativeImageFormatProvider.decode(bmp), mipmaps)

@Deprecated("Use ViewsContainer")
interface ViewsScope {
val views: Views
}

interface ViewsContainer {
val views: Views
}
Expand Down Expand Up @@ -468,7 +474,3 @@ interface BoundsProvider {
override val virtualBottom: Double = 0.0
}
}

interface ViewsScope {
val views: Views
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,17 @@
package com.soywiz.korge.storage

import com.soywiz.korge.service.storage.NativeStorage
import com.soywiz.korge.service.storage.item
import kotlin.test.BeforeTest
import kotlin.test.Test
import kotlin.test.assertEquals
import kotlin.test.assertFailsWith

class NativeStorageTest {
@BeforeTest
fun cleanup() {
NativeStorage.removeAll()
}
import com.soywiz.korge.service.storage.*
import com.soywiz.korge.tests.*
import kotlin.test.*

class NativeStorageTest : ViewsForTesting() {
@Test
fun test() {
val demo = NativeStorage.item<Int>("hello")
fun test() = viewsTest {
views.storage.removeAll()
val demo = views.storage.item<Int>("hello")
assertEquals(false, demo.isDefined)
assertFailsWith<Throwable> { demo.value }
assertEquals(0, demo.value)
//assertFailsWith<Throwable> { demo.value }
demo.value = 10
assertEquals(true, demo.isDefined)
assertEquals(10, demo.value)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
package com.soywiz.korge.service.storage

import com.soywiz.korge.view.*
import kotlin.browser.*

actual object NativeStorage : IStorage {
actual class NativeStorage actual constructor(val views: Views) : IStorage {
actual override fun set(key: String, value: String) {
localStorage.setItem(key, value)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,30 @@
package com.soywiz.korge.service.storage

import com.soywiz.korge.view.*
import java.io.*
import java.util.*

actual object NativeStorage : IStorage {
actual class NativeStorage actual constructor(val views: Views) : IStorage {
val props = Properties()
val file = File("game.storage")

init {
load()
}

private fun load() {
try {
FileInputStream(File("game.storage")).use { fis ->
props.load(fis)
}
} catch (e: IOException) {
e.printStackTrace()
}
}
if (!file.exists()) return

try {
FileInputStream(file).use { fis -> props.load(fis) }
} catch (e: IOException) {
e.printStackTrace()
}
}

private fun save() {
try {
FileOutputStream(File("game.storage")).use { fout ->
props.store(fout, "")
}
FileOutputStream(file).use { fout -> props.store(fout, "") }
} catch (e: IOException) {
e.printStackTrace()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ package com.soywiz.korge.service.storage
import com.soywiz.kds.*
import com.soywiz.kds.atomic.KdsAtomicRef
import com.soywiz.korge.native.*
import com.soywiz.korge.view.*
import com.soywiz.korio.lang.*
import com.soywiz.korio.serialization.json.*

actual object NativeStorage : IStorage {
actual class NativeStorage actual constructor(val views: Views) : IStorage {
private fun saveStr(data: String) = KorgeSimpleNativeSyncIO.writeBytes("settings.json", data.toByteArray(UTF8))
private fun loadStr(): String = KorgeSimpleNativeSyncIO.readBytes("settings.json").toString(UTF8)

Expand Down

0 comments on commit d102a4c

Please sign in to comment.