216 lines
7.8 KiB
Java
216 lines
7.8 KiB
Java
package org.gingercal;
|
|
|
|
import java.util.Calendar;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
|
|
import android.content.Context;
|
|
import android.graphics.Canvas;
|
|
import android.graphics.Paint;
|
|
import android.view.GestureDetector;
|
|
import android.view.MotionEvent;
|
|
import android.view.View;
|
|
|
|
/**
|
|
* Month grid drawn by hand (Android 2.3 has no calendar widget). Six weeks of days, each with
|
|
* up to four coloured marks for its events. Tap selects a day, long-press asks to create an
|
|
* event on it, a sideways swipe changes month.
|
|
*/
|
|
public class MonthView extends View {
|
|
|
|
public interface Listener {
|
|
void daySelected(long day);
|
|
|
|
void dayLongPressed(long day);
|
|
|
|
void monthSwiped(int delta);
|
|
}
|
|
|
|
static final int BG = 0xFF101418, GRID = 0xFF2A3036, WEEKDAY = 0xFF90A4AE, NUM = 0xFFECEFF1,
|
|
OTHER = 0xFF5C666E, TODAY = 0xFFD66E2A, SELECTED = 0xFF2E3B44;
|
|
|
|
final float density;
|
|
final Paint fill = new Paint(1 /* ANTI_ALIAS_FLAG */), text = new Paint(1), line = new Paint();
|
|
final GestureDetector gestures;
|
|
final String[] weekdays = new String[7];
|
|
final long[] days = new long[42];
|
|
Listener listener;
|
|
int month = -1; // Calendar.MONTH of the displayed month
|
|
long selected = -1, today;
|
|
Map<Long, List<Integer>> marks = new HashMap<Long, List<Integer>>();
|
|
|
|
public MonthView(Context c) {
|
|
super(c);
|
|
density = c.getResources().getDisplayMetrics().density;
|
|
line.setColor(GRID);
|
|
String[] shortNames = L.symbols().getShortWeekdays(); // index 1 = Sunday
|
|
int first = L.firstDayOfWeek();
|
|
for (int i = 0; i < 7; i++) {
|
|
String n = shortNames[(first - 1 + i) % 7 + 1];
|
|
weekdays[i] = n.length() > 3 ? n.substring(0, 3) : n;
|
|
}
|
|
gestures = new GestureDetector(c, new GestureDetector.SimpleOnGestureListener() {
|
|
@Override
|
|
public boolean onDown(MotionEvent e) {
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public boolean onSingleTapUp(MotionEvent e) {
|
|
long d = dayAt(e.getX(), e.getY());
|
|
if (d >= 0 && listener != null) listener.daySelected(d);
|
|
return true;
|
|
}
|
|
|
|
@Override
|
|
public void onLongPress(MotionEvent e) {
|
|
long d = dayAt(e.getX(), e.getY());
|
|
if (d >= 0 && listener != null) {
|
|
performHapticFeedback(0 /* HapticFeedbackConstants.LONG_PRESS */);
|
|
listener.dayLongPressed(d);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean onFling(MotionEvent a, MotionEvent b, float vx, float vy) {
|
|
if (listener != null && Math.abs(vx) > Math.abs(vy) * 1.5f && Math.abs(vx) > 250 * density) {
|
|
listener.monthSwiped(vx < 0 ? 1 : -1);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
});
|
|
}
|
|
|
|
/** Shows the month containing `anyDay`, weeks starting on the locale's first weekday. */
|
|
public void setMonth(long anyDay) {
|
|
Calendar c = Calendar.getInstance();
|
|
c.setTimeInMillis(ICal.startOfDay(anyDay));
|
|
c.set(Calendar.DAY_OF_MONTH, 1);
|
|
month = c.get(Calendar.MONTH);
|
|
int back = (c.get(Calendar.DAY_OF_WEEK) - L.firstDayOfWeek() + 7) % 7;
|
|
c.add(Calendar.DAY_OF_MONTH, -back);
|
|
for (int i = 0; i < 42; i++) {
|
|
days[i] = c.getTimeInMillis();
|
|
c.add(Calendar.DAY_OF_MONTH, 1);
|
|
}
|
|
today = ICal.startOfDay(System.currentTimeMillis());
|
|
invalidate();
|
|
}
|
|
|
|
public long gridStart() {
|
|
return days[0];
|
|
}
|
|
|
|
public long gridEnd() {
|
|
return ICal.addDays(days[41], 1);
|
|
}
|
|
|
|
public boolean inMonth(long day) {
|
|
Calendar c = Calendar.getInstance();
|
|
c.setTimeInMillis(day);
|
|
return c.get(Calendar.MONTH) == month;
|
|
}
|
|
|
|
public void setSelected(long day) {
|
|
selected = day;
|
|
invalidate();
|
|
}
|
|
|
|
public void setMarks(Map<Long, List<Integer>> m) {
|
|
marks = m;
|
|
invalidate();
|
|
}
|
|
|
|
// -------------------------------------------------------------- layout
|
|
|
|
float headerH() {
|
|
return 16 * density;
|
|
}
|
|
|
|
@Override
|
|
protected void onMeasure(int wSpec, int hSpec) {
|
|
int w = MeasureSpec.getSize(wSpec);
|
|
int h;
|
|
if (MeasureSpec.getMode(hSpec) == MeasureSpec.EXACTLY) {
|
|
h = MeasureSpec.getSize(hSpec);
|
|
} else {
|
|
float cell = Math.max(22 * density, Math.min(w / 7f * 0.66f, 44 * density));
|
|
h = (int) (headerH() + 6 * cell);
|
|
if (MeasureSpec.getMode(hSpec) == MeasureSpec.AT_MOST) h = Math.min(h, MeasureSpec.getSize(hSpec));
|
|
}
|
|
setMeasuredDimension(w, h);
|
|
}
|
|
|
|
long dayAt(float x, float y) {
|
|
float cw = getWidth() / 7f, ch = (getHeight() - headerH()) / 6f;
|
|
if (y < headerH() || cw <= 0 || ch <= 0) return -1;
|
|
int col = Math.min(6, Math.max(0, (int) (x / cw)));
|
|
int row = Math.min(5, Math.max(0, (int) ((y - headerH()) / ch)));
|
|
return days[row * 7 + col];
|
|
}
|
|
|
|
@Override
|
|
public boolean onTouchEvent(MotionEvent e) {
|
|
return gestures.onTouchEvent(e);
|
|
}
|
|
|
|
// ------------------------------------------------------------- drawing
|
|
|
|
@Override
|
|
protected void onDraw(Canvas canvas) {
|
|
if (month < 0) return;
|
|
float w = getWidth(), cw = w / 7f, top = headerH(), ch = (getHeight() - top) / 6f;
|
|
fill.setStyle(Paint.Style.FILL);
|
|
fill.setColor(BG);
|
|
canvas.drawRect(0, 0, w, getHeight(), fill);
|
|
|
|
text.setTextAlign(Paint.Align.CENTER);
|
|
text.setFakeBoldText(false);
|
|
text.setTextSize(11 * density);
|
|
text.setColor(WEEKDAY);
|
|
for (int i = 0; i < 7; i++) canvas.drawText(weekdays[i], cw * i + cw / 2, top - 4 * density, text);
|
|
|
|
float numSize = Math.min(14 * density, ch * 0.45f);
|
|
float mark = Math.max(2, 3 * density);
|
|
for (int i = 0; i < 42; i++) {
|
|
float x0 = cw * (i % 7), y0 = top + ch * (i / 7);
|
|
long d = days[i];
|
|
if (d == selected) {
|
|
fill.setColor(SELECTED);
|
|
canvas.drawRect(x0, y0, x0 + cw, y0 + ch, fill);
|
|
}
|
|
boolean isToday = d == today, here = inMonth(d);
|
|
Calendar c = Calendar.getInstance();
|
|
c.setTimeInMillis(d);
|
|
String num = String.valueOf(c.get(Calendar.DAY_OF_MONTH));
|
|
text.setTextSize(numSize);
|
|
text.setFakeBoldText(isToday);
|
|
text.setColor(isToday ? TODAY : here ? NUM : OTHER);
|
|
float ty = y0 + ch * 0.12f - text.ascent();
|
|
canvas.drawText(num, x0 + cw / 2, ty, text);
|
|
if (isToday) {
|
|
fill.setStyle(Paint.Style.STROKE);
|
|
fill.setStrokeWidth(Math.max(1, 1.5f * density));
|
|
fill.setColor(TODAY);
|
|
canvas.drawRect(x0 + 1, y0 + 1, x0 + cw - 1, y0 + ch - 1, fill);
|
|
fill.setStyle(Paint.Style.FILL);
|
|
}
|
|
List<Integer> m = marks.get(Long.valueOf(d));
|
|
if (m != null && !m.isEmpty()) {
|
|
int n = Math.min(4, m.size());
|
|
float gap = 2 * density, mw = Math.min(6 * density, (cw - gap * 5) / 4);
|
|
float total = n * mw + (n - 1) * gap, mx = x0 + (cw - total) / 2;
|
|
float my = Math.min(y0 + ch - mark - 3 * density, ty + text.descent() + 2 * density);
|
|
for (int k = 0; k < n; k++) {
|
|
fill.setColor(here ? m.get(k).intValue() : (m.get(k).intValue() & 0x00FFFFFF) | 0x77000000);
|
|
canvas.drawRect(mx, my, mx + mw, my + mark, fill);
|
|
mx += mw + gap;
|
|
}
|
|
}
|
|
}
|
|
for (int r = 0; r <= 6; r++) canvas.drawLine(0, top + ch * r, w, top + ch * r, line);
|
|
}
|
|
}
|