690 lines
25 KiB
Java
690 lines
25 KiB
Java
package org.gingercal;
|
|
|
|
import java.text.SimpleDateFormat;
|
|
import java.util.ArrayList;
|
|
import java.util.Date;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Locale;
|
|
import java.util.Map;
|
|
|
|
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.content.res.Configuration;
|
|
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;
|
|
|
|
/** Main screen: a month grid with the selected day's events, or the upcoming-events agenda. */
|
|
public class MainActivity extends Activity implements MonthView.Listener {
|
|
static final int M_SYNC = 1, M_NEW = 2, M_CALS = 3, M_SETTINGS = 4, M_VIEW = 5, M_TODAY = 6;
|
|
|
|
LinearLayout root, body;
|
|
TextView status, monthTitle;
|
|
ListView agendaList, dayList;
|
|
MonthView monthView;
|
|
final List<Object> agendaRows = new ArrayList<Object>(), dayRows = new ArrayList<Object>();
|
|
final Rows agendaAdapter = new Rows(agendaRows), dayAdapter = new Rows(dayRows);
|
|
boolean askedSetup, monthMode, uiGl;
|
|
long selectedDay;
|
|
List<Ev> monthEvents = new ArrayList<Ev>();
|
|
/** Months outside the synced window, fetched from the server on demand (key: grid start). */
|
|
final Map<Long, List<Ev>> fetched = new HashMap<Long, List<Ev>>();
|
|
long fetching = -1;
|
|
String fetchError;
|
|
|
|
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(true);
|
|
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);
|
|
requestWindowFeature(1 /* Window.FEATURE_NO_TITLE: every pixel counts at 240x320 */);
|
|
uiGl = L.gl;
|
|
monthMode = !"agenda".equals(App.get(this, "view"));
|
|
selectedDay = ICal.startOfDay(System.currentTimeMillis());
|
|
|
|
root = App.vbox(this);
|
|
status = App.text(this, "", 11, 0xFFAAAAAA);
|
|
int p = App.dp(this, 4);
|
|
status.setPadding(p * 2, p, p * 2, p);
|
|
status.setBackgroundColor(0xFF1C1C1C);
|
|
root.addView(status, new LinearLayout.LayoutParams(-1, -2));
|
|
|
|
agendaList = list(agendaAdapter, agendaRows);
|
|
dayList = list(dayAdapter, dayRows);
|
|
monthView = new MonthView(this);
|
|
monthView.listener = this;
|
|
monthView.setMonth(selectedDay);
|
|
monthView.setSelected(selectedDay);
|
|
monthTitle = App.text(this, "", 15, 0xFFFFFFFF);
|
|
monthTitle.setGravity(17 /* Gravity.CENTER */);
|
|
monthTitle.setOnClickListener(new View.OnClickListener() {
|
|
public void onClick(View v) {
|
|
goToday();
|
|
}
|
|
});
|
|
buildBody();
|
|
setContentView(root);
|
|
App.scheduleSync(this);
|
|
}
|
|
|
|
ListView list(Rows adapter, final List<Object> rows) {
|
|
ListView l = new ListView(this);
|
|
l.setAdapter(adapter);
|
|
l.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);
|
|
}
|
|
});
|
|
return l;
|
|
}
|
|
|
|
static void detach(View v) {
|
|
// Views can only have one parent; LinearLayout is the only container we use.
|
|
Object parent = v.getParent();
|
|
if (parent instanceof LinearLayout) ((LinearLayout) parent).removeView(v);
|
|
}
|
|
|
|
/** (Re)builds the area under the status line for the current mode and orientation. */
|
|
void buildBody() {
|
|
if (body != null) root.removeView(body);
|
|
detach(agendaList);
|
|
detach(dayList);
|
|
detach(monthView);
|
|
detach(monthTitle);
|
|
body = App.vbox(this);
|
|
if (!monthMode) {
|
|
body.addView(agendaList, new LinearLayout.LayoutParams(-1, -1));
|
|
} else {
|
|
LinearLayout nav = new LinearLayout(this);
|
|
nav.setOrientation(LinearLayout.HORIZONTAL);
|
|
nav.setBackgroundColor(0xFF1C2329);
|
|
nav.addView(navArrow("\u00ab", -1), new LinearLayout.LayoutParams(App.dp(this, 56), App.dp(this, 34)));
|
|
nav.addView(monthTitle, new LinearLayout.LayoutParams(0, App.dp(this, 34), 1f));
|
|
nav.addView(navArrow("\u00bb", 1), new LinearLayout.LayoutParams(App.dp(this, 56), App.dp(this, 34)));
|
|
body.addView(nav, new LinearLayout.LayoutParams(-1, -2));
|
|
boolean landscape = getResources().getDisplayMetrics().widthPixels
|
|
> getResources().getDisplayMetrics().heightPixels;
|
|
if (landscape) {
|
|
LinearLayout row = new LinearLayout(this);
|
|
row.setOrientation(LinearLayout.HORIZONTAL);
|
|
row.addView(monthView, new LinearLayout.LayoutParams(0, -1, 1.25f));
|
|
row.addView(dayList, new LinearLayout.LayoutParams(0, -1, 1f));
|
|
body.addView(row, new LinearLayout.LayoutParams(-1, -1));
|
|
} else {
|
|
body.addView(monthView, new LinearLayout.LayoutParams(-1, -2));
|
|
body.addView(dayList, new LinearLayout.LayoutParams(-1, -1));
|
|
}
|
|
}
|
|
root.addView(body, new LinearLayout.LayoutParams(-1, -1));
|
|
}
|
|
|
|
TextView navArrow(String label, final int delta) {
|
|
TextView b = App.text(this, label, 20, 0xFF3D8EF0); // « »: Latin-1, always in the 2.3 font
|
|
b.setGravity(17 /* Gravity.CENTER */);
|
|
int sel = App.sysDrawable(this, "list_selector_background");
|
|
if (sel != 0) b.setBackgroundResource(sel); // press highlight
|
|
b.setOnClickListener(new View.OnClickListener() {
|
|
public void onClick(View v) {
|
|
monthSwiped(delta);
|
|
}
|
|
});
|
|
return b;
|
|
}
|
|
|
|
@Override
|
|
public void onConfigurationChanged(Configuration c) {
|
|
super.onConfigurationChanged(c);
|
|
buildBody();
|
|
}
|
|
|
|
@Override
|
|
protected void onResume() {
|
|
super.onResume();
|
|
if (uiGl != L.gl) { // language switched in Settings: rebuild every label
|
|
finish();
|
|
startActivity(new Intent(this, MainActivity.class));
|
|
return;
|
|
}
|
|
registerReceiver(rx, new IntentFilter(SyncService.STATE));
|
|
if (!App.configured(this)) {
|
|
showStatus();
|
|
if (!askedSetup) {
|
|
askedSetup = true;
|
|
startActivity(new Intent(this, SettingsActivity.class));
|
|
}
|
|
return;
|
|
}
|
|
long now = ICal.startOfDay(System.currentTimeMillis());
|
|
if (now != monthView.today) monthView.setMonth(monthView.days[15]); // date rolled over
|
|
reload(true);
|
|
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(L.t("Not set up yet: press Menu > Settings."));
|
|
return;
|
|
}
|
|
if (SyncService.running) {
|
|
status.setText(L.t("Syncing\u2026"));
|
|
status.setTextColor(0xFFAAAAAA);
|
|
return;
|
|
}
|
|
if (monthMode && fetching >= 0) {
|
|
status.setText(L.f("Loading %s from the server\u2026", monthTitle.getText()));
|
|
status.setTextColor(0xFFAAAAAA);
|
|
return;
|
|
}
|
|
if (monthMode && fetchError != null) {
|
|
status.setText(L.f("Couldn't load this month: %s", fetchError));
|
|
status.setTextColor(0xFFFF8A80);
|
|
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 ? L.t("never") : App.day(last) + " " + App.time(this, last);
|
|
if (err) {
|
|
status.setText(L.f("Sync failed: %s\nLast good sync: %s", s, when));
|
|
status.setTextColor(0xFFFF8A80);
|
|
} else {
|
|
status.setText(L.f("Synced %s", when) + (s.length() > 0 ? " \u00b7 " + s : ""));
|
|
status.setTextColor(0xFFAAAAAA);
|
|
}
|
|
}
|
|
|
|
void reload(boolean allowFetch) {
|
|
if (monthMode) loadMonth(allowFetch);
|
|
else loadAgenda();
|
|
showStatus();
|
|
}
|
|
|
|
// -------------------------------------------------------------- agenda
|
|
|
|
void loadAgenda() {
|
|
agendaRows.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) {
|
|
agendaRows.add(Long.valueOf(d));
|
|
lastDay = d;
|
|
}
|
|
agendaRows.add(e);
|
|
}
|
|
if (agendaRows.isEmpty() && App.configured(this)) {
|
|
agendaRows.add(L.t("No upcoming events. Menu > New event, or Menu > Sync."));
|
|
}
|
|
agendaAdapter.notifyDataSetChanged();
|
|
}
|
|
|
|
// --------------------------------------------------------------- month
|
|
|
|
void loadMonth(boolean allowFetch) {
|
|
long gs = monthView.gridStart(), ge = monthView.gridEnd();
|
|
monthTitle.setText(L.monthTitle(monthView.days[15]));
|
|
long wf = App.prefs(this).getLong("win_from", 0), wt = App.prefs(this).getLong("win_to", 0);
|
|
if (wf > 0 && gs >= wf && ge <= wt) {
|
|
fetchError = null;
|
|
showMonthEvents(Db.get(this).agenda(gs, ge));
|
|
} else {
|
|
List<Ev> cached = fetched.get(Long.valueOf(gs));
|
|
showMonthEvents(cached != null ? cached : Db.get(this).agenda(gs, ge));
|
|
if (allowFetch && fetching != gs && App.configured(this)) new FetchTask(gs, ge).execute();
|
|
}
|
|
}
|
|
|
|
void showMonthEvents(List<Ev> evs) {
|
|
monthEvents = evs;
|
|
Map<Long, List<Integer>> marks = new HashMap<Long, List<Integer>>();
|
|
long gs = monthView.gridStart(), ge = monthView.gridEnd();
|
|
for (Ev e : evs) {
|
|
long d = Math.max(ICal.startOfDay(e.start), gs);
|
|
for (int guard = 0; d < ge && guard < 42; guard++) {
|
|
if (!(occupies(e, d))) break;
|
|
List<Integer> l = marks.get(Long.valueOf(d));
|
|
if (l == null) {
|
|
l = new ArrayList<Integer>();
|
|
marks.put(Long.valueOf(d), l);
|
|
}
|
|
l.add(Integer.valueOf(e.color));
|
|
d = ICal.addDays(d, 1);
|
|
}
|
|
}
|
|
monthView.setMarks(marks);
|
|
showDay();
|
|
}
|
|
|
|
static boolean occupies(Ev e, long d) {
|
|
return ICal.onDay(e.start, e.end, d);
|
|
}
|
|
|
|
void showDay() {
|
|
dayRows.clear();
|
|
int n = 0;
|
|
for (Ev e : monthEvents) {
|
|
if (occupies(e, selectedDay)) {
|
|
dayRows.add(e);
|
|
n++;
|
|
}
|
|
}
|
|
if (n == 0) dayRows.add(L.f("Nothing on %s. Long-press a day to add an event.", App.day(selectedDay)));
|
|
dayAdapter.notifyDataSetChanged();
|
|
}
|
|
|
|
public void daySelected(long day) {
|
|
selectedDay = day;
|
|
monthView.setSelected(day);
|
|
if (!monthView.inMonth(day)) {
|
|
monthView.setMonth(day);
|
|
reload(true);
|
|
} else {
|
|
showDay();
|
|
}
|
|
}
|
|
|
|
public void dayLongPressed(long day) {
|
|
daySelected(day);
|
|
newEvent(day);
|
|
}
|
|
|
|
public void monthSwiped(int delta) {
|
|
java.util.Calendar c = java.util.Calendar.getInstance();
|
|
c.setTimeInMillis(monthView.days[15]);
|
|
c.set(java.util.Calendar.DAY_OF_MONTH, 1);
|
|
c.add(java.util.Calendar.MONTH, delta);
|
|
long first = c.getTimeInMillis();
|
|
monthView.setMonth(first);
|
|
long today = ICal.startOfDay(System.currentTimeMillis());
|
|
selectedDay = monthView.inMonth(today) ? today : ICal.startOfDay(first);
|
|
monthView.setSelected(selectedDay);
|
|
fetchError = null;
|
|
reload(true);
|
|
}
|
|
|
|
void goToday() {
|
|
selectedDay = ICal.startOfDay(System.currentTimeMillis());
|
|
monthView.setMonth(selectedDay);
|
|
monthView.setSelected(selectedDay);
|
|
reload(true);
|
|
}
|
|
|
|
/** Loads a month outside the synced window straight from the server. */
|
|
class FetchTask extends AsyncTask<Void, Void, Object> {
|
|
final long gs, ge;
|
|
|
|
FetchTask(long gs, long ge) {
|
|
this.gs = gs;
|
|
this.ge = ge;
|
|
}
|
|
|
|
@Override
|
|
protected void onPreExecute() {
|
|
fetching = gs;
|
|
fetchError = null;
|
|
showStatus();
|
|
}
|
|
|
|
@Override
|
|
protected Object doInBackground(Void... v) {
|
|
Dav d = null;
|
|
try {
|
|
d = App.dav(MainActivity.this);
|
|
List<Ev> out = new ArrayList<Ev>();
|
|
for (Cal c : Db.get(MainActivity.this).calendarList()) {
|
|
if (!c.enabled) continue;
|
|
for (Ev e : d.events(c, gs, ge)) {
|
|
e.color = c.color;
|
|
e.writable = c.writable;
|
|
out.add(e);
|
|
}
|
|
}
|
|
java.util.Collections.sort(out, new java.util.Comparator<Ev>() {
|
|
public int compare(Ev a, Ev b) {
|
|
return a.start < b.start ? -1 : a.start > b.start ? 1 : 0;
|
|
}
|
|
});
|
|
return out;
|
|
} catch (Throwable t) {
|
|
return t;
|
|
} finally {
|
|
if (d != null) d.close();
|
|
}
|
|
}
|
|
|
|
@SuppressWarnings("unchecked")
|
|
@Override
|
|
protected void onPostExecute(Object r) {
|
|
if (fetching == gs) fetching = -1;
|
|
if (r instanceof Throwable) {
|
|
fetchError = App.describe((Throwable) r);
|
|
} else {
|
|
fetched.put(Long.valueOf(gs), (List<Ev>) r);
|
|
if (monthMode && monthView.gridStart() == gs) showMonthEvents((List<Ev>) r);
|
|
}
|
|
showStatus();
|
|
}
|
|
}
|
|
|
|
// ---------------------------------------------------------------- menu
|
|
|
|
@Override
|
|
public boolean onCreateOptionsMenu(Menu m) {
|
|
icon(m.add(0, M_VIEW, 0, L.t("View")), "ic_menu_month");
|
|
icon(m.add(0, M_NEW, 1, L.t("New event")), "ic_menu_add");
|
|
icon(m.add(0, M_SYNC, 2, L.t("Sync")), "ic_menu_refresh");
|
|
icon(m.add(0, M_TODAY, 3, L.t("Today")), "ic_menu_today");
|
|
icon(m.add(0, M_CALS, 4, L.t("Calendars")), "ic_menu_agenda");
|
|
icon(m.add(0, M_SETTINGS, 5, L.t("Settings")), "ic_menu_preferences");
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onPrepareOptionsMenu(Menu m) {
|
|
MenuItem v = m.findItem(M_VIEW);
|
|
if (v != null) {
|
|
v.setTitle(monthMode ? L.t("Agenda") : L.t("Month"));
|
|
icon(v, monthMode ? "ic_menu_agenda" : "ic_menu_month");
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void icon(MenuItem i, String name) {
|
|
int id = App.sysDrawable(this, name);
|
|
if (id == 0) id = App.sysDrawable(this, "ic_menu_my_calendar");
|
|
if (id != 0) i.setIcon(id);
|
|
}
|
|
|
|
@Override
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
switch (item.getItemId()) {
|
|
case M_VIEW:
|
|
monthMode = !monthMode;
|
|
App.prefs(this).edit().putString("view", monthMode ? "month" : "agenda").commit();
|
|
buildBody();
|
|
reload(true);
|
|
return true;
|
|
case M_TODAY:
|
|
if (monthMode) goToday();
|
|
else agendaList.setSelection(0);
|
|
return true;
|
|
case M_SYNC:
|
|
if (App.configured(this)) {
|
|
fetched.clear();
|
|
App.requestSync(this, true, false);
|
|
} else {
|
|
startActivity(new Intent(this, SettingsActivity.class));
|
|
}
|
|
return true;
|
|
case M_NEW:
|
|
newEvent(monthMode ? selectedDay : -1);
|
|
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;
|
|
}
|
|
|
|
void newEvent(long day) {
|
|
if (writableCalendars().isEmpty()) {
|
|
App.toast(this, L.t("Sync first: no writable calendar yet."));
|
|
return;
|
|
}
|
|
Intent i = new Intent(this, EditActivity.class);
|
|
if (day >= 0) i.putExtra("day", day);
|
|
fetched.clear();
|
|
startActivity(i);
|
|
}
|
|
|
|
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\n").append(L.f("Calendar: %s", cal.name));
|
|
if (e.recurring) m.append("\n\n").append(L.t("Repeating 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 : L.t("(no title)"))
|
|
.setMessage(m.toString())
|
|
.setNegativeButton(L.t("Close"), null);
|
|
if (e.writable && !e.recurring) {
|
|
b.setPositiveButton(L.t("Edit"), new DialogInterface.OnClickListener() {
|
|
public void onClick(DialogInterface d, int w) {
|
|
fetched.clear();
|
|
startActivity(EditActivity.editIntent(MainActivity.this, e));
|
|
}
|
|
});
|
|
}
|
|
if (e.writable) {
|
|
b.setNeutralButton(L.t("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(L.t("Delete event?"))
|
|
.setMessage(e.recurring ? L.f("This deletes EVERY occurrence of \"%s\".", e.title) : L.f("\"%s\" will be deleted on the server.", e.title))
|
|
.setPositiveButton(L.t("Delete"), new DialogInterface.OnClickListener() {
|
|
public void onClick(DialogInterface d, int w) {
|
|
new DeleteTask(e).execute();
|
|
}
|
|
})
|
|
.setNegativeButton(L.t("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, L.t("Deleting\u2026"));
|
|
}
|
|
|
|
@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(L.t("Delete failed"))
|
|
.setMessage(App.describe(t)).setPositiveButton(L.t("OK"), null).show();
|
|
return;
|
|
}
|
|
// Hide it right away; the sync that follows brings the cache in line.
|
|
List<Ev> keep = new ArrayList<Ev>();
|
|
for (Ev x : monthEvents) if (!x.href.equals(e.href)) keep.add(x);
|
|
fetched.clear();
|
|
for (int i = agendaRows.size() - 1; i >= 0; i--) {
|
|
Object o = agendaRows.get(i);
|
|
if (o instanceof Ev && ((Ev) o).href.equals(e.href)) agendaRows.remove(i);
|
|
}
|
|
agendaAdapter.notifyDataSetChanged();
|
|
if (monthMode) showMonthEvents(keep);
|
|
App.requestSync(MainActivity.this, true, false);
|
|
}
|
|
}
|
|
|
|
// ------------------------------------------------------------- adapter
|
|
|
|
static class Holder {
|
|
View bar;
|
|
TextView time, title, where;
|
|
}
|
|
|
|
/** Day headers (Long), events (Ev) and hint lines (String). */
|
|
class Rows extends BaseAdapter {
|
|
final List<Object> rows;
|
|
|
|
Rows(List<Object> rows) {
|
|
this.rows = rows;
|
|
}
|
|
|
|
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, 5);
|
|
h.setPadding(pad * 2, pad, pad, pad);
|
|
boolean header = o instanceof Long;
|
|
h.setBackgroundColor(header ? 0xFF2E3B44 : 0x00000000);
|
|
h.setTextColor(header ? 0xFFFFFFFF : 0xFF90A4AE);
|
|
h.setText(header ? 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, 5);
|
|
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 ? " " + L.t("(repeats)") : ""));
|
|
hd.title.setText(e.title.length() > 0 ? e.title : L.t("(no title)"));
|
|
hd.where.setText(e.location);
|
|
hd.where.setVisibility(e.location.length() > 0 ? View.VISIBLE : View.GONE);
|
|
return v;
|
|
}
|
|
}
|
|
}
|