Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
e321ec1
Match Android color tokens to web mobile
andrewmuratov Sep 13, 2026
f191532
Add shared web-mobile parity components
andrewmuratov Sep 13, 2026
b49fa1c
Rebuild timetable to mirror web mobile layout
andrewmuratov Sep 13, 2026
9981879
Match Today screen to web mobile hierarchy
andrewmuratov Sep 13, 2026
bf4c06c
Rebuild gap plan to mirror web mobile presentation
andrewmuratov Sep 13, 2026
3ba6b59
Match Map screen to web mobile route layout
andrewmuratov Sep 13, 2026
20d6b43
Add compact More drawer matching web mobile
andrewmuratov Sep 13, 2026
e11b434
Match Android shell and More drawer to mobile web
andrewmuratov Sep 13, 2026
f28a39e
Fix Gap plan Compose imports and parity polish
andrewmuratov Sep 13, 2026
93a83a6
Add temporary parity branch build check
andrewmuratov Sep 13, 2026
1bc26d0
Run parity build on pull requests
andrewmuratov Sep 13, 2026
0f93463
Build with provisioned Gradle distribution
andrewmuratov Sep 13, 2026
0d1dfd4
Expose parity build diagnostics on PR
andrewmuratov Sep 13, 2026
d24963c
Capture Kotlin compiler diagnostics in parity build
andrewmuratov Sep 13, 2026
ccba35b
Opt in to Material3 route options sheet and polish controls
andrewmuratov Sep 13, 2026
3350656
Use Kotlin compile task for fast parity validation
andrewmuratov Sep 13, 2026
b7f6694
Match timetable cards, activity colors and gap rows to mobile web
andrewmuratov Sep 13, 2026
a2b4668
Bring native campus map interactions in line with mobile web
andrewmuratov Sep 13, 2026
4d8bb9b
Match Android account settings to mobile web
andrewmuratov Sep 13, 2026
f90b7d0
Add focused mobile route preferences sheet
andrewmuratov Sep 13, 2026
3b4516b
Match More account menu to mobile web
andrewmuratov Sep 13, 2026
4fd7945
Wire gap navigation and web-style settings
andrewmuratov Sep 13, 2026
21f47fe
Make native campus map interactive and smooth like mobile web
andrewmuratov Sep 13, 2026
d8dbc62
Fix rendered building feature queries
andrewmuratov Sep 13, 2026
64b1671
Match web map class and entrance markers
andrewmuratov Sep 13, 2026
7a83080
Fix MapLibre marker positioning
andrewmuratov Sep 13, 2026
9126794
Add canonical campus map API client
andrewmuratov Sep 13, 2026
ba22718
Use canonical web map data and paths
andrewmuratov Sep 13, 2026
4a5df93
Cache canonical campus map data
andrewmuratov Sep 13, 2026
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
46 changes: 46 additions & 0 deletions .github/workflows/parity-build.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
name: Android parity build

on:
push:
branches: [web-mobile-ui-parity]
pull_request:
branches: [main]
workflow_dispatch:

permissions:
contents: read
pull-requests: write

jobs:
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-java@v4
with:
distribution: temurin
java-version: '17'
- uses: gradle/actions/setup-gradle@v4
with:
gradle-version: current
- name: Compile Android app
shell: bash
run: |
set -o pipefail
gradle :app:compileDebugKotlin --stacktrace 2>&1 | tee build.log
- name: Report build diagnostics
if: ${{ failure() && github.event_name == 'pull_request' }}
env:
GH_TOKEN: ${{ github.token }}
PR_NUMBER: ${{ github.event.pull_request.number }}
shell: bash
run: |
{
echo '<!-- parity-build-diagnostics -->'
echo 'Android parity compile failed. Kotlin/Gradle diagnostics:'
echo
echo '```text'
grep -E '(^e:| error:|Unresolved reference|Cannot infer|No parameter with name|Type mismatch|Argument type mismatch|Compilation error)' build.log | tail -n 220 || true
echo '```'
} > diagnostics.md
gh pr comment "$PR_NUMBER" --body-file diagnostics.md
10 changes: 8 additions & 2 deletions app/src/main/java/ca/gapwise/android/core/designsystem/Theme.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,14 @@ import androidx.compose.material3.lightColorScheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color

// sRGB equivalents of the canonical Gapwise web OKLCH tokens in src/brand-blue.css.
// Exact sRGB equivalents of the canonical Gapwise web OKLCH tokens in src/brand-blue.css.
// Keep these values in lock-step with the web app: the native shell should be visually
// indistinguishable from the 400px mobile web surface, not merely "Android themed".
private val LightColors = lightColorScheme(
primary = Color(0xFF007AD4),
onPrimary = Color(0xFFFCFDFD),
tertiary = Color(0xFF007DD3), // --accent
onTertiary = Color(0xFFFCFDFD),
background = Color(0xFFF9FAFB),
onBackground = Color(0xFF101214),
surface = Color(0xFFFEFEFF),
Expand All @@ -24,8 +28,10 @@ private val LightColors = lightColorScheme(
)

private val DarkColors = darkColorScheme(
primary = Color(0xFF33A3F1),
primary = Color(0xFF009AF1),
onPrimary = Color(0xFFFCFDFD),
tertiary = Color(0xFF33A3F1), // --accent
onTertiary = Color(0xFFFCFDFD),
background = Color(0xFF040405),
onBackground = Color(0xFFE9EBEE),
surface = Color(0xFF07080A),
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
package ca.gapwise.android.core.designsystem

import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.BoxScope
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.ColumnScope
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.heightIn
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
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.text.font.FontWeight
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp

val WebSurfaceShape = RoundedCornerShape(12.dp)
val WebControlShape = RoundedCornerShape(9.dp)
val WebInnerShape = RoundedCornerShape(7.dp)

/** The plain bordered card used by the mobile web app's .surface rule. */
@Composable
fun WebSurface(
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(16.dp),
content: @Composable ColumnScope.() -> Unit,
) {
Surface(
modifier = modifier,
shape = WebSurfaceShape,
color = MaterialTheme.colorScheme.surface,
border = BorderStroke(1.dp, MaterialTheme.colorScheme.outlineVariant),
shadowElevation = 0.dp,
tonalElevation = 0.dp,
) {
Column(modifier = Modifier.padding(padding), content = content)
}
}

/** Web-style uppercase kicker/eyebrow. */
@Composable
fun WebEyebrow(
text: String,
modifier: Modifier = Modifier,
accent: Boolean = true,
) {
Text(
text = text.uppercase(),
modifier = modifier,
color = if (accent) MaterialTheme.colorScheme.tertiary else MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 10.5.sp,
lineHeight = 13.sp,
fontWeight = FontWeight.Bold,
letterSpacing = 1.15.sp,
)
}

/** A single cell from the web segmented controls. */
@Composable
fun WebSegmentButton(
label: String,
selected: Boolean,
onClick: () -> Unit,
modifier: Modifier = Modifier,
minHeight: Dp = 40.dp,
secondaryLabel: String? = null,
) {
val selectedBackground = MaterialTheme.colorScheme.tertiary.copy(alpha = 0.10f)
val selectedColor = MaterialTheme.colorScheme.tertiary
val idleColor = MaterialTheme.colorScheme.onSurfaceVariant

Box(
modifier = modifier
.heightIn(min = minHeight)
.background(
color = if (selected) selectedBackground else Color.Transparent,
shape = WebInnerShape,
)
.clickable(onClick = onClick)
.padding(horizontal = 6.dp, vertical = 7.dp),
contentAlignment = Alignment.Center,
) {
Column(horizontalAlignment = Alignment.CenterHorizontally) {
Text(
text = label,
color = if (selected) selectedColor else idleColor,
fontSize = 12.sp,
lineHeight = 14.sp,
fontWeight = FontWeight.SemiBold,
maxLines = 1,
)
if (secondaryLabel != null) {
Text(
text = secondaryLabel,
color = if (selected) selectedColor.copy(alpha = 0.78f) else idleColor.copy(alpha = 0.78f),
fontSize = 9.5.sp,
lineHeight = 11.sp,
fontWeight = FontWeight.Medium,
modifier = Modifier.padding(top = 2.dp),
maxLines = 1,
)
}
}
}
}

@Composable
fun WebSegmentContainer(
modifier: Modifier = Modifier,
content: @Composable BoxScope.() -> Unit,
) {
Box(
modifier = modifier
.fillMaxWidth()
.background(MaterialTheme.colorScheme.background, WebControlShape)
.padding(3.dp),
content = content,
)
}
Loading
Loading