Skip to content

Commit e0f67d8

Browse files
committed
Athena: Rewrite
Change-Id: Ifdffd0690a1895ce6ce40c2139643b854ab36e41
1 parent 932f258 commit e0f67d8

File tree

174 files changed

+8666
-8159
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+8666
-8159
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ If you find a bug, [open an issue](https://github.com/SebaUbuntu/Athena/issues)
3131

3232
```
3333
#
34-
# SPDX-FileCopyrightText: 2023 Sebastiano Barezzi
34+
# SPDX-FileCopyrightText: Sebastiano Barezzi
3535
# SPDX-License-Identifier: Apache-2.0
3636
#
3737
```

app/build.gradle.kts

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import org.jetbrains.kotlin.gradle.dsl.JvmTarget
88
plugins {
99
alias(libs.plugins.android.application)
1010
alias(libs.plugins.kotlin.android)
11+
alias(libs.plugins.kotlin.compose)
1112
alias(libs.plugins.kotlin.serialization)
1213
}
1314

@@ -61,6 +62,10 @@ android {
6162
}
6263
}
6364

65+
buildFeatures {
66+
compose = true
67+
}
68+
6469
externalNativeBuild {
6570
cmake {
6671
path = file("src/main/cpp/CMakeLists.txt")
@@ -70,16 +75,23 @@ android {
7075
}
7176

7277
dependencies {
73-
implementation(libs.androidx.appcompat)
78+
implementation(libs.androidx.activity.compose)
7479
implementation(libs.androidx.biometric)
75-
implementation(libs.androidx.constraintlayout)
80+
implementation(libs.androidx.compose.material3)
81+
//implementation(libs.androidx.compose.material3.adaptive.navigation3)
82+
debugImplementation(libs.androidx.compose.ui.tooling)
83+
implementation(libs.androidx.compose.ui.tooling.preview)
7684
implementation(libs.androidx.core.ktx)
7785
implementation(libs.androidx.core.uwb)
7886
implementation(libs.androidx.lifecycle.livedata.ktx)
79-
implementation(libs.androidx.navigation.fragment.ktx)
80-
implementation(libs.androidx.navigation.ui.ktx)
87+
implementation(libs.androidx.lifecycle.viewmodel.compose)
88+
implementation(libs.androidx.lifecycle.viewmodel.navigation3)
89+
implementation(libs.androidx.navigation3.runtime)
90+
implementation(libs.androidx.navigation3.ui)
8191
implementation(libs.androidx.security.state)
8292
implementation(libs.kotlinx.serialization.json)
8393
implementation(libs.material)
8494
implementation(libs.okhttp)
95+
96+
implementation(project(":core"))
8597
}

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<!--
3-
SPDX-FileCopyrightText: 2023-2024 Sebastiano Barezzi
3+
SPDX-FileCopyrightText: Sebastiano Barezzi
44
SPDX-License-Identifier: Apache-2.0
55
-->
66
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
@@ -14,7 +14,6 @@
1414
android:required="false" />
1515

1616
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
17-
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
1817
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
1918
<uses-permission
2019
android:name="android.permission.BLUETOOTH"
@@ -25,15 +24,16 @@
2524
<uses-permission android:name="android.permission.BLUETOOTH_CONNECT" />
2625
<uses-permission android:name="android.permission.BODY_SENSORS" />
2726
<uses-permission android:name="android.permission.CAMERA" />
28-
<uses-permission android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"
27+
<uses-permission
28+
android:name="android.permission.HIGH_SAMPLING_RATE_SENSORS"
2929
tools:ignore="HighSamplingRate" />
3030
<uses-permission android:name="android.permission.INTERNET" />
3131
<uses-permission android:name="android.permission.NFC" />
3232
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
3333
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
3434
<uses-permission
3535
android:name="android.permission.USE_FINGERPRINT"
36-
android:maxSdkVersion="28" />
36+
android:maxSdkVersion="27" />
3737
<uses-permission android:name="android.permission.UWB_RANGING" />
3838

3939
<application
@@ -46,6 +46,7 @@
4646
tools:targetApi="tiramisu">
4747
<activity
4848
android:name=".MainActivity"
49+
android:configChanges="orientation|screenSize|screenLayout|keyboardHidden|mnc|colorMode|density|fontScale|fontWeightAdjustment|keyboard|layoutDirection|locale|mcc|navigation|smallestScreenSize|touchscreen|uiMode"
4950
android:exported="true">
5051
<intent-filter>
5152
<action android:name="android.intent.action.MAIN" />

app/src/main/java/dev/sebaubuntu/athena/AthenaApplication.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,22 @@ package dev.sebaubuntu.athena
77

88
import android.app.Application
99
import com.google.android.material.color.DynamicColors
10+
import dev.sebaubuntu.athena.core.components.ComponentsManager
1011

1112
class AthenaApplication : Application() {
13+
val componentsManager by lazy {
14+
ComponentsManager(this)
15+
}
16+
1217
override fun onCreate() {
1318
super.onCreate()
1419

1520
DynamicColors.applyToActivitiesIfAvailable(this)
21+
22+
// Load native library
23+
System.loadLibrary("athena")
24+
25+
// Start components manager
26+
componentsManager
1627
}
1728
}

app/src/main/java/dev/sebaubuntu/athena/MainActivity.kt

Lines changed: 12 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,203 +1,36 @@
11
/*
2-
* SPDX-FileCopyrightText: 2023 Sebastiano Barezzi
2+
* SPDX-FileCopyrightText: 2023-2025 Sebastiano Barezzi
33
* SPDX-License-Identifier: Apache-2.0
44
*/
55

66
package dev.sebaubuntu.athena
77

8-
import android.content.Intent
9-
import android.net.Uri
10-
import android.os.Build
118
import android.os.Bundle
12-
import android.view.Menu
13-
import android.view.MenuItem
14-
import android.view.View
9+
import androidx.activity.ComponentActivity
10+
import androidx.activity.compose.setContent
1511
import androidx.activity.enableEdgeToEdge
16-
import androidx.activity.result.contract.ActivityResultContracts
17-
import androidx.appcompat.app.AppCompatActivity
18-
import androidx.core.view.isVisible
19-
import androidx.lifecycle.lifecycleScope
20-
import androidx.navigation.fragment.NavHostFragment
21-
import androidx.navigation.ui.AppBarConfiguration
22-
import androidx.navigation.ui.setupWithNavController
23-
import com.google.android.material.appbar.MaterialToolbar
24-
import com.google.android.material.dialog.MaterialAlertDialogBuilder
25-
import com.google.android.material.progressindicator.LinearProgressIndicator
26-
import com.google.android.material.snackbar.Snackbar
27-
import dev.sebaubuntu.athena.models.data.Section
28-
import dev.sebaubuntu.athena.models.data.Section.Companion.listSerializer
29-
import dev.sebaubuntu.athena.models.data.Section.Companion.toSerializable
30-
import dev.sebaubuntu.athena.models.data.Subsection
31-
import dev.sebaubuntu.athena.sections.SectionEnum
32-
import kotlinx.coroutines.Dispatchers
33-
import kotlinx.coroutines.async
34-
import kotlinx.coroutines.awaitAll
35-
import kotlinx.coroutines.flow.single
36-
import kotlinx.coroutines.flow.take
37-
import kotlinx.coroutines.launch
38-
import kotlinx.coroutines.withContext
39-
import kotlinx.serialization.json.Json
40-
import java.io.FileWriter
12+
import androidx.compose.runtime.CompositionLocalProvider
13+
import dev.sebaubuntu.athena.ui.AthenaApp
14+
import dev.sebaubuntu.athena.ui.LocalPermissionsManager
15+
import dev.sebaubuntu.athena.utils.PermissionsManager
4116

42-
class MainActivity : AppCompatActivity() {
43-
// Views
44-
private val contentView by lazy { findViewById<View>(android.R.id.content) }
45-
private val linearProgressIndicator by lazy { findViewById<LinearProgressIndicator>(R.id.linearProgressIndicator) }
46-
private val toolbar by lazy { findViewById<MaterialToolbar>(R.id.toolbar) }
47-
48-
// Fragments
49-
private val navHostFragment by lazy { supportFragmentManager.findFragmentById(R.id.fragmentContainerView) as NavHostFragment }
50-
51-
private val navController by lazy { navHostFragment.navController }
52-
53-
// JSON export
54-
private val requestPermissionsContract = registerForActivityResult(
55-
ActivityResultContracts.RequestMultiplePermissions()
56-
) {
57-
if (it.isNotEmpty()) {
58-
if (it.all { permission -> permission.value }) {
59-
createDocumentContract.launch("data.json")
60-
} else {
61-
Snackbar.make(
62-
contentView, R.string.export_data_missing_permissions, Snackbar.LENGTH_LONG
63-
).show()
64-
}
65-
}
66-
}
67-
68-
private val createDocumentContract = registerForActivityResult(
69-
ActivityResultContracts.CreateDocument(JSON_MIME_TYPE)
70-
) {
71-
it?.also { uri ->
72-
exportData(uri)
73-
}
74-
}
17+
class MainActivity : ComponentActivity() {
18+
private val permissionsManager = PermissionsManager(this)
7519

7620
override fun onCreate(savedInstanceState: Bundle?) {
7721
super.onCreate(savedInstanceState)
7822

79-
setContentView(R.layout.activity_main)
80-
8123
// Enable edge-to-edge
8224
enableEdgeToEdge()
8325

84-
setSupportActionBar(toolbar)
85-
86-
val appBarConfiguration = AppBarConfiguration(navController.graph)
87-
toolbar.setupWithNavController(navController, appBarConfiguration)
88-
}
89-
90-
override fun onCreateOptionsMenu(menu: Menu?): Boolean {
91-
menuInflater.inflate(R.menu.activity_main, menu)
92-
return true
93-
}
94-
95-
override fun onOptionsItemSelected(item: MenuItem) = when (item.itemId) {
96-
R.id.exportData -> {
97-
MaterialAlertDialogBuilder(this)
98-
.setTitle(R.string.export_data)
99-
.setMessage(R.string.export_data_description)
100-
.setPositiveButton(R.string.yes) { _, _ ->
101-
requestPermissionsContract.launch(
102-
SectionEnum.entries.map {
103-
it.clazz.requiredPermissions.toList()
104-
}.flatten().toTypedArray()
105-
)
106-
}
107-
.setNegativeButton(R.string.no) { _, _ ->
108-
// Do nothing
109-
}
110-
.show()
111-
112-
true
113-
}
114-
115-
else -> super.onOptionsItemSelected(item)
116-
}
117-
118-
private fun exportData(uri: Uri) {
119-
lifecycleScope.launch {
120-
linearProgressIndicator.progress = 0
121-
linearProgressIndicator.isVisible = true
122-
123-
withContext(Dispatchers.IO) {
124-
contentResolver.openFileDescriptor(uri, "wt")?.use { parcelFileDescriptor ->
125-
FileWriter(parcelFileDescriptor.fileDescriptor).use { fileWriter ->
126-
val sections = SectionEnum.entries.map { it.clazz }
127-
128-
withContext(Dispatchers.Main) {
129-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
130-
linearProgressIndicator.min = 0
131-
}
132-
linearProgressIndicator.max = sections.size
133-
}
134-
135-
val sectionToData = mutableMapOf<Section, List<Subsection>>()
136-
137-
val updateData: suspend (
138-
Section, List<Subsection>?
139-
) -> Unit = { section, data ->
140-
data?.let {
141-
sectionToData[section] = it
142-
}
143-
144-
withContext(Dispatchers.Main) {
145-
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
146-
linearProgressIndicator.setProgress(
147-
sectionToData.size, true
148-
)
149-
} else {
150-
linearProgressIndicator.progress = sectionToData.size
151-
}
152-
}
153-
}
154-
155-
sections.map {
156-
async {
157-
updateData(
158-
it,
159-
runCatching {
160-
it.dataFlow(this@MainActivity)
161-
}.getOrNull()?.take(1)?.single()
162-
)
163-
}
164-
}.awaitAll()
165-
166-
val jsonData = Json.encodeToString(
167-
listSerializer<Map<Section, List<Subsection>>>(),
168-
sectionToData.toSerializable()
169-
)
170-
171-
fileWriter.write(jsonData)
172-
173-
withContext(Dispatchers.Main) {
174-
linearProgressIndicator.progress = linearProgressIndicator.max
175-
linearProgressIndicator.isVisible = false
176-
177-
Snackbar
178-
.make(contentView, R.string.export_data_done, Snackbar.LENGTH_SHORT)
179-
.setAction(R.string.export_data_open_file) {
180-
startActivity(
181-
Intent.createChooser(
182-
Intent(Intent.ACTION_VIEW, uri).apply {
183-
type = JSON_MIME_TYPE
184-
flags = Intent.FLAG_GRANT_READ_URI_PERMISSION
185-
},
186-
null,
187-
)
188-
)
189-
}
190-
.show()
191-
}
192-
}
193-
}
26+
setContent {
27+
CompositionLocalProvider(LocalPermissionsManager provides permissionsManager) {
28+
AthenaApp()
19429
}
19530
}
19631
}
19732

19833
companion object {
199-
private const val JSON_MIME_TYPE = "application/json"
200-
20134
init {
20235
System.loadLibrary("athena")
20336
}

0 commit comments

Comments
 (0)