diff --git a/android/buildSrc/build.gradle.kts b/android/buildSrc/build.gradle.kts
index 67897f605..c9add3c83 100644
--- a/android/buildSrc/build.gradle.kts
+++ b/android/buildSrc/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
id("org.jetbrains.kotlin.jvm") version "1.8.22"
+ `kotlin-dsl`
}
repositories {
diff --git a/android/pebblekit_android/build.gradle.kts b/android/pebblekit_android/build.gradle.kts
index b72afc06e..c404cd1d7 100644
--- a/android/pebblekit_android/build.gradle.kts
+++ b/android/pebblekit_android/build.gradle.kts
@@ -1,5 +1,6 @@
plugins {
alias(libs.plugins.android.library)
+ alias(libs.plugins.android.kotlin)
}
android {
@@ -26,12 +27,16 @@ android {
sourceCompatibility = JavaVersion.VERSION_1_8
targetCompatibility = JavaVersion.VERSION_1_8
}
+ kotlinOptions {
+ jvmTarget = "1.8"
+ }
}
dependencies {
implementation(libs.androidx.appcompat)
implementation(libs.material)
+ implementation(libs.androidx.core.ktx)
testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core)
diff --git a/android/pebblekit_android/src/main/java/com/getpebble/android/kit/util/PebbleDictionary.java b/android/pebblekit_android/src/main/java/com/getpebble/android/kit/util/PebbleDictionary.java
deleted file mode 100644
index 624ab24b7..000000000
--- a/android/pebblekit_android/src/main/java/com/getpebble/android/kit/util/PebbleDictionary.java
+++ /dev/null
@@ -1,342 +0,0 @@
-package com.getpebble.android.kit.util;
-
-import android.util.Base64;
-
-import org.json.JSONArray;
-import org.json.JSONException;
-import org.json.JSONObject;
-
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-
-/**
- * A collection of key-value pairs of heterogeneous types. PebbleDictionaries are the primary structure used to exchange
- * data between the phone and watch.
- *
- * To accommodate the mixed-types contained within a PebbleDictionary, an internal JSON representation is used when
- * exchanging the dictionary between Android processes.
- *
- * @author zulak@getpebble.com
- */
-public class PebbleDictionary implements Iterable {
-
- private static final String KEY = "key";
- private static final String TYPE = "type";
- private static final String LENGTH = "length";
- private static final String VALUE = "value";
-
- protected final Map tuples = new HashMap();
-
- /**
- * Deserializes a JSON representation of a PebbleDictionary.
- *
- * @param jsonString the JSON representation to be deserialized
- * @throws JSONException thrown if the specified JSON representation cannot be parsed
- */
- public static PebbleDictionary fromJson(String jsonString) throws JSONException {
- PebbleDictionary d = new PebbleDictionary();
-
- JSONArray elements = new JSONArray(jsonString);
- for (int idx = 0; idx < elements.length(); ++idx) {
- JSONObject o = elements.getJSONObject(idx);
- final int key = o.getInt(KEY);
- final PebbleTuple.TupleType type = PebbleTuple.TYPE_NAMES.get(o.getString(TYPE));
- final PebbleTuple.Width width = PebbleTuple.WIDTH_MAP.get(o.getInt(LENGTH));
-
- switch (type) {
- case BYTES:
- byte[] bytes = Base64.decode(o.getString(VALUE), Base64.NO_WRAP);
- d.addBytes(key, bytes);
- break;
- case STRING:
- d.addString(key, o.getString(VALUE));
- break;
- case INT:
- if (width == PebbleTuple.Width.BYTE) {
- d.addInt8(key, (byte) o.getInt(VALUE));
- } else if (width == PebbleTuple.Width.SHORT) {
- d.addInt16(key, (short) o.getInt(VALUE));
- } else if (width == PebbleTuple.Width.WORD) {
- d.addInt32(key, o.getInt(VALUE));
- }
- break;
- case UINT:
- if (width == PebbleTuple.Width.BYTE) {
- d.addUint8(key, (byte) o.getInt(VALUE));
- } else if (width == PebbleTuple.Width.SHORT) {
- d.addUint16(key, (short) o.getInt(VALUE));
- } else if (width == PebbleTuple.Width.WORD) {
- d.addUint32(key, o.getInt(VALUE));
- }
- break;
- }
- }
-
- return d;
- }
-
- private static JSONObject serializeTuple(PebbleTuple t) throws JSONException {
- JSONObject j = new JSONObject();
- j.put(KEY, t.key);
- j.put(TYPE, t.type.getName());
- j.put(LENGTH, t.width.value);
-
- switch (t.type) {
- case BYTES:
- j.put(VALUE, Base64.encodeToString((byte[]) t.value, Base64.NO_WRAP));
- break;
- case STRING:
- case INT:
- case UINT:
- j.put(VALUE, t.value);
- break;
- }
-
- return j;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public Iterator iterator() {
- return tuples.values().iterator();
- }
-
- /**
- * Returns the number of key-value pairs in this dictionary.
- *
- * @return the number of key-value pairs in this dictionary
- */
- public int size() {
- return tuples.size();
- }
-
- /**
- * Returns true if this dictionary contains a mapping for the specified key.
- *
- * @param key key whose presence in this dictionary is to be tested
- * @return true if this dictionary contains a mapping for the specified key
- */
- public boolean contains(final int key) {
- return tuples.containsKey(key);
- }
-
- /**
- * Removes the mapping for a key from this map if it is present.
- *
- * @param key key to be removed from the dictionary
- */
- public void remove(final int key) {
- tuples.remove(key);
- }
-
- /**
- * Associate the specified byte array with the provided key in the dictionary. If another key-value pair with the
- * same key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param bytes value to be associated with the specified key
- */
- public void addBytes(int key, byte[] bytes) {
- PebbleTuple t = PebbleTuple.create(key, PebbleTuple.TupleType.BYTES, PebbleTuple.Width.NONE, bytes);
- addTuple(t);
- }
-
- /**
- * Associate the specified String with the provided key in the dictionary. If another key-value pair with the same
- * key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param value value to be associated with the specified key
- */
- public void addString(int key, String value) {
- PebbleTuple t =
- PebbleTuple.create(key, PebbleTuple.TupleType.STRING, PebbleTuple.Width.NONE, value);
- addTuple(t);
- }
-
- /**
- * Associate the specified signed byte with the provided key in the dictionary. If another key-value pair with the
- * same key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param b value to be associated with the specified key
- */
- public void addInt8(final int key, final byte b) {
- PebbleTuple t = PebbleTuple.create(key, PebbleTuple.TupleType.INT, PebbleTuple.Width.BYTE, b);
- addTuple(t);
- }
-
- /**
- * Associate the specified unsigned byte with the provided key in the dictionary. If another key-value pair with the
- * same key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param b value to be associated with the specified key
- */
- public void addUint8(final int key, final byte b) {
- PebbleTuple t = PebbleTuple.create(key, PebbleTuple.TupleType.UINT, PebbleTuple.Width.BYTE, b);
- addTuple(t);
- }
-
- /**
- * Associate the specified signed short with the provided key in the dictionary. If another key-value pair with the
- * same key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param s value to be associated with the specified key
- */
- public void addInt16(final int key, final short s) {
- PebbleTuple t = PebbleTuple.create(key, PebbleTuple.TupleType.INT, PebbleTuple.Width.SHORT, s);
- addTuple(t);
- }
-
- /**
- * Associate the specified unsigned short with the provided key in the dictionary. If another key-value pair with
- * the same key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param s value to be associated with the specified key
- */
- public void addUint16(final int key, final short s) {
- PebbleTuple t = PebbleTuple.create(key, PebbleTuple.TupleType.UINT, PebbleTuple.Width.SHORT, s);
- addTuple(t);
- }
-
- /**
- * Associate the specified signed int with the provided key in the dictionary. If another key-value pair with the
- * same key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param i value to be associated with the specified key
- */
- public void addInt32(final int key, final int i) {
- PebbleTuple t = PebbleTuple.create(key, PebbleTuple.TupleType.INT, PebbleTuple.Width.WORD, i);
- addTuple(t);
- }
-
- /**
- * Associate the specified unsigned int with the provided key in the dictionary. If another key-value pair with the
- * same key is already present in the dictionary, it will be replaced.
- *
- * @param key key with which the specified value is associated
- * @param i value to be associated with the specified key
- */
- public void addUint32(final int key, final int i) {
- PebbleTuple t = PebbleTuple.create(key, PebbleTuple.TupleType.UINT, PebbleTuple.Width.WORD, i);
- addTuple(t);
- }
-
- private PebbleTuple getTuple(int key, PebbleTuple.TupleType type) {
- if (!tuples.containsKey(key) || tuples.get(key) == null) {
- return null;
- }
-
- PebbleTuple t = tuples.get(key);
- if (t.type != type) {
- throw new PebbleDictTypeException(key, type, t.type);
- }
- return t;
- }
-
- /**
- * Returns the signed integer to which the specified key is mapped, or null if the key does not exist in this
- * dictionary.
- *
- * @param key key whose associated value is to be returned
- * @return value to which the specified key is mapped
- */
- public Long getInteger(int key) {
- PebbleTuple tuple = getTuple(key, PebbleTuple.TupleType.INT);
- if (tuple == null) {
- return null;
- }
- return (Long) tuple.value;
- }
-
- /**
- * Returns the unsigned integer as a long to which the specified key is mapped, or null if the key does not exist in this
- * dictionary. We are using the Long type here so that we can remove the guava dependency. This is done so that we dont
- * have incompatibility issues with the UnsignedInteger class from the Holo application, which uses a newer version of Guava.
- *
- * @param key key whose associated value is to be returned
- * @return value to which the specified key is mapped
- */
- public Long getUnsignedIntegerAsLong(int key) {
- PebbleTuple tuple = getTuple(key, PebbleTuple.TupleType.UINT);
- if (tuple == null) {
- return null;
- }
- return (Long) tuple.value;
- }
-
- /**
- * Returns the byte array to which the specified key is mapped, or null if the key does not exist in this
- * dictionary.
- *
- * @param key key whose associated value is to be returned
- * @return value to which the specified key is mapped
- */
- public byte[] getBytes(int key) {
- PebbleTuple tuple = getTuple(key, PebbleTuple.TupleType.BYTES);
- if (tuple == null) {
- return null;
- }
- return (byte[]) tuple.value;
- }
-
- /**
- * Returns the string to which the specified key is mapped, or null if the key does not exist in this dictionary.
- *
- * @param key key whose associated value is to be returned
- * @return value to which the specified key is mapped
- */
- public String getString(int key) {
- PebbleTuple tuple = getTuple(key, PebbleTuple.TupleType.STRING);
- if (tuple == null) {
- return null;
- }
- return (String) tuple.value;
- }
-
- public void addTuple(PebbleTuple tuple) {
- if (tuples.size() > 0xff) {
- throw new TupleOverflowException();
- }
-
- tuples.put(tuple.key, tuple);
- }
-
- /**
- * Returns a JSON representation of this dictionary.
- *
- * @return a JSON representation of this dictionary
- */
- public String toJsonString() {
- try {
- JSONArray array = new JSONArray();
- for (PebbleTuple t : tuples.values()) {
- array.put(serializeTuple(t));
- }
- return array.toString();
- } catch (JSONException je) {
- je.printStackTrace();
- }
- return null;
- }
-
- public static class PebbleDictTypeException extends RuntimeException {
- public PebbleDictTypeException(long key, PebbleTuple.TupleType expected, PebbleTuple.TupleType actual) {
- super(String.format(
- "Expected type '%s', but got '%s' for key 0x%08x", expected.name(), actual.name(), key));
- }
- }
-
- public static class TupleOverflowException extends RuntimeException {
- public TupleOverflowException() {
- super("Too many tuples in dict");
- }
- }
-}
diff --git a/android/pebblekit_android/src/main/java/com/getpebble/android/kit/util/PebbleDictionary.kt b/android/pebblekit_android/src/main/java/com/getpebble/android/kit/util/PebbleDictionary.kt
new file mode 100644
index 000000000..2ef476f1b
--- /dev/null
+++ b/android/pebblekit_android/src/main/java/com/getpebble/android/kit/util/PebbleDictionary.kt
@@ -0,0 +1,321 @@
+package com.getpebble.android.kit.util
+
+import android.util.Base64
+import com.getpebble.android.kit.util.PebbleTuple.TupleType
+import org.json.JSONArray
+import org.json.JSONException
+import org.json.JSONObject
+
+/**
+ * A collection of key-value pairs of heterogeneous types. PebbleDictionaries are the primary structure used to exchange
+ * data between the phone and watch.
+ *
+ *
+ * To accommodate the mixed-types contained within a PebbleDictionary, an internal JSON representation is used when
+ * exchanging the dictionary between Android processes.
+ *
+ * @author zulak@getpebble.com
+ */
+class PebbleDictionary : Iterable {
+ protected val tuples: MutableMap = HashMap()
+
+ /**
+ * {@inheritDoc}
+ */
+ override fun iterator(): MutableIterator {
+ return tuples.values.iterator()
+ }
+
+ /**
+ * Returns the number of key-value pairs in this dictionary.
+ *
+ * @return the number of key-value pairs in this dictionary
+ */
+ fun size(): Int {
+ return tuples.size
+ }
+
+ /**
+ * Returns true if this dictionary contains a mapping for the specified key.
+ *
+ * @param key key whose presence in this dictionary is to be tested
+ * @return true if this dictionary contains a mapping for the specified key
+ */
+ fun contains(key: Int): Boolean {
+ return tuples.containsKey(key)
+ }
+
+ /**
+ * Removes the mapping for a key from this map if it is present.
+ *
+ * @param key key to be removed from the dictionary
+ */
+ fun remove(key: Int) {
+ tuples.remove(key)
+ }
+
+ /**
+ * Associate the specified byte array with the provided key in the dictionary. If another key-value pair with the
+ * same key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param bytes value to be associated with the specified key
+ */
+ fun addBytes(key: Int, bytes: ByteArray?) {
+ val t = PebbleTuple.create(key, TupleType.BYTES, PebbleTuple.Width.NONE, bytes)
+ addTuple(t)
+ }
+
+ /**
+ * Associate the specified String with the provided key in the dictionary. If another key-value pair with the same
+ * key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param value value to be associated with the specified key
+ */
+ fun addString(key: Int, value: String?) {
+ val t =
+ PebbleTuple.create(key, TupleType.STRING, PebbleTuple.Width.NONE, value)
+ addTuple(t)
+ }
+
+ /**
+ * Associate the specified signed byte with the provided key in the dictionary. If another key-value pair with the
+ * same key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param b value to be associated with the specified key
+ */
+ fun addInt8(key: Int, b: Byte) {
+ val t = PebbleTuple.create(key, TupleType.INT, PebbleTuple.Width.BYTE, b.toInt())
+ addTuple(t)
+ }
+
+ /**
+ * Associate the specified unsigned byte with the provided key in the dictionary. If another key-value pair with the
+ * same key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param b value to be associated with the specified key
+ */
+ fun addUint8(key: Int, b: Byte) {
+ val t = PebbleTuple.create(key, TupleType.UINT, PebbleTuple.Width.BYTE, b.toInt())
+ addTuple(t)
+ }
+
+ /**
+ * Associate the specified signed short with the provided key in the dictionary. If another key-value pair with the
+ * same key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param s value to be associated with the specified key
+ */
+ fun addInt16(key: Int, s: Short) {
+ val t = PebbleTuple.create(key, TupleType.INT, PebbleTuple.Width.SHORT, s.toInt())
+ addTuple(t)
+ }
+
+ /**
+ * Associate the specified unsigned short with the provided key in the dictionary. If another key-value pair with
+ * the same key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param s value to be associated with the specified key
+ */
+ fun addUint16(key: Int, s: Short) {
+ val t = PebbleTuple.create(key, TupleType.UINT, PebbleTuple.Width.SHORT, s.toInt())
+ addTuple(t)
+ }
+
+ /**
+ * Associate the specified signed int with the provided key in the dictionary. If another key-value pair with the
+ * same key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param i value to be associated with the specified key
+ */
+ fun addInt32(key: Int, i: Int) {
+ val t = PebbleTuple.create(key, TupleType.INT, PebbleTuple.Width.WORD, i)
+ addTuple(t)
+ }
+
+ /**
+ * Associate the specified unsigned int with the provided key in the dictionary. If another key-value pair with the
+ * same key is already present in the dictionary, it will be replaced.
+ *
+ * @param key key with which the specified value is associated
+ * @param i value to be associated with the specified key
+ */
+ fun addUint32(key: Int, i: Int) {
+ val t = PebbleTuple.create(key, TupleType.UINT, PebbleTuple.Width.WORD, i)
+ addTuple(t)
+ }
+
+ private fun getTuple(key: Int, type: TupleType): PebbleTuple? {
+ if (!tuples.containsKey(key) || tuples[key] == null) {
+ return null
+ }
+
+ val t = tuples[key]
+ if (t!!.type != type) {
+ throw PebbleDictTypeException(key.toLong(), type, t.type)
+ }
+ return t
+ }
+
+ /**
+ * Returns the signed integer to which the specified key is mapped, or null if the key does not exist in this
+ * dictionary.
+ *
+ * @param key key whose associated value is to be returned
+ * @return value to which the specified key is mapped
+ */
+ fun getInteger(key: Int): Long? {
+ val tuple = getTuple(key, TupleType.INT) ?: return null
+ return tuple.value as Long
+ }
+
+ /**
+ * Returns the unsigned integer as a long to which the specified key is mapped, or null if the key does not exist in this
+ * dictionary. We are using the Long type here so that we can remove the guava dependency. This is done so that we dont
+ * have incompatibility issues with the UnsignedInteger class from the Holo application, which uses a newer version of Guava.
+ *
+ * @param key key whose associated value is to be returned
+ * @return value to which the specified key is mapped
+ */
+ fun getUnsignedIntegerAsLong(key: Int): Long? {
+ val tuple = getTuple(key, TupleType.UINT) ?: return null
+ return tuple.value as Long
+ }
+
+ /**
+ * Returns the byte array to which the specified key is mapped, or null if the key does not exist in this
+ * dictionary.
+ *
+ * @param key key whose associated value is to be returned
+ * @return value to which the specified key is mapped
+ */
+ fun getBytes(key: Int): ByteArray? {
+ val tuple = getTuple(key, TupleType.BYTES) ?: return null
+ return tuple.value as ByteArray
+ }
+
+ /**
+ * Returns the string to which the specified key is mapped, or null if the key does not exist in this dictionary.
+ *
+ * @param key key whose associated value is to be returned
+ * @return value to which the specified key is mapped
+ */
+ fun getString(key: Int): String? {
+ val tuple = getTuple(key, TupleType.STRING) ?: return null
+ return tuple.value as String
+ }
+
+ fun addTuple(tuple: PebbleTuple) {
+ if (tuples.size > 0xff) {
+ throw TupleOverflowException()
+ }
+
+ tuples[tuple.key] = tuple
+ }
+
+ /**
+ * Returns a JSON representation of this dictionary.
+ *
+ * @return a JSON representation of this dictionary
+ */
+ fun toJsonString(): String? {
+ try {
+ val array = JSONArray()
+ for (t in tuples.values) {
+ array.put(serializeTuple(t))
+ }
+ return array.toString()
+ } catch (je: JSONException) {
+ je.printStackTrace()
+ }
+ return null
+ }
+
+ class PebbleDictTypeException(key: Long, expected: TupleType, actual: TupleType) :
+ RuntimeException(
+ String.format(
+ "Expected type '%s', but got '%s' for key 0x%08x", expected.name, actual.name, key
+ )
+ )
+
+ class TupleOverflowException : RuntimeException("Too many tuples in dict")
+ companion object {
+ private const val KEY = "key"
+ private const val TYPE = "type"
+ private const val LENGTH = "length"
+ private const val VALUE = "value"
+
+ /**
+ * Deserializes a JSON representation of a PebbleDictionary.
+ *
+ * @param jsonString the JSON representation to be deserialized
+ * @throws JSONException thrown if the specified JSON representation cannot be parsed
+ */
+ @JvmStatic
+ @Throws(JSONException::class)
+ fun fromJson(jsonString: String?): PebbleDictionary {
+ val d = PebbleDictionary()
+
+ val elements = JSONArray(jsonString)
+ for (idx in 0 until elements.length()) {
+ val o = elements.getJSONObject(idx)
+ val key = o.getInt(KEY)
+ val type = PebbleTuple.TYPE_NAMES[o.getString(TYPE)]
+ val width = PebbleTuple.WIDTH_MAP[o.getInt(LENGTH)]
+
+ when (type) {
+ TupleType.BYTES -> {
+ val bytes = Base64.decode(o.getString(VALUE), Base64.NO_WRAP)
+ d.addBytes(key, bytes)
+ }
+
+ TupleType.STRING -> d.addString(key, o.getString(VALUE))
+ TupleType.INT -> if (width == PebbleTuple.Width.BYTE) {
+ d.addInt8(key, o.getInt(VALUE).toByte())
+ } else if (width == PebbleTuple.Width.SHORT) {
+ d.addInt16(key, o.getInt(VALUE).toShort())
+ } else if (width == PebbleTuple.Width.WORD) {
+ d.addInt32(key, o.getInt(VALUE))
+ }
+
+ TupleType.UINT -> if (width == PebbleTuple.Width.BYTE) {
+ d.addUint8(key, o.getInt(VALUE).toByte())
+ } else if (width == PebbleTuple.Width.SHORT) {
+ d.addUint16(key, o.getInt(VALUE).toShort())
+ } else if (width == PebbleTuple.Width.WORD) {
+ d.addUint32(key, o.getInt(VALUE))
+ }
+
+ else -> {}
+ }
+ }
+
+ return d
+ }
+
+ @Throws(JSONException::class)
+ private fun serializeTuple(t: PebbleTuple): JSONObject {
+ val j = JSONObject()
+ j.put(KEY, t.key)
+ j.put(TYPE, t.type.getName())
+ j.put(LENGTH, t.width.value)
+
+ when (t.type) {
+ TupleType.BYTES -> j.put(
+ VALUE,
+ Base64.encodeToString(t.value as ByteArray, Base64.NO_WRAP)
+ )
+
+ TupleType.STRING, TupleType.INT, TupleType.UINT -> j.put(VALUE, t.value)
+ }
+
+ return j
+ }
+ }
+}
diff --git a/android/shared/build.gradle.kts b/android/shared/build.gradle.kts
index efe103279..b2545a0ad 100644
--- a/android/shared/build.gradle.kts
+++ b/android/shared/build.gradle.kts
@@ -80,6 +80,9 @@ kotlin {
implementation(project(":pebblekit_android"))
implementation(project(":speex_codec"))
}
+ androidUnitTest.dependencies {
+ implementation(libs.junit)
+ }
commonTest.dependencies {
implementation(kotlin("test"))
implementation(libs.ktor.client.mock)
diff --git a/android/app/src/test/java/io/rebble/cobble/middleware/PebbleDictionaryConverterTest.kt b/android/shared/src/androidUnitTest/kotlin/io/rebble/cobble/middleware/PebbleDictionaryConverterTest.kt
similarity index 100%
rename from android/app/src/test/java/io/rebble/cobble/middleware/PebbleDictionaryConverterTest.kt
rename to android/shared/src/androidUnitTest/kotlin/io/rebble/cobble/middleware/PebbleDictionaryConverterTest.kt