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

Commit 2aaa620

Browse files
authored
Implement nested subtasks (#21)
* Implement bare subtask of subtask * Implement SubtaskRetrieval - makes request to get a task (used for subtasks) in the new com.boruminc.borumjot.android.subtasks package * Implement subtask_activity (copy of task_activity except uses subtask_options_menu and label container is removed) * Implement barebone of SubtaskActivity - instantiate SubtaskRetrieval and call runAsync * Change subtask names to TextViews so they are clickable to the SubtaskActivity * Implement onEnterSubtaskClick - starts SubtaskActivity and sends id of subtask as extra to make the activity function properly and make a successful GET task request * Implement subtask_options_menu - the task_options_menu with label and share removed * Finish subtask of subtask * Polish for v2.6.0, update build * Set id for tag of subtask row rather than title for completion to function properly v2.6.0
1 parent 3de7880 commit 2aaa620

10 files changed

Lines changed: 1015 additions & 73 deletions

File tree

app/build.gradle

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@ android {
88
applicationId "com.boruminc.borumjot.android"
99
minSdkVersion 16
1010
targetSdkVersion 29
11-
versionCode 54
12-
versionName "2.5.0"
11+
versionCode 55
12+
versionName "2.6.0"
1313

1414
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
1515
}

app/src/main/AndroidManifest.xml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,10 @@
4949
android:name=".TaskActivity"
5050
android:label="Task" />
5151

52+
<activity android:name=".subtasks.SubtaskActivity"
53+
android:label="Subtask"
54+
android:parentActivityName=".TaskActivity" />
55+
5256
<activity
5357
android:name=".NoteActivity"
5458
android:label="Note" />

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
import java.util.ArrayList;
3333

34-
abstract class JottingActivity extends AppCompatActivity {
34+
public abstract class JottingActivity extends AppCompatActivity {
3535
private String jottingType;
3636
private String userApiKey;
3737
private Jotting jottingData;
@@ -270,5 +270,5 @@ protected void slideLabelsListUp(FragmentTransaction ft) {
270270
* Updates the UI of the appbar to display the passed in text
271271
* @param name The new name of the jotting
272272
*/
273-
abstract void setJottingName(String name);
273+
protected abstract void setJottingName(String name);
274274
}

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

Lines changed: 55 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@
4343
import com.boruminc.borumjot.android.server.requests.DeleteJottingRequest;
4444
import com.boruminc.borumjot.android.server.requests.PinJottingRequest;
4545
import com.boruminc.borumjot.android.server.requests.UpdateTaskRequest;
46+
import com.boruminc.borumjot.android.subtasks.SubtaskActivity;
4647
import com.google.android.material.appbar.MaterialToolbar;
4748

4849
import org.json.JSONException;
@@ -53,7 +54,6 @@
5354
import java.time.LocalDate;
5455
import java.time.ZoneId;
5556
import java.util.ArrayList;
56-
import java.util.Comparator;
5757
import java.util.Locale;
5858
import java.util.stream.Collectors;
5959
import java.util.stream.Stream;
@@ -256,7 +256,7 @@ public void onBackPressed() {
256256

257257
/* Helper Methods */
258258

259-
private void setSubtask(EditText view, boolean isCompleted) {
259+
private void setSubtask(TextView view, boolean isCompleted) {
260260
if (isCompleted)
261261
// Add a strikethrough to the already existing paint flags using "|" bitwise operator
262262
view.setPaintFlags(view.getPaintFlags() | Paint.STRIKE_THRU_TEXT_FLAG);
@@ -441,7 +441,7 @@ private void scheduleTaskDueDateNotification(long delay) {
441441
* @param name The new name of the jotting
442442
*/
443443
protected void setJottingName(String name) {
444-
taskTitle.setText(name);
444+
taskTitle.setText(SlashNormalizer.unescapeUserSlashes(name));
445445
}
446446

447447
/**
@@ -497,6 +497,7 @@ private void setSubtasksTableContent(ArrayList<Task> subtasks) {
497497
}
498498

499499
TableRow addSubtaskLayout = new TableRow(this);
500+
500501
addSubtaskLayout.setLayoutParams(new TableRow.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
501502
addSubtaskLayout.setGravity(Gravity.START);
502503
ImageButton addSubtaskBtn = generateCherryRasboraPlusButton();
@@ -542,6 +543,13 @@ private LinearLayout addSubtaskToTable(Task subtask) {
542543

543544
/* Event Handlers */
544545

546+
public void onEnterSubtaskClick(View v) {
547+
Intent subtask = new Intent(getApplicationContext(), SubtaskActivity.class);
548+
549+
subtask.putExtra("id", (int) ((ViewGroup) v.getParent()).getTag());
550+
startActivity(subtask);
551+
}
552+
545553
public void onDeleteClick() {
546554
android.app.AlertDialog.Builder deleteDialog = new android.app.AlertDialog.Builder(this);
547555
deleteDialog
@@ -640,44 +648,8 @@ public void onComplete(JSONObject result) {
640648
Task subtask = new Task(newSubtaskField.getText().toString());
641649
subtask.setId(result.getJSONObject("data").getInt("id"));
642650

643-
/*
644-
* Add right after last incomplete task using binary search for the last incomplete subtask
645-
* The algorithm is as follows:
646-
* 1. Start at first element and check if complete
647-
* 2. If complete, then
648-
* 3. If the first element OR the previous element is incomplete, then
649-
* ADD SUBTASK; END
650-
* 4. Else, (previous element is complete), then
651-
* (the last incomplete subtask is BEFORE i) divide i by 2 and REPEAT
652-
* 7. Else, (current element is incomplete), then
653-
* (the last incomplete subtask is AFTER i) set i to be between its current value
654-
* and the index of the last element
655-
*/
656-
ArrayList<Task> currSubtasks = getTaskData().getSubtasks();
657-
int i = getTaskData().getSubtasks().size() / 2;
658-
659-
while (i < getTaskData().getSubtasks().size()) {
660-
661-
if (currSubtasks.get(i).isCompleted()) {
662-
if (i == 0 || !currSubtasks.get(i - 1).isCompleted()) {
663-
getTaskData().addSubtask(i, subtask);
664-
subtaskList.addView(addSubtaskToTable(subtask), i);
665-
666-
break;
667-
} else {
668-
i /= 2;
669-
}
670-
} else {
671-
if (i != currSubtasks.size() - 1 && currSubtasks.get(i + 1).isCompleted()) {
672-
getTaskData().addSubtask(i, subtask);
673-
subtaskList.addView(addSubtaskToTable(subtask), i + 1);
674-
675-
break;
676-
} else {
677-
i = (currSubtasks.size() + i) / 2;
678-
}
679-
}
680-
}
651+
int indexAfterLastIncompleteTask = searchForLastIncompleteTask(subtask);
652+
subtaskList.addView(addSubtaskToTable(subtask), indexAfterLastIncompleteTask);
681653

682654
((EditText) subtaskList.findViewById(R.id.newSubtaskFieldId)).setText("");
683655
}
@@ -689,20 +661,48 @@ public void onComplete(JSONObject result) {
689661
);
690662
}
691663

692-
public void onDeleteSubtaskClick(View view) {
693-
TableRow subtaskRow = (TableRow) view.getParent();
694-
695-
new TaskRunner().executeAsync(
696-
new DeleteJottingRequest((Integer) subtaskRow.getTag(), getUserApiKey(), "task"), data -> {
697-
if (data != null) {
698-
if (data.optInt("statusCode") == 200) {
699-
TableLayout subtaskTable = (TableLayout) subtaskRow.getParent();
700-
getTaskData().getSubtasks().remove(subtaskTable.indexOfChild(subtaskRow));
701-
subtaskTable.removeView(subtaskRow);
702-
}
703-
}
664+
/**
665+
* Add right after last incomplete task using binary search for the last incomplete subtask
666+
* @implNote The algorithm is as follows:
667+
* <ol>
668+
* <li>Start at first element and check if complete</li>
669+
* <li>If complete, then</li>
670+
* <li>If the first element OR the previous element is incomplete, then</li>
671+
* ADD SUBTASK; END
672+
* <li>Else, (previous element is complete), then
673+
* (the last incomplete subtask is BEFORE i) divide i by 2 and REPEAT
674+
* <li>Else, (current element is incomplete), then</li>
675+
* (the last incomplete subtask is AFTER i) set i to be between its current value
676+
* and the index of the last element</li>
677+
* </ol>
678+
* @return one index after the last incomplete subtask, or 0 if there were no subtasks
679+
*/
680+
private int searchForLastIncompleteTask(Task subtask) {
681+
ArrayList<Task> currSubtasks = getTaskData().getSubtasks();
682+
int subtaskSize = currSubtasks.size();
683+
int i = subtaskSize / 2;
684+
685+
while (i < subtaskSize) {
686+
if (currSubtasks.get(i).isCompleted()) {
687+
if (i == 0 || !currSubtasks.get(i - 1).isCompleted()) {
688+
getTaskData().addSubtask(i, subtask);
689+
690+
return i;
691+
} else {
692+
i /= 2;
704693
}
705-
);
694+
} else {
695+
if (i != currSubtasks.size() - 1 && currSubtasks.get(i + 1).isCompleted()) {
696+
getTaskData().addSubtask(i + 1, subtask);
697+
return i + 1;
698+
} else {
699+
i = (int) Math.round((currSubtasks.size() + i) / 2.0);
700+
}
701+
}
702+
}
703+
704+
// There are no (incomplete or complete) subtasks
705+
return currSubtasks.size();
706706
}
707707

708708
public void onCompleteSubtaskClick(View view) {
@@ -734,7 +734,7 @@ public JSONObject call() {
734734
public void onComplete(JSONObject result) {
735735
super.onComplete(result);
736736
if (ranOk()) {
737-
EditText subtaskTxt = (EditText) subtaskRow.getChildAt(1);
737+
TextView subtaskTxt = (TextView) subtaskRow.getChildAt(1);
738738
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
739739
if (completed == 1)
740740
setSubtask(subtaskTxt, true);

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ public static ArrayList<Note> convertJSONToNotes(JSONArray data) throws JSONExce
7171
return notes;
7272
}
7373

74-
private static Task convertJSONToTask(JSONObject row) throws JSONException {
74+
public static Task convertJSONToTask(JSONObject row) throws JSONException {
7575
// Set task information
7676
Task task = new Task(row.getString("title"), row.optString("body"), new ArrayList<Label>());
7777
task.setId(row.getInt("id"));

0 commit comments

Comments
 (0)