package org.gingercal; import java.util.ArrayList; import java.util.Calendar; import java.util.List; import java.util.UUID; import android.app.Activity; import android.app.AlertDialog; import android.app.DatePickerDialog; import android.app.ProgressDialog; import android.app.TimePickerDialog; import android.content.DialogInterface; import android.os.AsyncTask; import android.os.Bundle; import android.view.View; import android.widget.Button; import android.widget.CheckBox; import android.widget.DatePicker; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ScrollView; import android.widget.TextView; import android.widget.TimePicker; import org.gingercal.Model.Cal; import org.gingercal.Model.Ev; /** Create or edit a single (non-repeating) event. */ public class EditActivity extends Activity { EditText title, location, notes; CheckBox allDay; Button startDate, startTime, endDate, endTime, alarmBtn, calBtn, save; final Calendar start = Calendar.getInstance(), end = Calendar.getInstance(); int alarm = 15, originalAlarm = -3; String calHref; Ev editing; String ics, etag; List cals = new ArrayList(); @Override protected void onCreate(Bundle b) { super.onCreate(b); long id = getIntent().getLongExtra("id", -1); if (id >= 0) editing = Db.get(this).event(id); setTitle(editing == null ? "New event" : "Edit event"); for (Cal c : Db.get(this).calendarList()) if (c.enabled && c.writable) cals.add(c); LinearLayout f = App.vbox(this); int p = App.dp(this, 8); f.setPadding(p, p, p, p); title = field(f, "Title", false); allDay = new CheckBox(this); allDay.setText("All day"); allDay.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { refresh(); } }); f.addView(allDay); f.addView(label("Starts")); startDate = new Button(this); startTime = new Button(this); f.addView(pair(startDate, startTime)); f.addView(label("Ends")); endDate = new Button(this); endTime = new Button(this); f.addView(pair(endDate, endTime)); location = field(f, "Location", false); notes = field(f, "Notes", true); alarmBtn = new Button(this); f.addView(alarmBtn, new LinearLayout.LayoutParams(-1, -2)); calBtn = new Button(this); f.addView(calBtn, new LinearLayout.LayoutParams(-1, -2)); save = new Button(this); save.setText("Save"); f.addView(save, new LinearLayout.LayoutParams(-1, -2)); ScrollView sv = new ScrollView(this); sv.addView(f); setContentView(sv); startDate.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { pickDate(start, true); } }); endDate.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { pickDate(end, false); } }); startTime.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { pickTime(start, true); } }); endTime.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { pickTime(end, false); } }); alarmBtn.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { pickAlarm(); } }); calBtn.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { pickCalendar(); } }); save.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { save(); } }); if (editing == null) { long now = System.currentTimeMillis(); start.setTimeInMillis(now - now % ICal.HOUR + ICal.HOUR); start.set(Calendar.SECOND, 0); start.set(Calendar.MILLISECOND, 0); end.setTimeInMillis(start.getTimeInMillis() + ICal.HOUR); String def = App.get(this, "defcal"); calHref = cals.isEmpty() ? null : cals.get(0).href; for (Cal c : cals) if (c.href.equals(def)) calHref = def; refresh(); } else { calHref = editing.cal; calBtn.setEnabled(false); fill(editing.title, editing.location, editing.descr, editing.start, editing.end, editing.allDay); save.setEnabled(false); new LoadTask().execute(); } } EditText field(LinearLayout f, String hint, boolean multi) { EditText e = new EditText(this); e.setHint(hint); if (multi) { e.setInputType(1 | 0x4000 | 0x20000); // text | cap sentences | multi-line e.setMinLines(3); } else { e.setSingleLine(true); e.setInputType(1 | 0x4000); } f.addView(e, new LinearLayout.LayoutParams(-1, -2)); return e; } TextView label(String s) { TextView t = App.text(this, s, 13, 0xFFB0BEC5); t.setPadding(App.dp(this, 4), App.dp(this, 6), 0, 0); return t; } LinearLayout pair(Button a, Button b) { LinearLayout l = new LinearLayout(this); l.setOrientation(LinearLayout.HORIZONTAL); l.addView(a, new LinearLayout.LayoutParams(0, -2, 3f)); l.addView(b, new LinearLayout.LayoutParams(0, -2, 2f)); return l; } void fill(String t, String loc, String desc, long s, long e, boolean ad) { title.setText(t); location.setText(loc); notes.setText(desc); allDay.setChecked(ad); start.setTimeInMillis(s); // all-day DTEND is exclusive; show the last day instead end.setTimeInMillis(ad ? Math.max(s, ICal.addDays(e, -1)) : e); refresh(); } void refresh() { boolean ad = allDay.isChecked(); startDate.setText(App.day(start.getTimeInMillis())); endDate.setText(App.day(end.getTimeInMillis())); startTime.setText(App.time(this, start.getTimeInMillis())); endTime.setText(App.time(this, end.getTimeInMillis())); startTime.setVisibility(ad ? View.GONE : View.VISIBLE); endTime.setVisibility(ad ? View.GONE : View.VISIBLE); alarmBtn.setText("Alarm: " + App.alarmLabel(alarm)); String name = "?"; for (Cal c : Db.get(this).calendarList()) if (c.href.equals(calHref)) name = c.name; calBtn.setText("Calendar: " + name); } void pickDate(final Calendar c, final boolean isStart) { new DatePickerDialog(this, new DatePickerDialog.OnDateSetListener() { public void onDateSet(DatePicker v, int y, int m, int d) { long dur = end.getTimeInMillis() - start.getTimeInMillis(); c.set(y, m, d); if (isStart) end.setTimeInMillis(start.getTimeInMillis() + Math.max(0, dur)); refresh(); } }, c.get(Calendar.YEAR), c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)).show(); } void pickTime(final Calendar c, final boolean isStart) { new TimePickerDialog(this, new TimePickerDialog.OnTimeSetListener() { public void onTimeSet(TimePicker v, int h, int m) { long dur = end.getTimeInMillis() - start.getTimeInMillis(); c.set(Calendar.HOUR_OF_DAY, h); c.set(Calendar.MINUTE, m); if (isStart) end.setTimeInMillis(start.getTimeInMillis() + Math.max(0, dur)); refresh(); } }, c.get(Calendar.HOUR_OF_DAY), c.get(Calendar.MINUTE), android.text.format.DateFormat.is24HourFormat(this)).show(); } void pickAlarm() { String[] labels = new String[App.ALARMS.length]; for (int i = 0; i < labels.length; i++) labels[i] = App.alarmLabel(App.ALARMS[i]); new AlertDialog.Builder(this).setTitle("Alarm (saved in the event)") .setItems(labels, new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int w) { alarm = App.ALARMS[w]; refresh(); } }).show(); } void pickCalendar() { String[] names = new String[cals.size()]; for (int i = 0; i < names.length; i++) names[i] = cals.get(i).name; new AlertDialog.Builder(this).setTitle("Calendar") .setItems(names, new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int w) { calHref = cals.get(w).href; refresh(); } }).show(); } static String txt(TextView t) { return t.getText().toString().trim(); } void save() { boolean ad = allDay.isChecked(); long s, e; if (ad) { s = ICal.startOfDay(start.getTimeInMillis()); e = ICal.addDays(ICal.startOfDay(end.getTimeInMillis()), 1); } else { s = start.getTimeInMillis() - start.getTimeInMillis() % 60000L; e = end.getTimeInMillis() - end.getTimeInMillis() % 60000L; } if (e < s || (ad && e <= s)) { App.toast(this, "The end is before the start."); return; } if (calHref == null) { App.toast(this, "No writable calendar. Sync first."); return; } String t = txt(title); new SaveTask(t.length() > 0 ? t : "(no title)", txt(location), notes.getText().toString().trim(), s, e, ad).execute(); } // ----------------------------------------------------------- network class LoadTask extends AsyncTask { ProgressDialog pd; @Override protected void onPreExecute() { pd = ProgressDialog.show(EditActivity.this, null, "Loading…"); } @Override protected Object doInBackground(Void... v) { Dav d = null; try { d = App.dav(EditActivity.this); return d.get(editing.href); } catch (Throwable t) { return t; } finally { if (d != null) d.close(); } } @Override protected void onPostExecute(Object r) { try { pd.dismiss(); } catch (RuntimeException ignored) { } if (r instanceof Throwable) { fail("Can't load event", App.describe((Throwable) r)); return; } String[] got = (String[]) r; ics = got[0]; etag = got[1] != null ? got[1] : editing.etag; if (ICal.isRecurring(ics)) { fail("Repeating event", "Repeating events can't be edited on the phone yet. Edit the series in Nextcloud."); return; } List vs = ICal.parse(ics); if (vs.isEmpty()) { fail("Can't edit", "The server returned no event data."); return; } ICal.VEvent v = vs.get(0); alarm = v.alarmMin; originalAlarm = v.alarmMin; fill(v.summary, v.location, v.description, v.start, v.end, v.allDay); save.setEnabled(true); } } void fail(String t, String msg) { new AlertDialog.Builder(this).setTitle(t).setMessage(msg) .setPositiveButton("OK", new DialogInterface.OnClickListener() { public void onClick(DialogInterface d, int w) { finish(); } }).show(); } class SaveTask extends AsyncTask { final String t, loc, desc; final long s, e; final boolean ad; ProgressDialog pd; SaveTask(String t, String loc, String desc, long s, long e, boolean ad) { this.t = t; this.loc = loc; this.desc = desc; this.s = s; this.e = e; this.ad = ad; } @Override protected void onPreExecute() { pd = ProgressDialog.show(EditActivity.this, null, "Saving…"); } @Override protected Throwable doInBackground(Void... v) { Dav d = null; try { d = App.dav(EditActivity.this); if (editing == null) { String uid = UUID.randomUUID().toString().toUpperCase(); d.create(calHref, uid, ICal.newEvent(uid, t, loc, desc, s, e, ad, alarm)); } else { int a = alarm == originalAlarm ? -2 : alarm; d.update(editing.href, etag, ICal.modify(ics, t, loc, desc, s, e, ad, a)); } return null; } catch (Throwable x) { return x; } finally { if (d != null) d.close(); } } @Override protected void onPostExecute(Throwable x) { try { pd.dismiss(); } catch (RuntimeException ignored) { } if (x != null) { new AlertDialog.Builder(EditActivity.this).setTitle("Not saved") .setMessage(App.describe(x)).setPositiveButton("OK", null).show(); return; } if (editing == null) App.prefs(EditActivity.this).edit().putString("defcal", calHref).commit(); App.requestSync(EditActivity.this, true, false); App.toast(EditActivity.this, "Saved"); finish(); } } }