Files
calendar-lite/android/org/gingercal/MonthView.java
T

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);
}
}