Skip to content

Commit 364e41b

Browse files
authored
Refactor Config Import/Export (#66)
* update * update * add generatePrefsDoc to preBuild * add json config * update * add config
1 parent f594283 commit 364e41b

34 files changed

+1855
-319
lines changed

app/build.gradle

Lines changed: 209 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
* SPDX-License-Identifier: apache2
66
*/
77

8+
9+
import groovy.xml.Namespace
10+
811
plugins {
912
id 'com.android.application'
1013
id 'androidx.navigation.safeargs'
@@ -169,6 +172,209 @@ dependencies {
169172
implementation "com.squareup.moshi:moshi-adapters:1.15.2"
170173
}
171174

172-
configurations.implementation {
173-
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
174-
}
175+
176+
tasks.register('generatePrefsDoc') {
177+
def resXmlDir = file("$projectDir/src/main/res/xml")
178+
def stringsFile = file("$projectDir/src/main/res/values/strings.xml")
179+
def arraysFile = file("$projectDir/src/main/res/values/arrays.xml")
180+
def outputFile = file("$projectDir/../docs/preferences.md")
181+
def jsonOutputFileDoc = file("$projectDir/../docs/config.json")
182+
def jsonOutputFileResources = file("$projectDir/src/main/res/raw/config.json")
183+
184+
doLast {
185+
def parseStringsXml = { File xmlFile ->
186+
def parser = new XmlSlurper()
187+
def root = parser.parse(xmlFile)
188+
def map = [:]
189+
root.string.each { s ->
190+
map[s.@name.toString()] = s.text().trim()
191+
}
192+
return map
193+
}
194+
195+
def parseArraysXml = { File xmlFile ->
196+
if (!xmlFile.exists()) return [:]
197+
def parser = new XmlSlurper()
198+
def root = parser.parse(xmlFile)
199+
def map = [:]
200+
root.array.each { arr ->
201+
def name = arr.@name.toString()
202+
map[name] = arr.item.collect { it.text().trim() }
203+
}
204+
return map
205+
}
206+
207+
def stringsMap = stringsFile.exists() ? parseStringsXml(stringsFile) : [:]
208+
def arraysMap = arraysFile.exists() ? parseArraysXml(arraysFile) : [:]
209+
210+
def resolveRef = { String ref ->
211+
if (!ref) return ""
212+
if (ref.startsWith("@string/")) {
213+
def key = ref.substring(8)
214+
return stringsMap.get(key, ref)
215+
} else if (ref.startsWith("@array/")) {
216+
def key = ref.substring(7)
217+
def arr = arraysMap.get(key)
218+
return arr ? arr.join(", ") : ref
219+
}
220+
return ref
221+
}
222+
223+
def getAttrValue = { attrs, List<String> keys ->
224+
for (k in keys) {
225+
if (attrs.containsKey(k)) {
226+
return attrs[k]
227+
}
228+
}
229+
return null
230+
}
231+
232+
def md = new StringBuilder("# Preferences Documentation\n\n")
233+
def jsonList = [] // full JSON list to serialize
234+
235+
resXmlDir.eachFile { xmlFile ->
236+
if (xmlFile.name.toLowerCase().startsWith("preference") && xmlFile.name.endsWith(".xml")) {
237+
def logicalName = xmlFile.name
238+
.replaceFirst(/^preference_/, '')
239+
.replaceFirst(/\.xml$/, '')
240+
.replaceAll(/_/, ' ')
241+
.capitalize()
242+
243+
md.append("## ${logicalName}\n\n")
244+
245+
def xml = new XmlSlurper(false, false).parse(xmlFile)
246+
def categories = [:]
247+
248+
249+
def processPrefs
250+
processPrefs = { node, categoryName = null ->
251+
def attrs = node.attributes()
252+
def prefType
253+
switch (node.name()) {
254+
case "SwitchPreferenceCompat":
255+
case "SwitchPreference":
256+
case "CheckBoxPreference":
257+
prefType = "boolean"
258+
break
259+
case "EditTextPreference":
260+
case "ListPreference":
261+
prefType = "string"
262+
break
263+
case "SeekBarPreference":
264+
prefType = "int"
265+
break
266+
case "MultiSelectListPreference":
267+
prefType = "set"
268+
break
269+
default:
270+
prefType = "string"
271+
}
272+
if (node.name() == "PreferenceCategory") {
273+
def catTitleRaw = getAttrValue(attrs, ['android:title', 'app:title', 'title'])
274+
def catSummaryRaw = getAttrValue(attrs, ['android:summary', 'app:summary', 'summary'])
275+
def catTitle = resolveRef(catTitleRaw) ?: "Unnamed Category"
276+
def catSummary = resolveRef(catSummaryRaw) ?: ""
277+
278+
if (!categories.containsKey(catTitle)) {
279+
categories[catTitle] = [summary: catSummary, prefs: []]
280+
}
281+
282+
node.children().each { child ->
283+
processPrefs(child, catTitle)
284+
}
285+
} else if (node.name() == "PreferenceScreen") {
286+
node.children().each { child ->
287+
processPrefs(child, categoryName)
288+
}
289+
} else {
290+
def keyRaw = getAttrValue(attrs, ['android:key', 'app:key', 'key'])
291+
if (keyRaw) {
292+
def titleRaw = getAttrValue(attrs, ['android:title', 'app:title', 'title'])
293+
def summaryRaw = getAttrValue(attrs, ['android:summary', 'app:summary', 'summary'])
294+
def defaultValueRaw = getAttrValue(attrs, ['android:defaultValue', 'app:defaultValue', 'defaultValue'])
295+
296+
def title = resolveRef(titleRaw)
297+
def summary = resolveRef(summaryRaw)
298+
def defaultValue = resolveRef(defaultValueRaw)
299+
300+
if (categoryName == null) {
301+
categoryName = "General Preferences"
302+
if (!categories.containsKey(categoryName)) {
303+
categories[categoryName] = [summary: "", prefs: []]
304+
}
305+
}
306+
307+
categories[categoryName].prefs << [
308+
key : keyRaw,
309+
title : title,
310+
nodeName : node.name(),
311+
summary : summary,
312+
defaultValue: defaultValue,
313+
type : prefType
314+
]
315+
}
316+
}
317+
}
318+
319+
processPrefs(xml)
320+
321+
def jsonEntry = [
322+
setting: logicalName.toLowerCase().replaceAll(" ", "_"),
323+
categories: []
324+
]
325+
326+
categories.each { catName, info ->
327+
md.append("### ${catName}\n\n")
328+
if (info.summary) {
329+
md.append("_${info.summary}_\n\n")
330+
}
331+
332+
md.append("| Key | Title | Summary | Default Value |\n")
333+
md.append("| --- | ----- | ------- | ------------- |\n")
334+
info.prefs.each { p ->
335+
def escTitle = p.title?.replaceAll("\\|", "\\\\|") ?: ""
336+
def escSummary = p.summary?.replaceAll("\\|", "\\\\|") ?: ""
337+
def escDefault = p.defaultValue ? "`${p.defaultValue.replaceAll("\\|", "\\\\|")}`" : ""
338+
md.append("| **${p.key}** | ${escTitle} | ${escSummary} | ${escDefault} |\n")
339+
}
340+
md.append("\n")
341+
jsonEntry.categories += info.prefs
342+
.findAll { p -> p.nodeName != "Preference" && p.key?.trim() }
343+
.groupBy { catName.replaceAll(" ", "_").toLowerCase() }
344+
.collect { cat, prefs ->
345+
[
346+
name: cat,
347+
preferences: prefs.collect { p -> [key: p.key, value: null, type: p.type] }
348+
]
349+
}
350+
.findAll { it.preferences }
351+
}
352+
353+
jsonList << jsonEntry
354+
}
355+
}
356+
357+
outputFile.parentFile.mkdirs()
358+
outputFile.text = md.toString()
359+
360+
jsonList.add([
361+
metadata: [
362+
version: android.defaultConfig.versionName,
363+
code : android.defaultConfig.versionCode,
364+
gitHash: getGitHash()
365+
]
366+
])
367+
def json = groovy.json.JsonOutput.prettyPrint(
368+
groovy.json.JsonOutput.toJson(jsonList)
369+
)
370+
371+
372+
jsonOutputFileDoc.text = json
373+
jsonOutputFileResources.text = json
374+
375+
println "Preferences Markdown generated at: ${outputFile.path}"
376+
println "Preferences JSON generated at: ${jsonOutputFileDoc.path}"
377+
}
378+
}
379+
380+
preBuild.dependsOn(tasks.named("generatePrefsDoc"))

app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DataProvider/DataProvider.java

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -403,7 +403,7 @@ public List<CellInformation> getNeighbourCellInformation() {
403403
@SuppressLint("ObsoleteSdkInt")
404404
public List<Point> getCellInformationPoint() {
405405
List<Point> points = new ArrayList<>();
406-
boolean nc = spg.getSharedPreference(SPType.logging_sp).getBoolean("log_neighbour_cells", false);
406+
boolean nc = spg.getSharedPreference(SPType.LOGGING).getBoolean("log_neighbour_cells", false);
407407
for (CellInformation ci_ : ci) {
408408
// check if want to log neighbour cells and skip non registered cells
409409
if (!nc) {
@@ -612,7 +612,7 @@ public Point getLocationPoint() {
612612
point.time(System.currentTimeMillis(), WritePrecision.MS);
613613
// falling back to fake if no location is available is not the best solution.
614614
// We should ask the user / add configuration what to do
615-
if (spg.getSharedPreference(SPType.logging_sp).getBoolean("fake_location", false) || li == null) {
615+
if (spg.getSharedPreference(SPType.LOGGING).getBoolean("fake_location", false) || li == null) {
616616
point.addField("longitude", 13.3143266);
617617
point.addField("latitude", 52.5259678);
618618
point.addField("altitude", 34.0);
@@ -719,7 +719,7 @@ public Point getBatteryInformationPoint() {
719719
*/
720720
@SuppressLint("ObsoleteSdkInt")
721721
public Map<String, String> getTagsMap() {
722-
String tags = spg.getSharedPreference(SPType.logging_sp).getString("tags", "").strip().replace(" ", "");
722+
String tags = spg.getSharedPreference(SPType.LOGGING).getString("tags", "").strip().replace(" ", "");
723723
Map<String, String> tags_map = Collections.emptyMap();
724724
if (!tags.isEmpty()) {
725725
try {
@@ -731,13 +731,13 @@ public Map<String, String> getTagsMap() {
731731
DeviceInformation di = getDeviceInformation();
732732

733733
Map<String, String> tags_map_modifiable = new HashMap<>(tags_map);
734-
tags_map_modifiable.put("measurement_name", spg.getSharedPreference(SPType.logging_sp).getString("measurement_name", "OMNT"));
734+
tags_map_modifiable.put("measurement_name", spg.getSharedPreference(SPType.LOGGING).getString("measurement_name", "OMNT"));
735735
tags_map_modifiable.put("manufacturer", di.getManufacturer());
736736
tags_map_modifiable.put("model", di.getModel());
737737
tags_map_modifiable.put("sdk_version", String.valueOf(di.getAndroidSDK()));
738738
tags_map_modifiable.put("android_version", di.getAndroidRelease());
739739
tags_map_modifiable.put("security_patch", di.getSecurityPatchLevel());
740-
String device = spg.getSharedPreference(SPType.default_sp).getString("device_name", "null").strip();
740+
String device = spg.getSharedPreference(SPType.MAIN).getString("device_name", "null").strip();
741741
//if(device.equals("null")); TODO handle this
742742
tags_map_modifiable.put("device", device);
743743
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {

app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/DetailFragment.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -489,7 +489,7 @@ private CardView get_cell_card_view() {
489489
List<CellInformation> cil = dp.getCellInformation();
490490
int cell = 1;
491491
for (CellInformation ci : cil) {
492-
if (!spg.getSharedPreference(SPType.default_sp).getBoolean("show_neighbour_cells", false) && !ci.isRegistered()) {
492+
if (!spg.getSharedPreference(SPType.MAIN).getBoolean("show_neighbour_cells", false) && !ci.isRegistered()) {
493493
continue;
494494
}
495495
TableRow title = rowBuilder("Cell " + cell, "");

app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/InfluxDB2x/InfluxdbConnection.java

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -71,19 +71,19 @@ public void open_write_api() {
7171
writeApi.listenEvents(BackpressureEvent.class, value -> Log.d(TAG, "open_write_api: Could not write to InfluxDBv2 due to backpressure"));
7272
writeApi.listenEvents(WriteSuccessEvent.class, value -> {
7373
//Log.d(TAG, "open_write_api: Write to InfluxDBv2 was successful");
74-
if ( spg.getSharedPreference(SPType.logging_sp).getBoolean("enable_influx", false)) {
74+
if ( spg.getSharedPreference(SPType.LOGGING).getBoolean("enable_influx", false)) {
7575
gv.getLog_status().setColorFilter(Color.argb(255, 0, 255, 0));
7676
}
7777
});
7878
writeApi.listenEvents(WriteErrorEvent.class, value -> {
7979
Log.d(TAG, "open_write_api: Could not write to InfluxDBv2 due to error");
80-
if ( spg.getSharedPreference(SPType.logging_sp).getBoolean("enable_influx", false)) {
80+
if ( spg.getSharedPreference(SPType.LOGGING).getBoolean("enable_influx", false)) {
8181
gv.getLog_status().setColorFilter(Color.argb(255, 255, 0, 0));
8282
}
8383
});
8484
writeApi.listenEvents(WriteRetriableErrorEvent.class, value -> {
8585
Log.d(TAG, "open_write_api: Could not write to InfluxDBv2 due to retriable error");
86-
if ( spg.getSharedPreference(SPType.logging_sp).getBoolean("enable_influx", false)) {
86+
if ( spg.getSharedPreference(SPType.LOGGING).getBoolean("enable_influx", false)) {
8787
gv.getLog_status().setColorFilter(Color.argb(255, 255, 0, 0));
8888
}
8989
});

app/src/main/java/de/fraunhofer/fokus/OpenMobileNetworkToolkit/InfluxDB2x/InfluxdbConnections.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -28,10 +28,10 @@ private InfluxdbConnections() {
2828
public static InfluxdbConnection getRicInstance(Context context) {
2929
if (ric == null) {
3030
SharedPreferencesGrouper spg = SharedPreferencesGrouper.getInstance(context);
31-
String url = spg.getSharedPreference(SPType.logging_sp).getString("influx_URL", "");
32-
String org = spg.getSharedPreference(SPType.logging_sp).getString("influx_org", "");
33-
String bucket = spg.getSharedPreference(SPType.logging_sp).getString("influx_bucket", "");
34-
String token = spg.getSharedPreference(SPType.logging_sp).getString("influx_token", "");
31+
String url = spg.getSharedPreference(SPType.LOGGING).getString("influx_URL", "");
32+
String org = spg.getSharedPreference(SPType.LOGGING).getString("influx_org", "");
33+
String bucket = spg.getSharedPreference(SPType.LOGGING).getString("influx_bucket", "");
34+
String token = spg.getSharedPreference(SPType.LOGGING).getString("influx_token", "");
3535
if (url.isEmpty() || org.isEmpty() || bucket.isEmpty() || token.isEmpty()) {
3636
Log.e(TAG, "Influx parameters incomplete, can't setup logging");
3737
// if we are an UI thread we make a toast, if not logging have to be enough

0 commit comments

Comments
 (0)