Skip to content

Commit 22d4042

Browse files
refactor: Migrate Internal ProviderPersistence class to kotlin (#533)
1 parent 54a8b6f commit 22d4042

File tree

2 files changed

+131
-149
lines changed

2 files changed

+131
-149
lines changed

android-core/src/main/java/com/mparticle/internal/ProviderPersistence.java

Lines changed: 0 additions & 149 deletions
This file was deleted.
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
package com.mparticle.internal
2+
3+
import android.content.Context
4+
import com.mparticle.internal.MPUtility.isEmpty
5+
import org.json.JSONObject
6+
import java.security.SecureRandom
7+
import java.util.Locale
8+
import java.util.UUID
9+
import java.util.regex.Pattern
10+
11+
/**
12+
* This class is responsible for pulling persistence from files from *other* SDKs, and serializing itself as a part of a batch.
13+
* The idea here is that a customer may want to remove an SDK from their app and move it
14+
* server side via mParticle. Rather than start from stratch, it's crucial that we can query data that
15+
* the given SDK had been storing client-side.
16+
*/
17+
internal class ProviderPersistence(config: JSONObject, context: Context) : JSONObject() {
18+
init {
19+
val configPersistence = config.getJSONArray(KEY_PERSISTENCE)
20+
for (i in 0 until configPersistence.length()) {
21+
val values = JSONObject()
22+
val jsonObjectAtIndex = configPersistence.getJSONObject(i)
23+
if (jsonObjectAtIndex.has(KEY_PERSISTENCE_ANDROID)) {
24+
val files = jsonObjectAtIndex.getJSONArray(KEY_PERSISTENCE_ANDROID)
25+
26+
for (fileIndex in 0 until files.length()) {
27+
val fileObject = files.getJSONObject(fileIndex)
28+
val preferences =
29+
context.getSharedPreferences(fileObject.getString(KEY_PERSISTENCE_FILE), fileObject.getInt(KEY_PERSISTENCE_MODE))
30+
val fileObjects = fileObject.getJSONArray(KEY_PERSISTENCE_KEY_LIST)
31+
val editor = preferences.edit()
32+
for (keyIndex in 0 until fileObjects.length()) {
33+
val fileObjectsAtKeyIndex = fileObjects.getJSONObject(keyIndex)
34+
val type = fileObjectsAtKeyIndex.getInt(KEY_PERSISTENCE_TYPE)
35+
val key = fileObjectsAtKeyIndex.getString(KEY_PERSISTENCE_KEY)
36+
val mpKey = fileObjectsAtKeyIndex.getString(KEY_PERSISTENCE_MPVAR)
37+
val mpPersistenceKey = MPPREFIX + mpKey
38+
if (preferences.contains(mpPersistenceKey)) {
39+
values.put(mpKey, preferences.getString(mpPersistenceKey, null))
40+
} else {
41+
var resolvedValue: String? = null
42+
if (preferences.contains(key)) {
43+
when (type) {
44+
PERSISTENCE_TYPE_STRING -> resolvedValue = preferences.getString(key, resolvedValue)
45+
PERSISTENCE_TYPE_INT -> resolvedValue = preferences.getInt(key, 0).toString()
46+
PERSISTENCE_TYPE_BOOLEAN -> resolvedValue = preferences.getBoolean(key, false).toString()
47+
PERSISTENCE_TYPE_FLOAT -> resolvedValue = preferences.getFloat(key, 0f).toString()
48+
PERSISTENCE_TYPE_LONG -> resolvedValue = preferences.getLong(key, 0).toString()
49+
}
50+
} else {
51+
resolvedValue = applyMacro(fileObjects.getJSONObject(keyIndex).getString(KEY_PERSISTENCE_DEFAULT))
52+
}
53+
54+
editor.putString(mpPersistenceKey, resolvedValue)
55+
editor.apply()
56+
values.put(mpKey, resolvedValue)
57+
}
58+
}
59+
}
60+
}
61+
put(configPersistence.getJSONObject(i).getInt(KEY_PERSISTENCE_ID).toString(), values)
62+
}
63+
}
64+
65+
companion object {
66+
const val KEY_PERSISTENCE: String = "cms"
67+
private const val KEY_PERSISTENCE_ID = "id"
68+
private const val KEY_PERSISTENCE_ANDROID = "pr"
69+
private const val KEY_PERSISTENCE_FILE = "f"
70+
private const val KEY_PERSISTENCE_MODE = "m"
71+
private const val KEY_PERSISTENCE_KEY_LIST = "ps"
72+
private const val KEY_PERSISTENCE_KEY = "k"
73+
private const val KEY_PERSISTENCE_TYPE = "t"
74+
private const val KEY_PERSISTENCE_MPVAR = "n"
75+
private const val KEY_PERSISTENCE_DEFAULT = "d"
76+
private const val MPPREFIX = "mp::"
77+
78+
private const val PERSISTENCE_TYPE_STRING = 1
79+
private const val PERSISTENCE_TYPE_INT = 2
80+
private const val PERSISTENCE_TYPE_BOOLEAN = 3
81+
private const val PERSISTENCE_TYPE_FLOAT = 4
82+
private const val PERSISTENCE_TYPE_LONG = 5
83+
84+
85+
private const val MACRO_GUID_NO_DASHES = "%gn%"
86+
private const val MACRO_OMNITURE_AID = "%oaid%"
87+
private const val MACRO_GUID = "%g%"
88+
private const val MACRO_TIMESTAMP = "%ts%"
89+
private const val MACRO_GUID_LEAST_SIG = "%glsb%"
90+
91+
/**
92+
* Macros are used so that the /config API call can come from a CDN (not user-specific).
93+
*/
94+
private fun applyMacro(defaultString: String): String {
95+
var defaultString = defaultString
96+
if (!isEmpty(defaultString) && defaultString.startsWith("%")) {
97+
defaultString = defaultString.lowercase(Locale.getDefault())
98+
return when {
99+
defaultString.equals(MACRO_GUID_NO_DASHES, ignoreCase = true) -> UUID.randomUUID().toString().replace("-", "")
100+
defaultString == MACRO_OMNITURE_AID -> generateAID()
101+
defaultString == MACRO_GUID -> UUID.randomUUID().toString()
102+
defaultString == MACRO_TIMESTAMP -> System.currentTimeMillis().toString()
103+
defaultString == MACRO_GUID_LEAST_SIG -> UUID.randomUUID().leastSignificantBits.toString()
104+
else -> defaultString
105+
}
106+
}
107+
return defaultString
108+
}
109+
110+
private fun generateAID(): String {
111+
var uuid = UUID.randomUUID().toString().replace("-", "")
112+
uuid = uuid.uppercase(Locale.getDefault())
113+
114+
val firstPattern = Pattern.compile("^[89A-F]")
115+
val secondPattern = Pattern.compile("^[4-9A-F]")
116+
val firstMatcher = firstPattern.matcher(uuid.substring(0, 16))
117+
val secondMatcher = secondPattern.matcher(uuid.substring(16, 32))
118+
119+
val r = SecureRandom()
120+
val vi_hi = firstMatcher.replaceAll(r.nextInt(7).toString())
121+
val vi_lo = secondMatcher.replaceAll(r.nextInt(3).toString())
122+
123+
val aidBuilder = StringBuilder(33)
124+
aidBuilder.append(vi_hi)
125+
aidBuilder.append("-")
126+
aidBuilder.append(vi_lo)
127+
128+
return aidBuilder.toString()
129+
}
130+
}
131+
}

0 commit comments

Comments
 (0)