mirror of https://github.com/iSoron/uhabits.git
commit
53a599c6b8
@ -0,0 +1,2 @@
|
|||||||
|
alter table Habits add column type integer not null default 0;
|
||||||
|
alter table Repetitions add column value integer not null default 2;
|
@ -0,0 +1,5 @@
|
|||||||
|
DROP TABLE Score;
|
||||||
|
CREATE TABLE Score (Id INTEGER PRIMARY KEY AUTOINCREMENT, habit INTEGER REFERENCES Habits(Id), score REAL, timestamp INTEGER);
|
||||||
|
CREATE INDEX idx_score_habit_timestamp on score(habit, timestamp);
|
||||||
|
delete from Streak;
|
||||||
|
delete from Checkmarks;
|
@ -0,0 +1,3 @@
|
|||||||
|
alter table Habits add column target_type integer not null default 0;
|
||||||
|
alter table Habits add column target_value real not null default 0;
|
||||||
|
alter table Habits add column unit text not null default "";
|
@ -0,0 +1,479 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.common.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.utils.InterfaceUtils.*;
|
||||||
|
|
||||||
|
public class BarChart extends ScrollableChart
|
||||||
|
{
|
||||||
|
private static final PorterDuffXfermode XFERMODE_CLEAR =
|
||||||
|
new PorterDuffXfermode(PorterDuff.Mode.CLEAR);
|
||||||
|
|
||||||
|
private static final PorterDuffXfermode XFERMODE_SRC =
|
||||||
|
new PorterDuffXfermode(PorterDuff.Mode.SRC);
|
||||||
|
|
||||||
|
private Paint pGrid;
|
||||||
|
|
||||||
|
private float em;
|
||||||
|
|
||||||
|
private SimpleDateFormat dfMonth;
|
||||||
|
|
||||||
|
private SimpleDateFormat dfDay;
|
||||||
|
|
||||||
|
private SimpleDateFormat dfYear;
|
||||||
|
|
||||||
|
private Paint pText, pGraph;
|
||||||
|
|
||||||
|
private RectF rect, prevRect;
|
||||||
|
|
||||||
|
private int baseSize;
|
||||||
|
|
||||||
|
private int paddingTop;
|
||||||
|
|
||||||
|
private float columnWidth;
|
||||||
|
|
||||||
|
private int columnHeight;
|
||||||
|
|
||||||
|
private int nColumns;
|
||||||
|
|
||||||
|
private int textColor;
|
||||||
|
|
||||||
|
private int gridColor;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private List<Checkmark> checkmarks;
|
||||||
|
|
||||||
|
private int primaryColor;
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
private int bucketSize = 7;
|
||||||
|
|
||||||
|
private int backgroundColor;
|
||||||
|
|
||||||
|
private Bitmap drawingCache;
|
||||||
|
|
||||||
|
private Canvas cacheCanvas;
|
||||||
|
|
||||||
|
private boolean isTransparencyEnabled;
|
||||||
|
|
||||||
|
private int skipYear = 0;
|
||||||
|
|
||||||
|
private String previousYearText;
|
||||||
|
|
||||||
|
private String previousMonthText;
|
||||||
|
|
||||||
|
private double maxValue;
|
||||||
|
|
||||||
|
private double target;
|
||||||
|
|
||||||
|
public BarChart(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BarChart(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void populateWithRandomData()
|
||||||
|
{
|
||||||
|
Random random = new Random();
|
||||||
|
List<Checkmark> checkmarks = new LinkedList<>();
|
||||||
|
|
||||||
|
long timestamp = DateUtils.getStartOfToday();
|
||||||
|
long day = DateUtils.millisecondsInOneDay;
|
||||||
|
|
||||||
|
for (int i = 1; i < 100; i++)
|
||||||
|
{
|
||||||
|
int value = random.nextInt(1000);
|
||||||
|
checkmarks.add(new Checkmark(timestamp, value));
|
||||||
|
timestamp -= day;
|
||||||
|
}
|
||||||
|
|
||||||
|
setCheckmarks(checkmarks);
|
||||||
|
setTarget(0.5);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Deprecated
|
||||||
|
public void setBucketSize(int bucketSize)
|
||||||
|
{
|
||||||
|
this.bucketSize = bucketSize;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCheckmarks(@NonNull List<Checkmark> checkmarks)
|
||||||
|
{
|
||||||
|
this.checkmarks = checkmarks;
|
||||||
|
|
||||||
|
maxValue = 1.0;
|
||||||
|
for (Checkmark c : checkmarks)
|
||||||
|
maxValue = Math.max(maxValue, c.getValue());
|
||||||
|
maxValue = Math.ceil(maxValue / 1000 * 1.05) * 1000;
|
||||||
|
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int primaryColor)
|
||||||
|
{
|
||||||
|
this.primaryColor = primaryColor;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setIsTransparencyEnabled(boolean enabled)
|
||||||
|
{
|
||||||
|
this.isTransparencyEnabled = enabled;
|
||||||
|
initColors();
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTarget(double target)
|
||||||
|
{
|
||||||
|
this.target = target;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas)
|
||||||
|
{
|
||||||
|
super.onDraw(canvas);
|
||||||
|
Canvas activeCanvas;
|
||||||
|
|
||||||
|
if (isTransparencyEnabled)
|
||||||
|
{
|
||||||
|
if (drawingCache == null) initCache(getWidth(), getHeight());
|
||||||
|
|
||||||
|
activeCanvas = cacheCanvas;
|
||||||
|
drawingCache.eraseColor(Color.TRANSPARENT);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
activeCanvas = canvas;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (checkmarks == null) return;
|
||||||
|
|
||||||
|
rect.set(0, 0, nColumns * columnWidth, columnHeight);
|
||||||
|
rect.offset(0, paddingTop);
|
||||||
|
|
||||||
|
drawGrid(activeCanvas, rect);
|
||||||
|
|
||||||
|
pText.setColor(textColor);
|
||||||
|
pGraph.setColor(primaryColor);
|
||||||
|
prevRect.setEmpty();
|
||||||
|
|
||||||
|
previousMonthText = "";
|
||||||
|
previousYearText = "";
|
||||||
|
skipYear = 0;
|
||||||
|
|
||||||
|
for (int k = 0; k < nColumns; k++)
|
||||||
|
{
|
||||||
|
int offset = nColumns - k - 1 + getDataOffset();
|
||||||
|
if (offset >= checkmarks.size()) continue;
|
||||||
|
|
||||||
|
double value = checkmarks.get(offset).getValue();
|
||||||
|
long timestamp = checkmarks.get(offset).getTimestamp();
|
||||||
|
int height = (int) (columnHeight * value / maxValue);
|
||||||
|
|
||||||
|
rect.set(0, 0, baseSize, height);
|
||||||
|
rect.offset(k * columnWidth + (columnWidth - baseSize) / 2,
|
||||||
|
paddingTop + columnHeight - height);
|
||||||
|
|
||||||
|
drawValue(activeCanvas, rect, value);
|
||||||
|
drawBar(activeCanvas, rect, value);
|
||||||
|
|
||||||
|
prevRect.set(rect);
|
||||||
|
rect.set(0, 0, columnWidth, columnHeight);
|
||||||
|
rect.offset(k * columnWidth, paddingTop);
|
||||||
|
|
||||||
|
drawFooter(activeCanvas, rect, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (activeCanvas != canvas) canvas.drawBitmap(drawingCache, 0, 0, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
int width = MeasureSpec.getSize(widthMeasureSpec);
|
||||||
|
int height = MeasureSpec.getSize(heightMeasureSpec);
|
||||||
|
setMeasuredDimension(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onSizeChanged(int width,
|
||||||
|
int height,
|
||||||
|
int oldWidth,
|
||||||
|
int oldHeight)
|
||||||
|
{
|
||||||
|
if (height < 9) height = 200;
|
||||||
|
|
||||||
|
float maxTextSize = getResources().getDimension(R.dimen.tinyTextSize);
|
||||||
|
float textSize = height * 0.06f;
|
||||||
|
pText.setTextSize(Math.min(textSize, maxTextSize));
|
||||||
|
em = pText.getFontSpacing();
|
||||||
|
|
||||||
|
int footerHeight = (int) (3 * em);
|
||||||
|
paddingTop = (int) (em);
|
||||||
|
|
||||||
|
baseSize = (height - footerHeight - paddingTop) / 12;
|
||||||
|
columnWidth = baseSize;
|
||||||
|
columnWidth = Math.max(columnWidth, getMaxDayWidth() * 1.5f);
|
||||||
|
columnWidth = Math.max(columnWidth, getMaxMonthWidth() * 1.2f);
|
||||||
|
|
||||||
|
nColumns = (int) (width / columnWidth);
|
||||||
|
columnWidth = (float) width / nColumns;
|
||||||
|
setScrollerBucketSize((int) columnWidth);
|
||||||
|
|
||||||
|
columnHeight = 12 * baseSize;
|
||||||
|
|
||||||
|
float minStrokeWidth = dpToPixels(getContext(), 1);
|
||||||
|
pGraph.setTextSize(baseSize * 0.5f);
|
||||||
|
pGraph.setStrokeWidth(baseSize * 0.1f);
|
||||||
|
pGrid.setStrokeWidth(Math.min(minStrokeWidth, baseSize * 0.05f));
|
||||||
|
|
||||||
|
if (isTransparencyEnabled) initCache(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawBar(Canvas canvas, RectF rect, double value)
|
||||||
|
{
|
||||||
|
float margin = baseSize * 0.225f;
|
||||||
|
|
||||||
|
int color = textColor;
|
||||||
|
if (value / 1000 >= target) color = primaryColor;
|
||||||
|
|
||||||
|
rect.inset(-margin, 0);
|
||||||
|
setModeOrColor(pGraph, XFERMODE_CLEAR, backgroundColor);
|
||||||
|
canvas.drawRect(rect, pGraph);
|
||||||
|
|
||||||
|
rect.inset(margin, 0);
|
||||||
|
setModeOrColor(pGraph, XFERMODE_SRC, color);
|
||||||
|
canvas.drawRect(rect, pGraph);
|
||||||
|
|
||||||
|
if (isTransparencyEnabled) pGraph.setXfermode(XFERMODE_SRC);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawFooter(Canvas canvas, RectF rect, long currentDate)
|
||||||
|
{
|
||||||
|
String yearText = dfYear.format(currentDate);
|
||||||
|
String monthText = dfMonth.format(currentDate);
|
||||||
|
String dayText = dfDay.format(currentDate);
|
||||||
|
|
||||||
|
GregorianCalendar calendar = DateUtils.getCalendar(currentDate);
|
||||||
|
pText.setColor(textColor);
|
||||||
|
|
||||||
|
String text;
|
||||||
|
int year = calendar.get(Calendar.YEAR);
|
||||||
|
|
||||||
|
boolean shouldPrintYear = true;
|
||||||
|
if (yearText.equals(previousYearText)) shouldPrintYear = false;
|
||||||
|
if (bucketSize >= 365 && (year % 2) != 0) shouldPrintYear = false;
|
||||||
|
|
||||||
|
if (skipYear > 0)
|
||||||
|
{
|
||||||
|
skipYear--;
|
||||||
|
shouldPrintYear = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (shouldPrintYear)
|
||||||
|
{
|
||||||
|
previousYearText = yearText;
|
||||||
|
previousMonthText = "";
|
||||||
|
|
||||||
|
pText.setTextAlign(Paint.Align.CENTER);
|
||||||
|
canvas.drawText(yearText, rect.centerX(), rect.bottom + em * 2.2f, pText);
|
||||||
|
skipYear = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (bucketSize < 365)
|
||||||
|
{
|
||||||
|
if (!monthText.equals(previousMonthText))
|
||||||
|
{
|
||||||
|
previousMonthText = monthText;
|
||||||
|
text = monthText;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
text = dayText;
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawText(text, rect.centerX(), rect.bottom + em * 1.2f,
|
||||||
|
pText);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawGrid(Canvas canvas, RectF rGrid)
|
||||||
|
{
|
||||||
|
int nRows = 5;
|
||||||
|
float rowHeight = rGrid.height() / nRows;
|
||||||
|
|
||||||
|
pText.setColor(textColor);
|
||||||
|
pGrid.setColor(gridColor);
|
||||||
|
|
||||||
|
for (int i = 0; i < nRows; i++)
|
||||||
|
{
|
||||||
|
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top,
|
||||||
|
pGrid);
|
||||||
|
rGrid.offset(0, rowHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void drawValue(Canvas canvas, RectF rect, double value)
|
||||||
|
{
|
||||||
|
if (value == 0) return;
|
||||||
|
|
||||||
|
int activeColor = textColor;
|
||||||
|
if (value / 1000 >= target)
|
||||||
|
activeColor = primaryColor;
|
||||||
|
|
||||||
|
String label = NumberButtonView.formatValue(value / 1000);
|
||||||
|
Rect rText = new Rect();
|
||||||
|
pText.getTextBounds(label, 0, label.length(), rText);
|
||||||
|
|
||||||
|
float offset = 0.5f * em;
|
||||||
|
float x = rect.centerX();
|
||||||
|
float y = rect.top - offset;
|
||||||
|
int cap = (int) (-0.1f * em);
|
||||||
|
|
||||||
|
rText.offset((int) x, (int) y);
|
||||||
|
rText.offset(-rText.width() / 2, 0);
|
||||||
|
rText.inset(3 * cap, cap);
|
||||||
|
|
||||||
|
setModeOrColor(pText, XFERMODE_CLEAR, backgroundColor);
|
||||||
|
canvas.drawRect(rText, pText);
|
||||||
|
|
||||||
|
setModeOrColor(pText, XFERMODE_SRC, activeColor);
|
||||||
|
canvas.drawText(label, x, y, pText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getMaxDayWidth()
|
||||||
|
{
|
||||||
|
float maxDayWidth = 0;
|
||||||
|
GregorianCalendar day = DateUtils.getStartOfTodayCalendar();
|
||||||
|
|
||||||
|
for (int i = 0; i < 28; i++)
|
||||||
|
{
|
||||||
|
day.set(Calendar.DAY_OF_MONTH, i);
|
||||||
|
float monthWidth = pText.measureText(dfMonth.format(day.getTime()));
|
||||||
|
maxDayWidth = Math.max(maxDayWidth, monthWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxDayWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private float getMaxMonthWidth()
|
||||||
|
{
|
||||||
|
float maxMonthWidth = 0;
|
||||||
|
GregorianCalendar day = DateUtils.getStartOfTodayCalendar();
|
||||||
|
|
||||||
|
for (int i = 0; i < 12; i++)
|
||||||
|
{
|
||||||
|
day.set(Calendar.MONTH, i);
|
||||||
|
float monthWidth = pText.measureText(dfMonth.format(day.getTime()));
|
||||||
|
maxMonthWidth = Math.max(maxMonthWidth, monthWidth);
|
||||||
|
}
|
||||||
|
|
||||||
|
return maxMonthWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
initPaints();
|
||||||
|
initColors();
|
||||||
|
initDateFormats();
|
||||||
|
initRects();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initCache(int width, int height)
|
||||||
|
{
|
||||||
|
if (drawingCache != null) drawingCache.recycle();
|
||||||
|
drawingCache =
|
||||||
|
Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||||||
|
cacheCanvas = new Canvas(drawingCache);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initColors()
|
||||||
|
{
|
||||||
|
StyledResources res = new StyledResources(getContext());
|
||||||
|
|
||||||
|
primaryColor = Color.BLACK;
|
||||||
|
textColor = res.getColor(R.attr.mediumContrastTextColor);
|
||||||
|
gridColor = res.getColor(R.attr.lowContrastTextColor);
|
||||||
|
backgroundColor = res.getColor(R.attr.cardBackgroundColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initDateFormats()
|
||||||
|
{
|
||||||
|
if (isInEditMode())
|
||||||
|
{
|
||||||
|
dfYear = new SimpleDateFormat("yyyy", Locale.US);
|
||||||
|
dfMonth = new SimpleDateFormat("MMM", Locale.US);
|
||||||
|
dfDay = new SimpleDateFormat("d", Locale.US);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
dfYear = DateFormats.fromSkeleton("yyyy");
|
||||||
|
dfMonth = DateFormats.fromSkeleton("MMM");
|
||||||
|
dfDay = DateFormats.fromSkeleton("d");
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initPaints()
|
||||||
|
{
|
||||||
|
pText = new Paint();
|
||||||
|
pText.setAntiAlias(true);
|
||||||
|
pText.setTextAlign(Paint.Align.CENTER);
|
||||||
|
|
||||||
|
pGraph = new Paint();
|
||||||
|
pGraph.setTextAlign(Paint.Align.CENTER);
|
||||||
|
pGraph.setAntiAlias(true);
|
||||||
|
|
||||||
|
pGrid = new Paint();
|
||||||
|
pGrid.setAntiAlias(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initRects()
|
||||||
|
{
|
||||||
|
rect = new RectF();
|
||||||
|
prevRect = new RectF();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setModeOrColor(Paint p, PorterDuffXfermode mode, int color)
|
||||||
|
{
|
||||||
|
if (isTransparencyEnabled) p.setXfermode(mode);
|
||||||
|
else p.setColor(color);
|
||||||
|
}
|
||||||
|
}
|
@ -1,263 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.activities.habits.edit;
|
|
||||||
|
|
||||||
import android.os.*;
|
|
||||||
import android.support.annotation.*;
|
|
||||||
import android.support.v7.app.*;
|
|
||||||
import android.text.format.*;
|
|
||||||
import android.view.*;
|
|
||||||
|
|
||||||
import com.android.datetimepicker.time.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.activities.*;
|
|
||||||
import org.isoron.uhabits.activities.common.dialogs.*;
|
|
||||||
import org.isoron.uhabits.commands.*;
|
|
||||||
import org.isoron.uhabits.models.*;
|
|
||||||
import org.isoron.uhabits.preferences.*;
|
|
||||||
|
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
import butterknife.*;
|
|
||||||
|
|
||||||
public abstract class BaseDialog extends AppCompatDialogFragment
|
|
||||||
{
|
|
||||||
@Nullable
|
|
||||||
protected Habit originalHabit;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected Habit modifiedHabit;
|
|
||||||
|
|
||||||
@Nullable
|
|
||||||
protected BaseDialogHelper helper;
|
|
||||||
|
|
||||||
protected Preferences prefs;
|
|
||||||
|
|
||||||
protected CommandRunner commandRunner;
|
|
||||||
|
|
||||||
protected HabitList habitList;
|
|
||||||
|
|
||||||
protected AppComponent appComponent;
|
|
||||||
|
|
||||||
protected ModelFactory modelFactory;
|
|
||||||
|
|
||||||
private ColorPickerDialogFactory colorPickerDialogFactory;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityCreated(Bundle savedInstanceState)
|
|
||||||
{
|
|
||||||
super.onActivityCreated(savedInstanceState);
|
|
||||||
|
|
||||||
BaseActivity activity = (BaseActivity) getActivity();
|
|
||||||
colorPickerDialogFactory =
|
|
||||||
activity.getComponent().getColorPickerDialogFactory();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public View onCreateView(LayoutInflater inflater,
|
|
||||||
ViewGroup container,
|
|
||||||
Bundle savedInstanceState)
|
|
||||||
{
|
|
||||||
View view = inflater.inflate(R.layout.edit_habit, container, false);
|
|
||||||
|
|
||||||
HabitsApplication app =
|
|
||||||
(HabitsApplication) getContext().getApplicationContext();
|
|
||||||
|
|
||||||
appComponent = app.getComponent();
|
|
||||||
prefs = appComponent.getPreferences();
|
|
||||||
habitList = appComponent.getHabitList();
|
|
||||||
commandRunner = appComponent.getCommandRunner();
|
|
||||||
modelFactory = appComponent.getModelFactory();
|
|
||||||
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
|
|
||||||
helper = new BaseDialogHelper(this, view);
|
|
||||||
getDialog().setTitle(getTitle());
|
|
||||||
initializeHabits();
|
|
||||||
restoreSavedInstance(savedInstanceState);
|
|
||||||
helper.populateForm(modifiedHabit);
|
|
||||||
return view;
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnItemSelected(R.id.sFrequency)
|
|
||||||
public void onFrequencySelected(int position)
|
|
||||||
{
|
|
||||||
if (position < 0 || position > 4) throw new IllegalArgumentException();
|
|
||||||
int freqNums[] = { 1, 1, 2, 5, 3 };
|
|
||||||
int freqDens[] = { 1, 7, 7, 7, 7 };
|
|
||||||
modifiedHabit.setFrequency(
|
|
||||||
new Frequency(freqNums[position], freqDens[position]));
|
|
||||||
helper.populateFrequencyFields(modifiedHabit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
public void onSaveInstanceState(Bundle outState)
|
|
||||||
{
|
|
||||||
super.onSaveInstanceState(outState);
|
|
||||||
outState.putInt("color", modifiedHabit.getColor());
|
|
||||||
if (modifiedHabit.hasReminder())
|
|
||||||
{
|
|
||||||
Reminder reminder = modifiedHabit.getReminder();
|
|
||||||
outState.putInt("reminderMin", reminder.getMinute());
|
|
||||||
outState.putInt("reminderHour", reminder.getHour());
|
|
||||||
outState.putInt("reminderDays", reminder.getDays().toInteger());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract int getTitle();
|
|
||||||
|
|
||||||
protected abstract void initializeHabits();
|
|
||||||
|
|
||||||
protected void restoreSavedInstance(@Nullable Bundle bundle)
|
|
||||||
{
|
|
||||||
if (bundle == null) return;
|
|
||||||
modifiedHabit.setColor(
|
|
||||||
bundle.getInt("color", modifiedHabit.getColor()));
|
|
||||||
|
|
||||||
modifiedHabit.setReminder(null);
|
|
||||||
|
|
||||||
int hour = (bundle.getInt("reminderHour", -1));
|
|
||||||
int minute = (bundle.getInt("reminderMin", -1));
|
|
||||||
int days = (bundle.getInt("reminderDays", -1));
|
|
||||||
|
|
||||||
if (hour >= 0 && minute >= 0)
|
|
||||||
{
|
|
||||||
Reminder reminder =
|
|
||||||
new Reminder(hour, minute, new WeekdayList(days));
|
|
||||||
modifiedHabit.setReminder(reminder);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected abstract void saveHabit();
|
|
||||||
|
|
||||||
@OnClick(R.id.buttonDiscard)
|
|
||||||
void onButtonDiscardClick()
|
|
||||||
{
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.tvReminderTime)
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
void onDateSpinnerClick()
|
|
||||||
{
|
|
||||||
int defaultHour = 8;
|
|
||||||
int defaultMin = 0;
|
|
||||||
|
|
||||||
if (modifiedHabit.hasReminder())
|
|
||||||
{
|
|
||||||
Reminder reminder = modifiedHabit.getReminder();
|
|
||||||
defaultHour = reminder.getHour();
|
|
||||||
defaultMin = reminder.getMinute();
|
|
||||||
}
|
|
||||||
|
|
||||||
showTimePicker(defaultHour, defaultMin);
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.buttonSave)
|
|
||||||
void onSaveButtonClick()
|
|
||||||
{
|
|
||||||
helper.parseFormIntoHabit(modifiedHabit);
|
|
||||||
if (!helper.validate(modifiedHabit)) return;
|
|
||||||
saveHabit();
|
|
||||||
dismiss();
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.tvReminderDays)
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
void onWeekdayClick()
|
|
||||||
{
|
|
||||||
if (!modifiedHabit.hasReminder()) return;
|
|
||||||
Reminder reminder = modifiedHabit.getReminder();
|
|
||||||
|
|
||||||
WeekdayPickerDialog dialog = new WeekdayPickerDialog();
|
|
||||||
dialog.setListener(new OnWeekdaysPickedListener());
|
|
||||||
dialog.setSelectedDays(reminder.getDays().toArray());
|
|
||||||
dialog.show(getFragmentManager(), "weekdayPicker");
|
|
||||||
}
|
|
||||||
|
|
||||||
@OnClick(R.id.buttonPickColor)
|
|
||||||
void showColorPicker()
|
|
||||||
{
|
|
||||||
int color = modifiedHabit.getColor();
|
|
||||||
ColorPickerDialog picker = colorPickerDialogFactory.create(color);
|
|
||||||
|
|
||||||
picker.setListener(c -> {
|
|
||||||
prefs.setDefaultHabitColor(c);
|
|
||||||
modifiedHabit.setColor(c);
|
|
||||||
helper.populateColor(c);
|
|
||||||
});
|
|
||||||
|
|
||||||
picker.show(getFragmentManager(), "picker");
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showTimePicker(int defaultHour, int defaultMin)
|
|
||||||
{
|
|
||||||
boolean is24HourMode = DateFormat.is24HourFormat(getContext());
|
|
||||||
TimePickerDialog timePicker =
|
|
||||||
TimePickerDialog.newInstance(new OnTimeSetListener(), defaultHour,
|
|
||||||
defaultMin, is24HourMode);
|
|
||||||
timePicker.show(getFragmentManager(), "timePicker");
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OnTimeSetListener
|
|
||||||
implements TimePickerDialog.OnTimeSetListener
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onTimeCleared(RadialPickerLayout view)
|
|
||||||
{
|
|
||||||
modifiedHabit.clearReminder();
|
|
||||||
helper.populateReminderFields(modifiedHabit);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onTimeSet(RadialPickerLayout view, int hour, int minute)
|
|
||||||
{
|
|
||||||
Reminder reminder =
|
|
||||||
new Reminder(hour, minute, WeekdayList.EVERY_DAY);
|
|
||||||
modifiedHabit.setReminder(reminder);
|
|
||||||
helper.populateReminderFields(modifiedHabit);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class OnWeekdaysPickedListener
|
|
||||||
implements WeekdayPickerDialog.OnWeekdaysPickedListener
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
public void onWeekdaysPicked(boolean[] selectedDays)
|
|
||||||
{
|
|
||||||
if (isSelectionEmpty(selectedDays)) Arrays.fill(selectedDays, true);
|
|
||||||
|
|
||||||
Reminder oldReminder = modifiedHabit.getReminder();
|
|
||||||
modifiedHabit.setReminder(
|
|
||||||
new Reminder(oldReminder.getHour(), oldReminder.getMinute(),
|
|
||||||
new WeekdayList(selectedDays)));
|
|
||||||
helper.populateReminderFields(modifiedHabit);
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean isSelectionEmpty(boolean[] selectedDays)
|
|
||||||
{
|
|
||||||
for (boolean d : selectedDays) if (d) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,195 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.activities.habits.edit;
|
|
||||||
|
|
||||||
import android.annotation.*;
|
|
||||||
import android.support.v4.app.*;
|
|
||||||
import android.view.*;
|
|
||||||
import android.widget.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.R;
|
|
||||||
import org.isoron.uhabits.models.*;
|
|
||||||
import org.isoron.uhabits.utils.*;
|
|
||||||
|
|
||||||
import butterknife.*;
|
|
||||||
|
|
||||||
public class BaseDialogHelper
|
|
||||||
{
|
|
||||||
private DialogFragment frag;
|
|
||||||
|
|
||||||
@BindView(R.id.tvName)
|
|
||||||
TextView tvName;
|
|
||||||
|
|
||||||
@BindView(R.id.tvDescription)
|
|
||||||
TextView tvDescription;
|
|
||||||
|
|
||||||
@BindView(R.id.tvFreqNum)
|
|
||||||
TextView tvFreqNum;
|
|
||||||
|
|
||||||
@BindView(R.id.tvFreqDen)
|
|
||||||
TextView tvFreqDen;
|
|
||||||
|
|
||||||
@BindView(R.id.tvReminderTime)
|
|
||||||
TextView tvReminderTime;
|
|
||||||
|
|
||||||
@BindView(R.id.tvReminderDays)
|
|
||||||
TextView tvReminderDays;
|
|
||||||
|
|
||||||
@BindView(R.id.sFrequency)
|
|
||||||
Spinner sFrequency;
|
|
||||||
|
|
||||||
@BindView(R.id.llCustomFrequency)
|
|
||||||
ViewGroup llCustomFrequency;
|
|
||||||
|
|
||||||
@BindView(R.id.llReminderDays)
|
|
||||||
ViewGroup llReminderDays;
|
|
||||||
|
|
||||||
public BaseDialogHelper(DialogFragment frag, View view)
|
|
||||||
{
|
|
||||||
this.frag = frag;
|
|
||||||
ButterKnife.bind(this, view);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected void populateForm(final Habit habit)
|
|
||||||
{
|
|
||||||
if (habit.getName() != null) tvName.setText(habit.getName());
|
|
||||||
if (habit.getDescription() != null)
|
|
||||||
tvDescription.setText(habit.getDescription());
|
|
||||||
|
|
||||||
populateColor(habit.getColor());
|
|
||||||
populateFrequencyFields(habit);
|
|
||||||
populateReminderFields(habit);
|
|
||||||
}
|
|
||||||
|
|
||||||
void parseFormIntoHabit(Habit habit)
|
|
||||||
{
|
|
||||||
habit.setName(tvName.getText().toString().trim());
|
|
||||||
habit.setDescription(tvDescription.getText().toString().trim());
|
|
||||||
String freqNum = tvFreqNum.getText().toString();
|
|
||||||
String freqDen = tvFreqDen.getText().toString();
|
|
||||||
if (!freqNum.isEmpty() && !freqDen.isEmpty())
|
|
||||||
{
|
|
||||||
int numerator = Integer.parseInt(freqNum);
|
|
||||||
int denominator = Integer.parseInt(freqDen);
|
|
||||||
habit.setFrequency(new Frequency(numerator, denominator));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void populateColor(int paletteColor)
|
|
||||||
{
|
|
||||||
tvName.setTextColor(
|
|
||||||
ColorUtils.getColor(frag.getContext(), paletteColor));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
void populateFrequencyFields(Habit habit)
|
|
||||||
{
|
|
||||||
int quickSelectPosition = -1;
|
|
||||||
|
|
||||||
Frequency freq = habit.getFrequency();
|
|
||||||
|
|
||||||
if (freq.equals(Frequency.DAILY))
|
|
||||||
quickSelectPosition = 0;
|
|
||||||
|
|
||||||
else if (freq.equals(Frequency.WEEKLY))
|
|
||||||
quickSelectPosition = 1;
|
|
||||||
|
|
||||||
else if (freq.equals(Frequency.TWO_TIMES_PER_WEEK))
|
|
||||||
quickSelectPosition = 2;
|
|
||||||
|
|
||||||
else if (freq.equals(Frequency.FIVE_TIMES_PER_WEEK))
|
|
||||||
quickSelectPosition = 3;
|
|
||||||
|
|
||||||
if (quickSelectPosition >= 0)
|
|
||||||
showSimplifiedFrequency(quickSelectPosition);
|
|
||||||
|
|
||||||
else showCustomFrequency();
|
|
||||||
|
|
||||||
tvFreqNum.setText(Integer.toString(freq.getNumerator()));
|
|
||||||
tvFreqDen.setText(Integer.toString(freq.getDenominator()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressWarnings("ConstantConditions")
|
|
||||||
void populateReminderFields(Habit habit)
|
|
||||||
{
|
|
||||||
if (!habit.hasReminder())
|
|
||||||
{
|
|
||||||
tvReminderTime.setText(R.string.reminder_off);
|
|
||||||
llReminderDays.setVisibility(View.GONE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Reminder reminder = habit.getReminder();
|
|
||||||
|
|
||||||
String time =
|
|
||||||
DateUtils.formatTime(frag.getContext(), reminder.getHour(),
|
|
||||||
reminder.getMinute());
|
|
||||||
tvReminderTime.setText(time);
|
|
||||||
llReminderDays.setVisibility(View.VISIBLE);
|
|
||||||
|
|
||||||
boolean weekdays[] = reminder.getDays().toArray();
|
|
||||||
tvReminderDays.setText(
|
|
||||||
DateUtils.formatWeekdayList(frag.getContext(), weekdays));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void showCustomFrequency()
|
|
||||||
{
|
|
||||||
sFrequency.setVisibility(View.GONE);
|
|
||||||
llCustomFrequency.setVisibility(View.VISIBLE);
|
|
||||||
}
|
|
||||||
|
|
||||||
@SuppressLint("SetTextI18n")
|
|
||||||
private void showSimplifiedFrequency(int quickSelectPosition)
|
|
||||||
{
|
|
||||||
sFrequency.setVisibility(View.VISIBLE);
|
|
||||||
sFrequency.setSelection(quickSelectPosition);
|
|
||||||
llCustomFrequency.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean validate(Habit habit)
|
|
||||||
{
|
|
||||||
Boolean valid = true;
|
|
||||||
|
|
||||||
if (habit.getName().length() == 0)
|
|
||||||
{
|
|
||||||
tvName.setError(
|
|
||||||
frag.getString(R.string.validation_name_should_not_be_blank));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
Frequency freq = habit.getFrequency();
|
|
||||||
|
|
||||||
if (freq.getNumerator() <= 0)
|
|
||||||
{
|
|
||||||
tvFreqNum.setError(
|
|
||||||
frag.getString(R.string.validation_number_should_be_positive));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (freq.getNumerator() > freq.getDenominator())
|
|
||||||
{
|
|
||||||
tvFreqNum.setError(
|
|
||||||
frag.getString(R.string.validation_at_most_one_rep_per_day));
|
|
||||||
valid = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return valid;
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,54 +0,0 @@
|
|||||||
/*
|
|
||||||
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
|
||||||
*
|
|
||||||
* This file is part of Loop Habit Tracker.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
|
||||||
* it under the terms of the GNU General Public License as published by the
|
|
||||||
* Free Software Foundation, either version 3 of the License, or (at your
|
|
||||||
* option) any later version.
|
|
||||||
*
|
|
||||||
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
|
||||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
|
||||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
||||||
* more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU General Public License along
|
|
||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package org.isoron.uhabits.activities.habits.edit;
|
|
||||||
|
|
||||||
import com.google.auto.factory.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
|
||||||
import org.isoron.uhabits.commands.*;
|
|
||||||
import org.isoron.uhabits.models.*;
|
|
||||||
|
|
||||||
@AutoFactory(allowSubclasses = true)
|
|
||||||
public class CreateHabitDialog extends BaseDialog
|
|
||||||
{
|
|
||||||
@Override
|
|
||||||
protected int getTitle()
|
|
||||||
{
|
|
||||||
return R.string.create_habit;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void initializeHabits()
|
|
||||||
{
|
|
||||||
modifiedHabit = modelFactory.buildHabit();
|
|
||||||
modifiedHabit.setFrequency(Frequency.DAILY);
|
|
||||||
modifiedHabit.setColor(
|
|
||||||
prefs.getDefaultHabitColor(modifiedHabit.getColor()));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void saveHabit()
|
|
||||||
{
|
|
||||||
Command command = appComponent
|
|
||||||
.getCreateHabitCommandFactory()
|
|
||||||
.create(habitList, modifiedHabit);
|
|
||||||
commandRunner.execute(command, null);
|
|
||||||
}
|
|
||||||
}
|
|
@ -0,0 +1,114 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.edit.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.text.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.utils.AttributeSetUtils.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An EditText that shows an example usage when there is no text
|
||||||
|
* currently set. The example disappears when the widget gains focus.
|
||||||
|
*/
|
||||||
|
public class ExampleEditText extends EditText
|
||||||
|
implements View.OnFocusChangeListener
|
||||||
|
{
|
||||||
|
|
||||||
|
private String example;
|
||||||
|
|
||||||
|
private String realText;
|
||||||
|
|
||||||
|
private int color;
|
||||||
|
|
||||||
|
private int exampleColor;
|
||||||
|
|
||||||
|
private int inputType;
|
||||||
|
|
||||||
|
public ExampleEditText(Context context, @Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
if (attrs != null)
|
||||||
|
example = getAttribute(context, attrs, "example", "");
|
||||||
|
|
||||||
|
inputType = getInputType();
|
||||||
|
realText = getText().toString();
|
||||||
|
color = getCurrentTextColor();
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRealText()
|
||||||
|
{
|
||||||
|
if(hasFocus()) return getText().toString();
|
||||||
|
else return realText;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFocusChange(View v, boolean hasFocus)
|
||||||
|
{
|
||||||
|
if (!hasFocus) realText = getText().toString();
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setExample(String example)
|
||||||
|
{
|
||||||
|
this.example = example;
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setRealText(String realText)
|
||||||
|
{
|
||||||
|
this.realText = realText;
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
StyledResources sr = new StyledResources(getContext());
|
||||||
|
exampleColor = sr.getColor(R.attr.mediumContrastTextColor);
|
||||||
|
setOnFocusChangeListener(this);
|
||||||
|
updateText();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateText()
|
||||||
|
{
|
||||||
|
if (realText.isEmpty() && !isFocused())
|
||||||
|
{
|
||||||
|
setTextColor(exampleColor);
|
||||||
|
setText(example);
|
||||||
|
setInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
setText(realText);
|
||||||
|
setTextColor(color);
|
||||||
|
setInputType(inputType);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,164 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.edit.views;
|
||||||
|
|
||||||
|
import android.annotation.*;
|
||||||
|
import android.content.*;
|
||||||
|
import android.content.res.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
|
||||||
|
import butterknife.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.R.id.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class FrequencyPanel extends FrameLayout
|
||||||
|
{
|
||||||
|
@BindView(numerator)
|
||||||
|
TextView tvNumerator;
|
||||||
|
|
||||||
|
@BindView(R.id.denominator)
|
||||||
|
TextView tvDenominator;
|
||||||
|
|
||||||
|
@BindView(R.id.spinner)
|
||||||
|
Spinner spinner;
|
||||||
|
|
||||||
|
@BindView(R.id.customFreqPanel)
|
||||||
|
ViewGroup customFreqPanel;
|
||||||
|
|
||||||
|
public FrequencyPanel(@NonNull Context context,
|
||||||
|
@Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
View view = inflate(context, R.layout.edit_habit_frequency, null);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
addView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public Frequency getFrequency()
|
||||||
|
{
|
||||||
|
String freqNum = tvNumerator.getText().toString();
|
||||||
|
String freqDen = tvDenominator.getText().toString();
|
||||||
|
|
||||||
|
if (!freqNum.isEmpty() && !freqDen.isEmpty())
|
||||||
|
{
|
||||||
|
int numerator = Integer.parseInt(freqNum);
|
||||||
|
int denominator = Integer.parseInt(freqDen);
|
||||||
|
return new Frequency(numerator, denominator);
|
||||||
|
}
|
||||||
|
|
||||||
|
return Frequency.DAILY;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("SetTextI18n")
|
||||||
|
public void setFrequency(@NonNull Frequency freq)
|
||||||
|
{
|
||||||
|
int position = getQuickSelectPosition(freq);
|
||||||
|
|
||||||
|
if (position >= 0) showSimplifiedFrequency(position);
|
||||||
|
else showCustomFrequency();
|
||||||
|
|
||||||
|
tvNumerator.setText(Integer.toString(freq.getNumerator()));
|
||||||
|
tvDenominator.setText(Integer.toString(freq.getDenominator()));
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnItemSelected(R.id.spinner)
|
||||||
|
public void onFrequencySelected(int position)
|
||||||
|
{
|
||||||
|
if (position < 0 || position > 4) throw new IllegalArgumentException();
|
||||||
|
int freqNums[] = { 1, 1, 2, 5, 3 };
|
||||||
|
int freqDens[] = { 1, 7, 7, 7, 7 };
|
||||||
|
setFrequency(new Frequency(freqNums[position], freqDens[position]));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validate()
|
||||||
|
{
|
||||||
|
boolean valid = true;
|
||||||
|
Resources res = getResources();
|
||||||
|
|
||||||
|
String freqNum = tvNumerator.getText().toString();
|
||||||
|
String freqDen = tvDenominator.getText().toString();
|
||||||
|
|
||||||
|
if (freqDen.isEmpty())
|
||||||
|
{
|
||||||
|
tvDenominator.setError(
|
||||||
|
res.getString(R.string.validation_show_not_be_blank));
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (freqNum.isEmpty())
|
||||||
|
{
|
||||||
|
tvNumerator.setError(
|
||||||
|
res.getString(R.string.validation_show_not_be_blank));
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!valid) return false;
|
||||||
|
|
||||||
|
int numerator = Integer.parseInt(freqNum);
|
||||||
|
int denominator = Integer.parseInt(freqDen);
|
||||||
|
|
||||||
|
if (numerator <= 0)
|
||||||
|
{
|
||||||
|
tvNumerator.setError(
|
||||||
|
res.getString(R.string.validation_number_should_be_positive));
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (numerator > denominator)
|
||||||
|
{
|
||||||
|
tvNumerator.setError(
|
||||||
|
res.getString(R.string.validation_at_most_one_rep_per_day));
|
||||||
|
valid = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return valid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getQuickSelectPosition(@NonNull Frequency freq)
|
||||||
|
{
|
||||||
|
if (freq.equals(Frequency.DAILY)) return 0;
|
||||||
|
if (freq.equals(Frequency.WEEKLY)) return 1;
|
||||||
|
if (freq.equals(Frequency.TWO_TIMES_PER_WEEK)) return 2;
|
||||||
|
if (freq.equals(Frequency.FIVE_TIMES_PER_WEEK)) return 3;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showCustomFrequency()
|
||||||
|
{
|
||||||
|
spinner.setVisibility(View.GONE);
|
||||||
|
customFreqPanel.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void showSimplifiedFrequency(int quickSelectPosition)
|
||||||
|
{
|
||||||
|
spinner.setVisibility(View.VISIBLE);
|
||||||
|
spinner.setSelection(quickSelectPosition);
|
||||||
|
customFreqPanel.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,152 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.edit.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.content.res.*;
|
||||||
|
import android.os.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.activities.common.views.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
|
import butterknife.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class NameDescriptionPanel extends FrameLayout
|
||||||
|
{
|
||||||
|
@BindView(R.id.tvName)
|
||||||
|
EditText tvName;
|
||||||
|
|
||||||
|
@BindView(R.id.tvDescription)
|
||||||
|
ExampleEditText tvDescription;
|
||||||
|
|
||||||
|
private int color;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Controller controller;
|
||||||
|
|
||||||
|
public NameDescriptionPanel(@NonNull Context context,
|
||||||
|
@Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
View view = inflate(context, R.layout.edit_habit_name, null);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
addView(view);
|
||||||
|
|
||||||
|
controller = new Controller() {};
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getColor()
|
||||||
|
{
|
||||||
|
return color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color)
|
||||||
|
{
|
||||||
|
this.color = color;
|
||||||
|
tvName.setTextColor(ColorUtils.getColor(getContext(), color));
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getDescription()
|
||||||
|
{
|
||||||
|
return tvDescription.getRealText().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public String getName()
|
||||||
|
{
|
||||||
|
return tvName.getText().toString().trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void populateFrom(@NonNull Habit habit)
|
||||||
|
{
|
||||||
|
Resources res = getResources();
|
||||||
|
|
||||||
|
if(habit.isNumerical())
|
||||||
|
tvDescription.setExample(res.getString(R.string.example_question_numerical));
|
||||||
|
else
|
||||||
|
tvDescription.setExample(res.getString(R.string.example_question_boolean));
|
||||||
|
|
||||||
|
setColor(habit.getColor());
|
||||||
|
tvName.setText(habit.getName());
|
||||||
|
tvDescription.setRealText(habit.getDescription());
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validate()
|
||||||
|
{
|
||||||
|
Resources res = getResources();
|
||||||
|
|
||||||
|
if (getName().isEmpty())
|
||||||
|
{
|
||||||
|
tvName.setError(
|
||||||
|
res.getString(R.string.validation_name_should_not_be_blank));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Parcelable state)
|
||||||
|
{
|
||||||
|
BundleSavedState bss = (BundleSavedState) state;
|
||||||
|
setColor(bss.bundle.getInt("color"));
|
||||||
|
super.onRestoreInstanceState(bss.getSuperState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Parcelable onSaveInstanceState()
|
||||||
|
{
|
||||||
|
Parcelable superState = super.onSaveInstanceState();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
bundle.putInt("color", color);
|
||||||
|
return new BundleSavedState(superState, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.buttonPickColor)
|
||||||
|
void showColorPicker()
|
||||||
|
{
|
||||||
|
controller.onColorPickerClicked(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(@NonNull Controller controller)
|
||||||
|
{
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the user has clicked the widget to select a new
|
||||||
|
* color for the habit.
|
||||||
|
*
|
||||||
|
* @param previousColor the color previously selected
|
||||||
|
*/
|
||||||
|
default void onColorPickerClicked(int previousColor) {}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,194 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.edit.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.os.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import com.android.datetimepicker.time.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.activities.common.dialogs.*;
|
||||||
|
import org.isoron.uhabits.activities.common.views.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
|
||||||
|
import butterknife.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.utils.DateUtils.*;
|
||||||
|
|
||||||
|
public class ReminderPanel extends FrameLayout
|
||||||
|
implements TimePickerDialog.OnTimeSetListener,
|
||||||
|
WeekdayPickerDialog.OnWeekdaysPickedListener
|
||||||
|
{
|
||||||
|
@BindView(R.id.tvReminderTime)
|
||||||
|
TextView tvReminderTime;
|
||||||
|
|
||||||
|
@BindView(R.id.llReminderDays)
|
||||||
|
ViewGroup llReminderDays;
|
||||||
|
|
||||||
|
@BindView(R.id.tvReminderDays)
|
||||||
|
TextView tvReminderDays;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Reminder reminder;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Controller controller;
|
||||||
|
|
||||||
|
public ReminderPanel(@NonNull Context context, @Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
View view = inflate(context, R.layout.edit_habit_reminder, null);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
addView(view);
|
||||||
|
|
||||||
|
controller = new Controller() {};
|
||||||
|
setReminder(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
public Reminder getReminder()
|
||||||
|
{
|
||||||
|
return reminder;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setReminder(@Nullable Reminder reminder)
|
||||||
|
{
|
||||||
|
this.reminder = reminder;
|
||||||
|
|
||||||
|
if (reminder == null)
|
||||||
|
{
|
||||||
|
tvReminderTime.setText(R.string.reminder_off);
|
||||||
|
llReminderDays.setVisibility(View.GONE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Context ctx = getContext();
|
||||||
|
String time = formatTime(ctx, reminder.getHour(), reminder.getMinute());
|
||||||
|
tvReminderTime.setText(time);
|
||||||
|
llReminderDays.setVisibility(View.VISIBLE);
|
||||||
|
|
||||||
|
boolean weekdays[] = reminder.getDays().toArray();
|
||||||
|
tvReminderDays.setText(formatWeekdayList(ctx, weekdays));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimeCleared(RadialPickerLayout view)
|
||||||
|
{
|
||||||
|
setReminder(null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTimeSet(RadialPickerLayout view, int hour, int minute)
|
||||||
|
{
|
||||||
|
setReminder(new Reminder(hour, minute, WeekdayList.EVERY_DAY));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWeekdaysSet(WeekdayList selectedDays)
|
||||||
|
{
|
||||||
|
if (reminder == null) return;
|
||||||
|
if (selectedDays.isEmpty()) selectedDays = WeekdayList.EVERY_DAY;
|
||||||
|
|
||||||
|
setReminder(new Reminder(reminder.getHour(), reminder.getMinute(),
|
||||||
|
selectedDays));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(@NonNull Controller controller)
|
||||||
|
{
|
||||||
|
this.controller = controller;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onRestoreInstanceState(Parcelable state)
|
||||||
|
{
|
||||||
|
BundleSavedState bss = (BundleSavedState) state;
|
||||||
|
if (!bss.bundle.isEmpty())
|
||||||
|
{
|
||||||
|
int days = bss.bundle.getInt("days");
|
||||||
|
int hour = bss.bundle.getInt("hour");
|
||||||
|
int minute = bss.bundle.getInt("minute");
|
||||||
|
reminder = new Reminder(hour, minute, new WeekdayList(days));
|
||||||
|
setReminder(reminder);
|
||||||
|
}
|
||||||
|
super.onRestoreInstanceState(bss.getSuperState());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Parcelable onSaveInstanceState()
|
||||||
|
{
|
||||||
|
Parcelable superState = super.onSaveInstanceState();
|
||||||
|
Bundle bundle = new Bundle();
|
||||||
|
if (reminder != null)
|
||||||
|
{
|
||||||
|
bundle.putInt("days", reminder.getDays().toInteger());
|
||||||
|
bundle.putInt("hour", reminder.getHour());
|
||||||
|
bundle.putInt("minute", reminder.getMinute());
|
||||||
|
}
|
||||||
|
return new BundleSavedState(superState, bundle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.tvReminderTime)
|
||||||
|
void onDateSpinnerClick()
|
||||||
|
{
|
||||||
|
int hour = 8;
|
||||||
|
int min = 0;
|
||||||
|
|
||||||
|
if (reminder != null)
|
||||||
|
{
|
||||||
|
hour = reminder.getHour();
|
||||||
|
min = reminder.getMinute();
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.onTimeClicked(hour, min);
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClick(R.id.tvReminderDays)
|
||||||
|
void onWeekdayClicked()
|
||||||
|
{
|
||||||
|
if (reminder == null) return;
|
||||||
|
controller.onWeekdayClicked(reminder.getDays());
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Controller
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the user has clicked the widget to change the time of
|
||||||
|
* the reminder.
|
||||||
|
*
|
||||||
|
* @param currentHour hour previously picked by the user
|
||||||
|
* @param currentMin minute previously picked by the user
|
||||||
|
*/
|
||||||
|
default void onTimeClicked(int currentHour, int currentMin) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the used has clicked the widget to change the days
|
||||||
|
* of the reminder.
|
||||||
|
*
|
||||||
|
* @param currentDays days previously selected by the user.
|
||||||
|
*/
|
||||||
|
default void onWeekdayClicked(WeekdayList currentDays) {}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,90 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.edit.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.content.res.*;
|
||||||
|
import android.icu.text.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
|
||||||
|
import butterknife.*;
|
||||||
|
|
||||||
|
|
||||||
|
public class TargetPanel extends FrameLayout
|
||||||
|
{
|
||||||
|
private DecimalFormat valueFormatter = new DecimalFormat("#.##");
|
||||||
|
|
||||||
|
@BindView(R.id.tvUnit)
|
||||||
|
ExampleEditText tvUnit;
|
||||||
|
|
||||||
|
@BindView(R.id.tvTargetCount)
|
||||||
|
TextView tvTargetValue;
|
||||||
|
|
||||||
|
public TargetPanel(@NonNull Context context, @Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
|
||||||
|
View view = inflate(context, R.layout.edit_habit_target, null);
|
||||||
|
ButterKnife.bind(this, view);
|
||||||
|
addView(view);
|
||||||
|
}
|
||||||
|
|
||||||
|
public double getTargetValue()
|
||||||
|
{
|
||||||
|
String sValue = tvTargetValue.getText().toString();
|
||||||
|
return Double.parseDouble(sValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setTargetValue(double targetValue)
|
||||||
|
{
|
||||||
|
tvTargetValue.setText(valueFormatter.format(targetValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getUnit()
|
||||||
|
{
|
||||||
|
return tvUnit.getRealText();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnit(String unit)
|
||||||
|
{
|
||||||
|
tvUnit.setRealText(unit);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean validate()
|
||||||
|
{
|
||||||
|
Resources res = getResources();
|
||||||
|
String sValue = tvTargetValue.getText().toString();
|
||||||
|
double value = Double.parseDouble(sValue);
|
||||||
|
|
||||||
|
if (value <= 0)
|
||||||
|
{
|
||||||
|
tvTargetValue.setError(
|
||||||
|
res.getString(R.string.validation_number_should_be_positive));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,102 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.list.controllers;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
|
import com.google.auto.factory.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.activities.habits.list.views.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.preferences.*;
|
||||||
|
|
||||||
|
@AutoFactory
|
||||||
|
public class NumberButtonController
|
||||||
|
{
|
||||||
|
@Nullable
|
||||||
|
private NumberButtonView view;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Listener listener;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private final Preferences prefs;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
private long timestamp;
|
||||||
|
|
||||||
|
public NumberButtonController(@Provided @NonNull Preferences prefs,
|
||||||
|
@NonNull Habit habit,
|
||||||
|
long timestamp)
|
||||||
|
{
|
||||||
|
this.habit = habit;
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.prefs = prefs;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onClick()
|
||||||
|
{
|
||||||
|
if (prefs.isShortToggleEnabled()) performEdit();
|
||||||
|
else performInvalidToggle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean onLongClick()
|
||||||
|
{
|
||||||
|
performEdit();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void performInvalidToggle()
|
||||||
|
{
|
||||||
|
if (listener != null) listener.onInvalidEdit();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void performEdit()
|
||||||
|
{
|
||||||
|
if (listener != null) listener.onEdit(habit, timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setListener(@Nullable Listener listener)
|
||||||
|
{
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setView(@Nullable NumberButtonView view)
|
||||||
|
{
|
||||||
|
this.view = view;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Listener
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Called when the user's attempt to edit the value is rejected.
|
||||||
|
*/
|
||||||
|
void onInvalidEdit();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a the user's attempt to edit the value has been accepted.
|
||||||
|
* @param habit the habit being edited
|
||||||
|
* @param timestamp the timestamp being edited
|
||||||
|
*/
|
||||||
|
void onEdit(@NonNull Habit habit, long timestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,189 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.list.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.content.res.*;
|
||||||
|
import android.graphics.*;
|
||||||
|
import android.icu.text.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.text.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.view.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.activities.habits.list.controllers.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.utils.AttributeSetUtils.*;
|
||||||
|
import static org.isoron.uhabits.utils.ColorUtils.*;
|
||||||
|
|
||||||
|
public class NumberButtonView extends View
|
||||||
|
{
|
||||||
|
private static Typeface BOLD_TYPEFACE =
|
||||||
|
Typeface.create("sans-serif-condensed", Typeface.BOLD);
|
||||||
|
|
||||||
|
private static Typeface NORMAL_TYPEFACE =
|
||||||
|
Typeface.create("sans-serif-condensed", Typeface.NORMAL);
|
||||||
|
|
||||||
|
private int color;
|
||||||
|
|
||||||
|
private double value;
|
||||||
|
|
||||||
|
private double threshold;
|
||||||
|
|
||||||
|
private String unit;
|
||||||
|
|
||||||
|
private RectF rect;
|
||||||
|
|
||||||
|
private TextPaint pRegular;
|
||||||
|
|
||||||
|
private Resources res;
|
||||||
|
|
||||||
|
private TextPaint pBold;
|
||||||
|
|
||||||
|
private int lightGrey;
|
||||||
|
|
||||||
|
private float em;
|
||||||
|
|
||||||
|
private int darkGrey;
|
||||||
|
|
||||||
|
public NumberButtonView(@Nullable Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberButtonView(@Nullable Context ctx, @Nullable AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(ctx, attrs);
|
||||||
|
init();
|
||||||
|
|
||||||
|
if (ctx != null && attrs != null)
|
||||||
|
{
|
||||||
|
int color = getIntAttribute(ctx, attrs, "color", 0);
|
||||||
|
int value = getIntAttribute(ctx, attrs, "value", 0);
|
||||||
|
int threshold = getIntAttribute(ctx, attrs, "threshold", 1);
|
||||||
|
String unit = getAttribute(ctx, attrs, "unit", "min");
|
||||||
|
setColor(getAndroidTestColor(color));
|
||||||
|
setThreshold(threshold);
|
||||||
|
setValue(value);
|
||||||
|
setUnit(unit);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param v
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String formatValue(double v)
|
||||||
|
{
|
||||||
|
if (v >= 1e9) return String.format("%.1fG", v / 1e9);
|
||||||
|
if (v >= 1e8) return String.format("%.0fM", v / 1e6);
|
||||||
|
if (v >= 1e7) return String.format("%.1fM", v / 1e6);
|
||||||
|
if (v >= 1e6) return String.format("%.1fM", v / 1e6);
|
||||||
|
if (v >= 1e5) return String.format("%.0fk", v / 1e3);
|
||||||
|
if (v >= 1e4) return String.format("%.1fk", v / 1e3);
|
||||||
|
if (v >= 1e3) return String.format("%.1fk", v / 1e3);
|
||||||
|
if (v >= 1e1) return new DecimalFormat("#.#").format(v);
|
||||||
|
return new DecimalFormat("#.##").format(v);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color)
|
||||||
|
{
|
||||||
|
this.color = color;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(final NumberButtonController controller)
|
||||||
|
{
|
||||||
|
setOnClickListener(v -> controller.onClick());
|
||||||
|
setOnLongClickListener(v -> controller.onLongClick());
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreshold(double threshold)
|
||||||
|
{
|
||||||
|
this.threshold = threshold;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnit(String unit)
|
||||||
|
{
|
||||||
|
this.unit = unit;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValue(double value)
|
||||||
|
{
|
||||||
|
this.value = value;
|
||||||
|
postInvalidate();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDraw(Canvas canvas)
|
||||||
|
{
|
||||||
|
int activeColor = lightGrey;
|
||||||
|
if(value > 0 && value < threshold) activeColor = darkGrey;
|
||||||
|
if(value >= threshold) activeColor = color;
|
||||||
|
|
||||||
|
pRegular.setColor(activeColor);
|
||||||
|
pBold.setColor(activeColor);
|
||||||
|
|
||||||
|
String fv = formatValue(value);
|
||||||
|
|
||||||
|
rect.set(0, 0, getWidth(), getHeight());
|
||||||
|
canvas.drawText(fv, rect.centerX(), rect.centerY(), pBold);
|
||||||
|
|
||||||
|
rect.offset(0, 1.2f * em);
|
||||||
|
canvas.drawText(unit, rect.centerX(), rect.centerY(), pRegular);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
|
||||||
|
{
|
||||||
|
int width = (int) res.getDimension(R.dimen.checkmarkWidth);
|
||||||
|
int height = (int) res.getDimension(R.dimen.checkmarkHeight);
|
||||||
|
setMeasuredDimension(width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
StyledResources sr = new StyledResources(getContext());
|
||||||
|
res = getContext().getResources();
|
||||||
|
|
||||||
|
rect = new RectF();
|
||||||
|
pRegular = new TextPaint();
|
||||||
|
pRegular.setTextSize(res.getDimension(R.dimen.smallerTextSize));
|
||||||
|
pRegular.setTypeface(NORMAL_TYPEFACE);
|
||||||
|
pRegular.setAntiAlias(true);
|
||||||
|
pRegular.setTextAlign(Paint.Align.CENTER);
|
||||||
|
|
||||||
|
pBold = new TextPaint();
|
||||||
|
pBold.setTextSize(res.getDimension(R.dimen.smallTextSize));
|
||||||
|
pBold.setTypeface(BOLD_TYPEFACE);
|
||||||
|
pBold.setAntiAlias(true);
|
||||||
|
pBold.setTextAlign(Paint.Align.CENTER);
|
||||||
|
|
||||||
|
em = pBold.measureText("m");
|
||||||
|
lightGrey = sr.getColor(R.attr.lowContrastTextColor);
|
||||||
|
darkGrey = sr.getColor(R.attr.mediumContrastTextColor);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,261 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.list.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.activities.habits.list.*;
|
||||||
|
import org.isoron.uhabits.activities.habits.list.controllers.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.preferences.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import static android.view.View.MeasureSpec.*;
|
||||||
|
import static org.isoron.uhabits.utils.AttributeSetUtils.*;
|
||||||
|
import static org.isoron.uhabits.utils.ColorUtils.*;
|
||||||
|
|
||||||
|
public class NumberPanelView extends LinearLayout
|
||||||
|
implements Preferences.Listener
|
||||||
|
{
|
||||||
|
private static final int LEFT_TO_RIGHT = 0;
|
||||||
|
|
||||||
|
private static final int RIGHT_TO_LEFT = 1;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Preferences prefs;
|
||||||
|
|
||||||
|
private double values[];
|
||||||
|
|
||||||
|
private double threshold;
|
||||||
|
|
||||||
|
private int nButtons;
|
||||||
|
|
||||||
|
private int color;
|
||||||
|
|
||||||
|
private Controller controller;
|
||||||
|
|
||||||
|
private String unit;
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
private int dataOffset;
|
||||||
|
|
||||||
|
public NumberPanelView(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberPanelView(Context ctx, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(ctx, attrs);
|
||||||
|
init();
|
||||||
|
|
||||||
|
if (ctx != null && attrs != null)
|
||||||
|
{
|
||||||
|
int paletteColor = getIntAttribute(ctx, attrs, "color", 0);
|
||||||
|
setColor(getAndroidTestColor(paletteColor));
|
||||||
|
setButtonCount(getIntAttribute(ctx, attrs, "button_count", 5));
|
||||||
|
setThreshold(getIntAttribute(ctx, attrs, "threshold", 1));
|
||||||
|
setUnit(getAttribute(ctx, attrs, "unit", "min"));
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isInEditMode()) initEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setUnit(String unit)
|
||||||
|
{
|
||||||
|
this.unit = unit;
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void initEditMode()
|
||||||
|
{
|
||||||
|
double values[] = new double[nButtons];
|
||||||
|
for(int i = 0; i < nButtons; i++)
|
||||||
|
values[i] = new Random().nextDouble() * (threshold * 3);
|
||||||
|
setValues(values);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NumberButtonView indexToButton(int i)
|
||||||
|
{
|
||||||
|
int position = i;
|
||||||
|
if (getCheckmarkOrder() == RIGHT_TO_LEFT) position = nButtons - i - 1;
|
||||||
|
return (NumberButtonView) getChildAt(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCheckmarkOrderChanged()
|
||||||
|
{
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setButtonCount(int newButtonCount)
|
||||||
|
{
|
||||||
|
if (nButtons != newButtonCount)
|
||||||
|
{
|
||||||
|
nButtons = newButtonCount;
|
||||||
|
addButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setColor(int color)
|
||||||
|
{
|
||||||
|
this.color = color;
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setController(Controller controller)
|
||||||
|
{
|
||||||
|
this.controller = controller;
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDataOffset(int dataOffset)
|
||||||
|
{
|
||||||
|
this.dataOffset = dataOffset;
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setHabit(@NonNull Habit habit)
|
||||||
|
{
|
||||||
|
this.habit = habit;
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setThreshold(double threshold)
|
||||||
|
{
|
||||||
|
this.threshold = threshold;
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setValues(double[] values)
|
||||||
|
{
|
||||||
|
this.values = values;
|
||||||
|
setupButtons();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onAttachedToWindow()
|
||||||
|
{
|
||||||
|
super.onAttachedToWindow();
|
||||||
|
if (prefs != null) prefs.addListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onDetachedFromWindow()
|
||||||
|
{
|
||||||
|
if (prefs != null) prefs.removeListener(this);
|
||||||
|
super.onDetachedFromWindow();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMeasure(int widthSpec, int heightSpec)
|
||||||
|
{
|
||||||
|
float buttonWidth = getResources().getDimension(R.dimen.checkmarkWidth);
|
||||||
|
float buttonHeight =
|
||||||
|
getResources().getDimension(R.dimen.checkmarkHeight);
|
||||||
|
|
||||||
|
float width = buttonWidth * nButtons;
|
||||||
|
|
||||||
|
widthSpec = makeMeasureSpec((int) width, EXACTLY);
|
||||||
|
heightSpec = makeMeasureSpec((int) buttonHeight, EXACTLY);
|
||||||
|
|
||||||
|
super.onMeasure(widthSpec, heightSpec);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void addButtons()
|
||||||
|
{
|
||||||
|
removeAllViews();
|
||||||
|
|
||||||
|
for (int i = 0; i < nButtons; i++)
|
||||||
|
addView(new NumberButtonView(getContext()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private int getCheckmarkOrder()
|
||||||
|
{
|
||||||
|
if (prefs == null) return LEFT_TO_RIGHT;
|
||||||
|
return prefs.shouldReverseCheckmarks() ? RIGHT_TO_LEFT : LEFT_TO_RIGHT;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
Context appContext = getContext().getApplicationContext();
|
||||||
|
if (appContext instanceof HabitsApplication)
|
||||||
|
{
|
||||||
|
HabitsApplication app = (HabitsApplication) appContext;
|
||||||
|
prefs = app.getComponent().getPreferences();
|
||||||
|
}
|
||||||
|
|
||||||
|
setWillNotDraw(false);
|
||||||
|
values = new double[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupButtonControllers(long timestamp,
|
||||||
|
NumberButtonView buttonView)
|
||||||
|
{
|
||||||
|
if (controller == null) return;
|
||||||
|
if (!(getContext() instanceof ListHabitsActivity)) return;
|
||||||
|
|
||||||
|
ListHabitsActivity activity = (ListHabitsActivity) getContext();
|
||||||
|
NumberButtonControllerFactory buttonControllerFactory = activity
|
||||||
|
.getListHabitsComponent()
|
||||||
|
.getNumberButtonControllerFactory();
|
||||||
|
|
||||||
|
NumberButtonController buttonController =
|
||||||
|
buttonControllerFactory.create(habit, timestamp);
|
||||||
|
buttonController.setListener(controller);
|
||||||
|
buttonController.setView(buttonView);
|
||||||
|
buttonView.setController(buttonController);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setupButtons()
|
||||||
|
{
|
||||||
|
long timestamp = DateUtils.getStartOfToday();
|
||||||
|
long day = DateUtils.millisecondsInOneDay;
|
||||||
|
timestamp -= day * dataOffset;
|
||||||
|
|
||||||
|
for (int i = 0; i < nButtons; i++)
|
||||||
|
{
|
||||||
|
NumberButtonView buttonView = indexToButton(i);
|
||||||
|
if (i + dataOffset >= values.length) break;
|
||||||
|
buttonView.setValue(values[i + dataOffset]);
|
||||||
|
buttonView.setColor(color);
|
||||||
|
buttonView.setThreshold(threshold);
|
||||||
|
buttonView.setUnit(unit);
|
||||||
|
setupButtonControllers(timestamp, buttonView);
|
||||||
|
timestamp -= day;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Controller extends NumberButtonController.Listener
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,115 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.activities.habits.show.views;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
import android.widget.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.R;
|
||||||
|
import org.isoron.uhabits.activities.common.views.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.tasks.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import butterknife.*;
|
||||||
|
|
||||||
|
public class BarCard extends HabitCard
|
||||||
|
{
|
||||||
|
@BindView(R.id.barChart)
|
||||||
|
BarChart chart;
|
||||||
|
|
||||||
|
@BindView(R.id.title)
|
||||||
|
TextView title;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private TaskRunner taskRunner;
|
||||||
|
|
||||||
|
public BarCard(Context context)
|
||||||
|
{
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BarCard(Context context, AttributeSet attrs)
|
||||||
|
{
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void refreshData()
|
||||||
|
{
|
||||||
|
if(taskRunner == null) return;
|
||||||
|
taskRunner.execute(new RefreshTask(getHabit()));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void init()
|
||||||
|
{
|
||||||
|
inflate(getContext(), R.layout.show_habit_bar, this);
|
||||||
|
ButterKnife.bind(this);
|
||||||
|
|
||||||
|
Context appContext = getContext().getApplicationContext();
|
||||||
|
if (appContext instanceof HabitsApplication)
|
||||||
|
{
|
||||||
|
HabitsApplication app = (HabitsApplication) appContext;
|
||||||
|
taskRunner = app.getComponent().getTaskRunner();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isInEditMode()) initEditMode();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void initEditMode()
|
||||||
|
{
|
||||||
|
int color = ColorUtils.getAndroidTestColor(1);
|
||||||
|
title.setTextColor(color);
|
||||||
|
chart.setColor(color);
|
||||||
|
chart.populateWithRandomData();
|
||||||
|
}
|
||||||
|
|
||||||
|
private class RefreshTask implements Task
|
||||||
|
{
|
||||||
|
private final Habit habit;
|
||||||
|
|
||||||
|
public RefreshTask(Habit habit) {this.habit = habit;}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void doInBackground()
|
||||||
|
{
|
||||||
|
long today = DateUtils.getStartOfToday();
|
||||||
|
List<Checkmark> checkmarks =
|
||||||
|
habit.getCheckmarks().getByInterval(0, today);
|
||||||
|
chart.setCheckmarks(checkmarks);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPreExecute()
|
||||||
|
{
|
||||||
|
int color = ColorUtils.getColor(getContext(), habit.getColor());
|
||||||
|
title.setTextColor(color);
|
||||||
|
chart.setColor(color);
|
||||||
|
chart.setTarget(habit.getTargetValue());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,78 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Command to toggle a repetition.
|
||||||
|
*/
|
||||||
|
public class CreateRepetitionCommand extends Command
|
||||||
|
{
|
||||||
|
@NonNull
|
||||||
|
private final Habit habit;
|
||||||
|
|
||||||
|
private final long timestamp;
|
||||||
|
|
||||||
|
private final int value;
|
||||||
|
|
||||||
|
private Repetition previousRep;
|
||||||
|
|
||||||
|
private Repetition newRep;
|
||||||
|
|
||||||
|
public CreateRepetitionCommand(@NonNull Habit habit,
|
||||||
|
long timestamp,
|
||||||
|
int value)
|
||||||
|
{
|
||||||
|
this.timestamp = timestamp;
|
||||||
|
this.habit = habit;
|
||||||
|
this.value = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void execute()
|
||||||
|
{
|
||||||
|
RepetitionList reps = habit.getRepetitions();
|
||||||
|
|
||||||
|
previousRep = reps.getByTimestamp(timestamp);
|
||||||
|
if (previousRep != null) reps.remove(previousRep);
|
||||||
|
|
||||||
|
newRep = new Repetition(timestamp, value);
|
||||||
|
reps.add(newRep);
|
||||||
|
|
||||||
|
habit.invalidateNewerThan(timestamp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public Habit getHabit()
|
||||||
|
{
|
||||||
|
return habit;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void undo()
|
||||||
|
{
|
||||||
|
habit.getRepetitions().remove(newRep);
|
||||||
|
if (previousRep != null) habit.getRepetitions().add(previousRep);
|
||||||
|
habit.invalidateNewerThan(timestamp);
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,69 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout style="@style/dialogFormRow"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="12dp"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/dialogFormLabel"
|
||||||
|
android:text="@string/repeat"/>
|
||||||
|
|
||||||
|
<android.support.v7.widget.AppCompatSpinner
|
||||||
|
android:id="@+id/spinner"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="25dp"
|
||||||
|
android:entries="@array/frequencyQuickSelect"
|
||||||
|
android:minWidth="400dp"
|
||||||
|
android:theme="@style/dialogFormText"
|
||||||
|
android:visibility="gone"/>
|
||||||
|
|
||||||
|
<org.apmem.tools.layouts.FlowLayout
|
||||||
|
android:id="@+id/customFreqPanel"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:gravity="fill"
|
||||||
|
android:visibility="visible">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/numerator"
|
||||||
|
style="@style/dialogFormInputLargeNumber"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/dialogFormText"
|
||||||
|
android:gravity="center"
|
||||||
|
android:text="@string/times_every"/>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/denominator"
|
||||||
|
style="@style/dialogFormInputLargeNumber"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/dialogFormText"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:paddingLeft="12dp"
|
||||||
|
android:text="@string/days"/>
|
||||||
|
|
||||||
|
</org.apmem.tools.layouts.FlowLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,70 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://isoron.org/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/dialogFormRow">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/tilName"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="6">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/tvName"
|
||||||
|
style="@style/dialogFormInput"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/name">
|
||||||
|
|
||||||
|
<requestFocus/>
|
||||||
|
</EditText>
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<ImageButton
|
||||||
|
android:id="@+id/buttonPickColor"
|
||||||
|
style="@style/dialogFormInputColor"
|
||||||
|
android:layout_weight="1"
|
||||||
|
android:contentDescription="@string/color_picker_default_title"
|
||||||
|
android:src="?dialogIconChangeColor"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.edit.views.ExampleEditText
|
||||||
|
android:id="@+id/tvDescription"
|
||||||
|
style="@style/dialogFormInputMultiline"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/question"
|
||||||
|
app:example="@string/example_question_numerical"/>
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
style="@style/dialogFormRow"
|
||||||
|
android:layout_marginTop="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/dialogFormLabel"
|
||||||
|
android:text="@string/reminder"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvReminderTime"
|
||||||
|
style="@style/dialogFormSpinner"/>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/llReminderDays"
|
||||||
|
style="@style/dialogFormRow"
|
||||||
|
android:layout_marginTop="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/dialogFormLabel"
|
||||||
|
android:text=""/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvReminderDays"
|
||||||
|
style="@style/dialogFormSpinner"/>
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,62 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout style="@style/dialogFormRow"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://isoron.org/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
style="@style/dialogFormLabel"
|
||||||
|
android:text="@string/target"/>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2">
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/tvTargetCount"
|
||||||
|
style="@style/dialogFormInput"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/count"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:text="@string/default_count"
|
||||||
|
/>
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_weight="2">
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.edit.views.ExampleEditText
|
||||||
|
android:id="@+id/tvUnit"
|
||||||
|
style="@style/dialogFormInput"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/unit"
|
||||||
|
android:inputType="text"
|
||||||
|
app:example="@string/example_units"/>
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,67 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://isoron.org/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?windowBackgroundColor">
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkButtonView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:color="0"
|
||||||
|
app:value="2"/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkButtonView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:color="0"
|
||||||
|
app:value="0"/>
|
||||||
|
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkButtonView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:color="0"
|
||||||
|
app:value="1"/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberButtonView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:threshold="10"
|
||||||
|
app:color="1"
|
||||||
|
app:value="5"/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberButtonView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:threshold="10"
|
||||||
|
app:color="1"
|
||||||
|
app:value="50"/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberButtonView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:threshold="10"
|
||||||
|
app:color="1"
|
||||||
|
app:value="25304"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,107 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://isoron.org/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?windowBackgroundColor"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="1"
|
||||||
|
app:threshold="10000"
|
||||||
|
app:unit="steps"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="2"
|
||||||
|
app:threshold="2000"
|
||||||
|
app:unit="cals"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="2"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="6"
|
||||||
|
app:threshold="2"
|
||||||
|
app:unit="min"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="6"
|
||||||
|
/>
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="4"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="10"
|
||||||
|
app:threshold="750"
|
||||||
|
app:unit="words"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.CheckmarkPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="10"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.list.views.NumberPanelView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:button_count="8"
|
||||||
|
app:color="8"
|
||||||
|
app:threshold="75"
|
||||||
|
app:unit="pages"
|
||||||
|
/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,52 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:gravity="center"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content">
|
||||||
|
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/picker"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvSeparator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="."/>
|
||||||
|
|
||||||
|
<NumberPicker
|
||||||
|
android:id="@+id/picker2"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:descendantFocusability="blocksDescendants"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/tvUnit"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"/>
|
||||||
|
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,37 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<merge
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:paddingBottom="0dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/title"
|
||||||
|
style="@style/CardHeader"
|
||||||
|
android:text="@string/history"/>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.common.views.BarChart
|
||||||
|
android:id="@+id/barChart"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="220dp"/>
|
||||||
|
|
||||||
|
</merge>
|
@ -0,0 +1,31 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
~
|
||||||
|
~ This file is part of Loop Habit Tracker.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
~ it under the terms of the GNU General Public License as published by the
|
||||||
|
~ Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
~ option) any later version.
|
||||||
|
~
|
||||||
|
~ Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
~ WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
~ or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
~ more details.
|
||||||
|
~
|
||||||
|
~ You should have received a copy of the GNU General Public License along
|
||||||
|
~ with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
>
|
||||||
|
|
||||||
|
<org.isoron.uhabits.activities.habits.show.views.BarCard
|
||||||
|
android:id="@+id/barCard"
|
||||||
|
style="@style/Card"
|
||||||
|
android:gravity="center"/>
|
||||||
|
</LinearLayout>
|
@ -0,0 +1,70 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* This file is part of Loop Habit Tracker.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by the
|
||||||
|
* Free Software Foundation, either version 3 of the License, or (at your
|
||||||
|
* option) any later version.
|
||||||
|
*
|
||||||
|
* Loop Habit Tracker is distributed in the hope that it will be useful, but
|
||||||
|
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||||
|
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||||
|
* more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License along
|
||||||
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.commands;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
import org.junit.*;
|
||||||
|
|
||||||
|
import static junit.framework.Assert.*;
|
||||||
|
import static org.isoron.uhabits.models.Checkmark.CHECKED_EXPLICITLY;
|
||||||
|
|
||||||
|
public class CreateRepetitionCommandTest extends BaseUnitTest
|
||||||
|
{
|
||||||
|
|
||||||
|
private CreateRepetitionCommand command;
|
||||||
|
|
||||||
|
private Habit habit;
|
||||||
|
|
||||||
|
private long today;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Before
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
super.setUp();
|
||||||
|
|
||||||
|
habit = fixtures.createShortHabit();
|
||||||
|
|
||||||
|
today = DateUtils.getStartOfToday();
|
||||||
|
command = new CreateRepetitionCommand(habit, today, 100);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testExecuteUndoRedo()
|
||||||
|
{
|
||||||
|
RepetitionList reps = habit.getRepetitions();
|
||||||
|
|
||||||
|
Repetition rep = reps.getByTimestamp(today);
|
||||||
|
assertNotNull(rep);
|
||||||
|
assertEquals(CHECKED_EXPLICITLY, rep.getValue());
|
||||||
|
|
||||||
|
command.execute();
|
||||||
|
rep = reps.getByTimestamp(today);
|
||||||
|
assertNotNull(rep);
|
||||||
|
assertEquals(100, rep.getValue());
|
||||||
|
|
||||||
|
command.undo();
|
||||||
|
rep = reps.getByTimestamp(today);
|
||||||
|
assertNotNull(rep);
|
||||||
|
assertEquals(CHECKED_EXPLICITLY, rep.getValue());
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in new issue