Skip to content
This repository was archived by the owner on Nov 4, 2023. It is now read-only.

Commit cfc1976

Browse files
committed
Implement 2nd level subtasks for subtasks
Copy createSecondLevelSubtaskTable, requestSubtasks, createSubtask, handleCreateSubtaskResponse, handleGetSubtasksResponse, handleGetSecondLevelSubtasksResponse from TaskActivity * Make subtask color black * Rename SubtaskRetrieval to TaskRetrieval
1 parent 778e40a commit cfc1976

3 files changed

Lines changed: 154 additions & 78 deletions

File tree

app/src/main/java/com/boruminc/borumjot/android/subtasks/SubtaskActivity.java

Lines changed: 149 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@
5454
import java.time.ZoneId;
5555
import java.util.ArrayList;
5656
import java.util.Locale;
57+
import java.util.Objects;
5758
import java.util.stream.Collectors;
5859
import java.util.stream.Stream;
5960

@@ -154,7 +155,7 @@ public JSONObject call() {
154155
});
155156
else if (getIntent().hasExtra("id")) {
156157
int subtaskId = getIntent().getIntExtra("id", 0);
157-
new SubtaskRetrieval(subtaskId).runAsync(getSharedPreferences("user identification", Context.MODE_PRIVATE).getString("apiKey", ""), new ApiResponseExecutor() {
158+
new TaskRetrieval(subtaskId).runAsync(getSharedPreferences("user identification", Context.MODE_PRIVATE).getString("apiKey", ""), new ApiResponseExecutor() {
158159
@Override
159160
public void onComplete(JSONObject result) {
160161
super.onComplete(result);
@@ -258,31 +259,8 @@ private void setSubtask(TextView view, boolean isCompleted) {
258259
*/
259260
private void loadSubtasks() {
260261
new TaskRunner().executeAsync(
261-
new ApiRequestExecutor() {
262-
@Override
263-
protected void initialize() {
264-
super.initialize();
265-
setRequestMethod("GET");
266-
addRequestHeader("Authorization", "Basic " + getUserApiKey());
267-
}
268-
269-
@Override
270-
public JSONObject call() {
271-
super.call();
272-
return this.connectToApi(encodeQueryString("subtasks", "id=" + getJottingData().getId()));
273-
}
274-
}, data -> {
275-
try {
276-
if (data != null && data.has("data") && data.getInt("statusCode") == 200) {
277-
ArrayList<Task> subtaskData = JSONToModel.convertJSONToTasks(data.getJSONArray("data"));
278-
setSubtasksTableContent(subtaskData);
279-
getTaskData().setSubtasks(subtaskData);
280-
}
281-
} catch (JSONException e) {
282-
e.printStackTrace();
283-
Toast.makeText(this, "The subtasks could not load", Toast.LENGTH_SHORT).show();
284-
}
285-
}
262+
requestSubtasks(getTaskData().getId()),
263+
handleGetSubtasksResponse()
286264
);
287265

288266
}
@@ -481,8 +459,12 @@ private void setSubtasksTableContent(ArrayList<Task> subtasks) {
481459

482460
subtaskList.setColumnShrinkable(2, true);
483461

484-
for (Task subtask : subtasks) {
485-
subtaskList.addView(addSubtaskToTable(subtask));
462+
for (int i = 0; i < subtasks.size(); i++) {
463+
Task subtask = subtasks.get(i);
464+
subtaskList.addView(createSubtaskRow(subtask), i * 2);
465+
subtaskList.addView(new TableRow(getApplicationContext()), i * 2 + 1);
466+
467+
new TaskRunner().executeAsync(requestSubtasks(subtask.getId()), handleGetSecondLevelSubtasksResponse(i * 2 + 1));
486468
}
487469

488470
TableRow addSubtaskLayout = new TableRow(this);
@@ -513,12 +495,13 @@ private void setDueDate(Date dueDate) {
513495
}
514496
}
515497

516-
private LinearLayout addSubtaskToTable(Task subtask) {
498+
private LinearLayout createSubtaskRow(Task subtask) {
517499
LinearLayout horizLayout = (LinearLayout) getLayoutInflater().inflate(R.layout.subtask, null);
518500
horizLayout.setTag(subtask.getId());
519501

520502
TextView title = horizLayout.findViewById(R.id.subtask_title);
521503
title.setText(SlashNormalizer.unescapeUserSlashes(subtask.getName()));
504+
522505
title.setOnFocusChangeListener(this::onSubtaskBoxFocus);
523506
// Display strikethrough if the subtask is marked as complete
524507
if (subtask.isCompleted())
@@ -530,6 +513,30 @@ private LinearLayout addSubtaskToTable(Task subtask) {
530513
return horizLayout;
531514
}
532515

516+
/**
517+
* @param secondLevelSubtasks The list of subtasks of a subtask
518+
* @return The table of subtasks for a subtask
519+
*/
520+
private LinearLayout createSecondLevelSubtaskTable(ArrayList<Task> secondLevelSubtasks) {
521+
LinearLayout secondLevelSubtaskList = new LinearLayout(getApplicationContext());
522+
secondLevelSubtaskList.setOrientation(LinearLayout.VERTICAL);
523+
524+
for (Task subtask : secondLevelSubtasks) {
525+
LinearLayout subtaskRow = createSubtaskRow(subtask);
526+
LinearLayout.LayoutParams subtaskRowParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
527+
subtaskRowParams.bottomMargin = 10;
528+
subtaskRowParams.topMargin = 10;
529+
subtaskRowParams.leftMargin = 50;
530+
531+
subtaskRow.setLayoutParams(subtaskRowParams);
532+
533+
secondLevelSubtaskList.addView(subtaskRow);
534+
}
535+
536+
return secondLevelSubtaskList;
537+
}
538+
539+
533540
/* Event Handlers */
534541

535542
public void onEnterSubtaskClick(View v) {
@@ -602,6 +609,116 @@ public void onComplete(JSONObject data) {
602609
);
603610
}
604611

612+
private ApiRequestExecutor createSubtask(int parentId, String subtaskName) {
613+
return new ApiRequestExecutor(String.valueOf(parentId), subtaskName) {
614+
@Override
615+
protected void initialize() {
616+
super.initialize();
617+
setRequestMethod("POST");
618+
addRequestHeader("Authorization", "Basic " + getUserApiKey());
619+
setQuery(encodePostQuery("id=%s&name=%s"));
620+
}
621+
622+
@Override
623+
public JSONObject call() {
624+
super.call();
625+
return this.connectToApi(this.encodeQueryString("subtasks"));
626+
}
627+
};
628+
}
629+
630+
private ApiRequestExecutor requestSubtasks(int id) {
631+
return new ApiRequestExecutor() {
632+
@Override
633+
protected void initialize() {
634+
super.initialize();
635+
setRequestMethod("GET");
636+
addRequestHeader("Authorization", "Basic " + getUserApiKey());
637+
}
638+
639+
@Override
640+
public JSONObject call() {
641+
super.call();
642+
return this.connectToApi(encodeQueryString("subtasks", "id=" + id));
643+
}
644+
};
645+
}
646+
647+
private ApiResponseExecutor handleCreateSubtaskResponse() {
648+
return new ApiResponseExecutor() {
649+
@Override
650+
public void onComplete(JSONObject result) {
651+
super.onComplete(result);
652+
try {
653+
if (ranOk()) {
654+
Task subtask = new Task(Objects.requireNonNull(newSubtaskField.getText()).toString());
655+
subtask.setId(result.getJSONObject("data").getInt("id"));
656+
657+
int indexAfterLastIncompleteTask = searchForLastIncompleteTask(subtask);
658+
subtaskList.addView(createSubtaskRow(subtask), indexAfterLastIncompleteTask);
659+
660+
((EditText) subtaskList.findViewById(R.id.newSubtaskFieldId)).setText("");
661+
}
662+
} catch (JSONException e) {
663+
e.printStackTrace();
664+
}
665+
}
666+
};
667+
}
668+
669+
private ApiResponseExecutor handleGetSubtasksResponse() {
670+
return new ApiResponseExecutor() {
671+
@Override
672+
public void onComplete(JSONObject data) {
673+
super.onComplete(data);
674+
try {
675+
if (data != null && data.has("data") && data.getInt("statusCode") == 200) {
676+
ArrayList<Task> subtaskData = JSONToModel.convertJSONToTasks(data.getJSONArray("data"));
677+
setSubtasksTableContent(subtaskData);
678+
getTaskData().setSubtasks(subtaskData);
679+
}
680+
} catch (JSONException e) {
681+
e.printStackTrace();
682+
Toast.makeText(getApplicationContext(), "The subtasks could not load", Toast.LENGTH_SHORT).show();
683+
}
684+
}
685+
};
686+
}
687+
688+
private ApiResponseExecutor handleGetSecondLevelSubtasksResponse(int loc) {
689+
return new ApiResponseExecutor() {
690+
@Override
691+
public void onComplete(JSONObject data) {
692+
super.onComplete(data);
693+
try {
694+
if (ranOk()) {
695+
ArrayList<Task> subtaskData = JSONToModel.convertJSONToTasks(data.getJSONArray("data"));
696+
TableRow subtaskListRow = new TableRow(getApplicationContext());
697+
subtaskListRow.addView(createSecondLevelSubtaskTable(subtaskData));
698+
subtaskList.removeViewAt(loc);
699+
subtaskList.addView(subtaskListRow, loc);
700+
701+
if (subtaskData.isEmpty()) return;
702+
703+
// Set subtask data in task for non-UI reference
704+
ArrayList<Task> topLevelSubtasks = getTaskData().getSubtasks();
705+
int numTopLevelSubtasks = topLevelSubtasks.size();
706+
for (int i = 0; i < numTopLevelSubtasks; i++) {
707+
Task subtask = topLevelSubtasks.get(i);
708+
if (subtask.getId() == subtaskData.get(0).getParentId()) {
709+
subtask.setSubtasks(subtaskData);
710+
getTaskData().setSubtask(i, subtask);
711+
}
712+
}
713+
}
714+
} catch (JSONException e) {
715+
e.printStackTrace();
716+
}
717+
}
718+
};
719+
}
720+
721+
605722
public void onAddSubtaskClick(View view) {
606723
// Exit early if the field unexpectedly doesn't exist
607724
if (newSubtaskField == null || newSubtaskField.getText() == null) return;
@@ -613,41 +730,11 @@ public void onAddSubtaskClick(View view) {
613730
}
614731

615732
new TaskRunner().executeAsync(
616-
new ApiRequestExecutor(String.valueOf(getTaskData().getId()), newSubtaskField.getText().toString()) {
617-
@Override
618-
protected void initialize() {
619-
super.initialize();
620-
setRequestMethod("POST");
621-
addRequestHeader("Authorization", "Basic " + getUserApiKey());
622-
setQuery(encodePostQuery("id=%s&name=%s"));
623-
}
624-
625-
@Override
626-
public JSONObject call() {
627-
super.call();
628-
return this.connectToApi(this.encodeQueryString("subtasks"));
629-
}
630-
}, new ApiResponseExecutor() {
631-
@Override
632-
public void onComplete(JSONObject result) {
633-
super.onComplete(result);
634-
try {
635-
if (ranOk()) {
636-
Task subtask = new Task(newSubtaskField.getText().toString());
637-
subtask.setId(result.getJSONObject("data").getInt("id"));
638-
639-
int indexAfterLastIncompleteTask = searchForLastIncompleteTask(subtask);
640-
subtaskList.addView(addSubtaskToTable(subtask), indexAfterLastIncompleteTask);
641-
642-
((EditText) subtaskList.findViewById(R.id.newSubtaskFieldId)).setText("");
643-
}
644-
} catch (JSONException e) {
645-
e.printStackTrace();
646-
}
647-
}
648-
}
733+
createSubtask(getTaskData().getId(), newSubtaskField.getText().toString()),
734+
handleCreateSubtaskResponse()
649735
);
650736
}
737+
651738
/**
652739
* Add right after last incomplete task using binary search for the last incomplete subtask
653740
* @implNote The algorithm is as follows:
@@ -692,7 +779,6 @@ private int searchForLastIncompleteTask(Task subtask) {
692779
return currSubtasks.size();
693780
}
694781

695-
696782
public void onCompleteSubtaskClick(View view) {
697783
TableRow subtaskRow = (TableRow) view.getParent();
698784
int completed = ((CheckBox) view).isChecked() ? 1 : 0;

app/src/main/java/com/boruminc/borumjot/android/subtasks/SubtaskRetrieval.java renamed to app/src/main/java/com/boruminc/borumjot/android/subtasks/TaskRetrieval.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,14 +6,14 @@
66

77
import org.json.JSONObject;
88

9-
public class SubtaskRetrieval {
9+
class TaskRetrieval {
1010
private int id;
1111

12-
SubtaskRetrieval(int id) {
12+
TaskRetrieval(int id) {
1313
this.id = id;
1414
}
1515

16-
private ApiRequestExecutor makeSubtaskGetRequest(String userApiKey) {
16+
private ApiRequestExecutor makeTaskGetRequest(String userApiKey) {
1717
return new ApiRequestExecutor() {
1818
@Override
1919
protected void initialize() {
@@ -31,6 +31,6 @@ public JSONObject call() {
3131
}
3232

3333
void runAsync(String userApiKey, Callback<JSONObject> callback) {
34-
new TaskRunner().executeAsync(makeSubtaskGetRequest(userApiKey), callback);
34+
new TaskRunner().executeAsync(makeTaskGetRequest(userApiKey), callback);
3535
}
3636
}

app/src/main/res/layout/subtask.xml

Lines changed: 1 addition & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -17,23 +17,13 @@
1717
android:layout_height="wrap_content"
1818
android:layout_weight="25"
1919
android:maxLines="4"
20+
android:textColor="@android:color/black"
2021
android:minLines="1"
2122
android:scrollHorizontally="false"
2223
android:textSize="20sp"
2324
android:layout_marginHorizontal="5dp"
2425
android:padding="10dp"
2526
android:onClick="onEnterSubtaskClick" />
2627

27-
<!-- <ImageButton-->
28-
<!-- android:id="@+id/delete_subtask_btn"-->
29-
<!-- android:layout_width="0dp"-->
30-
<!-- android:layout_height="wrap_content"-->
31-
<!-- android:layout_weight="2"-->
32-
<!-- android:layout_gravity="center_vertical"-->
33-
<!-- android:src="@drawable/ic_close_black_24dp"-->
34-
<!-- android:background="@android:color/transparent"-->
35-
<!-- android:contentDescription="@string/delete_subtask_content_desc"-->
36-
<!-- android:onClick="onDeleteSubtaskClick" />-->
37-
3828

3929
</TableRow>

0 commit comments

Comments
 (0)