diff --git a/CHANGELOG.md b/CHANGELOG.md
index 315814ec..64f386bb 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Changelog
+## Unreleased
+
+- Adds a Portable Chrome Nova theme matching the Polaris dim Moonlight-grey early-2000s silver palette, including settings/theme-picker entries and restrained Compose library surfaces.
+
## 1.1.3 - 2026-06-12
Nova 1.1.3 is the public APK publishing patch for the 1.1.2 confidence build. It preserves the same handheld Library, Command Center, NovaHUD, Insert-key, stale-session recovery, input, crash, and dependency hardening work from 1.1.2, while bumping the Android package version so GitHub Releases / Obtainium installs can update cleanly.
diff --git a/app/src/main/java/com/papi/nova/ui/NovaThemeManager.kt b/app/src/main/java/com/papi/nova/ui/NovaThemeManager.kt
index aea68ab9..8c5f22e6 100644
--- a/app/src/main/java/com/papi/nova/ui/NovaThemeManager.kt
+++ b/app/src/main/java/com/papi/nova/ui/NovaThemeManager.kt
@@ -19,6 +19,7 @@ object NovaThemeManager {
const val THEME_POLARIS = "polaris"
const val THEME_OLED = "oled"
const val THEME_MIAMI = "miami"
+ const val THEME_PORTABLE_CHROME = "portable_chrome"
const val THEME_HIGH_CONTRAST = "high_contrast"
const val THEME_MATERIAL_YOU = "material_you"
@@ -34,6 +35,8 @@ object NovaThemeManager {
theme == THEME_OLED -> activity.setTheme(R.style.AppTheme_OLED)
theme == THEME_MIAMI && isSettings -> activity.setTheme(R.style.SettingsTheme_Miami)
theme == THEME_MIAMI -> activity.setTheme(R.style.AppTheme_Miami)
+ theme == THEME_PORTABLE_CHROME && isSettings -> activity.setTheme(R.style.SettingsTheme_PortableChrome)
+ theme == THEME_PORTABLE_CHROME -> activity.setTheme(R.style.AppTheme_PortableChrome)
theme == THEME_HIGH_CONTRAST && isSettings -> activity.setTheme(R.style.SettingsTheme_HighContrast)
theme == THEME_HIGH_CONTRAST -> activity.setTheme(R.style.AppTheme_HighContrast)
theme == THEME_MATERIAL_YOU && isSettings -> activity.setTheme(R.style.SettingsTheme_MaterialYou)
@@ -70,7 +73,7 @@ object NovaThemeManager {
private fun normalizeTheme(theme: String?): String {
return when (theme) {
- THEME_POLARIS, THEME_OLED, THEME_MIAMI, THEME_HIGH_CONTRAST, THEME_MATERIAL_YOU -> theme
+ THEME_POLARIS, THEME_OLED, THEME_MIAMI, THEME_PORTABLE_CHROME, THEME_HIGH_CONTRAST, THEME_MATERIAL_YOU -> theme
else -> THEME_POLARIS
}
}
@@ -86,6 +89,7 @@ object NovaThemeManager {
fun isOled(context: Context): Boolean = getTheme(context) == THEME_OLED
fun isMiami(context: Context): Boolean = getTheme(context) == THEME_MIAMI
+ fun isPortableChrome(context: Context): Boolean = getTheme(context) == THEME_PORTABLE_CHROME
fun isHighContrast(context: Context): Boolean = getTheme(context) == THEME_HIGH_CONTRAST
fun isMaterialYou(context: Context): Boolean = getTheme(context) == THEME_MATERIAL_YOU
@@ -93,7 +97,8 @@ object NovaThemeManager {
val next = when (getTheme(context)) {
THEME_POLARIS -> THEME_OLED
THEME_OLED -> THEME_MIAMI
- THEME_MIAMI -> THEME_HIGH_CONTRAST
+ THEME_MIAMI -> THEME_PORTABLE_CHROME
+ THEME_PORTABLE_CHROME -> THEME_HIGH_CONTRAST
THEME_HIGH_CONTRAST -> if (isMaterialYouAvailable()) THEME_MATERIAL_YOU else THEME_POLARIS
THEME_MATERIAL_YOU -> THEME_POLARIS
else -> THEME_POLARIS
@@ -106,6 +111,7 @@ object NovaThemeManager {
return when (theme) {
THEME_OLED -> context.getString(R.string.nova_theme_oled_label)
THEME_MIAMI -> context.getString(R.string.nova_theme_miami_label)
+ THEME_PORTABLE_CHROME -> context.getString(R.string.nova_theme_portable_chrome_label)
THEME_HIGH_CONTRAST -> context.getString(R.string.nova_theme_high_contrast_label)
THEME_MATERIAL_YOU -> context.getString(R.string.nova_theme_material_you_label)
else -> context.getString(R.string.nova_theme_polaris_label)
@@ -130,6 +136,7 @@ object NovaThemeManager {
return when {
isOled(context) -> Color.BLACK
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_bg_window)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_bg_window)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_bg_window)
isMaterialYou(context) && isMaterialYouAvailable() ->
resolveThemeColor(context, android.R.attr.colorBackground, ContextCompat.getColor(context, R.color.nova_bg_window))
@@ -160,6 +167,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_bg_card)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_bg_card)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_bg_card)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_bg_card)
isMaterialYou(context) && isMaterialYouAvailable() ->
resolveThemeColor(context, com.google.android.material.R.attr.colorSurface, ContextCompat.getColor(context, R.color.nova_bg_card))
@@ -172,6 +180,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_dialog_bg)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_dialog_bg)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_dialog_bg)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_dialog_bg)
isMaterialYou(context) && isMaterialYouAvailable() ->
resolveThemeColor(context, com.google.android.material.R.attr.colorSurface, ContextCompat.getColor(context, R.color.nova_dialog_bg))
@@ -196,6 +205,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_accent)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_accent)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_accent)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_accent)
else -> ContextCompat.getColor(context, R.color.nova_accent)
}
@@ -206,6 +216,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_accent_surface)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_accent_surface)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_accent_surface)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_accent_surface)
else -> ContextCompat.getColor(context, R.color.nova_accent_surface)
}
@@ -216,6 +227,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_divider)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_divider)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_divider)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_divider)
isMaterialYou(context) && isMaterialYouAvailable() ->
resolveThemeColor(context, com.google.android.material.R.attr.colorOutline, ContextCompat.getColor(context, R.color.nova_divider))
@@ -228,6 +240,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_text_primary)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_text_primary)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_text_primary)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_text_primary)
isMaterialYou(context) && isMaterialYouAvailable() ->
resolveThemeColor(context, android.R.attr.textColorPrimary, ContextCompat.getColor(context, R.color.nova_text_primary))
@@ -240,6 +253,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_text_secondary)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_text_secondary)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_text_secondary)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_text_secondary)
isMaterialYou(context) && isMaterialYouAvailable() ->
resolveThemeColor(context, android.R.attr.textColorSecondary, ContextCompat.getColor(context, R.color.nova_text_secondary))
@@ -252,6 +266,7 @@ object NovaThemeManager {
return when {
isOled(context) -> ContextCompat.getColor(context, R.color.nova_oled_text_muted)
isMiami(context) -> ContextCompat.getColor(context, R.color.nova_miami_text_muted)
+ isPortableChrome(context) -> ContextCompat.getColor(context, R.color.nova_portable_text_muted)
isHighContrast(context) -> ContextCompat.getColor(context, R.color.nova_hc_text_muted)
isMaterialYou(context) && isMaterialYouAvailable() ->
resolveThemeColor(context, android.R.attr.textColorSecondary, ContextCompat.getColor(context, R.color.nova_text_muted))
diff --git a/app/src/main/java/com/papi/nova/ui/compose/NovaComposeTheme.kt b/app/src/main/java/com/papi/nova/ui/compose/NovaComposeTheme.kt
index f653677d..4df508fb 100644
--- a/app/src/main/java/com/papi/nova/ui/compose/NovaComposeTheme.kt
+++ b/app/src/main/java/com/papi/nova/ui/compose/NovaComposeTheme.kt
@@ -77,12 +77,14 @@ val LocalNovaLibrarySurfaces = staticCompositionLocalOf {
fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
val isOled = theme == NovaThemeManager.THEME_OLED
val isMiami = theme == NovaThemeManager.THEME_MIAMI
+ val isPortableChrome = theme == NovaThemeManager.THEME_PORTABLE_CHROME
val isHighContrast = theme == NovaThemeManager.THEME_HIGH_CONTRAST
val isMaterialYou = theme == NovaThemeManager.THEME_MATERIAL_YOU
return NovaLibrarySurfaces(
backgroundScrim = when {
isOled -> Color.Transparent
isMiami -> window.copy(alpha = 0.60f)
+ isPortableChrome -> Color.Black.copy(alpha = 0.20f)
isHighContrast -> Color.Black.copy(alpha = 0.72f)
isMaterialYou -> window.copy(alpha = 0.28f)
else -> window.copy(alpha = 0.56f)
@@ -90,6 +92,7 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
panel = when {
isOled -> dialog.copy(alpha = 0.88f)
isMiami -> dialog.copy(alpha = 0.82f)
+ isPortableChrome -> dialog.copy(alpha = 0.90f)
isHighContrast -> dialog.copy(alpha = 0.96f)
isMaterialYou -> card.copy(alpha = 0.76f)
else -> dialog.copy(alpha = 0.64f)
@@ -97,6 +100,7 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
panelBorder = when {
isOled -> divider.copy(alpha = 0.78f)
isMiami -> accent.copy(alpha = 0.18f)
+ isPortableChrome -> divider.copy(alpha = 0.62f)
isHighContrast -> divider.copy(alpha = 0.92f)
isMaterialYou -> divider.copy(alpha = 0.46f)
else -> divider.copy(alpha = 0.44f)
@@ -104,6 +108,7 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
tile = when {
isOled -> card.copy(alpha = 0.90f)
isMiami -> card.copy(alpha = 0.82f)
+ isPortableChrome -> card.copy(alpha = 0.88f)
isHighContrast -> card.copy(alpha = 0.98f)
isMaterialYou -> card.copy(alpha = 0.78f)
else -> card.copy(alpha = 0.74f)
@@ -111,12 +116,14 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
tileBorder = when {
isOled -> divider.copy(alpha = 0.78f)
isMiami -> divider.copy(alpha = 0.58f)
+ isPortableChrome -> divider.copy(alpha = 0.62f)
isHighContrast -> divider.copy(alpha = 0.90f)
else -> divider.copy(alpha = 0.50f)
},
control = when {
isOled -> card.copy(alpha = 0.78f)
isMiami -> card.copy(alpha = 0.76f)
+ isPortableChrome -> card.copy(alpha = 0.84f)
isHighContrast -> card.copy(alpha = 1f)
isMaterialYou -> card.copy(alpha = 0.70f)
else -> card.copy(alpha = 0.72f)
@@ -124,6 +131,7 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
selectedControl = accent.copy(alpha = when {
isHighContrast -> 0.34f
isMiami -> 0.22f
+ isPortableChrome -> 0.20f
isOled -> 0.22f
else -> 0.18f
}),
@@ -131,12 +139,14 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
focusHalo = accent.copy(alpha = when {
isHighContrast -> 0.36f
isMiami -> 0.28f
+ isPortableChrome -> 0.20f
isOled -> 0.24f
else -> 0.18f
}),
mediaPlaceholder = when {
isOled -> Color(0xFF08080C)
isMiami -> Color(0xFF2C1734)
+ isPortableChrome -> Color(0xFFB8C1CC)
isHighContrast -> Color(0xFF111827)
isMaterialYou -> card.copy(alpha = 1f)
else -> divider.copy(alpha = 1f)
@@ -152,12 +162,14 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
focusedArtworkAlpha = when {
isOled -> 0.10f
isMiami -> 0.26f
+ isPortableChrome -> 0.16f
isMaterialYou -> 0.18f
else -> 0.24f
},
focusedArtworkScrim = Color.Black.copy(alpha = when {
isOled -> 0.82f
isMiami -> 0.76f
+ isPortableChrome -> 0.64f
else -> 0.72f
}),
particlesEnabled = !isOled,
@@ -165,6 +177,7 @@ fun NovaComposeColors.librarySurfaces(theme: String): NovaLibrarySurfaces {
isOled -> 0f
isHighContrast -> 0.28f
isMiami -> 0.68f
+ isPortableChrome -> 0.32f
isMaterialYou -> 0.42f
else -> 1f
}
@@ -190,7 +203,11 @@ fun NovaComposeTheme(content: @Composable () -> Unit) {
onAccent = Color(
ContextCompat.getColor(
context,
- if (theme == NovaThemeManager.THEME_MIAMI) R.color.nova_miami_void else R.color.nova_ice
+ when (theme) {
+ NovaThemeManager.THEME_MIAMI -> R.color.nova_miami_void
+ NovaThemeManager.THEME_PORTABLE_CHROME -> R.color.nova_portable_on_accent
+ else -> R.color.nova_ice
+ }
)
)
)
diff --git a/app/src/main/res/values-v31/styles.xml b/app/src/main/res/values-v31/styles.xml
index 1dc362db..e8374403 100644
--- a/app/src/main/res/values-v31/styles.xml
+++ b/app/src/main/res/values-v31/styles.xml
@@ -27,4 +27,10 @@
- @color/nova_miami_bg_window
- @color/nova_miami_bg_window
+
diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml
index e30cab3a..fefbe78e 100644
--- a/app/src/main/res/values/arrays.xml
+++ b/app/src/main/res/values/arrays.xml
@@ -4,6 +4,7 @@
- Polaris Aurora
- Console OLED
- Miami Nebula
+ - Portable Chrome
- High Contrast
- Material You
@@ -11,6 +12,7 @@
- polaris
- oled
- miami
+ - portable_chrome
- high_contrast
- material_you
diff --git a/app/src/main/res/values/colors_nova.xml b/app/src/main/res/values/colors_nova.xml
index 1903b218..9040e8e5 100644
--- a/app/src/main/res/values/colors_nova.xml
+++ b/app/src/main/res/values/colors_nova.xml
@@ -55,6 +55,32 @@
#73FF5CAB
#22FF5CAB
+
+ #FF98A6B5
+ #FFB8C1CC
+ #FFA7B2BE
+ #FF7F8C9A
+ #FFD6DDE5
+ #FFEAF0F5
+ #FFB8C1CC
+ #FFB8C1CC
+ #E6D6DDE5
+ #FFE0E6EC
+ #FFC8D1DC
+ #FFD6DDE5
+ #FFC7D1DD
+ #FF7F8C9A
+ #FF25313D
+ #FF4F5D6B
+ #FF667584
+ #FF557395
+ #22557395
+ #33557395
+ #22557395
+ #FF315B45
+ #FF315B45
+ #FF557395
+ #FFFFFFFF
#FF05070c
#F00f172a
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 79f39878..06faa290 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -50,6 +50,7 @@
Polaris Aurora
Console OLED
Miami Nebula
+ Portable Chrome
High Contrast
Material You
Open Nova Library
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index 84a206e9..0873fd63 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -55,6 +55,20 @@
- @color/nova_miami_text_primary
+
+
+