Import GingerCal 1.0 source
This commit is contained in:
commit
01f49852ab
116 files changed
+5079
No files matched your search
@@ -0,0 +1,373 @@
|
||||
package org.gingercal;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import android.app.Activity;
|
||||
import android.app.AlertDialog;
|
||||
import android.app.ProgressDialog;
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.Bundle;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.BaseAdapter;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.ListView;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.gingercal.Model.Cal;
|
||||
import org.gingercal.Model.Ev;
|
||||
|
||||
/** Agenda: upcoming events grouped by day. */
|
||||
public class MainActivity extends Activity {
|
||||
static final int M_SYNC = 1, M_NEW = 2, M_CALS = 3, M_SETTINGS = 4;
|
||||
|
||||
TextView status;
|
||||
ListView list;
|
||||
final List<Object> rows = new ArrayList<Object>();
|
||||
final Agenda adapter = new Agenda();
|
||||
boolean askedSetup;
|
||||
|
||||
final BroadcastReceiver rx = new BroadcastReceiver() {
|
||||
@Override
|
||||
public void onReceive(Context c, Intent i) {
|
||||
String state = i.getStringExtra("state");
|
||||
if ("start".equals(state)) {
|
||||
showStatus();
|
||||
return;
|
||||
}
|
||||
reload();
|
||||
String fp = i.getStringExtra("fp");
|
||||
if (fp != null) {
|
||||
DavException.UntrustedCert u = new DavException.UntrustedCert(fp, i.getStringExtra("subject"),
|
||||
i.getStringExtra("issuer"), i.getStringExtra("expires"), i.getBooleanExtra("mismatch", false));
|
||||
App.askTrust(MainActivity.this, u, new App.Trusted() {
|
||||
public void trusted(String f) {
|
||||
App.prefs(MainActivity.this).edit().putString("pin", f).commit();
|
||||
App.requestSync(MainActivity.this, true, false);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle b) {
|
||||
super.onCreate(b);
|
||||
setTitle("GingerCal");
|
||||
LinearLayout root = App.vbox(this);
|
||||
status = App.text(this, "", 12, 0xFFAAAAAA);
|
||||
int p = App.dp(this, 6);
|
||||
status.setPadding(p * 2, p, p * 2, p);
|
||||
status.setBackgroundColor(0xFF1C1C1C);
|
||||
root.addView(status, new LinearLayout.LayoutParams(-1, -2));
|
||||
list = new ListView(this);
|
||||
list.setAdapter(adapter);
|
||||
list.setOnItemClickListener(new AdapterView.OnItemClickListener() {
|
||||
public void onItemClick(AdapterView<?> parent, View v, int pos, long id) {
|
||||
Object o = rows.get(pos);
|
||||
if (o instanceof Ev) showEvent((Ev) o);
|
||||
}
|
||||
});
|
||||
root.addView(list, new LinearLayout.LayoutParams(-1, -1));
|
||||
setContentView(root);
|
||||
App.scheduleSync(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onResume() {
|
||||
super.onResume();
|
||||
registerReceiver(rx, new IntentFilter(SyncService.STATE));
|
||||
if (!App.configured(this)) {
|
||||
showStatus();
|
||||
if (!askedSetup) {
|
||||
askedSetup = true;
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
}
|
||||
return;
|
||||
}
|
||||
reload();
|
||||
long last = App.prefs(this).getLong("lastsync", 0);
|
||||
if (!SyncService.running && System.currentTimeMillis() - last > 15 * ICal.MIN) {
|
||||
App.requestSync(this, true, false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPause() {
|
||||
super.onPause();
|
||||
unregisterReceiver(rx);
|
||||
}
|
||||
|
||||
void showStatus() {
|
||||
if (!App.configured(this)) {
|
||||
status.setText("Not set up yet: press Menu > Settings.");
|
||||
return;
|
||||
}
|
||||
if (SyncService.running) {
|
||||
status.setText("Syncing…");
|
||||
status.setTextColor(0xFFAAAAAA);
|
||||
return;
|
||||
}
|
||||
long last = App.prefs(this).getLong("lastsync", 0);
|
||||
boolean err = App.prefs(this).getBoolean("error", false);
|
||||
String s = App.get(this, "status");
|
||||
String when = last == 0 ? "never" : App.day(last) + " " + App.time(this, last);
|
||||
if (err) {
|
||||
status.setText("Sync failed: " + s + "\nLast good sync: " + when);
|
||||
status.setTextColor(0xFFFF8A80);
|
||||
} else {
|
||||
status.setText("Synced " + when + (s.length() > 0 ? " · " + s : ""));
|
||||
status.setTextColor(0xFFAAAAAA);
|
||||
}
|
||||
}
|
||||
|
||||
void reload() {
|
||||
rows.clear();
|
||||
long today = ICal.startOfDay(System.currentTimeMillis());
|
||||
List<Ev> evs = Db.get(this).agenda(today, ICal.addDays(today, App.ahead(this) + 1));
|
||||
long lastDay = -1;
|
||||
for (Ev e : evs) {
|
||||
long d = Math.max(ICal.startOfDay(e.start), today);
|
||||
if (d != lastDay) {
|
||||
rows.add(Long.valueOf(d));
|
||||
lastDay = d;
|
||||
}
|
||||
rows.add(e);
|
||||
}
|
||||
if (rows.isEmpty() && App.configured(this)) rows.add("No upcoming events. Menu > New event, or Menu > Sync.");
|
||||
adapter.notifyDataSetChanged();
|
||||
showStatus();
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------- menu
|
||||
|
||||
@Override
|
||||
public boolean onCreateOptionsMenu(Menu m) {
|
||||
icon(m.add(0, M_SYNC, 0, "Sync"), "ic_menu_refresh");
|
||||
icon(m.add(0, M_NEW, 1, "New event"), "ic_menu_add");
|
||||
icon(m.add(0, M_CALS, 2, "Calendars"), "ic_menu_agenda");
|
||||
icon(m.add(0, M_SETTINGS, 3, "Settings"), "ic_menu_preferences");
|
||||
return true;
|
||||
}
|
||||
|
||||
void icon(MenuItem i, String name) {
|
||||
int id = App.sysDrawable(this, name);
|
||||
if (id != 0) i.setIcon(id);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case M_SYNC:
|
||||
if (App.configured(this)) App.requestSync(this, true, false);
|
||||
else startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
case M_NEW:
|
||||
if (writableCalendars().isEmpty()) {
|
||||
App.toast(this, "Sync first: no writable calendar yet.");
|
||||
} else {
|
||||
startActivity(new Intent(this, EditActivity.class));
|
||||
}
|
||||
return true;
|
||||
case M_CALS:
|
||||
startActivity(new Intent(this, CalendarsActivity.class));
|
||||
return true;
|
||||
case M_SETTINGS:
|
||||
startActivity(new Intent(this, SettingsActivity.class));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Cal> writableCalendars() {
|
||||
List<Cal> out = new ArrayList<Cal>();
|
||||
for (Cal c : Db.get(this).calendarList()) if (c.enabled && c.writable) out.add(c);
|
||||
return out;
|
||||
}
|
||||
|
||||
// -------------------------------------------------------------- details
|
||||
|
||||
void showEvent(final Ev e) {
|
||||
StringBuilder m = new StringBuilder();
|
||||
m.append(App.day(e.start)).append("\n").append(App.when(this, e));
|
||||
if (e.location.length() > 0) m.append("\n\n").append(e.location);
|
||||
if (e.descr.length() > 0) m.append("\n\n").append(e.descr);
|
||||
Cal cal = Db.get(this).calendars().get(e.cal);
|
||||
if (cal != null) m.append("\n\nCalendar: ").append(cal.name);
|
||||
if (e.recurring) m.append("\n\nRepeating event: edit the series in Nextcloud. Delete removes the whole series.");
|
||||
AlertDialog.Builder b = new AlertDialog.Builder(this)
|
||||
.setTitle(e.title.length() > 0 ? e.title : "(no title)")
|
||||
.setMessage(m.toString())
|
||||
.setNegativeButton("Close", null);
|
||||
if (e.writable && !e.recurring) {
|
||||
b.setPositiveButton("Edit", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int w) {
|
||||
Intent i = new Intent(MainActivity.this, EditActivity.class);
|
||||
i.putExtra("id", e.id);
|
||||
startActivity(i);
|
||||
}
|
||||
});
|
||||
}
|
||||
if (e.writable) {
|
||||
b.setNeutralButton("Delete", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int w) {
|
||||
confirmDelete(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
b.show();
|
||||
}
|
||||
|
||||
void confirmDelete(final Ev e) {
|
||||
new AlertDialog.Builder(this)
|
||||
.setTitle("Delete event?")
|
||||
.setMessage(e.recurring ? "This deletes EVERY occurrence of \"" + e.title + "\"." : "\"" + e.title + "\" will be deleted on the server.")
|
||||
.setPositiveButton("Delete", new DialogInterface.OnClickListener() {
|
||||
public void onClick(DialogInterface d, int w) {
|
||||
new DeleteTask(e).execute();
|
||||
}
|
||||
})
|
||||
.setNegativeButton("Cancel", null)
|
||||
.show();
|
||||
}
|
||||
|
||||
class DeleteTask extends AsyncTask<Void, Void, Throwable> {
|
||||
final Ev e;
|
||||
ProgressDialog pd;
|
||||
|
||||
DeleteTask(Ev e) {
|
||||
this.e = e;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPreExecute() {
|
||||
pd = ProgressDialog.show(MainActivity.this, null, "Deleting…");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Throwable doInBackground(Void... v) {
|
||||
Dav d = null;
|
||||
try {
|
||||
d = App.dav(MainActivity.this);
|
||||
d.delete(e.href, e.etag);
|
||||
return null;
|
||||
} catch (Throwable t) {
|
||||
return t;
|
||||
} finally {
|
||||
if (d != null) d.close();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPostExecute(Throwable t) {
|
||||
try {
|
||||
pd.dismiss();
|
||||
} catch (RuntimeException ignored) {
|
||||
}
|
||||
if (t != null) {
|
||||
new AlertDialog.Builder(MainActivity.this).setTitle("Delete failed")
|
||||
.setMessage(App.describe(t)).setPositiveButton("OK", null).show();
|
||||
return;
|
||||
}
|
||||
App.requestSync(MainActivity.this, true, false);
|
||||
for (int i = rows.size() - 1; i >= 0; i--) {
|
||||
Object o = rows.get(i);
|
||||
if (o instanceof Ev && ((Ev) o).href.equals(e.href)) rows.remove(i);
|
||||
}
|
||||
adapter.notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------- adapter
|
||||
|
||||
static class Holder {
|
||||
View bar;
|
||||
TextView time, title, where;
|
||||
}
|
||||
|
||||
class Agenda extends BaseAdapter {
|
||||
public int getCount() {
|
||||
return rows.size();
|
||||
}
|
||||
|
||||
public Object getItem(int p) {
|
||||
return rows.get(p);
|
||||
}
|
||||
|
||||
public long getItemId(int p) {
|
||||
return p;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getViewTypeCount() {
|
||||
return 2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemViewType(int p) {
|
||||
return rows.get(p) instanceof Ev ? 1 : 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean areAllItemsEnabled() {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isEnabled(int p) {
|
||||
return rows.get(p) instanceof Ev;
|
||||
}
|
||||
|
||||
public View getView(int p, View convert, ViewGroup parent) {
|
||||
Object o = rows.get(p);
|
||||
Context c = MainActivity.this;
|
||||
if (!(o instanceof Ev)) {
|
||||
TextView h = convert instanceof TextView ? (TextView) convert : App.text(c, "", 13, 0xFFFFFFFF);
|
||||
int pad = App.dp(c, 6);
|
||||
h.setPadding(pad * 2, pad, pad, pad);
|
||||
h.setBackgroundColor(0xFF2E3B44);
|
||||
h.setText(o instanceof Long ? App.dayHeader(((Long) o).longValue()) : String.valueOf(o));
|
||||
return h;
|
||||
}
|
||||
Ev e = (Ev) o;
|
||||
Holder hd;
|
||||
View v = convert;
|
||||
if (v == null || !(v.getTag() instanceof Holder)) {
|
||||
hd = new Holder();
|
||||
LinearLayout row = new LinearLayout(c);
|
||||
row.setOrientation(LinearLayout.HORIZONTAL);
|
||||
hd.bar = new View(c);
|
||||
row.addView(hd.bar, new LinearLayout.LayoutParams(App.dp(c, 5), -1));
|
||||
LinearLayout col = App.vbox(c);
|
||||
int pad = App.dp(c, 6);
|
||||
col.setPadding(pad * 2, pad, pad, pad);
|
||||
hd.time = App.text(c, "", 12, 0xFFB0BEC5);
|
||||
hd.title = App.text(c, "", 16, 0xFFFFFFFF);
|
||||
hd.where = App.text(c, "", 12, 0xFF90A4AE);
|
||||
col.addView(hd.time);
|
||||
col.addView(hd.title);
|
||||
col.addView(hd.where);
|
||||
row.addView(col, new LinearLayout.LayoutParams(-1, -2));
|
||||
row.setTag(hd);
|
||||
v = row;
|
||||
} else {
|
||||
hd = (Holder) v.getTag();
|
||||
}
|
||||
hd.bar.setBackgroundColor(e.color);
|
||||
hd.time.setText(App.when(c, e) + (e.recurring ? " (repeats)" : ""));
|
||||
hd.title.setText(e.title.length() > 0 ? e.title : "(no title)");
|
||||
hd.where.setText(e.location);
|
||||
hd.where.setVisibility(e.location.length() > 0 ? View.VISIBLE : View.GONE);
|
||||
return v;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user