MiniABS 1.5: Audiobookshelf icon and purple theme

- Launcher icon from the Audiobookshelf icon pack (art/icon_master_512.png),
  including an xhdpi size
- Notification icon cut out of the same artwork; player icons redrawn in the
  icon's lilac (tools/make_icons.py)
- Purple colour scheme taken from the icon: gradient accent buttons, lilac
  text accents (accent_light), purple-tinted backgrounds
- README: where the icon and colours come from

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
xockoandClaude Opus 5.5 committed 2026-10-15 10:07:49 +02:00
1 parent 0fea07f1b2
commit 2c23ec9131
36 files changed
+70 -65

No files matched your search

+2 -2
View File
@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.miniabs"
android:versionCode="5"
android:versionName="1.4"
android:versionCode="6"
android:versionName="1.5"
android:installLocation="auto">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
+1 -1
View File
@@ -1,6 +1,6 @@
# MiniABS
A tiny Audiobookshelf **podcast** client for the Samsung Galaxy Mini (GT-S5570) and other Android 2.2+ phones. Native Java, no libraries, ~130 KB. The interface is in Galician, with large text and touch targets sized for the Mini's 240×320 screen.
A tiny Audiobookshelf **podcast** client for the Samsung Galaxy Mini (GT-S5570) and other Android 2.2+ phones. Native Java, no libraries, ~130 KB. The interface is in Galician, with large text and touch targets sized for the Mini's 240×320 screen. Its purple colour scheme and launcher icon come from the Audiobookshelf icon pack (`art/icon_master_512.png`); `tools/make_icons.py` derives the notification and player icons from it.
## Install
Binary file not shown.

After

Width:  |  Height:  |  Size: 224 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.2 KiB

After

Width:  |  Height:  |  Size: 7.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 312 B

After

Width:  |  Height:  |  Size: 272 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 B

After

Width:  |  Height:  |  Size: 533 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 760 B

After

Width:  |  Height:  |  Size: 588 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 690 B

After

Width:  |  Height:  |  Size: 977 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 354 B

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 413 B

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 467 B

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 656 B

After

Width:  |  Height:  |  Size: 647 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 326 B

After

Width:  |  Height:  |  Size: 434 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.3 KiB

After

Width:  |  Height:  |  Size: 4.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 355 B

After

Width:  |  Height:  |  Size: 301 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 486 B

After

Width:  |  Height:  |  Size: 494 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 561 B

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 834 B

After

Width:  |  Height:  |  Size: 806 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 457 B

After

Width:  |  Height:  |  Size: 567 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

+2 -2
View File
@@ -1,12 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape><solid android:color="#FFC766" /><corners android:radius="6dp" /></shape>
<shape><gradient android:startColor="#AA5CFF" android:endColor="#7022E6" android:angle="270" /><corners android:radius="6dp" /></shape>
</item>
<item android:state_enabled="false">
<shape><solid android:color="@color/dim" /><corners android:radius="6dp" /></shape>
</item>
<item>
<shape><solid android:color="@color/accent" /><corners android:radius="6dp" /></shape>
<shape><gradient android:startColor="@color/accent_top" android:endColor="@color/accent_bottom" android:angle="270" /><corners android:radius="6dp" /></shape>
</item>
</selector>
+1 -1
View File
@@ -4,7 +4,7 @@
<shape>
<solid android:color="@color/pressed" />
<corners android:radius="6dp" />
<stroke android:width="1dp" android:color="@color/accent" />
<stroke android:width="1dp" android:color="@color/accent_light" />
</shape>
</item>
<item android:state_enabled="false">
+2 -2
View File
@@ -1,9 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true">
<shape><solid android:color="#FFC766" /><corners android:radius="23dp" /></shape>
<shape><gradient android:startColor="#AA5CFF" android:endColor="#7022E6" android:angle="270" /><corners android:radius="23dp" /></shape>
</item>
<item>
<shape><solid android:color="@color/accent" /><corners android:radius="23dp" /></shape>
<shape><gradient android:startColor="@color/accent_top" android:endColor="@color/accent_bottom" android:angle="270" /><corners android:radius="23dp" /></shape>
</item>
</selector>
+1 -1
View File
@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/accent" />
<solid android:color="@color/accent_light" />
<stroke android:width="2dp" android:color="@color/bg" />
<size android:width="@dimen/seek_thumb" android:height="@dimen/seek_thumb" />
</shape>
+1 -1
View File
@@ -12,7 +12,7 @@
android:text="MiniABS"
android:textSize="@dimen/header_text"
android:textStyle="bold"
android:textColor="@color/accent" />
android:textColor="@color/accent_light" />
<TextView
android:id="@+id/library"
+3 -3
View File
@@ -34,7 +34,7 @@
android:text="MiniABS"
android:textSize="@dimen/login_title"
android:textStyle="bold"
android:textColor="@color/accent" />
android:textColor="@color/accent_light" />
<TextView
android:layout_width="wrap_content"
@@ -82,7 +82,7 @@
android:text="Conectar"
android:textStyle="bold"
android:textSize="@dimen/login_btn_text"
android:textColor="#121519" />
android:textColor="#FFFFFF" />
<TextView
android:id="@+id/status"
@@ -90,7 +90,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:textSize="@dimen/login_status"
android:textColor="@color/accent" />
android:textColor="@color/accent_light" />
<TextView
android:layout_width="fill_parent"
+1 -1
View File
@@ -14,7 +14,7 @@
android:paddingRight="10dp"
android:textSize="@dimen/header_sub"
android:textStyle="bold"
android:textColor="@color/accent"
android:textColor="@color/accent_light"
android:singleLine="true"
android:ellipsize="end"
android:background="@drawable/row_sel" />
+1 -1
View File
@@ -27,7 +27,7 @@
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="@dimen/text_show"
android:textColor="@color/accent"
android:textColor="@color/accent_light"
android:singleLine="true"
android:ellipsize="end" />
+1 -1
View File
@@ -6,4 +6,4 @@
android:gravity="center"
android:textSize="@dimen/text_more"
android:textStyle="bold"
android:textColor="@color/accent" />
android:textColor="@color/accent_light" />
+13 -9
View File
@@ -2,16 +2,20 @@
<resources>
<string name="app_name">MiniABS</string>
<color name="bg">#121519</color>
<color name="surface">#1C2127</color>
<color name="header">#0B0D10</color>
<color name="line">#2A3038</color>
<color name="text">#ECEFF2</color>
<color name="sub">#A9B2BC</color>
<color name="dim">#6E7882</color>
<color name="accent">#F5A623</color>
<!-- Colours taken from the Audiobookshelf icon: gradient #9437FC to #570DCF, waves #BC8AFC -->
<color name="bg">#13111A</color>
<color name="surface">#1D1928</color>
<color name="header">#0C0A12</color>
<color name="line">#2C2638</color>
<color name="text">#EEEBF3</color>
<color name="sub">#ADA6BC</color>
<color name="dim">#736C82</color>
<color name="accent">#7A25EC</color> <!-- fills: tabs, progress, seek bar -->
<color name="accent_light">#BC8AFC</color> <!-- text and small marks on dark backgrounds -->
<color name="accent_top">#9437FC</color> <!-- button gradient, as in the icon -->
<color name="accent_bottom">#5A11D3</color>
<color name="fresh">#6EC1E4</color>
<color name="pressed">#2C333C</color>
<color name="pressed">#2E2642</color>
<style name="MiniTheme" parent="@android:style/Theme.NoTitleBar">
<item name="android:windowBackground">@color/bg</item>
+1 -1
View File
@@ -50,7 +50,7 @@
<PreferenceCategory android:title="Acerca de">
<Preference
android:key="about"
android:title="MiniABS 1.4"
android:title="MiniABS 1.5"
android:summary="Un pequeno cliente de podcasts de Audiobookshelf para Android 2.2+" />
</PreferenceCategory>
</PreferenceScreen>
+1 -1
View File
@@ -26,7 +26,7 @@ import android.os.Build;
/** Audiobookshelf REST client (API as of server 2.x, including the 2.26+ token system). */
final class Api {
static final String VERSION = "1.4";
static final String VERSION = "1.5";
static class ApiException extends IOException {
final int code;
+1 -1
View File
@@ -201,7 +201,7 @@ final class Rows extends BaseAdapter {
} else {
bar.setVisibility(View.GONE);
}
int color = act.getResources().getColor(finished ? R.color.line : started ? R.color.accent : R.color.fresh);
int color = act.getResources().getColor(finished ? R.color.line : started ? R.color.accent_light : R.color.fresh);
dotView.setBackgroundDrawable(dot(color));
}
}
+38 -37
View File
@@ -1,6 +1,11 @@
"""Draws MiniABS's PNG icons for ldpi (Galaxy Mini), mdpi and hdpi.
Android 2.x has no vector drawables, so every icon is rendered here at 4x and
The launcher icon (res/drawable-*/ic_launcher.png) comes from the supplied
Audiobookshelf icon pack and is not generated here. The notification icon is
cut out of that same artwork (art/icon_master_512.png): the white headphones
and equalizer become a white silhouette, as Android 2.3's status bar expects.
Android 2.x has no vector drawables, so the player icons are rendered at 4x and
downsampled for smooth edges.
"""
import os
@@ -8,9 +13,9 @@ import sys
from PIL import Image, ImageDraw
RES = sys.argv[1]
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
DENS = {"ldpi": 0.75, "mdpi": 1.0, "hdpi": 1.5}
AMBER = (245, 166, 35, 255)
DARK = (18, 21, 25, 255)
LILAC = (188, 138, 252, 255) # the icon's sound-wave colour (#BC8AFC)
WHITE = (255, 255, 255, 255)
SS = 4 # supersampling
@@ -27,36 +32,32 @@ def save(img, px, folder, name):
out.save(os.path.join(d, name + ".png"))
def broadcast(d, s, cx, cy, scale, color, bg):
"""Podcast mark: a dot with two arcs on each side."""
r = s * 0.075 * scale
d.ellipse([cx - r, cy - r, cx + r, cy + r], fill=color)
w = max(1, int(s * 0.07 * scale))
for k in (0.22, 0.37):
rr = s * k * scale
box = [cx - rr, cy - rr, cx + rr, cy + rr]
d.arc(box, 140, 220, fill=color, width=w)
d.arc(box, -40, 40, fill=color, width=w)
def status_silhouette():
"""White mask of the icon's light parts (headphones, bars, waves) on transparency."""
src = Image.open(os.path.join(ROOT, "art", "icon_master_512.png")).convert("RGBA")
out = Image.new("RGBA", src.size, (0, 0, 0, 0))
sp, op = src.load(), out.load()
for y in range(src.size[1]):
for x in range(src.size[0]):
r, g, b, a = sp[x, y]
# the purple background has a low green channel; the white/lilac artwork a high one
level = min(255, max(0, (g - 80) * 255 // 50))
op[x, y] = (255, 255, 255, level * a // 255)
box = out.getbbox()
out = out.crop(box)
side = max(out.size)
sq = Image.new("RGBA", (side, side), (0, 0, 0, 0))
sq.paste(out, ((side - out.size[0]) // 2, (side - out.size[1]) // 2))
return sq
def launcher(px, folder):
img, s = canvas(px)
d = ImageDraw.Draw(img)
m = s * 0.06
d.rounded_rectangle([m, m, s - m, s - m], radius=s * 0.2, fill=DARK, outline=AMBER, width=int(s * 0.04))
broadcast(d, s, s / 2, s * 0.44, 1.0, AMBER, DARK)
# little stand under the mark
w = s * 0.05
d.rectangle([s / 2 - w / 2, s * 0.5, s / 2 + w / 2, s * 0.74], fill=AMBER)
d.rounded_rectangle([s * 0.36, s * 0.72, s * 0.64, s * 0.78], radius=w / 2, fill=AMBER)
save(img, px, folder, "ic_launcher")
def status(px, folder):
img, s = canvas(px)
d = ImageDraw.Draw(img)
broadcast(d, s, s / 2, s / 2, 1.25, WHITE, None)
save(img, px, folder, "ic_stat")
def status(sil, px, folder):
pad = max(1, round(px * 0.06))
inner = sil.resize((px - 2 * pad, px - 2 * pad), Image.LANCZOS)
img = Image.new("RGBA", (px, px), (0, 0, 0, 0))
img.paste(inner, (pad, pad))
d = os.path.join(RES, "drawable-" + folder)
img.save(os.path.join(d, "ic_stat.png"))
def play(px, folder, name, color):
@@ -85,12 +86,12 @@ def refresh(px, folder):
save(img, px, folder, "ic_refresh")
sil = status_silhouette()
for folder, k in DENS.items():
launcher(round(48 * k), folder)
status(round(25 * k), folder)
play(round(32 * k), folder, "ic_play", DARK)
pause(round(32 * k), folder, "ic_pause", DARK)
play(round(38 * k), folder, "ic_play_small", AMBER)
pause(round(38 * k), folder, "ic_pause_small", AMBER)
status(sil, round(25 * k), folder)
play(round(32 * k), folder, "ic_play", WHITE) # on the purple play button
pause(round(32 * k), folder, "ic_pause", WHITE)
play(round(38 * k), folder, "ic_play_small", LILAC) # mini player bar, on dark
pause(round(38 * k), folder, "ic_pause_small", LILAC)
refresh(round(30 * k), folder)
print("icons ok")