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

Commit 778e40a

Browse files
committed
Implement 2nd-level subtasks for tasks
* Implement TaskActivity#createSecondLevelSubtaskTable - creates a vertical linear layout of horizontal linear layout (invoking createSubtaskRow) with margins to indicate they are second level subtasks * Insert second level subtasks as a TableRow after each first level subtask * Extract GET subtasks into requestSubtasks() for use twice * Implement handleGetSecondLevelSubtasksResponse() - Replaces placeholder subtask row with second-level subtask table * Set parent id in JSONToModel.convertJSONToTask()
1 parent 2aaa620 commit 778e40a

3 files changed

Lines changed: 108 additions & 32 deletions

File tree

app/src/main/java/com/boruminc/borumjot/Task.java

Lines changed: 17 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,17 @@
77

88
public class Task extends Jotting implements Comparable<Jotting> {
99
private int userId;
10-
private int status;
1110
private Date timeCreated;
1211
private boolean completed;
1312
private ArrayList<Task> subtasks;
1413
private Date dueDate;
14+
private int parentId;
1515

1616
public Task() {
1717
super();
1818
subtasks = new ArrayList<Task>();
1919
completed = false;
20-
status = 0;
20+
parentId = 0;
2121
}
2222

2323
public Task(String n) {
@@ -28,10 +28,16 @@ public Task(String n, String b, ArrayList<Label> labels) {
2828
super(n, b, labels);
2929
}
3030

31+
public Task(String n, String b, ArrayList<Label> labels, int p) {
32+
super(n, b, labels);
33+
parentId = p;
34+
}
35+
3136
@NonNull
3237
public String toString() {
3338
String str = super.toString();
3439
str += "Completed: " + completed;
40+
str += "Parent Id: " + parentId;
3541

3642
return str;
3743
}
@@ -49,19 +55,11 @@ public void setUserId(int userId) {
4955
this.userId = userId;
5056
}
5157

52-
public int getStatus() {
53-
return status;
54-
}
55-
56-
public void setStatus(int status) {
57-
this.status = status;
58-
}
59-
6058
public Date getTimeCreated() {
6159
return timeCreated;
6260
}
6361

64-
public void setTimeCreated(Date timeCreated) {
62+
void setTimeCreated(Date timeCreated) {
6563
this.timeCreated = timeCreated;
6664
}
6765

@@ -89,11 +87,19 @@ public void addSubtask(int loc, Task newSubtask) {
8987
subtasks.add(loc, newSubtask);
9088
}
9189

90+
public void setSubtask(int loc, Task subtask) {
91+
subtasks.set(loc, subtask);
92+
}
93+
9294
public Date getDueDate() {
9395
return dueDate;
9496
}
9597

9698
public void setDueDate(Date newDate) {
9799
dueDate = newDate;
98100
}
101+
102+
public int getParentId() { return parentId; }
103+
104+
public void setParentId(int newParentId) { parentId = newParentId; }
99105
}

app/src/main/java/com/boruminc/borumjot/android/TaskActivity.java

Lines changed: 90 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.boruminc.borumjot.android;
22

3+
import android.annotation.SuppressLint;
34
import android.app.AlarmManager;
45
import android.app.DatePickerDialog;
56
import android.app.Dialog;
@@ -193,7 +194,7 @@ else if (getIntent().hasExtra("data")) {
193194

194195
getLabelsList().setArguments(b);
195196

196-
}
197+
}
197198

198199
@RequiresApi(api = Build.VERSION_CODES.N)
199200
private void onToggleCompletedTasks(MenuItem item) {
@@ -264,29 +265,36 @@ private void setSubtask(TextView view, boolean isCompleted) {
264265
view.setPaintFlags(view.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
265266
}
266267

268+
private ApiRequestExecutor requestSubtasks(int id) {
269+
return new ApiRequestExecutor() {
270+
@Override
271+
protected void initialize() {
272+
super.initialize();
273+
setRequestMethod("GET");
274+
addRequestHeader("Authorization", "Basic " + getUserApiKey());
275+
}
276+
277+
@Override
278+
public JSONObject call() {
279+
super.call();
280+
return this.connectToApi(encodeQueryString("subtasks", "id=" + id));
281+
}
282+
};
283+
}
284+
285+
;
286+
267287
/**
268288
* Loads the subtasks.
269289
*/
270290
private void loadSubtasks() {
271291
new TaskRunner().executeAsync(
272-
new ApiRequestExecutor() {
273-
@Override
274-
protected void initialize() {
275-
super.initialize();
276-
setRequestMethod("GET");
277-
addRequestHeader("Authorization", "Basic " + getUserApiKey());
278-
}
279-
280-
@Override
281-
public JSONObject call() {
282-
super.call();
283-
return this.connectToApi(encodeQueryString("subtasks", "id=" + getJottingData().getId()));
284-
}
285-
}, data -> {
292+
requestSubtasks(getJottingData().getId()), data -> {
286293
try {
287294
if (data != null && data.has("data") && data.getInt("statusCode") == 200) {
288295
ArrayList<Task> subtaskData = JSONToModel.convertJSONToTasks(data.getJSONArray("data"));
289296
setSubtasksTableContent(subtaskData);
297+
290298
getTaskData().setSubtasks(subtaskData);
291299
}
292300
} catch (JSONException e) {
@@ -326,7 +334,6 @@ public void onComplete(JSONObject result) {
326334
super.onComplete(result);
327335
try {
328336
if (ranOk()) {
329-
Log.d("Due Date after update", String.valueOf(chosenDueDate.getTime()));
330337
setDueDate(chosenDueDate);
331338
getTaskData().setDueDate(chosenDueDate);
332339
} else if (result.has("error") && result.getJSONObject("error").has("message")) {
@@ -484,6 +491,29 @@ private void displayStrikethrough(boolean on) {
484491
taskTitle.setPaintFlags(taskTitle.getPaintFlags() & (~Paint.STRIKE_THRU_TEXT_FLAG));
485492
}
486493

494+
/**
495+
* @param secondLevelSubtasks The list of subtasks of a subtask
496+
* @return The table of subtasks for a subtask
497+
*/
498+
private LinearLayout createSecondLevelSubtaskTable(ArrayList<Task> secondLevelSubtasks) {
499+
LinearLayout secondLevelSubtaskList = new LinearLayout(getApplicationContext());
500+
secondLevelSubtaskList.setOrientation(LinearLayout.VERTICAL);
501+
502+
for (Task subtask : secondLevelSubtasks) {
503+
LinearLayout subtaskRow = createSubtaskRow(subtask);
504+
LinearLayout.LayoutParams subtaskRowParams = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
505+
subtaskRowParams.bottomMargin = 10;
506+
subtaskRowParams.topMargin = 10;
507+
subtaskRowParams.leftMargin = 50;
508+
509+
subtaskRow.setLayoutParams(subtaskRowParams);
510+
511+
secondLevelSubtaskList.addView(subtaskRow);
512+
}
513+
514+
return secondLevelSubtaskList;
515+
}
516+
487517
private void setSubtasksTableContent(ArrayList<Task> subtasks) {
488518
subtaskList.removeAllViews();
489519

@@ -492,8 +522,12 @@ private void setSubtasksTableContent(ArrayList<Task> subtasks) {
492522

493523
subtaskList.setColumnShrinkable(2, true);
494524

495-
for (Task subtask : subtasks) {
496-
subtaskList.addView(addSubtaskToTable(subtask));
525+
for (int i = 0; i < subtasks.size(); i++) {
526+
Task subtask = subtasks.get(i);
527+
subtaskList.addView(createSubtaskRow(subtask), i * 2);
528+
subtaskList.addView(new TableRow(getApplicationContext()), i * 2 + 1);
529+
530+
new TaskRunner().executeAsync(requestSubtasks(subtask.getId()), handleGetSecondLevelSubtasksResponse(i * 2 + 1));
497531
}
498532

499533
TableRow addSubtaskLayout = new TableRow(this);
@@ -524,12 +558,13 @@ private void setDueDate(Date dueDate) {
524558
}
525559
}
526560

527-
private LinearLayout addSubtaskToTable(Task subtask) {
561+
private LinearLayout createSubtaskRow(Task subtask) {
528562
LinearLayout horizLayout = (LinearLayout) getLayoutInflater().inflate(R.layout.subtask, null);
529563
horizLayout.setTag(subtask.getId());
530564

531565
TextView title = horizLayout.findViewById(R.id.subtask_title);
532566
title.setText(SlashNormalizer.unescapeUserSlashes(subtask.getName()));
567+
533568
title.setOnFocusChangeListener(this::onSubtaskBoxFocus);
534569
// Display strikethrough if the subtask is marked as complete
535570
if (subtask.isCompleted())
@@ -550,6 +585,39 @@ public void onEnterSubtaskClick(View v) {
550585
startActivity(subtask);
551586
}
552587

588+
private ApiResponseExecutor handleGetSecondLevelSubtasksResponse(int loc) {
589+
return new ApiResponseExecutor() {
590+
@Override
591+
public void onComplete(JSONObject data) {
592+
super.onComplete(data);
593+
try {
594+
if (ranOk()) {
595+
ArrayList<Task> subtaskData = JSONToModel.convertJSONToTasks(data.getJSONArray("data"));
596+
TableRow subtaskListRow = new TableRow(getApplicationContext());
597+
subtaskListRow.addView(createSecondLevelSubtaskTable(subtaskData));
598+
subtaskList.removeViewAt(loc);
599+
subtaskList.addView(subtaskListRow, loc);
600+
601+
if (subtaskData.isEmpty()) return;
602+
603+
// Set subtask data in task for non-UI reference
604+
ArrayList<Task> topLevelSubtasks = getTaskData().getSubtasks();
605+
int numTopLevelSubtasks = topLevelSubtasks.size();
606+
for (int i = 0; i < numTopLevelSubtasks; i++) {
607+
Task subtask = topLevelSubtasks.get(i);
608+
if (subtask.getId() == subtaskData.get(0).getParentId()) {
609+
subtask.setSubtasks(subtaskData);
610+
getTaskData().setSubtask(i, subtask);
611+
}
612+
}
613+
}
614+
} catch (JSONException e) {
615+
e.printStackTrace();
616+
}
617+
}
618+
};
619+
}
620+
553621
public void onDeleteClick() {
554622
android.app.AlertDialog.Builder deleteDialog = new android.app.AlertDialog.Builder(this);
555623
deleteDialog
@@ -649,7 +717,7 @@ public void onComplete(JSONObject result) {
649717
subtask.setId(result.getJSONObject("data").getInt("id"));
650718

651719
int indexAfterLastIncompleteTask = searchForLastIncompleteTask(subtask);
652-
subtaskList.addView(addSubtaskToTable(subtask), indexAfterLastIncompleteTask);
720+
subtaskList.addView(createSubtaskRow(subtask), indexAfterLastIncompleteTask * 2);
653721

654722
((EditText) subtaskList.findViewById(R.id.newSubtaskFieldId)).setText("");
655723
}
@@ -663,6 +731,8 @@ public void onComplete(JSONObject result) {
663731

664732
/**
665733
* Add right after last incomplete task using binary search for the last incomplete subtask
734+
*
735+
* @return one index after the last incomplete subtask, or 0 if there were no subtasks
666736
* @implNote The algorithm is as follows:
667737
* <ol>
668738
* <li>Start at first element and check if complete</li>
@@ -675,7 +745,6 @@ public void onComplete(JSONObject result) {
675745
* (the last incomplete subtask is AFTER i) set i to be between its current value
676746
* and the index of the last element</li>
677747
* </ol>
678-
* @return one index after the last incomplete subtask, or 0 if there were no subtasks
679748
*/
680749
private int searchForLastIncompleteTask(Task subtask) {
681750
ArrayList<Task> currSubtasks = getTaskData().getSubtasks();

app/src/main/java/com/boruminc/borumjot/android/server/JSONToModel.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ public static Task convertJSONToTask(JSONObject row) throws JSONException {
7777
task.setId(row.getInt("id"));
7878
task.setCompleted(row.optString("completed").equals("1"));
7979
task.setPriority(row.optInt("priority"));
80+
task.setParentId(row.optInt("parentId"));
8081

8182
if (!row.isNull("due_date")) {
8283
Date dueDate = new Date(row.getLong("due_date") * 1000);

0 commit comments

Comments
 (0)