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 { defaultConfig {
applicationId "org.isoron.uhabits" applicationId "org.isoron.uhabits"
minSdkVersion 21 minSdkVersion 15
targetSdkVersion 22 targetSdkVersion 22
} }

@ -1,49 +1,51 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest
package="org.isoron.uhabits" package="org.isoron.uhabits"
xmlns:android="http://schemas.android.com/apk/res/android"
android:versionCode="1" android:versionCode="1"
android:versionName="1.0" > android:versionName="1.0">
<uses-sdk <uses-sdk
android:minSdkVersion="21" android:minSdkVersion="14"
android:targetSdkVersion="22" /> android:targetSdkVersion="21"/>
<uses-permission android:name="android.permission.VIBRATE" /> <uses-permission android:name="android.permission.VIBRATE"/>
<application <application
android:name="com.activeandroid.app.Application" android:name="com.activeandroid.app.Application"
android:allowBackup="true" android:allowBackup="true"
android:icon="@drawable/ic_launcher" android:icon="@drawable/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/AppTheme" > android:theme="@style/AppTheme">
<meta-data <meta-data
android:name="AA_DB_NAME" android:name="AA_DB_NAME"
android:value="uhabits.db" /> android:value="uhabits.db"/>
<meta-data <meta-data
android:name="AA_DB_VERSION" android:name="AA_DB_VERSION"
android:value="6" /> android:value="6"/>
<activity <activity
android:name=".MainActivity" android:name=".MainActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:launchMode="singleInstance" > android:launchMode="singleInstance">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </activity>
<receiver android:name=".ReminderAlarmReceiver" > <receiver android:name=".ReminderAlarmReceiver">
</receiver> </receiver>
<activity <activity
android:name=".ShowHabitActivity" android:name=".ShowHabitActivity"
android:label="@string/title_activity_show_habit" android:label="@string/title_activity_show_habit"
android:parentActivityName=".MainActivity" > android:parentActivityName=".MainActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value="org.isoron.uhabits.MainActivity" /> android:value="org.isoron.uhabits.MainActivity"/>
</activity> </activity>
</application> </application>

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

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

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

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

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

@ -10,6 +10,8 @@ import org.isoron.uhabits.R;
import org.isoron.uhabits.ShowHabitActivity; import org.isoron.uhabits.ShowHabitActivity;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
import org.isoron.uhabits.views.HabitHistoryView; import org.isoron.uhabits.views.HabitHistoryView;
import org.isoron.uhabits.views.HabitStreakView;
import org.isoron.uhabits.views.RingView;
import android.app.Fragment; import android.app.Fragment;
import android.graphics.Color; import android.graphics.Color;
@ -27,9 +29,8 @@ import android.widget.TextView;
public class ShowHabitFragment extends Fragment public class ShowHabitFragment extends Fragment
{ {
protected ShowHabitActivity activity; protected ShowHabitActivity activity;
private Habit habit;
@Override @Override
public void onStart() public void onStart()
{ {
super.onStart(); super.onStart();
@ -43,24 +44,34 @@ public class ShowHabitFragment extends Fragment
View view = inflater.inflate(R.layout.show_habit, container, false); View view = inflater.inflate(R.layout.show_habit, container, false);
activity = (ShowHabitActivity) getActivity(); activity = (ShowHabitActivity) getActivity();
habit = activity.habit; Habit habit = activity.habit;
int darkerHabitColor = ColorHelper.mixColors(habit.color, Color.BLACK, 0.75f); if (android.os.Build.VERSION.SDK_INT >= 21)
activity.getWindow().setStatusBarColor(darkerHabitColor); {
int darkerHabitColor = ColorHelper.mixColors(habit.color, Color.BLACK, 0.75f);
activity.getWindow().setStatusBarColor(darkerHabitColor);
}
TextView tvHistory = (TextView) view.findViewById(R.id.tvHistory); TextView tvHistory = (TextView) view.findViewById(R.id.tvHistory);
TextView tvOverview = (TextView) view.findViewById(R.id.tvOverview); TextView tvOverview = (TextView) view.findViewById(R.id.tvOverview);
TextView tvStreaks= (TextView) view.findViewById(R.id.tvStreaks);
tvHistory.setTextColor(habit.color); tvHistory.setTextColor(habit.color);
tvOverview.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); LinearLayout llHistory = (LinearLayout) view.findViewById(R.id.llHistory);
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);
HabitHistoryView hhv = new HabitHistoryView(activity, habit, 40);
llHistory.addView(hhv);
return view; return view;
} }
} }

@ -1,14 +1,11 @@
package org.isoron.uhabits.models; 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.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.Model;
import com.activeandroid.annotation.Column; import com.activeandroid.annotation.Column;
import com.activeandroid.annotation.Table; import com.activeandroid.annotation.Table;
@ -18,467 +15,479 @@ import com.activeandroid.query.Select;
import com.activeandroid.query.Update; import com.activeandroid.query.Update;
import com.activeandroid.util.SQLiteUtils; 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") @Table(name = "Habits")
public class Habit extends Model public class Habit extends Model
{ {
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Fields *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
public static final int HALF_STAR_CUTOFF = 5999000; public static final int HALF_STAR_CUTOFF = 5999000;
public static final int FULL_STAR_CUTOFF = 12973000; public static final int FULL_STAR_CUTOFF = 12973000;
public static final int MAX_SCORE = 19259500; public static final int MAX_SCORE = 19259500;
@Column(name = "name") @Column(name = "name")
public String name; public String name;
@Column(name = "description") @Column(name = "description")
public String description; public String description;
@Column(name = "freq_num") @Column(name = "freq_num")
public Integer freq_num; public Integer freq_num;
@Column(name = "freq_den") @Column(name = "freq_den")
public Integer freq_den; public Integer freq_den;
@Column(name = "color") @Column(name = "color")
public Integer color; public Integer color;
@Column(name = "position") @Column(name = "position")
public Integer position; public Integer position;
@Column(name = "reminder_hour") @Column(name = "reminder_hour")
public Integer reminder_hour; public Integer reminder_hour;
@Column(name = "reminder_min") @Column(name = "reminder_min")
public Integer reminder_min; public Integer reminder_min;
@Column(name = "highlight") @Column(name = "highlight")
public Integer highlight; public Integer highlight;
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * public Habit(Habit model)
* Commands * {
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ copyAttributes(model);
}
public static class CreateCommand extends Command
{ public Habit()
private Habit model; {
private Long savedId; this.color = ColorHelper.palette[11];
this.position = Habit.getCount();
public CreateCommand(Habit model) this.highlight = 0;
{ }
this.model = model;
} public static Habit get(Long id)
{
@Override return Habit.load(Habit.class, id);
public void execute() }
{
Habit savedHabit = new Habit(model); @SuppressLint("DefaultLocale")
if(savedId == null) public static void updateId(long oldId, long newId)
{ {
savedHabit.save(); SQLiteUtils.execSql(String.format(
savedId = savedHabit.getId(); "update Habits set Id = %d where Id = %d", newId, oldId));
} }
else
{ protected static From select()
savedHabit.save(savedId); {
} return new Select().from(Habit.class).orderBy("position");
} }
@Override public static int getCount()
public void undo() {
{ return select().count();
Habit.get(savedId).delete(); }
}
public static Habit getByPosition(int position)
@Override {
public Integer getExecuteStringId() return select().offset(position).executeSingle();
{ }
return R.string.toast_habit_created;
} public static java.util.List<Habit> getHabits()
{
@Override return select().execute();
public Integer getUndoStringId() }
{
return R.string.toast_habit_deleted; public static java.util.List<Habit> getHighlightedHabits()
} {
return select().where("highlight = 1").orderBy("reminder_hour desc, reminder_min desc")
} .execute();
}
public class EditCommand extends Command
{ public static java.util.List<Habit> getHabitsWithReminder()
private Habit original; {
private Habit modified; return select().where("reminder_hour is not null").execute();
private long savedId; }
private boolean hasIntervalChanged;
public static void reorder(int from, int to)
public EditCommand(Habit modified) {
{ if (from == to)
this.savedId = getId(); return;
this.modified = new Habit(modified);
this.original = new Habit(Habit.this); Habit h = Habit.getByPosition(from);
if (to < from)
hasIntervalChanged = (this.original.freq_den != this.modified.freq_den new Update(Habit.class).set("position = position + 1")
|| this.original.freq_num != this.modified.freq_num); .where("position >= ? and position < ?", to, from)
} .execute();
else
public void execute() new Update(Habit.class).set("position = position - 1")
{ .where("position > ? and position <= ?", from, to)
Habit habit = Habit.get(savedId); .execute();
habit.copyAttributes(modified);
habit.save(); h.position = to;
if(hasIntervalChanged) h.save();
habit.deleteScoresNewerThan(0); }
}
public static void rebuildOrder()
public void undo() {
{ List<Habit> habits = select().execute();
Habit habit = Habit.get(savedId); int i = 0;
habit.copyAttributes(original); for (Habit h : habits)
habit.save(); {
if(hasIntervalChanged) h.position = i++;
habit.deleteScoresNewerThan(0); h.save();
} }
}
public Integer getExecuteStringId()
{ public static void roundTimestamps()
return R.string.toast_habit_changed; {
} List<Repetition> reps = new Select().from(Repetition.class).execute();
for (Repetition r : reps)
public Integer getUndoStringId() {
{ r.timestamp = DateHelper.getStartOfDay(r.timestamp);
return R.string.toast_habit_changed_back; r.save();
} }
} }
public class ToggleRepetitionCommand extends Command public static int getStarCount()
{ {
private Long offset; String args[] = {};
return SQLiteUtils.intQuery(
public ToggleRepetitionCommand(long offset) "select count(*) from (select score, max(timestamp) from " +
{ "score group by habit) as scores where scores.score >= "
this.offset = offset; + Integer.toString(12973000), args);
}
}
@Override
public void execute() public void copyAttributes(Habit model)
{ {
toggleRepetition(offset); this.name = model.name;
} this.description = model.description;
this.freq_num = model.freq_num;
@Override this.freq_den = model.freq_den;
public void undo() this.color = model.color;
{ this.position = model.position;
execute(); this.reminder_hour = model.reminder_hour;
} this.reminder_min = model.reminder_min;
} this.highlight = model.highlight;
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Accessors *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ public void save(Long id)
{
public Habit(Habit model) save();
{ Habit.updateId(getId(), id);
copyAttributes(model); }
}
protected From selectReps()
public void copyAttributes(Habit model) {
{ return new Select().from(Repetition.class).where("habit = ?", getId())
this.name = model.name; .orderBy("timestamp");
this.description = model.description; }
this.freq_num = model.freq_num;
this.freq_den = model.freq_den; protected From selectRepsFromTo(long timeFrom, long timeTo)
this.color = model.color; {
this.position = model.position; return selectReps().and("timestamp >= ?", timeFrom).and(
this.reminder_hour = model.reminder_hour; "timestamp <= ?", timeTo);
this.reminder_min = model.reminder_min; }
this.highlight = model.highlight;
} public boolean hasRep(long timestamp)
{
public Habit() int count = selectReps().where("timestamp = ?", timestamp).count();
{ return (count > 0);
this.color = ColorHelper.palette[11]; }
this.position = Habit.getCount();
this.highlight = 0; public boolean hasRepToday()
} {
return hasRep(DateHelper.getStartOfToday());
public static Habit get(Long id) }
{
return Habit.load(Habit.class, id); public void deleteReps(long timestamp)
} {
new Delete().from(Repetition.class).where("habit = ?", getId())
public void save(Long id) .and("timestamp = ?", timestamp).execute();
{ }
save();
Habit.updateId(getId(), id); public int[] getReps(long timeFrom, long timeTo)
} {
long timeFromExtended = timeFrom - (long) (freq_den) * DateHelper.millisecondsInOneDay;
@SuppressLint("DefaultLocale") List<Repetition> reps = selectRepsFromTo(timeFromExtended, timeTo).execute();
public static void updateId(long oldId, long newId)
{ int nDaysExtended = (int) ((timeTo - timeFromExtended) / DateHelper.millisecondsInOneDay);
SQLiteUtils.execSql(String.format( int checkExtended[] = new int[nDaysExtended + 1];
"update Habits set Id = %d where Id = %d", newId, oldId));
} int nDays = (int) ((timeTo - timeFrom) / DateHelper.millisecondsInOneDay);
protected static From select() // mark explicit checks
{ for (Repetition rep : reps)
return new Select().from(Habit.class).orderBy("position"); {
} int offset = (int) ((rep.timestamp - timeFrom) / DateHelper.millisecondsInOneDay);
checkExtended[nDays - offset] = 2;
public static int getCount() }
{
return select().count(); // marks implicit checks
} for (int i = 0; i < nDays; i++)
{
public static Habit getByPosition(int position) int counter = 0;
{
return select().offset(position).executeSingle(); for (int j = 0; j < freq_den; j++)
} if (checkExtended[i + j] == 2) counter++;
public static java.util.List<Habit> getHabits() if (counter >= freq_num)
{ checkExtended[i] = Math.max(checkExtended[i], 1);
return select().execute(); }
}
int check[] = new int[nDays + 1];
public static java.util.List<Habit> getHighlightedHabits() for (int i = 0; i < nDays + 1; i++)
{ check[i] = checkExtended[i];
return select().where("highlight = 1").orderBy("reminder_hour desc, reminder_min desc")
.execute(); return check;
} }
public static java.util.List<Habit> getHabitsWithReminder() public boolean hasImplicitRepToday()
{ {
return select().where("reminder_hour is not null").execute(); long today = DateHelper.getStartOfToday();
} int reps[] = getReps(today - DateHelper.millisecondsInOneDay, today);
return (reps[0] > 0);
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Repetitions * public Repetition getOldestRep()
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ {
return (Repetition) selectReps().limit(1).executeSingle();
protected From selectReps() }
{
return new Select().from(Repetition.class).where("habit = ?", getId()) public void toggleRepetition(long timestamp)
.orderBy("timestamp"); {
} if (hasRep(timestamp))
{
protected From selectRepsFromTo(long timeFrom, long timeTo) deleteReps(timestamp);
{ } else
return selectReps().and("timestamp >= ?", timeFrom).and( {
"timestamp <= ?", timeTo); Repetition rep = new Repetition();
} rep.habit = this;
rep.timestamp = timestamp;
public boolean hasRep(long timestamp) rep.save();
{ }
int count = selectReps().where("timestamp = ?", timestamp).count();
return (count > 0); deleteScoresNewerThan(timestamp);
} }
public boolean hasRepToday() public void toggleRepetitionToday()
{ {
return hasRep(DateHelper.getStartOfToday()); toggleRepetition(DateHelper.getStartOfToday());
} }
public void deleteReps(long timestamp) public Score getNewestScore()
{ {
new Delete().from(Repetition.class).where("habit = ?", getId()) return new Select().from(Score.class).where("habit = ?", getId())
.and("timestamp = ?", timestamp).execute(); .orderBy("timestamp desc").limit(1).executeSingle();
} }
public int[] getReps(long timeFrom, long timeTo) public void deleteScoresNewerThan(long timestamp)
{ {
long timeFromExtended = timeFrom - (long)(freq_den) * DateHelper.millisecondsInOneDay; new Delete().from(Score.class).where("habit = ?", getId())
List<Repetition> reps = selectRepsFromTo(timeFromExtended, timeTo).execute(); .and("timestamp >= ?", timestamp).execute();
}
int nDaysExtended = (int) ((timeTo - timeFromExtended) / DateHelper.millisecondsInOneDay);
int checkExtended[] = new int[nDaysExtended + 1]; public Integer getScore()
{
int nDays = (int) ((timeTo - timeFrom) / DateHelper.millisecondsInOneDay); int beginningScore;
long beginningTime;
// mark explicit checks
for (Repetition rep : reps) long today = DateHelper.getStartOfDay(DateHelper.getLocalTime());
{ long day = DateHelper.millisecondsInOneDay;
int offset = (int) ((rep.timestamp - timeFrom) / DateHelper.millisecondsInOneDay);
checkExtended[nDays - offset] = 2; double freq = ((double) freq_num) / freq_den;
} double multiplier = Math.pow(0.5, 1.0 / (14.0 / freq - 1));
// marks implicit checks Score newestScore = getNewestScore();
for(int i=0; i<nDays; i++) if (newestScore == null)
{ {
int counter = 0; Repetition oldestRep = getOldestRep();
if (oldestRep == null)
for(int j=0; j<freq_den; j++) return 0;
if(checkExtended[i+j] == 2) counter++; beginningTime = oldestRep.timestamp;
beginningScore = 0;
if(counter >= freq_num) } else
checkExtended[i] = Math.max(checkExtended[i], 1); {
} beginningTime = newestScore.timestamp + day;
beginningScore = newestScore.score;
int check[] = new int[nDays + 1]; }
for(int i=0; i<nDays+1; i++)
check[i] = checkExtended[i]; long nDays = (today - beginningTime) / day;
if (nDays < 0)
return check; return newestScore.score;
}
int reps[] = getReps(beginningTime, today);
public boolean hasImplicitRepToday()
{ int lastScore = beginningScore;
long today = DateHelper.getStartOfToday(); for (int i = 0; i < reps.length; i++)
int reps[] = getReps(today - DateHelper.millisecondsInOneDay, today); {
return (reps[0] > 0); Score s = new Score();
} s.habit = this;
s.timestamp = beginningTime + day * i;
public Repetition getOldestRep() s.score = (int) (lastScore * multiplier);
{ if (reps[reps.length - i - 1] == 2)
return (Repetition) selectReps().limit(1).executeSingle(); {
} s.score += 1000000;
s.score = Math.min(s.score, 19259500);
public void toggleRepetition(long timestamp) }
{ s.save();
if(hasRep(timestamp))
{ lastScore = s.score;
deleteReps(timestamp); }
}
else return lastScore;
{ }
Repetition rep = new Repetition();
rep.habit = this; public long[] getStreaks()
rep.timestamp = timestamp; {
rep.save(); 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" +
deleteScoresNewerThan(timestamp); " r1.timestamp = r2.timestamp + 24*60*60*1000)) as neighbors\n" +
} "from repetitions r1 where r1.habit = ?";
public void toggleRepetitionToday() String query2 =
{ "select time from T, (select 0 union select 1) where neighbors = 0 union all\n" +
toggleRepetition(DateHelper.getStartOfToday()); "select time from T where neighbors = 1 order by time;";
}
String args[] = {getId().toString()};
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Scoring * SQLiteDatabase db = Cache.openDatabase();
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ db.beginTransaction();
db.execSQL(query, args);
public Score getNewestScore() Cursor cursor = db.rawQuery(query2, null);
{
return new Select().from(Score.class).where("habit = ?", getId()) long streaks[] = new long[cursor.getCount()];
.orderBy("timestamp desc").limit(1).executeSingle(); int current = 0;
}
Log.d("Streaks", String.format("%d rows", cursor.getCount()));
public void deleteScoresNewerThan(long timestamp)
{ if (cursor.moveToFirst())
new Delete().from(Score.class).where("habit = ?", getId()) {
.and("timestamp >= ?", timestamp).execute(); do
} {
streaks[current++] = cursor.getLong(0);
public Integer getScore() } while (cursor.moveToNext());
{ }
int beginningScore;
long beginningTime; db.endTransaction();
return streaks;
long today = DateHelper.getStartOfDay(DateHelper.getLocalTime()); }
long day = DateHelper.millisecondsInOneDay;
public static class CreateCommand extends Command
double freq = ((double) freq_num) / freq_den; {
double multiplier = Math.pow(0.5, 1.0 / (14.0 / freq - 1)); private Habit model;
private Long savedId;
Score newestScore = getNewestScore();
if(newestScore == null) public CreateCommand(Habit model)
{ {
Repetition oldestRep = getOldestRep(); this.model = model;
if(oldestRep == null) }
return 0;
beginningTime = oldestRep.timestamp; @Override
beginningScore = 0; public void execute()
} {
else Habit savedHabit = new Habit(model);
{ if (savedId == null)
beginningTime = newestScore.timestamp + day; {
beginningScore = newestScore.score; savedHabit.save();
} savedId = savedHabit.getId();
} else
long nDays = (today - beginningTime) / day; {
if(nDays < 0) savedHabit.save(savedId);
return newestScore.score; }
}
int reps[] = getReps(beginningTime, today);
@Override
int lastScore = beginningScore; public void undo()
for (int i = 0; i < reps.length; i++) {
{ Habit.get(savedId).delete();
Score s = new Score(); }
s.habit = this;
s.timestamp = beginningTime + day * i; @Override
s.score = (int) (lastScore * multiplier); public Integer getExecuteStringId()
if(reps[reps.length-i-1] == 2) { {
s.score += 1000000; return R.string.toast_habit_created;
s.score = Math.min(s.score, 19259500); }
}
s.save(); @Override
public Integer getUndoStringId()
lastScore = s.score; {
} return R.string.toast_habit_deleted;
}
return lastScore;
} }
public class EditCommand extends Command
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * {
* Ordering * private Habit original;
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ private Habit modified;
private long savedId;
public static void reorder(int from, int to) private boolean hasIntervalChanged;
{
if(from == to) public EditCommand(Habit modified)
return; {
this.savedId = getId();
Habit h = Habit.getByPosition(from); this.modified = new Habit(modified);
if(to < from) this.original = new Habit(Habit.this);
new Update(Habit.class).set("position = position + 1")
.where("position >= ? and position < ?", to, from) hasIntervalChanged = (this.original.freq_den != this.modified.freq_den
.execute(); || this.original.freq_num != this.modified.freq_num);
else }
new Update(Habit.class).set("position = position - 1")
.where("position > ? and position <= ?", from, to) public void execute()
.execute(); {
Habit habit = Habit.get(savedId);
h.position = to; habit.copyAttributes(modified);
h.save(); habit.save();
} if (hasIntervalChanged)
habit.deleteScoresNewerThan(0);
public static void rebuildOrder() }
{
List<Habit> habits = select().execute(); public void undo()
int i = 0; {
for (Habit h : habits) Habit habit = Habit.get(savedId);
{ habit.copyAttributes(original);
h.position = i++; habit.save();
h.save(); if (hasIntervalChanged)
} habit.deleteScoresNewerThan(0);
} }
public static void roundTimestamps() public Integer getExecuteStringId()
{ {
List<Repetition> reps = new Select().from(Repetition.class).execute(); return R.string.toast_habit_changed;
for (Repetition r : reps) }
{
r.timestamp = DateHelper.getStartOfDay(r.timestamp); public Integer getUndoStringId()
r.save(); {
} return R.string.toast_habit_changed_back;
} }
}
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Statistics * public class ToggleRepetitionCommand extends Command
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ {
private Long offset;
public static int getStarCount()
{ public ToggleRepetitionCommand(long offset)
String args[] = {}; {
return SQLiteUtils.intQuery( this.offset = offset;
"select count(*) from (select score, max(timestamp) from " + }
"score group by habit) as scores where scores.score >= "
+ Integer.toString(12973000), args); @Override
public void execute()
} {
toggleRepetition(offset);
}
@Override
public void undo()
{
execute();
}
}
} }

@ -30,8 +30,7 @@ public class HabitHistoryView extends View
private Context context; private Context context;
private Paint pSquareBg, pSquareFg, pTextHeader; private Paint pSquareBg, pSquareFg, pTextHeader;
private int width, height; private int squareSize, squareSpacing;
private int squareSize, squareSpacing;
private int nColumns, offsetWeeks; private int nColumns, offsetWeeks;
private int colorPrimary, colorPrimaryBrighter, grey; private int colorPrimary, colorPrimaryBrighter, grey;
@ -78,9 +77,7 @@ public class HabitHistoryView extends View
@Override @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) protected void onSizeChanged(int w, int h, int oldw, int oldh)
{ {
width = w; nColumns = (w / squareSize) - 1;
height = h;
nColumns = (w / squareSize) - 1;
fetchReps(); 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_height="wrap_content"
android:layout_weight="1" android:layout_weight="1"
android:minHeight="48dp" android:minHeight="48dp"
android:text="Clear" android:text="@string/clear"
android:textSize="@dimen/done_label_size" android:textSize="@dimen/done_label_size"
android:textColor="@color/done_text_color" /> android:textColor="@color/done_text_color" />

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

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

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

@ -14,4 +14,19 @@
<style name="MyDialogStyle" parent="android:Theme.Material.Light.Dialog"> <style name="MyDialogStyle" parent="android:Theme.Material.Light.Dialog">
</style> </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> </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_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen> <dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="square_size">20dp</dimen>
<!-- Color picker --> <!-- Color picker -->
<dimen name="color_swatch_large">64dip</dimen> <dimen name="color_swatch_large">64dip</dimen>
<dimen name="color_swatch_small">48dip</dimen> <dimen name="color_swatch_small">48dip</dimen>

@ -42,5 +42,19 @@
<string name="title_activity_show_habit">ShowHabitActivity</string> <string name="title_activity_show_habit">ShowHabitActivity</string>
<string name="hello_world">Hello world!</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> </resources>

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

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

Loading…
Cancel
Save