11package com .boruminc .borumjot .android ;
22
3+ import android .annotation .SuppressLint ;
34import android .app .AlarmManager ;
45import android .app .DatePickerDialog ;
56import 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 ();
0 commit comments