Initial commit

This commit is contained in:
defiQUG
2025-12-26 10:48:33 -08:00
commit 97f75e144f
270 changed files with 35886 additions and 0 deletions

131
app/build.gradle.kts Normal file
View File

@@ -0,0 +1,131 @@
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
id("kotlin-kapt")
id("dagger.hilt.android.plugin")
id("kotlin-parcelize")
}
android {
namespace = "com.smoa"
compileSdk = AppConfig.compileSdk
defaultConfig {
applicationId = AppConfig.applicationId
minSdk = AppConfig.minSdk
targetSdk = AppConfig.targetSdk
versionCode = AppConfig.versionCode
versionName = AppConfig.versionName
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
useSupportLibrary = true
}
}
buildTypes {
release {
isMinifyEnabled = true
isShrinkResources = true
proguardFiles(
getDefaultProguardFile("proguard-android-optimize.txt"),
"proguard-rules.pro"
)
}
}
compileOptions {
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17
}
kotlinOptions {
jvmTarget = "17"
}
buildFeatures {
compose = true
}
composeOptions {
kotlinCompilerExtensionVersion = "1.5.4"
}
packaging {
resources {
excludes += "/META-INF/{AL2.0,LGPL2.1}"
excludes += "/META-INF/DEPENDENCIES"
excludes += "/META-INF/LICENSE*"
excludes += "/META-INF/NOTICE*"
pickFirsts += "META-INF/blueprint.handlers"
pickFirsts += "META-INF/blueprint.schemas"
pickFirsts += "META-INF/spring.schemas"
pickFirsts += "META-INF/spring.handlers"
pickFirsts += "META-INF/wsdl.plugin.xml"
pickFirsts += "META-INF/cxf/bus-extensions.txt"
pickFirsts += "org/apache/cxf/endpoint/dynamic/simple-binding.xjb"
}
}
}
dependencies {
implementation(platform(Dependencies.composeBom))
implementation(Dependencies.composeUi)
implementation(Dependencies.composeUiGraphics)
implementation(Dependencies.composeUiToolingPreview)
implementation(Dependencies.composeMaterial3)
implementation(Dependencies.androidxActivityCompose)
implementation(Dependencies.androidxCoreKtx)
implementation(Dependencies.androidxLifecycleRuntimeKtx)
// Navigation
implementation(Dependencies.navigationCompose)
// Hilt
implementation(Dependencies.hiltAndroid)
kapt(Dependencies.hiltAndroidCompiler)
implementation(Dependencies.hiltNavigationCompose)
// Core modules
implementation(project(":core:auth"))
implementation(project(":core:security"))
implementation(project(":core:common"))
implementation(project(":core:barcode"))
implementation(project(":core:as4"))
implementation(project(":core:eidas"))
implementation(project(":core:signing"))
implementation(project(":core:certificates"))
// Feature modules
implementation(project(":modules:credentials"))
implementation(project(":modules:directory"))
implementation(project(":modules:communications"))
implementation(project(":modules:meetings"))
implementation(project(":modules:browser"))
implementation(project(":modules:orders"))
implementation(project(":modules:evidence"))
implementation(project(":modules:reports"))
implementation(project(":modules:atf"))
implementation(project(":modules:ncic"))
implementation(project(":modules:military"))
implementation(project(":modules:judicial"))
implementation(project(":modules:intelligence"))
// Security
implementation(Dependencies.securityCrypto)
implementation(Dependencies.biometric)
// Coroutines
implementation(Dependencies.coroutinesCore)
implementation(Dependencies.coroutinesAndroid)
// Testing
testImplementation(Dependencies.junit)
androidTestImplementation(Dependencies.androidxJunit)
androidTestImplementation(Dependencies.espressoCore)
androidTestImplementation(platform(Dependencies.composeBom))
androidTestImplementation(Dependencies.composeUiTestJunit4)
debugImplementation(Dependencies.composeUiTooling)
debugImplementation(Dependencies.composeUiTestManifest)
}

26
app/proguard-rules.pro vendored Normal file
View File

@@ -0,0 +1,26 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
# Keep Hilt classes
-keep class dagger.hilt.** { *; }
-keep class javax.inject.** { *; }
-keep class * extends dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper { *; }
# Keep Room classes
-keep class * extends androidx.room.RoomDatabase
-keep @androidx.room.Entity class *
# Keep data classes
-keepclassmembers class * {
@androidx.room.* <methods>;
}
# Keep security-related classes
-keep class androidx.security.** { *; }
-dontwarn androidx.security.**
# Keep Kotlin coroutines
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
<!-- Network permissions -->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<!-- Biometric permissions -->
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<!-- Audio permissions for communications and meetings -->
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<!-- Camera permission for meetings -->
<uses-permission android:name="android.permission.CAMERA" />
<!-- VPN permission -->
<uses-permission android:name="android.permission.BIND_VPN_SERVICE" />
<!-- Storage permissions (restricted) -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
android:maxSdkVersion="32" />
<application
android:name=".SMOAApplication"
android:allowBackup="false"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/Theme.SMOA"
android:usesCleartextTraffic="false"
android:networkSecurityConfig="@xml/network_security_config"
tools:targetApi="31">
<activity
android:name=".MainActivity"
android:exported="true"
android:theme="@style/Theme.SMOA"
android:windowSoftInputMode="adjustResize"
android:screenOrientation="fullSensor">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>

View File

@@ -0,0 +1,111 @@
package com.smoa
import android.content.res.Configuration
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.ui.Modifier
import com.smoa.core.common.ConnectivityManager
import com.smoa.core.common.FoldableStateManager
import com.smoa.ui.main.MainScreen
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@AndroidEntryPoint
class MainActivity : ComponentActivity() {
@Inject
lateinit var connectivityManager: ConnectivityManager
@Inject
lateinit var foldableStateManager: FoldableStateManager
@Inject
lateinit var userSession: com.smoa.core.auth.UserSession
@Inject
lateinit var directoryService: com.smoa.modules.directory.domain.DirectoryService
@Inject
lateinit var communicationsService: com.smoa.modules.communications.domain.CommunicationsService
@Inject
lateinit var meetingsService: com.smoa.modules.meetings.domain.MeetingsService
@Inject
lateinit var browserService: com.smoa.modules.browser.domain.BrowserService
@Inject
lateinit var urlFilter: com.smoa.modules.browser.domain.URLFilter
@Inject
lateinit var screenProtection: com.smoa.core.security.ScreenProtection
@Inject
lateinit var vpnManager: com.smoa.core.security.VPNManager
@Inject
lateinit var rbacFramework: com.smoa.core.auth.RBACFramework
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// Enable screen protection for the entire application
screenProtection.enableScreenProtection(this)
// Start VPN monitoring
vpnManager.startVPNMonitoring()
// Update fold state on configuration change
updateFoldState()
// Initialize default user session for testing
// In production, this would come from authentication
userSession.setUser(
com.smoa.core.auth.UserInfo(
userId = "user1",
userName = "Test User",
role = com.smoa.core.auth.RBACFramework.Role.OPERATOR,
unit = "Unit1",
clearanceLevel = null,
missionAssignment = null
)
)
setContent {
MaterialTheme {
Surface(
modifier = Modifier.fillMaxSize(),
color = MaterialTheme.colorScheme.background
) {
MainScreen(
connectivityManager = connectivityManager,
foldableStateManager = foldableStateManager,
userSession = userSession,
rbacFramework = rbacFramework,
directoryService = directoryService,
communicationsService = communicationsService,
meetingsService = meetingsService,
browserService = browserService,
urlFilter = urlFilter,
screenProtection = screenProtection,
vpnManager = vpnManager
)
}
}
}
}
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
updateFoldState()
}
private fun updateFoldState() {
foldableStateManager.updateFoldState(resources.configuration)
}
}

View File

@@ -0,0 +1,12 @@
package com.smoa
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class SMOAApplication : Application() {
override fun onCreate() {
super.onCreate()
}
}

View File

@@ -0,0 +1,180 @@
package com.smoa.ui.main
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.DrawerState
import androidx.compose.material3.DrawerValue
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.ModalNavigationDrawer
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Menu
import androidx.navigation.compose.rememberNavController
import com.smoa.core.auth.RBACFramework
import com.smoa.core.auth.UserSession
import com.smoa.core.common.ConnectivityManager
import com.smoa.core.common.FoldableStateManager
import com.smoa.modules.browser.domain.BrowserService
import com.smoa.modules.browser.domain.URLFilter
import com.smoa.modules.communications.domain.CommunicationsService
import com.smoa.modules.directory.domain.DirectoryService
import com.smoa.modules.meetings.domain.MeetingsService
import com.smoa.core.security.ScreenProtection
import com.smoa.core.security.VPNManager
import com.smoa.ui.navigation.SMOANavigation
/**
* Main application screen with module navigation.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun MainScreen(
connectivityManager: ConnectivityManager,
foldableStateManager: FoldableStateManager,
userSession: UserSession,
rbacFramework: RBACFramework,
directoryService: DirectoryService,
communicationsService: CommunicationsService,
meetingsService: MeetingsService,
browserService: BrowserService,
urlFilter: URLFilter,
screenProtection: ScreenProtection,
vpnManager: VPNManager,
modifier: Modifier = Modifier
) {
val navController = rememberNavController()
val currentUser by userSession.currentUser.collectAsState()
var drawerOpen by remember { mutableStateOf(false) }
val userRole = currentUser?.role ?: RBACFramework.Role.GUEST
val userUnit = currentUser?.unit
val userId = currentUser?.userId ?: "guest"
val drawerState = remember { DrawerState(DrawerValue.Closed) }
// Update drawer state when drawerOpen changes
LaunchedEffect(drawerOpen) {
if (drawerOpen) {
drawerState.open()
} else {
drawerState.close()
}
}
// Update drawerOpen when drawer state changes
LaunchedEffect(drawerState.currentValue) {
drawerOpen = drawerState.currentValue == DrawerValue.Open
}
ModalNavigationDrawer(
drawerState = drawerState,
drawerContent = {
com.smoa.ui.navigation.NavigationDrawer(
navController = navController,
userSession = userSession,
rbacFramework = rbacFramework,
onDrawerDismiss = { drawerOpen = false }
)
},
modifier = modifier
) {
Scaffold(
topBar = {
TopAppBar(
title = { Text("SMOA") },
navigationIcon = {
IconButton(onClick = { drawerOpen = true }) {
Icon(Icons.Default.Menu, contentDescription = "Menu")
}
},
actions = {
// VPN status indicator
VPNStatusIndicator(vpnManager)
// Connectivity status indicator
ConnectivityStatusIndicator(connectivityManager)
}
)
},
modifier = Modifier
) { paddingValues ->
SMOANavigation(
navController = navController,
connectivityManager = connectivityManager,
foldableStateManager = foldableStateManager,
directoryService = directoryService,
communicationsService = communicationsService,
meetingsService = meetingsService,
browserService = browserService,
urlFilter = urlFilter,
screenProtection = screenProtection,
userRole = userRole,
userUnit = userUnit,
userId = userId
)
}
}
}
@Composable
fun VPNStatusIndicator(
vpnManager: VPNManager,
modifier: Modifier = Modifier
) {
val vpnState by vpnManager.vpnState.collectAsState()
val statusText = when (vpnState) {
com.smoa.core.security.VPNState.Connected -> "VPN"
com.smoa.core.security.VPNState.Disconnected -> "NO VPN"
com.smoa.core.security.VPNState.PermissionRequired -> "VPN REQ"
com.smoa.core.security.VPNState.PermissionGranted -> "VPN OK"
com.smoa.core.security.VPNState.Error -> "VPN ERR"
com.smoa.core.security.VPNState.Unknown -> "VPN ?"
}
Text(
text = statusText,
style = MaterialTheme.typography.bodySmall,
color = when (vpnState) {
com.smoa.core.security.VPNState.Connected -> MaterialTheme.colorScheme.primary
com.smoa.core.security.VPNState.Disconnected,
com.smoa.core.security.VPNState.PermissionRequired -> MaterialTheme.colorScheme.error
else -> MaterialTheme.colorScheme.onSurface
},
modifier = modifier.padding(horizontal = 8.dp)
)
}
@Composable
fun ConnectivityStatusIndicator(
connectivityManager: ConnectivityManager,
modifier: Modifier = Modifier
) {
val state = connectivityManager.connectivityState.value
val statusText = when (state) {
ConnectivityManager.ConnectivityState.Online -> "ONLINE"
ConnectivityManager.ConnectivityState.Offline -> "OFFLINE"
ConnectivityManager.ConnectivityState.Restricted -> "RESTRICTED"
ConnectivityManager.ConnectivityState.Unknown -> "UNKNOWN"
}
Text(
text = statusText,
style = MaterialTheme.typography.bodySmall,
modifier = modifier.padding(horizontal = 8.dp)
)
}

View File

@@ -0,0 +1,117 @@
package com.smoa.ui.navigation
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.*
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import com.smoa.core.auth.RBACFramework
import com.smoa.core.auth.UserSession
import javax.inject.Inject
/**
* Navigation drawer for module selection.
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun NavigationDrawer(
navController: NavController,
userSession: UserSession,
rbacFramework: RBACFramework,
onDrawerDismiss: () -> Unit,
modifier: Modifier = Modifier
) {
val currentUser by userSession.currentUser.collectAsState()
val userRole = currentUser?.role ?: RBACFramework.Role.GUEST
ModalDrawerSheet(
modifier = modifier.width(280.dp)
) {
Column(
modifier = Modifier
.fillMaxHeight()
.padding(16.dp)
) {
// User info header
Text(
text = currentUser?.userName ?: "Guest",
style = MaterialTheme.typography.headlineSmall,
modifier = Modifier.padding(bottom = 8.dp)
)
Text(
text = "Role: ${userRole.name}",
style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.onSurfaceVariant,
modifier = Modifier.padding(bottom = 24.dp)
)
Divider(modifier = Modifier.padding(vertical = 8.dp))
// Navigation items
NavigationDrawerItem(
label = { Text("Credentials") },
selected = navController.currentDestination?.route == SMOARoute.Credentials.route,
onClick = {
navController.navigate(SMOARoute.Credentials.route)
onDrawerDismiss()
},
icon = { Icon(Icons.Default.Info, contentDescription = null) }
)
if (rbacFramework.canAccessModule(userRole, RBACFramework.Module.DIRECTORY)) {
NavigationDrawerItem(
label = { Text("Directory") },
selected = navController.currentDestination?.route == SMOARoute.Directory.route,
onClick = {
navController.navigate(SMOARoute.Directory.route)
onDrawerDismiss()
},
icon = { Icon(Icons.Default.Person, contentDescription = null) }
)
}
if (rbacFramework.canAccessModule(userRole, RBACFramework.Module.COMMUNICATIONS)) {
NavigationDrawerItem(
label = { Text("Communications") },
selected = navController.currentDestination?.route == SMOARoute.Communications.route,
onClick = {
navController.navigate(SMOARoute.Communications.route)
onDrawerDismiss()
},
icon = { Icon(Icons.Default.Phone, contentDescription = null) }
)
}
if (rbacFramework.canAccessModule(userRole, RBACFramework.Module.MEETINGS)) {
NavigationDrawerItem(
label = { Text("Meetings") },
selected = navController.currentDestination?.route == SMOARoute.Meetings.route,
onClick = {
navController.navigate(SMOARoute.Meetings.route)
onDrawerDismiss()
},
icon = { Icon(Icons.Default.Phone, contentDescription = null) }
)
}
if (rbacFramework.canAccessModule(userRole, RBACFramework.Module.BROWSER)) {
NavigationDrawerItem(
label = { Text("Browser") },
selected = navController.currentDestination?.route == SMOARoute.Browser.route,
onClick = {
navController.navigate(SMOARoute.Browser.route)
onDrawerDismiss()
},
icon = { Icon(Icons.Default.Info, contentDescription = null) }
)
}
}
}
}

View File

@@ -0,0 +1,99 @@
package com.smoa.ui.navigation
import androidx.compose.runtime.Composable
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import com.smoa.core.auth.RBACFramework
import com.smoa.modules.browser.BrowserModule
import com.smoa.modules.browser.domain.BrowserService
import com.smoa.modules.browser.domain.URLFilter
import com.smoa.modules.communications.CommunicationsModule
import com.smoa.modules.communications.domain.CommunicationsService
import com.smoa.modules.credentials.CredentialsModule
import com.smoa.modules.directory.DirectoryModule
import com.smoa.modules.directory.domain.DirectoryService
import com.smoa.modules.meetings.MeetingsModule
import com.smoa.modules.meetings.domain.MeetingsService
import com.smoa.core.common.ConnectivityManager
import com.smoa.core.common.FoldableStateManager
import com.smoa.core.security.ScreenProtection
/**
* Navigation routes for SMOA modules.
*/
sealed class SMOARoute(val route: String) {
object Credentials : SMOARoute("credentials")
object Directory : SMOARoute("directory")
object Communications : SMOARoute("communications")
object Meetings : SMOARoute("meetings")
object Browser : SMOARoute("browser")
}
/**
* Navigation module for SMOA.
* Handles navigation between different modules.
*/
@Composable
fun SMOANavigation(
navController: NavHostController,
connectivityManager: ConnectivityManager,
foldableStateManager: FoldableStateManager,
directoryService: DirectoryService,
communicationsService: CommunicationsService,
meetingsService: MeetingsService,
browserService: BrowserService,
urlFilter: URLFilter,
screenProtection: ScreenProtection,
userRole: RBACFramework.Role,
userUnit: String?,
userId: String
) {
NavHost(
navController = navController,
startDestination = SMOARoute.Credentials.route
) {
composable(SMOARoute.Credentials.route) {
CredentialsModule(
modifier = androidx.compose.ui.Modifier
)
}
composable(SMOARoute.Directory.route) {
DirectoryModule(
directoryService = directoryService,
userRole = userRole,
userUnit = userUnit,
modifier = androidx.compose.ui.Modifier
)
}
composable(SMOARoute.Communications.route) {
CommunicationsModule(
communicationsService = communicationsService,
userRole = userRole,
userUnit = userUnit,
modifier = androidx.compose.ui.Modifier
)
}
composable(SMOARoute.Meetings.route) {
MeetingsModule(
meetingsService = meetingsService,
userRole = userRole,
userId = userId,
modifier = androidx.compose.ui.Modifier
)
}
composable(SMOARoute.Browser.route) {
BrowserModule(
browserService = browserService,
urlFilter = urlFilter,
screenProtection = screenProtection,
modifier = androidx.compose.ui.Modifier
)
}
}
}

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/white"/>
<foreground android:drawable="@android:drawable/ic_menu_gallery"/>
</adaptive-icon>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android">
<background android:drawable="@android:color/white"/>
<foreground android:drawable="@android:drawable/ic_menu_gallery"/>
</adaptive-icon>

View File

@@ -0,0 +1,33 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">SMOA</string>
<!-- Authentication -->
<string name="auth_pin_required">Enter PIN</string>
<string name="auth_biometric_required">Biometric Authentication Required</string>
<string name="auth_fingerprint_required">Fingerprint Required</string>
<string name="auth_facial_required">Facial Recognition Required</string>
<string name="auth_all_factors_required">All authentication factors required</string>
<string name="auth_lockout">Too many failed attempts. Account locked.</string>
<string name="auth_retry">Retry</string>
<!-- Connectivity -->
<string name="status_online">ONLINE</string>
<string name="status_offline">OFFLINE</string>
<string name="status_restricted">RESTRICTED</string>
<!-- Modules -->
<string name="module_credentials">Issued Credentials</string>
<string name="module_directory">Internal Directory</string>
<string name="module_communications">Unit Communications</string>
<string name="module_meetings">Secure Meetings</string>
<string name="module_browser">Controlled Browser</string>
<!-- Common -->
<string name="lock">Lock</string>
<string name="unlock">Unlock</string>
<string name="cancel">Cancel</string>
<string name="ok">OK</string>
<string name="error">Error</string>
</resources>

View File

@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.SMOA" parent="android:Theme.Material.Light.NoActionBar">
<item name="android:statusBarColor">@android:color/transparent</item>
</style>
</resources>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<full-backup-content>
<!-- Exclude sensitive data from backups -->
<exclude domain="sharedpref" path="secure_prefs.xml" />
<exclude domain="database" path="smoa_database.db" />
<exclude domain="database" path="audit_logs.db" />
</full-backup-content>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<data-extraction-rules>
<cloud-backup>
<exclude domain="sharedpref" path="secure_prefs.xml" />
<exclude domain="database" path="smoa_database.db" />
<exclude domain="database" path="audit_logs.db" />
</cloud-backup>
</data-extraction-rules>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<network-security-config>
<!-- Base configuration for production -->
<base-config cleartextTrafficPermitted="false">
<trust-anchors>
<certificates src="system" />
</trust-anchors>
</base-config>
<!-- Certificate pinning will be configured programmatically -->
</network-security-config>