public abstract class AnswerFormat implements Serializable {
    /**
     * Default constructor. The appropriate subclass of AnswerFormat should be used instead of this
     * directly.
     */
    public AnswerFormat() {
    }

    /**
     * Returns the QuestionType for this answer format. Implement this in your subclass.
     *
     * @return the question type
     */
    public QuestionType getQuestionType() {
        return Type.None;
    }

    /**
     * The type of question. (read-only)
     * <p>
     * The type provides a default {@link org.researchstack.backbone.ui.step.body.StepBody} for that
     * type of question. A custom StepLayout implementation may provide it's own StepBody rather
     * than using the default provided by this AnswerFormat.
     */
    public enum Type implements QuestionType {
        None(NotImplementedStepBody.class),
        Scale(NotImplementedStepBody.class),
        SingleChoice(SingleChoiceQuestionBody.class),
        MultipleChoice(MultiChoiceQuestionBody.class),
        Decimal(DecimalQuestionBody.class),
        Integer(IntegerQuestionBody.class),
        Boolean(SingleChoiceQuestionBody.class),
        Eligibility(NotImplementedStepBody.class),
        Text(TextQuestionBody.class),
        TimeOfDay(DateQuestionBody.class),
        DateAndTime(DateQuestionBody.class),
        Date(DateQuestionBody.class),
        TimeInterval(NotImplementedStepBody.class),
        Duration(DurationQuestionBody.class),
        Location(NotImplementedStepBody.class),
        Form(FormBody.class);

        private Class<?> stepBodyClass;

        Type(Class<?> stepBodyClass) {
            this.stepBodyClass = stepBodyClass;
        }

        @Override
        public Class<?> getStepBodyClass() {
            return stepBodyClass;
        }

    }

    /**
     * The style of the question (that is, single or multiple choice).
     */
    public enum ChoiceAnswerStyle {
        SingleChoice,
        MultipleChoice
    }

    /**
     * An enumeration of the format styles available for scale answers.
     */
    public enum NumberFormattingStyle {
        Default,
        Percent
    }

    /**
     * The style of date picker to use in an {@link DateAnswerFormat} object.
     */
    public enum DateAnswerStyle {
        DateAndTime,
        Date,
        TimeOfDay
    }

    /**
     * Interface that {@link Type} implements. Since you cannot add a value to an existing enum, you
     * may implement this interface instead to provide your own QuestionType that provides a {@link
     * org.researchstack.backbone.ui.step.body.StepBody} class.
     */
    public interface QuestionType {
        Class<?> getStepBodyClass();
    }
} public class BirthDateAnswerFormat extends DateAnswerFormat {
    private final int minAge;
    private final int maxAge;

    public BirthDateAnswerFormat(Date defaultDate, int minAge, int maxAge) {
        super(DateAnswerStyle.Date, defaultDate, dateFromAge(maxAge), dateFromAge(minAge));
        this.minAge = minAge;
        this.maxAge = maxAge;
    }

    private static Date dateFromAge(int age) {
        Calendar calendar = Calendar.getInstance();
        if (age != 0) {
            calendar.add(Calendar.YEAR, -age);
            return calendar.getTime();
        }

        return null;
    }

    @Override
    public BodyAnswer validateAnswer(Date resultDate) {
        Date minDate = getMinimumDate();
        Date maxDate = getMaximumDate();

        if (minDate != null && isOnOrBefore(resultDate, minDate)) {
            return new BodyAnswer(false, R.string.rsb_birth_date_too_old, String.valueOf(maxAge));
        }

        if (maxDate != null && !isOnOrBefore(resultDate, maxDate)) {
            return new BodyAnswer(false, R.string.rsb_birth_date_too_young, String.valueOf(minAge));
        }

        return BodyAnswer.VALID;
    }

    private boolean isOnOrBefore(Date inputDate, Date cutoffDate) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(inputDate);
        int year = calendar.get(Calendar.YEAR);
        int dayOfYear = calendar.get(Calendar.DAY_OF_YEAR);
        calendar.setTime(cutoffDate);
        int cutoffYear = calendar.get(Calendar.YEAR);
        int cutoffDayOfYear = calendar.get(Calendar.DAY_OF_YEAR);

        return year < cutoffYear || (year == cutoffYear && dayOfYear <= cutoffDayOfYear);
    }
} public class DateAnswerFormat extends AnswerFormat {

    private DateAnswerStyle style;

    private Date defaultDate;

    private Date minimumDate;

    private Date maximumDate;

    public DateAnswerFormat(DateAnswerStyle style) {
        this.style = style;
    }

    public DateAnswerFormat(DateAnswerStyle style, Date defaultDate, Date minimumDate, Date maximumDate) {
        this.style = style;
        this.defaultDate = defaultDate;
        this.minimumDate = minimumDate;
        this.maximumDate = maximumDate;
    }

    /**
     * Returns the style of date entry.
     *
     * @return the style of the date entry
     */
    public DateAnswerStyle getStyle() {
        return style;
    }

    /**
     * Returns the date to use as the default.
     * <p>
     * The date is displayed in the user's time zone. When the value of this property is
     * <code>null</code>, the current time is used as the default.
     *
     * @return the default date for the date picker presented to the user, or null
     */
    public Date getDefaultDate() {
        return defaultDate;
    }

    /**
     * Returns the minimum allowed date.
     * <p>
     * When the value of this property is <code>null</code>, there is no minimum.
     *
     * @return returns the minimum allowed date, or null
     */
    public Date getMinimumDate() {
        return minimumDate;
    }

    /**
     * The maximum allowed date.
     * <p>
     * When the value of this property is <code>null</code>, there is no maximum.
     *
     * @return returns the maximum allowed date, or null
     */
    public Date getMaximumDate() {
        return maximumDate;
    }

    @Override
    public QuestionType getQuestionType() {
        if (style == DateAnswerStyle.Date) return Type.Date;
        if (style == DateAnswerStyle.DateAndTime) return Type.DateAndTime;
        if (style == DateAnswerStyle.TimeOfDay) return Type.TimeOfDay;

        return Type.None;
    }

    public BodyAnswer validateAnswer(Date resultDate) {
        if (minimumDate != null && resultDate.getTime() < minimumDate.getTime()) {
            return new BodyAnswer(false,
                    R.string.rsb_invalid_answer_date_under,
                    FormatHelper.SIMPLE_FORMAT_DATE.format(minimumDate));
        }

        if (maximumDate != null && resultDate.getTime() > maximumDate.getTime()) {
            return new BodyAnswer(false,
                    R.string.rsb_invalid_answer_date_over,
                    FormatHelper.SIMPLE_FORMAT_DATE.format(maximumDate));
        }

        return BodyAnswer.VALID;
    }

} public class Choice<T> implements Serializable {
    private String text;

    private T value;

    private String detailText;

    /**
     * Creates a choice object with the provided text and value, detailtext is null
     *
     * @param text  user-facing text representing the choice
     * @param value value of any type for this choice, type should match other choices in the step
     */
    public Choice(String text, T value) {
        this(text, value, null);
    }

    /**
     * Creates a choice object with the provided text, value, and detailtext
     *
     * @param text       user-facing text representing the choice
     * @param value      value of any type for this choice, type should match other choices in the
     *                   step
     * @param detailText extra detail text for the choice
     */
    public Choice(String text, T value, String detailText) {
        this.text = text;
        this.value = value;
        this.detailText = detailText;
    }

    /**
     * Return the user-facing text for this choice
     *
     * @return the text
     */
    public String getText() {
        return text;
    }

    /**
     * Sets the text that the user will see for this choice
     *
     * @param text the text
     */
    public void setText(String text) {
        this.text = text;
    }

    /**
     * Returns the value of type T for this choice
     *
     * @return the value
     */
    public T getValue() {
        return value;
    }

    /**
     * Sets the value of type T for this choice
     *
     * @param value the value of this choice
     */
    public void setValue(T value) {
        this.value = value;
    }

    /**
     * Gets the detail text for this choice, or null
     *
     * @return the detail text, or null if none
     */
    public String getDetailText() {
        return detailText;
    }

    /**
     * Sets the (optional) detail text for this choice
     *
     * @param detailText the detail text
     */
    public void setDetailText(String detailText) {
        this.detailText = detailText;
    }
} public class ConsentDocument implements Serializable {

    /**
     * The document's title in a localized string.
     * <p>
     * The title appears only in the generated PDF for review; it is not used in the visual consent
     * process.
     */
    private String title;

    /**
     * The sections to be in printed in the PDF file and or presented in the visual consent
     * sequence.
     * <p>
     * If the <code>htmlReviewContent</code> property is not set, this content is also used to
     * populate the document for review in the consent review step.
     * <p>
     * The PDF file contains all sections.
     */
    private List<ConsentSection> sections;

    /**
     * The title to be rendered on the signature page of the generated PDF in a localized string.
     * <p>
     * The title is ignored for visual consent. The title is also ignored if you supply a value for
     * the <code>htmlReviewContent</code> property.
     */
    private int signaturePageTitle;

    /**
     * The content to be rendered below the title on the signature page of the generated PDF in a
     * localized string.
     * <p>
     * The content is ignored for visual consent. The content is also ignored if you supply a value
     * for the <code>htmlReviewContent</code> property.
     */
    @Deprecated
    private String signaturePageContent;

    /**
     * The set of signatures that are required or prepopulated in the document.
     * <p>
     * To add a signature to the document after consent review, the <code>signatures</code> array
     * needs to be modified to incorporate the new signature content prior to PDF generation. For
     * more information, see `[ORKConsentSignatureResult applyToDocument:]`.
     */
    private List<ConsentSignature> signatures = new ArrayList<>(1);

    /**
     * Override HTML content for review.
     * <p>
     * Typically, the review content is generated from the values of the <code>sections</code> and
     * <code>signatures</code> properties.
     * <p>
     * When this property is set, the review content is reproduced exactly as provided in the
     * property in the consent review step, and the <code>sections</code> and
     * <code>signatures</code> properties are ignored.
     */
    private String htmlReviewContent;


    public void setTitle(String title) {
        this.title = title;
    }

    @StringRes
    public int getSignaturePageTitle() {
        return signaturePageTitle;
    }

    public void setSignaturePageTitle(@StringRes int signaturePageTitle) {
        this.signaturePageTitle = signaturePageTitle;
    }

    @Deprecated
    public void setSignaturePageContent(String signaturePageContent) {
        this.signaturePageContent = signaturePageContent;
    }

    /**
     * Adds a signature to the array of signatures.
     *
     * @param signature The signature object to add to the document.
     */
    public void addSignature(ConsentSignature signature) {
        signatures.add(signature);
    }

    public ConsentSignature getSignature(int location) {
        return signatures.get(location);
    }

    public List<ConsentSection> getSections() {
        return sections;
    }

    public void setSections(List<ConsentSection> sections) {
        this.sections = sections;
    }

    public String getHtmlReviewContent() {
        return htmlReviewContent;
    }

    public void setHtmlReviewContent(String htmlReviewContent) {
        this.htmlReviewContent = htmlReviewContent;
    }

} public class ConsentSection implements Serializable {

    /**
     * The type of section. (read-only)
     * <p>
     * The value of this property indicates whether a predefined image, title, and animation are
     * present.
     */
    @SerializedName("sectionType")
    private Type type;
    /**
     * The title of the consent section in a localized string.
     * <p>
     * The title is displayed as a step title in the animated consent sequence and is also included
     * in the PDF file, but it can be overridden by setting <code>formalTitle</code>.
     */
    @SerializedName("sectionTitle")
    private String title;
    /**
     * The formal title of the section in a localized string, for use in the legal document.
     * <p>
     * If the value of this property is <code>null</code>, the value of <code>title</code> is used
     * in the legal document instead.
     */
    @SerializedName("sectionFormalTitle")
    private String formalTitle;
    /**
     * A short summary of the content in a localized string.
     * <p>
     * The summary is displayed as description text in the animated consent sequence. The summary
     * should be limited in length, so that the consent can be reliably displayed on smaller
     * screens.
     */
    @SerializedName("sectionSummary")
    private String summary;
    /**
     * The content of the section in a localized string.
     * <p>
     * In a consent review step or in PDF file generation, the string is printed as the section's
     * content. The string is also displayed as Learn More content in a visual consent step.
     * <p>
     * This property is never prepopulated based on the value of <code>type</code>. If both
     * <code>content</code> and <code>htmlContent</code> are non-nil, the value of the
     * <code>htmlContent</code> property is used.
     */
    @SerializedName("sectionContent")
    private String content;
    private String escapedContent;
    /**
     * The HTML content used to override the <code>content</code> property if additional formatting
     * is needed. The content should be localized.
     * <p>
     * In cases where plain text content is not sufficient to convey important details during the
     * consent process, you can provide HTML content in this property. When you do this, the
     * <code>htmlContent</code> property takes precedence over the <code>content</code> property.
     * <p>
     * In a consent review step or in PDF file generation, the value of this property is printed as
     * the section's content; in a visual consent step, the content is displayed as Learn More
     * content.
     */
    @SerializedName("sectionHtmlContent")
    private String htmlContent;
    /**
     * A custom illustration for the consent.
     * <p>
     * The custom image can override the image associated with any of the predefined section types
     * for an {@link org.researchstack.backbone.step.ConsentVisualStep} object. It is ignored for a
     * consent review step and for PDF generation.
     * <p>
     * The image is used in template rendering mode, and is tinted using the tint color.
     */
    @SerializedName("sectionImage")
    private String customImageName;
    /**
     * A custom Learn More button title in a localized string.
     * <p>
     * The predefined section types have localized descriptive Learn More button titles for a visual
     * consent step. When this property is not <code>null</code>, it overrides that default text.
     */
    @SerializedName("sectionMoreTitle")
    private String customLearnMoreButtonTitle;
    /**
     * A file URL that specifies a custom transition animation video.
     * <p>
     * Animations of the illustration between one screen and the next are provided by default for
     * transitions between consecutive section <code>type</code> codes. Custom sections and
     * out-of-order transitions may require custom animations.
     * <p>
     * The animation loaded from the file URL is played aspect fill in the illustration area for
     * forward transitions only. The video is rendered in template mode, with white treated as if it
     * were transparent.
     */
    @SerializedName("sectionAnimationUrl")
    private String customAnimationURL;

    /**
     * Returns an initialized consent section using the specified type.
     *
     * @param type The consent section type.
     */
    public ConsentSection(Type type) {
        this.type = type;
        this.summary = null;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public String getFormalTitle() {
        return formalTitle;
    }

    public Type getType() {
        return type;
    }

    public String getHtmlContent() {
        return htmlContent;
    }

    public void setHtmlContent(String htmlContent) {
        this.htmlContent = htmlContent;
    }

    public String getCustomImageName() {
        return customImageName;
    }

    public String getContent() {
        return content;
    }

    public void setContent(String content) {
        this.content = content;
        this.escapedContent = null;
    }

    public String getSummary() {
        return summary;
    }

    public void setSummary(String summary) {
        this.summary = summary;
    }

    public String getEscapedContent() {
        // If its null, return that. If not, escape/replace chars in var content
        if (TextUtils.isEmpty(content)) {
            return content;
        }

        return escapedContent;
    }

    public String getCustomLearnMoreButtonTitle() {
        return customLearnMoreButtonTitle;
    }

    public enum Type implements Serializable {
        /**
         * Overview of the informed consent process.
         * <p>
         * This content can inform the user of what to expect during the process, and provide
         * general background information on the purpose of the study.
         */
        @SerializedName("overview")
        Overview(),

        /**
         * A section informing the user that sensor data will be collected.
         * <p>
         * This content can identify which sensors will be used, for how long, and for what
         * purpose.
         */
        @SerializedName("dataGathering")
        DataGathering(),

        /**
         * A section describing the privacy policies for the study.
         * <p>
         * This content can describe how data is protected, the processes used to sanitize the
         * collected data or make it anonymous, and address the risks
         * <p>
         * involved.
         */
        @SerializedName("privacy")
        Privacy(),

        /**
         * A section describing how the collected data will be used.
         * <p>
         * This content can include details about those who will have access to the data, the types
         * of analysis that will be performed, and the degree of control the participant may have
         * over the data after it is collected.
         */
        @SerializedName("dataUse")
        DataUse(),

        /**
         * A section describing how much time is required for the study.
         * <p>
         * This content can help users understand what to expect as they participate in the study.
         */
        @SerializedName("timeCommitment")
        TimeCommitment(),

        /**
         * A section describing active task use in the study.
         * <p>
         * This content can describe what types of tasks need to be performed, how often, and for
         * what purpose. Any risks that are involved can also be communicated in this section.
         */
        @SerializedName("studyTasks")
        StudyTasks(),

        /**
         * A section describing survey use in the study.
         * <p>
         * This content can explain how survey data will be collected, for what purpose, and make it
         * clear to what extent participation is optional.
         */
        @SerializedName("studySurvey")
        StudySurvey(),

        /**
         * A section describing how to withdraw from the study.
         * <p>
         * This section can describe the policies that govern the collected data if the user decides
         * to withdraw.
         */
        @SerializedName("withdrawing")
        Withdrawing(),

        /**
         * A custom section.
         * <p>
         * Custom sections don't have a predefined title, summary, content, image, or animation. A
         * consent document may have as many or as few custom sections as needed.
         */
        @SerializedName("custom")
        Custom,

        /**
         * Document-only sections.
         * <p>
         * Document-only sections are ignored for a visual consent step and are only displayed in a
         * consent review step (assuming no value is provided for the  <code>htmlReviewContent</code>
         * property).
         */
        @SerializedName("onlyInDocument")
        OnlyInDocument;

        public int getTitleResId() {
            switch (this) {
                case Overview:
                    return R.string.rsb_consent_section_welcome;
                case DataGathering:
                    return R.string.rsb_consent_section_data_gathering;
                case Privacy:
                    return R.string.rsb_consent_section_privacy;
                case DataUse:
                    return R.string.rsb_consent_section_data_use;
                case TimeCommitment:
                    return R.string.rsb_consent_section_time_commitment;
                case StudySurvey:
                    return R.string.rsb_consent_section_study_survey;
                case StudyTasks:
                    return R.string.rsb_consent_section_study_tasks;
                case Withdrawing:
                    return R.string.rsb_consent_section_withdrawing;
                default:
                    return -1;
            }
        }

        public String getImageName() {
            switch (this) {
                case DataGathering:
                    return "rsb_consent_section_data_gathering";
                case Privacy:
                    return "rsb_consent_section_privacy";
                case DataUse:
                    return "rsb_consent_section_data_use";
                case TimeCommitment:
                    return "rsb_consent_section_time_commitment";
                case StudySurvey:
                    return "rsb_consent_section_study_survey";
                case StudyTasks:
                    return "rsb_consent_section_study_tasks";
                case Withdrawing:
                    return "rsb_consent_section_withdrawing";
                default:
                    return null;
            }
        }

        public int getMoreInfoResId() {
            switch (this) {
                case Overview:
                    return R.string.rsb_consent_section_more_info_welcome;
                case DataGathering:
                    return R.string.rsb_consent_section_more_info_data_gathering;
                case Privacy:
                    return R.string.rsb_consent_section_more_info_privacy;
                case DataUse:
                    return R.string.rsb_consent_section_more_info_data_use;
                case TimeCommitment:
                    return R.string.rsb_consent_section_more_info_time_commitment;
                case StudySurvey:
                    return R.string.rsb_consent_section_more_info_study_survey;
                case StudyTasks:
                    return R.string.rsb_consent_section_more_info_study_tasks;
                case Withdrawing:
                    return R.string.rsb_consent_section_more_info_withdrawing;
                default:
                    return R.string.rsb_consent_section_more_info;
            }
        }

    }

} public class DocumentProperties {
    @SerializedName("htmlDocument")
    private String htmlDocument;

    @SerializedName("investigatorShortDescription")
    private String investigatorShortDescription;

    @SerializedName("investigatorLongDescription")
    private String investigatorLongDescription;

    @SerializedName("htmlContent")
    private String htmlContent;

    private boolean requiresSignature;
    private boolean requiresName;
    private boolean requiresBirthdate;

    public String getHtmlDocument() {
        return htmlDocument;
    }

    public void setHtmlDocument(String htmlDocument) {
        this.htmlDocument = htmlDocument;
    }

    public String getInvestigatorShortDescription() {
        return investigatorShortDescription;
    }

    public void setInvestigatorShortDescription(String investigatorShortDescription) {
        this.investigatorShortDescription = investigatorShortDescription;
    }

    public String getInvestigatorLongDescription() {
        return investigatorLongDescription;
    }

    public void setInvestigatorLongDescription(String investigatorLongDescription) {
        this.investigatorLongDescription = investigatorLongDescription;
    }

    public String getHtmlContent() {
        return htmlContent;
    }

    public void setHtmlContent(String htmlContent) {
        this.htmlContent = htmlContent;
    }

    public boolean requiresSignature() {
        return requiresSignature;
    }

    public void setRequiresSignature(boolean requiresSignature) {
        this.requiresSignature = requiresSignature;
    }

    public boolean requiresName() {
        return requiresName;
    }

    public void setRequiresName(boolean requiresName) {
        this.requiresName = requiresName;
    }

    public boolean requiresBirthdate() {
        return requiresBirthdate;
    }

    public void setRequiresBirthdate(boolean requiresBirthdate) {
        this.requiresBirthdate = requiresBirthdate;
    }
} public class StepResult<T> extends Result {
    /**
     * When StepResult only has a single value, pair that value with the following key
     */
    public static final String DEFAULT_KEY = "answer";

    private Map<String, T> results;

    private AnswerFormat answerFormat;

    /**
     * Creates a StepResult from a {@link Step}.
     * <p>
     * Using this constructor ensures that the StepResult has the correct identifier and answer
     * format for the corresponding step.
     *
     * @param step the step from which to create the StepResult
     */
    public StepResult(Step step) {
        super(step.getIdentifier());
        this.results = new HashMap<>();

        if (step instanceof QuestionStep) {
            answerFormat = ((QuestionStep) step).getAnswerFormat();
        }
        setStartDate(new Date());
        // this will be updated when the result is set
        setEndDate(new Date());
    }

    public Map<String, T> getResults() {
        return results;
    }

    public void setResults(Map<String, T> results) {
        this.results = results;
    }

    /**
     * Returns the result stored using {@link #setResult}.
     *
     * @return the result with the default identifier
     */
    public T getResult() {
        return getResultForIdentifier(DEFAULT_KEY);
    }

    /**
     * Sets the result using the default key, useful when there is only a single result.
     *
     * @param result the result to save with the default key
     */
    public void setResult(T result) {
        setResultForIdentifier(DEFAULT_KEY, result);
        setEndDate(new Date());
    }

    /**
     * Returns the result for the given identifier, use this when there are multiple results for the
     * step.
     *
     * @param identifier the identifier used as the key for storing this result
     * @return the result for the given identifier
     */
    public T getResultForIdentifier(String identifier) {
        return results.get(identifier);
    }

    /**
     * Sets the result for the given identifier, use when there are multiple results for the step.
     * <p>
     * If there is only one result, use the {@link #setResult} convenience method instead.
     *
     * @param identifier the identifier for the result
     * @param result     the result to save
     */
    public void setResultForIdentifier(String identifier, T result) {
        results.put(identifier, result);
    }

    /**
     * Gets the {@link AnswerFormat} for this step result. May be useful when processing the
     * result.
     *
     * @return the answer format associated with the step
     */
    public AnswerFormat getAnswerFormat() {
        return answerFormat;
    }
} public class ConsentVisualStep extends Step {
    private ConsentSection section;

    @Deprecated
    private String nextButtonString;

    public ConsentVisualStep(String identifier) {
        super(identifier);
    }

    @Override
    public int getStepTitle() {
        return org.researchstack.backbone.R.string.rsb_consent;
    }

    @Override
    public Class getStepLayoutClass() {
        return ConsentVisualStepLayout.class;
    }

    /**
     * Returns the ConsentSection associated with this step.
     *
     * @return the consent section for this step
     */
    public ConsentSection getSection() {
        return section;
    }

    /**
     * Sets the ConsentSection for this step.
     * <p>
     * This includes everything needed by the {@link ConsentVisualStepLayout} to render the step.
     *
     * @param section the consent section
     */
    public void setSection(ConsentSection section) {
        this.section = section;
    }

    @Deprecated
    public String getNextButtonString() {
        return nextButtonString;
    }

    @Deprecated
    public void setNextButtonString(String nextButtonString) {
        this.nextButtonString = nextButtonString;
    }
} public class FormStep extends QuestionStep {
    private List<QuestionStep> formSteps;

    public FormStep(String identifier, String title, String text) {
        super(identifier, title, new FormAnswerFormat());
        setText(text);
    }

    /**
     * Returns the list of items in the form.
     *
     * @return a list of QuestionSteps in the form
     */
    public List<QuestionStep> getFormSteps() {
        return formSteps;
    }

    public void setFormSteps(QuestionStep... formSteps) {
        setFormSteps(Arrays.asList(formSteps));
    }

    public void setFormSteps(List<QuestionStep> formSteps) {
        this.formSteps = formSteps;
    }
} public class QuestionStep extends Step {
    private AnswerFormat answerFormat;

    private String placeholder;

    /**
     * Returns a new question step that includes the specified identifier.
     *
     * @param identifier The identifier of the step (a step identifier should be unique within the
     *                   task).
     */
    public QuestionStep(String identifier) {
        super(identifier);
    }

    /**
     * Returns a new question step that includes the specified identifier, and title.
     *
     * @param identifier The identifier of the step (a step identifier should be unique within the
     *                   task).
     * @param title      A localized string that represents the primary text of the question.
     */
    public QuestionStep(String identifier, String title) {
        super(identifier, title);
    }

    /**
     * Returns a new question step that includes the specified identifier, title, and answer
     * format.
     *
     * @param identifier The identifier of the step (a step identifier should be unique within the
     *                   task).
     * @param title      A localized string that represents the primary text of the question.
     * @param format     The format in which the answer is expected.
     */
    public QuestionStep(String identifier, String title, AnswerFormat format) {
        super(identifier, title);
        this.answerFormat = format;
    }

    /**
     * Returns a special {@link org.researchstack.backbone.ui.step.layout.StepLayout} that is used
     * for all question steps.
     * <p>
     * This step layout uses the {@link #getStepBodyClass()} to fill in the user interaction portion
     * of the layout's UI.
     *
     * @return the StepLayout to be used for general QuestionSteps
     */
    @Override
    public Class getStepLayoutClass() {
        return SurveyStepLayout.class;
    }

    /**
     * Returns a subclass of {@link org.researchstack.backbone.ui.step.body.StepBody} responsible
     * for creating the ui for answering the question, base on the AnswerFormat.
     * <p>
     * This class is used by {@link SurveyStepLayout} to create the part of the layout where the
     * user answers the question. For example, a StepBody for a simple text question would be
     * responsible for creating an EditText for the SurveyStepLayout to place inside of its layout.
     * <p>
     * Override this method with your own StepBody implementation if you create a custom
     * QuestionStep.
     *
     * @return the StepBody implementation for this question step.
     */
    public Class<?> getStepBodyClass() {
        return answerFormat.getQuestionType().getStepBodyClass();
    }

    /**
     * Returns the format of the answer.
     * <p>
     * For example, the answer format might include the type of data to collect, the constraints to
     * place on the answer, or a list of available choices (in the case of single or multiple select
     * questions). It also provides the default {@link org.researchstack.backbone.ui.step.body.StepBody}
     * for questions of its type.
     *
     * @return the answer format for this question step
     */
    public AnswerFormat getAnswerFormat() {
        return answerFormat;
    }

    /**
     * Sets the answer format for this question step.
     *
     * @param answerFormat the answer format for this question step
     * @see #getAnswerFormat()
     */
    public void setAnswerFormat(AnswerFormat answerFormat) {
        this.answerFormat = answerFormat;
    }

    /**
     * Returns a localized string that represents the placeholder text displayed before an answer
     * has been entered.
     * <p>
     * For numeric and text-based answers, the placeholder content is displayed in the text field or
     * text area when an answer has not yet been entered.
     *
     * @return the placeholder string
     */
    public String getPlaceholder() {
        return placeholder;
    }

    /**
     * Sets a localized string that represents the placeholder text displayed before an answer has
     * been entered.
     * <p>
     * For numeric and text-based answers, the placeholder content is displayed in the text field or
     * text area when an answer has not yet been entered.
     *
     * @param placeholder the placeholder string
     */
    public void setPlaceholder(String placeholder) {
        this.placeholder = placeholder;
    }
} public class Step implements Serializable {
    private String identifier;

    private Class stepLayoutClass;

    private int stepTitle;

    private boolean optional = true;

    private String title;

    private String text;

    // The following fields are in RK but not implemented in ResearchStack
    // These options can be developed as needed or removed if we find they are not necessary
    private boolean restorable;
    private Task task;
    private boolean shouldTintImages;
    private boolean showsProgress;
    private boolean allowsBackNavigation;
    private boolean useSurveyMode;

    /**
     * Returns a new step initialized with the specified identifier.
     *
     * @param identifier The unique identifier of the step.
     */
    public Step(String identifier) {
        this.identifier = identifier;
    }

    /**
     * Returns a new step initialized with the specified identifier and title.
     *
     * @param identifier The unique identifier of the step.
     * @param title      The primary text to display for this step.
     */
    public Step(String identifier, String title) {
        this.identifier = identifier;
        this.title = title;
    }

    /**
     * A short string that uniquely identifies the step within the task.
     * <p>
     * The identifier is reproduced in the results of a step. In fact, the only way to link a result
     * (a {@link org.researchstack.backbone.result.StepResult} object) to the step that generated it
     * is to look at the value of <code>identifier</code>. To accurately identify step results, you
     * need to ensure that step identifiers are unique within each task.
     * <p>
     * In some cases, it can be useful to link the step identifier to a unique identifier in a
     * database; in other cases, it can make sense to make the identifier human readable.
     */
    public String getIdentifier() {
        return identifier;
    }

    /**
     * A boolean value indicating whether the user can skip the step without providing an answer.
     * <p>
     * The default value of this property is <code>true</code>. When the value is
     * <code>false</code>, the Skip button does not appear on this step.
     * <p>
     * This property may not be meaningful for all steps; for example, an active step might not
     * provide a way to skip, because it requires a timer to finish.
     *
     * @return a boolean indicating whether the step is skippable
     */
    public boolean isOptional() {
        return optional;
    }

    /**
     * Sets whether the step is skippable
     *
     * @param optional
     * @see #isOptional()
     */
    public void setOptional(boolean optional) {
        this.optional = optional;
    }

    /**
     * The primary text to display for the step in a localized string.
     * <p>
     * This text is also used as the label when a step is shown in the more compact version in a
     * {@link FormStep}.
     *
     * @return the primary text for the question
     */
    public String getTitle() {
        return title;
    }

    /**
     * Sets the primary text to display for the step in a localized string.
     *
     * @param title the primary text for the question.
     * @see #getTitle()
     */
    public void setTitle(String title) {
        this.title = title;
    }

    /**
     * Additional text to display for the step in a localized string.
     * <p>
     * The additional text is displayed in a smaller font below <code>title</code>. If you need to
     * display a long question, it can work well to keep the title short and put the additional
     * content in the <code>text</code> property.
     *
     * @return
     */
    public String getText() {
        return text;
    }

    /**
     * Sets the additional text for the step.
     *
     * @param text the detail text for the step
     * @see #getText()
     */
    public void setText(String text) {
        this.text = text;
    }

    /**
     * Gets the int id for the title to display in the action bar (optional).
     *
     * @return the id for the title to display in the action bar
     */
    public int getStepTitle() {
        return stepTitle;
    }

    /**
     * Gets the int id for the title to display in the action bar (optional).
     *
     * @param stepTitle the Android resource id for the title
     */
    public void setStepTitle(int stepTitle) {
        this.stepTitle = stepTitle;
    }

    /**
     * Returns the class that the {@link org.researchstack.backbone.ui.ViewTaskActivity} should
     * instantiate to display this step.
     * <p>
     * This method is used within the framework so that steps can define their step view controller
     * pairing.
     * <p>
     * Outside the framework, developers should instantiate the required view controller in their
     * ViewTaskActivity delegate to override the ViewTaskActivity's default.
     *
     * @return the class of the {@link org.researchstack.backbone.ui.step.layout.StepLayout} for
     * this step
     */
    public Class getStepLayoutClass() {
        return stepLayoutClass;
    }

    /**
     * Sets the class that should be used to display this step
     *
     * @param stepLayoutClass the {@link org.researchstack.backbone.ui.step.layout.StepLayout} class
     *                        to be used to display this step
     */
    public void setStepLayoutClass(Class stepLayoutClass) {
        this.stepLayoutClass = stepLayoutClass;
    }
} public class OrderedTask extends Task implements Serializable {

    protected List<Step> steps;

    /**
     * Returns an initialized ordered task using the specified identifier and array of steps.
     *
     * @param identifier The unique identifier for the task.
     * @param steps      An array of {@link Step} objects in the order in which they should be
     *                   presented.
     */
    public OrderedTask(String identifier, List<Step> steps) {
        super(identifier);
        this.steps = new ArrayList<>(steps);
    }

    /**
     * Returns an initialized ordered task using the specified identifier and array of steps.
     *
     * @param identifier The unique identifier for the task
     * @param steps      The {@link Step} objects in the order in which they should be presented.
     */
    public OrderedTask(String identifier, Step... steps) {
        this(identifier, Arrays.asList(steps));
    }

    /**
     * Returns the next step immediately after the passed in step in the list of steps, or null
     *
     * @param step   The reference step. Pass null to specify the first step.
     * @param result A snapshot of the current set of results.
     * @return the next step in <code>steps</code> after the passed in step, or null if at the end
     */
    @Override
    public Step getStepAfterStep(Step step, TaskResult result) {
        if (step == null) {
            return steps.get(0);
        }

        int nextIndex = steps.indexOf(step) + 1;

        if (nextIndex < steps.size()) {
            return steps.get(nextIndex);
        }

        return null;
    }

    /**
     * Returns the next step immediately before the passed in step in the list of steps, or null
     *
     * @param step   The reference step.
     * @param result A snapshot of the current set of results.
     * @return the next step in <code>steps</code> before the passed in step, or null if at the
     * start
     */
    @Override
    public Step getStepBeforeStep(Step step, TaskResult result) {
        int nextIndex = steps.indexOf(step) - 1;

        if (nextIndex >= 0) {
            return steps.get(nextIndex);
        }

        return null;
    }

    @Override
    public Step getStepWithIdentifier(String identifier) {
        for (Step step : steps) {
            if (identifier.equals(step.getIdentifier())) {
                return step;
            }
        }
        return null;
    }

    @Override
    public TaskProgress getProgressOfCurrentStep(Step step, TaskResult result) {
        int current = step == null ? -1 : steps.indexOf(step);
        return new TaskProgress(current, steps.size());
    }

    /**
     * Returns {@link Step#getStepTitle()} if it exists, otherwise returns string showing progress.
     *
     * @param context for fetching resources
     * @param step    the current step
     * @return the step title, or a progress string if it doesn't have one
     */
    @Override
    public String getTitleForStep(Context context, Step step) {
        String title = super.getTitleForStep(context, step);
        if (TextUtils.isEmpty(title)) {
            int currentIndex = steps.indexOf(step);
            title = context.getString(R.string.rsb_format_step_title,
                    currentIndex + 1,
                    steps.size());
        }
        return title;
    }

    /**
     * Validates that there are no duplicate identifiers in the list of steps
     *
     * @throws org.researchstack.backbone.task.Task.InvalidTaskException
     */
    @Override
    public void validateParameters() {
        Set<String> uniqueIds = new HashSet<>();
        for (Step step : steps) {
            uniqueIds.add(step.getIdentifier());
        }

        if (uniqueIds.size() != steps.size()) {
            throw new InvalidTaskException("OrderedTask has steps with duplicate ids");
        }
    }

    /**
     * Returns a copy of the list of steps.
     *
     * @return a copy of the ordered list of steps in the task
     */
    public List<Step> getSteps() {
        return new ArrayList<>(steps);
    }
} public class TextUtils {
    public static final Pattern EMAIL_ADDRESS = Pattern.compile(
            "[a-zA-Z0-9\\+\\.\\_\\%\\-\\+]{1,256}" +
                    "\\@" +
                    "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,64}" +
                    "(" +
                    "\\." +
                    "[a-zA-Z0-9][a-zA-Z0-9\\-]{0,25}" +
                    ")+");

    private TextUtils() {
    }

    /**
     * Returns true if the string is null or 0-length.
     *
     * @param str the string to be examined
     * @return true if str is null or zero length
     */
    public static boolean isEmpty(CharSequence str) {
        if (str == null || str.length() == 0) {
            return true;
        } else {
            return false;
        }
    }

    /**
     * Returns true if the char sequence is a valid email address
     *
     * @param text the email address to be validated
     * @return a boolean indicating whether the email is valid
     */
    public static boolean isValidEmail(CharSequence text) {
        return !isEmpty(text) && EMAIL_ADDRESS.matcher(text).matches();
    }


    public static class AlphabeticFilter implements InputFilter {
        @Override
        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            for (int i = start; i < end; i++) {
                if (!Character.isLetter(source.charAt(i))) {
                    return "";
                }
            }
            return null;
        }
    }

    public static class NumericFilter implements InputFilter {
        @Override
        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            for (int i = start; i < end; i++) {
                if (!Character.isDigit(source.charAt(i))) {
                    return "";
                }
            }
            return null;
        }
    }

    public static class AlphanumericFilter implements InputFilter {
        @Override
        public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
            for (int i = start; i < end; i++) {
                if (!Character.isLetterOrDigit(source.charAt(i))) {
                    return "";
                }
            }
            return null;
        }
    }
} public class ConsentQuizModel implements Serializable {
    private String failureTitle;
    private String failureMessage;
    private String successTitle;
    private String successMessage;
    private int allowedFailures;
    private List<QuizQuestion> questions;

    // fields with defaults
    private String incorrectIcon = "rsb_quiz_retry";
    private String correctIcon = "rss_ic_quiz_valid";

    public String getFailureTitle() {
        return failureTitle;
    }

    public String getFailureMessage() {
        return failureMessage;
    }

    public String getSuccessTitle() {
        return successTitle;
    }

    public String getSuccessMessage() {
        return successMessage;
    }

    public int getAllowedFailures() {
        return allowedFailures;
    }

    public List<QuizQuestion> getQuestions() {
        return questions;
    }

    public String getIncorrectIcon() {
        return incorrectIcon;
    }

    public String getCorrectIcon() {
        return correctIcon;
    }

    public class QuizQuestion implements Serializable {
        private String identifier;
        private String prompt;
        private String type;
        private String expectedAnswer;
        private String text;
        private List<String> textChoices;
        private String positiveFeedback;
        private String negativeFeedback;

        public String getIdentifier() {
            return identifier;
        }

        public void setIdentifier(String identifier) {
            this.identifier = identifier;
        }

        public String getPrompt() {
            return prompt;
        }

        public String getType() {
            return type;
        }

        public String getExpectedAnswer() {
            return expectedAnswer;
        }

        public String getText() {
            return text;
        }

        public List<String> getTextChoices() {
            return textChoices;
        }

        public String getPositiveFeedback() {
            return positiveFeedback == null ? "" : positiveFeedback;
        }

        public String getNegativeFeedback() {
            return negativeFeedback == null ? "" : negativeFeedback;
        }
    }
} public class ConsentQuizEvaluationStep extends Step {
    private ConsentQuizModel quizModel;

    private int attempt;
    private int incorrect;

    public ConsentQuizEvaluationStep(String identifier, ConsentQuizModel quizModel) {
        super(identifier);
        this.quizModel = quizModel;
    }

    @Override
    public int getStepTitle() {
        return R.string.rsb_quiz_evaluation;
    }

    @Override
    public Class getStepLayoutClass() {
        return ConsentQuizEvaluationStepLayout.class;
    }

    public int getAttempt() {
        return attempt;
    }

    public void setAttempt(int attempt) {
        this.attempt = attempt;
    }

    public void setIncorrectCount(int incorrect) {
        this.incorrect = incorrect;
    }

    public int getIncorrect() {
        return incorrect;
    }

    public ConsentQuizModel getQuizModel() {
        return quizModel;
    }

    public boolean isOverMaxAttempts() {
        return attempt >= 1;
    }

    public boolean isQuizPassed() {
        return incorrect <= quizModel.getAllowedFailures();
    }
}
import android.content.Context;
import android.content.res.Resources;
import org.researchstack.backbone.answerformat.AnswerFormat;
import org.researchstack.backbone.answerformat.BirthDateAnswerFormat;
import org.researchstack.backbone.answerformat.ChoiceAnswerFormat;
import org.researchstack.backbone.answerformat.DateAnswerFormat;
import org.researchstack.backbone.answerformat.TextAnswerFormat;
import org.researchstack.backbone.model.Choice;
import org.researchstack.backbone.model.ConsentDocument;
import org.researchstack.backbone.model.ConsentSection;
import org.researchstack.backbone.model.ConsentSignature;
import org.researchstack.backbone.model.DocumentProperties;
import org.researchstack.backbone.result.StepResult;
import org.researchstack.backbone.result.TaskResult;
import org.researchstack.backbone.step.ConsentDocumentStep;
import org.researchstack.backbone.step.ConsentSharingStep;
import org.researchstack.backbone.step.ConsentSignatureStep;
import org.researchstack.backbone.step.ConsentVisualStep;
import org.researchstack.backbone.step.FormStep;
import org.researchstack.backbone.step.QuestionStep;
import org.researchstack.backbone.step.Step;
import org.researchstack.backbone.task.OrderedTask;
import org.researchstack.backbone.ui.step.layout.ConsentSignatureStepLayout;
import org.researchstack.backbone.utils.LogExt;
import org.researchstack.backbone.utils.TextUtils;
import org.researchstack.skin.R;
import org.researchstack.skin.ResourceManager;
import org.researchstack.skin.model.ConsentQuizModel;
import org.researchstack.skin.model.ConsentSectionModel;
import org.researchstack.skin.step.ConsentQuizEvaluationStep;
import org.researchstack.skin.step.ConsentQuizQuestionStep;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;

        initConsentReviewSteps(context, doc, steps);

        return new ConsentTask(taskId, steps);
    }

    private static void initConsentSharingStep(Resources r, ConsentSectionModel data, List<Step> steps) {
        String investigatorShortDesc = data.getDocumentProperties()
                .getInvestigatorShortDescription();
        if (TextUtils.isEmpty(investigatorShortDesc)) {
            throw new IllegalArgumentException(INVALID_ARGUMENT_CANNOT_BE_NULL);
        }

        String investigatorLongDesc = data.getDocumentProperties().getInvestigatorLongDescription();
        if (TextUtils.isEmpty(investigatorLongDesc)) {
            throw new IllegalArgumentException(INVALID_ARGUMENT_CANNOT_BE_NULL);
        }

        String localizedLearnMoreHTMLContent = data.getDocumentProperties().getHtmlContent();
        if (TextUtils.isEmpty(localizedLearnMoreHTMLContent)) {
            throw new IllegalArgumentException(INVALID_ARGUMENT_CANNOT_BE_NULL);
        }

        ConsentSharingStep sharingStep = new ConsentSharingStep(ID_SHARING);
        sharingStep.setOptional(false);
        sharingStep.setStepTitle(R.string.rsb_consent);
        //        sharingStep.setLocalizedLearnMoreHTMLContent(localizedLearnMoreHTMLContent);

        String shareWidely = r.getString(R.string.rsb_consent_share_widely, investigatorLongDesc);
        Choice<String> shareWidelyChoice = new Choice<>(shareWidely, "sponsors_and_partners", null);

        String shareRestricted = r.getString(R.string.rsb_consent_share_only,
                investigatorShortDesc);
        Choice<String> shareRestrictedChoice = new Choice<>(shareRestricted,
                "all_qualified_researchers",
                null);

        sharingStep.setAnswerFormat(new ChoiceAnswerFormat(AnswerFormat.ChoiceAnswerStyle.SingleChoice,
                shareWidelyChoice,
                shareRestrictedChoice));

        sharingStep.setTitle(r.getString(R.string.rsb_consent_share_title));
        sharingStep.setText(r.getString(R.string.rsb_consent_share_description,
                investigatorLongDesc,
                localizedLearnMoreHTMLContent));

        steps.add(sharingStep);
    }

    private static void initVisualSteps(Context ctx, ConsentDocument doc, List<Step> steps) {
        for (int i = 0, size = doc.getSections().size(); i < size; i++) {
            ConsentSection section = doc.getSections().get(i);

            if (!TextUtils.isEmpty(section.getHtmlContent())) {
                String htmlFilePath = ResourceManager.getInstance()
                        .generatePath(ResourceManager.Resource.TYPE_HTML, section.getHtmlContent());
                section.setHtmlContent(ResourceManager.getResourceAsString(ctx, htmlFilePath));
            }

            ConsentVisualStep step = new ConsentVisualStep("consent_" + i);
            step.setSection(section);

            String nextString = ctx.getString(R.string.rsb_next);
            if (section.getType() == ConsentSection.Type.Overview) {
                nextString = ctx.getString(R.string.rsb_get_started);
            } else if (i == size - 1) {
                nextString = ctx.getString(R.string.rsb_done);
            }
            step.setNextButtonString(nextString);

            steps.add(step);
        }
    }

    private static void initQuizSteps(Context ctx, ConsentQuizModel model, List<Step> steps) {
        if (model == null) {
            LogExt.d(ConsentTask.class, "No quiz specified in consent json, skipping");
            return;
        }

        for (int i = 0; i < model.getQuestions().size(); i++) {
            ConsentQuizModel.QuizQuestion question = model.getQuestions().get(i);

            // We need to overwrite the id of the first question to later find it in our internal
            // map later on. This is done to clear and attain the incorrect question count.
            if (i == 0) {
                question.setIdentifier(ID_FIRST_QUESTION);
            }

            ConsentQuizQuestionStep quizStep = new ConsentQuizQuestionStep(question);
            steps.add(quizStep);
        }

        ConsentQuizEvaluationStep evaluationStep = new ConsentQuizEvaluationStep(ID_QUIZ_RESULT,
                model);
        steps.add(evaluationStep);
    }

    private static void initConsentReviewSteps(Context ctx, ConsentDocument doc, List<Step> steps) {
        // Add ConsentReviewDocumentStep (view html version of the PDF doc)
        StringBuilder docBuilder = new StringBuilder(
                "</br><div style=\"padding: 10px 10px 10px 10px;\" class='header'>");
        String title = ctx.getString(R.string.rsb_consent_review_title);
        docBuilder.append(String.format(
                "<h1 style=\"text-align: center; font-family:sans-serif-light;\">%1$s</h1>",
                title));
        String detail = ctx.getString(R.string.rsb_consent_review_instruction);
        docBuilder.append(String.format("<p style=\"text-align: center\">%1$s</p>", detail));
        docBuilder.append("</div></br>");
        docBuilder.append(doc.getHtmlReviewContent());

        ConsentDocumentStep step = new ConsentDocumentStep(ID_CONSENT_DOC);
        step.setConsentHTML(docBuilder.toString());
        step.setConfirmMessage(ctx.getString(R.string.rsb_consent_review_reason));
        steps.add(step);

        // Add full-name input
        boolean requiresName = doc.getSignature(0).requiresName();
        boolean requiresBirthDate = doc.getSignature(0).requiresBirthDate();
        if (requiresName || requiresBirthDate) {
            List<QuestionStep> formSteps = new ArrayList<>();