Merge pull request #552 from TheHollidayInn/todo-local-notifications

Added notifications for todos
This commit is contained in:
Phillip Thelen 2016-06-14 12:20:12 +02:00 committed by GitHub
commit a7ea0d7714
18 changed files with 718 additions and 2 deletions

View file

@ -17,6 +17,7 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="com.android.vending.BILLING" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<application
android:name=".HabiticaApplication"
@ -120,6 +121,13 @@
android:theme="@style/Theme.AppCompat.Light.NoActionBar" />
<receiver android:process=":remote" android:name=".NotificationPublisher" />
<receiver android:process=":remote" android:name=".receivers.TaskReceiver"></receiver>
<receiver android:name=".receivers.TaskAlarmBootReceiver" android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action._BOOT_COMPLETED"/>
</intent-filter>
</receiver>
<provider
android:name="android.support.v4.content.FileProvider"
android:authorities="com.habitrpg.android.habitica.fileprovider"

View file

@ -344,6 +344,50 @@
</LinearLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/task_reminders_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="30dp"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:text="@string/reminders"
android:textAppearance="?android:attr/textAppearanceLarge" />
<android.support.v7.widget.RecyclerView
android:id="@+id/reminders_recycler_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<EditText
android:id="@+id/new_reminder_edittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:hint="@string/start_date"
android:textColor="@android:color/black"
android:focusable="false" />
<Button
android:id="@+id/add_reminder_button"
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/add_checklist_item" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"

View file

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="4dp"
android:paddingBottom="4dp">
<EditText
android:id="@+id/item_edittext"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/black"
android:focusable="false" />
<Button
style="?android:attr/buttonStyleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/delete_item_button"
android:text="x" />
</LinearLayout>

View file

@ -80,6 +80,7 @@
<string name="positive_habit_form">Positive ( + )</string>
<string name="negative_habit_form">Negative ( - )</string>
<string name="checklist">Checklist</string>
<string name="reminders">Reminders</string>
<string name="actions">Actions</string>
<string name="attributes">Attributes</string>

View file

@ -35,6 +35,7 @@ import com.magicmicky.habitrpgwrapper.lib.models.inventory.QuestContent;
import com.magicmicky.habitrpgwrapper.lib.models.responses.FeedResponse;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ItemData;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.RemindersItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskList;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskTag;
@ -55,6 +56,7 @@ import com.magicmicky.habitrpgwrapper.lib.utils.MountListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PetListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PurchasedDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.QuestListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.RemindersItemSerializer;
import com.magicmicky.habitrpgwrapper.lib.utils.SkillDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskSerializer;
@ -217,6 +219,7 @@ public class APIHelper implements Action1<Throwable> {
.registerTypeAdapter(boolean.class, new BooleanAsIntAdapter())
.registerTypeAdapter(skillListType, new SkillDeserializer())
.registerTypeAdapter(ChecklistItem.class, new ChecklistItemSerializer())
.registerTypeAdapter(RemindersItem.class, new RemindersItemSerializer())
.registerTypeAdapter(TaskList.class, new TaskListDeserializer())
.registerTypeAdapter(Purchases.class, new PurchasedDeserializer())
.registerTypeAdapter(customizationListType, new CustomizationDeserializer())

View file

@ -7,5 +7,5 @@ public class HabitDatabase {
public static final String NAME = "Habitica";
public static final int VERSION = 20;
public static final int VERSION = 21;
}

View file

@ -27,6 +27,7 @@ import com.magicmicky.habitrpgwrapper.lib.models.inventory.QuestContent;
import com.magicmicky.habitrpgwrapper.lib.models.responses.FeedResponse;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ItemData;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.RemindersItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskList;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskTag;
@ -47,6 +48,7 @@ import com.magicmicky.habitrpgwrapper.lib.utils.MountListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PetListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.PurchasedDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.QuestListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.RemindersItemSerializer;
import com.magicmicky.habitrpgwrapper.lib.utils.SkillDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskListDeserializer;
import com.magicmicky.habitrpgwrapper.lib.utils.TaskSerializer;

View file

@ -0,0 +1,22 @@
package com.habitrpg.android.habitica.receivers;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.habitrpg.android.habitica.ui.helpers.TaskAlarmManager;
/**
* Created by keithholliday on 5/29/16.
*/
public class TaskAlarmBootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context arg0, Intent arg1) {
TaskAlarmManager taskAlarmManager = TaskAlarmManager.getInstance(arg0);
taskAlarmManager.scheduleAllSavedAlarms();
}
}

View file

@ -0,0 +1,57 @@
package com.habitrpg.android.habitica.receivers;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.ui.activities.MainActivity;
import com.habitrpg.android.habitica.ui.helpers.TaskAlarmManager;
/**
* Created by keithholliday on 5/29/16.
*/
public class TaskReceiver extends BroadcastReceiver {
private Context context;
private String taskId;
private String taskTitle;
private TaskAlarmManager taskAlarmManager;
@Override
public void onReceive(Context arg0, Intent arg1) {
context = arg0;
taskAlarmManager = TaskAlarmManager.getInstance(context);
Bundle extras = arg1.getExtras();
if (extras != null) {
taskTitle = extras.getString(TaskAlarmManager.TASK_NAME_INTENT_KEY);
taskId = extras.getString(TaskAlarmManager.TASK_ID_INTENT_KEY);
//This will set up the next reminders for dailies
taskAlarmManager.addAlarmForTaskId(taskId);
createNotification();
}
}
public void createNotification() {
Intent intent = new Intent(context, MainActivity.class);
PendingIntent pendingIntent = PendingIntent.getActivity(context, (int) System.currentTimeMillis(), intent, 0);
Notification notification = new Notification.Builder(context)
.setContentTitle(taskTitle)
.setContentText(taskTitle).setSmallIcon(R.drawable.ic_gryphon)
.setContentIntent(pendingIntent)
.build();
NotificationManager notificationManager = (NotificationManager) context.getSystemService(context.NOTIFICATION_SERVICE);
notification.flags |= Notification.FLAG_AUTO_CANCEL;
notificationManager.notify((int) System.currentTimeMillis(), notification);
}
}

View file

@ -8,12 +8,15 @@ import com.habitrpg.android.habitica.events.TaskSaveEvent;
import com.habitrpg.android.habitica.events.commands.DeleteTaskCommand;
import com.habitrpg.android.habitica.ui.WrapContentRecyclerViewLayoutManager;
import com.habitrpg.android.habitica.ui.adapter.tasks.CheckListAdapter;
import com.habitrpg.android.habitica.ui.adapter.tasks.RemindersAdapter;
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser;
import com.habitrpg.android.habitica.ui.helpers.SimpleItemTouchHelperCallback;
import com.habitrpg.android.habitica.ui.helpers.TaskAlarmManager;
import com.habitrpg.android.habitica.ui.helpers.ViewHelper;
import com.magicmicky.habitrpgwrapper.lib.models.Tag;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.ChecklistItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Days;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.RemindersItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.TaskTag;
import com.raizlabs.android.dbflow.runtime.transaction.BaseTransaction;
@ -25,6 +28,7 @@ import com.raizlabs.android.dbflow.sql.language.Set;
import org.greenrobot.eventbus.EventBus;
import android.app.DatePickerDialog;
import android.app.TimePickerDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@ -37,7 +41,6 @@ import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.helper.ItemTouchHelper;
import android.text.TextUtils;
import android.util.ArraySet;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuItem;
@ -55,6 +58,7 @@ import android.widget.NumberPicker;
import android.widget.Spinner;
import android.widget.TableRow;
import android.widget.TextView;
import android.widget.TimePicker;
import java.text.DateFormat;
import java.text.DecimalFormat;
@ -67,6 +71,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Locale;
import java.util.TreeSet;
import java.util.UUID;
import butterknife.BindView;
import rx.Observable;
@ -91,8 +96,11 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
private NumberPicker frequencyPicker;
private List<Tag> tags;
private CheckListAdapter checklistAdapter;
private RemindersAdapter remindersAdapter;
private List<CheckBox> tagCheckBoxList;
private TaskAlarmManager taskAlarmManager;
@BindView(R.id.task_value_edittext)
EditText taskValue;
@ -159,6 +167,18 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
@BindView(R.id.add_checklist_button)
Button button;
@BindView(R.id.task_reminders_wrapper)
LinearLayout remindersWrapper;
@BindView(R.id.new_reminder_edittext)
EditText newRemindersEditText;
@BindView(R.id.reminders_recycler_view)
RecyclerView remindersRecyclerView;
@BindView(R.id.add_reminder_button)
Button addReminderButton;
@BindView(R.id.emoji_toggle_btn0)
ImageButton emojiToggle0;
@ -215,6 +235,8 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
return;
}
taskAlarmManager = TaskAlarmManager.getInstance(this);
dueDateListener = new DateEditTextListener(dueDatePickerText);
startDateListener = new DateEditTextListener(startDatePickerText);
@ -255,6 +277,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
taskWrapper.removeView(startDateLayout);
mainWrapper.removeView(checklistWrapper);
mainWrapper.removeView(remindersWrapper);
positiveCheckBox.setChecked(true);
negativeCheckBox.setChecked(true);
@ -292,6 +315,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
} else {
mainWrapper.removeView(checklistWrapper);
mainWrapper.removeView(remindersWrapper);
difficultyWrapper.setVisibility(View.GONE);
attributeWrapper.setVisibility(View.GONE);
@ -314,6 +338,7 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
if (taskType.equals("todo") || taskType.equals("daily")) {
createCheckListRecyclerView();
createRemindersRecyclerView();
}
// Emoji keyboard stuff
@ -521,6 +546,65 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
});
}
private void createRemindersRecyclerView() {
List<RemindersItem> reminders = new ArrayList<>();
if (task != null && task.getReminders() != null) {
reminders = task.getReminders();
}
remindersAdapter = new RemindersAdapter(reminders);
LinearLayoutManager llm = new LinearLayoutManager(this);
llm.setOrientation(LinearLayoutManager.VERTICAL);
remindersRecyclerView.setLayoutManager(llm);
remindersRecyclerView.setAdapter(remindersAdapter);
remindersRecyclerView.setLayoutManager(new WrapContentRecyclerViewLayoutManager(this));
ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(remindersAdapter);
ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(callback);
mItemTouchHelper.attachToRecyclerView(remindersRecyclerView);
newRemindersEditText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar mcurrentTime = Calendar.getInstance();
int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
int minute = mcurrentTime.get(Calendar.MINUTE);
TimePickerDialog mTimePicker;
mTimePicker = new TimePickerDialog(TaskFormActivity.this, new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker timePicker, int selectedHour, int selectedMinute) {
newRemindersEditText.setText( selectedHour + ":" + selectedMinute);
}
}, hour, minute, true);
mTimePicker.setTitle("Select Time");
mTimePicker.show();
}
});
addReminderButton.setOnClickListener(v -> {
Date startDate = new Date(startDateListener.getCalendar().getTimeInMillis());
Date time = startDate;
String reminderTimeString = newRemindersEditText.getText().toString();
if (reminderTimeString.isEmpty()) return;
String[] reminderTimeSplit = reminderTimeString.split(":");
time.setHours(Integer.parseInt(reminderTimeSplit[0]));
time.setMinutes(Integer.parseInt(reminderTimeSplit[1]));
time.setSeconds(0);
RemindersItem item = new RemindersItem();
UUID randomUUID = UUID.randomUUID();
item.setId(randomUUID.toString());
item.setStartDate(startDate);
item.setTime(time);
remindersAdapter.addItem(item);
newRemindersEditText.setText("");
});
}
private void createTagsCheckBoxes() {
int position = 0;
@ -747,6 +831,12 @@ public class TaskFormActivity extends BaseActivity implements AdapterView.OnItem
}
}
if (remindersAdapter != null) {
if (remindersAdapter.getRemindersItems() != null) {
task.setReminders(remindersAdapter.getRemindersItems());
}
}
if (task.text.isEmpty())
return false;

View file

@ -0,0 +1,143 @@
package com.habitrpg.android.habitica.ui.adapter.tasks;
import android.app.TimePickerDialog;
import android.graphics.Color;
import android.support.v7.widget.RecyclerView;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TimePicker;
import com.github.data5tream.emojilib.EmojiEditText;
import com.habitrpg.android.habitica.R;
import com.habitrpg.android.habitica.ui.helpers.ItemTouchHelperAdapter;
import com.habitrpg.android.habitica.ui.helpers.ItemTouchHelperViewHolder;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.RemindersItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.RemindersItem;
import java.util.Calendar;
import java.util.Date;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import butterknife.BindView;
import butterknife.ButterKnife;
/**
* Created by keithholliday on 5/31/16.
*/
public class RemindersAdapter extends RecyclerView.Adapter<RemindersAdapter.ItemViewHolder>
implements ItemTouchHelperAdapter {
private final List<RemindersItem> reminders = new ArrayList<>();
public RemindersAdapter(List<RemindersItem> remindersInc) {
reminders.addAll(remindersInc);
}
@Override
public ItemViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.reminder_item, parent, false);
ItemViewHolder itemViewHolder = new ItemViewHolder(view);
return itemViewHolder;
}
@Override
public void onBindViewHolder(final ItemViewHolder holder, int position) {
Date time = reminders.get(position).getTime();
holder.reminderItemTextView.setText(time.getHours() + ":" + time.getMinutes());
}
public void addItem(RemindersItem item){
reminders.add(item);
notifyItemInserted(reminders.size() - 1);
}
public List<RemindersItem> getRemindersItems(){
return reminders;
}
@Override
public int getItemCount() {
return reminders.size();
}
@Override
public void onItemDismiss(int position) {
if(position >= 0 && position < reminders.size()){
reminders.get(position).async().delete();
reminders.remove(position);
notifyItemRemoved(position);
}
}
@Override
public void onItemMove(int fromPosition, int toPosition) {
Collections.swap(reminders, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
}
public class ItemViewHolder extends RecyclerView.ViewHolder implements
ItemTouchHelperViewHolder, Button.OnClickListener {
@BindView(R.id.item_edittext)
EditText reminderItemTextView;
@BindView(R.id.delete_item_button)
Button deleteButton;
public ItemViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
deleteButton.setOnClickListener(this);
reminderItemTextView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Calendar mcurrentTime = Calendar.getInstance();
int hour = mcurrentTime.get(Calendar.HOUR_OF_DAY);
int minute = mcurrentTime.get(Calendar.MINUTE);
TimePickerDialog mTimePicker;
mTimePicker = new TimePickerDialog(v.getContext(), new TimePickerDialog.OnTimeSetListener() {
@Override
public void onTimeSet(TimePicker timePicker, int selectedHour, int selectedMinute) {
reminderItemTextView.setText( selectedHour + ":" + selectedMinute);
RemindersItem reminder = reminders.get(getAdapterPosition());
Date time = reminder.getTime();
time.setHours(selectedHour);
time.setMinutes(selectedMinute);
time.setSeconds(0);
reminder.setTime(time);
reminder.async().save();
}
}, hour, minute, true);
mTimePicker.setTitle("Select Time");
mTimePicker.show();
}
});
}
@Override
public void onItemSelected() {
itemView.setBackgroundColor(Color.LTGRAY);
}
@Override
public void onItemClear() {
itemView.setBackgroundColor(0);
}
@Override
public void onClick(View v) {
if (v == deleteButton) {
RemindersAdapter.this.onItemDismiss(getAdapterPosition());
}
}
}
}

View file

@ -0,0 +1,135 @@
package com.habitrpg.android.habitica.ui.helpers;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import com.habitrpg.android.habitica.events.TaskSaveEvent;
import com.habitrpg.android.habitica.receivers.TaskReceiver;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.RemindersItem;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.Task;
import com.raizlabs.android.dbflow.sql.builder.Condition;
import com.raizlabs.android.dbflow.sql.language.Select;
import org.greenrobot.eventbus.EventBus;
import org.greenrobot.eventbus.Subscribe;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
/**
* Created by keithholliday on 5/29/16.
*/
public class TaskAlarmManager {
private static TaskAlarmManager instance = null;
private Context context;
public static final String TASK_ID_INTENT_KEY = "TASK_ID";
public static final String TASK_NAME_INTENT_KEY = "TASK_NAME";
private TaskAlarmManager(Context context) {
this.context = context;
EventBus.getDefault().register(this);
}
public static TaskAlarmManager getInstance(Context context){
if(instance == null)
{
instance = new TaskAlarmManager(context);
}
return instance;
}
@Subscribe
public void onEvent(TaskSaveEvent evnt) {
Task task = (Task) evnt.task;
List<RemindersItem> reminders = task.getReminders();
for (RemindersItem reminder : reminders) {
if (task.getType().equals(Task.TYPE_DAILY)) {
//Ensure that we set to the next available time
reminder = this.setTimeForDailyReminder(reminder, task);
}
this.setAlarmForRemindersItem(reminder);
reminder.async().save();
}
}
//This function is used from the TaskReceiver since we do not have access to the task
//We currently only use this function to schedule the next reminder for dailies
//We may be able to use repeating alarms instead of this in the future
public void addAlarmForTaskId(String taskId) {
List<Task> tasks = new Select()
.from(Task.class)
.where(Condition.column("id").eq(taskId))
.queryList();
Task task = tasks.get(0);
if (task == null) return;
if (!task.getType().equals(Task.TYPE_DAILY)) {
return;
}
List<RemindersItem> reminders = task.getReminders();
for (RemindersItem remindersItem : reminders) {
//Ensure that we set to the next available time
remindersItem = this.setTimeForDailyReminder(remindersItem, task);
this.setAlarmForRemindersItem(remindersItem);
remindersItem.async().save();
}
}
public void scheduleAllSavedAlarms() {
List<RemindersItem> reminders = new Select()
.from(RemindersItem.class)
.where(Condition.column("time").greaterThan(new Date()))
.orderBy(true, "time")
.queryList();
for (RemindersItem remindersItem : reminders) {
this.setAlarmForRemindersItem(remindersItem);
}
}
private RemindersItem setTimeForDailyReminder (RemindersItem remindersItem, Task task) {
Date oldTime = remindersItem.getTime();
Date newTime = task.getNextActiveDateAfter(oldTime);
newTime.setHours(oldTime.getHours());
newTime.setMinutes(oldTime.getMinutes());
newTime.setSeconds(0);
remindersItem.setTime(newTime);
return remindersItem;
}
private void setAlarmForRemindersItem(RemindersItem remindersItem) {
Task reminderItemTask = remindersItem.getTask();
Integer alarmId = remindersItem.getAlarmId();
if (alarmId == null) {
alarmId = (int) System.currentTimeMillis();
remindersItem.setAlarmId(alarmId);
}
Date now = new Date();
if (remindersItem.getTime().before(now)) {
return;
}
Calendar cal = Calendar.getInstance();
cal.setTime(remindersItem.getTime());
Intent intent = new Intent(context, TaskReceiver.class);
intent.putExtra(TASK_NAME_INTENT_KEY, reminderItemTask.getText());
intent.putExtra(TASK_ID_INTENT_KEY, reminderItemTask.getId());
PendingIntent sender = PendingIntent.getBroadcast(context, alarmId, intent, PendingIntent.FLAG_UPDATE_CURRENT);
AlarmManager am = (AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
am.set(AlarmManager.RTC_WAKEUP, cal.getTimeInMillis(), sender);
}
}

View file

@ -0,0 +1,91 @@
package com.magicmicky.habitrpgwrapper.lib.models.tasks;
import com.habitrpg.android.habitica.HabitDatabase;
import com.habitrpg.android.habitica.database.ExcludeCheckListItem;
import com.raizlabs.android.dbflow.annotation.Column;
import com.raizlabs.android.dbflow.annotation.ForeignKey;
import com.raizlabs.android.dbflow.annotation.ForeignKeyReference;
import com.raizlabs.android.dbflow.annotation.PrimaryKey;
import com.raizlabs.android.dbflow.annotation.Table;
import com.raizlabs.android.dbflow.structure.BaseModel;
import com.raizlabs.android.dbflow.structure.container.ForeignKeyContainer;
import java.util.Date;
/**
* Created by keithholliday on 5/31/16.
*/
@Table(databaseName = HabitDatabase.NAME)
public class RemindersItem extends BaseModel {
@Column
@PrimaryKey
private String id;
@Column
private Date startDate;
@Column
private Date time;
@Column
private Integer alarmId;
@Column
@ForeignKey(
references = {@ForeignKeyReference(columnName = "task_id",
columnType = String.class,
foreignColumnName = "id")},
saveForeignKeyModel = false)
@ExcludeCheckListItem
ForeignKeyContainer<Task> task;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public Integer getAlarmId() {
return this.alarmId;
}
public void setAlarmId(Integer alarmId) {
this.alarmId = alarmId;
}
public Date getStartDate() {
return startDate;
}
public void setStartDate(Date startDate) {
this.startDate = startDate;
}
public Date getTime() {
return time;
}
public void setTime(Date time) {
this.time = time;
}
public Task getTask() {
if (task != null) {
return task.toModel();
} else {
return null;
}
}
public void setTask(Task task) {
this.task = new ForeignKeyContainer<>(Task.class);
this.task.setModel(task);
this.task.put("id", task.getId());
}
@Override
public void save() {
if (this.getId() == null) {
return;
}
super.save();
}
}

View file

@ -78,6 +78,7 @@ public class Task extends BaseModel {
public boolean completed;
public List<ChecklistItem> checklist;
public List<RemindersItem> reminders;
//dailies
@ -291,6 +292,25 @@ public class Task extends BaseModel {
this.checklist = checklist;
}
@OneToMany(methods = {OneToMany.Method.SAVE, OneToMany.Method.DELETE}, variableName = "reminders")
public List<RemindersItem> getReminders() {
if(this.reminders == null) {
this.reminders = new Select()
.from(RemindersItem.class)
.where(Condition.column("task_id").eq(this.id))
.orderBy(true, "time")
.queryList();
}
return this.reminders;
}
public void setReminders(List<RemindersItem> reminders) {
for (RemindersItem remindersItem : reminders) {
remindersItem.setTask(this);
}
this.reminders = reminders;
}
public Integer getCompletedChecklistCount() {
Integer count = 0;
for (ChecklistItem item : this.getChecklist()) {
@ -387,11 +407,13 @@ public class Task extends BaseModel {
}
List<TaskTag> tmpTags = tags;
List<ChecklistItem> tmpChecklist = checklist;
List<RemindersItem> tmpReminders = reminders;
// remove them, so that the database don't add empty entries
tags = null;
checklist = null;
reminders = null;
if(repeat != null)
repeat.task_id = this.id;
@ -400,6 +422,7 @@ public class Task extends BaseModel {
tags = tmpTags;
checklist = tmpChecklist;
reminders = tmpReminders;
if (this.tags != null) {
for (TaskTag tag : this.tags) {
@ -407,6 +430,7 @@ public class Task extends BaseModel {
tag.async().save();
}
}
int position = 0;
if (this.checklist != null) {
for (ChecklistItem item : this.checklist) {
@ -418,6 +442,20 @@ public class Task extends BaseModel {
position++;
}
}
int index = 0;
if (this.reminders != null) {
for (RemindersItem item : this.reminders) {
if(item.getTask() == null) {
item.setTask(this);
}
if(item.getId() == null) {
item.setId(this.id + "task-reminder" + index);
}
item.async().save();
index++;
}
}
}
@Override
@ -537,4 +575,35 @@ public class Task extends BaseModel {
public Boolean isChecklistDisplayActive(int offset) {
return this.isDisplayedActive(offset) && (this.checklist.size() != this.getCompletedChecklistCount());
}
public Date getNextActiveDateAfter(Date oldTime) {
Calendar today = new GregorianCalendar();
today.set(Calendar.MILLISECOND, 0);
today.set(Calendar.SECOND, 0);
Calendar newTime = new GregorianCalendar();
newTime.setTime(oldTime);
if (this.getFrequency().equals(FREQUENCY_DAILY)) {
TimeUnit timeUnit = TimeUnit.DAYS;
long diffInMillies = newTime.getTimeInMillis() - today.getTimeInMillis();
long daySinceStart = timeUnit.convert(diffInMillies, TimeUnit.MILLISECONDS);
long daysUntilNextReminder = this.getEveryX() - (daySinceStart % this.getEveryX()) - 1;
if (newTime.before(today)) {
daysUntilNextReminder += this.getEveryX();
}
newTime.add(Calendar.DATE, (int) daysUntilNextReminder);
} else {
int nextActiveDayOfTheWeek = today.get(Calendar.DAY_OF_WEEK);
while (!this.getRepeat().getForDay(nextActiveDayOfTheWeek) || newTime.before(today)) {
if (nextActiveDayOfTheWeek == 6) nextActiveDayOfTheWeek = 0;
nextActiveDayOfTheWeek += 1;
newTime.add(Calendar.DATE, 1);
}
}
return newTime.getTime();
}
}

View file

@ -0,0 +1,25 @@
package com.magicmicky.habitrpgwrapper.lib.utils;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSerializationContext;
import com.google.gson.JsonSerializer;
import com.magicmicky.habitrpgwrapper.lib.models.tasks.RemindersItem;
import java.lang.reflect.Type;
/**
* Created by keithholliday on 6/4/16.
*/
public class RemindersItemSerializer
implements JsonSerializer<RemindersItem> {
@Override
public JsonElement serialize(RemindersItem src, Type typeOfSrc, JsonSerializationContext context) {
JsonObject object = new JsonObject();
object.addProperty("id", src.getId());
object.addProperty("startDate", src.getStartDate().toString());
object.addProperty("time", src.getTime().toString());
return object;
}
}

View file

@ -39,6 +39,7 @@ public class TaskSerializer implements JsonSerializer<Task> {
obj.add("startDate", context.serialize(task.getStartDate()));
obj.addProperty("streak", task.getStreak());
obj.add("checklist", context.serialize(task.getChecklist()));
obj.add("reminders", context.serialize(task.getReminders()));
obj.addProperty("completed", task.getCompleted());
break;
case "todo":
@ -48,6 +49,7 @@ public class TaskSerializer implements JsonSerializer<Task> {
obj.add("date", context.serialize(task.getDueDate()));
}
obj.add("checklist", context.serialize(task.getChecklist()));
obj.add("reminders", context.serialize(task.getReminders()));
obj.addProperty("completed", task.getCompleted());
break;
}

View file

@ -70,6 +70,7 @@ public class TaskAPITests extends BaseAPITests {
task.setType(type);
task.setTags(new ArrayList<>());
task.setChecklist(new ArrayList<>());
task.setReminders(new ArrayList<>());
return task;
}

View file

@ -4,3 +4,4 @@ distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-2.13-all.zip
org.gradle.jvmargs=-Xmx3072M