Skip to content

Commit

Permalink
Merge pull request #32 from wkarl/feature/PLAY-3895-local-storage
Browse files Browse the repository at this point in the history
 Default implementation for local storage (@wkarl)
  • Loading branch information
Eridana authored Oct 25, 2022
2 parents b817062 + 6fb639c commit e2923fa
Show file tree
Hide file tree
Showing 8 changed files with 151 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -18,14 +18,18 @@
import org.junit.After;
import org.junit.BeforeClass;
import org.junit.Test;

import androidx.test.platform.app.InstrumentationRegistry;
import timber.log.Timber;
import static org.junit.Assert.assertEquals;

import android.content.Context;
import android.os.RemoteException;

// Minimal test for using JsBridge from Java
public final class JsBridgeJavaTest {
private JsBridge jsBridge;

private final Context context = InstrumentationRegistry.getInstrumentation().getContext();
@BeforeClass
static public void setUpClass() {
Timber.plant(new Timber.DebugTree());
Expand Down Expand Up @@ -149,7 +153,7 @@ public void triggerCallback(TestAidlCallback cb) throws RemoteException {
// ---

private JsBridge createAndSetUpJsBridge() {
JsBridge jsBridge = new JsBridge(JsBridgeConfig.standardConfig());
JsBridge jsBridge = new JsBridge(JsBridgeConfig.standardConfig(), context);
this.jsBridge = jsBridge;
return jsBridge;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1161,7 +1161,7 @@ class JsBridgeTest {
val config = JsBridgeConfig.standardConfig().apply {
xhrConfig.okHttpClient = okHttpClient
}
val subject = JsBridge(config)
val subject = JsBridge(config, context)

val jsExpectations = JsExpectations()
val jsExpectationsJsValue = JsValue.fromNativeObject(subject, jsExpectations)
Expand Down Expand Up @@ -1532,7 +1532,7 @@ class JsBridgeTest {
val config = JsBridgeConfig.standardConfig().apply {
xhrConfig.okHttpClient = okHttpClient
}
val subject = JsBridge(config)
val subject = JsBridge(config, context)

val jsExpectations = JsExpectations()
val jsExpectationsJsValue = JsValue.fromNativeObject(subject, jsExpectations)
Expand Down Expand Up @@ -2572,7 +2572,7 @@ class JsBridgeTest {
messages.add(priority to message)
}
}
val subject = JsBridge(config)
val subject = JsBridge(config, context)
jsBridge = subject

// WHEN
Expand Down Expand Up @@ -2610,7 +2610,7 @@ class JsBridgeTest {
messages.add(priority to message)
}
}
val subject = JsBridge(config)
val subject = JsBridge(config, context)
jsBridge = subject

// WHEN
Expand Down Expand Up @@ -2648,7 +2648,7 @@ class JsBridgeTest {
hasMessage = true
}
}
val subject = JsBridge(config)
val subject = JsBridge(config, context)
jsBridge = subject

// WHEN
Expand Down Expand Up @@ -2818,6 +2818,31 @@ class JsBridgeTest {
}
}

@Test
fun testLocalStorage() {
// GIVEN
val subject = createAndSetUpJsBridge()

// WHEN
subject.evaluateBlocking<Unit>("""localStorage.setItem("foo", "bar");""")
val result = subject.evaluateBlocking<String>("""localStorage.getItem("foo");""")

// THEN
assertEquals(result, "bar")
assertTrue(errors.isEmpty())

// GIVEN
val subjectNoLocalStorage = createAndSetUpJsBridge(JsBridgeConfig.standardConfig().apply {
localStorageConfig.useDefaultLocalStorage = false
})

// WHEN
val localStorageNotSet = subjectNoLocalStorage.evaluateBlocking<Boolean>("""typeof localStorage == 'undefined';""")

// THEN
assertTrue(localStorageNotSet)
assertTrue(errors.isEmpty())
}

// JsExpectations
// ---
Expand Down Expand Up @@ -2860,18 +2885,20 @@ class JsBridgeTest {
// Private methods
// ---

private fun createAndSetUpJsBridge(): JsBridge {
val config = JsBridgeConfig.standardConfig().apply {
private fun createAndSetUpJsBridge(
config: JsBridgeConfig = JsBridgeConfig.standardConfig().apply {
xhrConfig.okHttpClient = okHttpClient
}
): JsBridge {

return JsBridge(config).also { jsBridge ->
return JsBridge(config, context).also { jsBridge ->
this@JsBridgeTest.jsBridge = jsBridge

jsBridge.registerErrorListener(createErrorListener())

// Map "jsToNativeFunctionMock" to JS as a global var "NativeFunctionMock"
JsValue.fromNativeFunction1(jsBridge, jsToNativeFunctionMock).assignToGlobal("nativeFunctionMock")
JsValue.fromNativeFunction1(jsBridge, jsToNativeFunctionMock)
.assignToGlobal("nativeFunctionMock")
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
*/
package de.prosiebensat1digital.oasisjsbridge

import android.content.Context
import androidx.test.platform.app.InstrumentationRegistry
import kotlin.test.assertEquals
import kotlinx.coroutines.*
Expand All @@ -26,6 +27,7 @@ import timber.log.Timber

class ReadmeTest {
private lateinit var jsBridge: JsBridge
private val context: Context = InstrumentationRegistry.getInstrumentation().context

companion object {
@BeforeClass
Expand All @@ -37,7 +39,7 @@ class ReadmeTest {

@Before
fun setUp() {
jsBridge = JsBridge(JsBridgeConfig.standardConfig())
jsBridge = JsBridge(JsBridgeConfig.standardConfig(), context)
}

@After
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ import java.lang.reflect.Proxy
// can be safely called in a "synchronous" way. though, because their executions are guaranteed
// to be performed sequentially (via an internal queue).
class JsBridge
constructor(config: JsBridgeConfig): CoroutineScope {
constructor(config: JsBridgeConfig, context: Context) : CoroutineScope {

companion object {
private var isLibraryLoaded = false
Expand Down Expand Up @@ -95,6 +95,7 @@ constructor(config: JsBridgeConfig): CoroutineScope {
private var setTimeoutExtension: SetTimeoutExtension? = null
private var consoleExtension: ConsoleExtension? = null
private var xhrExtension: XMLHttpRequestExtension? = null
private var localStorageExtension: LocalStorageExtension? = null

private var internalCounter = AtomicInteger(0)

Expand Down Expand Up @@ -152,6 +153,8 @@ constructor(config: JsBridgeConfig): CoroutineScope {
consoleExtension = ConsoleExtension(this@JsBridge, config.consoleConfig)
if (config.xhrConfig.enabled)
xhrExtension = XMLHttpRequestExtension(this@JsBridge, config.xhrConfig)
if (config.localStorageConfig.enabled)
localStorageExtension = LocalStorageExtension(this@JsBridge, config.localStorageConfig, context.applicationContext)
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ private constructor() {
xhrConfig.enabled = true
promiseConfig.enabled = true
consoleConfig.enabled = true
localStorageConfig.enabled = true
}
}

Expand All @@ -23,6 +24,7 @@ private constructor() {
val promiseConfig = PromiseConfig()
val consoleConfig = ConsoleConfig()
val jsDebuggerConfig = JsDebuggerConfig()
val localStorageConfig = LocalStorageConfig()

class SetTimeoutExtensionConfig {
var enabled: Boolean = false
Expand Down Expand Up @@ -55,4 +57,9 @@ private constructor() {
var enabled: Boolean = false
var port: Int = 9092
}

class LocalStorageConfig {
var enabled: Boolean = false
var useDefaultLocalStorage: Boolean = true
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
package de.prosiebensat1digital.oasisjsbridge.extensions

import android.content.Context

import de.prosiebensat1digital.oasisjsbridge.JsToNativeInterface
import de.prosiebensat1digital.oasisjsbridge.JsonObjectWrapper
import timber.log.Timber

interface LocalStorageInteface : JsToNativeInterface {
fun setItem(keyName: JsonObjectWrapper, keyValue: JsonObjectWrapper)
fun getItem(keyName: JsonObjectWrapper): JsonObjectWrapper
fun removeItem(keyName: JsonObjectWrapper)
fun clear()
}

class LocalStorage(context: Context) : LocalStorageInteface {

private val localStoragePreferences = context.getSharedPreferences(
context.applicationInfo.packageName + ".LOCAL_STORAGE_PREFERENCE_FILE_KEY",
Context.MODE_PRIVATE
)

override fun setItem(keyName: JsonObjectWrapper, keyValue: JsonObjectWrapper) {
with(localStoragePreferences.edit()) {
putString(keyName.jsonString, keyValue.jsonString)
Timber.d("Saving to local storage -> key: ${keyName.jsonString} value: ${keyValue.jsonString}")
commit()
}
}

override fun getItem(keyName: JsonObjectWrapper): JsonObjectWrapper {
val value = localStoragePreferences.getString(keyName.jsonString, "null")
checkNotNull(value)

Timber.d("Loaded from local storage -> key: ${keyName.jsonString} value: $value")

// Note: default value is an unquoted null-string which is parsed as a null value (not "null"!)
return JsonObjectWrapper(value)
}

override fun removeItem(keyName: JsonObjectWrapper) {
with(localStoragePreferences.edit()) {
remove(keyName.jsonString)
Timber.d("Removing from local storage -> key: ${keyName.jsonString}")
commit()
}
}

override fun clear() {
with(localStoragePreferences.edit()) {
clear()
Timber.d("Clearing local storage")
commit()
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Copyright (C) 2019 ProSiebenSat1.Digital GmbH.
*
* 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
*
* http://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 de.prosiebensat1digital.oasisjsbridge.extensions

import android.content.Context
import de.prosiebensat1digital.oasisjsbridge.JsBridge
import de.prosiebensat1digital.oasisjsbridge.JsBridgeConfig
import de.prosiebensat1digital.oasisjsbridge.JsValue

internal class LocalStorageExtension(
private val jsBridge: JsBridge,
val config: JsBridgeConfig.LocalStorageConfig,
context: Context,
) {

init {
if (config.useDefaultLocalStorage) {
val localStorageJsValue: JsValue
val localStorage: LocalStorageInteface = LocalStorage(context)
localStorageJsValue = JsValue.fromNativeObject(jsBridge, localStorage)
localStorageJsValue.assignToGlobal("localStorage")
}
}
}

2 changes: 1 addition & 1 deletion version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
0.16.1
0.17.0

0 comments on commit e2923fa

Please sign in to comment.