diff --git a/app/app.iml b/app/app.iml
index c8d4626b3..b9364a714 100644
--- a/app/app.iml
+++ b/app/app.iml
@@ -1,5 +1,5 @@
-
+
@@ -12,8 +12,9 @@
-
+
+
@@ -89,5 +90,4 @@
-
-
+
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 376c34db9..b34cfa3ef 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -14,7 +14,7 @@
diff --git a/app/src/main/ic_launcher-web.png b/app/src/main/ic_launcher-web.png
new file mode 100644
index 000000000..85ad242d1
Binary files /dev/null and b/app/src/main/ic_launcher-web.png differ
diff --git a/app/src/main/java/org/isoron/uhabits/MainActivity.java b/app/src/main/java/org/isoron/uhabits/MainActivity.java
index 7db75aa54..bbb97c0d8 100644
--- a/app/src/main/java/org/isoron/uhabits/MainActivity.java
+++ b/app/src/main/java/org/isoron/uhabits/MainActivity.java
@@ -6,6 +6,7 @@ import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@@ -125,25 +126,24 @@ public class MainActivity extends Activity
return super.onOptionsItemSelected(item);
}
- public void executeCommand(Command command, boolean datasetChanged)
+ public void executeCommand(Command command)
{
- executeCommand(command, datasetChanged, true);
+ executeCommand(command, false);
}
- public void executeCommand(Command command, boolean datasetChanged, boolean clearRedoStack)
+
+ public void executeCommand(Command command, boolean clearRedoStack)
{
undoList.push(command);
if (undoList.size() > MAX_UNDO_LEVEL)
undoList.removeLast();
if (clearRedoStack)
redoList.clear();
+
command.execute();
+ listHabitsFragment.notifyDataSetChanged();
showToast(command.getExecuteStringId());
- if (datasetChanged)
- {
- listHabitsFragment.notifyDataSetChanged();
- }
}
public void undo()
@@ -170,7 +170,7 @@ public class MainActivity extends Activity
return;
}
Command last = redoList.pop();
- executeCommand(last, true, false);
+ executeCommand(last, false);
}
private void showToast(Integer stringId)
diff --git a/app/src/main/java/org/isoron/uhabits/ReminderAlarmReceiver.java b/app/src/main/java/org/isoron/uhabits/ReminderAlarmReceiver.java
index ab0ee593b..f78135202 100644
--- a/app/src/main/java/org/isoron/uhabits/ReminderAlarmReceiver.java
+++ b/app/src/main/java/org/isoron/uhabits/ReminderAlarmReceiver.java
@@ -1,12 +1,6 @@
package org.isoron.uhabits;
-import java.util.Date;
-import java.util.List;
-
-import org.isoron.uhabits.models.Habit;
-
import android.app.Activity;
-import android.app.AlarmManager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
@@ -14,148 +8,133 @@ import android.content.BroadcastReceiver;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
+import android.graphics.BitmapFactory;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
+import org.isoron.uhabits.models.Habit;
+
+import java.util.Date;
+
public class ReminderAlarmReceiver extends BroadcastReceiver
{
-
- public static String ACTION_CHECK = "org.isoron.uhabits.ACTION_CHECK";
- public static String ACTION_DISMISS = "org.isoron.uhabits.ACTION_DISMISS";
- public static String ACTION_REMIND = "org.isoron.uhabits.ACTION_REMIND";
- public static String ACTION_REMOVE_REMINDER = "org.isoron.uhabits.ACTION_REMOVE_REMINDER";
- public static String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE";
-
- @Override
- public void onReceive(Context context, Intent intent)
- {
- String action = intent.getAction();
-
- if(action.equals(ACTION_REMIND))
- createNotification(context, intent.getData());
-
- else if(action.equals(ACTION_DISMISS))
- dismissAllHabits();
-
- else if(action.equals(ACTION_CHECK))
- checkHabit(context, intent.getData());
-
- else if(action.equals(ACTION_SNOOZE))
- snoozeHabit(context, intent.getData());
- }
-
- private void snoozeHabit(Context context, Uri data)
- {
- int delayMinutes = 15;
- Habit habit = Habit.get(ContentUris.parseId(data));
- MainActivity.createReminderAlarm(context, habit, new Date().getTime() + delayMinutes * 1000);
- dismissNotification(context);
- }
-
- private void checkHabit(Context context, Uri data)
- {
- Habit habit = Habit.get(ContentUris.parseId(data));
- habit.toggleRepetitionToday();
- habit.save();
- dismissNotification(context);
- }
-
- private void dismissAllHabits()
- {
- for(Habit h : Habit.getHighlightedHabits())
- {
- Log.d("Alarm", String.format("Removing highlight from: %s", h.name));
- h.highlight = 0;
- h.save();
- }
- }
-
- private void dismissNotification(Context context)
- {
- NotificationManager notificationManager = (NotificationManager) context
- .getSystemService(Activity.NOTIFICATION_SERVICE);
-
- notificationManager.cancel(1);
- }
-
-
- private void createNotification(Context context, Uri data)
- {
- Log.d("Alarm", "Alarm received!");
-
- Habit habit = Habit.get(ContentUris.parseId(data));
-
- if(habit.hasImplicitRepToday())
- {
- Log.d("Alarm", String.format("(%s) has implicit rep today", habit.name));
- return;
- }
-
- Log.d("Alarm", String.format("Applying highlight: %s", habit.name));
- habit.highlight = 1;
- habit.save();
-
- // Check if reminder has been turned off after alarm was scheduled
- if(habit.reminder_hour == null)
- return;
-
- Intent contentIntent = new Intent(context, MainActivity.class);
- contentIntent.setData(data);
- PendingIntent contentPendingIntent = PendingIntent.getActivity(context, 0, contentIntent, 0);
-
- Intent deleteIntent = new Intent(context, ReminderAlarmReceiver.class);
- deleteIntent.setAction(ACTION_DISMISS);
- PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
-
- Intent checkIntent = new Intent(context, ReminderAlarmReceiver.class);
- checkIntent.setData(data);
- checkIntent.setAction(ACTION_CHECK);
- PendingIntent checkIntentPending = PendingIntent.getBroadcast(context, 0, checkIntent, 0);
-
- Intent snoozeIntent = new Intent(context, ReminderAlarmReceiver.class);
- snoozeIntent.setData(data);
- snoozeIntent.setAction(ACTION_SNOOZE);
- PendingIntent snoozeIntentPending = PendingIntent.getBroadcast(context, 0, snoozeIntent, 0);
-
- Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
-
- NotificationCompat.InboxStyle inboxStyle = new NotificationCompat.InboxStyle();
- inboxStyle.setBigContentTitle("Habit Reminder:");
- List pendingHabits = Habit.getHighlightedHabits();
- StringBuffer contentText = new StringBuffer();
- for(Habit h : pendingHabits)
- {
- if(h.hasImplicitRepToday())
- continue;
-
- inboxStyle.addLine(h.name);
- if(contentText.length() > 0)
- contentText.append(", ");
- contentText.append(h.name);
- Log.d("Alarm", String.format("Found highlighted: %s", h.name));
- }
-
- Notification notification =
- new NotificationCompat.Builder(context)
- .setSmallIcon(R.drawable.ic_notification)
- .setContentTitle("Habit Reminder")
- .setContentText(contentText)
- .setContentIntent(contentPendingIntent)
- .setDeleteIntent(deletePendingIntent)
- .addAction(R.drawable.ic_action_check, "Check", checkIntentPending)
- .addAction(R.drawable.ic_action_snooze, "Later", snoozeIntentPending)
- .setSound(soundUri)
- .setStyle(inboxStyle)
- .build();
-
- notification.flags |= Notification.FLAG_AUTO_CANCEL;
-
- NotificationManager notificationManager = (NotificationManager) context
- .getSystemService(Activity.NOTIFICATION_SERVICE);
-
- notificationManager.notify(1, notification);
- }
+
+ public static String ACTION_CHECK = "org.isoron.uhabits.ACTION_CHECK";
+ public static String ACTION_DISMISS = "org.isoron.uhabits.ACTION_DISMISS";
+ public static String ACTION_REMIND = "org.isoron.uhabits.ACTION_REMIND";
+ public static String ACTION_REMOVE_REMINDER = "org.isoron.uhabits.ACTION_REMOVE_REMINDER";
+ public static String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE";
+
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ String action = intent.getAction();
+
+ if (action.equals(ACTION_REMIND)) createNotification(context, intent.getData());
+
+ else if (action.equals(ACTION_DISMISS)) dismissAllHabits();
+
+ else if (action.equals(ACTION_CHECK)) checkHabit(context, intent.getData());
+
+ else if (action.equals(ACTION_SNOOZE)) snoozeHabit(context, intent.getData());
+ }
+
+ private void snoozeHabit(Context context, Uri data)
+ {
+ int delayMinutes = 60;
+ Habit habit = Habit.get(ContentUris.parseId(data));
+ MainActivity.createReminderAlarm(context, habit,
+ new Date().getTime() + delayMinutes * 60 * 1000);
+ dismissNotification(context, habit);
+ }
+
+ private void checkHabit(Context context, Uri data)
+ {
+ Habit habit = Habit.get(ContentUris.parseId(data));
+ habit.toggleRepetitionToday();
+ habit.save();
+ dismissNotification(context, habit);
+ }
+
+ private void dismissAllHabits()
+ {
+ for (Habit h : Habit.getHighlightedHabits())
+ {
+ h.highlight = 0;
+ h.save();
+ }
+ }
+
+ private void dismissNotification(Context context, Habit habit)
+ {
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
+
+ int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
+ notificationManager.cancel(notificationId);
+ }
+
+
+ private void createNotification(Context context, Uri data)
+ {
+
+ Habit habit = Habit.get(ContentUris.parseId(data));
+
+ if (habit.hasImplicitRepToday()) return;
+
+ Log.d("Alarm", String.format("Applying highlight: %s", habit.name));
+ habit.highlight = 1;
+ habit.save();
+
+ // Check if reminder has been turned off after alarm was scheduled
+ if (habit.reminder_hour == null) return;
+
+ Intent contentIntent = new Intent(context, MainActivity.class);
+ contentIntent.setData(data);
+ PendingIntent contentPendingIntent =
+ PendingIntent.getActivity(context, 0, contentIntent, 0);
+
+ Intent deleteIntent = new Intent(context, ReminderAlarmReceiver.class);
+ deleteIntent.setAction(ACTION_DISMISS);
+ PendingIntent deletePendingIntent = PendingIntent.getBroadcast(context, 0, deleteIntent, 0);
+
+ Intent checkIntent = new Intent(context, ReminderAlarmReceiver.class);
+ checkIntent.setData(data);
+ checkIntent.setAction(ACTION_CHECK);
+ PendingIntent checkIntentPending = PendingIntent.getBroadcast(context, 0, checkIntent, 0);
+
+ Intent snoozeIntent = new Intent(context, ReminderAlarmReceiver.class);
+ snoozeIntent.setData(data);
+ snoozeIntent.setAction(ACTION_SNOOZE);
+ PendingIntent snoozeIntentPending = PendingIntent.getBroadcast(context, 0, snoozeIntent, 0);
+
+ Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
+
+ NotificationCompat.WearableExtender wearableExtender =
+ new NotificationCompat.WearableExtender().setBackground(
+ BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
+
+ Notification notification =
+ new NotificationCompat.Builder(context).setSmallIcon(R.drawable.ic_notification)
+ .setContentTitle(habit.name)
+ .setContentText(habit.description)
+ .setContentIntent(contentPendingIntent)
+ .setDeleteIntent(deletePendingIntent)
+ .addAction(R.drawable.ic_action_check, "Check", checkIntentPending)
+ .addAction(R.drawable.ic_action_snooze, "Later", snoozeIntentPending)
+ .setSound(soundUri)
+ .extend(wearableExtender)
+ .build();
+
+ notification.flags |= Notification.FLAG_AUTO_CANCEL;
+
+ NotificationManager notificationManager =
+ (NotificationManager) context.getSystemService(Activity.NOTIFICATION_SERVICE);
+
+ int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
+ notificationManager.notify(notificationId, notification);
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/dialogs/ListHabitsFragment.java b/app/src/main/java/org/isoron/uhabits/dialogs/ListHabitsFragment.java
index e4e63c126..7b87f3de5 100644
--- a/app/src/main/java/org/isoron/uhabits/dialogs/ListHabitsFragment.java
+++ b/app/src/main/java/org/isoron/uhabits/dialogs/ListHabitsFragment.java
@@ -1,18 +1,5 @@
package org.isoron.uhabits.dialogs;
-import java.util.Date;
-import java.util.GregorianCalendar;
-import java.util.Locale;
-import java.util.TimeZone;
-
-import org.isoron.helpers.Command;
-import org.isoron.helpers.DateHelper;
-import org.isoron.helpers.DialogHelper.OnSavedListener;
-import org.isoron.uhabits.MainActivity;
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.ShowHabitActivity;
-import org.isoron.uhabits.models.Habit;
-
import android.app.Fragment;
import android.content.Context;
import android.content.Intent;
@@ -20,9 +7,9 @@ import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Typeface;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Vibrator;
-import android.transition.Explode;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.ContextMenu;
@@ -50,362 +37,359 @@ import com.mobeta.android.dslv.DragSortController;
import com.mobeta.android.dslv.DragSortListView;
import com.mobeta.android.dslv.DragSortListView.DropListener;
-public class ListHabitsFragment extends Fragment implements OnSavedListener, OnItemClickListener,
- OnLongClickListener, DropListener, OnClickListener
+import org.isoron.helpers.Command;
+import org.isoron.helpers.DateHelper;
+import org.isoron.helpers.DialogHelper.OnSavedListener;
+import org.isoron.uhabits.MainActivity;
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.ShowHabitActivity;
+import org.isoron.uhabits.models.Habit;
+
+import java.util.Date;
+import java.util.GregorianCalendar;
+import java.util.Locale;
+import java.util.TimeZone;
+
+public class ListHabitsFragment extends Fragment
+ implements OnSavedListener, OnItemClickListener, OnLongClickListener, DropListener,
+ OnClickListener
{
-
- private int tvNameWidth;
- private int button_count;
- ListHabitsAdapter adapter;
- DragSortListView listView;
- MainActivity mainActivity;
- TextView tvNameHeader;
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Adapter *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
- class ListHabitsAdapter extends BaseAdapter
- {
-
- private Context context;
- private LayoutInflater inflater;
- private Typeface fontawesome;
-
- String habits[] = { "wake up early", "work out", "meditate", "take vitamins",
- "go to school",
- "cook dinner & lunch" };
-
- public ListHabitsAdapter(Context context)
- {
- this.context = context;
-
- inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
- }
-
- @Override
- public int getCount()
- {
- return Habit.getCount();
- }
-
- @Override
- public Object getItem(int position)
- {
- return Habit.getByPosition(position);
- }
-
- @Override
- public long getItemId(int position)
- {
- return ((Habit) getItem(position)).getId();
- }
-
- @Override
- public View getView(int position, View view, ViewGroup parent)
- {
- final Habit habit = (Habit) getItem(position);
-
- if(view == null || (Long) view.getTag(R.id.KEY_TIMESTAMP) != DateHelper.getStartOfToday())
- {
- view = inflater.inflate(R.layout.list_habits_item, null);
- ((TextView) view.findViewById(R.id.tvStar)).setTypeface(fontawesome);
-
- LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(tvNameWidth,
- LayoutParams.WRAP_CONTENT, 1);
- ((TextView) view.findViewById(R.id.tvName)).setLayoutParams(params);
-
- Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
- .getDefaultDisplay();
- Point size = new Point();
- display.getSize(size);
-
- LinearLayout.LayoutParams llp = new LinearLayout.LayoutParams(
- LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
- llp.setMargins(2, 0, 2, 0);
-
- for (int i = 0; i < button_count; i++)
- {
- View check = inflater.inflate(R.layout.list_habits_item_check, null);
- TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
- btCheck.setTypeface(fontawesome);
- btCheck.setOnLongClickListener(ListHabitsFragment.this);
+
+ ListHabitsAdapter adapter;
+ DragSortListView listView;
+ MainActivity mainActivity;
+ TextView tvNameHeader;
+ long lastLongClick = 0;
+ private int tvNameWidth;
+
+ private int button_count;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState)
+ {
+ View view = inflater.inflate(R.layout.list_habits_fragment, container, false);
+
+ DisplayMetrics dm = getResources().getDisplayMetrics();
+ int width = (int) (dm.widthPixels / dm.density);
+ button_count = (int) ((width - 160) / 42);
+ tvNameWidth = (int) ((width - 30 - button_count * 42) * dm.density);
+
+ tvNameHeader = (TextView) view.findViewById(R.id.tvNameHeader);
+// updateStarCount();
+
+ adapter = new ListHabitsAdapter(getActivity());
+ listView = (DragSortListView) view.findViewById(R.id.listView);
+ listView.setAdapter(adapter);
+ listView.setOnItemClickListener(this);
+ registerForContextMenu(listView);
+ listView.setDropListener(this);
+
+ DragSortController controller = new DragSortController(listView);
+ controller.setDragHandleId(R.id.tvStar);
+ controller.setRemoveEnabled(false);
+ controller.setSortEnabled(true);
+ controller.setDragInitMode(1);
+
+ listView.setFloatViewManager(controller);
+ listView.setOnTouchListener(controller);
+ listView.setDragEnabled(true);
+
+ GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
+ day.setTimeInMillis(DateHelper.getStartOfDay(DateHelper.getLocalTime()));
+
+ for (int i = 0; i < button_count; i++)
+ {
+ View check = inflater.inflate(R.layout.list_habits_header_check, null);
+ Button btCheck = (Button) check.findViewById(R.id.tvCheck);
+ btCheck.setText(
+ day.getDisplayName(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.SHORT,
+ Locale.US) + "\n" +
+ Integer.toString(day.get(GregorianCalendar.DAY_OF_MONTH)));
+ ((LinearLayout) view.findViewById(R.id.llButtonsHeader)).addView(check);
+
+ day.add(GregorianCalendar.DAY_OF_MONTH, -1);
+ }
+
+ mainActivity = (MainActivity) getActivity();
+
+ setHasOptionsMenu(true);
+ return view;
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
+ {
+ inflater.inflate(R.menu.show_habits_options, menu);
+ super.onCreateOptionsMenu(menu, inflater);
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo)
+ {
+ super.onCreateContextMenu(menu, view, menuInfo);
+ getActivity().getMenuInflater().inflate(R.menu.show_habits_context, menu);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ int id = item.getItemId();
+
+ if (id == R.id.action_add)
+ {
+ EditHabitFragment frag = EditHabitFragment.createHabitFragment();
+ frag.setOnSavedListener(this);
+ frag.show(getFragmentManager(), "dialog");
+ return true;
+ }
+
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem menuItem)
+ {
+ AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuItem.getMenuInfo();
+ final int id = menuItem.getItemId();
+ final Habit habit = Habit.get(info.id);
+
+ if (id == R.id.action_edit_habit)
+ {
+ EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(habit.getId());
+ frag.setOnSavedListener(this);
+ frag.show(getFragmentManager(), "dialog");
+ return true;
+ }
+
+ return super.onContextItemSelected(menuItem);
+ }
+
+ @Override
+ public void onItemClick(AdapterView> parent, View view, int position, long id)
+ {
+ if (new Date().getTime() - lastLongClick < 1000) return;
+
+ Habit habit = Habit.getByPosition(position);
+ Log.d("ItemClick", Long.toString(id));
+
+ Intent intent = new Intent(getActivity(), ShowHabitActivity.class);
+ intent.setData(Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId()));
+ startActivity(intent);
+ }
+
+ @Override
+ public void onSaved(Command command)
+ {
+ executeCommand(command);
+ MainActivity.createReminderAlarms(mainActivity);
+ }
+
+ public void notifyDataSetChanged()
+ {
+ adapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public boolean onLongClick(View v)
+ {
+ int id = v.getId();
+
+ if (id == R.id.tvCheck)
+ {
+ lastLongClick = new Date().getTime();
+ Habit habit = Habit.get((Long) v.getTag(R.string.habit_key));
+ int offset = (Integer) v.getTag(R.string.offset_key);
+ long timestamp = DateHelper.getStartOfDay(
+ DateHelper.getLocalTime() - offset * DateHelper.millisecondsInOneDay);
+
+ executeCommand(habit.new ToggleRepetitionCommand(timestamp));
+
+ Vibrator vb = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
+ vb.vibrate(100);
+
+ return true;
+ }
+
+ return false;
+ }
+
+ private void executeCommand(Command c)
+ {
+ mainActivity.executeCommand(c);
+ }
+
+ @Override
+ public void drop(int from, int to)
+ {
+ Habit.reorder(from, to);
+ notifyDataSetChanged();
+ }
+
+ @Override
+ public void onClick(View v)
+ {
+ }
+
+ void updateStarCount()
+ {
+ Log.d("StarCount", "updating star count");
+ String msg = "";
+ int starCount = Habit.getStarCount();
+
+ if (starCount == 1) msg = String.format("%d star", starCount);
+ else if (starCount > 1) msg = String.format("%d stars", starCount);
+
+ tvNameHeader.setText(msg);
+ }
+
+ class ListHabitsAdapter extends BaseAdapter
+ {
+
+ String habits[] = {"wake up early", "work out", "meditate", "take vitamins", "go to school",
+ "cook dinner & lunch"};
+ private Context context;
+ private LayoutInflater inflater;
+ private Typeface fontawesome;
+
+ public ListHabitsAdapter(Context context)
+ {
+ this.context = context;
+
+ inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+ fontawesome = Typeface.createFromAsset(context.getAssets(), "fontawesome-webfont.ttf");
+ }
+
+ @Override
+ public int getCount()
+ {
+ return Habit.getCount();
+ }
+
+ @Override
+ public Object getItem(int position)
+ {
+ return Habit.getByPosition(position);
+ }
+
+ @Override
+ public long getItemId(int position)
+ {
+ return ((Habit) getItem(position)).getId();
+ }
+
+ @Override
+ public View getView(int position, View view, ViewGroup parent)
+ {
+ final Habit habit = (Habit) getItem(position);
+
+ if (view == null ||
+ (Long) view.getTag(R.id.KEY_TIMESTAMP) != DateHelper.getStartOfToday())
+ {
+ view = inflater.inflate(R.layout.list_habits_item, null);
+ ((TextView) view.findViewById(R.id.tvStar)).setTypeface(fontawesome);
+
+ LinearLayout.LayoutParams params =
+ new LinearLayout.LayoutParams(tvNameWidth, LayoutParams.WRAP_CONTENT, 1);
+ ((TextView) view.findViewById(R.id.tvName)).setLayoutParams(params);
+
+ Display display = ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE))
+ .getDefaultDisplay();
+ Point size = new Point();
+ display.getSize(size);
+
+ LinearLayout.LayoutParams llp =
+ new LinearLayout.LayoutParams(LayoutParams.WRAP_CONTENT,
+ LayoutParams.WRAP_CONTENT);
+ llp.setMargins(2, 0, 2, 0);
+
+ for (int i = 0; i < button_count; i++)
+ {
+ View check = inflater.inflate(R.layout.list_habits_item_check, null);
+ TextView btCheck = (TextView) check.findViewById(R.id.tvCheck);
+ btCheck.setTypeface(fontawesome);
+ btCheck.setOnLongClickListener(ListHabitsFragment.this);
// btCheck.setLayoutParams(llp);
- ((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
- }
-
+ ((LinearLayout) view.findViewById(R.id.llButtons)).addView(check);
+ }
+
// LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
// llInner.setOnClickListener(ListHabitsFragment.this);
-
- view.setTag(R.id.KEY_TIMESTAMP, DateHelper.getStartOfToday());
- }
-
- TextView tvStar = (TextView) view.findViewById(R.id.tvStar);
- TextView tvName = (TextView) view.findViewById(R.id.tvName);
-
-
- if(habit == null)
- {
- tvName.setText(null);
- return view;
- }
-
- LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
- llInner.setTag(R.string.habit_key, habit.getId());
-
- int inactiveColor = Color.rgb(230, 230, 230);
- int activeColor = habit.color;
-
- tvName.setText(habit.name);
- tvName.setTextColor(activeColor);
-
- int score = habit.getScore();
-
- if(score < Habit.HALF_STAR_CUTOFF)
- {
- tvStar.setText(context.getString(R.string.fa_star_o));
- tvStar.setTextColor(inactiveColor);
- }
- else if(score < Habit.FULL_STAR_CUTOFF)
- {
- tvStar.setText(context.getString(R.string.fa_star_half_o));
- tvStar.setTextColor(inactiveColor);
- }
- else
- {
- tvStar.setText(context.getString(R.string.fa_star));
- tvStar.setTextColor(activeColor);
- }
-
- LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
- int m = llButtons.getChildCount();
-
- long dateTo = DateHelper.getStartOfDay(DateHelper.getLocalTime());
- long dateFrom = dateTo - m * DateHelper.millisecondsInOneDay;
-
- int isChecked[] = habit.getReps(dateFrom, dateTo);
-
- for (int i = 0; i < m; i++)
- {
-
- TextView tvCheck = (TextView) llButtons.getChildAt(i);
- tvCheck.setTag(R.string.habit_key, habit.getId());
- tvCheck.setTag(R.string.offset_key, i);
-
- switch(isChecked[i])
- {
- case 2:
- tvCheck.setText(R.string.fa_check);
- tvCheck.setTextColor(activeColor);
- break;
-
- case 1:
- tvCheck.setText(R.string.fa_check);
- tvCheck.setTextColor(inactiveColor);
- break;
-
- case 0:
- tvCheck.setText(R.string.fa_times);
- tvCheck.setTextColor(inactiveColor);
- break;
- }
- }
-
- return view;
- }
-
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Creation *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- View view = inflater.inflate(R.layout.list_habits_fragment, container, false);
-
- DisplayMetrics dm = getResources().getDisplayMetrics();
- int width = (int) (dm.widthPixels / dm.density);
- button_count = (int) ((width - 160) / 42);
- tvNameWidth = (int) ((width - 30 - button_count * 42) * dm.density);
-
- tvNameHeader = (TextView) view.findViewById(R.id.tvNameHeader);
-// updateStarCount();
- adapter = new ListHabitsAdapter(getActivity());
- listView = (DragSortListView) view.findViewById(R.id.listView);
- listView.setAdapter(adapter);
- listView.setOnItemClickListener(this);
- registerForContextMenu(listView);
- listView.setDropListener(this);
-
- DragSortController controller = new DragSortController(listView);
- controller.setDragHandleId(R.id.tvStar);
- controller.setRemoveEnabled(false);
- controller.setSortEnabled(true);
- controller.setDragInitMode(1);
-
- listView.setFloatViewManager(controller);
- listView.setOnTouchListener(controller);
- listView.setDragEnabled(true);
-
- GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
- day.setTimeInMillis(DateHelper.getStartOfDay(DateHelper.getLocalTime()));
-
- for (int i = 0; i < button_count; i++)
- {
- View check = inflater.inflate(R.layout.list_habits_header_check, null);
- Button btCheck = (Button) check.findViewById(R.id.tvCheck);
- btCheck.setText(day.getDisplayName(GregorianCalendar.DAY_OF_WEEK,
- GregorianCalendar.SHORT, Locale.US) + "\n"
- + Integer.toString(day.get(GregorianCalendar.DAY_OF_MONTH)));
- ((LinearLayout) view.findViewById(R.id.llButtonsHeader)).addView(check);
-
- day.add(GregorianCalendar.DAY_OF_MONTH, -1);
- }
-
- mainActivity = (MainActivity) getActivity();
-
- setHasOptionsMenu(true);
- return view;
- }
-
- @Override
- public void onCreateOptionsMenu(Menu menu, MenuInflater inflater)
- {
- inflater.inflate(R.menu.show_habits_options, menu);
- super.onCreateOptionsMenu(menu, inflater);
- }
-
- @Override
- public void onCreateContextMenu(ContextMenu menu, View view, ContextMenuInfo menuInfo)
- {
- super.onCreateContextMenu(menu, view, menuInfo);
- getActivity().getMenuInflater().inflate(R.menu.show_habits_context, menu);
- }
-
- /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
- * Callback *
- * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
-
- @Override
- public boolean onOptionsItemSelected(MenuItem item)
- {
- int id = item.getItemId();
-
- if(id == R.id.action_add)
- {
- EditHabitFragment frag = EditHabitFragment.createHabitFragment();
- frag.setOnSavedListener(this);
- frag.show(getFragmentManager(), "dialog");
- return true;
- }
-
- return super.onOptionsItemSelected(item);
- }
-
- @Override
- public boolean onContextItemSelected(MenuItem menuItem)
- {
- AdapterContextMenuInfo info = (AdapterContextMenuInfo) menuItem.getMenuInfo();
- final int id = menuItem.getItemId();
- final Habit habit = Habit.get(info.id);
-
- if(id == R.id.action_edit_habit)
- {
- EditHabitFragment frag = EditHabitFragment.editSingleHabitFragment(habit.getId());
- frag.setOnSavedListener(this);
- frag.show(getFragmentManager(), "dialog");
- return true;
- }
-
- return super.onContextItemSelected(menuItem);
- }
-
- long lastLongClick = 0;
-
- @Override
- public void onItemClick(AdapterView> parent, View view, int position, long id)
- {
- if(new Date().getTime() - lastLongClick < 1000) return;
-
- Habit habit = Habit.getByPosition(position);
- Log.d("ItemClick", Long.toString(id));
-
- Intent intent = new Intent(getActivity(), ShowHabitActivity.class);
- intent.setData(Uri.parse("content://org.isoron.uhabits/habit/"
- + habit.getId()));
- startActivity(intent);
- }
-
- @Override
- public void onSaved(Command command)
- {
- executeCommand(command);
- MainActivity.createReminderAlarms(mainActivity);
- }
-
- public void notifyDataSetChanged()
- {
- adapter.notifyDataSetChanged();
- }
-
- @Override
- public boolean onLongClick(View v)
- {
- int id = v.getId();
-
- if(id == R.id.tvCheck)
- {
- lastLongClick = new Date().getTime();
- Habit habit = Habit.get((Long) v.getTag(R.string.habit_key));
- int offset = (Integer) v.getTag(R.string.offset_key);
- long timestamp = DateHelper.getStartOfDay(DateHelper.getLocalTime() - offset
- * DateHelper.millisecondsInOneDay);
-
- executeCommand(habit.new ToggleRepetitionCommand(timestamp));
-
- Vibrator vb = (Vibrator) getActivity().getSystemService(Context.VIBRATOR_SERVICE);
- vb.vibrate(100);
-
- return true;
- }
-
- return false;
- }
-
- private void executeCommand(Command c)
- {
- mainActivity.executeCommand(c, false);
- notifyDataSetChanged();
- }
-
- @Override
- public void drop(int from, int to)
- {
- Habit.reorder(from, to);
- notifyDataSetChanged();
- }
-
- @Override
- public void onClick(View v)
- {
- }
-
- void updateStarCount()
- {
- Log.d("StarCount", "updating star count");
- String msg = "";
- int starCount = Habit.getStarCount();
-
- if(starCount == 1)
- msg = String.format("%d star", starCount);
- else if(starCount > 1)
- msg = String.format("%d stars", starCount);
-
- tvNameHeader.setText(msg);
- }
+ view.setTag(R.id.KEY_TIMESTAMP, DateHelper.getStartOfToday());
+ }
+
+ TextView tvStar = (TextView) view.findViewById(R.id.tvStar);
+ TextView tvName = (TextView) view.findViewById(R.id.tvName);
+
+
+ if (habit == null)
+ {
+ tvName.setText(null);
+ return view;
+ }
+
+ LinearLayout llInner = (LinearLayout) view.findViewById(R.id.llInner);
+ llInner.setTag(R.string.habit_key, habit.getId());
+
+ int inactiveColor = Color.rgb(230, 230, 230);
+ int activeColor = habit.color;
+
+ tvName.setText(habit.name);
+ tvName.setTextColor(activeColor);
+
+ int score = habit.getScore();
+
+ if (score < Habit.HALF_STAR_CUTOFF)
+ {
+ tvStar.setText(context.getString(R.string.fa_star_o));
+ tvStar.setTextColor(inactiveColor);
+ } else if (score < Habit.FULL_STAR_CUTOFF)
+ {
+ tvStar.setText(context.getString(R.string.fa_star_half_o));
+ tvStar.setTextColor(inactiveColor);
+ } else
+ {
+ tvStar.setText(context.getString(R.string.fa_star));
+ tvStar.setTextColor(activeColor);
+ }
+
+ LinearLayout llButtons = (LinearLayout) view.findViewById(R.id.llButtons);
+ int m = llButtons.getChildCount();
+
+ long dateTo = DateHelper.getStartOfDay(DateHelper.getLocalTime());
+ long dateFrom = dateTo - m * DateHelper.millisecondsInOneDay;
+
+ int isChecked[] = habit.getReps(dateFrom, dateTo);
+
+ for (int i = 0; i < m; i++)
+ {
+
+ TextView tvCheck = (TextView) llButtons.getChildAt(i);
+ tvCheck.setTag(R.string.habit_key, habit.getId());
+ tvCheck.setTag(R.string.offset_key, i);
+
+ switch (isChecked[i])
+ {
+ case 2:
+ tvCheck.setText(R.string.fa_check);
+ tvCheck.setTextColor(activeColor);
+ break;
+
+ case 1:
+ tvCheck.setText(R.string.fa_check);
+ tvCheck.setTextColor(inactiveColor);
+ break;
+
+ case 0:
+ tvCheck.setText(R.string.fa_times);
+ tvCheck.setTextColor(inactiveColor);
+ break;
+ }
+ }
+
+ return view;
+ }
+
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/dialogs/ShowHabitFragment.java b/app/src/main/java/org/isoron/uhabits/dialogs/ShowHabitFragment.java
index 8997d9ed4..782515e1b 100644
--- a/app/src/main/java/org/isoron/uhabits/dialogs/ShowHabitFragment.java
+++ b/app/src/main/java/org/isoron/uhabits/dialogs/ShowHabitFragment.java
@@ -1,49 +1,42 @@
package org.isoron.uhabits.dialogs;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-
-import org.isoron.helpers.ColorHelper;
-import org.isoron.helpers.DateHelper;
-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;
-import android.graphics.Typeface;
import android.os.Bundle;
-import android.util.DisplayMetrics;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
-import android.widget.LinearLayout.LayoutParams;
import android.widget.TextView;
+import org.isoron.helpers.ColorHelper;
+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.HabitScoreView;
+import org.isoron.uhabits.views.HabitStreakView;
+import org.isoron.uhabits.views.RingView;
+
public class ShowHabitFragment extends Fragment
{
- protected ShowHabitActivity activity;
+ protected ShowHabitActivity activity;
@Override
- public void onStart()
- {
- super.onStart();
- }
+ public void onStart()
+ {
+ super.onStart();
+ }
- @Override
- public View onCreateView(LayoutInflater inflater, ViewGroup container,
- Bundle savedInstanceState)
- {
- Log.d("ShowHabitActivity", "Creating view...");
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState)
+ {
+ Log.d("ShowHabitActivity", "Creating view...");
- View view = inflater.inflate(R.layout.show_habit, container, false);
- activity = (ShowHabitActivity) getActivity();
+ View view = inflater.inflate(R.layout.show_habit, container, false);
+ activity = (ShowHabitActivity) getActivity();
Habit habit = activity.habit;
if (android.os.Build.VERSION.SDK_INT >= 21)
@@ -52,26 +45,34 @@ public class ShowHabitFragment extends Fragment
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);
+ TextView tvHistory = (TextView) view.findViewById(R.id.tvHistory);
+ TextView tvOverview = (TextView) view.findViewById(R.id.tvOverview);
+ TextView tvStrength = (TextView) view.findViewById(R.id.tvStrength);
+ TextView tvStreaks = (TextView) view.findViewById(R.id.tvStreaks);
+ tvHistory.setTextColor(habit.color);
+ tvOverview.setTextColor(habit.color);
+ tvStrength.setTextColor(habit.color);
tvStreaks.setTextColor(habit.color);
LinearLayout llOverview = (LinearLayout) view.findViewById(R.id.llOverview);
- llOverview.addView(new RingView(activity, 200, habit.color, ((float) habit.getScore() / Habit.MAX_SCORE), "Habit strength"));
+ llOverview.addView(new RingView(activity,
+ (int) activity.getResources().getDimension(R.dimen.small_square_size) * 4, habit.color,
+ ((float) habit.getScore() / Habit.MAX_SCORE), "Habit strength"));
+
+ LinearLayout llStrength = (LinearLayout) view.findViewById(R.id.llStrength);
+ llStrength.addView(new HabitScoreView(activity, habit,
+ (int) activity.getResources().getDimension(R.dimen.small_square_size)));
- 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));
+ (int) activity.getResources().getDimension(R.dimen.small_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));
+ (int) activity.getResources().getDimension(R.dimen.small_square_size));
llStreaks.addView(hsv);
- return view;
- }
+ return view;
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/models/Habit.java b/app/src/main/java/org/isoron/uhabits/models/Habit.java
index 3ab6ac635..4f770c693 100644
--- a/app/src/main/java/org/isoron/uhabits/models/Habit.java
+++ b/app/src/main/java/org/isoron/uhabits/models/Habit.java
@@ -3,6 +3,7 @@ package org.isoron.uhabits.models;
import android.annotation.SuppressLint;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
+import android.os.AsyncTask;
import android.util.Log;
import com.activeandroid.Cache;
@@ -20,6 +21,7 @@ import org.isoron.helpers.Command;
import org.isoron.helpers.DateHelper;
import org.isoron.uhabits.R;
+import java.util.Date;
import java.util.List;
@Table(name = "Habits")
@@ -77,8 +79,7 @@ public class Habit extends Model
@SuppressLint("DefaultLocale")
public static void updateId(long oldId, long newId)
{
- SQLiteUtils.execSql(String.format(
- "update Habits set Id = %d where Id = %d", newId, oldId));
+ SQLiteUtils.execSql(String.format("update Habits set Id = %d where Id = %d", newId, oldId));
}
protected static From select()
@@ -114,18 +115,13 @@ public class Habit extends Model
public static void reorder(int from, int to)
{
- if (from == to)
- return;
+ 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();
+ 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();
@@ -152,13 +148,20 @@ public class Habit extends Model
}
}
+ public static void recomputeAllScores()
+ {
+ for (Habit habit : getHabits())
+ {
+ habit.deleteScoresNewerThan(0);
+ }
+ }
+
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);
+ return SQLiteUtils.intQuery("select count(*) from (select score, max(timestamp) from " +
+ "score group by habit) as scores where scores.score >= " +
+ Integer.toString(12973000), args);
}
@@ -184,14 +187,12 @@ public class Habit extends Model
protected From selectReps()
{
- return new Select().from(Repetition.class).where("habit = ?", getId())
- .orderBy("timestamp");
+ 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);
+ return selectReps().and("timestamp >= ?", timeFrom).and("timestamp <= ?", timeTo);
}
public boolean hasRep(long timestamp)
@@ -236,8 +237,7 @@ public class Habit extends Model
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);
+ if (counter >= freq_num) checkExtended[i] = Math.max(checkExtended[i], 1);
}
int check[] = new int[nDays + 1];
@@ -247,6 +247,13 @@ public class Habit extends Model
return check;
}
+ public int getRepsCount(int days)
+ {
+ long timeTo = DateHelper.getStartOfToday();
+ long timeFrom = timeTo - DateHelper.millisecondsInOneDay * days;
+ return selectRepsFromTo(timeFrom, timeTo).count();
+ }
+
public boolean hasImplicitRepToday()
{
long today = DateHelper.getStartOfToday();
@@ -282,14 +289,14 @@ public class Habit extends Model
public Score getNewestScore()
{
- return new Select().from(Score.class).where("habit = ?", getId())
- .orderBy("timestamp desc").limit(1).executeSingle();
+ 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();
+ new Delete().from(Score.class).where("habit = ?", getId()).and("timestamp >= ?", timestamp)
+ .execute();
}
public Integer getScore()
@@ -307,8 +314,7 @@ public class Habit extends Model
if (newestScore == null)
{
Repetition oldestRep = getOldestRep();
- if (oldestRep == null)
- return 0;
+ if (oldestRep == null) return 0;
beginningTime = oldestRep.timestamp;
beginningScore = 0;
} else
@@ -318,8 +324,7 @@ public class Habit extends Model
}
long nDays = (today - beginningTime) / day;
- if (nDays < 0)
- return newestScore.score;
+ if (nDays < 0) return newestScore.score;
int reps[] = getReps(beginningTime, today);
@@ -343,13 +348,26 @@ public class Habit extends Model
return lastScore;
}
+ public List getScores(long fromTimestamp, long toTimestamp)
+ {
+ return getScores(fromTimestamp, toTimestamp, 1, 0);
+ }
+
+ public List getScores(long fromTimestamp, long toTimestamp, int divisor, long offset)
+ {
+ return new Select().from(Score.class).where("habit = ? and timestamp > ? and " +
+ "timestamp <= ? and (timestamp - ?) % ? = 0", getId(), fromTimestamp, toTimestamp,
+ offset, divisor).execute();
+ }
+
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 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" +
@@ -436,8 +454,8 @@ public class Habit extends Model
this.modified = new Habit(modified);
this.original = new Habit(Habit.this);
- hasIntervalChanged = (this.original.freq_den != this.modified.freq_den
- || this.original.freq_num != this.modified.freq_num);
+ hasIntervalChanged = (this.original.freq_den != this.modified.freq_den ||
+ this.original.freq_num != this.modified.freq_num);
}
public void execute()
@@ -446,7 +464,30 @@ public class Habit extends Model
habit.copyAttributes(modified);
habit.save();
if (hasIntervalChanged)
- habit.deleteScoresNewerThan(0);
+ {
+ new AsyncTask()
+ {
+ @Override
+ protected Integer doInBackground(Habit... habits)
+ {
+ // HACK: We wait one second before deleting old score, otherwise the view will
+ // trigger the very slow getScore on the main thread at the same time, or even
+ // before us.
+ try
+ {
+ Thread.sleep(1000);
+ } catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+
+ habits[0].deleteScoresNewerThan(0);
+ habits[0].getScore();
+
+ return 0;
+ }
+ }.execute(habit);
+ }
}
public void undo()
@@ -455,7 +496,28 @@ public class Habit extends Model
habit.copyAttributes(original);
habit.save();
if (hasIntervalChanged)
- habit.deleteScoresNewerThan(0);
+ {
+ new AsyncTask()
+ {
+ @Override
+ protected Integer doInBackground(Habit... habits)
+ {
+ try
+ {
+ Thread.sleep(1000);
+ } catch (InterruptedException e)
+ {
+ e.printStackTrace();
+ }
+
+ habits[0].deleteScoresNewerThan(0);
+ habits[0].getScore();
+
+ return 0;
+ }
+ }.execute(habit);
+
+ }
}
public Integer getExecuteStringId()
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java
index c7e958499..68c9bd082 100644
--- a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java
@@ -1,233 +1,233 @@
package org.isoron.uhabits.views;
-import java.text.SimpleDateFormat;
-import java.util.Calendar;
-import java.util.GregorianCalendar;
-
-import org.isoron.helpers.ColorHelper;
-import org.isoron.helpers.DateHelper;
-import org.isoron.uhabits.R;
-import org.isoron.uhabits.models.Habit;
-
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.Align;
import android.graphics.Rect;
-import android.graphics.Typeface;
import android.support.v4.view.MotionEventCompat;
-import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
+import org.isoron.helpers.ColorHelper;
+import org.isoron.helpers.DateHelper;
+import org.isoron.uhabits.R;
+import org.isoron.uhabits.models.Habit;
+
+import java.text.SimpleDateFormat;
+import java.util.Calendar;
+import java.util.GregorianCalendar;
+
public class HabitHistoryView extends View
{
- private Habit habit;
- private int reps[];
+ private Habit habit;
+ private int reps[];
- private Context context;
- private Paint pSquareBg, pSquareFg, pTextHeader;
+ private Context context;
+ private Paint pSquareBg, pSquareFg, pTextHeader;
private int squareSize, squareSpacing;
- private int nColumns, offsetWeeks;
-
- private int colorPrimary, colorPrimaryBrighter, grey;
-
- public HabitHistoryView(Context context, Habit habit, int squareSize)
- {
- super(context);
- this.habit = habit;
- this.context = context;
- this.squareSize = squareSize;
-
- Typeface fontawesome = Typeface.createFromAsset(context.getAssets(),
- "fontawesome-webfont.ttf");
-
- colorPrimary = habit.color;
- colorPrimaryBrighter = ColorHelper.mixColors(colorPrimary, Color.WHITE, 0.5f);
- grey = Color.rgb(230, 230, 230);
- squareSpacing = 2;
-
- pTextHeader = new Paint();
- pTextHeader.setColor(Color.LTGRAY);
- pTextHeader.setTextAlign(Align.LEFT);
- pTextHeader.setTextSize(squareSize * 0.5f);
- pTextHeader.setAntiAlias(true);
-
- pSquareBg = new Paint();
- pSquareBg.setColor(habit.color);
-
- pSquareFg = new Paint();
- pSquareFg.setColor(Color.WHITE);
- pSquareFg.setAntiAlias(true);
-// pSquareFg.setTypeface(fontawesome);
- pSquareFg.setTextSize(squareSize * 0.5f);
- pSquareFg.setTextAlign(Align.CENTER);
- }
-
- @Override
- protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
- {
- super.onMeasure(widthMeasureSpec, heightMeasureSpec);
- setMeasuredDimension(getMeasuredWidth(), 8 * squareSize);
- }
-
- @Override
- protected void onSizeChanged(int w, int h, int oldw, int oldh)
- {
+ private int nColumns, offsetWeeks;
+
+ private int colorPrimary, colorPrimaryBrighter, grey;
+ private Float prevX, prevY;
+
+ public HabitHistoryView(Context context, Habit habit, int squareSize)
+ {
+ super(context);
+ this.habit = habit;
+ this.context = context;
+ this.squareSize = squareSize;
+
+ colorPrimary = habit.color;
+ colorPrimaryBrighter = ColorHelper.mixColors(colorPrimary, Color.WHITE, 0.5f);
+ grey = Color.rgb(230, 230, 230);
+ squareSpacing = 2;
+
+ pTextHeader = new Paint();
+ pTextHeader.setColor(Color.LTGRAY);
+ pTextHeader.setTextAlign(Align.LEFT);
+ pTextHeader.setTextSize(squareSize * 0.5f);
+ pTextHeader.setAntiAlias(true);
+
+ pSquareBg = new Paint();
+ pSquareBg.setColor(habit.color);
+
+ pSquareFg = new Paint();
+ pSquareFg.setColor(Color.WHITE);
+ pSquareFg.setAntiAlias(true);
+ pSquareFg.setTextSize(squareSize * 0.5f);
+ pSquareFg.setTextAlign(Align.CENTER);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ setMeasuredDimension(getMeasuredWidth(), 8 * squareSize);
+ }
+
+ @Override
+ protected void onSizeChanged(int w, int h, int oldw, int oldh)
+ {
nColumns = (w / squareSize) - 1;
- fetchReps();
- }
-
- private void fetchReps()
- {
- Calendar currentDate = new GregorianCalendar();
- currentDate.add(Calendar.DAY_OF_YEAR, -offsetWeeks * 7);
- int dayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK) % 7;
-
- long dateTo = DateHelper.getStartOfToday();
- for (int i = 0; i < 7 - dayOfWeek; i++)
- dateTo += DateHelper.millisecondsInOneDay;
-
- for (int i = 0; i < offsetWeeks * 7; i++)
- dateTo -= DateHelper.millisecondsInOneDay;
-
- long dateFrom = dateTo;
- for (int i = 0; i < nColumns * 7; i++)
- dateFrom -= DateHelper.millisecondsInOneDay;
-
- reps = habit.getReps(dateFrom, dateTo);
- }
-
- @Override
- protected void onDraw(Canvas canvas)
- {
- super.onDraw(canvas);
-
- Rect square = new Rect(0, 0, squareSize - squareSpacing, squareSize - squareSpacing);
-
- Calendar currentDate = new GregorianCalendar();
- currentDate.add(Calendar.DAY_OF_YEAR, -(offsetWeeks-1) * 7);
-
- int nDays = nColumns * 7;
- int todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
-
- currentDate.add(Calendar.DAY_OF_YEAR, -nDays);
-
- SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
- SimpleDateFormat dfYear = new SimpleDateFormat("yyyy");
-
- String previousMonth = "";
- String previousYear = "";
-
- int colors[] = { grey, colorPrimaryBrighter, colorPrimary };
- String markers[] = { context.getString(R.string.fa_times),
- context.getString(R.string.fa_check), context.getString(R.string.fa_check) };
-
- float squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
- float headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
- boolean justPrintedYear = false;
-
- int k = nDays;
- for (int i = 0; i < nColumns; i++)
- {
- String month = dfMonth.format(currentDate.getTime());
- String year = dfYear.format(currentDate.getTime());
-
- if(!month.equals(previousMonth))
- {
- int offset = 0;
- if(justPrintedYear)
- offset += squareSize;
-
- canvas.drawText(month, square.left + offset, square.bottom - headerTextOffset,
- pTextHeader);
- previousMonth = month;
- justPrintedYear = false;
- }
- else if(!year.equals(previousYear))
- {
- canvas.drawText(year, square.left, square.bottom - headerTextOffset, pTextHeader);
- previousYear = year;
- justPrintedYear = true;
- }
- else
- {
- justPrintedYear = false;
- }
-
-
- square.offset(0, squareSize);
-
- for (int j = 0; j < 7; j++)
- {
- if(!(i == nColumns - 1 && offsetWeeks == 0 && j > todayWeekday))
- {
- pSquareBg.setColor(colors[reps[k]]);
- canvas.drawRect(square, pSquareBg);
- // canvas.drawText(markers[reps[k]], square.centerX(), square.centerY()
- // + squareTextOffset, pSquareFg);
- canvas.drawText(Integer.toString(currentDate.get(Calendar.DAY_OF_MONTH)),
- square.centerX(), square.centerY() + squareTextOffset, pSquareFg);
- }
-
- currentDate.add(Calendar.DAY_OF_MONTH, 1);
- square.offset(0, squareSize);
- k--;
- }
-
- square.offset(squareSize, -8 * squareSize);
- }
-
- String wdays[] = { "Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri" };
-
- for (int i = 0; i < 7; i++)
- {
- square.offset(0, squareSize);
- canvas.drawText(wdays[i], square.left + headerTextOffset, square.bottom
- - headerTextOffset, pTextHeader);
- }
- }
-
- private Float prevX, prevY;
-
- @Override
- public boolean onTouchEvent(MotionEvent event)
- {
- int action = event.getAction();
-
- int pointerIndex = MotionEventCompat.getActionIndex(event);
- final float x = MotionEventCompat.getX(event, pointerIndex);
- final float y = MotionEventCompat.getY(event, pointerIndex);
-
- if(action == MotionEvent.ACTION_DOWN)
- {
- prevX = x;
- prevY = y;
- }
-
- if(action == MotionEvent.ACTION_MOVE)
- {
- float dx = x - prevX;
- float dy = y - prevY;
-
- int newOffsetWeeks = offsetWeeks + (int) (dx / squareSize);
- newOffsetWeeks = Math.max(0, newOffsetWeeks);
-
- if(newOffsetWeeks != offsetWeeks)
- {
- prevX = x;
- prevY = y;
- offsetWeeks = newOffsetWeeks;
-
- fetchReps();
- invalidate();
- }
-
- }
- return true;
- }
-
+ fetchReps();
+ }
+
+ private void fetchReps()
+ {
+ Calendar currentDate = new GregorianCalendar();
+ currentDate.add(Calendar.DAY_OF_YEAR, -offsetWeeks * 7);
+ int dayOfWeek = currentDate.get(Calendar.DAY_OF_WEEK) % 7;
+
+ long dateTo = DateHelper.getStartOfToday();
+ for (int i = 0; i < 7 - dayOfWeek; i++)
+ dateTo += DateHelper.millisecondsInOneDay;
+
+ for (int i = 0; i < offsetWeeks * 7; i++)
+ dateTo -= DateHelper.millisecondsInOneDay;
+
+ long dateFrom = dateTo;
+ for (int i = 0; i < nColumns * 7; i++)
+ dateFrom -= DateHelper.millisecondsInOneDay;
+
+ reps = habit.getReps(dateFrom, dateTo);
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas)
+ {
+ super.onDraw(canvas);
+
+ Rect square = new Rect(0, 0, squareSize - squareSpacing, squareSize - squareSpacing);
+
+ Calendar currentDate = new GregorianCalendar();
+ currentDate.add(Calendar.DAY_OF_YEAR, - (offsetWeeks - 1) * 7);
+
+ int nDays = nColumns * 7;
+ int todayWeekday = new GregorianCalendar().get(Calendar.DAY_OF_WEEK) % 7;
+
+ currentDate.add(Calendar.DAY_OF_YEAR, -nDays);
+ currentDate.add(Calendar.DAY_OF_YEAR, -todayWeekday);
+
+ SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
+ SimpleDateFormat dfYear = new SimpleDateFormat("yyyy");
+
+ String previousMonth = "";
+ String previousYear = "";
+
+ int colors[] = {grey, colorPrimaryBrighter, colorPrimary};
+ String markers[] =
+ {context.getString(R.string.fa_times), context.getString(R.string.fa_check),
+ context.getString(R.string.fa_check)};
+
+ float squareTextOffset = pSquareFg.getFontSpacing() * 0.4f;
+ float headerTextOffset = pTextHeader.getFontSpacing() * 0.3f;
+ boolean justPrintedYear = false;
+
+ int k = nDays;
+ for (int i = 0; i < nColumns; i++)
+ {
+ String month = dfMonth.format(currentDate.getTime());
+ String year = dfYear.format(currentDate.getTime());
+
+ if (!month.equals(previousMonth))
+ {
+ int offset = 0;
+ if (justPrintedYear) offset += squareSize;
+
+ canvas.drawText(month, square.left + offset, square.bottom - headerTextOffset,
+ pTextHeader);
+ previousMonth = month;
+ justPrintedYear = false;
+ } else if (!year.equals(previousYear))
+ {
+ canvas.drawText(year, square.left, square.bottom - headerTextOffset, pTextHeader);
+ previousYear = year;
+ justPrintedYear = true;
+ } else
+ {
+ justPrintedYear = false;
+ }
+
+
+ square.offset(0, squareSize);
+
+ for (int j = 0; j < 7; j++)
+ {
+ if (!(i == nColumns - 1 && offsetWeeks == 0 && j > todayWeekday))
+ {
+ pSquareBg.setColor(colors[reps[k]]);
+ canvas.drawRect(square, pSquareBg);
+ canvas.drawText(Integer.toString(currentDate.get(Calendar.DAY_OF_MONTH)),
+ square.centerX(), square.centerY() + squareTextOffset, pSquareFg);
+ }
+
+ currentDate.add(Calendar.DAY_OF_MONTH, 1);
+ square.offset(0, squareSize);
+ k--;
+ }
+
+ square.offset(squareSize, -8 * squareSize);
+ }
+
+ String wdays[] = {"Sat", "Sun", "Mon", "Tue", "Wed", "Thu", "Fri"};
+
+ for (int i = 0; i < 7; i++)
+ {
+ square.offset(0, squareSize);
+ canvas.drawText(wdays[i], square.left + headerTextOffset,
+ square.bottom - headerTextOffset, pTextHeader);
+ }
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ int action = event.getAction();
+
+ int pointerIndex = MotionEventCompat.getActionIndex(event);
+ final float x = MotionEventCompat.getX(event, pointerIndex);
+ final float y = MotionEventCompat.getY(event, pointerIndex);
+
+ if (action == MotionEvent.ACTION_DOWN)
+ {
+ prevX = x;
+ prevY = y;
+ }
+
+ if (action == MotionEvent.ACTION_MOVE)
+ {
+ float dx = x - prevX;
+ float dy = y - prevY;
+
+ if (Math.abs(dy) > Math.abs(dx)) return false;
+ getParent().requestDisallowInterceptTouchEvent(true);
+ if(move(dx))
+ {
+ prevX = x;
+ prevY = y;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean move(float dx)
+ {
+ int newOffsetWeeks = offsetWeeks + (int) (dx / squareSize);
+ newOffsetWeeks = Math.max(0, newOffsetWeeks);
+
+ if (newOffsetWeeks != offsetWeeks)
+ {
+ offsetWeeks = newOffsetWeeks;
+ fetchReps();
+ invalidate();
+ return true;
+ }
+ else
+ return false;
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java
new file mode 100644
index 000000000..324044243
--- /dev/null
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java
@@ -0,0 +1,252 @@
+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.support.v4.view.MotionEventCompat;
+import android.view.MotionEvent;
+import android.view.View;
+
+import org.isoron.helpers.ColorHelper;
+import org.isoron.helpers.DateHelper;
+import org.isoron.uhabits.models.Habit;
+import org.isoron.uhabits.models.Score;
+
+import java.text.SimpleDateFormat;
+import java.util.List;
+
+public class HabitScoreView extends View
+{
+ public static final int BUCKET_SIZE = 7;
+
+ private final Paint pGrid;
+ private final float em;
+ private Habit habit;
+ private int columnWidth, columnHeight, nColumns;
+
+ private Paint pText, pGraph;
+ private int dataOffset;
+
+ private int barHeaderHeight;
+
+ private int[] colors;
+ private float prevX;
+ private float prevY;
+ private List scores;
+
+ public HabitScoreView(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.LEFT);
+ pText.setTextSize(columnWidth * 0.5f);
+ pText.setAntiAlias(true);
+
+ pGraph = new Paint();
+ pGraph.setTextAlign(Paint.Align.CENTER);
+ pGraph.setTextSize(columnWidth * 0.5f);
+ pGraph.setAntiAlias(true);
+ pGraph.setStrokeWidth(columnWidth * 0.1f);
+
+ pGrid = new Paint();
+ pGrid.setColor(Color.LTGRAY);
+ pGrid.setAntiAlias(true);
+ pGrid.setStrokeWidth(columnWidth * 0.05f);
+
+ columnHeight = 8 * columnWidth;
+ barHeaderHeight = columnWidth;
+ em = pText.getFontSpacing();
+
+ 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);
+ }
+
+ private void fetchScores()
+ {
+
+ long toTimestamp = DateHelper.getStartOfToday();
+ for (int i = 0; i < dataOffset * BUCKET_SIZE; i++)
+ toTimestamp -= DateHelper.millisecondsInOneDay;
+
+ long fromTimestamp = toTimestamp;
+ for (int i = 0; i < nColumns * BUCKET_SIZE; i++)
+ fromTimestamp -= DateHelper.millisecondsInOneDay;
+
+ scores = habit.getScores(fromTimestamp, toTimestamp, BUCKET_SIZE * DateHelper.millisecondsInOneDay,
+ toTimestamp);
+ }
+
+ @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;
+ fetchScores();
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas)
+ {
+ super.onDraw(canvas);
+
+ float lineHeight = pText.getFontSpacing();
+ float barHeaderOffset = lineHeight * 0.4f;
+
+ RectF rGrid = new RectF(0, 0, nColumns * columnWidth, columnHeight);
+ rGrid.offset(0, barHeaderHeight);
+ drawGrid(canvas, rGrid);
+
+ SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
+ SimpleDateFormat dfDay = new SimpleDateFormat("d");
+
+ String previousMonth = "";
+
+ pGraph.setColor(habit.color);
+ RectF prevR = null;
+
+ for (int offset = nColumns - scores.size(); offset < nColumns; offset++)
+ {
+ Score score = scores.get(offset - nColumns + scores.size());
+ String month = dfMonth.format(score.timestamp);
+ String day = dfDay.format(score.timestamp);
+
+ long s = score.score;
+ double sRelative = ((double) s) / Habit.MAX_SCORE;
+
+ int height = (int) (columnHeight * sRelative);
+
+ RectF r = new RectF(0, 0, columnWidth, columnWidth);
+ r.offset(offset * columnWidth,
+ barHeaderHeight + columnHeight - height - columnWidth / 2);
+
+ if (prevR != null)
+ {
+ drawLine(canvas, prevR, r);
+ drawMarker(canvas, prevR);
+ }
+
+ if (offset == nColumns - 1) drawMarker(canvas, r);
+
+ prevR = r;
+
+ r = new RectF(0, 0, columnWidth, columnHeight);
+ r.offset(offset * columnWidth, barHeaderHeight);
+ if (!month.equals(previousMonth))
+ {
+ canvas.drawText(month, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
+ } else
+ {
+ canvas.drawText(day, r.centerX(), r.bottom + lineHeight * 1.2f, pText);
+ }
+
+ previousMonth = month;
+
+ }
+ }
+
+ private void drawGrid(Canvas canvas, RectF rGrid)
+ {
+// pGrid.setColor(Color.rgb(230, 230, 230));
+// pGrid.setStyle(Paint.Style.STROKE);
+// canvas.drawRect(rGrid, pGrid);
+
+ int nRows = 5;
+ float rowHeight = rGrid.height() / nRows;
+
+ pGrid.setColor(Color.rgb(240, 240, 240));
+ for (int i = 0; i < nRows; i++)
+ {
+ canvas.drawText(String.format("%d%%", (100 - i * 100 / nRows)), rGrid.left + 0.5f * em,
+ rGrid.top + 1f * em, pText);
+ canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
+ rGrid.offset(0, rowHeight);
+ }
+
+ canvas.drawLine(rGrid.left, rGrid.top, rGrid.right, rGrid.top, pGrid);
+ }
+
+ private void drawLine(Canvas canvas, RectF rectFrom, RectF rectTo)
+ {
+ pGraph.setColor(habit.color);
+ canvas.drawLine(rectFrom.centerX(), rectFrom.centerY(), rectTo.centerX(), rectTo.centerY(),
+ pGraph);
+ }
+
+ private void drawMarker(Canvas canvas, RectF rect)
+ {
+ rect.inset(columnWidth * 0.15f, columnWidth * 0.15f);
+ pGraph.setColor(Color.WHITE);
+ canvas.drawOval(rect, pGraph);
+
+ rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
+ pGraph.setColor(habit.color);
+ canvas.drawOval(rect, pGraph);
+
+ rect.inset(columnWidth * 0.1f, columnWidth * 0.1f);
+ pGraph.setColor(Color.WHITE);
+ canvas.drawOval(rect, pGraph);
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ int action = event.getAction();
+
+ int pointerIndex = MotionEventCompat.getActionIndex(event);
+ final float x = MotionEventCompat.getX(event, pointerIndex);
+ final float y = MotionEventCompat.getY(event, pointerIndex);
+
+ if (action == MotionEvent.ACTION_DOWN)
+ {
+ prevX = x;
+ prevY = y;
+ }
+
+ if (action == MotionEvent.ACTION_MOVE)
+ {
+ float dx = x - prevX;
+ float dy = y - prevY;
+
+ if (Math.abs(dy) > Math.abs(dx)) return false;
+ getParent().requestDisallowInterceptTouchEvent(true);
+ if (move(dx))
+ {
+ prevX = x;
+ prevY = y;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean move(float dx)
+ {
+ int newDataOffset = dataOffset + (int) (dx / columnWidth);
+ newDataOffset = Math.max(0, newDataOffset);
+
+ if (newDataOffset != dataOffset)
+ {
+ dataOffset = newDataOffset;
+ fetchScores();
+ invalidate();
+ return true;
+ } else return false;
+ }
+}
diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
index c90a96bd0..ec02a7a40 100644
--- a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java
@@ -5,6 +5,8 @@ import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Rect;
+import android.support.v4.view.MotionEventCompat;
+import android.view.MotionEvent;
import android.view.View;
import org.isoron.helpers.ColorHelper;
@@ -22,6 +24,7 @@ public class HabitStreakView extends View
private Paint pText, pBar;
private long streaks[];
+ private int dataOffset;
private long streakStart[], streakEnd[], streakLength[];
private long maxStreakLength;
@@ -29,6 +32,8 @@ public class HabitStreakView extends View
private int barHeaderHeight;
private int[] colors;
+ private float prevX;
+ private float prevY;
public HabitStreakView(Context context, Habit habit, int columnWidth)
{
@@ -98,7 +103,7 @@ public class HabitStreakView extends View
float lineHeight = pText.getFontSpacing();
float barHeaderOffset = lineHeight * 0.4f;
- int start = Math.max(0, streakStart.length - nColumns);
+ int start = Math.max(0, streakStart.length - nColumns - dataOffset);
SimpleDateFormat dfMonth = new SimpleDateFormat("MMM");
String previousMonth = "";
@@ -125,4 +130,51 @@ public class HabitStreakView extends View
previousMonth = month;
}
}
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event)
+ {
+ int action = event.getAction();
+
+ int pointerIndex = MotionEventCompat.getActionIndex(event);
+ final float x = MotionEventCompat.getX(event, pointerIndex);
+ final float y = MotionEventCompat.getY(event, pointerIndex);
+
+ if (action == MotionEvent.ACTION_DOWN)
+ {
+ prevX = x;
+ prevY = y;
+ }
+
+ if (action == MotionEvent.ACTION_MOVE)
+ {
+ float dx = x - prevX;
+ float dy = y - prevY;
+
+ if (Math.abs(dy) > Math.abs(dx)) return false;
+ getParent().requestDisallowInterceptTouchEvent(true);
+ if(move(dx))
+ {
+ prevX = x;
+ prevY = y;
+ }
+ }
+
+ return true;
+ }
+
+ private boolean move(float dx)
+ {
+ int newDataOffset = dataOffset + (int) (dx / columnWidth);
+ newDataOffset = Math.max(0, Math.min(streakStart.length - nColumns, newDataOffset));
+
+ if (newDataOffset != dataOffset)
+ {
+ dataOffset = newDataOffset;
+ invalidate();
+ return true;
+ }
+ else
+ return false;
+ }
}
diff --git a/app/src/main/java/org/isoron/uhabits/views/RingView.java b/app/src/main/java/org/isoron/uhabits/views/RingView.java
index a672e899e..07315657f 100644
--- a/app/src/main/java/org/isoron/uhabits/views/RingView.java
+++ b/app/src/main/java/org/isoron/uhabits/views/RingView.java
@@ -28,11 +28,8 @@ public class RingView extends View
pRing.setColor(color);
pRing.setAntiAlias(true);
pRing.setTextAlign(Paint.Align.CENTER);
- pRing.setTextSize(size * 0.15f);
this.label = label;
-
- lineHeight = pRing.getFontSpacing();
}
@Override
@@ -52,16 +49,19 @@ public class RingView extends View
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);
+ 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);
+ pRing.setTextSize(size * 0.2f);
+ lineHeight = pRing.getFontSpacing();
+ canvas.drawText(String.format("%.0f%%", perc * 100), r.centerX(), r.centerY()+lineHeight/3, pRing);
+
+ pRing.setTextSize(size * 0.15f);
canvas.drawText(label, size/2, size + lineHeight * 1.2f, pRing);
}
}
diff --git a/app/src/main/res/drawable-hdpi/ic_launcher.png b/app/src/main/res/drawable-hdpi/ic_launcher.png
deleted file mode 100644
index 7bc14b13c..000000000
Binary files a/app/src/main/res/drawable-hdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable-mdpi/ic_launcher.png b/app/src/main/res/drawable-mdpi/ic_launcher.png
deleted file mode 100644
index 364d5ee7a..000000000
Binary files a/app/src/main/res/drawable-mdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xhdpi/ic_launcher.png b/app/src/main/res/drawable-xhdpi/ic_launcher.png
deleted file mode 100644
index f54690a0c..000000000
Binary files a/app/src/main/res/drawable-xhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable-xxhdpi/ic_launcher.png b/app/src/main/res/drawable-xxhdpi/ic_launcher.png
deleted file mode 100644
index 884cb1b40..000000000
Binary files a/app/src/main/res/drawable-xxhdpi/ic_launcher.png and /dev/null differ
diff --git a/app/src/main/res/drawable/stripe.png b/app/src/main/res/drawable/stripe.png
new file mode 100644
index 000000000..16dd9fc72
Binary files /dev/null and b/app/src/main/res/drawable/stripe.png differ
diff --git a/app/src/main/res/layout/show_habit.xml b/app/src/main/res/layout/show_habit.xml
index f1f631dab..bca7927bf 100644
--- a/app/src/main/res/layout/show_habit.xml
+++ b/app/src/main/res/layout/show_habit.xml
@@ -1,21 +1,35 @@
+ xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:background="@color/windowBackground"
+ android:fillViewport="true">
-
+
+ android:text="@string/overview"/>
+
+
+
+
+
+
@@ -24,13 +38,13 @@
+ android:text="@string/history"/>
+ android:orientation="horizontal"/>
@@ -38,13 +52,13 @@
+ android:text="@string/streaks"/>
+ android:orientation="horizontal"/>
\ No newline at end of file
diff --git a/app/src/main/res/mipmap-hdpi/ic_launcher.png b/app/src/main/res/mipmap-hdpi/ic_launcher.png
new file mode 100644
index 000000000..c2cd08fa0
Binary files /dev/null and b/app/src/main/res/mipmap-hdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-mdpi/ic_launcher.png b/app/src/main/res/mipmap-mdpi/ic_launcher.png
new file mode 100644
index 000000000..9b6293ce5
Binary files /dev/null and b/app/src/main/res/mipmap-mdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/app/src/main/res/mipmap-xhdpi/ic_launcher.png
new file mode 100644
index 000000000..a9fcd24c7
Binary files /dev/null and b/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png
new file mode 100644
index 000000000..97b9df2d3
Binary files /dev/null and b/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png
new file mode 100644
index 000000000..ddde65f4c
Binary files /dev/null and b/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ
diff --git a/app/src/main/res/values-v21/styles.xml b/app/src/main/res/values-v21/styles.xml
index 6a12ca623..e06c1b261 100644
--- a/app/src/main/res/values-v21/styles.xml
+++ b/app/src/main/res/values-v21/styles.xml
@@ -14,19 +14,9 @@
-
\ No newline at end of file
diff --git a/app/src/main/res/values-v21/styles_list_habits.xml b/app/src/main/res/values-v21/styles_list_habits.xml
index f82246677..a7ce04361 100644
--- a/app/src/main/res/values-v21/styles_list_habits.xml
+++ b/app/src/main/res/values-v21/styles_list_habits.xml
@@ -1,20 +1,13 @@
-
-
+
\ No newline at end of file
diff --git a/app/src/main/res/values/attrs_dslv.xml b/app/src/main/res/values/attrs_dslv.xml
new file mode 100644
index 000000000..eda059eb4
--- /dev/null
+++ b/app/src/main/res/values/attrs_dslv.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml
index f996ac29b..d207cb030 100644
--- a/app/src/main/res/values/dimens.xml
+++ b/app/src/main/res/values/dimens.xml
@@ -1,54 +1,6 @@
-
-
16dp
16dp
-
- 20dp
-
-
- 64dip
- 48dip
- 8dip
- 4dip
-
-
- - 0.82
- - 0.85
- - 0.16
- - 0.19
- - 0.81
- - 0.60
- - 0.83
- - 0.17
- - 0.14
- - 0.11
- 60sp
- -30dp
- 16sp
- 14sp
- 6dip
- 4dip
- 96dip
- 48dip
- 48dip
- 24dip
- 270dip
- 270dp
- 30dp
- 155dp
- 270dp
- 42dp
- 50dp
- 10sp
- 16dp
- 45dp
- 30dp
- 75dp
- 30dp
- 14dp
- 16sp
- 16sp
- 64dp
- 22dp
+ 20dp
+ 42dp
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens_color_picker.xml b/app/src/main/res/values/dimens_color_picker.xml
new file mode 100644
index 000000000..27517fbae
--- /dev/null
+++ b/app/src/main/res/values/dimens_color_picker.xml
@@ -0,0 +1,7 @@
+
+
+ 64dip
+ 48dip
+ 8dip
+ 4dip
+
\ No newline at end of file
diff --git a/app/src/main/res/values/dimens_date_time.xml b/app/src/main/res/values/dimens_date_time.xml
new file mode 100644
index 000000000..ca2417527
--- /dev/null
+++ b/app/src/main/res/values/dimens_date_time.xml
@@ -0,0 +1,42 @@
+
+
+ - 0.82
+ - 0.85
+ - 0.16
+ - 0.19
+ - 0.81
+ - 0.60
+ - 0.83
+ - 0.17
+ - 0.14
+ - 0.11
+
+ 60sp
+ -30dp
+ 16sp
+ 14sp
+ 6dip
+ 4dip
+ 96dip
+ 48dip
+ 48dip
+ 24dip
+ 270dip
+ 270dp
+ 30dp
+ 155dp
+ 270dp
+ 42dp
+ 50dp
+ 10sp
+ 16dp
+ 45dp
+ 30dp
+ 75dp
+ 30dp
+ 14dp
+ 16sp
+ 16sp
+ 64dp
+ 22dp
+
\ No newline at end of file
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index ce091907f..752a982dd 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -25,20 +25,24 @@
- vertical
-
+
+
-
+
+
@@ -58,13 +62,16 @@
- 3dp
-
+
+