diff --git a/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/manager_app/PlanTaskListFragmentTest.java b/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/manager_app/PlanTaskListFragmentTest.java index 9c820623..b5ab735d 100644 --- a/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/manager_app/PlanTaskListFragmentTest.java +++ b/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/manager_app/PlanTaskListFragmentTest.java @@ -3,11 +3,15 @@ import android.app.FragmentManager; import android.app.FragmentTransaction; import android.os.Bundle; +import android.support.test.espresso.UiController; +import android.support.test.espresso.ViewAction; import android.support.test.espresso.contrib.RecyclerViewActions; import android.support.test.rule.ActivityTestRule; import android.support.test.runner.AndroidJUnit4; import android.support.v7.widget.RecyclerView; +import android.view.View; +import org.hamcrest.Matcher; import org.junit.Before; import org.junit.ClassRule; import org.junit.Rule; @@ -24,6 +28,7 @@ import static android.support.test.espresso.Espresso.onView; import static android.support.test.espresso.action.ViewActions.click; import static android.support.test.espresso.assertion.ViewAssertions.matches; +import static android.support.test.espresso.assertion.ViewAssertions.selectedDescendantsMatch; import static android.support.test.espresso.matcher.ViewMatchers.hasDescendant; import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed; import static android.support.test.espresso.matcher.ViewMatchers.withId; @@ -34,7 +39,8 @@ public class PlanTaskListFragmentTest { private static final String PLAN_ID = "PLAN_ID"; - private static final String TASK_NAME = "TASK_NAME"; + private static final String TASK_NAME_1 = "TASK_NAME_1"; + private static final String TASK_NAME_2 = "TASK_NAME_2"; private static final String PLAN_NAME = "PLAN NAME"; @ClassRule @@ -56,7 +62,7 @@ public class PlanTaskListFragmentTest { public void setUp() { PlanTaskListFragment fragment = new PlanTaskListFragment(); - taskTemplateRule.createTask(TASK_NAME); + taskTemplateRule.createTask(TASK_NAME_1); long planId = planTemplateRule.createPlan(PLAN_NAME); Bundle args = new Bundle(); @@ -97,6 +103,59 @@ public void whenAddingNewTaskToPlanExpectShowTaskOnList() { onView(withRecyclerView(R.id.rv_create_plan_task_list) .atPosition(0)) - .check(matches(hasDescendant(withText(TASK_NAME)))); + .check(matches(hasDescendant(withText(TASK_NAME_1)))); } + + @Test + public void whenStepIsRemovedExpectStepIsNotOnTheList() { + long planId = planTemplateRule.createPlanWithTasks(PLAN_NAME, TASK_NAME_1, TASK_NAME_2); + openPlanTaskListFragment(planId); + + onView(withId(R.id.rv_create_plan_task_list)).perform( + RecyclerViewActions + .actionOnItemAtPosition(0, clickChildViewWithId(R.id.id_remove_plan_task))); + + assertTaskDisplayed(0, TASK_NAME_2); + } + + private void openPlanTaskListFragment(long planId) { + PlanTaskListFragment fragment = new PlanTaskListFragment(); + Bundle args = new Bundle(); + args.putLong(PLAN_ID, planId); + fragment.setArguments(args); + + FragmentManager manager = activityRule.getActivity().getFragmentManager(); + FragmentTransaction transaction = manager.beginTransaction(); + transaction.replace(R.id.plan_container, fragment); + transaction.commit(); + } + + public static ViewAction clickChildViewWithId(final int id) { + return new ViewAction() { + @Override + public Matcher getConstraints() { + return null; + } + + @Override + public String getDescription() { + return null; + } + + @Override + public void perform(UiController uiController, View view) { + View v = view.findViewById(id); + v.performClick(); + } + }; + } + + private void assertTaskDisplayed(int position, String taskName) { + onView(withRecyclerView(R.id.rv_create_plan_task_list) + .atPosition(position)) + .check(selectedDescendantsMatch(withId(R.id.id_tv_task_name), + withText(taskName))); + } + + } diff --git a/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/resource/PlanTemplateRule.java b/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/resource/PlanTemplateRule.java index 86306164..0c1fa86f 100644 --- a/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/resource/PlanTemplateRule.java +++ b/Friendly-plans/app/src/androidTest/java/pg/autyzm/friendly_plans/resource/PlanTemplateRule.java @@ -38,7 +38,7 @@ public long createPlan(String planName) { return planId; } - public long createPlanWithTasksSteps(String planName, String taskName1, String taskName2) { + public long createPlanWithTasks(String planName, String taskName1, String taskName2) { Context context = activityRule.getActivity().getApplicationContext(); planTemplateRepository = new PlanTemplateRepository(daoSessionResource.getSession(context)); taskTemplateRepository = new TaskTemplateRepository(daoSessionResource.getSession(context)); @@ -50,8 +50,8 @@ public long createPlanWithTasksSteps(String planName, String taskName1, String t long taksId2 = taskTemplateRepository.create(taskName2, 2, null, null); taskIds.add(taksId1); taskIds.add(taksId2); - planTemplateRepository.setTasksWithThisPlan(planId, taksId1); - planTemplateRepository.setTasksWithThisPlan(planId, taksId2); + planTemplateRepository.setTaskWithPlan(planId, taksId1); + planTemplateRepository.setTaskWithPlan(planId, taksId2); return planId; } diff --git a/Friendly-plans/app/src/main/java/database/repository/PlanTemplateRepository.java b/Friendly-plans/app/src/main/java/database/repository/PlanTemplateRepository.java index 67f4e6b4..f7a12c89 100644 --- a/Friendly-plans/app/src/main/java/database/repository/PlanTemplateRepository.java +++ b/Friendly-plans/app/src/main/java/database/repository/PlanTemplateRepository.java @@ -30,16 +30,41 @@ public void update(Long planId, String name) { daoSession.getPlanTemplateDao().update(planTemplate); } + public void setTaskWithPlan(Long planId, Long taskId) { + List planTaskList = get(planId).getPlanTaskTemplates(); + int order = 0; + if(planTaskList != null){ + for(PlanTaskTemplate planTaskTemplate : planTaskList){ + if(planTaskTemplate.getOrder() > order) { + order = planTaskTemplate.getOrder(); + } + } + } + setTaskWithPlan(planId, taskId, order); + } - public void setTasksWithThisPlan(Long planId, Long taskId) { + public void setTaskWithPlan(Long planId, Long taskId, int order) { PlanTaskTemplate planTaskTemplate = new PlanTaskTemplate(); planTaskTemplate.setTaskTemplateId(taskId); planTaskTemplate.setPlanTemplateId(planId); + planTaskTemplate.setOrder(order); PlanTaskTemplateDao targetDao = daoSession.getPlanTaskTemplateDao(); targetDao.insert(planTaskTemplate); - get(planId).resetTasksWithThisPlan(); + get(planId).resetPlanTaskTemplates(); + } + + public void removeTaskFromPlan(Long planId, Long planTaskId) { + PlanTemplate planTemplate = daoSession.getPlanTemplateDao().load(planId); + List planTaskTemplatesList = planTemplate.getPlanTaskTemplates(); + PlanTaskTemplateDao targetDao = daoSession.getPlanTaskTemplateDao(); + for(PlanTaskTemplate planTaskTemplate : planTaskTemplatesList){ + if(planTaskTemplate.getId() == planTaskId){ + targetDao.delete(planTaskTemplate); + } + } + planTemplate.resetPlanTaskTemplates(); } public PlanTemplate get(Long id) { diff --git a/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_add_tasks/AddTasksToPlanFragment.java b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_add_tasks/AddTasksToPlanFragment.java index 047602db..de7239e3 100644 --- a/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_add_tasks/AddTasksToPlanFragment.java +++ b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_add_tasks/AddTasksToPlanFragment.java @@ -40,18 +40,16 @@ public class AddTasksToPlanFragment extends Fragment implements AddTasksToPlanEv new TaskRecyclerViewAdapter.TaskItemClickListener() { @Override public void onTaskItemClick(int position) { - planTemplateRepository.setTasksWithThisPlan(planId, taskListAdapter.getTaskItem(position).getId()); + planTemplateRepository.setTaskWithPlan(planId, taskListAdapter.getTaskItem(position).getId()); taskListAdapter.removeListItem(position); } @Override public void onRemoveTaskClick(int position){ - /*Item remove TODO*/ + /* Intentionally empty. Remove button is hidden */ } }; - - @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { diff --git a/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanCreateTaskListRecyclerViewAdapter.java b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanCreateTaskListRecyclerViewAdapter.java new file mode 100644 index 00000000..76c2d730 --- /dev/null +++ b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanCreateTaskListRecyclerViewAdapter.java @@ -0,0 +1,167 @@ +package pg.autyzm.friendly_plans.manager_app.view.plan_create_task_list; + +import android.graphics.Bitmap; +import android.graphics.BitmapFactory; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.TextView; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import database.entities.PlanTaskTemplate; +import database.entities.TaskTemplate; +import pg.autyzm.friendly_plans.R; +import pg.autyzm.friendly_plans.asset.AssetsHelper; +import pg.autyzm.friendly_plans.item_touch_helper.ItemTouchHelperAdapter; + +public class PlanCreateTaskListRecyclerViewAdapter extends + RecyclerView.Adapter implements ItemTouchHelperAdapter { + + private static final int ICON_PLACEHOLDER_PICTURE_ID = R.drawable.ic_placeholder; + private static final int ICON_PLACEHOLDER_SOUND_ID = R.drawable.ic_playing_sound; + private static final int ICON_PLACEHOLDER_TIME_ID = R.drawable.ic_placeholder_time; + private static List planTaskItemList = new ArrayList<>(); + private TaskItemClickListener taskItemClickListener; + private AssetsHelper assetsHelper; + + PlanCreateTaskListRecyclerViewAdapter(TaskItemClickListener taskItemClickListener) { + this.taskItemClickListener = taskItemClickListener; + } + + @Override + public boolean onItemMove(int fromPosition, int toPosition) { + if (fromPosition < toPosition) { + for (int i = fromPosition; i < toPosition; i++) { + Collections.swap(planTaskItemList, i, i + 1); + } + } else { + for (int i = fromPosition; i > toPosition; i--) { + Collections.swap(planTaskItemList, i, i - 1); + } + } + notifyItemMoved(fromPosition, toPosition); + return true; + } + + @Override + public void onItemDismiss(int position) { + taskItemClickListener.onRemoveTaskClick(planTaskItemList.get(position).getId()); + notifyItemRemoved(position); + } + + @Override + public void onItemReleased() { + taskItemClickListener.onMoveItem(); + } + + interface TaskItemClickListener { + + void onRemoveTaskClick(long itemId); + + void onMoveItem(); + } + + @Override + public TaskListViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { + assetsHelper = new AssetsHelper(parent.getContext()); + View view = LayoutInflater.from(parent.getContext()) + .inflate(R.layout.item_plan_task, parent, false); + return new TaskListViewHolder(view, taskItemClickListener); + } + + @Override + public void onBindViewHolder(TaskListViewHolder parent, int position) { + if (planTaskItemList != null && planTaskItemList.size() != 0) { + PlanTaskTemplate planTaskTemplate = planTaskItemList.get(position); + TaskTemplate taskTemplate = planTaskTemplate.getTaskTemplate(); + parent.taskName.setText(taskTemplate.getName()); + setPicture(parent, taskTemplate); + setSound(parent, taskTemplate); + setDurationTime(parent, taskTemplate); + } + } + + @Override + public int getItemCount() { + return planTaskItemList != null && planTaskItemList.size() != 0 ? planTaskItemList.size() : 0; + } + + public PlanTaskTemplate getPlanTaskTemplate(int id){ + return planTaskItemList.get(id); + } + + void setTaskItems(List planTaskItemList) { + this.planTaskItemList = planTaskItemList; + notifyDataSetChanged(); + } + + private void setDurationTime(TaskListViewHolder holder, TaskTemplate taskItem) { + if (taskItem.getDurationTime() != null && taskItem.getDurationTime() != 0) { + holder.taskDurationIcon.setImageResource(ICON_PLACEHOLDER_TIME_ID); + holder.taskDurationTime.setText(String.valueOf(taskItem.getDurationTime())); + holder.taskDurationIcon.setVisibility(View.VISIBLE); + } else { + holder.taskDurationTime.setText(""); + holder.taskDurationIcon.setVisibility(View.INVISIBLE); + } + } + + private void setSound(TaskListViewHolder holder, TaskTemplate taskItem) { + if (taskItem.getSound() != null && !taskItem.getSound().getFilename().isEmpty()) { + holder.taskSoundIcon.setImageResource(ICON_PLACEHOLDER_SOUND_ID); + holder.taskSoundIcon.setVisibility(View.VISIBLE); + } else { + holder.taskSoundIcon.setVisibility(View.GONE); + } + } + + private void setPicture(TaskListViewHolder holder, TaskTemplate taskItem) { + if (taskItem.getPicture() != null && !taskItem.getPicture().getFilename().isEmpty()) { + String picturePath = assetsHelper.getFileFullPath(taskItem.getPicture()); + Bitmap bitmap = BitmapFactory.decodeFile(picturePath); + holder.taskPicture.setImageBitmap(bitmap); + } else { + holder.taskPicture.setImageResource(ICON_PLACEHOLDER_PICTURE_ID); + } + } + + static class TaskListViewHolder extends RecyclerView.ViewHolder { + + TextView taskName; + ImageButton removeButton; + ImageView taskPicture; + ImageView taskSoundIcon; + ImageView taskDurationIcon; + TextView taskDurationTime; + + TaskListViewHolder(View itemView, final TaskItemClickListener taskItemClickListener) { + super(itemView); + this.taskName = (TextView) itemView + .findViewById(R.id.id_tv_task_name); + this.taskPicture = (ImageView) itemView + .findViewById(R.id.id_iv_task_picture); + this.taskSoundIcon = (ImageView) itemView + .findViewById(R.id.id_iv_task_sound_icon); + this.taskDurationIcon = (ImageView) itemView + .findViewById(R.id.id_iv_task_duration_icon); + this.taskDurationTime = (TextView) itemView + .findViewById(R.id.id_tv_task_duration_time); + this.removeButton = (ImageButton) itemView + .findViewById(R.id.id_remove_plan_task); + this.removeButton.setOnClickListener(new View.OnClickListener() { + + @Override + public void onClick(View v) { + long id = planTaskItemList.get(getAdapterPosition()).getId(); + taskItemClickListener.onRemoveTaskClick(id); + } + }); + } + } +} diff --git a/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListEvents.java b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListEvents.java index f135c1e3..ab4e4ce0 100644 --- a/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListEvents.java +++ b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListEvents.java @@ -6,5 +6,5 @@ public interface PlanTaskListEvents { void eventAddTasksToPlan(View view); - + void eventSaveAndFinish(View view); } diff --git a/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListFragment.java b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListFragment.java index e168b576..85cce0e8 100644 --- a/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListFragment.java +++ b/Friendly-plans/app/src/main/java/pg/autyzm/friendly_plans/manager_app/view/plan_create_task_list/PlanTaskListFragment.java @@ -2,43 +2,74 @@ import android.app.Fragment; import android.app.FragmentTransaction; +import android.content.Intent; import android.databinding.DataBindingUtil; import android.os.Bundle; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.helper.ItemTouchHelper; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import javax.inject.Inject; +import database.entities.PlanTaskTemplate; import database.entities.PlanTemplate; import database.repository.PlanTemplateRepository; import pg.autyzm.friendly_plans.ActivityProperties; import pg.autyzm.friendly_plans.App; import pg.autyzm.friendly_plans.R; import pg.autyzm.friendly_plans.databinding.FragmentPlanTaskListBinding; +import pg.autyzm.friendly_plans.item_touch_helper.SimpleItemTouchHelperCallback; +import pg.autyzm.friendly_plans.manager_app.view.main_screen.MainActivity; import pg.autyzm.friendly_plans.manager_app.view.plan_create_add_tasks.AddTasksToPlanFragment; -import pg.autyzm.friendly_plans.manager_app.view.task_list.TaskRecyclerViewAdapter; +import pg.autyzm.friendly_plans.notifications.ToastUserNotifier; public class PlanTaskListFragment extends Fragment implements PlanTaskListEvents { - private TaskRecyclerViewAdapter taskListAdapter; + private PlanCreateTaskListRecyclerViewAdapter taskListAdapter; private Long planId; @Inject PlanTemplateRepository planTemplateRepository; + @Inject + ToastUserNotifier toastUserNotifier; + + PlanCreateTaskListRecyclerViewAdapter.TaskItemClickListener taskItemClickListener = + new PlanCreateTaskListRecyclerViewAdapter.TaskItemClickListener() { + + private boolean removedTask = false; - TaskRecyclerViewAdapter.TaskItemClickListener taskItemClickListener = - new TaskRecyclerViewAdapter.TaskItemClickListener() { @Override - public void onTaskItemClick(int position) { - /* What to do after click? Remove task? Edit? */ + public void onRemoveTaskClick(long itemId) { + planTemplateRepository.removeTaskFromPlan(planId, itemId); + PlanTemplate planTemplate = planTemplateRepository.get(planId); + taskListAdapter + .setTaskItems(planTemplate.getPlanTaskTemplates()); + toastUserNotifier.displayNotifications( + R.string.step_removed_message, + getActivity().getApplicationContext()); + removedTask = true; } @Override - public void onRemoveTaskClick(int position){ - /*Item remove TODO*/ + public void onMoveItem() { + Boolean reordered = false; + for(int i = 0; i < taskListAdapter.getItemCount(); i++){ + PlanTaskTemplate planTaskItem = taskListAdapter.getPlanTaskTemplate(i); + if(i != planTaskItem.getOrder()) { + planTemplateRepository.removeTaskFromPlan(planId, planTaskItem.getId()); + planTemplateRepository.setTaskWithPlan(planId, planTaskItem.getTaskTemplateId(), i); + reordered = true; + } + if(!removedTask && reordered){ + toastUserNotifier.displayNotifications( + R.string.steps_reordered_message, + getActivity().getApplicationContext()); + } + taskListAdapter.notifyDataSetChanged(); + } } }; @@ -70,6 +101,19 @@ public void eventAddTasksToPlan(View view) { transaction.commit(); } + @Override + public void eventSaveAndFinish(View view) { + toastUserNotifier.displayNotifications( + R.string.plan_saved_message, + getActivity().getApplicationContext()); + showMainMenu(); + } + + private void showMainMenu() { + Intent intent = new Intent(getActivity(), MainActivity.class); + startActivity(intent); + } + @Override public void onViewCreated(View view, Bundle savedInstanceState) { Bundle arguments = getArguments(); @@ -81,16 +125,21 @@ public void onViewCreated(View view, Bundle savedInstanceState) { recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(new LinearLayoutManager(getActivity())); - taskListAdapter = new TaskRecyclerViewAdapter(taskItemClickListener); + taskListAdapter = new PlanCreateTaskListRecyclerViewAdapter(taskItemClickListener); recyclerView.setAdapter(taskListAdapter); PlanTemplate planTemplate = planTemplateRepository.get(planId); - taskListAdapter.setTaskItems(planTemplate.getTasksWithThisPlan()); + taskListAdapter.setTaskItems(planTemplate.getPlanTaskTemplates()); + + + ItemTouchHelper.Callback callback = new SimpleItemTouchHelperCallback(taskListAdapter); + ItemTouchHelper mItemTouchHelper = new ItemTouchHelper(callback); + mItemTouchHelper.attachToRecyclerView(recyclerView); } public void onResume() { PlanTemplate planTemplate = planTemplateRepository.get(planId); - taskListAdapter.setTaskItems(planTemplate.getTasksWithThisPlan()); + taskListAdapter.setTaskItems(planTemplate.getPlanTaskTemplates()); taskListAdapter.notifyDataSetChanged(); super.onResume(); } diff --git a/Friendly-plans/app/src/main/res/layout/fragment_plan_task_list.xml b/Friendly-plans/app/src/main/res/layout/fragment_plan_task_list.xml index 86b1dc42..b1f6abdf 100644 --- a/Friendly-plans/app/src/main/res/layout/fragment_plan_task_list.xml +++ b/Friendly-plans/app/src/main/res/layout/fragment_plan_task_list.xml @@ -33,7 +33,9 @@ android:id="@+id/id_btn_save_plan_tasks" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:text="@string/save"/> + android:onClick="@{planTaskListEvents::eventSaveAndFinish}" + android:text="@string/save_and_finish"/> + @@ -46,7 +48,7 @@ android:layout_alignParentEnd="true" android:layout_below="@+id/id_nav_bar" android:scrollbars="vertical" - tools:listitem="@layout/item_task"> + tools:listitem="@layout/item_plan_task"> diff --git a/Friendly-plans/app/src/main/res/layout/item_plan_task.xml b/Friendly-plans/app/src/main/res/layout/item_plan_task.xml new file mode 100644 index 00000000..d6d53fe1 --- /dev/null +++ b/Friendly-plans/app/src/main/res/layout/item_plan_task.xml @@ -0,0 +1,78 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file