brand: loading state, themes, manifest, art preview, pairing screen updates
This commit is contained in:
parent
fed91dbe46
commit
95cad84cb5
|
|
@ -75,3 +75,5 @@ ClaudeReport.md
|
||||||
docs/brand/visual-identity.md
|
docs/brand/visual-identity.md
|
||||||
docs/brand/asset-system.md
|
docs/brand/asset-system.md
|
||||||
docs/brand/visual-identity.md
|
docs/brand/visual-identity.md
|
||||||
|
docs/brand/asset-system.md
|
||||||
|
ClaudeBrandingReview.md
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,9 @@ Legend: ✅ on-brand / no art needed · ➕ add/þwire art (prompt below) ·
|
||||||
Priority order = the moments a user feels most. Each is self-contained after you paste the House Style block first.
|
Priority order = the moments a user feels most. Each is self-contained after you paste the House Style block first.
|
||||||
|
|
||||||
**A1 · Pairing success celebration** — *1:1, transparent bg.*
|
**A1 · Pairing success celebration** — *1:1, transparent bg.*
|
||||||
> Scene: two equal heart-halves (soft pink + soft lavender) sliding together into one whole heart at the center, with
|
> Scene: the Closer mark resolving into place — a soft-pink upper C-arc and a lavender lower sweep curving together to
|
||||||
> a gentle burst of small floating hearts and petals around it. Convey "you're connected now." Calm, joyful, no text.
|
> enclose a heart-shaped space with a small keyhole at its center — with a gentle burst of small floating hearts and
|
||||||
|
> petals around it. Convey "you're connected now." Calm, joyful, no text. (White keyhole if on a dark background.)
|
||||||
|
|
||||||
**A2 · Answer history empty state** — *1:1.*
|
**A2 · Answer history empty state** — *1:1.*
|
||||||
> Scene: a small soft journal/photo-album, slightly open, with two paired cards tucked inside and a few faint floating
|
> Scene: a small soft journal/photo-album, slightly open, with two paired cards tucked inside and a few faint floating
|
||||||
|
|
@ -151,7 +152,7 @@ Priority order = the moments a user feels most. Each is self-contained after you
|
||||||
> alarm imagery.
|
> alarm imagery.
|
||||||
|
|
||||||
**G-set · Notification + relationship glyphs** — *single-color vector, square, legible at 24 dp.* (One prompt each:)
|
**G-set · Notification + relationship glyphs** — *single-color vector, square, legible at 24 dp.* (One prompt each:)
|
||||||
> Simple single-color flat glyph in the Closer style, no text, no background: **heart-of-two-halves**, **paired sealed
|
> Simple single-color flat glyph in the Closer style, no text, no background: **the Closer C-heart-keyhole mark**, **paired sealed
|
||||||
> cards**, **daily card**, **sealed answer (card with small lock)**, **memory capsule**, **date-card with heart**,
|
> cards**, **daily card**, **sealed answer (card with small lock)**, **memory capsule**, **date-card with heart**,
|
||||||
> **quiet-hours moon**, **couple-premium (heart + small crown/spark, tasteful)**, **export-data**, **delete-account**.
|
> **quiet-hours moon**, **couple-premium (heart + small crown/spark, tasteful)**, **export-data**, **delete-account**.
|
||||||
> Export in lavender for in-app and as high-contrast monochrome for the platform notification glyph.
|
> Export in lavender for in-app and as high-contrast monochrome for the platform notification glyph.
|
||||||
|
|
|
||||||
|
|
@ -131,6 +131,7 @@ dependencies {
|
||||||
implementation(composeBom)
|
implementation(composeBom)
|
||||||
|
|
||||||
implementation("androidx.core:core-ktx:1.15.0")
|
implementation("androidx.core:core-ktx:1.15.0")
|
||||||
|
implementation("androidx.core:core-splashscreen:1.0.1")
|
||||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
|
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.7")
|
||||||
implementation("androidx.activity:activity-compose:1.9.3")
|
implementation("androidx.activity:activity-compose:1.9.3")
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -25,7 +25,7 @@
|
||||||
android:name=".MainActivity"
|
android:name=".MainActivity"
|
||||||
android:exported="true"
|
android:exported="true"
|
||||||
android:windowSoftInputMode="adjustResize"
|
android:windowSoftInputMode="adjustResize"
|
||||||
android:theme="@style/Theme.Closer">
|
android:theme="@style/Theme.Closer.Splash">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MAIN" />
|
<action android:name="android.intent.action.MAIN" />
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,7 @@ import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.activity.result.contract.ActivityResultContracts
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
|
|
@ -62,7 +63,21 @@ class MainActivity : AppCompatActivity() {
|
||||||
private val pendingDeepLink = mutableStateOf<String?>(null)
|
private val pendingDeepLink = mutableStateOf<String?>(null)
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
val splashScreen = installSplashScreen()
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
|
// Gentle scale-up + fade exit, handing off to the animated in-app logo loader.
|
||||||
|
splashScreen.setOnExitAnimationListener { provider ->
|
||||||
|
provider.iconView.animate()
|
||||||
|
.scaleX(1.15f)
|
||||||
|
.scaleY(1.15f)
|
||||||
|
.setDuration(300L)
|
||||||
|
.start()
|
||||||
|
provider.view.animate()
|
||||||
|
.alpha(0f)
|
||||||
|
.setDuration(300L)
|
||||||
|
.withEndAction { provider.remove() }
|
||||||
|
.start()
|
||||||
|
}
|
||||||
maybeRequestNotificationPermission()
|
maybeRequestNotificationPermission()
|
||||||
registerFcmToken()
|
registerFcmToken()
|
||||||
pendingDeepLink.value = deepLinkRouteFromIntent(intent)
|
pendingDeepLink.value = deepLinkRouteFromIntent(intent)
|
||||||
|
|
|
||||||
|
|
@ -4,29 +4,43 @@ import app.closer.R
|
||||||
import app.closer.ui.theme.closerCardColor
|
import app.closer.ui.theme.closerCardColor
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.compose.animation.core.FastOutSlowInEasing
|
import androidx.compose.animation.core.FastOutSlowInEasing
|
||||||
|
import androidx.compose.animation.core.LinearEasing
|
||||||
import androidx.compose.animation.core.RepeatMode
|
import androidx.compose.animation.core.RepeatMode
|
||||||
import androidx.compose.animation.core.animateFloat
|
import androidx.compose.animation.core.animateFloat
|
||||||
import androidx.compose.animation.core.infiniteRepeatable
|
import androidx.compose.animation.core.infiniteRepeatable
|
||||||
import androidx.compose.animation.core.rememberInfiniteTransition
|
import androidx.compose.animation.core.rememberInfiniteTransition
|
||||||
import androidx.compose.animation.core.tween
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Canvas
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
|
import androidx.compose.material3.LocalContentColor
|
||||||
import androidx.compose.material3.MaterialTheme
|
import androidx.compose.material3.MaterialTheme
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.geometry.CornerRadius
|
||||||
|
import androidx.compose.ui.geometry.Offset
|
||||||
|
import androidx.compose.ui.geometry.Size
|
||||||
|
import androidx.compose.ui.graphics.Brush
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ImageBitmap
|
||||||
|
import androidx.compose.ui.graphics.StrokeCap
|
||||||
|
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||||
|
import androidx.compose.ui.graphics.drawscope.clipRect
|
||||||
|
import androidx.compose.ui.graphics.drawscope.rotate
|
||||||
import androidx.compose.ui.graphics.graphicsLayer
|
import androidx.compose.ui.graphics.graphicsLayer
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.imageResource
|
||||||
import androidx.compose.ui.semantics.clearAndSetSemantics
|
import androidx.compose.ui.semantics.clearAndSetSemantics
|
||||||
import androidx.compose.ui.text.style.TextAlign
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
|
import androidx.compose.ui.unit.IntOffset
|
||||||
|
import androidx.compose.ui.unit.IntSize
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
|
@ -68,6 +82,15 @@ fun CloserHeartLoader(
|
||||||
CloserMarkLoader(modifier = modifier, size = size)
|
CloserMarkLoader(modifier = modifier, size = size)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Below this size we use a lightweight inline spinner instead of the full app-icon chip. */
|
||||||
|
private val ChipLoaderMinSize = 40.dp
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The brand loader. At prominent sizes it renders the **white-keyhole logo on an aubergine app-icon
|
||||||
|
* chip** with a soft fill that rises through the mark (the "charging the connection" motion). At small
|
||||||
|
* inline sizes (e.g. inside buttons) it falls back to a lightweight indeterminate arc tinted to the
|
||||||
|
* current content color, so it never shows a dark square on a colored button. Honors "remove animations".
|
||||||
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun CloserMarkLoader(
|
fun CloserMarkLoader(
|
||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
|
|
@ -81,21 +104,48 @@ fun CloserMarkLoader(
|
||||||
1f
|
1f
|
||||||
) == 0f
|
) == 0f
|
||||||
}
|
}
|
||||||
|
if (size >= ChipLoaderMinSize) {
|
||||||
|
CloserMarkChipLoader(modifier = modifier, size = size, reducedMotion = reducedMotion)
|
||||||
|
} else {
|
||||||
|
CloserInlineSpinner(
|
||||||
|
modifier = modifier,
|
||||||
|
size = size,
|
||||||
|
color = LocalContentColor.current,
|
||||||
|
reducedMotion = reducedMotion
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CloserMarkChipLoader(
|
||||||
|
modifier: Modifier,
|
||||||
|
size: Dp,
|
||||||
|
reducedMotion: Boolean
|
||||||
|
) {
|
||||||
val transition = rememberInfiniteTransition(label = "closerMarkLoader")
|
val transition = rememberInfiniteTransition(label = "closerMarkLoader")
|
||||||
val animatedPulse = transition.animateFloat(
|
val fill = if (reducedMotion) 1f else transition.animateFloat(
|
||||||
initialValue = 0.96f,
|
initialValue = 0f,
|
||||||
targetValue = 1.04f,
|
targetValue = 1f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(durationMillis = 1500, easing = FastOutSlowInEasing),
|
||||||
|
repeatMode = RepeatMode.Restart
|
||||||
|
),
|
||||||
|
label = "closerMarkFill"
|
||||||
|
).value
|
||||||
|
val pulse = if (reducedMotion) 1f else transition.animateFloat(
|
||||||
|
initialValue = 0.97f,
|
||||||
|
targetValue = 1.03f,
|
||||||
animationSpec = infiniteRepeatable(
|
animationSpec = infiniteRepeatable(
|
||||||
animation = tween(durationMillis = 900, easing = FastOutSlowInEasing),
|
animation = tween(durationMillis = 900, easing = FastOutSlowInEasing),
|
||||||
repeatMode = RepeatMode.Reverse
|
repeatMode = RepeatMode.Reverse
|
||||||
),
|
),
|
||||||
label = "closerMarkPulse"
|
label = "closerMarkPulse"
|
||||||
)
|
).value
|
||||||
val pulse = if (reducedMotion) 1f else animatedPulse.value
|
|
||||||
|
|
||||||
Image(
|
// White-keyhole mark (already safe-zone padded) drawn over the aubergine app-icon chip.
|
||||||
painter = painterResource(R.drawable.closer_mark_loader),
|
val mark = ImageBitmap.imageResource(R.drawable.closer_launcher_foreground)
|
||||||
contentDescription = null,
|
|
||||||
|
Canvas(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(size)
|
.size(size)
|
||||||
.graphicsLayer {
|
.graphicsLayer {
|
||||||
|
|
@ -103,5 +153,88 @@ fun CloserMarkLoader(
|
||||||
scaleY = pulse
|
scaleY = pulse
|
||||||
}
|
}
|
||||||
.clearAndSetSemantics {}
|
.clearAndSetSemantics {}
|
||||||
)
|
) {
|
||||||
|
val w = this.size.width
|
||||||
|
val h = this.size.height
|
||||||
|
val corner = CornerRadius(w * 0.22f, h * 0.22f)
|
||||||
|
// Aubergine gradient chip (matches ic_launcher_background palette).
|
||||||
|
drawRoundRect(
|
||||||
|
brush = Brush.linearGradient(
|
||||||
|
colors = listOf(Color(0xFF5A2F74), Color(0xFF3A1D4D), Color(0xFF24122F)),
|
||||||
|
start = Offset(0f, 0f),
|
||||||
|
end = Offset(w, h)
|
||||||
|
),
|
||||||
|
cornerRadius = corner
|
||||||
|
)
|
||||||
|
val src = IntSize(mark.width, mark.height)
|
||||||
|
val dst = IntSize(w.toInt(), h.toInt())
|
||||||
|
// Ghost of the full mark…
|
||||||
|
drawImage(
|
||||||
|
image = mark,
|
||||||
|
srcOffset = IntOffset.Zero,
|
||||||
|
srcSize = src,
|
||||||
|
dstOffset = IntOffset.Zero,
|
||||||
|
dstSize = dst,
|
||||||
|
alpha = 0.16f
|
||||||
|
)
|
||||||
|
// …then the fill rises bottom→top.
|
||||||
|
clipRect(top = h * (1f - fill)) {
|
||||||
|
drawImage(
|
||||||
|
image = mark,
|
||||||
|
srcOffset = IntOffset.Zero,
|
||||||
|
srcSize = src,
|
||||||
|
dstOffset = IntOffset.Zero,
|
||||||
|
dstSize = dst,
|
||||||
|
alpha = 1f
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Faint ring so the dark chip separates on dark backgrounds.
|
||||||
|
drawRoundRect(
|
||||||
|
color = Color.White.copy(alpha = 0.10f),
|
||||||
|
cornerRadius = corner,
|
||||||
|
style = Stroke(width = 1.dp.toPx())
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
private fun CloserInlineSpinner(
|
||||||
|
modifier: Modifier,
|
||||||
|
size: Dp,
|
||||||
|
color: Color,
|
||||||
|
reducedMotion: Boolean
|
||||||
|
) {
|
||||||
|
val angle = if (reducedMotion) 0f else rememberInfiniteTransition(label = "closerInlineSpinner")
|
||||||
|
.animateFloat(
|
||||||
|
initialValue = 0f,
|
||||||
|
targetValue = 360f,
|
||||||
|
animationSpec = infiniteRepeatable(
|
||||||
|
animation = tween(durationMillis = 900, easing = LinearEasing),
|
||||||
|
repeatMode = RepeatMode.Restart
|
||||||
|
),
|
||||||
|
label = "closerInlineAngle"
|
||||||
|
).value
|
||||||
|
|
||||||
|
Canvas(
|
||||||
|
modifier = modifier
|
||||||
|
.size(size)
|
||||||
|
.clearAndSetSemantics {}
|
||||||
|
) {
|
||||||
|
val stroke = this.size.minDimension * 0.12f
|
||||||
|
val inset = stroke / 2f
|
||||||
|
val d = this.size.minDimension - stroke
|
||||||
|
rotate(degrees = angle) {
|
||||||
|
drawArc(
|
||||||
|
brush = Brush.sweepGradient(
|
||||||
|
colors = listOf(color.copy(alpha = 0.12f), color)
|
||||||
|
),
|
||||||
|
startAngle = 0f,
|
||||||
|
sweepAngle = 270f,
|
||||||
|
useCenter = false,
|
||||||
|
topLeft = Offset(inset, inset),
|
||||||
|
size = Size(d, d),
|
||||||
|
style = Stroke(width = stroke, cap = StrokeCap.Round)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -26,6 +26,7 @@ import androidx.compose.ui.unit.dp
|
||||||
import app.closer.R
|
import app.closer.R
|
||||||
import app.closer.ui.components.CelebrationOverlay
|
import app.closer.ui.components.CelebrationOverlay
|
||||||
import app.closer.ui.components.CloserActionButton
|
import app.closer.ui.components.CloserActionButton
|
||||||
|
import app.closer.ui.components.CloserMarkLoader
|
||||||
import app.closer.ui.components.CloserButtonStyle
|
import app.closer.ui.components.CloserButtonStyle
|
||||||
import app.closer.ui.components.CloserCard
|
import app.closer.ui.components.CloserCard
|
||||||
import app.closer.ui.settings.SettingsMuted
|
import app.closer.ui.settings.SettingsMuted
|
||||||
|
|
@ -84,6 +85,16 @@ fun ArtPreviewScreen(onNavigate: (String) -> Unit = {}) {
|
||||||
Image(painterResource(R.drawable.particle_petal), null, Modifier.size(56.dp))
|
Image(painterResource(R.drawable.particle_petal), null, Modifier.size(56.dp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ArtCard("Loading mark (animated)") {
|
||||||
|
androidx.compose.foundation.layout.Row(
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(24.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
CloserMarkLoader(size = 76.dp) // chip + rising fill
|
||||||
|
CloserMarkLoader(size = 40.dp) // chip threshold
|
||||||
|
CloserMarkLoader(size = 24.dp) // inline arc
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
CloserActionButton(
|
CloserActionButton(
|
||||||
label = "Play the celebration",
|
label = "Play the celebration",
|
||||||
|
|
|
||||||
|
|
@ -203,19 +203,25 @@ fun PairingSuccessScreen(
|
||||||
.size(60.dp)
|
.size(60.dp)
|
||||||
.zIndex(2f)
|
.zIndex(2f)
|
||||||
.align(Alignment.Center)
|
.align(Alignment.Center)
|
||||||
|
.scale(pulse)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.background(MaterialTheme.colorScheme.background)
|
.background(MaterialTheme.colorScheme.background)
|
||||||
.padding(5.dp)
|
.padding(4.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape),
|
||||||
.background(Color(0xFFFFF8FC)),
|
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
|
// White-keyhole app-icon chip: aubergine gradient + the brand mark.
|
||||||
Image(
|
Image(
|
||||||
painter = painterResource(R.drawable.closer_mark_loader),
|
painter = painterResource(R.drawable.ic_launcher_background),
|
||||||
contentDescription = null,
|
contentDescription = null,
|
||||||
modifier = Modifier
|
contentScale = ContentScale.Crop,
|
||||||
.size(38.dp)
|
modifier = Modifier.matchParentSize()
|
||||||
.scale(pulse)
|
)
|
||||||
|
Image(
|
||||||
|
painter = painterResource(R.drawable.ic_launcher_foreground),
|
||||||
|
contentDescription = null,
|
||||||
|
contentScale = ContentScale.Fit,
|
||||||
|
modifier = Modifier.matchParentSize()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 23 KiB |
|
|
@ -0,0 +1,5 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<!-- Cold-launch splash background: brand aubergine, behind the white-keyhole icon. -->
|
||||||
|
<color name="splash_background">#FF24122F</color>
|
||||||
|
</resources>
|
||||||
|
|
@ -1,4 +1,13 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<style name="Theme.Closer" parent="Theme.AppCompat.Light.NoActionBar" />
|
<style name="Theme.Closer" parent="Theme.AppCompat.Light.NoActionBar" />
|
||||||
|
|
||||||
|
<!-- Cold-launch splash: aubergine background + the white-keyhole app icon, then hands off to
|
||||||
|
Theme.Closer (and the animated in-app logo loader). Splash attrs are from core-splashscreen
|
||||||
|
(no android: prefix) so they back-compat down to the minSdk. -->
|
||||||
|
<style name="Theme.Closer.Splash" parent="Theme.SplashScreen">
|
||||||
|
<item name="windowSplashScreenBackground">@color/splash_background</item>
|
||||||
|
<item name="windowSplashScreenAnimatedIcon">@mipmap/ic_launcher</item>
|
||||||
|
<item name="postSplashScreenTheme">@style/Theme.Closer</item>
|
||||||
|
</style>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
||||||
|
|
@ -97,18 +97,17 @@ masters (do not ship them directly); ship the exports and the wired Android rast
|
||||||
| `docs/brand/sources/closer-approved-icon-square.png` | Square 1024 master | iOS `AppIcon` 1024, store 1024 |
|
| `docs/brand/sources/closer-approved-icon-square.png` | Square 1024 master | iOS `AppIcon` 1024, store 1024 |
|
||||||
| `docs/brand/sources/closer-mark.svg` | Vector master of the mark | Source for all PNG exports |
|
| `docs/brand/sources/closer-mark.svg` | Vector master of the mark | Source for all PNG exports |
|
||||||
| `docs/brand/sources/closer-mark-transparent-keyhole-white.png` | Transparent mark, white keyhole (for dark aubergine/purple) | **Source of** `drawable-nodpi/closer_launcher_foreground.png` (adaptive fg over the aubergine bg) + the Auth/Onboarding logo tile (dark surface) |
|
| `docs/brand/sources/closer-mark-transparent-keyhole-white.png` | Transparent mark, white keyhole (for dark aubergine/purple) | **Source of** `drawable-nodpi/closer_launcher_foreground.png` (adaptive fg over the aubergine bg) + the Auth/Onboarding logo tile (dark surface) |
|
||||||
| `docs/brand/sources/closer-mark-transparent-keyhole-aubergine.png` | Transparent mark, dark keyhole (for light/blush surfaces) | **Source of** `drawable-nodpi/closer_mark_loader.png` (all loaders + the PairingSuccess hero badge on blush) |
|
| `docs/brand/sources/closer-mark-transparent-keyhole-aubergine.png` | Transparent mark, dark keyhole (for light/blush surfaces) | Logo on light/blush placements + web/docs exports (no in-app wiring today — in-app loaders now use the white-keyhole chip) |
|
||||||
| `docs/brand/sources/closer-mark-transparent-keyhole-black.png` | Mono/max-contrast mark | Print/legal, max-contrast light |
|
| `docs/brand/sources/closer-mark-transparent-keyhole-black.png` | Mono/max-contrast mark | Print/legal, max-contrast light |
|
||||||
| `docs/brand/exports/logo/transparent-keyhole-aubergine/closer-mark-{24..1024}.png` | Sized PNGs, dark keyhole | Web/email/social on light, docs, favicons |
|
| `docs/brand/exports/logo/transparent-keyhole-aubergine/closer-mark-{24..1024}.png` | Sized PNGs, dark keyhole | Web/email/social on light, docs, favicons |
|
||||||
| `docs/brand/exports/logo/transparent-keyhole-white/closer-mark-{24..1024}.png` | Sized PNGs, white keyhole | Same, on dark surfaces |
|
| `docs/brand/exports/logo/transparent-keyhole-white/closer-mark-{24..1024}.png` | Sized PNGs, white keyhole | Same, on dark surfaces |
|
||||||
| `docs/brand/exports/logo/transparent-keyhole-black/closer-mark-{24..1024}.png` | Sized PNGs, mono | One-color/print/footer |
|
| `docs/brand/exports/logo/transparent-keyhole-black/closer-mark-{24..1024}.png` | Sized PNGs, mono | One-color/print/footer |
|
||||||
| `docs/brand/exports/logo/social/closer-mark-social-{1024,2048}.png` | Square social avatars / OG | Social profile + open-graph square |
|
| `docs/brand/exports/logo/social/closer-mark-social-{1024,2048}.png` | Square social avatars / OG | Social profile + open-graph square |
|
||||||
| `docs/brand/exports/logo/app-icon/closer-app-icon-512.png` | Play listing icon | `docs/store/app-icon-512.png` (Play Console) |
|
| `docs/brand/exports/logo/app-icon/closer-app-icon-512.png` | Play listing icon | `docs/store/app-icon-512.png` (Play Console) |
|
||||||
| `app/src/main/res/drawable-nodpi/closer_launcher_foreground.png` | Adaptive **foreground** (white-keyhole mark, safe-zone) | `drawable/ic_launcher_foreground.xml` → `mipmap-anydpi-*/ic_launcher*.xml`; also the Auth/Onboarding logo tile |
|
| `app/src/main/res/drawable-nodpi/closer_launcher_foreground.png` | Adaptive **foreground** (white-keyhole mark, safe-zone) | `drawable/ic_launcher_foreground.xml` → `mipmap-anydpi-*/ic_launcher*.xml`; Auth/Onboarding logo tile; **`CloserMarkLoader` chip + `PairingSuccess` hero** (white-keyhole logo on the aubergine app-icon chip) |
|
||||||
| `app/src/main/res/drawable/ic_launcher_background.xml` | Adaptive **background** (aubergine gradient) | `mipmap-anydpi-*/ic_launcher*.xml` |
|
| `app/src/main/res/drawable/ic_launcher_background.xml` | Adaptive **background** (aubergine gradient) | `mipmap-anydpi-*/ic_launcher*.xml` |
|
||||||
| `app/src/main/res/drawable-nodpi/closer_launcher_monochrome.png` | Themed-icon **monochrome** (grayscale+alpha) | `drawable/ic_launcher_monochrome.xml` → `mipmap-anydpi-v33/*` |
|
| `app/src/main/res/drawable-nodpi/closer_launcher_monochrome.png` | Themed-icon **monochrome** (grayscale+alpha) | `drawable/ic_launcher_monochrome.xml` → `mipmap-anydpi-v33/*` |
|
||||||
| `app/src/main/res/drawable-nodpi/ic_notification_closer.png` | Notification small icon (white+alpha silhouette) | `NotificationHelper`, `PartnerNotificationManager` `setSmallIcon` |
|
| `app/src/main/res/drawable-nodpi/ic_notification_closer.png` | Notification small icon (white+alpha silhouette) | `NotificationHelper`, `PartnerNotificationManager` `setSmallIcon` |
|
||||||
| `app/src/main/res/drawable-nodpi/closer_mark_loader.png` | Full-color transparent mark | `CloserMarkLoader` (all loaders), `PairingSuccessScreen` hero, `AuthLogoMark`/Onboarding tile |
|
|
||||||
|
|
||||||
Note: the section-1 rows `closer-mark-on-dark.svg` / `closer-mark-on-light.svg` and the lockup/favicon
|
Note: the section-1 rows `closer-mark-on-dark.svg` / `closer-mark-on-light.svg` and the lockup/favicon
|
||||||
rows are still **to-build** — the on-disk equivalents today are the `transparent-keyhole-white` (dark)
|
rows are still **to-build** — the on-disk equivalents today are the `transparent-keyhole-white` (dark)
|
||||||
|
|
|
||||||
|
|
@ -42,8 +42,11 @@ Use the keyhole color to support contrast while keeping the official icon visual
|
||||||
the black-keyhole variant only when maximum contrast is needed.
|
the black-keyhole variant only when maximum contrast is needed.
|
||||||
- **Notification small icon:** use the single-color platform glyph; do not use the full-color
|
- **Notification small icon:** use the single-color platform glyph; do not use the full-color
|
||||||
launcher art.
|
launcher art.
|
||||||
- **Loading mark:** use the aubergine-keyhole variant on light/card surfaces so it stays calmer than
|
- **Loading mark:** the in-app loader is the **white-keyhole logo on an aubergine app-icon chip** with a
|
||||||
the launcher icon.
|
soft fill that rises through the mark (a "charging" loop). Below ~40dp (e.g. inside buttons) it falls
|
||||||
|
back to a lightweight indeterminate arc tinted to the surrounding content color.
|
||||||
|
- **Cold-launch splash:** aubergine background + the white-keyhole app icon (static, system-masked), with
|
||||||
|
a scale/fade exit that hands off to the in-app loading mark above.
|
||||||
|
|
||||||
## Core colors
|
## Core colors
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue