diff --git a/collect_app/src/main/AndroidManifest.xml b/collect_app/src/main/AndroidManifest.xml index ae95c4ed85b..2820c39836a 100644 --- a/collect_app/src/main/AndroidManifest.xml +++ b/collect_app/src/main/AndroidManifest.xml @@ -195,8 +195,8 @@ the specific language governing permissions and limitations under the License. - - + + children = element.getChildren(); - for (int i = 0; i < children.size(); i++) { - formList.remove(position + 1); - } - element.setIcon(ContextCompat.getDrawable(this, R.drawable.expander_ic_minimized)); - break; - case COLLAPSED: - element.setType(EXPANDED); - ArrayList children1 = element.getChildren(); - for (int i = 0; i < children1.size(); i++) { - Timber.i("adding child: %s", children1.get(i).getFormIndex()); - formList.add(position + 1 + i, children1.get(i)); - - } - element.setIcon(ContextCompat.getDrawable(this, R.drawable.expander_ic_maximized)); - break; - case QUESTION: - Collect.getInstance().getFormController().jumpToIndex(index); - if (Collect.getInstance().getFormController().indexIsInFieldList()) { - try { - Collect.getInstance().getFormController().stepToPreviousScreenEvent(); - } catch (JavaRosaException e) { - Timber.d(e); - createErrorDialog(e.getCause().getMessage()); - return; - } - } - setResult(RESULT_OK); - finish(); - return; - case CHILD: - Collect.getInstance().getFormController().jumpToIndex(element.getFormIndex()); - setResult(RESULT_OK); - refreshView(); - return; - } - - recyclerView.setAdapter(new HierarchyListAdapter(formList, this::onElementClick)); - ((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(position, 0); - } - - @Override - public boolean onKeyDown(int keyCode, KeyEvent event) { - switch (keyCode) { - case KeyEvent.KEYCODE_BACK: - FormController fc = Collect.getInstance().getFormController(); - if (fc != null) { - fc.getTimerLogger().exitView(); - fc.jumpToIndex(startIndex); - } - } - return super.onKeyDown(keyCode, event); - } -} diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java index 6473d719e24..9def827e828 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormEntryActivity.java @@ -987,7 +987,7 @@ public boolean onOptionsItemSelected(MenuItem item) { 0, null, false, true); } - Intent i = new Intent(this, EditFormHierarchyActivity.class); + Intent i = new Intent(this, FormHierarchyActivity.class); startActivityForResult(i, RequestCodes.HIERARCHY_ACTIVITY); return true; case R.id.menu_preferences: @@ -2377,11 +2377,11 @@ public void denied() { if (formMode == null || ApplicationConstants.FormModes.EDIT_SAVED.equalsIgnoreCase(formMode)) { formController.getTimerLogger().logTimerEvent(TimerLogger.EventTypes.FORM_RESUME, 0, null, false, true); formController.getTimerLogger().logTimerEvent(TimerLogger.EventTypes.HIERARCHY, 0, null, false, true); - startActivity(new Intent(this, EditFormHierarchyActivity.class)); + startActivity(new Intent(this, FormHierarchyActivity.class)); return; // so we don't show the intro screen before jumping to the hierarchy } else { if (ApplicationConstants.FormModes.VIEW_SENT.equalsIgnoreCase(formMode)) { - startActivity(new Intent(this, ViewFormHierarchyActivity.class)); + startActivity(new Intent(this, ViewOnlyFormHierarchyActivity.class)); } finish(); } diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/FormHierarchyActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/FormHierarchyActivity.java index a3481bf4afb..2cb95a3f750 100644 --- a/collect_app/src/main/java/org/odk/collect/android/activities/FormHierarchyActivity.java +++ b/collect_app/src/main/java/org/odk/collect/android/activities/FormHierarchyActivity.java @@ -23,7 +23,6 @@ import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.view.View; -import android.view.View.OnClickListener; import android.widget.Button; import android.widget.TextView; @@ -35,6 +34,7 @@ import org.odk.collect.android.R; import org.odk.collect.android.adapters.HierarchyListAdapter; import org.odk.collect.android.application.Collect; +import org.odk.collect.android.exception.JavaRosaException; import org.odk.collect.android.logic.FormController; import org.odk.collect.android.logic.HierarchyElement; import org.odk.collect.android.utilities.FormEntryPromptUtils; @@ -45,18 +45,54 @@ import timber.log.Timber; -public abstract class FormHierarchyActivity extends CollectAbstractActivity { +/** + * Displays the structure of a form along with the answers for the current instance. Different form + * elements are displayed in the following ways: + * - Questions each take up a row with their full label shown and their answers below + * - Non-repeat groups are not represented at all + * - Repeat groups are initially shown as collapsed and are expanded when tapped, revealing instances + * of that repeat + * - Repeat instances are displayed with their label and a count after (e.g. My group (1)) + * + * Tapping on a repeat instance shows all the questions in that repeat instance using the display + * rules above. + * + * Tapping on a question sets the app-wide current question to that question and terminates the + * activity, returning to {@link FormEntryActivity}. + * + * Although the user gets the impression of navigating "into" a repeat, the view is refreshed in + * {@link #refreshView()} rather than another activity/fragment being added to the backstack. + * + * Buttons at the bottom of the screen allow users to navigate the form. + */ +public class FormHierarchyActivity extends CollectAbstractActivity { + /** + * The questions and repeats at the current level. If a repeat is expanded, also includes the + * instances of that repeat. Recreated every time {@link #refreshView()} is called. Modified + * by the expand/collapse behavior in {@link #onElementClick(HierarchyElement)}. + */ + private List elementsToDisplay; - protected static final int CHILD = 1; - protected static final int EXPANDED = 2; - protected static final int COLLAPSED = 3; - protected static final int QUESTION = 4; + /** + * The label shown at the top of a hierarchy screen for a repeat instance. Set by + * {@link #getCurrentPath()}. + */ + private TextView groupPathTextView; - List formList; - TextView path; + /** + * The index of the question or the field list the FormController was set to when the hierarchy + * was accessed. Used to jump the user back to where they were if applicable. + */ + private FormIndex startIndex; - FormIndex startIndex; + /** + * The index of the question that is being displayed in the hierarchy. On first launch, it is + * the same as {@link #startIndex}. It can then become the index of a repeat instance. + * TODO: Is keeping this as a field necessary? I believe what it is used for is to send the user + * to edit a question that caused an error in the hierarchy. + */ private FormIndex currentIndex; + protected Button jumpPreviousButton; protected Button jumpBeginningButton; protected Button jumpEndButton; @@ -84,59 +120,32 @@ public void onCreate(Bundle savedInstanceState) { return; } - // We use a static FormEntryController to make jumping faster. startIndex = formController.getFormIndex(); setTitle(formController.getFormTitle()); - path = findViewById(R.id.pathtext); + groupPathTextView = findViewById(R.id.pathtext); jumpPreviousButton = findViewById(R.id.jumpPreviousButton); - jumpPreviousButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - goUpLevel(); - } - }); - jumpBeginningButton = findViewById(R.id.jumpBeginningButton); - jumpBeginningButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - FormController fc = Collect.getInstance().getFormController(); - if (fc != null) { - fc.getTimerLogger().exitView(); - fc.jumpToIndex(FormIndex.createBeginningOfFormIndex()); - } - setResult(RESULT_OK); - finish(); - } - }); - jumpEndButton = findViewById(R.id.jumpEndButton); - jumpEndButton.setOnClickListener(new OnClickListener() { - @Override - public void onClick(View v) { - FormController fc = Collect.getInstance().getFormController(); - if (fc != null) { - fc.getTimerLogger().exitView(); - fc.jumpToIndex(FormIndex.createEndOfFormIndex()); - } - setResult(RESULT_OK); - finish(); - } - }); + configureButtons(formController); refreshView(); - // Kinda slow, but works. This scrolls to the last question the user was looking at. + // Scroll to the last question the user was looking at + // TODO: avoid another iteration through all displayed elements if (recyclerView != null && recyclerView.getAdapter() != null && recyclerView.getAdapter().getItemCount() > 0) { emptyView.setVisibility(View.GONE); recyclerView.post(() -> { int position = 0; - for (HierarchyElement hierarchyElement : formList) { - if (shouldScrollToTheGivenIndex(hierarchyElement.getFormIndex(), formController)) { - position = formList.indexOf(hierarchyElement); + // Iterate over all the elements currently displayed looking for a match with the + // startIndex which can either represent a question or a field list. + for (HierarchyElement hierarchyElement : elementsToDisplay) { + FormIndex indexToCheck = hierarchyElement.getFormIndex(); + if (startIndex.equals(indexToCheck) + || (formController.indexIsInFieldList(startIndex) && indexToCheck.toString().startsWith(startIndex.toString()))) { + position = elementsToDisplay.indexOf(hierarchyElement); break; } } @@ -145,9 +154,28 @@ public void onClick(View v) { } } - private boolean shouldScrollToTheGivenIndex(FormIndex formIndex, FormController formController) { - return startIndex.equals(formIndex) - || (formController.indexIsInFieldList(startIndex) && formIndex.toString().startsWith(startIndex.toString())); + + /** + * Configure the navigation buttons at the bottom of the screen. + */ + void configureButtons(FormController formController) { + jumpPreviousButton.setOnClickListener(v -> goUpLevel()); + + jumpBeginningButton.setOnClickListener(v -> { + formController.getTimerLogger().exitView(); + formController.jumpToIndex(FormIndex.createBeginningOfFormIndex()); + + setResult(RESULT_OK); + finish(); + }); + + jumpEndButton.setOnClickListener(v -> { + formController.getTimerLogger().exitView(); + formController.jumpToIndex(FormIndex.createEndOfFormIndex()); + + setResult(RESULT_OK); + finish(); + }); } protected void goUpLevel() { @@ -156,6 +184,9 @@ protected void goUpLevel() { refreshView(); } + /** + * Builds a string representing the path of the current group. Each level is separated by a <. + */ private String getCurrentPath() { FormController formController = Collect.getInstance().getFormController(); FormIndex index = formController.getFormIndex(); @@ -170,6 +201,11 @@ private String getCurrentPath() { return ODKView.getGroupsPath(groups.toArray(new FormEntryCaption[groups.size()])); } + /** + * Rebuilds the view to reflect the elements that should be displayed based on the + * FormController's current index. This index is either set prior to the activity opening or + * mutated by {@link #onElementClick(HierarchyElement)} if a repeat instance was tapped. + */ public void refreshView() { try { FormController formController = Collect.getInstance().getFormController(); @@ -180,7 +216,7 @@ public void refreshView() { // display // everything enclosed within that group. String contextGroupRef = ""; - formList = new ArrayList<>(); + elementsToDisplay = new ArrayList<>(); // If we're currently at a repeat node, record the name of the node and step to the next // node to display. @@ -222,11 +258,11 @@ public void refreshView() { formController.stepToNextEvent(FormController.STEP_INTO_GROUP); contextGroupRef = formController.getFormIndex().getReference().getParentRef().toString(true); - path.setVisibility(View.GONE); + groupPathTextView.setVisibility(View.GONE); jumpPreviousButton.setEnabled(false); } else { - path.setVisibility(View.VISIBLE); - path.setText(getCurrentPath()); + groupPathTextView.setVisibility(View.VISIBLE); + groupPathTextView.setText(getCurrentPath()); jumpPreviousButton.setEnabled(true); } @@ -283,9 +319,9 @@ public void refreshView() { // show the question if it is an editable field. // or if it is read-only and the label is not blank. String answerDisplay = FormEntryPromptUtils.getAnswerText(fp, this, formController); - formList.add( + elementsToDisplay.add( new HierarchyElement(FormEntryPromptUtils.markQuestionIfIsRequired(label, fp.isRequired()), answerDisplay, null, - QUESTION, fp.getIndex())); + HierarchyElement.Type.QUESTION, fp.getIndex())); } break; case FormEntryController.EVENT_GROUP: @@ -313,8 +349,8 @@ public void refreshView() { HierarchyElement group = new HierarchyElement(getLabel(fc), null, ContextCompat .getDrawable(this, R.drawable.expander_ic_minimized), - COLLAPSED, fc.getIndex()); - formList.add(group); + HierarchyElement.Type.COLLAPSED, fc.getIndex()); + elementsToDisplay.add(group); } String repeatLabel = getLabel(fc); if (fc.getFormElement().getChildren().size() == 1 && fc.getFormElement().getChild(0) instanceof GroupDef) { @@ -326,17 +362,16 @@ public void refreshView() { } repeatLabel += " (" + (fc.getMultiplicity() + 1) + ")\u200E"; // Add this group name to the drop down list for this repeating group. - HierarchyElement h = formList.get(formList.size() - 1); - h.addChild(new HierarchyElement(repeatLabel, null, null, CHILD, fc.getIndex())); + HierarchyElement h = elementsToDisplay.get(elementsToDisplay.size() - 1); + h.addChild(new HierarchyElement(repeatLabel, null, null, HierarchyElement.Type.CHILD, fc.getIndex())); break; } event = formController.stepToNextEvent(FormController.STEP_INTO_GROUP); } - recyclerView.setAdapter(new HierarchyListAdapter(formList, this::onElementClick)); + recyclerView.setAdapter(new HierarchyListAdapter(elementsToDisplay, this::onElementClick)); - // set the controller back to the current index in case the user hits 'back' formController.jumpToIndex(currentIndex); } catch (Exception e) { Timber.e(e); @@ -344,7 +379,88 @@ public void refreshView() { } } - protected abstract void onElementClick(HierarchyElement element); + /** + * Handles clicks on a specific row in the hierarchy view. Clicking on a: + * - group makes it toggle between expanded and collapsed + * - question jumps to the form filling view with that question shown. If the question is in a + * field list, shows that entire field list. + * - group's child element causes this hierarchy view to be refreshed with that element's + * questions shown + */ + public void onElementClick(HierarchyElement element) { + int position = elementsToDisplay.indexOf(element); + FormIndex index = element.getFormIndex(); + + switch (element.getType()) { + case EXPANDED: + element.setType(HierarchyElement.Type.COLLAPSED); + ArrayList children = element.getChildren(); + for (int i = 0; i < children.size(); i++) { + elementsToDisplay.remove(position + 1); + } + element.setIcon(ContextCompat.getDrawable(this, R.drawable.expander_ic_minimized)); + break; + case COLLAPSED: + element.setType(HierarchyElement.Type.EXPANDED); + ArrayList children1 = element.getChildren(); + for (int i = 0; i < children1.size(); i++) { + Timber.i("adding child: %s", children1.get(i).getFormIndex()); + elementsToDisplay.add(position + 1 + i, children1.get(i)); + + } + element.setIcon(ContextCompat.getDrawable(this, R.drawable.expander_ic_maximized)); + break; + case QUESTION: + onQuestionClicked(index); + return; + case CHILD: + Collect.getInstance().getFormController().jumpToIndex(element.getFormIndex()); + setResult(RESULT_OK); + refreshView(); + return; + } + + recyclerView.setAdapter(new HierarchyListAdapter(elementsToDisplay, this::onElementClick)); + ((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(position, 0); + } + + /** + * Handles clicks on a question. Jumps to the form filling view with the selected question shown. + * If the selected question is in a field list, show the entire field list. + */ + void onQuestionClicked(FormIndex index) { + Collect.getInstance().getFormController().jumpToIndex(index); + if (Collect.getInstance().getFormController().indexIsInFieldList()) { + try { + Collect.getInstance().getFormController().stepToPreviousScreenEvent(); + } catch (JavaRosaException e) { + Timber.d(e); + createErrorDialog(e.getCause().getMessage()); + return; + } + } + setResult(RESULT_OK); + finish(); + } + + /** + * When the device back button is pressed, go back to the previous activity, NOT the previous + * level in the hierarchy as the "Go Up" button does. + */ + @Override + public void onBackPressed() { + FormController formController = Collect.getInstance().getFormController(); + if (formController != null) { + formController.getTimerLogger().exitView(); + formController.jumpToIndex(startIndex); + } + + onBackPressedWithoutLogger(); + } + + protected void onBackPressedWithoutLogger() { + super.onBackPressed(); + } /** * Creates and displays dialog with the given errorMsg. diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/ViewFormHierarchyActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/ViewFormHierarchyActivity.java deleted file mode 100644 index 0dca2e12523..00000000000 --- a/collect_app/src/main/java/org/odk/collect/android/activities/ViewFormHierarchyActivity.java +++ /dev/null @@ -1,114 +0,0 @@ -/* - * Copyright (C) 2017 Shobhit - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except - * in compliance with the License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License - * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express - * or implied. See the License for the specific language governing permissions and limitations under - * the License. - */ - -package org.odk.collect.android.activities; - -import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.support.v7.widget.LinearLayoutManager; -import android.view.View; -import android.widget.Button; - -import org.javarosa.core.model.FormIndex; -import org.odk.collect.android.R; -import org.odk.collect.android.adapters.HierarchyListAdapter; -import org.odk.collect.android.application.Collect; -import org.odk.collect.android.exception.JavaRosaException; -import org.odk.collect.android.logic.HierarchyElement; - -import java.util.ArrayList; - -import timber.log.Timber; - -public class ViewFormHierarchyActivity extends FormHierarchyActivity { - - @Override - public void onCreate(Bundle savedInstanceState) { - super.onCreate(savedInstanceState); - - // super.onCreate() can call finish() if it finds the FormController to be null - // in that case, we want to shortcut this method, and let the Activity finish itself - if (isFinishing()) { - return; - } - - Collect.getInstance().getFormController().stepToOuterScreenEvent(); - - Button exitButton = findViewById(R.id.exitButton); - exitButton.setOnClickListener(new View.OnClickListener() { - @Override - public void onClick(View v) { - setResult(RESULT_OK); - finish(); - } - }); - - exitButton.setVisibility(View.VISIBLE); - - jumpBeginningButton.setVisibility(View.GONE); - jumpEndButton.setVisibility(View.GONE); - } - - @Override - public void onElementClick(HierarchyElement element) { - int position = formList.indexOf(element); - FormIndex index = element.getFormIndex(); - if (index == null) { - goUpLevel(); - return; - } - - switch (element.getType()) { - case EXPANDED: - element.setType(COLLAPSED); - ArrayList children = element.getChildren(); - for (int i = 0; i < children.size(); i++) { - formList.remove(position + 1); - } - element.setIcon(ContextCompat.getDrawable(this, R.drawable.expander_ic_minimized)); - break; - case COLLAPSED: - element.setType(EXPANDED); - ArrayList children1 = element.getChildren(); - for (int i = 0; i < children1.size(); i++) { - Timber.i("adding child: %s", children1.get(i).getFormIndex()); - formList.add(position + 1 + i, children1.get(i)); - - } - element.setIcon(ContextCompat.getDrawable(this, R.drawable.expander_ic_maximized)); - break; - case QUESTION: - Collect.getInstance().getFormController().jumpToIndex(index); - if (Collect.getInstance().getFormController().indexIsInFieldList()) { - try { - Collect.getInstance().getFormController().stepToPreviousScreenEvent(); - } catch (JavaRosaException e) { - Timber.d(e); - createErrorDialog(e.getCause().getMessage()); - return; - } - } - setResult(RESULT_OK); - return; - case CHILD: - Collect.getInstance().getFormController().jumpToIndex(element.getFormIndex()); - setResult(RESULT_OK); - refreshView(); - return; - } - - recyclerView.setAdapter(new HierarchyListAdapter(formList, this::onElementClick)); - ((LinearLayoutManager) recyclerView.getLayoutManager()).scrollToPositionWithOffset(position, 0); - } -} diff --git a/collect_app/src/main/java/org/odk/collect/android/activities/ViewOnlyFormHierarchyActivity.java b/collect_app/src/main/java/org/odk/collect/android/activities/ViewOnlyFormHierarchyActivity.java new file mode 100644 index 00000000000..f3017ac6d3d --- /dev/null +++ b/collect_app/src/main/java/org/odk/collect/android/activities/ViewOnlyFormHierarchyActivity.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 Shobhit + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except + * in compliance with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software distributed under the License + * is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express + * or implied. See the License for the specific language governing permissions and limitations under + * the License. + */ + +package org.odk.collect.android.activities; + +import android.view.View; +import android.widget.Button; + +import org.javarosa.core.model.FormIndex; +import org.odk.collect.android.R; +import org.odk.collect.android.logic.FormController; + +/** + * Displays the structure of a form along with the answers for the current instance. Disables all + * features that allow the user to edit the form instance. + */ +public class ViewOnlyFormHierarchyActivity extends FormHierarchyActivity { + /** + * Hides buttons to jump to the beginning and to the end of the form instance to edit it. Adds + * an extra exit button that exits this activity. + */ + @Override + void configureButtons(FormController formController) { + jumpPreviousButton.setOnClickListener(v -> goUpLevel()); + + Button exitButton = findViewById(R.id.exitButton); + exitButton.setOnClickListener(v -> { + setResult(RESULT_OK); + finish(); + }); + + exitButton.setVisibility(View.VISIBLE); + + jumpBeginningButton.setVisibility(View.GONE); + jumpEndButton.setVisibility(View.GONE); + } + + /** + * Prevents the user from clicking on individual questions to jump into the form-filling view. + */ + @Override + void onQuestionClicked(FormIndex index) { + // Do nothing + } + + /** + * Prevents logging an audit event when the user exits the activity. + */ + @Override + public void onBackPressed() { + onBackPressedWithoutLogger(); + } +} diff --git a/collect_app/src/main/java/org/odk/collect/android/logic/HierarchyElement.java b/collect_app/src/main/java/org/odk/collect/android/logic/HierarchyElement.java index fcca05fe41b..979a619d7af 100644 --- a/collect_app/src/main/java/org/odk/collect/android/logic/HierarchyElement.java +++ b/collect_app/src/main/java/org/odk/collect/android/logic/HierarchyElement.java @@ -15,21 +15,57 @@ package org.odk.collect.android.logic; import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; import org.javarosa.core.model.FormIndex; import java.util.ArrayList; +/** + * Represents a question or repeat to be shown in + * {@link org.odk.collect.android.activities.FormHierarchyActivity}. + */ public class HierarchyElement { + /** + * Repeat instances (always of type {@link Type#CHILD}) if this element is a repeat + * ({@link Type#COLLAPSED} or {@link Type#EXPANDED}). Not relevant otherwise. + */ private final ArrayList children = new ArrayList<>(); - private int type; + /** + * The type and state of this element. See {@link Type}. + */ + @NonNull + private Type type; + + /** + * The form index of this element. + */ + @NonNull private final FormIndex formIndex; + + /** + * The primary text this element should be displayed with. + */ + @NonNull private final String primaryText; + + /** + * The secondary text this element should be displayed with. + */ + @Nullable private final String secondaryText; + + /** + * The collapsed or expanded icon if this element is a repeat ({@link Type#COLLAPSED} or + * {@link Type#EXPANDED}). Not relevant otherwise. + */ + @Nullable private Drawable icon; - public HierarchyElement(String primaryText, String secondaryText, Drawable icon, int type, FormIndex formIndex) { + public HierarchyElement(@NonNull String primaryText, @Nullable String secondaryText, + @Nullable Drawable icon, @NonNull Type type, @NonNull FormIndex formIndex) { this.primaryText = primaryText; this.secondaryText = secondaryText; this.icon = icon; @@ -37,31 +73,36 @@ public HierarchyElement(String primaryText, String secondaryText, Drawable icon, this.formIndex = formIndex; } + @NonNull public String getPrimaryText() { return primaryText; } + @Nullable public String getSecondaryText() { return secondaryText; } + @Nullable public Drawable getIcon() { return icon; } - public void setIcon(Drawable icon) { + public void setIcon(@Nullable Drawable icon) { this.icon = icon; } + @NonNull public FormIndex getFormIndex() { return formIndex; } - public int getType() { + @NonNull + public Type getType() { return type; } - public void setType(int newType) { + public void setType(@NonNull Type newType) { type = newType; } @@ -72,4 +113,29 @@ public ArrayList getChildren() { public void addChild(HierarchyElement h) { children.add(h); } + + /** + * The type and state of this element. + */ + public enum Type { + /** + * A repeat instance. + */ + CHILD, + + /** + * A repeat that should be displayed as expanded. + */ + EXPANDED, + + /** + * A repeat that should be displayed as collapsed. + */ + COLLAPSED, + + /** + * A question. + */ + QUESTION; + } }