mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Compatibility with older devices; more statistics
This commit is contained in:
@@ -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,9 +57,327 @@ public class Habit extends Model
|
||||
@Column(name = "highlight")
|
||||
public Integer highlight;
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Commands *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
public Habit(Habit model)
|
||||
{
|
||||
copyAttributes(model);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@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();
|
||||
}
|
||||
|
||||
public static void reorder(int from, int to)
|
||||
{
|
||||
if (from == to)
|
||||
return;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static void rebuildOrder()
|
||||
{
|
||||
List<Habit> habits = select().execute();
|
||||
int i = 0;
|
||||
for (Habit h : habits)
|
||||
{
|
||||
h.position = i++;
|
||||
h.save();
|
||||
}
|
||||
}
|
||||
|
||||
public static void roundTimestamps()
|
||||
{
|
||||
List<Repetition> reps = new Select().from(Repetition.class).execute();
|
||||
for (Repetition r : reps)
|
||||
{
|
||||
r.timestamp = DateHelper.getStartOfDay(r.timestamp);
|
||||
r.save();
|
||||
}
|
||||
}
|
||||
|
||||
public static int getStarCount()
|
||||
{
|
||||
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)
|
||||
{
|
||||
this.name = model.name;
|
||||
this.description = model.description;
|
||||
this.freq_num = model.freq_num;
|
||||
this.freq_den = model.freq_den;
|
||||
this.color = model.color;
|
||||
this.position = model.position;
|
||||
this.reminder_hour = model.reminder_hour;
|
||||
this.reminder_min = model.reminder_min;
|
||||
this.highlight = model.highlight;
|
||||
}
|
||||
|
||||
|
||||
public void save(Long id)
|
||||
{
|
||||
save();
|
||||
Habit.updateId(getId(), id);
|
||||
}
|
||||
|
||||
protected From selectReps()
|
||||
{
|
||||
return new Select().from(Repetition.class).where("habit = ?", getId())
|
||||
.orderBy("timestamp");
|
||||
}
|
||||
|
||||
protected From selectRepsFromTo(long timeFrom, long timeTo)
|
||||
{
|
||||
return selectReps().and("timestamp >= ?", timeFrom).and(
|
||||
"timestamp <= ?", timeTo);
|
||||
}
|
||||
|
||||
public boolean hasRep(long timestamp)
|
||||
{
|
||||
int count = selectReps().where("timestamp = ?", timestamp).count();
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
public boolean hasRepToday()
|
||||
{
|
||||
return hasRep(DateHelper.getStartOfToday());
|
||||
}
|
||||
|
||||
public void deleteReps(long timestamp)
|
||||
{
|
||||
new Delete().from(Repetition.class).where("habit = ?", getId())
|
||||
.and("timestamp = ?", timestamp).execute();
|
||||
}
|
||||
|
||||
public int[] getReps(long timeFrom, long timeTo)
|
||||
{
|
||||
long timeFromExtended = timeFrom - (long) (freq_den) * DateHelper.millisecondsInOneDay;
|
||||
List<Repetition> reps = selectRepsFromTo(timeFromExtended, timeTo).execute();
|
||||
|
||||
int nDaysExtended = (int) ((timeTo - timeFromExtended) / DateHelper.millisecondsInOneDay);
|
||||
int checkExtended[] = new int[nDaysExtended + 1];
|
||||
|
||||
int nDays = (int) ((timeTo - timeFrom) / DateHelper.millisecondsInOneDay);
|
||||
|
||||
// mark explicit checks
|
||||
for (Repetition rep : reps)
|
||||
{
|
||||
int offset = (int) ((rep.timestamp - timeFrom) / DateHelper.millisecondsInOneDay);
|
||||
checkExtended[nDays - offset] = 2;
|
||||
}
|
||||
|
||||
// marks implicit checks
|
||||
for (int i = 0; i < nDays; i++)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
for (int j = 0; j < freq_den; j++)
|
||||
if (checkExtended[i + j] == 2) counter++;
|
||||
|
||||
if (counter >= freq_num)
|
||||
checkExtended[i] = Math.max(checkExtended[i], 1);
|
||||
}
|
||||
|
||||
int check[] = new int[nDays + 1];
|
||||
for (int i = 0; i < nDays + 1; i++)
|
||||
check[i] = checkExtended[i];
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
public boolean hasImplicitRepToday()
|
||||
{
|
||||
long today = DateHelper.getStartOfToday();
|
||||
int reps[] = getReps(today - DateHelper.millisecondsInOneDay, today);
|
||||
return (reps[0] > 0);
|
||||
}
|
||||
|
||||
public Repetition getOldestRep()
|
||||
{
|
||||
return (Repetition) selectReps().limit(1).executeSingle();
|
||||
}
|
||||
|
||||
public void toggleRepetition(long timestamp)
|
||||
{
|
||||
if (hasRep(timestamp))
|
||||
{
|
||||
deleteReps(timestamp);
|
||||
} else
|
||||
{
|
||||
Repetition rep = new Repetition();
|
||||
rep.habit = this;
|
||||
rep.timestamp = timestamp;
|
||||
rep.save();
|
||||
}
|
||||
|
||||
deleteScoresNewerThan(timestamp);
|
||||
}
|
||||
|
||||
public void toggleRepetitionToday()
|
||||
{
|
||||
toggleRepetition(DateHelper.getStartOfToday());
|
||||
}
|
||||
|
||||
public Score getNewestScore()
|
||||
{
|
||||
return new Select().from(Score.class).where("habit = ?", getId())
|
||||
.orderBy("timestamp desc").limit(1).executeSingle();
|
||||
}
|
||||
|
||||
public void deleteScoresNewerThan(long timestamp)
|
||||
{
|
||||
new Delete().from(Score.class).where("habit = ?", getId())
|
||||
.and("timestamp >= ?", timestamp).execute();
|
||||
}
|
||||
|
||||
public Integer getScore()
|
||||
{
|
||||
int beginningScore;
|
||||
long beginningTime;
|
||||
|
||||
long today = DateHelper.getStartOfDay(DateHelper.getLocalTime());
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
|
||||
double freq = ((double) freq_num) / freq_den;
|
||||
double multiplier = Math.pow(0.5, 1.0 / (14.0 / freq - 1));
|
||||
|
||||
Score newestScore = getNewestScore();
|
||||
if (newestScore == null)
|
||||
{
|
||||
Repetition oldestRep = getOldestRep();
|
||||
if (oldestRep == null)
|
||||
return 0;
|
||||
beginningTime = oldestRep.timestamp;
|
||||
beginningScore = 0;
|
||||
} else
|
||||
{
|
||||
beginningTime = newestScore.timestamp + day;
|
||||
beginningScore = newestScore.score;
|
||||
}
|
||||
|
||||
long nDays = (today - beginningTime) / day;
|
||||
if (nDays < 0)
|
||||
return newestScore.score;
|
||||
|
||||
int reps[] = getReps(beginningTime, today);
|
||||
|
||||
int lastScore = beginningScore;
|
||||
for (int i = 0; i < reps.length; i++)
|
||||
{
|
||||
Score s = new Score();
|
||||
s.habit = this;
|
||||
s.timestamp = beginningTime + day * i;
|
||||
s.score = (int) (lastScore * multiplier);
|
||||
if (reps[reps.length - i - 1] == 2)
|
||||
{
|
||||
s.score += 1000000;
|
||||
s.score = Math.min(s.score, 19259500);
|
||||
}
|
||||
s.save();
|
||||
|
||||
lastScore = s.score;
|
||||
}
|
||||
|
||||
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 = ?";
|
||||
|
||||
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;";
|
||||
|
||||
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())
|
||||
{
|
||||
do
|
||||
{
|
||||
streaks[current++] = cursor.getLong(0);
|
||||
} while (cursor.moveToNext());
|
||||
}
|
||||
|
||||
db.endTransaction();
|
||||
return streaks;
|
||||
}
|
||||
|
||||
public static class CreateCommand extends Command
|
||||
{
|
||||
@@ -79,8 +397,7 @@ public class Habit extends Model
|
||||
{
|
||||
savedHabit.save();
|
||||
savedId = savedHabit.getId();
|
||||
}
|
||||
else
|
||||
} else
|
||||
{
|
||||
savedHabit.save(savedId);
|
||||
}
|
||||
@@ -173,312 +490,4 @@ public class Habit extends Model
|
||||
execute();
|
||||
}
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Accessors *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
public Habit(Habit model)
|
||||
{
|
||||
copyAttributes(model);
|
||||
}
|
||||
|
||||
public void copyAttributes(Habit model)
|
||||
{
|
||||
this.name = model.name;
|
||||
this.description = model.description;
|
||||
this.freq_num = model.freq_num;
|
||||
this.freq_den = model.freq_den;
|
||||
this.color = model.color;
|
||||
this.position = model.position;
|
||||
this.reminder_hour = model.reminder_hour;
|
||||
this.reminder_min = model.reminder_min;
|
||||
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)
|
||||
{
|
||||
save();
|
||||
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())
|
||||
.orderBy("timestamp");
|
||||
}
|
||||
|
||||
protected From selectRepsFromTo(long timeFrom, long timeTo)
|
||||
{
|
||||
return selectReps().and("timestamp >= ?", timeFrom).and(
|
||||
"timestamp <= ?", timeTo);
|
||||
}
|
||||
|
||||
public boolean hasRep(long timestamp)
|
||||
{
|
||||
int count = selectReps().where("timestamp = ?", timestamp).count();
|
||||
return (count > 0);
|
||||
}
|
||||
|
||||
public boolean hasRepToday()
|
||||
{
|
||||
return hasRep(DateHelper.getStartOfToday());
|
||||
}
|
||||
|
||||
public void deleteReps(long timestamp)
|
||||
{
|
||||
new Delete().from(Repetition.class).where("habit = ?", getId())
|
||||
.and("timestamp = ?", timestamp).execute();
|
||||
}
|
||||
|
||||
public int[] getReps(long timeFrom, long timeTo)
|
||||
{
|
||||
long timeFromExtended = timeFrom - (long)(freq_den) * DateHelper.millisecondsInOneDay;
|
||||
List<Repetition> reps = selectRepsFromTo(timeFromExtended, timeTo).execute();
|
||||
|
||||
int nDaysExtended = (int) ((timeTo - timeFromExtended) / DateHelper.millisecondsInOneDay);
|
||||
int checkExtended[] = new int[nDaysExtended + 1];
|
||||
|
||||
int nDays = (int) ((timeTo - timeFrom) / DateHelper.millisecondsInOneDay);
|
||||
|
||||
// mark explicit checks
|
||||
for (Repetition rep : reps)
|
||||
{
|
||||
int offset = (int) ((rep.timestamp - timeFrom) / DateHelper.millisecondsInOneDay);
|
||||
checkExtended[nDays - offset] = 2;
|
||||
}
|
||||
|
||||
// marks implicit checks
|
||||
for(int i=0; i<nDays; i++)
|
||||
{
|
||||
int counter = 0;
|
||||
|
||||
for(int j=0; j<freq_den; j++)
|
||||
if(checkExtended[i+j] == 2) counter++;
|
||||
|
||||
if(counter >= freq_num)
|
||||
checkExtended[i] = Math.max(checkExtended[i], 1);
|
||||
}
|
||||
|
||||
int check[] = new int[nDays + 1];
|
||||
for(int i=0; i<nDays+1; i++)
|
||||
check[i] = checkExtended[i];
|
||||
|
||||
return check;
|
||||
}
|
||||
|
||||
public boolean hasImplicitRepToday()
|
||||
{
|
||||
long today = DateHelper.getStartOfToday();
|
||||
int reps[] = getReps(today - DateHelper.millisecondsInOneDay, today);
|
||||
return (reps[0] > 0);
|
||||
}
|
||||
|
||||
public Repetition getOldestRep()
|
||||
{
|
||||
return (Repetition) selectReps().limit(1).executeSingle();
|
||||
}
|
||||
|
||||
public void toggleRepetition(long timestamp)
|
||||
{
|
||||
if(hasRep(timestamp))
|
||||
{
|
||||
deleteReps(timestamp);
|
||||
}
|
||||
else
|
||||
{
|
||||
Repetition rep = new Repetition();
|
||||
rep.habit = this;
|
||||
rep.timestamp = timestamp;
|
||||
rep.save();
|
||||
}
|
||||
|
||||
deleteScoresNewerThan(timestamp);
|
||||
}
|
||||
|
||||
public void toggleRepetitionToday()
|
||||
{
|
||||
toggleRepetition(DateHelper.getStartOfToday());
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Scoring *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
public Score getNewestScore()
|
||||
{
|
||||
return new Select().from(Score.class).where("habit = ?", getId())
|
||||
.orderBy("timestamp desc").limit(1).executeSingle();
|
||||
}
|
||||
|
||||
public void deleteScoresNewerThan(long timestamp)
|
||||
{
|
||||
new Delete().from(Score.class).where("habit = ?", getId())
|
||||
.and("timestamp >= ?", timestamp).execute();
|
||||
}
|
||||
|
||||
public Integer getScore()
|
||||
{
|
||||
int beginningScore;
|
||||
long beginningTime;
|
||||
|
||||
long today = DateHelper.getStartOfDay(DateHelper.getLocalTime());
|
||||
long day = DateHelper.millisecondsInOneDay;
|
||||
|
||||
double freq = ((double) freq_num) / freq_den;
|
||||
double multiplier = Math.pow(0.5, 1.0 / (14.0 / freq - 1));
|
||||
|
||||
Score newestScore = getNewestScore();
|
||||
if(newestScore == null)
|
||||
{
|
||||
Repetition oldestRep = getOldestRep();
|
||||
if(oldestRep == null)
|
||||
return 0;
|
||||
beginningTime = oldestRep.timestamp;
|
||||
beginningScore = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
beginningTime = newestScore.timestamp + day;
|
||||
beginningScore = newestScore.score;
|
||||
}
|
||||
|
||||
long nDays = (today - beginningTime) / day;
|
||||
if(nDays < 0)
|
||||
return newestScore.score;
|
||||
|
||||
int reps[] = getReps(beginningTime, today);
|
||||
|
||||
int lastScore = beginningScore;
|
||||
for (int i = 0; i < reps.length; i++)
|
||||
{
|
||||
Score s = new Score();
|
||||
s.habit = this;
|
||||
s.timestamp = beginningTime + day * i;
|
||||
s.score = (int) (lastScore * multiplier);
|
||||
if(reps[reps.length-i-1] == 2) {
|
||||
s.score += 1000000;
|
||||
s.score = Math.min(s.score, 19259500);
|
||||
}
|
||||
s.save();
|
||||
|
||||
lastScore = s.score;
|
||||
}
|
||||
|
||||
return lastScore;
|
||||
}
|
||||
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Ordering *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
public static void reorder(int from, int to)
|
||||
{
|
||||
if(from == to)
|
||||
return;
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public static void rebuildOrder()
|
||||
{
|
||||
List<Habit> habits = select().execute();
|
||||
int i = 0;
|
||||
for (Habit h : habits)
|
||||
{
|
||||
h.position = i++;
|
||||
h.save();
|
||||
}
|
||||
}
|
||||
|
||||
public static void roundTimestamps()
|
||||
{
|
||||
List<Repetition> reps = new Select().from(Repetition.class).execute();
|
||||
for (Repetition r : reps)
|
||||
{
|
||||
r.timestamp = DateHelper.getStartOfDay(r.timestamp);
|
||||
r.save();
|
||||
}
|
||||
}
|
||||
|
||||
/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
|
||||
* Statistics *
|
||||
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
|
||||
|
||||
public static int getStarCount()
|
||||
{
|
||||
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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
128
app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
Normal file
128
app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
Normal file
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
67
app/src/main/java/org/isoron/uhabits/views/RingView.java
Normal file
67
app/src/main/java/org/isoron/uhabits/views/RingView.java
Normal file
@@ -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);
|
||||
}
|
||||
}
|
||||
39
app/src/main/res/drawable/card_background.xml
Normal file
39
app/src/main/res/drawable/card_background.xml
Normal file
@@ -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>
|
||||
26
app/src/main/res/drawable/habits_list_header_background.xml
Normal file
26
app/src/main/res/drawable/habits_list_header_background.xml
Normal file
@@ -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,31 +2,20 @@
|
||||
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" />
|
||||
|
||||
<LinearLayout style="@style/cardRowStyle">
|
||||
|
||||
<TextView
|
||||
style="@style/cardLabelStyle"
|
||||
android:text="Habit strength" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStrength"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="" />
|
||||
</LinearLayout>
|
||||
android:text="@string/overview" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -35,7 +24,7 @@
|
||||
<TextView
|
||||
android:id="@+id/tvHistory"
|
||||
style="@style/cardHeaderStyle"
|
||||
android:text="History" />
|
||||
android:text="@string/history" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llHistory"
|
||||
@@ -43,5 +32,19 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout style="@style/cardStyle">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tvStreaks"
|
||||
style="@style/cardHeaderStyle"
|
||||
android:text="@string/streaks" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/llStreaks"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
@@ -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>
|
||||
20
app/src/main/res/values-v21/styles_list_habits.xml
Normal file
20
app/src/main/res/values-v21/styles_list_habits.xml
Normal file
@@ -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>
|
||||
|
||||
Reference in New Issue
Block a user