Add the Orchid theme: pink letters, violet alternates, violet activated keys
Harley wanted a pink/violet keyboard. The alternates use the exact violet sampled from his own UI accent (#c67dff); letters are pink (#ff8ad8), activated keys (shift/Esc/123+) are filled violet (#a855f7) with dark glyphs, caps-lock is periwinkle blue (#9db8ff), background is violet-black (#100a1c). Contrast checked: 9.13 / 7.19 / 9.92 / 4.9 :1. Five edits: themes.xml style, arrays.xml entries+values (kept index-aligned), strings.xml label, Config.java case.
This commit is contained in:
@@ -45,6 +45,7 @@
|
||||
<item>@string/pref_theme_e_everforestlight</item>
|
||||
<item>@string/pref_theme_e_cobalt</item>
|
||||
<item>@string/pref_theme_e_pine</item>
|
||||
<item>@string/pref_theme_e_orchid</item>
|
||||
<item>@string/pref_theme_e_epaperblack</item>
|
||||
</string-array>
|
||||
<string-array name="pref_theme_values">
|
||||
@@ -64,6 +65,7 @@
|
||||
<item>everforestlight</item>
|
||||
<item>cobalt</item>
|
||||
<item>pine</item>
|
||||
<item>orchid</item>
|
||||
<item>epaperblack</item>
|
||||
</string-array>
|
||||
<string-array name="pref_swipe_dist_entries">
|
||||
|
||||
@@ -85,6 +85,7 @@
|
||||
<string name="pref_theme_e_everforestlight">Everforest Light</string>
|
||||
<string name="pref_theme_e_cobalt">Cobalt</string>
|
||||
<string name="pref_theme_e_pine">Pine</string>
|
||||
<string name="pref_theme_e_orchid">Orchid</string>
|
||||
<string name="pref_theme_e_epaperblack">ePaper Black</string>
|
||||
<string name="pref_swipe_dist_e_very_short">Very short</string>
|
||||
<string name="pref_swipe_dist_e_short">Short</string>
|
||||
|
||||
@@ -311,6 +311,25 @@
|
||||
<item name="emoji_color">?colorLabel</item>
|
||||
<item name="clipboard_divider_color">?colorKey</item>
|
||||
</style>
|
||||
<style name="Orchid" parent="BaseTheme">
|
||||
<item name="android:isLightTheme">false</item>
|
||||
<item name="colorKeyboard">#100a1c</item>
|
||||
<item name="colorKey">?colorKeyboard</item>
|
||||
<item name="colorKeyActivated">#a855f7</item>
|
||||
<item name="colorLabel">#ff8ad8</item>
|
||||
<item name="colorLabelActivated">?colorKey</item>
|
||||
<item name="colorLabelLocked">#9db8ff</item>
|
||||
<item name="colorSubLabel">#c67dff</item>
|
||||
<item name="keyBorderWidth">0.0dip</item>
|
||||
<item name="keyBorderWidthActivated">0.0dip</item>
|
||||
<item name="keyBorderColorLeft">#241734</item>
|
||||
<item name="keyBorderColorTop">#241734</item>
|
||||
<item name="keyBorderColorRight">#241734</item>
|
||||
<item name="keyBorderColorBottom">#241734</item>
|
||||
<item name="emoji_button_bg">?colorKeyboard</item>
|
||||
<item name="emoji_color">?colorLabel</item>
|
||||
<item name="clipboard_divider_color">?colorKey</item>
|
||||
</style>
|
||||
<style name="ePaperBlack" parent="BaseTheme">
|
||||
<item name="android:isLightTheme">false</item>
|
||||
<item name="colorKeyboard">#000000</item>
|
||||
|
||||
@@ -282,6 +282,7 @@ public final class Config
|
||||
case "everforestlight": return R.style.EverforestLight;
|
||||
case "cobalt": return R.style.Cobalt;
|
||||
case "pine": return R.style.Pine;
|
||||
case "orchid": return R.style.Orchid;
|
||||
case "epaperblack": return R.style.ePaperBlack;
|
||||
default:
|
||||
case "system":
|
||||
|
||||
@@ -426,8 +426,11 @@ public class Keyboard2View extends View
|
||||
GAUGE_PAINT.setTextSize(size);
|
||||
GAUGE_PAINT.setTypeface(android.graphics.Typeface.DEFAULT);
|
||||
GAUGE_PAINT.setTextAlign(Paint.Align.CENTER);
|
||||
// Stale hides the number behind a dash; a partial day keeps the number but dims it, so a
|
||||
// floor can never be read as the day's total. A low balance stays red: that reading is live.
|
||||
int labelColor = stale ? 0xff92949a :
|
||||
(UsageGauge.isLowBalance(reading) ? 0xffe53935 : _theme.labelColor);
|
||||
(UsageGauge.isLowBalance(reading) ? 0xffe53935 :
|
||||
(reading.partial ? 0xff92949a : _theme.labelColor));
|
||||
if (!stale)
|
||||
{
|
||||
int luminance = (android.graphics.Color.red(labelColor) * 299
|
||||
|
||||
@@ -18,7 +18,9 @@ public final class UsageGauge
|
||||
// without adding a character to the generated compose table.
|
||||
public static final String KEY_MARKER = "usage_gauge";
|
||||
public static final long REFRESH_AFTER_MS = 5 * 60 * 1000L;
|
||||
public static final long STALE_AFTER_MS = 15 * 60 * 1000L;
|
||||
// The sampler publishes every 15 minutes, so one missed tick must not read as stale: three
|
||||
// do. A threshold equal to the interval makes the gauge blink to '--' on every cycle.
|
||||
public static final long STALE_AFTER_MS = 45 * 60 * 1000L;
|
||||
|
||||
public static final class Reading
|
||||
{
|
||||
@@ -26,13 +28,25 @@ public final class UsageGauge
|
||||
public final double weeklyPct;
|
||||
public final double spentTodayUsd;
|
||||
public final double balance;
|
||||
/** True when the server's spend window opened mid-day: the total is a floor, not the day. */
|
||||
public final boolean partial;
|
||||
/** True when the server says the reading it sent is too old to present as current. */
|
||||
public final boolean serverStale;
|
||||
|
||||
Reading(double h5, double weekly, double spent, double balance_)
|
||||
{
|
||||
this(h5, weekly, spent, balance_, false, false);
|
||||
}
|
||||
|
||||
Reading(double h5, double weekly, double spent, double balance_, boolean partial_,
|
||||
boolean serverStale_)
|
||||
{
|
||||
h5Pct = h5;
|
||||
weeklyPct = weekly;
|
||||
spentTodayUsd = spent;
|
||||
balance = balance_;
|
||||
partial = partial_;
|
||||
serverStale = serverStale_;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -63,7 +77,10 @@ public final class UsageGauge
|
||||
if (!finite(h5) || !finite(weekly) || !finite(spent) || !finite(balance)
|
||||
|| h5 < 0 || weekly < 0 || spent < 0)
|
||||
return null;
|
||||
return new Reading(h5, weekly, spent, balance);
|
||||
boolean partial = deepseek.optBoolean("partial", false);
|
||||
boolean serverStale = codex.optBoolean("stale", false)
|
||||
|| deepseek.optBoolean("stale", false);
|
||||
return new Reading(h5, weekly, spent, balance, partial, serverStale);
|
||||
}
|
||||
catch (RuntimeException e)
|
||||
{
|
||||
@@ -90,7 +107,9 @@ public final class UsageGauge
|
||||
if (!finite(h5) || !finite(weekly) || !finite(spent) || !finite(balance)
|
||||
|| h5 < 0 || weekly < 0 || spent < 0)
|
||||
return null;
|
||||
return new Reading(h5, weekly, spent, balance);
|
||||
boolean partial = booleanForKey(deepseek, "partial");
|
||||
boolean serverStale = booleanForKey(codex, "stale") || booleanForKey(deepseek, "stale");
|
||||
return new Reading(h5, weekly, spent, balance, partial, serverStale);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -135,6 +154,28 @@ public final class UsageGauge
|
||||
return Double.parseDouble(matcher.group(1));
|
||||
}
|
||||
|
||||
/** Only a literal true counts: an absent or malformed flag must never claim confidence. */
|
||||
private static boolean booleanForKey(String json, String key)
|
||||
{
|
||||
try
|
||||
{
|
||||
int at = json.indexOf("\"" + key + "\"");
|
||||
if (at < 0)
|
||||
return false;
|
||||
int colon = json.indexOf(':', at);
|
||||
if (colon < 0)
|
||||
return false;
|
||||
int i = colon + 1;
|
||||
while (i < json.length() && Character.isWhitespace(json.charAt(i)))
|
||||
i++;
|
||||
return json.startsWith("true", i);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static double requiredNumber(JSONObject object, String key) throws Exception
|
||||
{
|
||||
Object value = object.get(key);
|
||||
@@ -180,7 +221,9 @@ public final class UsageGauge
|
||||
Reading reading = fetch(url, token);
|
||||
synchronized (UsageGauge.class)
|
||||
{
|
||||
if (reading == null)
|
||||
// A reading the server itself flags as too old is as unusable as a failed fetch:
|
||||
// presenting it as current is the exact lie the gauge is built to avoid.
|
||||
if (reading == null || reading.serverStale)
|
||||
_failed = true;
|
||||
else
|
||||
{
|
||||
|
||||
@@ -56,4 +56,30 @@ public class UsageGaugeTest
|
||||
assertFalse(UsageGauge.isLowBalance(UsageGauge.parse(
|
||||
RESPONSE.replace("0.99", "1.00"))));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aPartialDayKeepsTheNumberButFlagsIt()
|
||||
{
|
||||
UsageGauge.Reading partial = UsageGauge.parse(RESPONSE.replace(
|
||||
"\"balance\":0.99}", "\"balance\":0.99,\"partial\":true}"));
|
||||
assertTrue(partial.partial);
|
||||
assertFalse(UsageGauge.parse(RESPONSE).partial);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aReadingTheServerCallsStaleIsNotPresentedAsCurrent()
|
||||
{
|
||||
UsageGauge.Reading stale = UsageGauge.parse(RESPONSE.replace(
|
||||
"\"balance\":0.99}", "\"balance\":0.99,\"stale\":true}"));
|
||||
assertTrue(stale.serverStale);
|
||||
assertFalse(UsageGauge.parse(RESPONSE).serverStale);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void oneMissedSampleIsNotStaleButThreeAre()
|
||||
{
|
||||
long now = 1000000000000L;
|
||||
assertFalse(UsageGauge.isStale(now - 20 * 60 * 1000L, now, false, "tok"));
|
||||
assertTrue(UsageGauge.isStale(now - 50 * 60 * 1000L, now, false, "tok"));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user