Compare commits
5
Commits
v1.5
...
775aa04cdf
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
775aa04cdf | ||
|
|
9442afb2bd | ||
|
|
a625e20a56 | ||
|
|
82acdb79f6 | ||
|
|
478fc60527 |
No files matched your search
+2
-2
@@ -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="6"
|
android:versionCode="7"
|
||||||
android:versionName="1.5">
|
android:versionName="1.6">
|
||||||
|
|
||||||
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
|
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="10" />
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
# Calendar Lite — 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 Plexamp Lite, 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
|
||||||
@@ -139,14 +139,16 @@ you should turn `proxy_ssl_verify` on.
|
|||||||
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 CalendarLite.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, `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):
|
||||||
|
|||||||
@@ -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 = "CalendarLite/1.5 (Android 2.3)";
|
org.gingercal.net.Http.USER_AGENT = "CalendarLite/1.6 (Android 2.3)";
|
||||||
tlsTexts();
|
tlsTexts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -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);
|
||||||
|
|||||||
Reference in new issue
Block a user