Files
calendar-lite/android/org/gingercal/SettingsActivity.java
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

263 lines
11 KiB
Java

package org.gingercal;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.ScrollView;
import android.widget.TextView;
import org.gingercal.Model.Cal;
/** Account, TLS pin, sync interval and reminder settings. */
public class SettingsActivity extends Activity {
EditText url, user, pass, pin, ahead;
Button intervalBtn, remindBtn, langBtn;
int interval, remind;
@Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setTitle(L.t("Calendar Lite settings"));
LinearLayout f = App.vbox(this);
int p = App.dp(this, 8);
f.setPadding(p, p, p, p);
f.addView(App.text(this, L.t("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."), 12, 0xFFB0BEC5));
url = field(f, L.t("Server address"), "https://cloud.example.com", 1 | 0x10 /* URI */);
user = field(f, L.t("User name"), "", 1 | 0x80000 /* no suggestions */);
pass = field(f, L.t("App password"), "", 1 | 0x80 /* password */);
pin = field(f, L.t("Pinned certificate SHA-256 (filled in for you)"), L.t("optional"), 1 | 0x80000);
ahead = field(f, L.t("Days ahead to sync"), "60", 2 /* number */);
intervalBtn = new Button(this);
f.addView(intervalBtn, new LinearLayout.LayoutParams(-1, -2));
remindBtn = new Button(this);
f.addView(remindBtn, new LinearLayout.LayoutParams(-1, -2));
langBtn = new Button(this);
f.addView(langBtn, new LinearLayout.LayoutParams(-1, -2));
Button test = new Button(this);
test.setText(L.t("Save and test connection"));
f.addView(test, new LinearLayout.LayoutParams(-1, -2));
Button resync = new Button(this);
resync.setText(L.t("Re-download everything"));
f.addView(resync, new LinearLayout.LayoutParams(-1, -2));
ScrollView sv = new ScrollView(this);
sv.addView(f);
setContentView(sv);
url.setText(App.get(this, "url"));
user.setText(App.get(this, "user"));
pass.setText(App.get(this, "pass"));
pin.setText(App.get(this, "pin"));
ahead.setText(String.valueOf(App.ahead(this)));
interval = App.interval(this);
remind = App.remind(this);
labels();
intervalBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final String[] l = new String[App.INTERVALS.length];
for (int i = 0; i < l.length; i++) l[i] = intervalLabel(App.INTERVALS[i]);
new AlertDialog.Builder(SettingsActivity.this).setTitle(L.t("Background sync"))
.setItems(l, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int w) {
interval = App.INTERVALS[w];
labels();
}
}).show();
}
});
remindBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
final int[] opts = {-1, 0, 5, 10, 15, 30, 60};
String[] l = new String[opts.length];
for (int i = 0; i < l.length; i++) l[i] = opts[i] < 0 ? L.t("Off") : App.alarmLabel(opts[i]);
new AlertDialog.Builder(SettingsActivity.this).setTitle(L.t("Phone notification"))
.setItems(l, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int w) {
remind = opts[w];
labels();
}
}).show();
}
});
langBtn.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
new AlertDialog.Builder(SettingsActivity.this).setTitle("Lingua / Language")
.setItems(new String[] {"Galego", "English"}, new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int w) {
boolean toGl = w == 0;
if (toGl == L.gl) return;
save();
App.prefs(SettingsActivity.this).edit().putString("lang", toGl ? "gl" : "en").commit();
L.gl = toGl;
GingerApp.tlsTexts();
finish(); // reopen so every label is redrawn in the new language
startActivity(new Intent(SettingsActivity.this, SettingsActivity.class));
}
}).show();
}
});
test.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
confirmAndTest();
}
});
resync.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
save();
Db.get(SettingsActivity.this).forgetSyncState();
App.requestSync(SettingsActivity.this, true, true);
App.toast(SettingsActivity.this, L.t("Re-downloading in the background"));
finish();
}
});
}
EditText field(LinearLayout f, String label, String hint, int type) {
TextView l = App.text(this, label, 13, 0xFFFFFFFF);
l.setPadding(App.dp(this, 4), App.dp(this, 8), 0, 0);
f.addView(l);
EditText e = new EditText(this);
e.setHint(hint);
e.setSingleLine(true);
e.setInputType(type);
f.addView(e, new LinearLayout.LayoutParams(-1, -2));
return e;
}
static String intervalLabel(int h) {
return h == 0 ? L.t("Manual only") : h == 1 ? L.t("Every hour") : L.f("Every %d hours", h);
}
void labels() {
intervalBtn.setText(L.f("Background sync: %s", intervalLabel(interval)));
remindBtn.setText(L.f("Notification: %s", remind < 0 ? L.t("off") : App.alarmLabel(remind)));
langBtn.setText(L.f("Language: %s", L.gl ? "Galego" : "English"));
}
static String txt(TextView t) {
return t.getText().toString().trim();
}
@Override
protected void onPause() {
super.onPause();
if (txt(url).length() > 0) save();
}
void save() {
String oldAccount = App.get(this, "url") + "|" + App.get(this, "user");
int days = 60;
try {
days = Math.max(7, Math.min(365, Integer.parseInt(txt(ahead))));
} catch (NumberFormatException ignored) {
}
String u = txt(url);
if (u.length() > 0) u = Dav.normalize(u);
App.prefs(this).edit()
.putString("url", u)
.putString("user", txt(user))
.putString("pass", pass.getText().toString())
.putString("pin", Tls.normPin(txt(pin)).length() == 64 ? txt(pin) : "")
.putInt("ahead", days)
.putInt("interval", interval)
.putInt("remind", remind)
.commit();
if (!oldAccount.equals(u + "|" + txt(user))) {
Db.get(this).clearAll();
App.prefs(this).edit().putLong("lastsync", 0).putLong("win_from", 0).putLong("win_to", 0)
.putString("status", "").commit();
}
App.scheduleSync(this);
Reminders.schedule(this);
}
void confirmAndTest() {
String u = txt(url);
if (u.length() == 0 || txt(user).length() == 0) {
App.toast(this, L.t("Server address and user name are required."));
return;
}
if (Dav.normalize(u).startsWith("http://") && !App.isLan(u)) {
new AlertDialog.Builder(this).setTitle(L.t("Unencrypted connection"))
.setMessage(L.t("http:// sends your password in clear text over the internet. Use the https:// address instead, or only use http:// to a LAN address."))
.setPositiveButton(L.t("Use anyway"), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int w) {
save();
new TestTask().execute();
}
})
.setNegativeButton(L.t("Cancel"), null).show();
return;
}
save();
new TestTask().execute();
}
class TestTask extends AsyncTask<Void, Void, Object> {
ProgressDialog pd;
@Override
protected void onPreExecute() {
pd = ProgressDialog.show(SettingsActivity.this, null, L.t("Connecting\u2026"));
}
@Override
protected Object doInBackground(Void... v) {
Dav d = null;
try {
d = App.dav(SettingsActivity.this);
return d.calendars(d.calendarHome());
} catch (Throwable t) {
return t;
} finally {
if (d != null) d.close();
}
}
@SuppressWarnings("unchecked")
@Override
protected void onPostExecute(Object r) {
try {
pd.dismiss();
} catch (RuntimeException ignored) {
}
if (r instanceof DavException.UntrustedCert) {
App.askTrust(SettingsActivity.this, (DavException.UntrustedCert) r, new App.Trusted() {
public void trusted(String fp) {
pin.setText(fp);
save();
new TestTask().execute();
}
});
return;
}
if (r instanceof Throwable) {
new AlertDialog.Builder(SettingsActivity.this).setTitle(L.t("Connection failed"))
.setMessage(App.describe((Throwable) r)).setPositiveButton(L.t("OK"), null).show();
return;
}
List<Cal> cals = (List<Cal>) r;
StringBuilder s = new StringBuilder(L.f("Connected. Calendars found: %d\n", cals.size()));
for (Cal c : cals) s.append("\n\u2022 ").append(c.name).append(c.writable ? "" : " " + L.t("(read-only)"));
new AlertDialog.Builder(SettingsActivity.this).setTitle(L.t("It works"))
.setMessage(s.toString())
.setPositiveButton(L.t("Sync now"), new DialogInterface.OnClickListener() {
public void onClick(DialogInterface d, int w) {
App.requestSync(SettingsActivity.this, true, false);
finish();
}
}).show();
}
}
}