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 @@ - - + \ 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 - + +