Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
a008b1b
gradle / depency updates
PeterHasse Jul 1, 2025
110625a
bump release version
PeterHasse Jul 1, 2025
3416e6e
bump APG to 8.11.1
hajoha Jul 21, 2025
b0c27ef
fix visibilty bug
hajoha Jul 21, 2025
6a872a8
add length parameter
hajoha Jul 21, 2025
d8c43c8
Bump Gradle version and depencies
PeterHasse Jul 21, 2025
ea8a561
show history of ping (#61)
hajoha Jul 21, 2025
2b9502c
Merge branch 'rel/0.7' of github.com:omnt/OpenMobileNetworkToolkit in…
PeterHasse Jul 21, 2025
901c984
Add Description to Logging Preference (#62)
hajoha Jul 22, 2025
c21b7c4
clean up
hajoha Jul 22, 2025
b1d2489
update doc (#63)
hajoha Jul 23, 2025
42df6eb
fix bug
hajoha Jul 24, 2025
d566171
add default PR template
hajoha Aug 4, 2025
4a142fd
bump dependencies
hajoha Aug 5, 2025
96fa3a2
fix name of Debug Stage in workflow (#69)
bjoern-r Aug 20, 2025
e63a027
Fix Streams bug (#68)
hajoha Sep 1, 2025
74d9d53
Ping addons (#64)
hajoha Sep 1, 2025
f594283
add possibility to configure app via intent (#65)
hajoha Sep 1, 2025
364e41b
Refactor Config Import/Export (#66)
hajoha Sep 2, 2025
dbcb77c
fixed leftover from renaming
PeterHasse Sep 2, 2025
c397226
bump gradle, work and some other depencies
PeterHasse Sep 2, 2025
52f0533
fix preference bug
hajoha Sep 10, 2025
209cc05
Fix iperf3 streams (#76)
hajoha Nov 25, 2025
8ab0501
fix addTag bug (#78)
hajoha Nov 25, 2025
6534739
fix mqtt multiple client bug (#79)
hajoha Nov 25, 2025
555b491
Feature/cleanup misc (#80)
hajoha Nov 25, 2025
7345bd7
Bump gradle and lib versions.
PeterHasse Nov 25, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE/default.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: Defaukt Pull Request
about: Use this template for creating PR request.
title: "PR_TITLE"
labels:
assignees: ""
---
## Added

## Changed

## Fixed

## Removed

## Breaking Changes

## Related Issues

## Screenshots (if applicable)
4 changes: 2 additions & 2 deletions .github/workflows/android.yml
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ jobs:
RELEASE_KEY_PASSWORD: ${{ secrets.KEY_PW }}
run: ./gradlew assembleRelease --stacktrace

- name: Build Release APK
- name: Build Debug APK
env:
ENCODED_STRING: ${{ secrets.KEYSTORE }}
RELEASE_KEYSTORE_PASSWORD: ${{ secrets.KEYSTORE_PW }}
Expand Down Expand Up @@ -81,4 +81,4 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: omnt-${{ steps.releaseApk.outputs.sha_short }}
path: ./upload
path: ./upload
248 changes: 225 additions & 23 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,10 @@
*
* SPDX-License-Identifier: apache2
*/

plugins {
id 'com.android.application'
id 'androidx.navigation.safeargs'
id("org.spdx.sbom") version "0.8.0"
id("org.spdx.sbom") version "0.9.0"
}

def keystoreProperties = new Properties()
Expand Down Expand Up @@ -60,8 +59,8 @@ android {
applicationId "de.fraunhofer.fokus.OpenMobileNetworkToolkit"
minSdk 31
targetSdk 36
versionCode 6
versionName "0.6"
versionCode 7
versionName "0.7"
resValue("string", "git_hash", getGitHash())

testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
Expand Down Expand Up @@ -127,12 +126,12 @@ spdxSbom {
}

dependencies {
def work_version = "2.10.1"
def work_version = "2.11.0"
def room_version = "2.7.1"

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.2.1'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.6.1'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.7.0'
annotationProcessor "androidx.room:room-compiler:$room_version"
androidTestImplementation "androidx.work:work-testing:$work_version"

Expand All @@ -142,33 +141,236 @@ dependencies {
implementation "androidx.work:work-gcm:$work_version"
implementation "androidx.work:work-multiprocess:$work_version"
implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.7.3"
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-guava:1.10.2"
implementation 'androidx.preference:preference:1.2.1'
implementation "androidx.room:room-runtime:$room_version"
implementation 'androidx.appcompat:appcompat:1.7.1'
implementation 'com.google.guava:guava:33.4.0-jre'
implementation 'androidx.concurrent:concurrent-futures:1.2.0'
implementation 'androidx.activity:activity:1.10.1'
implementation 'androidx.fragment:fragment:1.8.8'
implementation 'com.google.android.material:material:1.12.0'
implementation 'com.google.guava:guava:33.5.0-jre'
implementation 'androidx.concurrent:concurrent-futures:1.3.0'
implementation 'androidx.activity:activity:1.12.0'
implementation 'androidx.fragment:fragment:1.8.9'
implementation 'com.google.android.material:material:1.13.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'
implementation 'androidx.navigation:navigation-fragment:2.9.0'
implementation 'androidx.navigation:navigation-ui:2.9.0'
implementation 'androidx.navigation:navigation-fragment:2.9.6'
implementation 'androidx.navigation:navigation-ui:2.9.6'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
implementation 'androidx.preference:preference-ktx:1.2.1'
implementation 'androidx.recyclerview:recyclerview:1.4.0'
implementation 'androidx.recyclerview:recyclerview-selection:1.2.0'
implementation 'com.influxdb:influxdb-client-java:7.3.0'
implementation 'com.influxdb:influxdb-client-java:7.4.0'
implementation 'com.google.android.gms:play-services-location:21.3.0'
implementation 'com.github.anastr:speedviewlib:1.6.1'
implementation "androidx.viewpager2:viewpager2:1.1.0"
implementation "androidx.compose.material3:material3:1.3.2"
implementation "com.hivemq:hivemq-mqtt-client:1.3.4"
implementation "androidx.compose.material3:material3:1.3.2"
implementation "androidx.compose.material3:material3:1.4.0"
implementation "com.hivemq:hivemq-mqtt-client:1.3.10"
implementation "androidx.compose.material3:material3:1.4.0"
implementation "com.squareup.moshi:moshi:1.15.2"
implementation "com.squareup.moshi:moshi-adapters:1.8.0"
implementation "com.squareup.moshi:moshi-adapters:1.15.2"
}


tasks.register('generatePrefsDoc') {
def resXmlDir = file("$projectDir/src/main/res/xml")
def stringsFile = file("$projectDir/src/main/res/values/strings.xml")
def arraysFile = file("$projectDir/src/main/res/values/arrays.xml")
def outputFile = file("$projectDir/../docs/preferences.md")
def jsonOutputFileDoc = file("$projectDir/../docs/config.json")
def jsonOutputFileResources = file("$projectDir/src/main/res/raw/config.json")

doLast {
def parseStringsXml = { File xmlFile ->
def parser = new XmlSlurper()
def root = parser.parse(xmlFile)
def map = [:]
root.string.each { s ->
map[s.@name.toString()] = s.text().trim()
}
return map
}

def parseArraysXml = { File xmlFile ->
if (!xmlFile.exists()) return [:]
def parser = new XmlSlurper()
def root = parser.parse(xmlFile)
def map = [:]
root.array.each { arr ->
def name = arr.@name.toString()
map[name] = arr.item.collect { it.text().trim() }
}
return map
}

def stringsMap = stringsFile.exists() ? parseStringsXml(stringsFile) : [:]
def arraysMap = arraysFile.exists() ? parseArraysXml(arraysFile) : [:]

def resolveRef = { String ref ->
if (!ref) return ""
if (ref.startsWith("@string/")) {
def key = ref.substring(8)
return stringsMap.get(key, ref)
} else if (ref.startsWith("@array/")) {
def key = ref.substring(7)
def arr = arraysMap.get(key)
return arr ? arr.join(", ") : ref
}
return ref
}

def getAttrValue = { attrs, List<String> keys ->
for (k in keys) {
if (attrs.containsKey(k)) {
return attrs[k]
}
}
return null
}

def md = new StringBuilder("# Preferences Documentation\n\n")
def jsonList = [] // full JSON list to serialize

resXmlDir.eachFile { xmlFile ->
if (xmlFile.name.toLowerCase().startsWith("preference") && xmlFile.name.endsWith(".xml")) {
def logicalName = xmlFile.name
.replaceFirst(/^preference_/, '')
.replaceFirst(/\.xml$/, '')
.replaceAll(/_/, ' ')
.capitalize()

md.append("## ${logicalName}\n\n")

def xml = new XmlSlurper(false, false).parse(xmlFile)
def categories = [:]


def processPrefs
processPrefs = { node, categoryName = null ->
def attrs = node.attributes()
def prefType
switch (node.name()) {
case "SwitchPreferenceCompat":
case "SwitchPreference":
case "CheckBoxPreference":
prefType = "boolean"
break
case "EditTextPreference":
case "ListPreference":
prefType = "string"
break
case "SeekBarPreference":
prefType = "int"
break
case "MultiSelectListPreference":
prefType = "set"
break
default:
prefType = "string"
}
if (node.name() == "PreferenceCategory") {
def catTitleRaw = getAttrValue(attrs, ['android:title', 'app:title', 'title'])
def catSummaryRaw = getAttrValue(attrs, ['android:summary', 'app:summary', 'summary'])
def catTitle = resolveRef(catTitleRaw) ?: "Unnamed Category"
def catSummary = resolveRef(catSummaryRaw) ?: ""

if (!categories.containsKey(catTitle)) {
categories[catTitle] = [summary: catSummary, prefs: []]
}

node.children().each { child ->
processPrefs(child, catTitle)
}
} else if (node.name() == "PreferenceScreen") {
node.children().each { child ->
processPrefs(child, categoryName)
}
} else {
def keyRaw = getAttrValue(attrs, ['android:key', 'app:key', 'key'])
if (keyRaw) {
def titleRaw = getAttrValue(attrs, ['android:title', 'app:title', 'title'])
def summaryRaw = getAttrValue(attrs, ['android:summary', 'app:summary', 'summary'])
def defaultValueRaw = getAttrValue(attrs, ['android:defaultValue', 'app:defaultValue', 'defaultValue'])

def title = resolveRef(titleRaw)
def summary = resolveRef(summaryRaw)
def defaultValue = resolveRef(defaultValueRaw)

if (categoryName == null) {
categoryName = "General Preferences"
if (!categories.containsKey(categoryName)) {
categories[categoryName] = [summary: "", prefs: []]
}
}

categories[categoryName].prefs << [
key : keyRaw,
title : title,
nodeName : node.name(),
summary : summary,
defaultValue: defaultValue,
type : prefType
]
}
}
}

processPrefs(xml)

def jsonEntry = [
setting: logicalName.toLowerCase().replaceAll(" ", "_"),
categories: []
]

categories.each { catName, info ->
md.append("### ${catName}\n\n")
if (info.summary) {
md.append("_${info.summary}_\n\n")
}

md.append("| Key | Title | Summary | Default Value |\n")
md.append("| --- | ----- | ------- | ------------- |\n")
info.prefs.each { p ->
def escTitle = p.title?.replaceAll("\\|", "\\\\|") ?: ""
def escSummary = p.summary?.replaceAll("\\|", "\\\\|") ?: ""
def escDefault = p.defaultValue ? "`${p.defaultValue.replaceAll("\\|", "\\\\|")}`" : ""
md.append("| **${p.key}** | ${escTitle} | ${escSummary} | ${escDefault} |\n")
}
md.append("\n")
jsonEntry.categories += info.prefs
.findAll { p -> p.nodeName != "Preference" && p.key?.trim() }
.groupBy { catName.replaceAll(" ", "_").toLowerCase() }
.collect { cat, prefs ->
[
name: cat,
preferences: prefs.collect { p -> [key: p.key, value: null, type: p.type] }
]
}
.findAll { it.preferences }
}

jsonList << jsonEntry
}
}

outputFile.parentFile.mkdirs()
outputFile.text = md.toString()

jsonList.add([
metadata: [
version: android.defaultConfig.versionName,
code : android.defaultConfig.versionCode,
gitHash: getGitHash()
]
])
def json = groovy.json.JsonOutput.prettyPrint(
groovy.json.JsonOutput.toJson(jsonList)
)


jsonOutputFileDoc.text = json
jsonOutputFileResources.text = json

println "Preferences Markdown generated at: ${outputFile.path}"
println "Preferences JSON generated at: ${jsonOutputFileDoc.path}"
}
}

configurations.implementation {
exclude group: 'org.jetbrains.kotlin', module: 'kotlin-stdlib-jdk8'
}
preBuild.dependsOn(tasks.named("generatePrefsDoc"))
14 changes: 14 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.HIDE_OVERLAY_WINDOWS" />
Expand Down Expand Up @@ -146,6 +147,19 @@
<action android:name="android.intent.action.BOOT_COMPLETED" />
</intent-filter>
</receiver>
<receiver android:name="de.fraunhofer.fokus.OpenMobileNetworkToolkit.Receiver.JsonConfigReceiver"
android:exported="true">
<intent-filter>
<action android:name="de.fraunhofer.fokus.OpenMobileNetworkToolkit.CONFIG_JSON" />
</intent-filter>
</receiver>

<receiver android:name="de.fraunhofer.fokus.OpenMobileNetworkToolkit.Receiver.FileConfigReceiver"
android:exported="true">
<intent-filter>
<action android:name="de.fraunhofer.fokus.OpenMobileNetworkToolkit.CONFIG_FILE" />
</intent-filter>
</receiver>

<activity
android:name="de.fraunhofer.fokus.OpenMobileNetworkToolkit.MainActivity"
Expand Down
Loading