Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
82acdb79f6 | ||
|
|
478fc60527 | ||
|
|
361adef1a0 | ||
|
|
503beac559 | ||
|
|
9a4f6794d9 | ||
|
|
748f6add54 |
No files matched your search
@@ -1,8 +1,8 @@
|
|||||||
<?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="org.gingercal"
|
package="org.gingercal"
|
||||||
android:versionCode="5"
|
android:versionCode="7"
|
||||||
android:versionName="1.4">
|
android:versionName="1.6">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
|
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
# GingerCal — Nextcloud CalDAV for Android 2.3 (Galaxy Mini)
|
# Calendar Lite — Nextcloud CalDAV for Android 2.3 (Galaxy Mini)
|
||||||
|
|
||||||
A small native calendar client for Gingerbread (API 9/10). Built for a Samsung Galaxy Mini
|
A small native calendar client for Gingerbread (API 9/10). Built for a Samsung Galaxy Mini
|
||||||
(GT-S5570, 240×320, Android 2.3.5) talking to Nextcloud, reached through Pangolin.
|
(GT-S5570, 240×320, Android 2.3.5) talking to Nextcloud at its normal `https://` address.
|
||||||
|
|
||||||
**Since 1.4 it brings its own TLS 1.2** (Bouncy Castle, the same client as Minamp, with Mozilla's
|
**Since 1.4 it brings its own TLS 1.2** (Bouncy Castle, the same client as Plexamp Lite, with Mozilla's
|
||||||
root certificates and keep-alive connections), like the iPod version does with BearSSL. Put the
|
root certificates and keep-alive connections), like the iPod version does with BearSSL. Put the
|
||||||
normal `https://` Nextcloud address in Settings, at home or away: no bridge is needed. The bridge
|
normal `https://` Nextcloud address in Settings, at home or away: no bridge is needed. The bridge
|
||||||
sections below are kept for reference; a bridge address (pinned certificate) still works.
|
sections below are kept for reference; a bridge address (pinned certificate) still works.
|
||||||
@@ -131,22 +131,24 @@ you should turn `proxy_ssl_verify` on.
|
|||||||
## Install on the phone
|
## Install on the phone
|
||||||
|
|
||||||
1. Settings, Applications, tick **Unknown sources**.
|
1. Settings, Applications, tick **Unknown sources**.
|
||||||
2. Copy `GingerCal.apk` to the SD card (USB) and open it with the file manager, or download it
|
2. Copy `CalendarLite.apk` to the SD card (USB) and open it with the file manager, or download it
|
||||||
over plain `http://` (the 2.3 browser can't open most HTTPS sites either).
|
over plain `http://` (the 2.3 browser can't open most HTTPS sites either).
|
||||||
3. Open GingerCal, Menu, Settings. In Nextcloud create an app password: Settings, Security,
|
3. Open Calendar Lite, Menu, Settings. In Nextcloud create an app password: Settings, Security,
|
||||||
Devices & sessions. Fill in server, user, app password, then **Save and test connection**.
|
Devices & sessions. Fill in server, user, app password, then **Save and test connection**.
|
||||||
|
|
||||||
Menu key: View (Month/Agenda), New event, Sync, Today, Calendars, Settings. Tap an event for
|
Menu key: View (Month/Agenda), New event, Sync, Today, Calendars, Settings. Tap an event for
|
||||||
details, Edit and Delete.
|
details, Edit and Delete.
|
||||||
|
|
||||||
If installation fails, check the file arrived whole (53 KB; some browsers save a truncated or
|
If installation fails, check the file arrived whole (about 1 MB; some browsers save a truncated or
|
||||||
renamed `.apk.zip`) and that there are a few MB free on the internal storage. On a PC with
|
renamed `.apk.zip`) and that there are a few MB free on the internal storage. On a PC with
|
||||||
`adb`, `adb install GingerCal.apk` prints the exact reason (`INSTALL_FAILED_...`).
|
`adb`, `adb install CalendarLite.apk` prints the exact reason (`INSTALL_FAILED_...`).
|
||||||
|
|
||||||
## Building
|
## Building
|
||||||
|
|
||||||
Needs JDK 11+, Python 3 with Pillow, `d8` and `apksigner.jar` (Android SDK build-tools), and
|
Needs JDK 11+, Python 3, `d8` and `apksigner.jar` (Android SDK build-tools),
|
||||||
`apktool.jar` (2.9+, which bundles Google's `aapt` and smali). No Gradle.
|
`apktool.jar` (2.9+, which bundles Google's `aapt` and smali), and the Bouncy Castle sources renamed
|
||||||
|
to `com.minamp.bc` that Plexamp Lite's `tools/setup_toolchain.sh` prepares (`BC_SRC`, default
|
||||||
|
`~/android-toolchain/bcsrc`). No Gradle.
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
D8=d8 APKTOOL=~/apktool.jar APKSIGNER=$SDK/build-tools/34.0.0/lib/apksigner.jar ./build.sh
|
D8=d8 APKTOOL=~/apktool.jar APKSIGNER=$SDK/build-tools/34.0.0/lib/apksigner.jar ./build.sh
|
||||||
@@ -165,7 +167,7 @@ over the existing app.
|
|||||||
Tests (desktop JVM, need a CalDAV server such as Radicale on :5232 with user `xose`/`secret`):
|
Tests (desktop JVM, need a CalDAV server such as Radicale on :5232 with user `xose`/`secret`):
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
javac -d out src/org/gingercal/*.java test/org/gingercal/*.java
|
javac -d out src/org/gingercal/*.java $(ls test/org/gingercal/*.java | grep -v NetTest)
|
||||||
java -cp out org.gingercal.CoreTest # discovery, sync, recurrence, create/edit/delete
|
java -cp out org.gingercal.CoreTest # discovery, sync, recurrence, create/edit/delete
|
||||||
java -cp out org.gingercal.RedirectTest # redirect handling (no server needed)
|
java -cp out org.gingercal.RedirectTest # redirect handling (no server needed)
|
||||||
# the bundled TLS transport (needs the android.jar and Bouncy Castle sources to compile):
|
# the bundled TLS transport (needs the android.jar and Bouncy Castle sources to compile):
|
||||||
@@ -181,12 +183,12 @@ declarations.
|
|||||||
|
|
||||||
- `src/` CalDAV core, platform-neutral: discovery, PROPFIND/REPORT, iCalendar, sync, pinning
|
- `src/` CalDAV core, platform-neutral: discovery, PROPFIND/REPORT, iCalendar, sync, pinning
|
||||||
- `android/` activities, sync service, alarms, SQLite cache, `NetHttp` transport
|
- `android/` activities, sync service, alarms, SQLite cache, `NetHttp` transport
|
||||||
- `android/org/gingercal/net/` the bundled TLS 1.2 HTTP client from Minamp; `assets/roots/` Mozilla roots
|
- `android/org/gingercal/net/` the bundled TLS 1.2 HTTP client from Plexamp Lite; `assets/roots/` Mozilla roots
|
||||||
- `stubs/` compile-only Android API 10 signatures (never packaged)
|
- `stubs/` compile-only Android API 10 signatures (never packaged)
|
||||||
- `bridge/` legacy-TLS endpoint: standalone nginx (config, compose file), `npm/http.conf` for
|
- `bridge/` legacy-TLS endpoint: standalone nginx (config, compose file), `npm/http.conf` for
|
||||||
Nginx Proxy Manager, certificate script
|
Nginx Proxy Manager, certificate script
|
||||||
- `res/` app name; the icon is drawn at build time (mdpi + ldpi for the Galaxy Mini)
|
- `res/` app name, launcher icon (ldpi to xhdpi; master in `art/`) and `stat_notify`, the white status-bar icon for reminders
|
||||||
- `tools/` icon, dex disassembler helper, zipalign, dex/API checkers, d8 workaround
|
- `tools/` dex disassembler helper, zipalign, dex/API checkers, d8 workaround
|
||||||
|
|
||||||
## Security notes
|
## Security notes
|
||||||
|
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ public class AlarmReceiver extends BroadcastReceiver {
|
|||||||
static synchronized void acquire(Context c) {
|
static synchronized void acquire(Context c) {
|
||||||
if (lock == null) {
|
if (lock == null) {
|
||||||
PowerManager pm = (PowerManager) c.getApplicationContext().getSystemService("power");
|
PowerManager pm = (PowerManager) c.getApplicationContext().getSystemService("power");
|
||||||
lock = pm.newWakeLock(1 /* PARTIAL_WAKE_LOCK */, "GingerCal sync");
|
lock = pm.newWakeLock(1 /* PARTIAL_WAKE_LOCK */, "Calendar Lite sync");
|
||||||
lock.setReferenceCounted(false);
|
lock.setReferenceCounted(false);
|
||||||
}
|
}
|
||||||
lock.acquire(5 * 60 * 1000L);
|
lock.acquire(5 * 60 * 1000L);
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ public class GingerApp extends Application {
|
|||||||
super.onCreate();
|
super.onCreate();
|
||||||
L.gl = !"en".equals(getSharedPreferences("gingercal", 0).getString("lang", "gl"));
|
L.gl = !"en".equals(getSharedPreferences("gingercal", 0).getString("lang", "gl"));
|
||||||
org.gingercal.net.CertVerifier.setRootStore(new org.gingercal.net.AssetRoots(this));
|
org.gingercal.net.CertVerifier.setRootStore(new org.gingercal.net.AssetRoots(this));
|
||||||
org.gingercal.net.Http.USER_AGENT = "GingerCal/1.4 (Android 2.3)";
|
org.gingercal.net.Http.USER_AGENT = "CalendarLite/1.6 (Android 2.3)";
|
||||||
tlsTexts();
|
tlsTexts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -159,7 +159,7 @@ public class MainActivity extends Activity implements MonthView.Listener {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TextView navArrow(String label, final int delta) {
|
TextView navArrow(String label, final int delta) {
|
||||||
TextView b = App.text(this, label, 20, 0xFFD66E2A); // « »: Latin-1, always in the 2.3 font
|
TextView b = App.text(this, label, 20, 0xFF3D8EF0); // « »: Latin-1, always in the 2.3 font
|
||||||
b.setGravity(17 /* Gravity.CENTER */);
|
b.setGravity(17 /* Gravity.CENTER */);
|
||||||
int sel = App.sysDrawable(this, "list_selector_background");
|
int sel = App.sysDrawable(this, "list_selector_background");
|
||||||
if (sel != 0) b.setBackgroundResource(sel); // press highlight
|
if (sel != 0) b.setBackgroundResource(sel); // press highlight
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ public class MonthView extends View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static final int BG = 0xFF101418, GRID = 0xFF2A3036, WEEKDAY = 0xFF90A4AE, NUM = 0xFFECEFF1,
|
static final int BG = 0xFF101418, GRID = 0xFF2A3036, WEEKDAY = 0xFF90A4AE, NUM = 0xFFECEFF1,
|
||||||
OTHER = 0xFF5C666E, TODAY = 0xFFD66E2A, SELECTED = 0xFF2E3B44;
|
OTHER = 0xFF5C666E, TODAY = 0xFF3D8EF0, SELECTED = 0xFF2E3B44;
|
||||||
|
|
||||||
final float density;
|
final float density;
|
||||||
final Paint fill = new Paint(1 /* ANTI_ALIAS_FLAG */), text = new Paint(1), line = new Paint();
|
final Paint fill = new Paint(1 /* ANTI_ALIAS_FLAG */), text = new Paint(1), line = new Paint();
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ import java.util.concurrent.ExecutorService;
|
|||||||
import org.gingercal.net.CertVerifier;
|
import org.gingercal.net.CertVerifier;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Android 2.3 transport through the bundled TLS 1.2 client (org.gingercal.net, from Minamp):
|
* Android 2.3 transport through the bundled TLS 1.2 client (org.gingercal.net, from Plexamp Lite):
|
||||||
* the phone's own TLS 1.0 can't reach Nextcloud, so this talks to the normal https:// address
|
* the phone's own TLS 1.0 can't reach Nextcloud, so this talks to the normal https:// address
|
||||||
* directly, any method (PROPFIND/REPORT included), with keep-alive. A pinned certificate (a
|
* directly, any method (PROPFIND/REPORT included), with keep-alive. A pinned certificate (a
|
||||||
* self-signed server or an old bridge) is still honoured.
|
* self-signed server or an old bridge) is still honoured.
|
||||||
|
|||||||
@@ -43,8 +43,9 @@ public final class Reminders {
|
|||||||
List<Ev> due = Db.get(c).startingBetween(Math.max(notified, now - 30 * ICal.MIN), now + m * ICal.MIN + 30000L);
|
List<Ev> due = Db.get(c).startingBetween(Math.max(notified, now - 30 * ICal.MIN), now + m * ICal.MIN + 30000L);
|
||||||
NotificationManager nm = (NotificationManager) c.getSystemService("notification");
|
NotificationManager nm = (NotificationManager) c.getSystemService("notification");
|
||||||
long newest = notified;
|
long newest = notified;
|
||||||
int icon = App.sysDrawable(c, "ic_popup_reminder");
|
// our own white status-bar glyph: the system's colour alarm-clock icon looks out of place up there
|
||||||
if (icon == 0) icon = App.sysDrawable(c, "ic_dialog_info");
|
int icon = c.getResources().getIdentifier("stat_notify", "drawable", c.getPackageName());
|
||||||
|
if (icon == 0) icon = App.sysDrawable(c, "ic_popup_reminder");
|
||||||
for (Ev e : due) {
|
for (Ev e : due) {
|
||||||
Intent open = new Intent(c, MainActivity.class);
|
Intent open = new Intent(c, MainActivity.class);
|
||||||
open.setFlags(0x10000000 /* FLAG_ACTIVITY_NEW_TASK */);
|
open.setFlags(0x10000000 /* FLAG_ACTIVITY_NEW_TASK */);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ public class SettingsActivity extends Activity {
|
|||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle b) {
|
protected void onCreate(Bundle b) {
|
||||||
super.onCreate(b);
|
super.onCreate(b);
|
||||||
setTitle(L.t("GingerCal settings"));
|
setTitle(L.t("Calendar Lite settings"));
|
||||||
LinearLayout f = App.vbox(this);
|
LinearLayout f = App.vbox(this);
|
||||||
int p = App.dp(this, 8);
|
int p = App.dp(this, 8);
|
||||||
f.setPadding(p, p, p, p);
|
f.setPadding(p, p, p, p);
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ import com.minamp.bc.crypto.util.PublicKeyFactory;
|
|||||||
* Minimal Web PKI path validation for the bundled TLS stack:
|
* Minimal Web PKI path validation for the bundled TLS stack:
|
||||||
* hostname match, validity window, signature chain up to a bundled Mozilla root.
|
* hostname match, validity window, signature chain up to a bundled Mozilla root.
|
||||||
* Optionally one pinned leaf certificate (SHA-256) is trusted as-is, for self-signed servers.
|
* Optionally one pinned leaf certificate (SHA-256) is trusted as-is, for self-signed servers.
|
||||||
* Shared with Minamp (tools/tlskit in the iOS ports' history); keep the copies in sync.
|
* Shared with Plexamp Lite (tools/tlskit in the iOS ports' history); keep the copies in sync.
|
||||||
*/
|
*/
|
||||||
public final class CertVerifier {
|
public final class CertVerifier {
|
||||||
|
|
||||||
|
|||||||
@@ -33,11 +33,11 @@ import com.minamp.bc.tls.crypto.impl.bc.BcTlsCrypto;
|
|||||||
/**
|
/**
|
||||||
* Small HTTP/1.1 client with its own TLS 1.2 (Bouncy Castle), keep-alive pooling and
|
* Small HTTP/1.1 client with its own TLS 1.2 (Bouncy Castle), keep-alive pooling and
|
||||||
* session resumption. Android 2.x's built-in SSL only speaks TLS 1.0 and lacks today's roots,
|
* session resumption. Android 2.x's built-in SSL only speaks TLS 1.0 and lacks today's roots,
|
||||||
* so every request in the app goes through here. From Minamp; keep the copies in sync.
|
* so every request in the app goes through here. From Plexamp Lite; keep the copies in sync.
|
||||||
*/
|
*/
|
||||||
public final class Http {
|
public final class Http {
|
||||||
|
|
||||||
public static String USER_AGENT = "Minamp/1.0";
|
public static String USER_AGENT = "PlexampLite/1.0";
|
||||||
|
|
||||||
/** Optional HTTP CONNECT proxy, only used when running the networking code on a desktop JVM. */
|
/** Optional HTTP CONNECT proxy, only used when running the networking code on a desktop JVM. */
|
||||||
public static String proxyHost;
|
public static String proxyHost;
|
||||||
|
|||||||
|
After Width: | Height: | Size: 973 KiB |
@@ -1,5 +1,5 @@
|
|||||||
TASK FOR CLAUDE CODE (on the Pangolin server, 141.227.129.182)
|
TASK FOR CLAUDE CODE (on the Pangolin server, 141.227.129.182)
|
||||||
Add a raw TCP resource on port 8443 so the GingerCal app (Android 2.3) can reach Nextcloud CalDAV
|
Add a raw TCP resource on port 8443 so the Calendar Lite app (Android 2.3) can reach Nextcloud CalDAV
|
||||||
==================================================================================================
|
==================================================================================================
|
||||||
|
|
||||||
CONTEXT
|
CONTEXT
|
||||||
@@ -72,7 +72,7 @@ STEPS
|
|||||||
|
|
||||||
7. Pangolin dashboard (the user does this, or guide them through it):
|
7. Pangolin dashboard (the user does this, or guide them through it):
|
||||||
- Org of the home server, Resources, Add Resource, type "Raw TCP/UDP resource".
|
- Org of the home server, Resources, Add Resource, type "Raw TCP/UDP resource".
|
||||||
- Name: GingerCal CalDAV. Protocol: TCP. Public port: 8443.
|
- Name: Calendar Lite CalDAV. Protocol: TCP. Public port: 8443.
|
||||||
- Site: the site whose newt runs on the home server 172.30.1.123 (most likely the "casa" site).
|
- Site: the site whose newt runs on the home server 172.30.1.123 (most likely the "casa" site).
|
||||||
- Target: IP 172.30.1.123, port 8443. Leave "Enable Proxy Protocol" OFF (the bridge doesn't
|
- Target: IP 172.30.1.123, port 8443. Leave "Enable Proxy Protocol" OFF (the bridge doesn't
|
||||||
expect it; enabling only one side breaks every connection).
|
expect it; enabling only one side breaks every connection).
|
||||||
@@ -104,7 +104,7 @@ STEPS
|
|||||||
|
|
||||||
PHONE SETUP (after step 8 passes)
|
PHONE SETUP (after step 8 passes)
|
||||||
---------------------------------
|
---------------------------------
|
||||||
GingerCal, Menu, Settings:
|
Calendar Lite, Menu, Settings:
|
||||||
Server: https://141.227.129.182:8443 (any *.oxose.org name works too, e.g.
|
Server: https://141.227.129.182:8443 (any *.oxose.org name works too, e.g.
|
||||||
https://cal-old.oxose.org:8443: raw TCP ignores the hostname and the app pins the
|
https://cal-old.oxose.org:8443: raw TCP ignores the hostname and the app pins the
|
||||||
certificate, not the name)
|
certificate, not the name)
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# Builds GingerCal.apk for Android 2.3 (API 10) without Gradle.
|
# Builds CalendarLite.apk for Android 2.3 (API 10) without Gradle.
|
||||||
#
|
#
|
||||||
# Needs: JDK 11+ (javac, keytool), python3 with Pillow (icon), and three tools:
|
# Needs: JDK 11+ (javac, keytool), python3, and three tools:
|
||||||
# D8 command that runs d8 (Android SDK build-tools; default "d8" on PATH)
|
# D8 command that runs d8 (Android SDK build-tools; default "d8" on PATH)
|
||||||
# APKTOOL path to apktool.jar (https://apktool.org; bundles aapt and smali)
|
# APKTOOL path to apktool.jar (https://apktool.org; bundles aapt and smali)
|
||||||
# APKSIGNER path to apksigner.jar (Android SDK build-tools/<ver>/lib/apksigner.jar)
|
# APKSIGNER path to apksigner.jar (Android SDK build-tools/<ver>/lib/apksigner.jar)
|
||||||
@@ -20,7 +20,7 @@ VERSION_CODE=$(sed -n 's/.*android:versionCode="\([0-9]*\)".*/\1/p' AndroidManif
|
|||||||
VERSION_NAME=$(sed -n 's/.*android:versionName="\([^"]*\)".*/\1/p' AndroidManifest.xml)
|
VERSION_NAME=$(sed -n 's/.*android:versionName="\([^"]*\)".*/\1/p' AndroidManifest.xml)
|
||||||
|
|
||||||
rm -rf "$OUT"
|
rm -rf "$OUT"
|
||||||
mkdir -p "$OUT/stubs" "$OUT/classes" "$OUT/dex" "$OUT/apk/res/drawable" "$OUT/apk/res/drawable-ldpi"
|
mkdir -p "$OUT/stubs" "$OUT/classes" "$OUT/dex" "$OUT/apk/res"
|
||||||
|
|
||||||
# 0. every interface string must have its Galician translation
|
# 0. every interface string must have its Galician translation
|
||||||
python3 tools/check_i18n.py .
|
python3 tools/check_i18n.py .
|
||||||
@@ -46,13 +46,11 @@ java -cp "$APKTOOL:$OUT/tools" Disassemble "$OUT/dex/classes.dex" "$OUT/apk/smal
|
|||||||
|
|
||||||
# 3. resources + manifest, compiled by real aapt (v1, the tool of the Gingerbread era)
|
# 3. resources + manifest, compiled by real aapt (v1, the tool of the Gingerbread era)
|
||||||
cp AndroidManifest.xml "$OUT/apk/"
|
cp AndroidManifest.xml "$OUT/apk/"
|
||||||
cp -r res/values "$OUT/apk/res/"
|
cp -r res/* "$OUT/apk/res/" # launcher icon (ldpi..xhdpi) and the status-bar reminder icon
|
||||||
cp -r assets "$OUT/apk/" # Mozilla root certificates for the bundled TLS
|
cp -r assets "$OUT/apk/" # Mozilla root certificates for the bundled TLS
|
||||||
python3 tools/mkicon.py "$OUT/apk/res/drawable/icon.png" 48
|
|
||||||
python3 tools/mkicon.py "$OUT/apk/res/drawable-ldpi/icon.png" 36
|
|
||||||
cat > "$OUT/apk/apktool.yml" <<EOF
|
cat > "$OUT/apk/apktool.yml" <<EOF
|
||||||
version: 2.9.3
|
version: 2.9.3
|
||||||
apkFileName: GingerCal.apk
|
apkFileName: CalendarLite.apk
|
||||||
isFrameworkApk: false
|
isFrameworkApk: false
|
||||||
usesFramework:
|
usesFramework:
|
||||||
ids:
|
ids:
|
||||||
@@ -80,14 +78,14 @@ java -jar "$APKTOOL" -q b --use-aapt1 -api 8 -f -o "$OUT/unsigned.apk" "$OUT/apk
|
|||||||
# attributes; apksigner picks exactly that for --min-sdk-version 8. (jarsigner does not.)
|
# attributes; apksigner picks exactly that for --min-sdk-version 8. (jarsigner does not.)
|
||||||
if [ ! -f "$KS" ]; then
|
if [ ! -f "$KS" ]; then
|
||||||
keytool -genkeypair -keystore "$KS" -storetype PKCS12 -storepass android -keypass android \
|
keytool -genkeypair -keystore "$KS" -storetype PKCS12 -storepass android -keypass android \
|
||||||
-alias gingercal -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 10000 -dname "CN=GingerCal" 2>/dev/null
|
-alias gingercal -keyalg RSA -keysize 2048 -sigalg SHA1withRSA -validity 10000 -dname "CN=Calendar Lite" 2>/dev/null
|
||||||
fi
|
fi
|
||||||
java -jar "$APKSIGNER" sign --ks "$KS" --ks-key-alias gingercal --ks-pass pass:android --key-pass pass:android \
|
java -jar "$APKSIGNER" sign --ks "$KS" --ks-key-alias gingercal --ks-pass pass:android --key-pass pass:android \
|
||||||
--min-sdk-version 8 --v1-signing-enabled true --v2-signing-enabled false --v3-signing-enabled false \
|
--min-sdk-version 8 --v1-signing-enabled true --v2-signing-enabled false --v3-signing-enabled false \
|
||||||
--v4-signing-enabled false --out "$OUT/signed.apk" "$OUT/unsigned.apk"
|
--v4-signing-enabled false --out "$OUT/signed.apk" "$OUT/unsigned.apk"
|
||||||
python3 tools/zipalign.py "$OUT/signed.apk" GingerCal.apk
|
python3 tools/zipalign.py "$OUT/signed.apk" CalendarLite.apk
|
||||||
|
|
||||||
# 5. refuse to ship anything Android 2.3 would reject
|
# 5. refuse to ship anything Android 2.3 would reject
|
||||||
java -jar "$APKSIGNER" verify --min-sdk-version 8 --max-sdk-version 10 GingerCal.apk
|
java -jar "$APKSIGNER" verify --min-sdk-version 8 --max-sdk-version 10 CalendarLite.apk
|
||||||
python3 tools/check_dex.py GingerCal.apk
|
python3 tools/check_dex.py CalendarLite.apk
|
||||||
ls -l GingerCal.apk
|
ls -l CalendarLite.apk
|
||||||
|
After Width: | Height: | Size: 7.5 KiB |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 2.6 KiB |
|
After Width: | Height: | Size: 499 B |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 666 B |
|
After Width: | Height: | Size: 12 KiB |
|
After Width: | Height: | Size: 1.2 KiB |
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<string name="app_name">GingerCal</string>
|
<string name="app_name">Calendar Lite</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -431,7 +431,7 @@ public class ICal {
|
|||||||
List<String> l = new ArrayList<String>();
|
List<String> l = new ArrayList<String>();
|
||||||
l.add("BEGIN:VCALENDAR");
|
l.add("BEGIN:VCALENDAR");
|
||||||
l.add("VERSION:2.0");
|
l.add("VERSION:2.0");
|
||||||
l.add("PRODID:-//GingerCal//Android 2.3//EN");
|
l.add("PRODID:-//CalendarLite//Android 2.3//EN");
|
||||||
l.add("BEGIN:VEVENT");
|
l.add("BEGIN:VEVENT");
|
||||||
l.add("UID:" + uid);
|
l.add("UID:" + uid);
|
||||||
long now = System.currentTimeMillis();
|
long now = System.currentTimeMillis();
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ public final class L {
|
|||||||
static final String[] GL_DAYS_SHORT = {"", "dom", "lun", "mar", "mér", "xov", "ven", "sáb"};
|
static final String[] GL_DAYS_SHORT = {"", "dom", "lun", "mar", "mér", "xov", "ven", "sáb"};
|
||||||
|
|
||||||
public static DateFormatSymbols symbols() {
|
public static DateFormatSymbols symbols() {
|
||||||
if (!gl) return new DateFormatSymbols(Locale.getDefault());
|
if (!gl) return new DateFormatSymbols(Locale.US); // English names, whatever the phone's language
|
||||||
DateFormatSymbols s = new DateFormatSymbols(Locale.US);
|
DateFormatSymbols s = new DateFormatSymbols(Locale.US);
|
||||||
s.setMonths(GL_MONTHS);
|
s.setMonths(GL_MONTHS);
|
||||||
s.setShortMonths(GL_MONTHS_SHORT);
|
s.setShortMonths(GL_MONTHS_SHORT);
|
||||||
@@ -149,7 +149,7 @@ public final class L {
|
|||||||
put("Default calendar", "Calendario predeterminado");
|
put("Default calendar", "Calendario predeterminado");
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
put("GingerCal settings", "Configuración de GingerCal");
|
put("Calendar Lite settings", "Configuración de Calendar Lite");
|
||||||
put("Nextcloud server: your normal https:// address. This app brings its own modern TLS, so no bridge is needed, at home or away. Use an app password, not your real one.",
|
put("Nextcloud server: your normal https:// address. This app brings its own modern TLS, so no bridge is needed, at home or away. Use an app password, not your real one.",
|
||||||
"Servidor Nextcloud: o teu enderezo https:// de sempre. Esta aplicación trae o seu propio TLS moderno, así que non fai falta ningunha ponte, nin na casa nin fóra. Usa un contrasinal de aplicación, non o teu contrasinal real.");
|
"Servidor Nextcloud: o teu enderezo https:// de sempre. Esta aplicación trae o seu propio TLS moderno, así que non fai falta ningunha ponte, nin na casa nin fóra. Usa un contrasinal de aplicación, non o teu contrasinal real.");
|
||||||
put("Server address", "Enderezo do servidor");
|
put("Server address", "Enderezo do servidor");
|
||||||
|
|||||||
@@ -1,33 +0,0 @@
|
|||||||
#!/usr/bin/env python3
|
|
||||||
"""Draws the launcher icon: a little calendar page in ginger tones.
|
|
||||||
usage: mkicon.py out.png [size] (48 = mdpi, 36 = ldpi like the Galaxy Mini)"""
|
|
||||||
import sys
|
|
||||||
from PIL import Image, ImageDraw
|
|
||||||
|
|
||||||
S = 4 # supersample then shrink for smooth edges
|
|
||||||
W = 48 * S
|
|
||||||
img = Image.new("RGBA", (W, W), (0, 0, 0, 0))
|
|
||||||
d = ImageDraw.Draw(img)
|
|
||||||
# shadow
|
|
||||||
d.rounded_rectangle((5 * S, 7 * S, 45 * S, 46 * S), radius=6 * S, fill=(0, 0, 0, 90))
|
|
||||||
# page
|
|
||||||
d.rounded_rectangle((3 * S, 5 * S, 43 * S, 44 * S), radius=6 * S, fill=(250, 246, 238, 255))
|
|
||||||
# ginger header band
|
|
||||||
d.rounded_rectangle((3 * S, 5 * S, 43 * S, 17 * S), radius=6 * S, fill=(214, 110, 42, 255))
|
|
||||||
d.rectangle((3 * S, 11 * S, 43 * S, 17 * S), fill=(214, 110, 42, 255))
|
|
||||||
# binder rings
|
|
||||||
for x in (13, 33):
|
|
||||||
d.rounded_rectangle(((x - 2) * S, 2 * S, (x + 2) * S, 10 * S), radius=2 * S, fill=(90, 60, 40, 255))
|
|
||||||
# day grid, one day highlighted
|
|
||||||
for row in range(3):
|
|
||||||
for col in range(4):
|
|
||||||
x0 = (8 + col * 8.5) * S
|
|
||||||
y0 = (21 + row * 7.5) * S
|
|
||||||
box = (x0, y0, x0 + 5.5 * S, y0 + 5 * S)
|
|
||||||
if (row, col) == (1, 2):
|
|
||||||
d.rounded_rectangle(box, radius=1.5 * S, fill=(0, 130, 201, 255)) # Nextcloud blue
|
|
||||||
else:
|
|
||||||
d.rounded_rectangle(box, radius=1.5 * S, fill=(200, 188, 172, 255))
|
|
||||||
size = int(sys.argv[2]) if len(sys.argv) > 2 else 48
|
|
||||||
img = img.resize((size, size), Image.LANCZOS)
|
|
||||||
img.save(sys.argv[1] if len(sys.argv) > 1 else "icon.png", optimize=True)
|
|
||||||