Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
04ac5ae725 | ||
|
|
084d7e9cad | ||
|
|
db1175d25c | ||
|
|
10a2aa9ef5 |
No files matched your search
@@ -1,9 +1,9 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.miniabs"
|
package="com.miniabs"
|
||||||
android:versionCode="10"
|
android:versionCode="12"
|
||||||
android:versionName="1.9"
|
android:versionName="1.11"
|
||||||
android:installLocation="auto">
|
android:installLocation="internalOnly">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
|
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
# Podlite
|
# Podlite
|
||||||
|
|
||||||
A tiny Audiobookshelf **podcast** client for the Samsung Galaxy Mini (GT-S5570) and other Android 2.2+ phones. Native Java; the APK is about 1.1 MB, nearly all of it the bundled TLS stack. Formerly MiniABS. The interface is in Galician, with large text and touch targets sized for the Mini's 240×320 screen. The launcher icon is the "Podcasts Radio" pack (master in `art/icon-master.png`); `tools/make_icons.py` draws the notification icon (a white radio silhouette) and the player icons. The purple colour scheme comes from the earlier Audiobookshelf icon.
|
A tiny Audiobookshelf **podcast** client for the Samsung Galaxy Mini (GT-S5570) and other Android 2.2+ phones. Native Java; the APK is about 1.1 MB, nearly all of it the bundled TLS stack. Formerly MiniABS. The interface is in Galician, with large text and touch targets sized for the Mini's 240×320 screen. The launcher icon is the "Podcasts Radio" pack (master in `art/icon-master.png`); `tools/make_icons.py` draws the notification icon (a white radio silhouette) and the player icons. Its warm dark colours and amber accent come from the radio: brass and the glow of its dial.
|
||||||
|
|
||||||
## Install
|
## Install
|
||||||
|
|
||||||
1. Copy `Podlite.apk` to the phone (USB to the SD card, or download it in the browser).
|
1. Copy `Podlite.apk` to the phone (USB to the SD card, or download it in the browser).
|
||||||
2. On the phone: **Settings → Applications → Unknown sources** → tick it.
|
2. On the phone: **Settings → Applications → Unknown sources** → tick it.
|
||||||
3. Open the APK from a file manager and install. It can be moved to the SD card (Settings → Applications → Manage applications → Podlite → Move to SD card).
|
3. Open the APK from a file manager and install. Podlite installs to internal storage (about 1 MB): an app on the SD card is killed whenever the phone shares its SD card over USB, which stopped playback and broke HTTPS until a restart.
|
||||||
|
|
||||||
## Connecting
|
## Connecting
|
||||||
|
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 533 B After Width: | Height: | Size: 564 B |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 434 B After Width: | Height: | Size: 453 B |
|
Before Width: | Height: | Size: 647 B After Width: | Height: | Size: 679 B |
|
Before Width: | Height: | Size: 494 B After Width: | Height: | Size: 531 B |
|
Before Width: | Height: | Size: 806 B After Width: | Height: | Size: 863 B |
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:state_pressed="true">
|
<item android:state_pressed="true">
|
||||||
<shape><gradient android:startColor="#AA5CFF" android:endColor="#7022E6" android:angle="270" /><corners android:radius="6dp" /></shape>
|
<shape><gradient android:startColor="#E39A38" android:endColor="#A9621A" android:angle="270" /><corners android:radius="6dp" /></shape>
|
||||||
</item>
|
</item>
|
||||||
<item android:state_enabled="false">
|
<item android:state_enabled="false">
|
||||||
<shape><solid android:color="@color/dim" /><corners android:radius="6dp" /></shape>
|
<shape><solid android:color="@color/dim" /><corners android:radius="6dp" /></shape>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:state_pressed="true">
|
<item android:state_pressed="true">
|
||||||
<shape><gradient android:startColor="#AA5CFF" android:endColor="#7022E6" android:angle="270" /><corners android:radius="23dp" /></shape>
|
<shape><gradient android:startColor="#E39A38" android:endColor="#A9621A" android:angle="270" /><corners android:radius="23dp" /></shape>
|
||||||
</item>
|
</item>
|
||||||
<item>
|
<item>
|
||||||
<shape><gradient android:startColor="@color/accent_top" android:endColor="@color/accent_bottom" android:angle="270" /><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>
|
||||||
|
|||||||
@@ -2,20 +2,20 @@
|
|||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">Podlite</string>
|
<string name="app_name">Podlite</string>
|
||||||
|
|
||||||
<!-- Colours taken from the Audiobookshelf icon: gradient #9437FC to #570DCF, waves #BC8AFC -->
|
<!-- Colours taken from the radio icon: brass #C8964F, dial glow #CC6000, on warm near-black -->
|
||||||
<color name="bg">#13111A</color>
|
<color name="bg">#15120F</color>
|
||||||
<color name="surface">#1D1928</color>
|
<color name="surface">#211C17</color>
|
||||||
<color name="header">#0C0A12</color>
|
<color name="header">#0E0B09</color>
|
||||||
<color name="line">#2C2638</color>
|
<color name="line">#342A21</color>
|
||||||
<color name="text">#EEEBF3</color>
|
<color name="text">#F2EDE6</color>
|
||||||
<color name="sub">#ADA6BC</color>
|
<color name="sub">#BFB4A6</color>
|
||||||
<color name="dim">#736C82</color>
|
<color name="dim">#827567</color>
|
||||||
<color name="accent">#7A25EC</color> <!-- fills: tabs, progress, seek bar -->
|
<color name="accent">#C27A1E</color> <!-- fills: tabs, progress, seek bar -->
|
||||||
<color name="accent_light">#BC8AFC</color> <!-- text and small marks on dark backgrounds -->
|
<color name="accent_light">#E8A64A</color> <!-- text and small marks on dark backgrounds -->
|
||||||
<color name="accent_top">#9437FC</color> <!-- button gradient, as in the icon -->
|
<color name="accent_top">#D98C2B</color> <!-- button gradient, as in the icon -->
|
||||||
<color name="accent_bottom">#5A11D3</color>
|
<color name="accent_bottom">#A35F14</color>
|
||||||
<color name="fresh">#6EC1E4</color>
|
<color name="fresh">#6EC1E4</color>
|
||||||
<color name="pressed">#2E2642</color>
|
<color name="pressed">#3B2D20</color>
|
||||||
|
|
||||||
<style name="MiniTheme" parent="@android:style/Theme.NoTitleBar">
|
<style name="MiniTheme" parent="@android:style/Theme.NoTitleBar">
|
||||||
<item name="android:windowBackground">@color/bg</item>
|
<item name="android:windowBackground">@color/bg</item>
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
<PreferenceCategory android:title="Acerca de">
|
<PreferenceCategory android:title="Acerca de">
|
||||||
<Preference
|
<Preference
|
||||||
android:key="about"
|
android:key="about"
|
||||||
android:title="Podlite 1.9"
|
android:title="Podlite 1.10"
|
||||||
android:summary="Un pequeno cliente de podcasts de Audiobookshelf para Android 2.2+" />
|
android:summary="Un pequeno cliente de podcasts de Audiobookshelf para Android 2.2+" />
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
</PreferenceScreen>
|
</PreferenceScreen>
|
||||||
@@ -26,7 +26,7 @@ import android.os.Build;
|
|||||||
|
|
||||||
/** Audiobookshelf REST client (API as of server 2.x, including the 2.26+ token system). */
|
/** Audiobookshelf REST client (API as of server 2.x, including the 2.26+ token system). */
|
||||||
final class Api {
|
final class Api {
|
||||||
static final String VERSION = "1.9";
|
static final String VERSION = "1.11";
|
||||||
|
|
||||||
static class ApiException extends IOException {
|
static class ApiException extends IOException {
|
||||||
final int code;
|
final int code;
|
||||||
|
|||||||
@@ -4,35 +4,47 @@ import java.io.ByteArrayOutputStream;
|
|||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.io.InputStream;
|
import java.io.InputStream;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
|
||||||
/** Mozilla roots shipped as assets/roots/<sha1(subject)>-<n>.der */
|
/**
|
||||||
|
* Mozilla roots shipped as assets/roots/<sha1(subject)>-<n>.der
|
||||||
|
*
|
||||||
|
* Roots are read from the APK once and kept: an app installed on the SD card loses its APK while the
|
||||||
|
* card is shared over USB, and a failed read must not leave every server "untrusted" until a restart.
|
||||||
|
*/
|
||||||
public final class AssetRoots implements CertVerifier.RootStore {
|
public final class AssetRoots implements CertVerifier.RootStore {
|
||||||
private final Context ctx;
|
private final Context ctx;
|
||||||
private Set<String> names;
|
private Set<String> names; // asset file names, once listed
|
||||||
|
private final Map<String, List<byte[]>> found = new HashMap<String, List<byte[]>>(); // by subject hash; null = none
|
||||||
|
|
||||||
public AssetRoots(Context c) { ctx = c.getApplicationContext(); }
|
public AssetRoots(Context c) { ctx = c.getApplicationContext(); }
|
||||||
|
|
||||||
public synchronized List<byte[]> find(byte[] subjectDer) {
|
public synchronized List<byte[]> find(byte[] subjectDer) {
|
||||||
|
String h = CertVerifier.sha1Hex(subjectDer);
|
||||||
|
if (found.containsKey(h)) return found.get(h);
|
||||||
try {
|
try {
|
||||||
if (names == null) {
|
if (names == null) {
|
||||||
names = new HashSet<String>();
|
|
||||||
String[] l = ctx.getAssets().list("roots");
|
String[] l = ctx.getAssets().list("roots");
|
||||||
if (l != null) for (int i = 0; i < l.length; i++) names.add(l[i]);
|
if (l == null || l.length == 0) return null; // APK unreadable right now: try again next time
|
||||||
|
Set<String> n = new HashSet<String>();
|
||||||
|
for (int i = 0; i < l.length; i++) n.add(l[i]);
|
||||||
|
names = n;
|
||||||
}
|
}
|
||||||
String h = CertVerifier.sha1Hex(subjectDer);
|
|
||||||
List<byte[]> out = null;
|
List<byte[]> out = null;
|
||||||
for (int i = 0; names.contains(h + "-" + i + ".der"); i++) {
|
for (int i = 0; names.contains(h + "-" + i + ".der"); i++) {
|
||||||
if (out == null) out = new ArrayList<byte[]>();
|
if (out == null) out = new ArrayList<byte[]>();
|
||||||
out.add(readAll(ctx.getAssets().open("roots/" + h + "-" + i + ".der")));
|
out.add(readAll(ctx.getAssets().open("roots/" + h + "-" + i + ".der")));
|
||||||
}
|
}
|
||||||
|
found.put(h, out);
|
||||||
return out;
|
return out;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
return null;
|
return null; // not cached: the next handshake reads again
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -15,7 +15,7 @@ from PIL import Image, ImageDraw
|
|||||||
RES = sys.argv[1]
|
RES = sys.argv[1]
|
||||||
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
ROOT = os.path.join(os.path.dirname(os.path.abspath(__file__)), "..")
|
||||||
DENS = {"ldpi": 0.75, "mdpi": 1.0, "hdpi": 1.5}
|
DENS = {"ldpi": 0.75, "mdpi": 1.0, "hdpi": 1.5}
|
||||||
LILAC = (188, 138, 252, 255) # the icon's sound-wave colour (#BC8AFC)
|
AMBER = (232, 166, 74, 255) # accent_light (#E8A64A), from the radio icon's brass
|
||||||
WHITE = (255, 255, 255, 255)
|
WHITE = (255, 255, 255, 255)
|
||||||
SS = 4 # supersampling
|
SS = 4 # supersampling
|
||||||
|
|
||||||
@@ -91,9 +91,9 @@ def refresh(px, folder):
|
|||||||
sil = status_silhouette()
|
sil = status_silhouette()
|
||||||
for folder, k in DENS.items():
|
for folder, k in DENS.items():
|
||||||
status(sil, round(25 * k), folder)
|
status(sil, round(25 * k), folder)
|
||||||
play(round(32 * k), folder, "ic_play", WHITE) # on the purple play button
|
play(round(32 * k), folder, "ic_play", WHITE) # on the amber play button
|
||||||
pause(round(32 * k), folder, "ic_pause", WHITE)
|
pause(round(32 * k), folder, "ic_pause", WHITE)
|
||||||
play(round(38 * k), folder, "ic_play_small", LILAC) # mini player bar, on dark
|
play(round(38 * k), folder, "ic_play_small", AMBER) # mini player bar, on dark
|
||||||
pause(round(38 * k), folder, "ic_pause_small", LILAC)
|
pause(round(38 * k), folder, "ic_pause_small", AMBER)
|
||||||
refresh(round(30 * k), folder)
|
refresh(round(30 * k), folder)
|
||||||
print("icons ok")
|
print("icons ok")
|
||||||