Skip to content

Commit b5e0b39

Browse files
bastian-srcBastian Schmidt
authored andcommitted
Integrate netmonster cellular data
* Retrieve CellData with netmonster library * Set service type to Location -> otherwise, no cell data is provided when running in background * Extend CellData by estimated bandwidth and NR frequency * Limit execution of tasks in MainThread (UI-Thread) to a minimum
1 parent cc873bf commit b5e0b39

File tree

16 files changed

+409
-96
lines changed

16 files changed

+409
-96
lines changed

.idea/kotlinc.xml

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,6 @@
22

33
An Android app to publish device information through an extensible API interface.
44

5-
* The API is created using [Javalin](https://javalin.io/)
5+
* The API is created using [Ktor](https://ktor.io/)
66
* The base version of DevicePublisher features publishing cell information using [netmonster-core](https://github.com/mroczis/netmonster-core)
77
* The app follows a modular design to allow further device infor extensions

app/build.gradle.kts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -83,4 +83,7 @@ dependencies {
8383
implementation(libs.ktor.server.request.validation)
8484
implementation(libs.ktor.server.content.negotiation)
8585
implementation(libs.ktor.serialization.kotlinx.json)
86+
87+
// netmonster
88+
implementation(libs.netmonster.core)
8689
}

app/src/main/AndroidManifest.xml

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,24 @@
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:tools="http://schemas.android.com/tools">
44

5+
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
6+
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
7+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
58
<uses-permission android:name="android.permission.BODY_SENSORS" />
69

710
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
8-
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CONNECTED_DEVICE"/>
11+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_LOCATION"/>
912
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
1013

14+
1115
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
1216
<uses-permission android:name="android.permission.INTERNET" />
1317
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
1418
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE"/>
1519
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
1620

1721
<application
22+
android:name=".MainApplication"
1823
android:allowBackup="true"
1924
android:dataExtractionRules="@xml/data_extraction_rules"
2025
android:fullBackupContent="@xml/backup_rules"
@@ -27,10 +32,11 @@
2732

2833
<uses-library android:name="org.apache.http.legacy" android:required="false" />
2934

35+
<!-- Type must be location, to retrieve celldata even in background -->
3036
<service
3137
android:name=".service.APIService"
3238
android:enabled="true"
33-
android:foregroundServiceType="connectedDevice"
39+
android:foregroundServiceType="location"
3440
android:exported="false" >
3541
</service>
3642

@@ -43,13 +49,6 @@
4349
<category android:name="android.intent.category.LAUNCHER" />
4450
</intent-filter>
4551
</activity>
46-
47-
<service
48-
android:name=".service.DevicePublisherService"
49-
android:directBootAware="true"
50-
android:enabled="true"
51-
android:exported="false"
52-
tools:replace="android:enabled" />
5352
</application>
5453

5554
</manifest>

app/src/main/java/eu/bschmidt/devicepublisher/MainActivity.kt

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package eu.bschmidt.devicepublisher
22

33
import android.content.ComponentName
4+
import android.content.Context
45
import android.content.Intent
56
import android.content.ServiceConnection
7+
import android.content.pm.PackageManager
68
import android.os.Build
79
import android.os.Bundle
810
import android.os.IBinder
@@ -31,7 +33,7 @@ class MainActivity : AppCompatActivity() {
3133

3234
// TODO: Change the service running state to an enum to include "Loading"
3335
private var isServiceRunning: Boolean = false
34-
private val notificationPermissionLauncher =
36+
private val permissionLauncher =
3537
registerForActivityResult(
3638
ActivityResultContracts.RequestPermission()
3739
) {
@@ -65,11 +67,13 @@ class MainActivity : AppCompatActivity() {
6567

6668
override fun onCreate(savedInstanceState: Bundle?) {
6769
super.onCreate(savedInstanceState)
70+
MainActivity.appContext = applicationContext
6871
binding = ActivityMainBinding.inflate(layoutInflater)
6972
binding.fab.setOnClickListener { toggleService() }
7073
setContentView(binding.root)
7174

72-
checkAndRequestNotificationPermission()
75+
// checkAndRequestNotificationPermission()
76+
checkAndRequestPermissions()
7377
tryToBindToServiceIfRunning()
7478
initUI()
7579
}
@@ -89,12 +93,40 @@ class MainActivity : AppCompatActivity() {
8993
}
9094

9195
else -> {
92-
notificationPermissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
96+
permissionLauncher.launch(android.Manifest.permission.POST_NOTIFICATIONS)
9397
}
9498
}
99+
100+
}
101+
}
102+
103+
private fun checkAndRequestPermissions() {
104+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
105+
val permissionsToCheck = arrayOf(
106+
android.Manifest.permission.POST_NOTIFICATIONS,
107+
android.Manifest.permission.ACCESS_FINE_LOCATION,
108+
android.Manifest.permission.ACCESS_COARSE_LOCATION,
109+
android.Manifest.permission.READ_PHONE_STATE
110+
)
111+
112+
val permissionsToRequest = mutableListOf<String>()
113+
114+
for (permission in permissionsToCheck) {
115+
if (ContextCompat.checkSelfPermission(this, permission) != PackageManager.PERMISSION_GRANTED) {
116+
permissionsToRequest.add(permission)
117+
}
118+
}
119+
120+
if (permissionsToRequest.isNotEmpty()) {
121+
requestPermissions(permissionsToRequest.toTypedArray(), 200)
122+
// permissionLauncher.launch(permissionsToRequest.toTypedArray().toString())
123+
} else {
124+
// All permissions already granted
125+
}
95126
}
96127
}
97128

129+
98130
private fun tryToBindToServiceIfRunning() {
99131
Intent(this, APIService::class.java).also { intent ->
100132
bindService(intent, connection, 0)
@@ -134,5 +166,6 @@ class MainActivity : AppCompatActivity() {
134166

135167
companion object {
136168
private const val TAG = "MainActivity"
169+
lateinit var appContext: Context
137170
}
138171
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package eu.bschmidt.devicepublisher
2+
3+
import android.app.Application
4+
import android.content.Context
5+
6+
class MainApplication: Application() {
7+
override fun onCreate() {
8+
super.onCreate()
9+
MainApplication.appContext = this
10+
}
11+
12+
companion object {
13+
lateinit var appContext: Context
14+
}
15+
}

app/src/main/java/eu/bschmidt/devicepublisher/api/celldata/routeCellData.kt

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ package eu.bschmidt.devicepublisher.api.celldata
33
import eu.bschmidt.devicepublisher.model.celldata.CellData
44
import eu.bschmidt.devicepublisher.model.celldata.CellDataViewModel
55
import eu.bschmidt.devicepublisher.util.DevPubUtils
6+
import eu.bschmidt.devicepublisher.api.celldata.getAllCells
7+
import eu.bschmidt.devicepublisher.api.celldata.getConnectedCells
68
import io.ktor.http.HttpStatusCode
79
import io.ktor.server.application.call
810
import io.ktor.server.response.respond
@@ -12,34 +14,67 @@ import io.ktor.server.routing.*
1214

1315
const val TAG = "routeCellData"
1416

15-
suspend fun getCellList(viewModel: CellDataViewModel): MutableList<CellData> {
17+
suspend fun getAllCells(viewModel: CellDataViewModel): MutableList<CellData> {
1618
val localCellList: MutableList<CellData> = mutableListOf<CellData>()
17-
DevPubUtils.waitForMainThread {
18-
viewModel.cellDataList.value?.let { cellDataList ->
19-
localCellList.addAll(cellDataList.toMutableList())
20-
}
21-
}
19+
localCellList.addAll(getConnectedCells(viewModel))
20+
localCellList.addAll(getAvailableCells(viewModel))
21+
return localCellList
22+
}
23+
24+
suspend fun getConnectedCells(viewModel: CellDataViewModel): MutableList<CellData> {
25+
val localCellList: MutableList<CellData> = mutableListOf<CellData>()
26+
localCellList.addAll(viewModel.getConnectedCells().toMutableList())
27+
return localCellList
28+
}
29+
30+
suspend fun getAvailableCells(viewModel: CellDataViewModel): MutableList<CellData> {
31+
val localCellList: MutableList<CellData> = mutableListOf<CellData>()
32+
localCellList.addAll(viewModel.getAvailableCells().toMutableList())
2233
return localCellList
2334
}
2435

2536
fun Route.routeCellData() {
2637
val viewModel: CellDataViewModel = CellDataViewModel.getInstance()
2738

2839
route("/celldata") {
29-
get("/cells") {
30-
val cellList = getCellList(viewModel)
40+
get("/all") {
41+
val cellList = getAllCells(viewModel)
3142
call.respond(cellList)
3243
}
44+
45+
route("/connected") {
46+
get("/all") {
47+
val cellList = getConnectedCells(viewModel)
48+
call.respond(cellList)
49+
}
50+
get("/cell/{id}") {
51+
val id = call.parameters["id"]?.toIntOrNull()
52+
val cellList = getConnectedCells(viewModel)
53+
if (id != null && id >= 0 && id < cellList.size) {
54+
val someCell: CellData = cellList[id]
55+
call.respond(someCell)
56+
} else {
57+
call.respondText("Cell not found", status = HttpStatusCode.NotFound)
58+
}
59+
}
60+
}
3361

34-
get("/cell/{id}") {
35-
val id = call.parameters["id"]?.toIntOrNull()
36-
val cellList = getCellList(viewModel)
37-
if (id != null && id >= 0 && id < cellList.size) {
38-
val someCell: CellData = cellList[id]
39-
call.respond(someCell)
40-
} else {
41-
call.respondText("Cell not found", status = HttpStatusCode.NotFound)
62+
route("/available") {
63+
get("/all") {
64+
val cellList = getAvailableCells(viewModel)
65+
call.respond(cellList)
66+
}
67+
get("/cell/{id}") {
68+
val id = call.parameters["id"]?.toIntOrNull()
69+
val cellList = getAvailableCells(viewModel)
70+
if (id != null && id >= 0 && id < cellList.size) {
71+
val someCell: CellData = cellList[id]
72+
call.respond(someCell)
73+
} else {
74+
call.respondText("Cell not found", status = HttpStatusCode.NotFound)
75+
}
4276
}
4377
}
78+
4479
}
4580
}

app/src/main/java/eu/bschmidt/devicepublisher/service/DevicePublisherService.kt renamed to app/src/main/java/eu/bschmidt/devicepublisher/bak/DevicePublisherService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
package eu.bschmidt.devicepublisher.service
1+
package eu.bschmidt.devicepublisher.bak
22

33
import android.app.Service
44
import android.content.Context

app/src/main/java/eu/bschmidt/devicepublisher/model/ModelUpdater.kt

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,7 @@ class ModelUpdater: Thread(), StoppableThread {
1717
while (running) {
1818
Thread.sleep(UPDATE_INTERVAL_MS)
1919

20-
DevPubUtils.dispatchToMainThread {
21-
viewModels.forEach { it.update() }
22-
}
20+
viewModels.forEach { it.update() }
2321
}
2422
}
2523

0 commit comments

Comments
 (0)