Monorepo: Gitea CI, docs, auth/sync, backend APIs, gitignore

- Add Gitea Actions workflow; point README to gitea.d-bis.org/Sankofa_Phoenix/SMOA
- Expand .gitignore for Spring H2 data, secrets, Kotlin .kotlin/, tooling
- Track docs/api/generated ReDoc bundle; refresh api docs README
- Android: network/auth/sync, UI shell, tests; backend credentials/integrity APIs
- Docs, scripts (generate-api-docs), modules and core updates

Made-with: Cursor
This commit is contained in:
defiQUG
2026-03-23 20:19:24 -07:00
parent f97e23592c
commit a2dc194a49
234 changed files with 10008 additions and 1149 deletions
+10 -3
View File
@@ -1,8 +1,8 @@
plugins {
id("com.android.library")
id("org.jetbrains.kotlin.android")
id("com.google.dagger.hilt.android")
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
}
android {
@@ -29,6 +29,14 @@ android {
composeOptions {
kotlinCompilerExtensionVersion = "1.5.4"
}
hilt {
enableAggregatingTask = true
}
}
kapt {
correctErrorTypes = true
}
dependencies {
@@ -49,9 +57,8 @@ dependencies {
implementation(Dependencies.retrofit)
implementation(Dependencies.okHttp)
// WebRTC - TODO: Configure WebRTC dependency
// Optional: implementation(Dependencies.webrtc) when using org.webrtc:google-webrtc AAR
// WebRTC library needs to be built from source or obtained separately
// Uncomment when WebRTC is available:
// implementation(Dependencies.webrtc)
}
@@ -12,7 +12,7 @@ import com.smoa.modules.communications.domain.MediaCodecPolicy
import com.smoa.modules.communications.domain.MediaRoutingPolicy
import com.smoa.modules.communications.domain.NetworkPathSelector
import com.smoa.modules.communications.domain.SmartRoutingService
import com.smoa.modules.communications.domain.StubConnectionQualityMonitor
import com.smoa.modules.communications.domain.NetworkEstimatesConnectionQualityMonitor
import com.smoa.modules.communications.domain.VoiceTransport
import com.smoa.modules.communications.domain.WebRTCManager
import dagger.Module
@@ -28,8 +28,8 @@ object CommunicationsModule {
@Provides
@Singleton
fun provideConnectionQualityMonitor(
stub: StubConnectionQualityMonitor
): ConnectionQualityMonitor = stub
networkEstimates: NetworkEstimatesConnectionQualityMonitor
): ConnectionQualityMonitor = networkEstimates
@Provides
@Singleton
@@ -13,8 +13,31 @@ class ChannelManager @Inject constructor() {
private val channels = mutableMapOf<String, Channel>()
init {
// Initialize default channels (can be loaded from policy/config)
// Example channels would be added here
// Demo channels until policy/backend supplies live definitions
registerChannel(
Channel(
id = "unit1-ops",
name = "Unit 1 Ops",
description = "Demo — replace with backend-fed channel list",
unitRestricted = true,
allowedUnits = setOf("Unit1"),
allowedRoles = setOf(RBACFramework.Role.OPERATOR, RBACFramework.Role.ADMIN)
)
)
registerChannel(
Channel(
id = "command-net",
name = "Command Net",
description = "Demo shared net",
unitRestricted = false,
allowedUnits = emptySet(),
allowedRoles = setOf(
RBACFramework.Role.OPERATOR,
RBACFramework.Role.VIEWER,
RBACFramework.Role.ADMIN
)
)
)
}
/**
@@ -0,0 +1,79 @@
package com.smoa.modules.communications.domain
import android.content.Context
import android.net.ConnectivityManager
import android.net.Network
import android.net.NetworkCapabilities
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import javax.inject.Inject
import javax.inject.Singleton
/**
* Derives coarse [ConnectionQuality] from Android [NetworkCapabilities] link bandwidth
* (downstream kbps). RTT and packet loss stay unknown until WebRTC `getStats()` is integrated;
* call [applyWebRtcStats] from a future WebRTC-backed monitor to merge metrics.
*/
@Singleton
class NetworkEstimatesConnectionQualityMonitor @Inject constructor(
@ApplicationContext context: Context
) : ConnectionQualityMonitor {
private val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
private val _currentQuality = MutableStateFlow(snapshot(cm))
init {
cm.registerDefaultNetworkCallback(object : ConnectivityManager.NetworkCallback() {
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) {
_currentQuality.value = fromCapabilities(networkCapabilities)
}
override fun onLost(network: Network) {
_currentQuality.value = ConnectionQuality(
estimatedBandwidthKbps = 0,
rttMs = -1,
packetLossFraction = -1f,
tier = ConnectionTier.MEDIUM
)
}
})
}
override val currentQuality: StateFlow<ConnectionQuality> = _currentQuality.asStateFlow()
override fun qualityUpdates(): Flow<ConnectionQuality> = currentQuality
/**
* Optional hook for WebRTC stats layer: overrides bandwidth/tier when RTT/loss known.
*/
fun applyWebRtcStats(estimatedBandwidthKbps: Int, rttMs: Int, packetLoss: Float) {
val tier = connectionTierFromBandwidth(estimatedBandwidthKbps, rttMs, packetLoss)
_currentQuality.value = ConnectionQuality(
estimatedBandwidthKbps = estimatedBandwidthKbps,
rttMs = rttMs,
packetLossFraction = packetLoss,
tier = tier
)
}
private fun snapshot(cm: ConnectivityManager): ConnectionQuality {
val n = cm.activeNetwork ?: return ConnectionQuality(0, -1, -1f, ConnectionTier.MEDIUM)
val caps = cm.getNetworkCapabilities(n) ?: return ConnectionQuality(0, -1, -1f, ConnectionTier.MEDIUM)
return fromCapabilities(caps)
}
private fun fromCapabilities(caps: NetworkCapabilities): ConnectionQuality {
val kbps = caps.linkDownstreamBandwidthKbps
val effective = kbps.coerceAtLeast(0)
val tier = connectionTierFromBandwidth(effective, -1, -1f)
return ConnectionQuality(
estimatedBandwidthKbps = effective,
rttMs = -1,
packetLossFraction = -1f,
tier = tier
)
}
}
@@ -10,7 +10,10 @@ import javax.inject.Singleton
/**
* WebRTC Manager for voice and video communication.
* Provides WebRTC peer connection management for Communications and Meetings modules.
*
* **Stub:** Uses in-memory [WebRTCPeerConnection] placeholders. For production, add the
* `org.webrtc:google-webrtc` (or vendor) AAR, then replace stubs with `PeerConnectionFactory`,
* `PeerConnection`, and `getStats()` feeding [NetworkEstimatesConnectionQualityMonitor.applyWebRtcStats].
*/
@Singleton
class WebRTCManager @Inject constructor(
@@ -36,14 +39,7 @@ class WebRTCManager @Inject constructor(
// Create peer connection configuration
val rtcConfig = createRTCConfiguration()
// TODO: Initialize actual WebRTC PeerConnection when library is fully integrated
// This would:
// 1. Initialize PeerConnectionFactory with options
// 2. Create PeerConnection with rtcConfig
// 3. Set up audio/video tracks based on isAudioOnly
// 4. Configure ICE candidates
// 5. Set up signaling channel
// STUB: create domain placeholder; swap for real PeerConnection when WebRTC AAR is linked.
val peerConnection = WebRTCPeerConnection(
channelId = channelId,
isAudioOnly = isAudioOnly,
@@ -106,12 +102,6 @@ class WebRTCManager @Inject constructor(
stopVideoTransmission(peerConnection)
}
// TODO: Close actual WebRTC PeerConnection when library is fully integrated
// This would:
// 1. Close peer connection
// 2. Release all tracks
// 3. Dispose of resources
peerConnections.remove(peerConnection.channelId)
_connectionState.value = WebRTCConnectionState.Disconnected
Result.Success(Unit)
@@ -125,15 +115,7 @@ class WebRTCManager @Inject constructor(
*/
suspend fun startAudioTransmission(peerConnection: WebRTCPeerConnection): Result<Unit> {
return try {
// TODO: Start audio capture when WebRTC library is fully integrated
// This would:
// 1. Create AudioSource with constraints
// 2. Create AudioTrack from source
// 3. Add track to peer connection's sender
// 4. Enable track
// 5. Start audio capture
peerConnection.isAudioActive = true
peerConnection.isAudioActive = true // STUB: wire AudioTrack when WebRTC is integrated
Result.Success(Unit)
} catch (e: Exception) {
Result.Error(e)
@@ -145,13 +127,6 @@ class WebRTCManager @Inject constructor(
*/
suspend fun stopAudioTransmission(peerConnection: WebRTCPeerConnection): Result<Unit> {
return try {
// TODO: Stop audio capture when WebRTC library is fully integrated
// This would:
// 1. Disable audio track
// 2. Remove track from peer connection sender
// 3. Stop track
// 4. Release audio source
peerConnection.isAudioActive = false
Result.Success(Unit)
} catch (e: Exception) {
@@ -168,15 +143,7 @@ class WebRTCManager @Inject constructor(
return Result.Error(IllegalStateException("Video not supported for audio-only connection"))
}
// TODO: Start video capture when WebRTC library is fully integrated
// This would:
// 1. Create VideoSource with camera constraints
// 2. Create VideoTrack from source
// 3. Add track to peer connection's sender
// 4. Enable track
// 5. Start camera capture
peerConnection.isVideoActive = true
peerConnection.isVideoActive = true // STUB: wire VideoTrack / CameraX when WebRTC is integrated
Result.Success(Unit)
} catch (e: Exception) {
Result.Error(e)
@@ -188,13 +155,6 @@ class WebRTCManager @Inject constructor(
*/
suspend fun stopVideoTransmission(peerConnection: WebRTCPeerConnection): Result<Unit> {
return try {
// TODO: Stop video capture when WebRTC library is fully integrated
// This would:
// 1. Disable video track
// 2. Remove track from peer connection sender
// 3. Stop track
// 4. Release video source and camera
peerConnection.isVideoActive = false
Result.Success(Unit)
} catch (e: Exception) {
@@ -212,8 +172,6 @@ data class WebRTCPeerConnection(
val config: RTCConfiguration,
var isAudioActive: Boolean = false,
var isVideoActive: Boolean = false
// TODO: Add actual WebRTC PeerConnection instance when library is integrated
// private val peerConnection: PeerConnection
)
/**