feat(android): establish Jetpack Compose foundation with navigation and shared theme

- Set up project namespace to com.shieldai.android
- Add navigation-compose dependency
- Create ShieldAI brand color palette (Color.kt) with light/dark tokens
- Add Material3 Typography scale (Type.kt) with all text styles
- Create Shape.kt with small/medium/large rounded corners
- Implement ShieldAITheme with light/dark color schemes and optional dynamic color
- Define Screen sealed class with all destinations (Dashboard, Services, Alerts, Settings, Account, Login, Signup, Onboarding, ServiceDetail)
- Create NavGraph with composable routes and placeholder screens
- Implement BottomNavBar with 5 navigation items with icons
- Create AppNavigation with Scaffold, NavHost, and bottom nav visibility logic
- Add ShieldAIApp Application class and updated MainActivity
- Create vector drawables for bottom nav icons
- Add test files under new package
- Remove old template package (com.mikefreno.shieldai)
This commit is contained in:
2026-05-25 20:08:42 -04:00
parent 78c63f018c
commit 35bc5f4af1
28 changed files with 520 additions and 249 deletions

View File

@@ -4,7 +4,7 @@ plugins {
}
android {
namespace = "com.mikefreno.shieldai"
namespace = "com.shieldai.android"
compileSdk {
version = release(36) {
minorApiLevel = 1
@@ -12,7 +12,7 @@ android {
}
defaultConfig {
applicationId = "com.mikefreno.shieldai"
applicationId = "com.shieldai.android"
minSdk = 26
targetSdk = 36
versionCode = 1
@@ -43,6 +43,7 @@ dependencies {
implementation(libs.androidx.core.ktx)
implementation(libs.androidx.lifecycle.runtime.ktx)
implementation(libs.androidx.activity.compose)
implementation(libs.androidx.navigation.compose)
implementation(platform(libs.androidx.compose.bom))
implementation(libs.androidx.compose.ui)
implementation(libs.androidx.compose.ui.graphics)
@@ -56,4 +57,4 @@ dependencies {
androidTestImplementation(libs.androidx.compose.ui.test.junit4)
debugImplementation(libs.androidx.compose.ui.tooling)
debugImplementation(libs.androidx.compose.ui.test.manifest)
}
}

View File

@@ -1,24 +1,16 @@
package com.mikefreno.shieldai
package com.shieldai.android
import androidx.test.platform.app.InstrumentationRegistry
import androidx.test.ext.junit.runners.AndroidJUnit4
import androidx.test.platform.app.InstrumentationRegistry
import org.junit.Assert.assertEquals
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.Assert.*
/**
* Instrumented test, which will execute on an Android device.
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
@RunWith(AndroidJUnit4::class)
class ExampleInstrumentedTest {
@Test
fun useAppContext() {
// Context of the app under test.
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
assertEquals("com.mikefreno.shieldai", appContext.packageName)
assertEquals("com.shieldai.android", appContext.packageName)
}
}
}

View File

@@ -3,6 +3,7 @@
xmlns:tools="http://schemas.android.com/tools">
<application
android:name=".ShieldAIApp"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@@ -18,10 +19,9 @@
android:theme="@style/Theme.ShieldAI">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
</manifest>

View File

@@ -1,91 +0,0 @@
package com.mikefreno.shieldai
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.material3.adaptive.navigationsuite.NavigationSuiteScaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import com.mikefreno.shieldai.ui.theme.ShieldAITheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ShieldAITheme {
ShieldAIApp()
}
}
}
}
@PreviewScreenSizes
@Composable
fun ShieldAIApp() {
var currentDestination by rememberSaveable { mutableStateOf(AppDestinations.HOME) }
NavigationSuiteScaffold(
navigationSuiteItems = {
AppDestinations.entries.forEach {
item(
icon = {
Icon(
painterResource(it.icon),
contentDescription = it.label
)
},
label = { Text(it.label) },
selected = it == currentDestination,
onClick = { currentDestination = it }
)
}
}
) {
Scaffold(modifier = Modifier.fillMaxSize()) { innerPadding ->
Greeting(
name = "Android",
modifier = Modifier.padding(innerPadding)
)
}
}
}
enum class AppDestinations(
val label: String,
val icon: Int,
) {
HOME("Home", R.drawable.ic_home),
FAVORITES("Favorites", R.drawable.ic_favorite),
PROFILE("Profile", R.drawable.ic_account_box),
}
@Composable
fun Greeting(name: String, modifier: Modifier = Modifier) {
Text(
text = "Hello $name!",
modifier = modifier
)
}
@Preview(showBackground = true)
@Composable
fun GreetingPreview() {
ShieldAITheme {
Greeting("Android")
}
}

View File

@@ -1,11 +0,0 @@
package com.mikefreno.shieldai.ui.theme
import androidx.compose.ui.graphics.Color
val Purple80 = Color(0xFFD0BCFF)
val PurpleGrey80 = Color(0xFFCCC2DC)
val Pink80 = Color(0xFFEFB8C8)
val Purple40 = Color(0xFF6650a4)
val PurpleGrey40 = Color(0xFF625b71)
val Pink40 = Color(0xFF7D5260)

View File

@@ -1,58 +0,0 @@
package com.mikefreno.shieldai.ui.theme
import android.app.Activity
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val DarkColorScheme = darkColorScheme(
primary = Purple80,
secondary = PurpleGrey80,
tertiary = Pink80
)
private val LightColorScheme = lightColorScheme(
primary = Purple40,
secondary = PurpleGrey40,
tertiary = Pink40
/* Other default colors to override
background = Color(0xFFFFFBFE),
surface = Color(0xFFFFFBFE),
onPrimary = Color.White,
onSecondary = Color.White,
onTertiary = Color.White,
onBackground = Color(0xFF1C1B1F),
onSurface = Color(0xFF1C1B1F),
*/
)
@Composable
fun ShieldAITheme(
darkTheme: Boolean = isSystemInDarkTheme(),
// Dynamic color is available on Android 12+
dynamicColor: Boolean = true,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
content = content
)
}

View File

@@ -1,34 +0,0 @@
package com.mikefreno.shieldai.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
// Set of Material typography styles to start with
val Typography = Typography(
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
)
/* Other default text styles to override
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 22.sp,
lineHeight = 28.sp,
letterSpacing = 0.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
*/
)

View File

@@ -0,0 +1,20 @@
package com.shieldai.android
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge
import com.shieldai.android.navigation.AppNavigation
import com.shieldai.android.ui.theme.ShieldAITheme
class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
enableEdgeToEdge()
setContent {
ShieldAITheme {
AppNavigation()
}
}
}
}

View File

@@ -0,0 +1,9 @@
package com.shieldai.android
import android.app.Application
class ShieldAIApp : Application() {
override fun onCreate() {
super.onCreate()
}
}

View File

@@ -0,0 +1,47 @@
package com.shieldai.android.navigation
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.Modifier
import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController
@Composable
fun AppNavigation() {
val navController = rememberNavController()
val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route
val bottomNavScreens = setOf(
Screen.Dashboard.route,
Screen.Services.route,
Screen.Alerts.route,
Screen.Settings.route,
Screen.Account.route
)
val showBottomBar = currentRoute in bottomNavScreens
Scaffold(
bottomBar = {
if (showBottomBar) {
BottomNavBar(
currentRoute = currentRoute,
onNavigate = { screen ->
navController.navigate(screen.route) {
popUpTo(navController.graph.startDestinationId)
launchSingleTop = true
restoreState = true
}
}
)
}
}
) { innerPadding ->
NavGraph(
navController = navController,
modifier = Modifier.padding(innerPadding)
)
}
}

View File

@@ -0,0 +1,41 @@
package com.shieldai.android.navigation
import androidx.compose.material3.Icon
import androidx.compose.material3.NavigationBar
import androidx.compose.material3.NavigationBarItem
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.vectorResource
import com.shieldai.android.R
data class BottomNavItem(
val screen: Screen,
val label: String,
val icon: ImageVector
)
@Composable
fun BottomNavBar(
currentRoute: String?,
onNavigate: (Screen) -> Unit
) {
val items = listOf(
BottomNavItem(Screen.Dashboard, "Dashboard", ImageVector.vectorResource(R.drawable.ic_dashboard)),
BottomNavItem(Screen.Services, "Services", ImageVector.vectorResource(R.drawable.ic_services)),
BottomNavItem(Screen.Alerts, "Alerts", ImageVector.vectorResource(R.drawable.ic_alerts)),
BottomNavItem(Screen.Settings, "Settings", ImageVector.vectorResource(R.drawable.ic_settings)),
BottomNavItem(Screen.Account, "Account", ImageVector.vectorResource(R.drawable.ic_account_box))
)
NavigationBar {
items.forEach { item ->
NavigationBarItem(
icon = { Icon(item.icon, contentDescription = item.label) },
label = { Text(item.label) },
selected = currentRoute == item.screen.route,
onClick = { onNavigate(item.screen) }
)
}
}
}

View File

@@ -0,0 +1,74 @@
package com.shieldai.android.navigation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign
import androidx.navigation.NavHostController
import androidx.navigation.NavType
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.navArgument
@Composable
fun NavGraph(
navController: NavHostController,
modifier: Modifier = Modifier
) {
NavHost(
navController = navController,
startDestination = Screen.Dashboard.route,
modifier = modifier
) {
composable(Screen.Dashboard.route) {
PlaceholderScreen(title = "Dashboard")
}
composable(Screen.Services.route) {
PlaceholderScreen(title = "Services")
}
composable(Screen.Alerts.route) {
PlaceholderScreen(title = "Alerts")
}
composable(Screen.Settings.route) {
PlaceholderScreen(title = "Settings")
}
composable(Screen.Account.route) {
PlaceholderScreen(title = "Account")
}
composable(Screen.Login.route) {
PlaceholderScreen(title = "Login")
}
composable(Screen.Signup.route) {
PlaceholderScreen(title = "Signup")
}
composable(Screen.Onboarding.route) {
PlaceholderScreen(title = "Onboarding")
}
composable(
route = Screen.ServiceDetail.ROUTE,
arguments = listOf(navArgument("serviceId") { type = NavType.StringType })
) { backStackEntry ->
val serviceId = backStackEntry.arguments?.getString("serviceId") ?: ""
PlaceholderScreen(title = "Service: $serviceId")
}
}
}
@Composable
private fun PlaceholderScreen(title: String) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = title,
style = MaterialTheme.typography.headlineMedium,
textAlign = TextAlign.Center,
color = MaterialTheme.colorScheme.onBackground
)
}
}

View File

@@ -0,0 +1,18 @@
package com.shieldai.android.navigation
sealed class Screen(val route: String) {
data object Dashboard : Screen("dashboard")
data object Services : Screen("services")
data object Alerts : Screen("alerts")
data object Settings : Screen("settings")
data object Account : Screen("account")
data object Login : Screen("login")
data object Signup : Screen("signup")
data object Onboarding : Screen("onboarding")
data class ServiceDetail(val serviceId: String) : Screen("service_detail/{serviceId}") {
companion object {
const val ROUTE = "service_detail/{serviceId}"
fun createRoute(serviceId: String) = "service_detail/$serviceId"
}
}
}

View File

@@ -0,0 +1,37 @@
package com.shieldai.android.ui.theme
import androidx.compose.ui.graphics.Color
val BrandPrimary = Color(0xFF4F46E5)
val BrandPrimaryLight = Color(0xFF818CF8)
val BrandPrimaryDark = Color(0xFF3730A3)
val BrandAccent = Color(0xFF06B6D4)
val BrandAccentLight = Color(0xFF67E8F9)
val BrandAccentDark = Color(0xFF0891B2)
val BgPrimaryLight = Color(0xFFFFFFFF)
val BgSecondaryLight = Color(0xFFF8FAFC)
val BgTertiaryLight = Color(0xFFF1F5F9)
val BgPrimaryDark = Color(0xFF0F172A)
val BgSecondaryDark = Color(0xFF1E293B)
val BgTertiaryDark = Color(0xFF334155)
val TextPrimaryLight = Color(0xFF0F172A)
val TextSecondaryLight = Color(0xFF475569)
val TextTertiaryLight = Color(0xFF94A3B8)
val TextPrimaryDark = Color(0xFFF1F5F9)
val TextSecondaryDark = Color(0xFF94A3B8)
val TextTertiaryDark = Color(0xFF64748B)
val Success = Color(0xFF22C55E)
val Warning = Color(0xFFF59E0B)
val Error = Color(0xFFEF4444)
val Info = Color(0xFF3B82F6)
val SurfaceLight = Color(0xFFFFFFFF)
val SurfaceDark = Color(0xFF1E293B)
val OutlineLight = Color(0xFFE2E8F0)
val OutlineDark = Color(0xFF475569)

View File

@@ -0,0 +1,11 @@
package com.shieldai.android.ui.theme
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Shapes
import androidx.compose.ui.unit.dp
val Shapes = Shapes(
small = RoundedCornerShape(8.dp),
medium = RoundedCornerShape(12.dp),
large = RoundedCornerShape(16.dp)
)

View File

@@ -0,0 +1,80 @@
package com.shieldai.android.ui.theme
import android.os.Build
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.darkColorScheme
import androidx.compose.material3.dynamicDarkColorScheme
import androidx.compose.material3.dynamicLightColorScheme
import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalContext
private val LightColorScheme = lightColorScheme(
primary = BrandPrimary,
onPrimary = BgPrimaryLight,
primaryContainer = BrandPrimaryLight,
onPrimaryContainer = BgPrimaryLight,
secondary = BrandAccent,
onSecondary = BgPrimaryLight,
secondaryContainer = BrandAccentLight,
onSecondaryContainer = BgPrimaryDark,
tertiary = BrandPrimaryDark,
onTertiary = BgPrimaryLight,
background = BgPrimaryLight,
onBackground = TextPrimaryLight,
surface = SurfaceLight,
onSurface = TextPrimaryLight,
surfaceVariant = BgSecondaryLight,
onSurfaceVariant = TextSecondaryLight,
outline = OutlineLight,
outlineVariant = BgTertiaryLight,
error = Error,
onError = BgPrimaryLight
)
private val DarkColorScheme = darkColorScheme(
primary = BrandPrimaryLight,
onPrimary = BgPrimaryDark,
primaryContainer = BrandPrimary,
onPrimaryContainer = TextPrimaryDark,
secondary = BrandAccentLight,
onSecondary = BgPrimaryDark,
secondaryContainer = BrandAccent,
onSecondaryContainer = TextPrimaryDark,
tertiary = BrandPrimaryDark,
onTertiary = TextPrimaryDark,
background = BgPrimaryDark,
onBackground = TextPrimaryDark,
surface = SurfaceDark,
onSurface = TextPrimaryDark,
surfaceVariant = BgSecondaryDark,
onSurfaceVariant = TextSecondaryDark,
outline = OutlineDark,
outlineVariant = BgTertiaryDark,
error = Error,
onError = BgPrimaryDark
)
@Composable
fun ShieldAITheme(
darkTheme: Boolean = isSystemInDarkTheme(),
dynamicColor: Boolean = false,
content: @Composable () -> Unit
) {
val colorScheme = when {
dynamicColor && Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
val context = LocalContext.current
if (darkTheme) dynamicDarkColorScheme(context) else dynamicLightColorScheme(context)
}
darkTheme -> DarkColorScheme
else -> LightColorScheme
}
MaterialTheme(
colorScheme = colorScheme,
typography = Typography,
shapes = Shapes,
content = content
)
}

View File

@@ -0,0 +1,109 @@
package com.shieldai.android.ui.theme
import androidx.compose.material3.Typography
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.font.FontFamily
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.sp
val Typography = Typography(
displayLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 57.sp,
lineHeight = 64.sp,
letterSpacing = (-0.25).sp
),
displayMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 45.sp,
lineHeight = 52.sp
),
displaySmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Bold,
fontSize = 36.sp,
lineHeight = 44.sp
),
headlineLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.SemiBold,
fontSize = 32.sp,
lineHeight = 40.sp
),
headlineMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.SemiBold,
fontSize = 28.sp,
lineHeight = 36.sp
),
headlineSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.SemiBold,
fontSize = 24.sp,
lineHeight = 32.sp
),
titleLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.SemiBold,
fontSize = 22.sp,
lineHeight = 28.sp
),
titleMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.15.sp
),
titleSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp
),
bodyLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 16.sp,
lineHeight = 24.sp,
letterSpacing = 0.5.sp
),
bodyMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.25.sp
),
bodySmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Normal,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.4.sp
),
labelLarge = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 14.sp,
lineHeight = 20.sp,
letterSpacing = 0.1.sp
),
labelMedium = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 12.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
),
labelSmall = TextStyle(
fontFamily = FontFamily.Default,
fontWeight = FontWeight.Medium,
fontSize = 11.sp,
lineHeight = 16.sp,
letterSpacing = 0.5.sp
)
)

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,22c1.1,0 2,-0.9 2,-2h-4c0,1.1 0.89,2 2,2zM18,16v-5c0,-3.07 -1.64,-5.64 -4.5,-6.32V4c0,-0.83 -0.67,-1.5 -1.5,-1.5s-1.5,0.67 -1.5,1.5v0.68C7.63,5.36 6,7.92 6,11v5l-2,2v1h16v-1l-2,-2z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M3,13h8L11,3L3,3v10zM3,21h8v-6L3,15v6zM13,21h8L21,11h-8v10zM13,3v6h8L21,3h-8z" />
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,839L439,802Q333.23,704.88 264.12,634.44Q195,564 154,508.5Q113,453 96.5,408Q80,363 80,317Q80,226.85 140.5,166.42Q201,106 290,106Q347,106 395.5,133Q444,160 480,211Q522,157 569,131.5Q616,106 670,106Q759,106 819.5,166.42Q880,226.85 880,317Q880,363 863.5,408Q847,453 806,508.5Q765,564 695.88,634.44Q626.77,704.88 521,802L480,839ZM480,760Q581.24,667 646.62,600.5Q712,534 750.5,484Q789,434 804.5,394.86Q820,355.73 820,317.14Q820,251 778,208.5Q736,166 670.22,166Q618.7,166 574.85,197.5Q531,229 504,286L504,286L455,286L455,286Q429,230 385.15,198Q341.3,166 289.78,166Q224,166 182,208.5Q140,251 140,317.32Q140,356 155.5,395.5Q171,435 209.5,485.5Q248,536 314,602Q380,668 480,760ZM480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463L480,463L480,463L480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Q480,463 480,463Z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,6c1.1,0 2,0.9 2,2s-0.9,2 -2,2 -2,-0.9 -2,-2 0.9,-2 2,-2zM16,17H8v-1c0,-1.33 2.67,-2 4,-2s4,0.67 4,2v1z" />
</vector>

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/white"
android:pathData="M19.14,12.94c0.04,-0.3 0.06,-0.61 0.06,-0.94c0,-0.32 -0.02,-0.64 -0.07,-0.94l2.03,-1.58c0.18,-0.14 0.23,-0.41 0.12,-0.61l-1.92,-3.32c-0.12,-0.22 -0.37,-0.29 -0.59,-0.22l-2.39,0.96c-0.5,-0.38 -1.03,-0.7 -1.62,-0.94L14.4,2.81c-0.04,-0.24 -0.24,-0.41 -0.48,-0.41h-3.84c-0.24,0 -0.43,0.17 -0.47,0.41L9.25,5.35C8.66,5.59 8.12,5.92 7.63,6.29L5.24,5.33c-0.22,-0.08 -0.47,0 -0.59,0.22L2.74,8.87C2.62,9.08 2.66,9.34 2.86,9.48l2.03,1.58C4.84,11.36 4.8,11.69 4.8,12s0.02,0.64 0.07,0.94l-2.03,1.58c-0.18,0.14 -0.23,0.41 -0.12,0.61l1.92,3.32c0.12,0.22 0.37,0.29 0.59,0.22l2.39,-0.96c0.5,0.38 1.03,0.7 1.62,0.94l0.36,2.54c0.05,0.24 0.24,0.41 0.48,0.41h3.84c0.24,0 0.44,-0.17 0.47,-0.41l0.36,-2.54c0.59,-0.24 1.13,-0.56 1.62,-0.94l2.39,0.96c0.22,0.08 0.47,0 0.59,-0.22l1.92,-3.32c0.12,-0.22 0.07,-0.47 -0.12,-0.61L19.14,12.94zM12,15.6c-1.98,0 -3.6,-1.62 -3.6,-3.6s1.62,-3.6 3.6,-3.6 3.6,1.62 3.6,3.6 -1.62,3.6 -3.6,3.6z" />
</vector>

View File

@@ -1,10 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="purple_200">#FFBB86FC</color>
<color name="purple_500">#FF6200EE</color>
<color name="purple_700">#FF3700B3</color>
<color name="teal_200">#FF03DAC5</color>
<color name="teal_700">#FF018786</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
</resources>
<color name="brand_primary">#FF4F46E5</color>
<color name="brand_primary_light">#FF818CF8</color>
<color name="brand_accent">#FF06B6D4</color>
<color name="bg_primary">#FFFFFFFF</color>
<color name="bg_primary_dark">#FF0F172A</color>
<color name="text_primary">#FF0F172A</color>
<color name="text_primary_dark">#FFF1F5F9</color>
<color name="success">#FF22C55E</color>
<color name="warning">#FFF59E0B</color>
<color name="error">#FFEF4444</color>
<color name="info">#FF3B82F6</color>
</resources>

View File

@@ -1,3 +1,3 @@
<resources>
<string name="app_name">ShieldAI</string>
</resources>
</resources>

View File

@@ -1,5 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="Theme.ShieldAI" parent="android:Theme.Material.Light.NoActionBar" />
</resources>
</resources>

View File

@@ -1,17 +0,0 @@
package com.mikefreno.shieldai
import org.junit.Test
import org.junit.Assert.*
/**
* Example local unit test, which will execute on the development machine (host).
*
* See [testing documentation](http://d.android.com/tools/testing).
*/
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@@ -0,0 +1,11 @@
package com.shieldai.android
import org.junit.Assert.assertEquals
import org.junit.Test
class ExampleUnitTest {
@Test
fun addition_isCorrect() {
assertEquals(4, 2 + 2)
}
}

View File

@@ -6,6 +6,7 @@ junitVersion = "1.1.5"
espressoCore = "3.5.1"
lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0"
navigationCompose = "2.7.7"
kotlin = "2.2.10"
composeBom = "2025.12.00"
@@ -16,6 +17,7 @@ androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "j
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" }
androidx-navigation-compose = { group = "androidx.navigation", name = "navigation-compose", version.ref = "navigationCompose" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-compose-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-compose-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }