Files
XoséandClaude Opus 5.5 748f6add54 Calendar Lite 1.5: GingerCal is now Calendar Lite, new icon and reminder icon
Renames the app (label, texts, User-Agent, PRODID, APK) and uses the new launcher icon pack
(ldpi to xhdpi, master in art/) instead of drawing one at build time. Reminders show a white
calendar glyph in the status bar (stat_notify, 18-48 px) instead of the system's colour
alarm clock, which didn't fit the Gingerbread bar. The package name, database and signing key
stay, so it installs over GingerCal and keeps its data.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-19 16:57:48 +02:00

79 lines
3.1 KiB
Java

package org.gingercal;
import java.io.IOException;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import org.gingercal.net.CertVerifier;
/**
* 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
* directly, any method (PROPFIND/REPORT included), with keep-alive. A pinned certificate (a
* self-signed server or an old bridge) is still honoured.
*/
public class NetHttp implements Http {
/** AsyncTask and IntentService threads have Android 2.x's small default stack, which the
* TLS handshake overflows: requests run here, on big-stack threads, and the caller waits. */
private static final ExecutorService NET = org.gingercal.net.Threads.pool("net", 3);
final String pin;
final int timeoutMs;
public NetHttp(String pin, int timeoutMs) {
this.pin = pin == null ? "" : pin;
this.timeoutMs = timeoutMs;
}
public Resp exec(final String method, final String url, final String[] headers, final String body,
final String contentType) throws IOException {
try {
return NET.submit(new Callable<Resp>() {
public Resp call() throws IOException {
return execHere(method, url, headers, body, contentType);
}
}).get();
} catch (ExecutionException e) {
Throwable c = e.getCause();
if (c instanceof IOException) throw (IOException) c;
if (c instanceof RuntimeException) throw (RuntimeException) c;
if (c instanceof Error) throw (Error) c;
throw new IOException(String.valueOf(c));
} catch (InterruptedException e) {
throw new IOException(L.t("Interrupted"));
}
}
Resp execHere(String method, String url, String[] headers, String body, String contentType)
throws IOException {
CertVerifier.setPin(pin);
Map<String, String> h = new LinkedHashMap<String, String>();
for (int i = 0; i + 1 < headers.length; i += 2) h.put(headers[i], headers[i + 1]);
byte[] data = null;
if (body != null) {
data = body.getBytes("UTF-8");
if (contentType != null) h.put("Content-Type", contentType);
}
org.gingercal.net.Http.Response r;
try {
// one hop at a time: Dav decides which redirects are safe to follow
r = org.gingercal.net.Http.requestNoRedirect(method, url, h, data, timeoutMs);
} catch (CertVerifier.Untrusted u) {
throw new DavException.UntrustedCert(u.fingerprint, u.subject, u.issuer, u.expires, u.changed);
}
Resp out = new Resp();
out.code = r.code;
out.etag = r.header("etag");
out.location = r.header("location");
out.body = r.readString();
return out;
}
public void close() {
// pooled connections are shared and expire by themselves
}
}