From 35bc5f4af1a2e2e5ed99c8ffea3432cfb5cf1fef Mon Sep 17 00:00:00 2001 From: Michael Freno Date: Mon, 25 May 2026 20:08:42 -0400 Subject: [PATCH] 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) --- android/ShieldAI/app/build.gradle.kts | 7 +- .../android}/ExampleInstrumentedTest.kt | 18 +-- .../ShieldAI/app/src/main/AndroidManifest.xml | 4 +- .../com/mikefreno/shieldai/MainActivity.kt | 91 --------------- .../com/mikefreno/shieldai/ui/theme/Color.kt | 11 -- .../com/mikefreno/shieldai/ui/theme/Theme.kt | 58 ---------- .../com/mikefreno/shieldai/ui/theme/Type.kt | 34 ------ .../java/com/shieldai/android/MainActivity.kt | 20 ++++ .../java/com/shieldai/android/ShieldAIApp.kt | 9 ++ .../android/navigation/AppNavigation.kt | 47 ++++++++ .../android/navigation/BottomNavBar.kt | 41 +++++++ .../shieldai/android/navigation/NavGraph.kt | 74 ++++++++++++ .../com/shieldai/android/navigation/Screen.kt | 18 +++ .../com/shieldai/android/ui/theme/Color.kt | 37 ++++++ .../com/shieldai/android/ui/theme/Shape.kt | 11 ++ .../com/shieldai/android/ui/theme/Theme.kt | 80 +++++++++++++ .../com/shieldai/android/ui/theme/Type.kt | 109 ++++++++++++++++++ .../app/src/main/res/drawable/ic_alerts.xml | 9 ++ .../src/main/res/drawable/ic_dashboard.xml | 9 ++ .../app/src/main/res/drawable/ic_favorite.xml | 9 -- .../app/src/main/res/drawable/ic_services.xml | 9 ++ .../app/src/main/res/drawable/ic_settings.xml | 9 ++ .../app/src/main/res/values/colors.xml | 20 ++-- .../app/src/main/res/values/strings.xml | 2 +- .../app/src/main/res/values/themes.xml | 3 +- .../com/mikefreno/shieldai/ExampleUnitTest.kt | 17 --- .../com/shieldai/android/ExampleUnitTest.kt | 11 ++ android/ShieldAI/gradle/libs.versions.toml | 2 + 28 files changed, 520 insertions(+), 249 deletions(-) rename android/ShieldAI/app/src/androidTest/java/com/{mikefreno/shieldai => shieldai/android}/ExampleInstrumentedTest.kt (53%) delete mode 100644 android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/MainActivity.kt delete mode 100644 android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Color.kt delete mode 100644 android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Theme.kt delete mode 100644 android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Type.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/MainActivity.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/ShieldAIApp.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/AppNavigation.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/BottomNavBar.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/NavGraph.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/Screen.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Color.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Shape.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Theme.kt create mode 100644 android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Type.kt create mode 100644 android/ShieldAI/app/src/main/res/drawable/ic_alerts.xml create mode 100644 android/ShieldAI/app/src/main/res/drawable/ic_dashboard.xml delete mode 100644 android/ShieldAI/app/src/main/res/drawable/ic_favorite.xml create mode 100644 android/ShieldAI/app/src/main/res/drawable/ic_services.xml create mode 100644 android/ShieldAI/app/src/main/res/drawable/ic_settings.xml delete mode 100644 android/ShieldAI/app/src/test/java/com/mikefreno/shieldai/ExampleUnitTest.kt create mode 100644 android/ShieldAI/app/src/test/java/com/shieldai/android/ExampleUnitTest.kt diff --git a/android/ShieldAI/app/build.gradle.kts b/android/ShieldAI/app/build.gradle.kts index 3e9ba6b..bad07e0 100644 --- a/android/ShieldAI/app/build.gradle.kts +++ b/android/ShieldAI/app/build.gradle.kts @@ -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) -} \ No newline at end of file +} diff --git a/android/ShieldAI/app/src/androidTest/java/com/mikefreno/shieldai/ExampleInstrumentedTest.kt b/android/ShieldAI/app/src/androidTest/java/com/shieldai/android/ExampleInstrumentedTest.kt similarity index 53% rename from android/ShieldAI/app/src/androidTest/java/com/mikefreno/shieldai/ExampleInstrumentedTest.kt rename to android/ShieldAI/app/src/androidTest/java/com/shieldai/android/ExampleInstrumentedTest.kt index 8e2b10f..8392f02 100644 --- a/android/ShieldAI/app/src/androidTest/java/com/mikefreno/shieldai/ExampleInstrumentedTest.kt +++ b/android/ShieldAI/app/src/androidTest/java/com/shieldai/android/ExampleInstrumentedTest.kt @@ -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) } -} \ No newline at end of file +} diff --git a/android/ShieldAI/app/src/main/AndroidManifest.xml b/android/ShieldAI/app/src/main/AndroidManifest.xml index 0cea239..9b03a97 100644 --- a/android/ShieldAI/app/src/main/AndroidManifest.xml +++ b/android/ShieldAI/app/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ xmlns:tools="http://schemas.android.com/tools"> - - \ No newline at end of file + diff --git a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/MainActivity.kt b/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/MainActivity.kt deleted file mode 100644 index df13126..0000000 --- a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/MainActivity.kt +++ /dev/null @@ -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") - } -} \ No newline at end of file diff --git a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Color.kt b/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Color.kt deleted file mode 100644 index 3b0f6f1..0000000 --- a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Color.kt +++ /dev/null @@ -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) \ No newline at end of file diff --git a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Theme.kt b/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Theme.kt deleted file mode 100644 index 1685537..0000000 --- a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Theme.kt +++ /dev/null @@ -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 - ) -} \ No newline at end of file diff --git a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Type.kt b/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Type.kt deleted file mode 100644 index c85542f..0000000 --- a/android/ShieldAI/app/src/main/java/com/mikefreno/shieldai/ui/theme/Type.kt +++ /dev/null @@ -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 - ) - */ -) \ No newline at end of file diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/MainActivity.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/MainActivity.kt new file mode 100644 index 0000000..0d7fc5e --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/MainActivity.kt @@ -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() + } + } + } +} diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/ShieldAIApp.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/ShieldAIApp.kt new file mode 100644 index 0000000..d262e1b --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/ShieldAIApp.kt @@ -0,0 +1,9 @@ +package com.shieldai.android + +import android.app.Application + +class ShieldAIApp : Application() { + override fun onCreate() { + super.onCreate() + } +} diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/AppNavigation.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/AppNavigation.kt new file mode 100644 index 0000000..2fbb44c --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/AppNavigation.kt @@ -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) + ) + } +} diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/BottomNavBar.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/BottomNavBar.kt new file mode 100644 index 0000000..e43ef71 --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/BottomNavBar.kt @@ -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) } + ) + } + } +} diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/NavGraph.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/NavGraph.kt new file mode 100644 index 0000000..9c91dc7 --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/NavGraph.kt @@ -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 + ) + } +} diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/Screen.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/Screen.kt new file mode 100644 index 0000000..685c27d --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/navigation/Screen.kt @@ -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" + } + } +} diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Color.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Color.kt new file mode 100644 index 0000000..0657a80 --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Color.kt @@ -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) diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Shape.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Shape.kt new file mode 100644 index 0000000..4bb24cf --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Shape.kt @@ -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) +) diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Theme.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Theme.kt new file mode 100644 index 0000000..827fb99 --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Theme.kt @@ -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 + ) +} diff --git a/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Type.kt b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Type.kt new file mode 100644 index 0000000..c2fb516 --- /dev/null +++ b/android/ShieldAI/app/src/main/java/com/shieldai/android/ui/theme/Type.kt @@ -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 + ) +) diff --git a/android/ShieldAI/app/src/main/res/drawable/ic_alerts.xml b/android/ShieldAI/app/src/main/res/drawable/ic_alerts.xml new file mode 100644 index 0000000..1e31ffa --- /dev/null +++ b/android/ShieldAI/app/src/main/res/drawable/ic_alerts.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/ShieldAI/app/src/main/res/drawable/ic_dashboard.xml b/android/ShieldAI/app/src/main/res/drawable/ic_dashboard.xml new file mode 100644 index 0000000..3a07383 --- /dev/null +++ b/android/ShieldAI/app/src/main/res/drawable/ic_dashboard.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/ShieldAI/app/src/main/res/drawable/ic_favorite.xml b/android/ShieldAI/app/src/main/res/drawable/ic_favorite.xml deleted file mode 100644 index bdd6245..0000000 --- a/android/ShieldAI/app/src/main/res/drawable/ic_favorite.xml +++ /dev/null @@ -1,9 +0,0 @@ - - - \ No newline at end of file diff --git a/android/ShieldAI/app/src/main/res/drawable/ic_services.xml b/android/ShieldAI/app/src/main/res/drawable/ic_services.xml new file mode 100644 index 0000000..82ee5ba --- /dev/null +++ b/android/ShieldAI/app/src/main/res/drawable/ic_services.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/ShieldAI/app/src/main/res/drawable/ic_settings.xml b/android/ShieldAI/app/src/main/res/drawable/ic_settings.xml new file mode 100644 index 0000000..018f673 --- /dev/null +++ b/android/ShieldAI/app/src/main/res/drawable/ic_settings.xml @@ -0,0 +1,9 @@ + + + diff --git a/android/ShieldAI/app/src/main/res/values/colors.xml b/android/ShieldAI/app/src/main/res/values/colors.xml index f8c6127..c32b548 100644 --- a/android/ShieldAI/app/src/main/res/values/colors.xml +++ b/android/ShieldAI/app/src/main/res/values/colors.xml @@ -1,10 +1,14 @@ - #FFBB86FC - #FF6200EE - #FF3700B3 - #FF03DAC5 - #FF018786 - #FF000000 - #FFFFFFFF - \ No newline at end of file + #FF4F46E5 + #FF818CF8 + #FF06B6D4 + #FFFFFFFF + #FF0F172A + #FF0F172A + #FFF1F5F9 + #FF22C55E + #FFF59E0B + #FFEF4444 + #FF3B82F6 + diff --git a/android/ShieldAI/app/src/main/res/values/strings.xml b/android/ShieldAI/app/src/main/res/values/strings.xml index 2994f3d..27135d5 100644 --- a/android/ShieldAI/app/src/main/res/values/strings.xml +++ b/android/ShieldAI/app/src/main/res/values/strings.xml @@ -1,3 +1,3 @@ ShieldAI - \ No newline at end of file + diff --git a/android/ShieldAI/app/src/main/res/values/themes.xml b/android/ShieldAI/app/src/main/res/values/themes.xml index 2549da6..cfa19dc 100644 --- a/android/ShieldAI/app/src/main/res/values/themes.xml +++ b/android/ShieldAI/app/src/main/res/values/themes.xml @@ -1,5 +1,4 @@ -