Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions buildSrc/src/main/kotlin/convention.compose.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ plugins {
dependencies {
"implementation"(androidx.compose.foundation)
"implementation"(androidx.compose.material)
"implementation"(androidx.compose.material3)
"implementation"(androidx.compose.ui)
"implementation"(androidx.compose.runtime)
"implementation"(androidx.lifecycle.viewmodel.compose)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,4 +34,5 @@ kotlin {

dependencies {
implementation(project(":panel-core"))
implementation(project(":panel-ui-kit"))
}
2 changes: 2 additions & 0 deletions panel-core/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ kotlin {
}

dependencies {
implementation(project(":panel-ui-kit"))

implementation(androidx.activity.compose)
implementation(androidx.navigation.compose)
implementation(androidx.core)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,17 @@ package com.redmadrobot.debug.core.ui.debugpanel
import android.os.Bundle
import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.remember
import com.redmadrobot.debug.core.inapp.compose.DebugPanelScreen
import com.redmadrobot.debug.uikit.theme.DebugPanelTheme
import com.redmadrobot.debug.uikit.theme.ThemeState

internal class DebugPanelActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContent {
MaterialTheme {
val themeState = remember { ThemeState() }
DebugPanelTheme(themeState = themeState) {
DebugPanelScreen(onClose = { finish() })
}
}
Expand Down
45 changes: 45 additions & 0 deletions panel-ui-kit/build.gradle.kts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
plugins {
id("com.android.library")
id("convention.compose")
id("convention-publish")
id("convention.detekt")
}

description = "Debug panel UI kit: theme, design tokens, shared components"

android {
compileSdk = Project.COMPILE_SDK
lint.targetSdk = Project.TARGET_SDK

defaultConfig {
minSdk = Project.MIN_SDK

consumerProguardFile("consumer-rules.pro")
}

buildTypes {
getByName(Project.BuildTypes.release) {
isMinifyEnabled = false
proguardFiles(
getDefaultProguardFile(Project.Proguard.androidOptimizedRules),
Project.Proguard.projectRules
)
}
}

kotlin {
jvmToolchain(17)
}

namespace = "com.redmadrobot.debug.uikit"
}

kotlin {
explicitApi()
}

dependencies {
implementation(androidx.compose.material3)
implementation(androidx.compose.ui.tooling)
implementation(androidx.compose.ui.tooling.preview)
}
Empty file added panel-ui-kit/consumer-rules.pro
Empty file.
1 change: 1 addition & 0 deletions panel-ui-kit/proguard-rules.pro
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Add project specific ProGuard rules here.
2 changes: 2 additions & 0 deletions panel-ui-kit/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest />
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package com.redmadrobot.debug.uikit.theme

import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.spring
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.ui.graphics.Color

private val ColorAnimSpec = spring<Color>(stiffness = 600f)

@Composable
internal fun DebugPanelColors.animated(): DebugPanelColors = DebugPanelColors(
background = background.animated(),
button = button.animated(),
content = content.animated(),
stroke = stroke.animated(),
surface = surface.animated(),
source = source.animated(),
)

@Composable
private fun animateColor(target: Color): Color {
val animated by animateColorAsState(
targetValue = target,
animationSpec = ColorAnimSpec,
label = "",
)
return animated
}

@Composable
private fun BackgroundColors.animated(): BackgroundColors = BackgroundColors(
primary = animateColor(primary),
secondary = animateColor(secondary),
tertiary = animateColor(tertiary),
)

@Composable
private fun ButtonColors.animated(): ButtonColors = ButtonColors(
primary = animateColor(primary),
onPrimary = animateColor(onPrimary),
secondary = animateColor(secondary),
onSecondary = animateColor(onSecondary),
error = animateColor(error),
onError = animateColor(onError),
)

@Composable
private fun ContentColors.animated(): ContentColors = ContentColors(
primary = animateColor(primary),
secondary = animateColor(secondary),
tertiary = animateColor(tertiary),
accent = animateColor(accent),
error = animateColor(error),
teal = animateColor(teal),
)

@Composable
private fun StrokeColors.animated(): StrokeColors = StrokeColors(
primary = animateColor(primary),
secondary = animateColor(secondary),
)

@Composable
private fun SurfaceColors.animated(): SurfaceColors = SurfaceColors(
primary = animateColor(primary),
secondary = animateColor(secondary),
tertiary = animateColor(tertiary),
dialog = animateColor(dialog),
selected = animateColor(selected),
)

@Composable
private fun SourceColors.animated(): SourceColors = SourceColors(
defaultText = animateColor(defaultText),
defaultBackground = animateColor(defaultBackground),
debugText = animateColor(debugText),
debugBackground = animateColor(debugBackground),
remoteText = animateColor(remoteText),
remoteBackground = animateColor(remoteBackground),
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
@file:Suppress("MagicNumber")

package com.redmadrobot.debug.uikit.theme

import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.ExperimentalLayoutApi
import androidx.compose.foundation.layout.FlowRow
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.luminance
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

internal object BaseColors {
// Purple
val Purple10 = Color(0xFF21005D)
val Purple20 = Color(0xFF381E72)
val Purple30 = Color(0xFF4A4458)
val Purple40 = Color(0xFF6750A4)
val Purple80 = Color(0xFFD0BCFF)
val Purple90 = Color(0xFFE8DEF8)
val Purple95 = Color(0xFFF3EDF7)
val Purple99 = Color(0xFFFEF7FF)

// Neutral
val Neutral10 = Color(0xFF1C1B1F)
val Neutral20 = Color(0xFF313033)
val Neutral30 = Color(0xFF49454F)
val Neutral40 = Color(0xFF605D66)
val Neutral50 = Color(0xFF79747E)
val Neutral60 = Color(0xFF938F99)
val Neutral80 = Color(0xFFCAC4D0)
val Neutral87 = Color(0xFFE6E1E5)
val Neutral90 = Color(0xFFE6E0E9)
val Neutral92 = Color(0xFFECE6F0)
val Neutral94 = Color(0xFFF4EFF4)
val Neutral95 = Color(0xFFE7E0EC)
val Neutral99 = Color(0xFFFFFBFE)

// Neutral variant (dark surfaces)
val NeutralVariant20 = Color(0xFF2B2930)
val NeutralVariant30 = Color(0xFF36343B)
val NeutralVariant40 = Color(0xFF414046)

// Secondary
val Secondary40 = Color(0xFF625B71)
val Secondary80 = Color(0xFFCCC2DC)
val Secondary90 = Color(0xFFE8DEF8)

// Tertiary
val Tertiary20 = Color(0xFF492532)
val Tertiary30 = Color(0xFF633B48)
val Tertiary40 = Color(0xFF7D5260)
val Tertiary80 = Color(0xFFEFB8C8)
val Tertiary90 = Color(0xFFFFD8E4)

// Error
val Error20 = Color(0xFF601410)
val Error30 = Color(0xFF8C1D18)
val Error40 = Color(0xFFB3261E)
val Error80 = Color(0xFFF2B8B5)
val Error90 = Color(0xFFF9DEDC)

// Status / accent
val Teal = Color(0xFF03DAC6)
val Green = Color(0xFF1B6E2D)
val GreenLight = Color(0xFFD6F5E0)
val GreenDark = Color(0xFF1B3D2A)
val GreenDarkText = Color(0xFF7DD99E)
val Orange = Color(0xFFE65100)
val OrangeLight = Color(0xFFFFF3E0)
val OrangeDark = Color(0xFF3D2E10)
val OrangeDarkText = Color(0xFFFFB74D)

val White = Color(0xFFFFFFFF)
val Black = Color(0xFF000000)
}

@Suppress("LongMethod")
@OptIn(ExperimentalLayoutApi::class)
@Composable
@Preview(showBackground = true)
private fun Preview() {
DebugPanelTheme {
FlowRow(
modifier = Modifier
.fillMaxSize()
.verticalScroll(state = rememberScrollState())
.padding(horizontal = 8.dp),
horizontalArrangement = Arrangement.spacedBy(space = 4.dp),
verticalArrangement = Arrangement.spacedBy(space = 4.dp),
) {
// Purple
ColorElement(BaseColors.Purple10, "Purple10")
ColorElement(BaseColors.Purple20, "Purple20")
ColorElement(BaseColors.Purple30, "Purple30")
ColorElement(BaseColors.Purple40, "Purple40")
ColorElement(BaseColors.Purple80, "Purple80")
ColorElement(BaseColors.Purple90, "Purple90")
ColorElement(BaseColors.Purple95, "Purple95")
ColorElement(BaseColors.Purple99, "Purple99")

// Neutral
ColorElement(BaseColors.Neutral10, "Neutral10")
ColorElement(BaseColors.Neutral20, "Neutral20")
ColorElement(BaseColors.Neutral30, "Neutral30")
ColorElement(BaseColors.Neutral40, "Neutral40")
ColorElement(BaseColors.Neutral50, "Neutral50")
ColorElement(BaseColors.Neutral60, "Neutral60")
ColorElement(BaseColors.Neutral80, "Neutral80")
ColorElement(BaseColors.Neutral87, "Neutral87")
ColorElement(BaseColors.Neutral90, "Neutral90")
ColorElement(BaseColors.Neutral92, "Neutral92")
ColorElement(BaseColors.Neutral94, "Neutral94")
ColorElement(BaseColors.Neutral95, "Neutral95")
ColorElement(BaseColors.Neutral99, "Neutral99")

// Neutral variant
ColorElement(BaseColors.NeutralVariant20, "NV20")
ColorElement(BaseColors.NeutralVariant30, "NV30")
ColorElement(BaseColors.NeutralVariant40, "NV40")

// Secondary
ColorElement(BaseColors.Secondary40, "Secondary40")
ColorElement(BaseColors.Secondary80, "Secondary80")
ColorElement(BaseColors.Secondary90, "Secondary90")

// Tertiary
ColorElement(BaseColors.Tertiary20, "Tertiary20")
ColorElement(BaseColors.Tertiary30, "Tertiary30")
ColorElement(BaseColors.Tertiary40, "Tertiary40")
ColorElement(BaseColors.Tertiary80, "Tertiary80")
ColorElement(BaseColors.Tertiary90, "Tertiary90")

// Error
ColorElement(BaseColors.Error20, "Error20")
ColorElement(BaseColors.Error30, "Error30")
ColorElement(BaseColors.Error40, "Error40")
ColorElement(BaseColors.Error80, "Error80")
ColorElement(BaseColors.Error90, "Error90")

// Status / accent
ColorElement(BaseColors.Teal, "Teal")
ColorElement(BaseColors.Green, "Green")
ColorElement(BaseColors.GreenLight, "GreenLight")
ColorElement(BaseColors.GreenDark, "GreenDark")
ColorElement(BaseColors.GreenDarkText, "GreenDkTxt")
ColorElement(BaseColors.Orange, "Orange")
ColorElement(BaseColors.OrangeLight, "OrangeLight")
ColorElement(BaseColors.OrangeDark, "OrangeDark")
ColorElement(BaseColors.OrangeDarkText, "OrangeDkTxt")

// Black & White
ColorElement(BaseColors.White, "White")
ColorElement(BaseColors.Black, "Black")
}
}
}

@Composable
private fun ColorElement(color: Color, label: String, modifier: Modifier = Modifier) {
val textColor = if (color.luminance() > 0.5f) Color.Black else Color.White

Box(
modifier = modifier
.size(size = 100.dp)
.background(color = color),
contentAlignment = Alignment.Center,
) {
Text(
text = label,
color = textColor,
fontSize = 10.sp,
)
}
}
Loading
Loading