Compatibility with older devices; more statistics

pull/30/head
Alinson S. Xavier 11 years ago
parent 1ff3c1c857
commit a1f05714ba

@ -6,7 +6,7 @@ android {
defaultConfig {
applicationId "org.isoron.uhabits"
minSdkVersion 21
minSdkVersion 15
targetSdkVersion 22
}

@ -1,12 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
<manifest
package="org.isoron.uhabits"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1"
android:versionName="1.0">
<uses-sdk
android:minSdkVersion="21"
android:targetSdkVersion="22" />
android:minSdkVersion="14"
android:targetSdkVersion="21"/>
<uses-permission android:name="android.permission.VIBRATE"/>
@ -16,9 +17,11 @@
android:icon="@drawable/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme">
<meta-data
android:name="AA_DB_NAME"
android:value="uhabits.db"/>
<meta-data
android:name="AA_DB_VERSION"
android:value="6"/>
@ -29,7 +32,6 @@
android:launchMode="singleInstance">
<intent-filter>
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>

@ -91,8 +91,6 @@ public class DatePickerDialog extends DialogFragment implements
private TextView mYearView;
private DayPickerView mDayPickerView;
private YearPickerView mYearPickerView;
private Button mDoneButton;
private Button mClearButton;
private int mCurrentView = UNINITIALIZED;
@ -245,12 +243,15 @@ public class DatePickerDialog extends DialogFragment implements
animation2.setDuration(ANIMATION_DURATION);
mAnimator.setOutAnimation(animation2);
mDoneButton = (Button) view.findViewById(R.id.done);
mDoneButton.setOnClickListener(new OnClickListener() {
Button mDoneButton = (Button) view.findViewById(R.id.done);
mDoneButton.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v) {
public void onClick(View v)
{
tryVibrate();
if (mCallBack != null) {
if (mCallBack != null)
{
mCallBack.onDateSet(DatePickerDialog.this, mCalendar.get(Calendar.YEAR),
mCalendar.get(Calendar.MONTH), mCalendar.get(Calendar.DAY_OF_MONTH));
}
@ -258,12 +259,15 @@ public class DatePickerDialog extends DialogFragment implements
}
});
mClearButton = (Button) view.findViewById(R.id.clear);
mClearButton.setOnClickListener(new OnClickListener() {
Button mClearButton = (Button) view.findViewById(R.id.clear);
mClearButton.setOnClickListener(new OnClickListener()
{
@Override
public void onClick(View v) {
public void onClick(View v)
{
tryVibrate();
if (mCallBack != null) {
if (mCallBack != null)
{
mCallBack.onDateCleared(DatePickerDialog.this);
}
dismiss();

@ -40,8 +40,7 @@ public class DateHelper
public static int differenceInDays(Date from, Date to)
{
long milliseconds = getStartOfDay(to.getTime()) - getStartOfDay(from.getTime());
int days = (int) (milliseconds / millisecondsInOneDay);
return days;
return (int) (milliseconds / millisecondsInOneDay);
}
public static String differenceInWords(Date from, Date to)

@ -83,6 +83,7 @@ public class MainActivity extends Activity
{
super.onCreate(savedInstanceState);
if (android.os.Build.VERSION.SDK_INT >= 21)
getActionBar().setElevation(5);
setContentView(R.layout.list_habits_activity);

@ -16,22 +16,24 @@ public class ShowHabitActivity extends Activity
{
public Habit habit;
private ShowHabitFragment showHabitFragment;
@Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
getActionBar().setElevation(5);
Uri data = getIntent().getData();
habit = Habit.get(ContentUris.parseId(data));
getActionBar().setTitle(habit.name);
if (android.os.Build.VERSION.SDK_INT >= 21)
{
getActionBar().setElevation(5);
getActionBar().setBackgroundDrawable(new ColorDrawable(habit.color));
}
setContentView(R.layout.show_habit_activity);
showHabitFragment = (ShowHabitFragment) getFragmentManager().findFragmentById(
R.id.fragment2);
}
@Override

@ -338,7 +338,6 @@ public class ListHabitsFragment extends Fragment implements OnSavedListener, OnI
Intent intent = new Intent(getActivity(), ShowHabitActivity.class);
intent.setData(Uri.parse("content://org.isoron.uhabits/habit/"
+ habit.getId()));
getActivity().getWindow().setExitTransition(new Explode());
startActivity(intent);
}

@ -10,6 +10,8 @@ import org.isoron.uhabits.R;
import org.isoron.uhabits.ShowHabitActivity;
import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.HabitHistoryView;
import org.isoron.uhabits.views.HabitStreakView;
import org.isoron.uhabits.views.RingView;
import android.app.Fragment;
import android.graphics.Color;
@ -27,7 +29,6 @@ import android.widget.TextView;
public class ShowHabitFragment extends Fragment
{
protected ShowHabitActivity activity;
private Habit habit;
@Override
public void onStart()
@ -43,24 +44,34 @@ public class ShowHabitFragment extends Fragment
View view = inflater.inflate(R.layout.show_habit, container, false);
activity = (ShowHabitActivity) getActivity();
habit = activity.habit;
Habit habit = activity.habit;
if (android.os.Build.VERSION.SDK_INT >= 21)
{
int darkerHabitColor = ColorHelper.mixColors(habit.color, Color.BLACK, 0.75f);
activity.getWindow().setStatusBarColor(darkerHabitColor);
}
TextView tvHistory = (TextView) view.findViewById(R.id.tvHistory);
TextView tvOverview = (TextView) view.findViewById(R.id.tvOverview);
TextView tvStreaks= (TextView) view.findViewById(R.id.tvStreaks);
tvHistory.setTextColor(habit.color);
tvOverview.setTextColor(habit.color);
tvStreaks.setTextColor(habit.color);
TextView tvStrength = (TextView) view.findViewById(R.id.tvStrength);
tvStrength.setText(String.format("%.2f%%", ((float) habit.getScore() / Habit.MAX_SCORE) * 100));
LinearLayout llOverview = (LinearLayout) view.findViewById(R.id.llOverview);
llOverview.addView(new RingView(activity, 200, habit.color, ((float) habit.getScore() / Habit.MAX_SCORE), "Habit strength"));
LinearLayout llHistory = (LinearLayout) view.findViewById(R.id.llHistory);
HabitHistoryView hhv = new HabitHistoryView(activity, habit, 40);
HabitHistoryView hhv = new HabitHistoryView(activity, habit,
(int) activity.getResources().getDimension(R.dimen.square_size));
llHistory.addView(hhv);
LinearLayout llStreaks = (LinearLayout) view.findViewById(R.id.llStreaks);
HabitStreakView hsv = new HabitStreakView(activity, habit,
(int) activity.getResources().getDimension(R.dimen.square_size));
llStreaks.addView(hsv);
return view;
}
}

@ -1,14 +1,11 @@
package org.isoron.uhabits.models;
import java.util.List;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.Command;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.util.Log;
import com.activeandroid.Cache;
import com.activeandroid.Model;
import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table;
@ -18,14 +15,17 @@ import com.activeandroid.query.Select;
import com.activeandroid.query.Update;
import com.activeandroid.util.SQLiteUtils;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.Command;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
import java.util.List;
@Table(name = "Habits")
public class Habit extends Model
{
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Fields *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public static final int HALF_STAR_CUTOFF = 5999000;
public static final int FULL_STAR_CUTOFF = 12973000;
public static final int MAX_SCORE = 19259500;
@ -57,130 +57,109 @@ public class Habit extends Model
@Column(name = "highlight")
public Integer highlight;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Commands *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public static class CreateCommand extends Command
{
private Habit model;
private Long savedId;
public CreateCommand(Habit model)
public Habit(Habit model)
{
this.model = model;
copyAttributes(model);
}
@Override
public void execute()
{
Habit savedHabit = new Habit(model);
if(savedId == null)
{
savedHabit.save();
savedId = savedHabit.getId();
}
else
public Habit()
{
savedHabit.save(savedId);
}
this.color = ColorHelper.palette[11];
this.position = Habit.getCount();
this.highlight = 0;
}
@Override
public void undo()
public static Habit get(Long id)
{
Habit.get(savedId).delete();
return Habit.load(Habit.class, id);
}
@Override
public Integer getExecuteStringId()
@SuppressLint("DefaultLocale")
public static void updateId(long oldId, long newId)
{
return R.string.toast_habit_created;
SQLiteUtils.execSql(String.format(
"update Habits set Id = %d where Id = %d", newId, oldId));
}
@Override
public Integer getUndoStringId()
protected static From select()
{
return R.string.toast_habit_deleted;
}
return new Select().from(Habit.class).orderBy("position");
}
public class EditCommand extends Command
{
private Habit original;
private Habit modified;
private long savedId;
private boolean hasIntervalChanged;
public EditCommand(Habit modified)
public static int getCount()
{
this.savedId = getId();
this.modified = new Habit(modified);
this.original = new Habit(Habit.this);
hasIntervalChanged = (this.original.freq_den != this.modified.freq_den
|| this.original.freq_num != this.modified.freq_num);
return select().count();
}
public void execute()
public static Habit getByPosition(int position)
{
Habit habit = Habit.get(savedId);
habit.copyAttributes(modified);
habit.save();
if(hasIntervalChanged)
habit.deleteScoresNewerThan(0);
return select().offset(position).executeSingle();
}
public void undo()
public static java.util.List<Habit> getHabits()
{
Habit habit = Habit.get(savedId);
habit.copyAttributes(original);
habit.save();
if(hasIntervalChanged)
habit.deleteScoresNewerThan(0);
return select().execute();
}
public Integer getExecuteStringId()
public static java.util.List<Habit> getHighlightedHabits()
{
return R.string.toast_habit_changed;
return select().where("highlight = 1").orderBy("reminder_hour desc, reminder_min desc")
.execute();
}
public Integer getUndoStringId()
public static java.util.List<Habit> getHabitsWithReminder()
{
return R.string.toast_habit_changed_back;
}
return select().where("reminder_hour is not null").execute();
}
public class ToggleRepetitionCommand extends Command
public static void reorder(int from, int to)
{
private Long offset;
if (from == to)
return;
public ToggleRepetitionCommand(long offset)
{
this.offset = offset;
Habit h = Habit.getByPosition(from);
if (to < from)
new Update(Habit.class).set("position = position + 1")
.where("position >= ? and position < ?", to, from)
.execute();
else
new Update(Habit.class).set("position = position - 1")
.where("position > ? and position <= ?", from, to)
.execute();
h.position = to;
h.save();
}
@Override
public void execute()
public static void rebuildOrder()
{
toggleRepetition(offset);
List<Habit> habits = select().execute();
int i = 0;
for (Habit h : habits)
{
h.position = i++;
h.save();
}
}
@Override
public void undo()
public static void roundTimestamps()
{
execute();
List<Repetition> reps = new Select().from(Repetition.class).execute();
for (Repetition r : reps)
{
r.timestamp = DateHelper.getStartOfDay(r.timestamp);
r.save();
}
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Accessors *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public Habit(Habit model)
public static int getStarCount()
{
copyAttributes(model);
String args[] = {};
return SQLiteUtils.intQuery(
"select count(*) from (select score, max(timestamp) from " +
"score group by habit) as scores where scores.score >= "
+ Integer.toString(12973000), args);
}
public void copyAttributes(Habit model)
@ -196,17 +175,6 @@ public class Habit extends Model
this.highlight = model.highlight;
}
public Habit()
{
this.color = ColorHelper.palette[11];
this.position = Habit.getCount();
this.highlight = 0;
}
public static Habit get(Long id)
{
return Habit.load(Habit.class, id);
}
public void save(Long id)
{
@ -214,49 +182,6 @@ public class Habit extends Model
Habit.updateId(getId(), id);
}
@SuppressLint("DefaultLocale")
public static void updateId(long oldId, long newId)
{
SQLiteUtils.execSql(String.format(
"update Habits set Id = %d where Id = %d", newId, oldId));
}
protected static From select()
{
return new Select().from(Habit.class).orderBy("position");
}
public static int getCount()
{
return select().count();
}
public static Habit getByPosition(int position)
{
return select().offset(position).executeSingle();
}
public static java.util.List<Habit> getHabits()
{
return select().execute();
}
public static java.util.List<Habit> getHighlightedHabits()
{
return select().where("highlight = 1").orderBy("reminder_hour desc, reminder_min desc")
.execute();
}
public static java.util.List<Habit> getHabitsWithReminder()
{
return select().where("reminder_hour is not null").execute();
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Repetitions *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
protected From selectReps()
{
return new Select().from(Repetition.class).where("habit = ?", getId())
@ -339,8 +264,7 @@ public class Habit extends Model
if (hasRep(timestamp))
{
deleteReps(timestamp);
}
else
} else
{
Repetition rep = new Repetition();
rep.habit = this;
@ -356,10 +280,6 @@ public class Habit extends Model
toggleRepetition(DateHelper.getStartOfToday());
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Scoring *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public Score getNewestScore()
{
return new Select().from(Score.class).where("habit = ?", getId())
@ -391,8 +311,7 @@ public class Habit extends Model
return 0;
beginningTime = oldestRep.timestamp;
beginningScore = 0;
}
else
} else
{
beginningTime = newestScore.timestamp + day;
beginningScore = newestScore.score;
@ -411,7 +330,8 @@ public class Habit extends Model
s.habit = this;
s.timestamp = beginningTime + day * i;
s.score = (int) (lastScore * multiplier);
if(reps[reps.length-i-1] == 2) {
if (reps[reps.length - i - 1] == 2)
{
s.score += 1000000;
s.score = Math.min(s.score, 19259500);
}
@ -423,62 +343,151 @@ public class Habit extends Model
return lastScore;
}
public long[] getStreaks()
{
String query = "create temporary table T as select distinct r1.habit as habit, r1.timestamp as time,\n" +
" (select count(*) from repetitions r2 where r1.habit = r2.habit and\n" +
" (r1.timestamp = r2.timestamp - 24*60*60*1000 or\n" +
" r1.timestamp = r2.timestamp + 24*60*60*1000)) as neighbors\n" +
"from repetitions r1 where r1.habit = ?";
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Ordering *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
String query2 =
"select time from T, (select 0 union select 1) where neighbors = 0 union all\n" +
"select time from T where neighbors = 1 order by time;";
public static void reorder(int from, int to)
String args[] = {getId().toString()};
SQLiteDatabase db = Cache.openDatabase();
db.beginTransaction();
db.execSQL(query, args);
Cursor cursor = db.rawQuery(query2, null);
long streaks[] = new long[cursor.getCount()];
int current = 0;
Log.d("Streaks", String.format("%d rows", cursor.getCount()));
if (cursor.moveToFirst())
{
if(from == to)
return;
do
{
streaks[current++] = cursor.getLong(0);
} while (cursor.moveToNext());
}
Habit h = Habit.getByPosition(from);
if(to < from)
new Update(Habit.class).set("position = position + 1")
.where("position >= ? and position < ?", to, from)
.execute();
else
new Update(Habit.class).set("position = position - 1")
.where("position > ? and position <= ?", from, to)
.execute();
db.endTransaction();
return streaks;
}
h.position = to;
h.save();
public static class CreateCommand extends Command
{
private Habit model;
private Long savedId;
public CreateCommand(Habit model)
{
this.model = model;
}
public static void rebuildOrder()
@Override
public void execute()
{
List<Habit> habits = select().execute();
int i = 0;
for (Habit h : habits)
Habit savedHabit = new Habit(model);
if (savedId == null)
{
h.position = i++;
h.save();
savedHabit.save();
savedId = savedHabit.getId();
} else
{
savedHabit.save(savedId);
}
}
public static void roundTimestamps()
@Override
public void undo()
{
List<Repetition> reps = new Select().from(Repetition.class).execute();
for (Repetition r : reps)
Habit.get(savedId).delete();
}
@Override
public Integer getExecuteStringId()
{
r.timestamp = DateHelper.getStartOfDay(r.timestamp);
r.save();
return R.string.toast_habit_created;
}
@Override
public Integer getUndoStringId()
{
return R.string.toast_habit_deleted;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Statistics *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
}
public static int getStarCount()
public class EditCommand extends Command
{
String args[] = {};
return SQLiteUtils.intQuery(
"select count(*) from (select score, max(timestamp) from " +
"score group by habit) as scores where scores.score >= "
+ Integer.toString(12973000), args);
private Habit original;
private Habit modified;
private long savedId;
private boolean hasIntervalChanged;
public EditCommand(Habit modified)
{
this.savedId = getId();
this.modified = new Habit(modified);
this.original = new Habit(Habit.this);
hasIntervalChanged = (this.original.freq_den != this.modified.freq_den
|| this.original.freq_num != this.modified.freq_num);
}
public void execute()
{
Habit habit = Habit.get(savedId);
habit.copyAttributes(modified);
habit.save();
if (hasIntervalChanged)
habit.deleteScoresNewerThan(0);
}
public void undo()
{
Habit habit = Habit.get(savedId);
habit.copyAttributes(original);
habit.save();
if (hasIntervalChanged)
habit.deleteScoresNewerThan(0);
}
public Integer getExecuteStringId()
{
return R.string.toast_habit_changed;
}
public Integer getUndoStringId()
{
return R.string.toast_habit_changed_back;
}
}
public class ToggleRepetitionCommand extends Command
{
private Long offset;
public ToggleRepetitionCommand(long offset)
{
this.offset = offset;
}
@Override
public void execute()
{
toggleRepetition(offset);
}
@Override
public void undo()
{
execute();
}
}
}

@ -30,7 +30,6 @@ public class HabitHistoryView extends View
private Context context;
private Paint pSquareBg, pSquareFg, pTextHeader;
private int width, height;
private int squareSize, squareSpacing;
private int nColumns, offsetWeeks;
@ -78,8 +77,6 @@ public class HabitHistoryView extends View
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
width = w;
height = h;
nColumns = (w / squareSize) - 1;
fetchReps();
}

@ -0,0 +1,128 @@
package org.isoron.uhabits.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
import android.view.View;
import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.models.Habit;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.GregorianCalendar;
public class HabitStreakView extends View
{
private Habit habit;
private int columnWidth, columnHeight, nColumns;
private Paint pText, pBar;
private long streaks[];
private long streakStart[], streakEnd[], streakLength[];
private long maxStreakLength;
private int barHeaderHeight;
private int[] colors;
public HabitStreakView(Context context, Habit habit, int columnWidth)
{
super(context);
this.habit = habit;
this.columnWidth = columnWidth;
pText = new Paint();
pText.setColor(Color.LTGRAY);
pText.setTextAlign(Paint.Align.CENTER);
pText.setTextSize(columnWidth * 0.5f);
pText.setAntiAlias(true);
pBar = new Paint();
pBar.setTextAlign(Paint.Align.CENTER);
pBar.setTextSize(columnWidth * 0.5f);
pBar.setAntiAlias(true);
columnHeight = 8 * columnWidth;
barHeaderHeight = columnWidth;
colors = new int[4];
colors[0] = Color.rgb(230, 230, 230);
colors[3] = habit.color;
colors[1] = ColorHelper.mixColors(colors[0], colors[3], 0.66f);
colors[2] = ColorHelper.mixColors(colors[0], colors[3], 0.33f);
fetchStreaks();
}
private void fetchStreaks()
{
streaks = habit.getStreaks();
streakStart = new long[streaks.length / 2];
streakEnd = new long[streaks.length / 2];
streakLength = new long[streaks.length / 2];
for(int i=0; i<streaks.length / 2; i++)
{
streakStart[i] = streaks[i * 2];
streakEnd[i] = streaks[i * 2 + 1];
streakLength[i] = (streakEnd[i] - streakStart[i]) / DateHelper.millisecondsInOneDay + 1;
maxStreakLength = Math.max(maxStreakLength, streakLength[i]);
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(), columnHeight + 2*barHeaderHeight);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh)
{
super.onSizeChanged(w, h, oldw, oldh);
nColumns = w / columnWidth;
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
float lineHeight = pText.getFontSpacing();
float barHeaderOffset = lineHeight * 0.4f;
int start = Math.max(0, streakStart.length - nColumns);
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
String previousMonth = "";
for (int offset = 0; offset < nColumns && start+offset < streakStart.length; offset++)
{
String month = dfMonth.format(streakStart[start+offset]);
long l = streakLength[offset+start];
double lRelative = ((double) l) / maxStreakLength;
pBar.setColor(colors[(int) Math.floor(lRelative*3)]);
int height = (int) (columnHeight * lRelative);
Rect r = new Rect(0,0,columnWidth-2, height);
r.offset(offset * columnWidth, barHeaderHeight + columnHeight - height);
canvas.drawRect(r, pBar);
canvas.drawText(Long.toString(streakLength[offset+start]), r.centerX(), r.top - barHeaderOffset, pBar);
if(!month.equals(previousMonth))
canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
previousMonth = month;
}
}
}

@ -0,0 +1,67 @@
package org.isoron.uhabits.views;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.RectF;
import android.view.View;
public class RingView extends View
{
private int size;
private int color;
private float perc;
private Paint pRing;
private float lineHeight;
private String label;
public RingView(Context context, int size, int color, float perc, String label)
{
super(context);
this.size = size;
this.color = color;
this.perc = perc;
pRing = new Paint();
pRing.setColor(color);
pRing.setAntiAlias(true);
pRing.setTextAlign(Paint.Align.CENTER);
pRing.setTextSize(size * 0.15f);
this.label = label;
lineHeight = pRing.getFontSpacing();
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
{
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(size, size + (int) (2*lineHeight));
}
@Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
float thickness = size * 0.15f;
pRing.setColor(color);
RectF r = new RectF(0, 0, size, size);
canvas.drawArc(r, -90, 360 * perc, true, pRing);
pRing.setColor(Color.rgb(230, 230, 230));
canvas.drawArc(r, 360 * perc - 90 + 2, 360 * (1-perc) - 4, true, pRing);
pRing.setColor(Color.WHITE);
r.inset(thickness, thickness);
canvas.drawArc(r, -90, 360, true, pRing);
pRing.setColor(Color.GRAY);
canvas.drawText(String.format("%.2f%%", perc*100), r.centerX(), r.centerY()+lineHeight/3, pRing);
canvas.drawText(label, size/2, size + lineHeight * 1.2f, pRing);
}
}

@ -0,0 +1,39 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:top="0dp"
android:bottom="0dp"
android:left="0dp"
android:right="0dp">
<shape>
<solid android:color="#d6d6d6" />
</shape>
</item>
<item
android:top="0dp"
android:bottom="1dp"
android:left="0dp"
android:right="0dp">
<shape>
<solid android:color="#c0c0c0" />
</shape>
</item>
<item
android:top="0dp"
android:bottom="1.5dp"
android:left="0dp"
android:right="0dp">
<shape>
<solid android:color="#d6d6d6"/>
</shape>
</item>
<item
android:top="0.5dp"
android:bottom="1.5dp"
android:left="0.5dp"
android:right="0.5dp">
<shape>
<solid android:color="@color/white"/>
</shape>
</item>
</layer-list>

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:top="40dp">
<shape android:shape="rectangle" >
<gradient
android:startColor="#30000000"
android:endColor="#00000000"
android:angle="270"/>
</shape>
</item>
<item android:top="21dp" android:bottom="2dp">
<shape android:shape="rectangle" >
<gradient
android:angle="270"
android:endColor="#ccffffff"
android:startColor="#ffffff" />
</shape>
</item>
<item android:bottom="21dp">
<shape android:shape="rectangle" >
<solid android:color="#ffffff" />
</shape>
</item>
</layer-list>

@ -26,7 +26,7 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:minHeight="48dp"
android:text="Clear"
android:text="@string/clear"
android:textSize="@dimen/done_label_size"
android:textColor="@color/done_text_color" />

@ -29,7 +29,7 @@
<EditText
android:id="@+id/input_description"
android:hint="Description"
android:hint="@string/description"
style="@style/dialogFormInputMultiline" />
<LinearLayout
@ -39,29 +39,29 @@
<TextView
android:id="@+id/textView1"
style="@style/dialogFormLabel"
android:text="Repeat " />
android:text="@string/repeat" />
<EditText
android:id="@+id/input_freq_num"
android:text="3"
android:text="@string/default_freq_num"
style="@style/dialogFormInputSmallNumber" />
<TextView
android:id="@+id/textView3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" times every " />
android:text="@string/times_every" />
<EditText
android:id="@+id/input_freq_den"
android:text="7"
android:text="@string/default_freq_den"
style="@style/dialogFormInputSmallNumber" />
<TextView
android:id="@+id/textView5"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=" days" />
android:text="@string/days" />
</LinearLayout>
<LinearLayout
@ -71,7 +71,7 @@
<TextView
android:id="@+id/TextView2"
style="@style/dialogFormLabel"
android:text="Reminder" />
android:text="@string/reminder" />
<TextView
android:id="@+id/input_reminder_time"
@ -85,7 +85,6 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="end"
android:onClick="onClick"
android:paddingEnd="16dp">
<Button
@ -93,13 +92,13 @@
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Discard" />
android:text="@string/discard" />
<Button
android:id="@+id/buttonSave"
style="?android:attr/buttonBarButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Save" />
android:text="@string/save" />
</LinearLayout>
</LinearLayout>

@ -14,7 +14,8 @@
dslv:float_alpha="0.5"
dslv:sort_enabled="true"
dslv:track_drag_sort="false"
dslv:use_default_controller="true" />
dslv:use_default_controller="true"
/>
<LinearLayout style="@style/habitsListHeaderStyle">

@ -2,43 +2,46 @@
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:fillViewport="true">
android:fillViewport="true"
android:background="@color/windowBackground">
<LinearLayout
style="@style/cardsListStyle"
tools:context="org.isoron.uhabits.ShowHabitActivity">
<LinearLayout style="@style/cardStyle">
<LinearLayout style="@style/cardStyle"
android:id="@+id/llOverview">
<TextView
android:id="@+id/tvOverview"
style="@style/cardHeaderStyle"
android:text="Overview" />
android:text="@string/overview" />
<LinearLayout style="@style/cardRowStyle">
</LinearLayout>
<TextView
style="@style/cardLabelStyle"
android:text="Habit strength" />
<LinearLayout style="@style/cardStyle">
<TextView
android:id="@+id/tvStrength"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="" />
</LinearLayout>
android:id="@+id/tvHistory"
style="@style/cardHeaderStyle"
android:text="@string/history" />
<LinearLayout
android:id="@+id/llHistory"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" />
</LinearLayout>
<LinearLayout style="@style/cardStyle">
<TextView
android:id="@+id/tvHistory"
android:id="@+id/tvStreaks"
style="@style/cardHeaderStyle"
android:text="History" />
android:text="@string/streaks" />
<LinearLayout
android:id="@+id/llHistory"
android:id="@+id/llStreaks"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" />

@ -14,4 +14,19 @@
<style name="MyDialogStyle" parent="android:Theme.Material.Light.Dialog">
</style>
<style name="cardStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">@color/white</item>
<item name="android:elevation">1dp</item>
<item name="android:orientation">vertical</item>
<item name="android:paddingTop">16dp</item>
<item name="android:paddingBottom">16dp</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">4dp</item>
<item name="android:layout_marginBottom">3dp</item>
<item name="android:layout_marginLeft">3dp</item>
<item name="android:layout_marginRight">3dp</item>
</style>
</resources>

@ -0,0 +1,20 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style name="habitsListHeaderStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_alignParentTop">true</item>
<item name="android:background">#f0f0f0</item>
<item name="android:elevation">2dp</item>
<item name="android:paddingRight">4dp</item>
</style>
<style name="habitsListCheckStyle">
<item name="android:focusable">false</item>
<item name="android:minHeight">42dp</item>
<item name="android:minWidth">42dp</item>
<item name="android:gravity">center</item>
<item name="android:background">@drawable/ripple_background</item>
</style>
</resources>

@ -4,6 +4,8 @@
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="square_size">20dp</dimen>
<!-- Color picker -->
<dimen name="color_swatch_large">64dip</dimen>
<dimen name="color_swatch_small">48dip</dimen>

@ -42,5 +42,19 @@
<string name="title_activity_show_habit">ShowHabitActivity</string>
<string name="hello_world">Hello world!</string>
<string name="overview">Overview</string>
<string name="habit_strength">Habit strength</string>
<string name="history">History</string>
<string name="clear">Clear</string>
<string name="description">Description</string>
<string name="repeat">Repeat</string>
<string name="default_freq_num">3</string>
<string name="times_every">times every</string>
<string name="default_freq_den">7</string>
<string name="days">days</string>
<string name="reminder">Reminder</string>
<string name="discard">Discard</string>
<string name="save">Save</string>
<string name="streaks">Streaks</string>
</resources>

@ -28,14 +28,13 @@
<style name="cardStyle">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:background">@color/white</item>
<item name="android:elevation">1dp</item>
<item name="android:background">@drawable/card_background</item>
<item name="android:orientation">vertical</item>
<item name="android:paddingTop">16dp</item>
<item name="android:paddingBottom">16dp</item>
<item name="android:paddingLeft">16dp</item>
<item name="android:paddingRight">4dp</item>
<item name="android:layout_marginBottom">3dp</item>
<item name="android:layout_marginBottom">1dp</item>
<item name="android:layout_marginLeft">3dp</item>
<item name="android:layout_marginRight">3dp</item>
</style>

@ -14,9 +14,8 @@
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_alignParentTop">true</item>
<item name="android:background">#f0f0f0</item>
<item name="android:elevation">2dp</item>
<item name="android:paddingRight">4dp</item>
<item name="android:background">@drawable/habits_list_header_background</item>
</style>
<style name="habitsListStarStyle">
@ -64,13 +63,12 @@
<item name="android:minHeight">42dp</item>
<item name="android:minWidth">42dp</item>
<item name="android:gravity">center</item>
<item name="android:background">@drawable/ripple_background</item>
</style>
<style name="habitsListHeaderCheckStyle" parent="habitsListCheckStyle">
<item name="android:layout_width">42dp</item>
<item name="android:layout_height">match_parent</item>
<item name="android:background">#f0f0f0</item>
<item name="android:background">@color/transparent</item>
<item name="android:focusable">false</item>
<item name="android:textSize">10sp</item>
<item name="android:textColor">#606060</item>

Loading…
Cancel
Save