/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package net.redgeek.android.eventrend.calendar; import java.util.ArrayList; import java.util.Calendar; import net.redgeek.android.eventrend.Preferences; import net.redgeek.android.eventrend.calendar.CalendarPlot.PaintIndex; import net.redgeek.android.eventrend.primitives.Datapoint; import net.redgeek.android.eventrend.primitives.TimeSeries; import net.redgeek.android.eventrend.primitives.TimeSeriesCollector; import net.redgeek.android.eventrend.primitives.Tuple; import net.redgeek.android.eventrend.util.DateUtil; import net.redgeek.android.eventrend.util.Number; import net.redgeek.android.eventrend.util.DateUtil.Period; import net.redgeek.android.eventrend.util.Number.TrendState; import android.content.Context; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.Path; import android.graphics.RectF; public class CalendarPlotMonth { // UI elements private ArrayList<Paint> mPaints; // Private data private Context mCtx; private TimeSeriesCollector mTSC; private DateUtil mDates; private float mSensitivity; private float mCellWidth; private float mCellHeight; private float mColorHeight; private Tuple mDimensions; private long mStartMS; private long mFocusStart; private long mFocusEnd; public CalendarPlotMonth(Context context, TimeSeriesCollector tsc, ArrayList<Paint> paints, Tuple dimensions) { mTSC = tsc; mCtx = context; mPaints = paints; setupData(dimensions); } private void setupData(Tuple dimensions) { mDimensions = new Tuple(); setDimensions(dimensions); mDates = new DateUtil(); mSensitivity = Preferences.getStdDevSensitivity(mCtx); setCellSizes(); } public void setDimensions(Tuple dimensions) { mDimensions.set(dimensions); setCellSizes(); } public void setStart(long startMs) { mStartMS = startMs; } private void setCellSizes() { mCellHeight = (mDimensions.y) / 6.0f; mCellWidth = (mDimensions.x) / 7.0f; mColorHeight = mCellHeight / mTSC.numSeries(); } public synchronized void plot(Canvas canvas) { if (canvas == null) return; if (mDimensions.x <= 0 || mDimensions.y <= 0) return; drawMonth(canvas); return; } private int positionToRow(int position) { return position / 7; } private int positionToColumn(int position) { return position % 7; } private Tuple getCellTopLeft(int position) { Tuple t = new Tuple(); t.x = (positionToColumn(position) * mCellWidth); t.y = (positionToRow(position) * mCellHeight); return t; } private Tuple getCellBottomRight(int position) { Tuple t = new Tuple(); t.x = (positionToColumn(position) * mCellWidth) + mCellWidth; t.y = (positionToRow(position) * mCellHeight) + mCellHeight; return t; } private Paint mapTrendStateToPaint(TrendState state) { if (state == TrendState.UP_45_GOOD || state == TrendState.DOWN_45_GOOD) return mPaints.get(PaintIndex.DATUM_GOOD4.ordinal()); else if (state == TrendState.UP_30_GOOD || state == TrendState.DOWN_30_GOOD) return mPaints.get(PaintIndex.DATUM_GOOD3.ordinal()); else if (state == TrendState.UP_15_GOOD || state == TrendState.DOWN_15_GOOD) return mPaints.get(PaintIndex.DATUM_GOOD2.ordinal()); else if (state == TrendState.UP_45_BAD || state == TrendState.DOWN_45_BAD) return mPaints.get(PaintIndex.DATUM_BAD4.ordinal()); else if (state == TrendState.UP_30_BAD || state == TrendState.DOWN_30_BAD) return mPaints.get(PaintIndex.DATUM_BAD3.ordinal()); else if (state == TrendState.UP_15_BAD || state == TrendState.DOWN_15_BAD) return mPaints.get(PaintIndex.DATUM_BAD2.ordinal()); else if (state == TrendState.UP_15 || state == TrendState.DOWN_15 || state == TrendState.FLAT) return mPaints.get(PaintIndex.DATUM_EVEN.ordinal()); else if (state == TrendState.FLAT_GOAL) return mPaints.get(PaintIndex.DATUM_EVEN_GOAL.ordinal()); return null; } private int setStartTime() { int focusMonth; Calendar cal = mDates.getCalendar(); cal.setTimeInMillis(mStartMS); DateUtil.setToPeriodStart(cal, Period.MONTH); long ms = cal.getTimeInMillis(); mDates.setBaseTime(ms); int month = mDates.get(Calendar.MONTH); int position = mDates.get(Calendar.DAY_OF_WEEK); focusMonth = month; if (position == cal.getFirstDayOfWeek()) { mDates.advance(Period.DAY, -7); } else { mDates.advance(Period.DAY, -position + 1); } return focusMonth; } private void drawMonthDayValue(Canvas canvas, int position, float prevValue, float thisValue, float goal, float stdDev) { Paint p; Tuple topLeft = getCellTopLeft(position); Tuple bottomRight = getCellBottomRight(position); float unit = stdDev * mSensitivity; float half = unit / 2; float quarter = half / 2; float delta = thisValue - prevValue; float absDelta = Math.abs(delta); if (absDelta > 0 && absDelta > quarter) { if ((delta > 0 && goal > thisValue) || (delta < 0 && goal < thisValue)) { if (delta > unit) p = mPaints.get(PaintIndex.DATUM_GOOD4.ordinal()); else if (delta > half + quarter) p = mPaints.get(PaintIndex.DATUM_GOOD3.ordinal()); else if (delta > half) p = mPaints.get(PaintIndex.DATUM_GOOD2.ordinal()); else // if (delta > quarter) p = mPaints.get(PaintIndex.DATUM_GOOD1.ordinal()); } else { if (delta > unit) p = mPaints.get(PaintIndex.DATUM_BAD4.ordinal()); else if (delta > half + quarter) p = mPaints.get(PaintIndex.DATUM_BAD3.ordinal()); else if (delta > half) p = mPaints.get(PaintIndex.DATUM_BAD2.ordinal()); else // if (delta > quarter) p = mPaints.get(PaintIndex.DATUM_BAD1.ordinal()); } } else { // even if (Math.abs(thisValue - goal) < half) p = mPaints.get(PaintIndex.DATUM_EVEN_GOAL.ordinal()); else p = mPaints.get(PaintIndex.DATUM_EVEN.ordinal()); } // Paint p = mPaints.get(PaintIndex.VALUE.ordinal()); canvas.drawText("" + Number.Round(thisValue), topLeft.x + 3, bottomRight.y - 4, p); } private void drawMonthDayBackground(Canvas canvas, boolean focused, int position, int date, float lastTrend, float thisTrend, float goal, float stdDev) { Paint p = null; Tuple topLeft = getCellTopLeft(position); Tuple bottomRight = getCellBottomRight(position); // RectF cell = new RectF(topLeft.x, topLeft.y, bottomRight.x, // bottomRight.y); RectF cell = new RectF(topLeft.x, topLeft.y, topLeft.x + 20, topLeft.y + 18); float unit = stdDev * mSensitivity; float half = unit / 2; float quarter = half / 2; float delta = thisTrend - lastTrend; float absDelta = Math.abs(delta); if (absDelta > 0 && absDelta > quarter) { if ((delta > 0 && goal > thisTrend) || (delta < 0 && goal < thisTrend)) { if (delta > unit) p = mPaints.get(PaintIndex.DATUM_GOOD4.ordinal()); else if (delta > half + quarter) p = mPaints.get(PaintIndex.DATUM_GOOD3.ordinal()); else if (delta > half) p = mPaints.get(PaintIndex.DATUM_GOOD2.ordinal()); else // if (delta > quarter) p = mPaints.get(PaintIndex.DATUM_GOOD1.ordinal()); } else { if (delta > unit) p = mPaints.get(PaintIndex.DATUM_BAD4.ordinal()); else if (delta > half + quarter) p = mPaints.get(PaintIndex.DATUM_BAD3.ordinal()); else if (delta > half) p = mPaints.get(PaintIndex.DATUM_BAD2.ordinal()); else // if (delta > quarter) p = mPaints.get(PaintIndex.DATUM_BAD1.ordinal()); } } else { // even if (Math.abs(thisTrend - goal) < half) p = mPaints.get(PaintIndex.DATUM_EVEN_GOAL.ordinal()); else p = null; // p = mPaints.get(PaintIndex.DATUM_EVEN.ordinal()); } if (p != null) { canvas.drawRect(cell, p); } } private void drawMonthDayBorder(Canvas canvas, boolean focused, int position, int date) { Tuple topLeft = getCellTopLeft(position); Tuple bottomRight = getCellBottomRight(position); Paint p; if (focused == true) { p = mPaints.get(PaintIndex.BORDER_SECONDARY.ordinal()); RectF cell = new RectF(topLeft.x, topLeft.y, topLeft.x + 20, topLeft.y + 18); canvas.drawRect(cell, p); cell = new RectF(topLeft.x, topLeft.y, bottomRight.x, bottomRight.y); canvas.drawRect(cell, p); p = mPaints.get(PaintIndex.LABEL.ordinal()); canvas.drawText("" + date, topLeft.x + 3, topLeft.y + CalendarView.TEXT_HEIGHT + 3, p); } else { p = mPaints.get(PaintIndex.BORDER_SECONDARY.ordinal()); Path border = new Path(); border.moveTo(topLeft.x, topLeft.y + 18); border.lineTo(topLeft.x + 20, topLeft.y + 18); border.lineTo(topLeft.x + 20, topLeft.y); if (position < 14) { border.moveTo(topLeft.x, bottomRight.y); border.lineTo(topLeft.x, topLeft.y); border.lineTo(bottomRight.x, topLeft.y); border.lineTo(bottomRight.x, bottomRight.y); } else { border.moveTo(topLeft.x, topLeft.y); border.lineTo(topLeft.x, bottomRight.y); border.lineTo(bottomRight.x, bottomRight.y); border.lineTo(bottomRight.x, topLeft.y); } canvas.drawPath(border, p); p = mPaints.get(PaintIndex.LABEL.ordinal()); canvas.drawText("" + date, topLeft.x + 3, topLeft.y + CalendarView.TEXT_HEIGHT + 3, p); } } private void drawFocusBorder(Canvas canvas, int firstPosition, int lastPosition) { Tuple start = new Tuple(); Tuple corner1, corner2; int firstRow = positionToRow(firstPosition); int firstCol = positionToColumn(firstPosition); int lastRow = positionToRow(lastPosition); int lastCol = positionToColumn(lastPosition); Path border = new Path(); corner1 = getCellTopLeft(firstPosition); corner2 = getCellBottomRight(firstPosition); start.x = corner1.x; start.y = corner2.y; border.moveTo(start.x, start.y); border.lineTo(corner1.x, corner1.y); corner2 = getCellBottomRight(firstPosition - 1 + (7 - (firstPosition % 7))); border.lineTo(corner2.x, corner1.y); corner1 = getCellBottomRight(lastPosition - ((lastPosition + 1) % 7)); border.lineTo(corner2.x, corner1.y); corner2 = getCellTopLeft(lastPosition - 6); border.lineTo(corner2.x, corner1.y); corner1 = getCellBottomRight(lastPosition); border.lineTo(corner2.x, corner1.y); corner2 = getCellTopLeft(lastPosition - (lastPosition % 7)); border.lineTo(corner2.x, corner1.y); corner1 = getCellTopLeft(firstPosition + (7 - ((firstPosition - 1) % 7))); border.lineTo(corner2.x, corner1.y); border.lineTo(start.x, start.y); Paint p = mPaints.get(PaintIndex.BORDER_PRIMARY.ordinal()); canvas.drawPath(border, p); p = mPaints.get(PaintIndex.BORDER_HIGHLIGHT.ordinal()); canvas.drawPath(border, p); } private void drawMonth(Canvas canvas) { Calendar tmp = Calendar.getInstance(); TimeSeries ts; Datapoint prev, current; int focusMonth; int firstPosition = 0; int lastPosition = 0; int i = 42; // as usual focusMonth = setStartTime(); Calendar cal = mDates.getCalendar(); long ms = cal.getTimeInMillis(); int position = mDates.get(Calendar.DAY_OF_WEEK); int day, month; position--; while (i-- > 0) { day = mDates.get(Calendar.DAY_OF_MONTH); month = mDates.get(Calendar.MONTH); ms = mDates.getCalendar().getTimeInMillis(); for (int s = 0; s < mTSC.numSeries(); s++) { ts = (TimeSeries) mTSC.getSeries(s); if (ts == null || mTSC.isSeriesEnabled(ts.getDbRow().getId()) == false) continue; current = ts.findPostNeighbor(ms); prev = ts.findPreNeighbor(ms - 1); if (current != null && prev != null) { if (focusMonth == mDates.get(Calendar.MONTH)) { drawMonthDayBackground(canvas, true, position, day, prev.mTrend.y, current.mTrend.y, ts.getDbRow().getGoal(), current.mStdDev); } else { drawMonthDayBackground(canvas, false, position, day, prev.mTrend.y, current.mTrend.y, ts.getDbRow().getGoal(), current.mStdDev); } } if (current != null) { float oldVal = current.mValue.y; if (prev != null) oldVal = prev.mValue.y; tmp.setTimeInMillis(current.mMillis); if (month == tmp.get(Calendar.MONTH) && day == tmp.get(Calendar.DAY_OF_MONTH)) { drawMonthDayValue(canvas, position, oldVal, current.mValue.y, ts .getDbRow().getGoal(), current.mStdDev); } } } if (focusMonth == mDates.get(Calendar.MONTH)) { if (day == 1) { firstPosition = position; mFocusStart = ms; } if (day == mDates.getCalendar().getActualMaximum(Calendar.DAY_OF_MONTH)) { lastPosition = position; mFocusEnd = ms + DateUtil.DAY_MS - 1; } drawMonthDayBorder(canvas, true, position, day); } else { drawMonthDayBorder(canvas, false, position, day); } mDates.advance(Period.DAY, 1); position++; } drawFocusBorder(canvas, firstPosition, lastPosition); } public long getFocusStart() { return mFocusStart; } public long getFocusEnd() { return mFocusEnd; } }