diff --git a/app/build.gradle b/app/build.gradle index 3e5c206f4e..ab11e018fa 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -32,7 +32,7 @@ android { minSdkVersion 19 targetSdkVersion 27 versionCode 33 - versionName "1.0.2" + versionName "1.0.3" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" vectorDrawables.useSupportLibrary = true multiDexEnabled true diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 5657cbd593..6fe589a62f 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,4 +1,3 @@ - @@ -35,11 +34,8 @@ - - - @@ -97,6 +93,10 @@ android:name="io.fabric.ApiKey" android:value="a5caa46009119f5e584e4964c30a922095b1075c" /> + + + + diff --git a/app/src/main/java/org/dhis2/Bindings/Bindings.java b/app/src/main/java/org/dhis2/Bindings/Bindings.java index 248adfe0a3..4016abc32f 100644 --- a/app/src/main/java/org/dhis2/Bindings/Bindings.java +++ b/app/src/main/java/org/dhis2/Bindings/Bindings.java @@ -33,7 +33,6 @@ import org.dhis2.data.tuples.Pair; import org.dhis2.utils.CatComboAdapter; import org.dhis2.utils.DateUtils; -import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.category.CategoryOptionComboModel; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.enrollment.EnrollmentModel; @@ -429,7 +428,7 @@ public static void setEventIcon(ImageView view, EventModel event, EnrollmentMode .observeOn(AndroidSchedulers.mainThread()) .subscribe( program -> { - if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType()!=null?eventProgramStage.periodType():program.expiryPeriodType())) { + if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType() != null ? eventProgramStage.periodType() : program.expiryPeriodType())) { view.setImageDrawable(ContextCompat.getDrawable(view.getContext(), R.drawable.ic_eye_red)); } else { view.setImageDrawable(ContextCompat.getDrawable(view.getContext(), R.drawable.ic_edit)); @@ -479,7 +478,7 @@ public static void setEventText(TextView view, EventModel event, EnrollmentModel .observeOn(AndroidSchedulers.mainThread()) .subscribe( program -> { - if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType()!=null?eventProgramStage.periodType():program.expiryPeriodType())) { + if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType() != null ? eventProgramStage.periodType() : program.expiryPeriodType())) { view.setText(view.getContext().getString(R.string.event_expired)); } else { view.setText(view.getContext().getString(R.string.event_open)); @@ -495,7 +494,7 @@ public static void setEventText(TextView view, EventModel event, EnrollmentModel .observeOn(AndroidSchedulers.mainThread()) .subscribe( program -> { - if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType()!=null?eventProgramStage.periodType():program.expiryPeriodType())) { + if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType() != null ? eventProgramStage.periodType() : program.expiryPeriodType())) { view.setText(view.getContext().getString(R.string.event_expired)); } else { view.setText(view.getContext().getString(R.string.event_completed)); @@ -511,7 +510,7 @@ public static void setEventText(TextView view, EventModel event, EnrollmentModel .observeOn(AndroidSchedulers.mainThread()) .subscribe( program -> { - if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType()!=null?eventProgramStage.periodType():program.expiryPeriodType())) { + if (DateUtils.getInstance().hasExpired(event, program.expiryDays(), program.completeEventsExpiryDays(), eventProgramStage.periodType() != null ? eventProgramStage.periodType() : program.expiryPeriodType())) { view.setText(view.getContext().getString(R.string.event_expired)); } else { view.setText(view.getContext().getString(R.string.event_schedule)); @@ -631,9 +630,11 @@ public static void setCategoryOptionComboName(TextView textView, String category .observeOn(AndroidSchedulers.mainThread()) .subscribe( categoryOptionModel -> { - if (!CategoryComboModel.DEFAULT_UID.equals(categoryOptionModel.uid())) { + if (!categoryOptionModel.isDefault()) { + textView.setVisibility(View.VISIBLE); textView.setText(categoryOptionComboModel.displayName()); } else { + textView.setVisibility(View.GONE); textView.setText(""); } }, diff --git a/app/src/main/java/org/dhis2/data/forms/EnrollmentFormRepository.java b/app/src/main/java/org/dhis2/data/forms/EnrollmentFormRepository.java index 7dffa38849..0d8e1ae1c7 100644 --- a/app/src/main/java/org/dhis2/data/forms/EnrollmentFormRepository.java +++ b/app/src/main/java/org/dhis2/data/forms/EnrollmentFormRepository.java @@ -22,6 +22,7 @@ import org.hisp.dhis.android.core.period.PeriodType; import org.hisp.dhis.android.core.program.ProgramModel; import org.hisp.dhis.android.core.program.ProgramStageModel; +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeModel; import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueModel; import org.hisp.dhis.android.core.trackedentity.TrackedEntityInstanceModel; import org.hisp.dhis.rules.RuleEngine; @@ -76,27 +77,56 @@ class EnrollmentFormRepository implements FormRepository { private static final String SELECT_INCIDENT_DATE = "SELECT Enrollment.* FROM Enrollment WHERE Enrollment.uid = ? LIMIT 1"; - private static final String SELECT_AUTO_GENERATE_PROGRAM_STAGE = "SELECT ProgramStage.uid, " + - "Program.uid, Enrollment.organisationUnit, ProgramStage.minDaysFromStart, ProgramStage.generatedByEnrollmentDate, Enrollment.incidentDate, Enrollment.enrollmentDate, ProgramStage.periodType \n" + + private static final String SELECT_AUTO_GENERATE_PROGRAM_STAGE = "SELECT " + + "ProgramStage.uid, " + + "Program.uid, " + + "Enrollment.organisationUnit, " + + "ProgramStage.minDaysFromStart, " + + "ProgramStage.generatedByEnrollmentDate, " + + "Enrollment.incidentDate, " + + "Enrollment.enrollmentDate, " + + "ProgramStage.periodType \n" + "FROM Enrollment\n" + " JOIN Program ON Enrollment.program = Program.uid\n" + - " JOIN ProgramStage ON Program.uid = ProgramStage.program AND ProgramStage.autoGenerateEvent = 1\n" + - "WHERE Enrollment.uid = ?"; + " JOIN ProgramStage ON Program.uid = ProgramStage.program \n" + + "WHERE Enrollment.uid = ? AND ProgramStage.autoGenerateEvent = 1"; + + private static final String GET_FIRST_STAGE = "SELECT " + + "ProgramStage.* FROM ProgramStage " + + "WHERE ProgramStage.program = ? " + + "AND ProgramStage.sortOrder != 0 " + + "ORDER BY ProgramStage.sortOrder ASC LIMIT 1"; + private static final String CHECK_IF_FIRST_STAGE_EVENT_EXIST_IN_ENROLLMENT = "SELECT " + + "Event.uid, " + + "Program.trackedEntityType "+ + "FROM Event " + + "JOIN Enrollment ON Enrollment.uid = Event.enrollment " + + "JOIN Program ON Program.uid = Enrollment.program " + + "WHERE Event.programStage = ? AND Enrollment.uid = ?"; private static final String SELECT_USE_FIRST_STAGE = - "SELECT ProgramStage.uid, ProgramStage.program, Enrollment.organisationUnit, Program.trackedEntityType, Event.uid\n" + + "SELECT ProgramStage.uid, " + + "ProgramStage.program, " + + "Enrollment.organisationUnit, " + + "Program.trackedEntityType, " + + "Event.uid\n" + "FROM Enrollment\n" + " JOIN Program ON Enrollment.program = Program.uid\n" + " JOIN ProgramStage ON Program.uid = ProgramStage.program\n" + " JOIN Event ON event.enrollment = Enrollment.uid\n" + - "WHERE Enrollment.uid = ? AND ProgramStage.sortOrder = 1 AND (Program.useFirstStageDuringRegistration = 1 OR ProgramStage.openAfterEnrollment = 1)"; + "WHERE Enrollment.uid = ? AND (Program.useFirstStageDuringRegistration = 1 OR ProgramStage.openAfterEnrollment = 1) " + + "ORDER BY ProgramStage.sortOrder ASC LIMIT 1"; private static final String SELECT_USE_FIRST_STAGE_WITHOUT_AUTOGENERATE_EVENT = - "SELECT ProgramStage.uid, ProgramStage.program, Enrollment.organisationUnit, Program.trackedEntityType\n" + + "SELECT ProgramStage.uid, " + + "ProgramStage.program, " + + "Enrollment.organisationUnit, " + + "Program.trackedEntityType\n" + "FROM Enrollment\n" + " JOIN Program ON Enrollment.program = Program.uid\n" + " JOIN ProgramStage ON Program.uid = ProgramStage.program\n" + - "WHERE Enrollment.uid = ? AND ProgramStage.sortOrder = 1 AND (Program.useFirstStageDuringRegistration = 1 OR ProgramStage.openAfterEnrollment = 1)"; + "WHERE Enrollment.uid = ? AND (Program.useFirstStageDuringRegistration = 1 OR ProgramStage.openAfterEnrollment = 1) " + + "ORDER BY ProgramStage.sortOrder ASC"; private static final String SELECT_PROGRAM = "SELECT \n" + " program\n" + @@ -113,6 +143,7 @@ class EnrollmentFormRepository implements FormRepository { private static final String SELECT_VALUES = "SELECT TrackedEntityAttributeValue.value FROM TrackedEntityAttributeValue " + "JOIN TrackedEntityInstance ON TrackedEntityInstance.uid = TrackedEntityAttributeValue.trackedEntityInstance " + "JOIN Enrollment ON Enrollment.trackedEntityInstance = TrackedEntityInstance.uid WHERE Enrollment.uid = ?"; + private static final String QUERY = "SELECT \n" + " Field.id,\n" + " Field.label,\n" + @@ -120,11 +151,12 @@ class EnrollmentFormRepository implements FormRepository { " Field.mandatory,\n" + " Field.optionSet,\n" + " Value.value,\n" + - " Option.name,\n" + + " Option.displayName,\n" + " Field.allowFutureDate,\n" + " Field.generated,\n" + " Enrollment.organisationUnit,\n" + - " Enrollment.status\n" + + " Enrollment.status,\n" + + " Field.displayDescription\n" + "FROM (Enrollment INNER JOIN Program ON Program.uid = Enrollment.program)\n" + " LEFT OUTER JOIN (\n" + " SELECT\n" + @@ -135,7 +167,8 @@ class EnrollmentFormRepository implements FormRepository { " ProgramTrackedEntityAttribute.program AS program,\n" + " ProgramTrackedEntityAttribute.mandatory AS mandatory,\n" + " ProgramTrackedEntityAttribute.allowFutureDate AS allowFutureDate,\n" + - " TrackedEntityAttribute.generated AS generated\n" + + " TrackedEntityAttribute.generated AS generated,\n" + + " TrackedEntityAttribute.displayDescription AS displayDescription\n" + " FROM ProgramTrackedEntityAttribute INNER JOIN TrackedEntityAttribute\n" + " ON TrackedEntityAttribute.uid = ProgramTrackedEntityAttribute.trackedEntityAttribute\n" + " ) AS Field ON Field.program = Program.uid\n" + @@ -488,6 +521,7 @@ private FieldViewModel transform(@NonNull Cursor cursor) { String section = cursor.getString(7); Boolean allowFutureDates = cursor.getInt(8) == 1; EnrollmentStatus status = EnrollmentStatus.valueOf(cursor.getString(10)); + String description = cursor.getString(11); if (!isEmpty(optionCodeName)) { dataValue = optionCodeName; } @@ -504,13 +538,63 @@ private FieldViewModel transform(@NonNull Cursor cursor) { ""); return fieldFactory.create(uid, label, valueType, mandatory, optionSetUid, dataValue, section, - allowFutureDates, status == EnrollmentStatus.ACTIVE, null); + allowFutureDates, status == EnrollmentStatus.ACTIVE, null,description); } @NonNull @Override public Observable> useFirstStageDuringRegistration() { //EnrollmentUid, - return briteDatabase.createQuery(ProgramStageModel.TABLE, SELECT_USE_FIRST_STAGE, enrollmentUid == null ? "" : enrollmentUid) + + return briteDatabase.createQuery(ProgramStageModel.TABLE, GET_FIRST_STAGE, programUid) + .mapToOne(ProgramStageModel::create) + .map(programStage->{ + Cursor eventCursor = briteDatabase.query(CHECK_IF_FIRST_STAGE_EVENT_EXIST_IN_ENROLLMENT,programStage.uid(),enrollmentUid); + if(eventCursor!=null && eventCursor.moveToFirst()){ //Event exist + return Trio.create(enrollmentUid,eventCursor.getString(1),eventCursor.getString(0)); + }else{//Event does not exist + Cursor newCursor = briteDatabase.query(SELECT_USE_FIRST_STAGE_WITHOUT_AUTOGENERATE_EVENT, enrollmentUid); + if (newCursor.moveToFirst()) { + String programStageUid = programStage.uid(); + String programStageProgram = programStage.program(); + String enrollmentOrgUnit = newCursor.getString(2); + String trackedEntityType = newCursor.getString(3); + + Calendar cal = Calendar.getInstance(); + cal.setTime(cal.getTime()); + cal.set(Calendar.HOUR_OF_DAY, 0); + cal.set(Calendar.MINUTE, 0); + cal.set(Calendar.SECOND, 0); + cal.set(Calendar.MILLISECOND, 0); + + Date createdDate = Calendar.getInstance().getTime(); + EventModel event = EventModel.builder() + .uid(codeGenerator.generate()) + .created(createdDate) + .lastUpdated(createdDate) + .eventDate(cal.getTime()) + .enrollment(enrollmentUid) + .program(programStageProgram) + .programStage(programStageUid) + .organisationUnit(enrollmentOrgUnit) + .status(EventStatus.ACTIVE) + .state(State.TO_POST) + .build(); + + if (briteDatabase.insert(EventModel.TABLE, event.toContentValues()) < 0) { + throw new OnErrorNotImplementedException(new Throwable("Unable to store event:" + event)); + } + updateProgramTable(createdDate, programStageProgram); + return Trio.create(enrollmentUid, trackedEntityType, event.uid()); + } else { + Cursor tetCursor = briteDatabase.query(SELECT_TE_TYPE, enrollmentUid == null ? "" : enrollmentUid); + tetCursor.moveToFirst(); + + return Trio.create(tetCursor.getString(0), tetCursor.getString(1), ""); + } + }}); + + + /*return briteDatabase.createQuery(ProgramStageModel.TABLE, SELECT_USE_FIRST_STAGE, enrollmentUid == null ? "" : enrollmentUid) .map(query -> { String trackedEntityType = ""; String eventUid; @@ -561,7 +645,7 @@ public Observable> useFirstStageDuringRegistration( return Trio.create(tetCursor.getString(0), tetCursor.getString(1), ""); } } - }); + });*/ } @NonNull diff --git a/app/src/main/java/org/dhis2/data/forms/EventRepository.java b/app/src/main/java/org/dhis2/data/forms/EventRepository.java index 0be3690103..aca3380933 100644 --- a/app/src/main/java/org/dhis2/data/forms/EventRepository.java +++ b/app/src/main/java/org/dhis2/data/forms/EventRepository.java @@ -5,14 +5,14 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.google.android.gms.maps.model.LatLng; +import com.squareup.sqlbrite2.BriteDatabase; + import org.dhis2.data.forms.dataentry.fields.FieldViewModel; import org.dhis2.data.forms.dataentry.fields.FieldViewModelFactoryImpl; import org.dhis2.data.tuples.Pair; import org.dhis2.data.tuples.Trio; import org.dhis2.utils.DateUtils; -import com.google.android.gms.maps.model.LatLng; -import com.squareup.sqlbrite2.BriteDatabase; - import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.common.ValueType; import org.hisp.dhis.android.core.enrollment.EnrollmentModel; @@ -30,7 +30,6 @@ import java.util.Arrays; import java.util.Calendar; import java.util.Date; -import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -105,11 +104,14 @@ public class EventRepository implements FormRepository { " Field.mandatory,\n" + " Field.optionSet,\n" + " Value.value,\n" + - " Option.name,\n" + + " Option.displayName,\n" + " Field.section,\n" + " Field.allowFutureDate,\n" + " Event.status,\n" + - " Field.formLabel\n" + + " Field.formLabel,\n" + + " Field.displayDescription,\n" + + " Field.formOrder,\n" + + " Field.sectionOrder\n" + "FROM Event\n" + " LEFT OUTER JOIN (\n" + " SELECT\n" + @@ -121,10 +123,13 @@ public class EventRepository implements FormRepository { " ProgramStageDataElement.sortOrder AS formOrder,\n" + " ProgramStageDataElement.programStage AS stage,\n" + " ProgramStageDataElement.compulsory AS mandatory,\n" + - " ProgramStageDataElement.programStageSection AS section,\n" + - " ProgramStageDataElement.allowFutureDate AS allowFutureDate\n" + + " ProgramStageSectionDataElementLink.programStageSection AS section,\n" + + " ProgramStageDataElement.allowFutureDate AS allowFutureDate,\n" + + " DataElement.displayDescription AS displayDescription,\n" + + " ProgramStageSectionDataElementLink.sortOrder AS sectionOrder\n" + " FROM ProgramStageDataElement\n" + " INNER JOIN DataElement ON DataElement.uid = ProgramStageDataElement.dataElement\n" + + " LEFT JOIN ProgramStageSectionDataElementLink ON ProgramStageSectionDataElementLink.dataElement = ProgramStageDataElement.dataElement\n" + " ) AS Field ON (Field.stage = Event.programStage)\n" + " LEFT OUTER JOIN TrackedEntityDataValue AS Value ON (\n" + " Value.event = Event.uid AND Value.dataElement = Field.id\n" + @@ -133,7 +138,10 @@ public class EventRepository implements FormRepository { " Field.optionSet = Option.optionSet AND Value.value = Option.code\n" + " )\n" + " %s " + - "ORDER BY Field.formOrder ASC;"; + "ORDER BY CASE" + + " WHEN Field.sectionOrder IS NULL THEN Field.formOrder" + + " WHEN Field.sectionOrder IS NOT NULL THEN Field.sectionOrder" + + " END ASC;"; @NonNull private final BriteDatabase briteDatabase; @@ -350,7 +358,6 @@ public Observable getTrackedEntityInstanceUid() { private FieldViewModel transform(@NonNull Cursor cursor) { String uid = cursor.getString(0); String label = cursor.getString(1); - String formLabel = cursor.getString(10); ValueType valueType = ValueType.valueOf(cursor.getString(2)); boolean mandatory = cursor.getInt(3) == 1; String optionSetUid = cursor.getString(4); @@ -359,6 +366,8 @@ private FieldViewModel transform(@NonNull Cursor cursor) { String section = cursor.getString(7); Boolean allowFutureDates = cursor.getInt(8) == 1; EventStatus status = EventStatus.valueOf(cursor.getString(9)); + String formLabel = cursor.getString(10); + String description = cursor.getString(11); if (!isEmpty(optionCodeName)) { dataValue = optionCodeName; } @@ -374,7 +383,9 @@ private FieldViewModel transform(@NonNull Cursor cursor) { "", ""); - return fieldFactory.create(uid, isEmpty(formLabel) ? label : formLabel, valueType, mandatory, optionSetUid, dataValue, section, allowFutureDates, status == EventStatus.ACTIVE, null); + return fieldFactory.create(uid, isEmpty(formLabel) ? label : formLabel, valueType, + mandatory, optionSetUid, dataValue, section, allowFutureDates, + status == EventStatus.ACTIVE, null, description); } @NonNull diff --git a/app/src/main/java/org/dhis2/data/forms/FormFragment.java b/app/src/main/java/org/dhis2/data/forms/FormFragment.java index 09f79c2379..c613d40eaf 100644 --- a/app/src/main/java/org/dhis2/data/forms/FormFragment.java +++ b/app/src/main/java/org/dhis2/data/forms/FormFragment.java @@ -89,6 +89,7 @@ public class FormFragment extends FragmentGlobalAbstract implements FormView, Co private String messageOnComplete = ""; private boolean canComplete = true; private LinearLayout dateLayout; + private View datesLayout; private NestedScrollView nestedScrollView; private final int RQ_EVENT = 9876; private RuleActionErrorOnCompletion errorOnCompletion; @@ -140,7 +141,7 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat tabLayout = view.findViewById(R.id.tablayout_data_entry); toolbar = view.findViewById(R.id.toolbar); View appBarLayout = view.findViewById(R.id.appbarlayout_data_entry); - View datesLayout = view.findViewById(R.id.data_entry_dates); + datesLayout = view.findViewById(R.id.data_entry_dates); reportDate = view.findViewById(R.id.report_date); incidentDateLayout = view.findViewById(R.id.incident_date_layout); incidentDate = view.findViewById(R.id.incident_date_text); @@ -357,7 +358,7 @@ public void renderStatusChangeSnackBar(@NonNull ReportStatus reportStatus) { private ReportStatus getReportStatusFromButton() { - dateLayout.requestFocus(); + datesLayout.requestFocus(); return nextButton.isActivated() ? ReportStatus.ACTIVE : ReportStatus.COMPLETED; } diff --git a/app/src/main/java/org/dhis2/data/forms/RulesRepository.java b/app/src/main/java/org/dhis2/data/forms/RulesRepository.java index ef9bf99d86..b107eac158 100644 --- a/app/src/main/java/org/dhis2/data/forms/RulesRepository.java +++ b/app/src/main/java/org/dhis2/data/forms/RulesRepository.java @@ -222,6 +222,12 @@ public Flowable> ruleVariables(@NonNull String programUid) { .mapToList(RulesRepository::mapToRuleVariable).toFlowable(BackpressureStrategy.LATEST); } + @NonNull + public Flowable> ruleVariablesProgramStages(@NonNull String programUid) { + return briteDatabase.createQuery(ProgramRuleVariableModel.TABLE, QUERY_VARIABLES, programUid == null ? "" : programUid) + .mapToList(RulesRepository::mapToRuleVariableProgramStages).toFlowable(BackpressureStrategy.LATEST); + } + @NonNull private Flowable>> queryRules( @NonNull String programUid) { @@ -349,6 +355,50 @@ private static RuleVariable mapToRuleVariable(@NonNull Cursor cursor) { } } + @NonNull + private static RuleVariable mapToRuleVariableProgramStages(@NonNull Cursor cursor) { + String name = cursor.getString(0); + String stage = cursor.getString(1); + String sourceType = cursor.getString(2); + String dataElement = cursor.getString(3); + String attribute = cursor.getString(4); + + // Mime types of the attribute and data element. + String attributeType = cursor.getString(5); + String elementType = cursor.getString(6); + + // String representation of value type. + RuleValueType mimeType = null; + if (!isEmpty(attributeType)) { + mimeType = convertType(attributeType); + } else if (!isEmpty(elementType)) { + mimeType = convertType(elementType); + } + + if (mimeType == null) +// throw new IllegalArgumentException(String.format("No ValueType was supplied attributeType=%s, elementType=%s, mimeTye =%s", attributeType, elementType, mimeType)); + mimeType = RuleValueType.TEXT; + + switch (ProgramRuleVariableSourceType.valueOf(sourceType)) { + case TEI_ATTRIBUTE: + return RuleVariableAttribute.create(name, attribute, mimeType); + case DATAELEMENT_CURRENT_EVENT: + case DATAELEMENT_NEWEST_EVENT_PROGRAM: + return RuleVariableNewestEvent.create(name, dataElement, mimeType); + case DATAELEMENT_NEWEST_EVENT_PROGRAM_STAGE: + if (stage == null) + stage = ""; + return RuleVariableNewestStageEvent.create(name, dataElement, stage, mimeType); + case DATAELEMENT_PREVIOUS_EVENT: + return RuleVariablePreviousEvent.create(name, dataElement, mimeType); + case CALCULATED_VALUE: + return RuleVariableCalculatedValue.create(name); + default: + throw new IllegalArgumentException("Unsupported variable " + + "source type: " + sourceType); + } + } + @NonNull private static RuleValueType convertType(@NonNull String type) { ValueType valueType = ValueType.valueOf(type); diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryAdapter.java b/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryAdapter.java index c9422e5664..a7f71f8849 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryAdapter.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryAdapter.java @@ -38,6 +38,7 @@ import java.util.ArrayList; import java.util.List; +import java.util.concurrent.TimeUnit; import io.reactivex.Observable; import io.reactivex.processors.FlowableProcessor; diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryPresenterImpl.java b/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryPresenterImpl.java index 6159d8bac1..80fa2a49ca 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryPresenterImpl.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/DataEntryPresenterImpl.java @@ -8,7 +8,6 @@ import org.dhis2.data.schedulers.SchedulerProvider; import org.dhis2.utils.CodeGenerator; import org.dhis2.utils.Result; - import org.hisp.dhis.android.core.common.ValueType; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; import org.hisp.dhis.rules.models.RuleAction; @@ -181,13 +180,13 @@ private void applyRuleEffects(Map fieldViewModels, Resul String uid = codeGenerator.generate(); RuleActionDisplayText displayText = (RuleActionDisplayText) ruleAction; EditTextViewModel textViewModel = EditTextViewModel.create(uid, - displayText.content(), false, ruleEffect.data(), "Information", 1, ValueType.TEXT, null, false); + displayText.content(), false, ruleEffect.data(), "Information", 1, ValueType.TEXT, null, false, null); fieldViewModels.put(uid, textViewModel); } else if (ruleAction instanceof RuleActionDisplayKeyValuePair) { String uid = codeGenerator.generate(); RuleActionDisplayKeyValuePair displayKeyValuePair = (RuleActionDisplayKeyValuePair) ruleAction; EditTextViewModel textViewModel = EditTextViewModel.create(uid, - displayKeyValuePair.content(), false, ruleEffect.data(), "Information", 1, ValueType.TEXT, null, false); + displayKeyValuePair.content(), false, ruleEffect.data(), "Information", 1, ValueType.TEXT, null, false,null); fieldViewModels.put(uid, textViewModel); } else if (ruleAction instanceof RuleActionHideSection) { diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/EnrollmentRepository.java b/app/src/main/java/org/dhis2/data/forms/dataentry/EnrollmentRepository.java index 6790b15e9b..99dde63082 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/EnrollmentRepository.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/EnrollmentRepository.java @@ -6,10 +6,10 @@ import android.support.annotation.NonNull; import android.util.Log; -import org.dhis2.data.forms.dataentry.fields.FieldViewModel; -import org.dhis2.data.forms.dataentry.fields.FieldViewModelFactory; import com.squareup.sqlbrite2.BriteDatabase; +import org.dhis2.data.forms.dataentry.fields.FieldViewModel; +import org.dhis2.data.forms.dataentry.fields.FieldViewModelFactory; import org.hisp.dhis.android.core.D2; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.common.D2CallException; @@ -37,11 +37,12 @@ final class EnrollmentRepository implements DataEntryRepository { " Field.mandatory,\n" + " Field.optionSet,\n" + " Value.value,\n" + - " Option.name,\n" + + " Option.displayName,\n" + " Field.allowFutureDate,\n" + " Field.generated,\n" + " Enrollment.organisationUnit,\n" + - " Enrollment.status\n" + + " Enrollment.status,\n" + + " Field.displayDescription\n" + "FROM (Enrollment INNER JOIN Program ON Program.uid = Enrollment.program)\n" + " LEFT OUTER JOIN (\n" + " SELECT\n" + @@ -52,7 +53,8 @@ final class EnrollmentRepository implements DataEntryRepository { " ProgramTrackedEntityAttribute.program AS program,\n" + " ProgramTrackedEntityAttribute.mandatory AS mandatory,\n" + " ProgramTrackedEntityAttribute.allowFutureDate AS allowFutureDate,\n" + - " TrackedEntityAttribute.generated AS generated\n" + + " TrackedEntityAttribute.generated AS generated,\n" + + " TrackedEntityAttribute.displayDescription AS displayDescription\n" + " FROM ProgramTrackedEntityAttribute INNER JOIN TrackedEntityAttribute\n" + " ON TrackedEntityAttribute.uid = ProgramTrackedEntityAttribute.trackedEntityAttribute\n" + " ) AS Field ON Field.program = Program.uid\n" + @@ -113,7 +115,7 @@ private FieldViewModel transform(@NonNull Cursor cursor) { String optionCodeName = cursor.getString(6); EnrollmentStatus enrollmentStatus = EnrollmentStatus.valueOf(cursor.getString(10)); - + String description = cursor.getString(11); if (!isEmpty(optionCodeName)) { dataValue = optionCodeName; } @@ -129,14 +131,14 @@ private FieldViewModel transform(@NonNull Cursor cursor) { tei.close(); } - if(teiUid!=null) { //checks if tei has been deleted + if (teiUid != null) { //checks if tei has been deleted dataValue = d2.popTrackedEntityAttributeReservedValue(uid, orgUnitUid); //Checks if ValueType is Numeric and that it start with a 0, then removes the 0 - if(valueType == ValueType.NUMBER) - while(dataValue.startsWith("0")){ + if (valueType == ValueType.NUMBER) + while (dataValue.startsWith("0")) { dataValue = d2.popTrackedEntityAttributeReservedValue(uid, orgUnitUid); - } + } String INSERT = "INSERT INTO TrackedEntityAttributeValue\n" + "(lastUpdated, value, trackedEntityAttribute, trackedEntityInstance)\n" + @@ -160,7 +162,7 @@ private FieldViewModel transform(@NonNull Cursor cursor) { return fieldFactory.create(uid, label, valueType, mandatory, optionSet, dataValue, null, allowFutureDates, - !generated && enrollmentStatus == EnrollmentStatus.ACTIVE, null); + !generated && enrollmentStatus == EnrollmentStatus.ACTIVE, null, description); } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/ProgramStageRepository.java b/app/src/main/java/org/dhis2/data/forms/dataentry/ProgramStageRepository.java index b144f63147..c5972c2ee1 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/ProgramStageRepository.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/ProgramStageRepository.java @@ -6,10 +6,10 @@ import android.support.annotation.Nullable; import android.util.Log; -import org.dhis2.data.forms.dataentry.fields.FieldViewModel; -import org.dhis2.data.forms.dataentry.fields.FieldViewModelFactory; import com.squareup.sqlbrite2.BriteDatabase; +import org.dhis2.data.forms.dataentry.fields.FieldViewModel; +import org.dhis2.data.forms.dataentry.fields.FieldViewModelFactory; import org.hisp.dhis.android.core.common.ValueType; import org.hisp.dhis.android.core.event.EventStatus; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; @@ -38,11 +38,14 @@ final class ProgramStageRepository implements DataEntryRepository { " Field.mandatory,\n" + " Field.optionSet,\n" + " Value.value,\n" + - " Option.name,\n" + + " Option.displayName,\n" + " Field.section,\n" + " Field.allowFutureDate,\n" + " Event.status,\n" + - " Field.formLabel\n" + + " Field.formLabel,\n" + + " Field.displayDescription,\n" + + " Field.formOrder,\n" + + " Field.sectionOrder\n" + "FROM Event\n" + " LEFT OUTER JOIN (\n" + " SELECT\n" + @@ -54,10 +57,13 @@ final class ProgramStageRepository implements DataEntryRepository { " ProgramStageDataElement.sortOrder AS formOrder,\n" + " ProgramStageDataElement.programStage AS stage,\n" + " ProgramStageDataElement.compulsory AS mandatory,\n" + - " ProgramStageDataElement.programStageSection AS section,\n" + - " ProgramStageDataElement.allowFutureDate AS allowFutureDate\n" + + " ProgramStageSectionDataElementLink.programStageSection AS section,\n" + + " ProgramStageDataElement.allowFutureDate AS allowFutureDate,\n" + + " DataElement.displayDescription AS displayDescription,\n" + + " ProgramStageSectionDataElementLink.sortOrder AS sectionOrder\n" + //This should override dataElement formOrder " FROM ProgramStageDataElement\n" + " INNER JOIN DataElement ON DataElement.uid = ProgramStageDataElement.dataElement\n" + + " LEFT JOIN ProgramStageSectionDataElementLink ON ProgramStageSectionDataElementLink.dataElement = ProgramStageDataElement.dataElement\n" + " ) AS Field ON (Field.stage = Event.programStage)\n" + " LEFT OUTER JOIN TrackedEntityDataValue AS Value ON (\n" + " Value.event = Event.uid AND Value.dataElement = Field.id\n" + @@ -66,7 +72,10 @@ final class ProgramStageRepository implements DataEntryRepository { " Field.optionSet = Option.optionSet AND Value.value = Option.code\n" + " )\n" + " %s " + - "ORDER BY Field.formOrder ASC;"; + "ORDER BY CASE" + + " WHEN Field.sectionOrder IS NULL THEN Field.formOrder" + + " WHEN Field.sectionOrder IS NOT NULL THEN Field.sectionOrder" + + " END ASC;"; private static final String SECTION_RENDERING_TYPE = "SELECT ProgramStageSection.mobileRenderType FROM ProgramStageSection WHERE ProgramStageSection.uid = ?"; private static final String ACCESS_QUERY = "SELECT ProgramStage.accessDataWrite FROM ProgramStage JOIN Event ON Event.programStage = ProgramStage.uid WHERE Event.uid = ?"; @@ -146,7 +155,7 @@ private List checkRenderType(List fieldViewModel fieldViewModel.uid() + "." + uid, //fist displayName, ValueType.TEXT, false, fieldViewModel.optionSet(), fieldViewModel.value(), fieldViewModel.programStageSection(), - fieldViewModel.allowFutureDate(), fieldViewModel.editable() == null ? false : fieldViewModel.editable(), renderingType)); + fieldViewModel.allowFutureDate(), fieldViewModel.editable() == null ? false : fieldViewModel.editable(), renderingType, fieldViewModel.description())); cursor.moveToNext(); } @@ -198,13 +207,14 @@ private FieldViewModel transform(@NonNull Cursor cursor) { Boolean allowFutureDates = cursor.getInt(8) == 1; EventStatus eventStatus = EventStatus.valueOf(cursor.getString(9)); String formLabel = cursor.getString(10); + String description = cursor.getString(11); if (!isEmpty(optionCodeName)) { dataValue = optionCodeName; } return fieldFactory.create(uid, isEmpty(formLabel) ? label : formLabel, valueType, mandatory, optionSetUid, dataValue, section, - allowFutureDates, accessDataWrite && eventStatus == EventStatus.ACTIVE, renderingType); + allowFutureDates, accessDataWrite && eventStatus == EventStatus.ACTIVE, renderingType, description); } @NonNull diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModel.java index cbb5224c99..05fcf6230e 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModel.java @@ -42,4 +42,7 @@ public abstract class FieldViewModel { @NonNull public abstract FieldViewModel withError(@NonNull String error); + + @Nullable + public abstract String description(); } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactory.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactory.java index c262f15c7c..140b456d3b 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactory.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactory.java @@ -9,8 +9,15 @@ public interface FieldViewModelFactory { @NonNull - FieldViewModel create(@NonNull String id, @NonNull String label, @NonNull ValueType valueType, - @NonNull Boolean mandatory, @Nullable String optionSet, @Nullable String value, - @Nullable String programStageSection, @Nullable Boolean AllowFutureDate, - @NonNull Boolean editable, @Nullable ProgramStageSectionRenderingType renderingType); + FieldViewModel create(@NonNull String id, + @NonNull String label, + @NonNull ValueType valueType, + @NonNull Boolean mandatory, + @Nullable String optionSet, + @Nullable String value, + @Nullable String programStageSection, + @Nullable Boolean AllowFutureDate, + @NonNull Boolean editable, + @Nullable ProgramStageSectionRenderingType renderingType, + @Nullable String description); } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactoryImpl.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactoryImpl.java index 5938e8c7b1..3ed679f851 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactoryImpl.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FieldViewModelFactoryImpl.java @@ -12,7 +12,6 @@ import org.dhis2.data.forms.dataentry.fields.radiobutton.RadioButtonViewModel; import org.dhis2.data.forms.dataentry.fields.spinner.SpinnerViewModel; import org.dhis2.data.forms.dataentry.fields.unsupported.UnsupportedViewModel; - import org.hisp.dhis.android.core.common.ValueType; import org.hisp.dhis.android.core.program.ProgramStageSectionRenderingType; @@ -72,19 +71,20 @@ public FieldViewModelFactoryImpl(@NonNull String hintEnterText, @NonNull String }) public FieldViewModel create(@NonNull String id, @NonNull String label, @NonNull ValueType type, @NonNull Boolean mandatory, @Nullable String optionSet, @Nullable String value, - @Nullable String section, @Nullable Boolean allowFutureDates, @NonNull Boolean editable, @Nullable ProgramStageSectionRenderingType renderingType) { + @Nullable String section, @Nullable Boolean allowFutureDates, @NonNull Boolean editable, @Nullable ProgramStageSectionRenderingType renderingType, + @Nullable String description) { isNull(type, "type must be supplied"); if (!isEmpty(optionSet)) { if (renderingType == null || renderingType == ProgramStageSectionRenderingType.LISTING) - return SpinnerViewModel.create(id, label, hintFilterOptions, mandatory, optionSet, value, section, editable); + return SpinnerViewModel.create(id, label, hintFilterOptions, mandatory, optionSet, value, section, editable, description); else - return ImageViewModel.create(id, label, optionSet, value, section, editable, mandatory); //transforms option set into image option selector + return ImageViewModel.create(id, label, optionSet, value, section, editable, mandatory, description); //transforms option set into image option selector } switch (type) { case AGE: - return AgeViewModel.create(id, label, mandatory, value, section, editable); + return AgeViewModel.create(id, label, mandatory, value, section, editable, description); case TEXT: case EMAIL: case LETTER: @@ -98,25 +98,25 @@ public FieldViewModel create(@NonNull String id, @NonNull String label, @NonNull case INTEGER_ZERO_OR_POSITIVE: case UNIT_INTERVAL: case URL: - return EditTextViewModel.create(id, label, mandatory, value, hintEnterText, 1, type, section, editable); + return EditTextViewModel.create(id, label, mandatory, value, hintEnterText, 1, type, section, editable, description); case TIME: case DATE: case DATETIME: - return DateTimeViewModel.create(id, label, mandatory, type, value, section, allowFutureDates, editable); + return DateTimeViewModel.create(id, label, mandatory, type, value, section, allowFutureDates, editable, description); case COORDINATE: - return CoordinateViewModel.create(id, label, mandatory, value, section, editable); + return CoordinateViewModel.create(id, label, mandatory, value, section, editable, description); case BOOLEAN: case TRUE_ONLY: - return RadioButtonViewModel.fromRawValue(id, label, type, mandatory, value, section, editable); + return RadioButtonViewModel.fromRawValue(id, label, type, mandatory, value, section, editable, description); case ORGANISATION_UNIT: - return OrgUnitViewModel.create(id, label, mandatory, value, section, editable); + return OrgUnitViewModel.create(id, label, mandatory, value, section, editable, description); case FILE_RESOURCE: case IMAGE: case TRACKER_ASSOCIATE: case USERNAME: - return UnsupportedViewModel.create(id, label, mandatory, value, section, editable); + return UnsupportedViewModel.create(id, label, mandatory, value, section, editable, description); default: - return EditTextViewModel.create(id, label, mandatory, value, hintEnterText, 1, type, section, editable); + return EditTextViewModel.create(id, label, mandatory, value, hintEnterText, 1, type, section, editable, description); } } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FormViewHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FormViewHolder.java index afb3bf21bc..453d038c8a 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FormViewHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/FormViewHolder.java @@ -1,11 +1,12 @@ package org.dhis2.data.forms.dataentry.fields; import android.databinding.ViewDataBinding; -import android.support.v7.app.AlertDialog; import android.support.v7.widget.RecyclerView; import android.widget.ImageView; import org.dhis2.R; +import org.dhis2.utils.Constants; +import org.dhis2.utils.CustomViews.CustomDialog; /** * QUADRAM. Created by ppajuelo on 06/11/2017. @@ -22,12 +23,18 @@ public FormViewHolder(ViewDataBinding binding) { super(binding.getRoot()); this.binding = binding; this.description = binding.getRoot().findViewById(R.id.descriptionLabel); - if (description != null) - description.setOnClickListener(v -> new AlertDialog.Builder(itemView.getContext()) - .setPositiveButton(android.R.string.ok, null) - .setTitle("Info") - .setMessage(label != null ? label : "No info for this field") - .show()); + if (description != null) { + description.setOnClickListener(v -> + new CustomDialog( + itemView.getContext(), + label.toString(), + descriptionText != null ? descriptionText : "No info for this field", + itemView.getContext().getString(R.string.action_accept), + null, + Constants.DESCRIPTION_DIALOG, + null + ).show()); + } } public abstract void dispose(); diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeHolder.java index 3920c0b77b..0d7f00aaa7 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeHolder.java @@ -16,39 +16,11 @@ public class AgeHolder extends FormViewHolder { FormAgeCustomBinding binding; - /* @NonNull - private BehaviorProcessor model;*/ -// CompositeDisposable disposable; AgeViewModel ageViewModel; AgeHolder(FormAgeCustomBinding binding, FlowableProcessor processor) { super(binding); this.binding = binding; -// disposable = new CompositeDisposable(); -// model = BehaviorProcessor.create(); - - /* disposable.add(model.subscribe(ageViewModel -> { - StringBuilder label = new StringBuilder(ageViewModel.label()); - if (ageViewModel.mandatory()) - label.append("*"); - binding.customAgeview.setLabel(label.toString()); - if (!isEmpty(ageViewModel.value())) { - binding.customAgeview.setInitialValue(ageViewModel.value()); - } - - if (ageViewModel.warning() != null) - binding.customAgeview.setWarningOrError(ageViewModel.warning()); - else if (ageViewModel.error() != null) - binding.customAgeview.setWarningOrError(ageViewModel.error()); - else - binding.customAgeview.setWarningOrError(null); - - binding.customAgeview.setEditable(ageViewModel.editable()); - - binding.executePendingBindings(); - }, - Timber::d));*/ - binding.customAgeview.setAgeChangedListener(ageDate -> { if (ageViewModel.value() == null || !ageViewModel.value().equals(DateUtils.databaseDateFormat().format(ageDate))) processor.onNext(RowAction.create(ageViewModel.uid(), DateUtils.databaseDateFormat().format(ageDate))); @@ -61,10 +33,11 @@ public void update(AgeViewModel ageViewModel) { // model.onNext(viewModel); this.ageViewModel = ageViewModel; - StringBuilder label = new StringBuilder(ageViewModel.label()); + descriptionText = ageViewModel.description(); + label = new StringBuilder(ageViewModel.label()); if (ageViewModel.mandatory()) label.append("*"); - binding.customAgeview.setLabel(label.toString()); + binding.customAgeview.setLabel(label.toString(),ageViewModel.description()); if (!isEmpty(ageViewModel.value())) { binding.customAgeview.setInitialValue(ageViewModel.value()); } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeViewModel.java index a8cadf3c3c..f88428a1b6 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/age/AgeViewModel.java @@ -3,9 +3,10 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import org.dhis2.data.forms.dataentry.fields.FieldViewModel; import com.google.auto.value.AutoValue; +import org.dhis2.data.forms.dataentry.fields.FieldViewModel; + /** * QUADRAM. Created by frodriguez on 20/03/2018. */ @@ -19,24 +20,24 @@ public abstract class AgeViewModel extends FieldViewModel { @Nullable public abstract String value(); - public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section, Boolean editable) { - return new AutoValue_AgeViewModel(id, label, section, null, editable, null, null, null, mandatory, value); + public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section, Boolean editable, String description) { + return new AutoValue_AgeViewModel(id, label, section, null, editable, null, null, null, description, mandatory, value); } @Override public FieldViewModel setMandatory() { - return new AutoValue_AgeViewModel(uid(), label(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error(), true, value()); + return new AutoValue_AgeViewModel(uid(), label(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error(), description(), true, value()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { - return new AutoValue_AgeViewModel(uid(), label(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error, mandatory(), value()); + return new AutoValue_AgeViewModel(uid(), label(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error, description(), mandatory(), value()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { - return new AutoValue_AgeViewModel(uid(), label(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning, error(), mandatory(), value()); + return new AutoValue_AgeViewModel(uid(), label(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning, error(), description(), mandatory(), value()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateHolder.java index 7897e9cc8e..fee3e8a1d0 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateHolder.java @@ -2,7 +2,6 @@ import android.annotation.SuppressLint; -import android.support.annotation.NonNull; import org.dhis2.data.forms.dataentry.fields.FormViewHolder; import org.dhis2.data.forms.dataentry.fields.RowAction; @@ -11,28 +10,22 @@ import java.util.Locale; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.processors.BehaviorProcessor; import io.reactivex.processors.FlowableProcessor; -import timber.log.Timber; import static android.text.TextUtils.isEmpty; public class CoordinateHolder extends FormViewHolder { - CompositeDisposable disposable; - @NonNull - private - BehaviorProcessor model; + CustomFormCoordinateBinding binding; + CoordinateViewModel model; @SuppressLint("CheckResult") CoordinateHolder(CustomFormCoordinateBinding binding, FlowableProcessor processor) { super(binding); - disposable = new CompositeDisposable(); - + this.binding = binding; binding.formCoordinates.setCurrentLocationListener((latitude, longitude) -> processor.onNext( - RowAction.create(model.getValue().uid(), + RowAction.create(model.uid(), String.format(Locale.US, "[%.5f,%.5f]", latitude, longitude)) )); @@ -40,36 +33,34 @@ public class CoordinateHolder extends FormViewHolder { (CoordinatesView.OnMapPositionClick) binding.formCoordinates.getContext() ); - model = BehaviorProcessor.create(); + } + + void update(CoordinateViewModel coordinateViewModel) { + model = coordinateViewModel; - disposable.add(model.subscribe(coordinateViewModel -> { - StringBuilder label = new StringBuilder(coordinateViewModel.label()); - if (coordinateViewModel.mandatory()) - label.append("*"); - binding.formCoordinates.setLabel(label.toString()); - if (!isEmpty(coordinateViewModel.value())) - binding.formCoordinates.setInitialValue(coordinateViewModel.value()); + descriptionText = coordinateViewModel.description(); + label = new StringBuilder(coordinateViewModel.label()); + if (coordinateViewModel.mandatory()) + label.append("*"); + binding.formCoordinates.setLabel(label.toString()); + binding.formCoordinates.setDescription(descriptionText); - if (coordinateViewModel.warning() != null) - binding.formCoordinates.setWargingOrError(coordinateViewModel.warning()); - else if (coordinateViewModel.error() != null) - binding.formCoordinates.setWargingOrError(coordinateViewModel.error()); - else - binding.formCoordinates.setWargingOrError(null); + if (!isEmpty(coordinateViewModel.value())) + binding.formCoordinates.setInitialValue(coordinateViewModel.value()); - binding.formCoordinates.setEditable(coordinateViewModel.editable()); + if (coordinateViewModel.warning() != null) + binding.formCoordinates.setWargingOrError(coordinateViewModel.warning()); + else if (coordinateViewModel.error() != null) + binding.formCoordinates.setWargingOrError(coordinateViewModel.error()); + else + binding.formCoordinates.setWargingOrError(null); - binding.executePendingBindings(); - }, - Timber::d)); - } + binding.formCoordinates.setEditable(coordinateViewModel.editable()); - void update(CoordinateViewModel viewModel) { - model.onNext(viewModel); + binding.executePendingBindings(); } @Override public void dispose() { - disposable.clear(); } } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateViewModel.java index de62803142..ddc415daf6 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/coordinate/CoordinateViewModel.java @@ -11,24 +11,24 @@ @AutoValue public abstract class CoordinateViewModel extends FieldViewModel { - public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section,Boolean editable) { - return new AutoValue_CoordinateViewModel(id, label, mandatory, value, section, null, editable, null, null, null); + public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section,Boolean editable,String description) { + return new AutoValue_CoordinateViewModel(id, label, mandatory, value, section, null, editable, null, null, null,description); } @Override public FieldViewModel setMandatory() { - return new AutoValue_CoordinateViewModel(uid(), label(), true, value(), programStageSection(), null, editable(), null, warning(), error()); + return new AutoValue_CoordinateViewModel(uid(), label(), true, value(), programStageSection(), null, editable(), null, warning(), error(),description()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { - return new AutoValue_CoordinateViewModel(uid(), label(), true, value(), programStageSection(), null, editable(), null, warning, error()); + return new AutoValue_CoordinateViewModel(uid(), label(), true, value(), programStageSection(), null, editable(), null, warning, error(),description()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { - return new AutoValue_CoordinateViewModel(uid(), label(), true, value(), programStageSection(), null, editable(), null, warning(), error); + return new AutoValue_CoordinateViewModel(uid(), label(), true, value(), programStageSection(), null, editable(), null, warning(), error,description()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeHolder.java index dea505801d..5fbe5e01cb 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeHolder.java @@ -55,12 +55,13 @@ public class DateTimeHolder extends FormViewHolder implements OnDateSelected { public void update(DateTimeViewModel viewModel) { this.dateTimeViewModel = viewModel; // model.onNext(viewModel); - + descriptionText = viewModel.description(); label = new StringBuilder(dateTimeViewModel.label()); if (dateTimeViewModel.mandatory()) label.append("*"); binding.setVariable(BR.label, label.toString()); + binding.setVariable(BR.description, descriptionText); if (!isEmpty(dateTimeViewModel.value())) { binding.setVariable(BR.initData, dateTimeViewModel.value()); diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeViewModel.java index 73178285ed..c25c6bcd82 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/datetime/DateTimeViewModel.java @@ -17,25 +17,25 @@ public abstract class DateTimeViewModel extends FieldViewModel { @NonNull public abstract ValueType valueType(); - public static FieldViewModel create(String id, String label, Boolean mandatory, ValueType type, String value, String section, Boolean allowFutureDates, Boolean editable) { - return new AutoValue_DateTimeViewModel(id, label, mandatory, value,section, allowFutureDates,editable,null,null,null, type); + public static FieldViewModel create(String id, String label, Boolean mandatory, ValueType type, String value, String section, Boolean allowFutureDates, Boolean editable,String description) { + return new AutoValue_DateTimeViewModel(id, label, mandatory, value,section, allowFutureDates,editable,null,null,null,description, type); } @Override public FieldViewModel setMandatory() { return new AutoValue_DateTimeViewModel(uid(),label(),true,value(),programStageSection(), - allowFutureDate(),editable(),optionSet(),warning(),error(),valueType()); + allowFutureDate(),editable(),optionSet(),warning(),error(),description(),valueType()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { return new AutoValue_DateTimeViewModel(uid(),label(),mandatory(),value(),programStageSection(), - allowFutureDate(),editable(),optionSet(),warning(),error,valueType()); } + allowFutureDate(),editable(),optionSet(),warning(),error,description(),valueType()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { return new AutoValue_DateTimeViewModel(uid(),label(),mandatory(),value(),programStageSection(), - allowFutureDate(),editable(),optionSet(),warning,error(),valueType()); } + allowFutureDate(),editable(),optionSet(),warning,error(),description(),valueType()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextCustomHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextCustomHolder.java index 5b056ff9ad..c45537884f 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextCustomHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextCustomHolder.java @@ -61,7 +61,7 @@ final class EditTextCustomHolder extends FormViewHolder { editText.setOnFocusChangeListener((v, hasFocus) -> { if (!hasFocus && editTextModel != null && editTextModel.editable()) { - if (!isEmpty(editText.getText())) + if (!isEmpty(editText.getText()) && validate()) processor.onNext(RowAction.create(editTextModel.uid(), editText.getText().toString())); else processor.onNext(RowAction.create(editTextModel.uid(), null)); @@ -170,14 +170,14 @@ public void update(@NonNull FieldViewModel model) { label.append("*"); inputLayout.setHint(label); - if (label.length() > 16) + if (label.length() > 16 || model.description() != null) description.setVisibility(View.VISIBLE); else description.setVisibility(View.GONE); } - + descriptionText = editTextModel.description(); setInputType(editTextModel.valueType()); } @@ -229,7 +229,6 @@ private boolean validate() { if (Float.valueOf(editText.getText().toString()) >= 0 && Float.valueOf(editText.getText().toString()) <= 100) return true; else { - inputLayout.setError(editText.getContext().getString(R.string.invalid_percentage)); return false; } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextViewModel.java index f7c5cc682d..d8015d7fb5 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/edittext/EditTextViewModel.java @@ -4,9 +4,9 @@ import android.support.annotation.Nullable; import android.text.InputType; -import org.dhis2.data.forms.dataentry.fields.FieldViewModel; import com.google.auto.value.AutoValue; +import org.dhis2.data.forms.dataentry.fields.FieldViewModel; import org.hisp.dhis.android.core.common.ValueType; /** @@ -19,29 +19,29 @@ public abstract class EditTextViewModel extends EditTextModel { @NonNull public static EditTextViewModel create(@NonNull String uid, @NonNull String label, @NonNull Boolean mandatory, @Nullable String value, @NonNull String hint, - @NonNull Integer lines, @NonNull ValueType valueType, @Nullable String section, @NonNull Boolean editable) { + @NonNull Integer lines, @NonNull ValueType valueType, @Nullable String section, @NonNull Boolean editable, @Nullable String description) { return new AutoValue_EditTextViewModel(uid, label, mandatory, - value, section, null, editable, null, hint, lines, InputType.TYPE_CLASS_TEXT, valueType, null, null); + value, section, null, editable, null, description, hint, lines, InputType.TYPE_CLASS_TEXT, valueType, null, null); } @NonNull @Override public EditTextViewModel withWarning(@NonNull String warning) { return new AutoValue_EditTextViewModel(uid(), label(), mandatory(), - value(), programStageSection(), null, true, null, hint(), maxLines(), inputType(), valueType(), warning, error()); + value(), programStageSection(), null, true, null, description(), hint(), maxLines(), inputType(), valueType(), warning, error()); } @NonNull @Override public EditTextViewModel withError(@NonNull String error) { return new AutoValue_EditTextViewModel(uid(), label(), mandatory(), - value(), programStageSection(), null, true, null, hint(), maxLines(), inputType(), valueType(), warning(), error); + value(), programStageSection(), null, true, null, description(), hint(), maxLines(), inputType(), valueType(), warning(), error); } @NonNull @Override public FieldViewModel setMandatory() { return new AutoValue_EditTextViewModel(uid(), label(), true, - value(), programStageSection(), null, editable(), null, hint(), maxLines(), InputType.TYPE_CLASS_TEXT, valueType(), warning(), error()); + value(), programStageSection(), null, editable(), null, description(), hint(), maxLines(), InputType.TYPE_CLASS_TEXT, valueType(), warning(), error()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/file/FileViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/file/FileViewModel.java index e85a0677de..5d22011fed 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/file/FileViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/file/FileViewModel.java @@ -11,28 +11,28 @@ @AutoValue public abstract class FileViewModel extends FieldViewModel { - public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section) { + public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section,String description) { return new AutoValue_FileViewModel(id, label, mandatory, value, section, null, - true, null, null, null); + true, null, null, null,description); } @Override public FieldViewModel setMandatory() { return new AutoValue_FileViewModel(uid(), label(), true, value(), programStageSection(), - allowFutureDate(), editable(), optionSet(), warning(), error()); + allowFutureDate(), editable(), optionSet(), warning(), error(),description()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { return new AutoValue_FileViewModel(uid(), label(), mandatory(), value(), programStageSection(), - allowFutureDate(), editable(), optionSet(), warning(), error); + allowFutureDate(), editable(), optionSet(), warning(), error,description()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { return new AutoValue_FileViewModel(uid(), label(), mandatory(), value(), programStageSection(), - allowFutureDate(), editable(), optionSet(), warning, error()); + allowFutureDate(), editable(), optionSet(), warning, error(),description()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageHolder.java index a59c74e151..76f91f1b18 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageHolder.java @@ -2,16 +2,19 @@ import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; -import android.util.Log; import android.view.View; import org.dhis2.Bindings.Bindings; +import org.dhis2.data.forms.dataentry.fields.FieldViewHolder; +import org.dhis2.data.forms.dataentry.fields.FieldViewModel; +import org.dhis2.data.forms.dataentry.fields.FormViewHolder; import org.dhis2.data.forms.dataentry.fields.RowAction; import org.dhis2.databinding.FormImageBinding; +import java.util.concurrent.TimeUnit; + import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.processors.BehaviorProcessor; import io.reactivex.processors.FlowableProcessor; import io.reactivex.schedulers.Schedulers; import timber.log.Timber; @@ -20,84 +23,79 @@ * QUADRAM. Created by ppajuelo on 31/05/2018. */ -public class ImageHolder extends RecyclerView.ViewHolder { +public class ImageHolder extends FormViewHolder { private final CompositeDisposable disposable; private final FlowableProcessor processor; private final FormImageBinding binding; private boolean isEditable; private String valuePendingUpdate; - @NonNull - private BehaviorProcessor model; + + ImageViewModel model; public ImageHolder(FormImageBinding mBinding, FlowableProcessor processor, boolean isBackgroundTransparent, String renderType, View rootView, FlowableProcessor imageSelector) { - super(rootView != null ? rootView : mBinding.getRoot()); + super(mBinding); this.processor = processor; this.binding = mBinding; this.disposable = new CompositeDisposable(); - model = BehaviorProcessor.create(); if (imageSelector != null) disposable.add(imageSelector + .debounce(1000, TimeUnit.MILLISECONDS, Schedulers.io()) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .subscribe(selectedValue -> { - if (selectedValue.equals(model.getValue().value())||selectedValue.equals(valuePendingUpdate)) + if (selectedValue.equals(model.value()) || selectedValue.equals(valuePendingUpdate)) binding.frame.setVisibility(View.VISIBLE); else binding.frame.setVisibility(View.GONE); }, Timber::d)); - disposable.add(model.subscribe(viewModel -> { - this.isEditable = viewModel.editable(); - StringBuilder label = new StringBuilder(viewModel.label()); - if (viewModel.mandatory()) - label.append("*"); - binding.setLabel(label.toString()); - String[] uids = viewModel.uid().split("\\."); - Bindings.setObjectStyle(binding.icon, itemView, uids[1]); - if (viewModel.value() != null && viewModel.value().equals(viewModel.label())) - binding.frame.setVisibility(View.VISIBLE); - else - binding.frame.setVisibility(View.GONE); - - if (viewModel.warning() != null) { - binding.errorMessage.setVisibility(View.VISIBLE); - binding.errorMessage.setText(viewModel.warning()); - } else if (viewModel.error() != null) { - binding.errorMessage.setVisibility(View.VISIBLE); - binding.errorMessage.setText(viewModel.error()); - } else { - binding.errorMessage.setVisibility(View.GONE); - binding.errorMessage.setText(null); - } - } - , t -> Log.d("DHIS_ERROR", t.getMessage()))); - itemView.setOnClickListener(v -> { if (isEditable) { String value = null; - String[] uids = model.getValue().uid().split("\\."); - /*if (binding.frame.getVisibility() == View.GONE) { - binding.frame.setVisibility(View.VISIBLE); - value = model.getValue().label(); - } else { - binding.frame.setVisibility(View.GONE); - }*/ - value = model.getValue().label(); + String[] uids = model.uid().split("\\."); + value = model.label(); valuePendingUpdate = value; - binding.frame.setVisibility(View.VISIBLE); - if (imageSelector != null) - imageSelector.onNext(value); - processor.onNext(RowAction.create(uids[0], value)); + binding.frame.setVisibility(binding.frame.getVisibility() == View.GONE ? View.VISIBLE : View.GONE); + if(binding.frame.getVisibility()==View.VISIBLE) { + if (imageSelector != null) + imageSelector.onNext(value); + processor.onNext(RowAction.create(uids[0], value)); + }else + processor.onNext(RowAction.create(uids[0], null)); } }); } public void update(ImageViewModel viewModel) { - model.onNext(viewModel); + this.model = viewModel; + + this.isEditable = viewModel.editable(); + descriptionText = viewModel.description(); + label = new StringBuilder(viewModel.label()); + if (viewModel.mandatory()) + label.append("*"); + binding.setLabel(label.toString()); + String[] uids = viewModel.uid().split("\\."); + Bindings.setObjectStyle(binding.icon, itemView, uids[1]); + if (viewModel.value() != null && viewModel.value().equals(viewModel.label())) + binding.frame.setVisibility(View.VISIBLE); + else + binding.frame.setVisibility(View.GONE); + + if (viewModel.warning() != null) { + binding.errorMessage.setVisibility(View.VISIBLE); + binding.errorMessage.setText(viewModel.warning()); + } else if (viewModel.error() != null) { + binding.errorMessage.setVisibility(View.VISIBLE); + binding.errorMessage.setText(viewModel.error()); + } else { + binding.errorMessage.setVisibility(View.GONE); + binding.errorMessage.setText(null); + } } public void dispose() { diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageViewModel.java index 7382e3c1ef..ecbe56f431 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/image/ImageViewModel.java @@ -11,27 +11,27 @@ @AutoValue public abstract class ImageViewModel extends FieldViewModel { - public static ImageViewModel create(String id, String label, String optionSet, String value, String section, Boolean editable, Boolean mandatory) { - return new AutoValue_ImageViewModel(id, label, mandatory, value, section, true, editable, optionSet, null, null); + public static ImageViewModel create(String id, String label, String optionSet, String value, String section, Boolean editable, Boolean mandatory, String description) { + return new AutoValue_ImageViewModel(id, label, mandatory, value, section, true, editable, optionSet, null, null, description); } @Override public FieldViewModel setMandatory() { return new AutoValue_ImageViewModel(uid(), label(), true, value(), programStageSection(), - allowFutureDate(), editable(), optionSet(), warning(), error()); + allowFutureDate(), editable(), optionSet(), warning(), error(),description()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { return new AutoValue_ImageViewModel(uid(), label(), true, value(), programStageSection(), - allowFutureDate(), editable(), optionSet(), warning(), error); + allowFutureDate(), editable(), optionSet(), warning(), error,description()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { return new AutoValue_ImageViewModel(uid(), label(), true, value(), programStageSection(), - allowFutureDate(), editable(), optionSet(), warning, error()); + allowFutureDate(), editable(), optionSet(), warning, error(),description()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitHolder.java index 1dc71a41ac..367535a897 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitHolder.java @@ -57,7 +57,7 @@ public class OrgUnitHolder extends FormViewHolder { orgUnitDialog.dismiss(); editText.setEnabled(true); }) - .setNegativeListener(data-> { + .setNegativeListener(data -> { orgUnitDialog.dismiss(); editText.setEnabled(true); }); @@ -66,30 +66,6 @@ public class OrgUnitHolder extends FormViewHolder { }); -// model = BehaviorProcessor.create(); - - /* compositeDisposable.add( - model.subscribe(viewModel -> { - StringBuilder label = new StringBuilder(viewModel.label()); - if (viewModel.mandatory()) - label.append("*"); - this.inputLayout.setHint(label.toString()); - - if (viewModel.warning() != null) - editText.setError(viewModel.warning()); - else if (viewModel.error() != null) - editText.setError(viewModel.error()); - else - editText.setError(null); - - if (viewModel.value() != null && !viewModel.value().equals(viewModel.value())) { - getOrgUnits(); - } - editText.setEnabled(viewModel.editable()); - }, - Timber::d) - );*/ - getOrgUnits(); } @@ -99,14 +75,14 @@ public void dispose() { } public void update(OrgUnitViewModel viewModel) { -// model.onNext(viewModel); - StringBuilder label = new StringBuilder(viewModel.label()); + descriptionText = viewModel.description(); + label = new StringBuilder(viewModel.label()); if (viewModel.mandatory()) label.append("*"); this.inputLayout.setHint(label.toString()); - if (label.length() > 16) + if (label.length() > 16 || viewModel.description() != null) description.setVisibility(View.VISIBLE); else description.setVisibility(View.GONE); diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitViewModel.java index 0310ecaaf0..af780723b3 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/orgUnit/OrgUnitViewModel.java @@ -11,24 +11,24 @@ @AutoValue public abstract class OrgUnitViewModel extends FieldViewModel { - public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section, Boolean editable) { - return new AutoValue_OrgUnitViewModel(id, label, mandatory, value,section, null,editable,null,null,null); + public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section, Boolean editable,String description) { + return new AutoValue_OrgUnitViewModel(id, label, mandatory, value,section, null,editable,null,null,null,description); } @Override public FieldViewModel setMandatory() { - return new AutoValue_OrgUnitViewModel(uid(),label(),true,value(),programStageSection(),allowFutureDate(),editable(),optionSet(),warning(),error()); + return new AutoValue_OrgUnitViewModel(uid(),label(),true,value(),programStageSection(),allowFutureDate(),editable(),optionSet(),warning(),error(),description()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { - return new AutoValue_OrgUnitViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),optionSet(),warning(),error); + return new AutoValue_OrgUnitViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),optionSet(),warning(),error,description()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { - return new AutoValue_OrgUnitViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),optionSet(),warning,error()); + return new AutoValue_OrgUnitViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),optionSet(),warning,error(),description()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonHolder.java index 99240d7268..7755f2b993 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonHolder.java @@ -1,11 +1,11 @@ package org.dhis2.data.forms.dataentry.fields.radiobutton; -import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import android.widget.RadioGroup; import org.dhis2.R; +import org.dhis2.data.forms.dataentry.fields.FormViewHolder; import org.dhis2.data.forms.dataentry.fields.RowAction; import org.dhis2.databinding.FormYesNoBinding; @@ -16,85 +16,33 @@ * QUADRAM. Created by frodriguez on 18/01/2018. */ -public class RadioButtonHolder extends RecyclerView.ViewHolder { +public class RadioButtonHolder extends FormViewHolder { private final FlowableProcessor processor; - /* @NonNull - private BehaviorProcessor model;*/ + final RadioGroup radioGroup; final FormYesNoBinding binding; RadioButtonViewModel viewModel; RadioButtonHolder(ViewGroup parent, FormYesNoBinding binding, FlowableProcessor processor) { - super(binding.getRoot()); + super(binding); radioGroup = binding.customYesNo.getRadioGroup(); this.binding = binding; this.processor = processor; - - /* model - .subscribe(checkBoxViewModel -> { - StringBuilder label = new StringBuilder(checkBoxViewModel.label()); - if (checkBoxViewModel.mandatory()) - label.append("*"); - binding.setLabel(label.toString()); - binding.setValueType(checkBoxViewModel.valueType()); - if (checkBoxViewModel.value() != null && Boolean.valueOf(checkBoxViewModel.value())) - binding.customYesNo.getRadioGroup().check(R.id.yes); - else if (checkBoxViewModel.value() != null) - binding.customYesNo.getRadioGroup().check(R.id.no); - else - binding.customYesNo.getRadioGroup().check(R.id.no_value); - - if (checkBoxViewModel.warning() != null) { - binding.warningError.setVisibility(View.VISIBLE); - binding.warningError.setText(checkBoxViewModel.warning()); - } else if (checkBoxViewModel.error() != null) { - binding.warningError.setVisibility(View.VISIBLE); - binding.warningError.setText(checkBoxViewModel.error()); - } else { - binding.warningError.setVisibility(View.GONE); - binding.warningError.setText(null); - } - - for (int i = 0; i < radioGroup.getChildCount(); i++) { - radioGroup.getChildAt(i).setEnabled(checkBoxViewModel.editable()); - } - }, - Timber::d); - - RxRadioGroup.checkedChanges(radioGroup).takeUntil(RxView.detaches(parent)) - .filter(checkIc -> model.hasValue()) - .map(checkId -> - { - switch (checkId) { - case R.id.yes: - return RowAction.create(model.getValue().uid(), String.valueOf(true)); - case R.id.no: - return RowAction.create(model.getValue().uid(), String.valueOf(false)); - default: - return RowAction.create(model.getValue().uid(), null); - } - } - ) - .subscribe( - processor::onNext, - Timber::d);*/ } - /*private boolean checkValue(RadioButtonViewModel checkBoxViewModel) { - return model.getValue() == null || !model.getValue().equals(checkBoxViewModel); - }*/ public void update(RadioButtonViewModel checkBoxViewModel) { -// model.onNext(viewModel); this.viewModel = checkBoxViewModel; radioGroup.setOnCheckedChangeListener(null); + descriptionText = viewModel.description(); + binding.setDescription(descriptionText); + label = new StringBuilder(checkBoxViewModel.label()); binding.customYesNo.setValueType(checkBoxViewModel.valueType()); - StringBuilder label = new StringBuilder(checkBoxViewModel.label()); if (checkBoxViewModel.mandatory()) label.append("*"); binding.setLabel(label.toString()); diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonViewModel.java index 39d338f30c..99a3d3ae85 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/radiobutton/RadioButtonViewModel.java @@ -3,9 +3,9 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import org.dhis2.data.forms.dataentry.fields.FieldViewModel; import com.google.auto.value.AutoValue; +import org.dhis2.data.forms.dataentry.fields.FieldViewModel; import org.hisp.dhis.android.core.common.ValueType; import java.util.Locale; @@ -41,15 +41,15 @@ public String toString() { @NonNull public static RadioButtonViewModel fromRawValue(@NonNull String id, @NonNull String label, @NonNull ValueType type, - @NonNull Boolean mandatory, @Nullable String value, @Nullable String section, Boolean editable) { + @NonNull Boolean mandatory, @Nullable String value, @Nullable String section, Boolean editable, @Nullable String description) { if (value == null) { - return new AutoValue_RadioButtonViewModel(id, label, null, section, null, editable, null, null, null, mandatory, type); + return new AutoValue_RadioButtonViewModel(id, label, null, section, null, editable, null, null, null, description, mandatory, type); } else if (value.toLowerCase(Locale.US).equals(Value.CHECKED.toString())) { - return new AutoValue_RadioButtonViewModel(id, label, Value.CHECKED.toString(), section, null, editable, null, null, null, mandatory, type); + return new AutoValue_RadioButtonViewModel(id, label, Value.CHECKED.toString(), section, null, editable, null, null, null, description, mandatory, type); } else if (value.toLowerCase(Locale.US).equals(Value.UNCHECKED.toString())) { - return new AutoValue_RadioButtonViewModel(id, label, Value.UNCHECKED.toString(), section, null, editable, null, null, null, mandatory, type); + return new AutoValue_RadioButtonViewModel(id, label, Value.UNCHECKED.toString(), section, null, editable, null, null, null, description, mandatory, type); } else if (value.toLowerCase(Locale.US).equals(Value.CHECKED_NO.toString())) { - return new AutoValue_RadioButtonViewModel(id, label, Value.CHECKED_NO.toString(), section, null, editable, null, null, null, mandatory, type); + return new AutoValue_RadioButtonViewModel(id, label, Value.CHECKED_NO.toString(), section, null, editable, null, null, null, description, mandatory, type); } else { throw new IllegalArgumentException("Unsupported value: " + value); } @@ -57,19 +57,19 @@ public static RadioButtonViewModel fromRawValue(@NonNull String id, @NonNull Str @Override public FieldViewModel setMandatory() { - return new AutoValue_RadioButtonViewModel(uid(), label(), value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error(), true, valueType()); + return new AutoValue_RadioButtonViewModel(uid(), label(), value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error(), description(), true, valueType()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { - return new AutoValue_RadioButtonViewModel(uid(), label(), value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error, mandatory(), valueType()); + return new AutoValue_RadioButtonViewModel(uid(), label(), value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error, description(), mandatory(), valueType()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { - return new AutoValue_RadioButtonViewModel(uid(), label(), value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning, error(), mandatory(), valueType()); + return new AutoValue_RadioButtonViewModel(uid(), label(), value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning, error(), description(), mandatory(), valueType()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerHolder.java index 2dde43e613..ce25fab8bc 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerHolder.java @@ -12,6 +12,7 @@ import org.dhis2.Bindings.Bindings; import org.dhis2.R; +import org.dhis2.data.forms.dataentry.fields.FormViewHolder; import org.dhis2.data.forms.dataentry.fields.RowAction; import org.hisp.dhis.android.core.option.OptionModel; @@ -28,7 +29,7 @@ * QUADRAM. Created by ppajuelo on 07/11/2017. */ -public class SpinnerHolder extends RecyclerView.ViewHolder implements View.OnClickListener, PopupMenu.OnMenuItemClickListener { +public class SpinnerHolder extends FormViewHolder implements View.OnClickListener, PopupMenu.OnMenuItemClickListener { private final CompositeDisposable disposable; private final FlowableProcessor processor; @@ -42,7 +43,7 @@ public class SpinnerHolder extends RecyclerView.ViewHolder implements View.OnCli List options; SpinnerHolder(ViewDataBinding mBinding, FlowableProcessor processor, boolean isBackgroundTransparent, String renderType) { - super(mBinding.getRoot()); + super(mBinding); this.editText = mBinding.getRoot().findViewById(R.id.input_editText); this.iconView = mBinding.getRoot().findViewById(R.id.renderImage); this.inputLayout = mBinding.getRoot().findViewById(R.id.input_layout); @@ -55,29 +56,10 @@ public class SpinnerHolder extends RecyclerView.ViewHolder implements View.OnCli this.disposable = new CompositeDisposable(); - /*model = BehaviorProcessor.create(); - disposable.add(model - .subscribe(viewModel -> { - Bindings.setObjectStyle(iconView, itemView, viewModel.uid()); - editText.setEnabled(viewModel.editable()); - editText.setFocusable(false); - editText.setClickable(viewModel.editable()); - editText.setText(viewModel.value()); - - if (!isEmpty(viewModel.warning())) { - inputLayout.setError(viewModel.warning()); - } else if (!isEmpty(viewModel.error())) { - inputLayout.setError(viewModel.error()); - } else - inputLayout.setError(null); - - } - , Timber::d)); -*/ } public void update(SpinnerViewModel viewModel) { -// model.onNext(viewModel); + this.viewModel = viewModel; options = Bindings.setOptionSet(viewModel.optionSet()); @@ -85,11 +67,9 @@ public void update(SpinnerViewModel viewModel) { editText.setEnabled(viewModel.editable()); editText.setFocusable(false); editText.setClickable(viewModel.editable()); - if(viewModel.value() != null){ - for (OptionModel optionModel : options) - if(viewModel.value().equals(optionModel.code())) - editText.setText(optionModel.displayName()); - } + + + editText.setText(viewModel.value()); //option code is already transformed to value in the fieldviewmodelfactory implementation if (!isEmpty(viewModel.warning())) { @@ -100,11 +80,13 @@ public void update(SpinnerViewModel viewModel) { inputLayout.setError(null); if (inputLayout.getHint() == null || !inputLayout.getHint().toString().equals(viewModel.label())) { - StringBuilder label = new StringBuilder(viewModel.label()); + label = new StringBuilder(viewModel.label()); if (viewModel.mandatory()) label.append("*"); inputLayout.setHint(label); } + + descriptionText = viewModel.description(); } public void dispose() { diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerViewModel.java index f485532ad6..0e2a04bfef 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/spinner/SpinnerViewModel.java @@ -18,24 +18,24 @@ public abstract class SpinnerViewModel extends FieldViewModel { @NonNull public abstract String optionSet(); - public static SpinnerViewModel create(String id, String label, String hintFilterOptions, Boolean mandatory, String optionSet, String value, String section, Boolean editable) { - return new AutoValue_SpinnerViewModel(id, label, mandatory, value, section, null, editable,null,null, hintFilterOptions, optionSet); + public static SpinnerViewModel create(String id, String label, String hintFilterOptions, Boolean mandatory, String optionSet, String value, String section, Boolean editable,String description) { + return new AutoValue_SpinnerViewModel(id, label, mandatory, value, section, null, editable,null,null, description,hintFilterOptions, optionSet); } @Override public FieldViewModel setMandatory() { - return new AutoValue_SpinnerViewModel(uid(),label(),true,value(),programStageSection(),allowFutureDate(),editable(),warning(),error(),hint(),optionSet()); + return new AutoValue_SpinnerViewModel(uid(),label(),true,value(),programStageSection(),allowFutureDate(),editable(),warning(),error(),description(),hint(),optionSet()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { - return new AutoValue_SpinnerViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),warning(),error,hint(),optionSet()); + return new AutoValue_SpinnerViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),warning(),error,description(),hint(),optionSet()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { - return new AutoValue_SpinnerViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),warning,error(),hint(),optionSet()); + return new AutoValue_SpinnerViewModel(uid(),label(),mandatory(),value(),programStageSection(),allowFutureDate(),editable(),warning,error(),description(),hint(),optionSet()); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedHolder.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedHolder.java index 51999ee9e1..ef07ce684d 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedHolder.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedHolder.java @@ -23,5 +23,6 @@ public void dispose() { public void update(UnsupportedViewModel viewModel) { button.setText(viewModel.label()); + descriptionText = viewModel.description(); } } diff --git a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedViewModel.java b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedViewModel.java index c0ef9514c4..ed686a7012 100644 --- a/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedViewModel.java +++ b/app/src/main/java/org/dhis2/data/forms/dataentry/fields/unsupported/UnsupportedViewModel.java @@ -7,24 +7,24 @@ @AutoValue public abstract class UnsupportedViewModel extends FieldViewModel { - public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section, Boolean editable) { - return new AutoValue_UnsupportedViewModel(id, label, false, value, section, null, editable, null, null, null); + public static FieldViewModel create(String id, String label, Boolean mandatory, String value, String section, Boolean editable,String description) { + return new AutoValue_UnsupportedViewModel(id, label, false, value, section, null, editable, null, null, null,description); } @Override public FieldViewModel setMandatory() { - return new AutoValue_UnsupportedViewModel(uid(), label(), false, value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error()); + return new AutoValue_UnsupportedViewModel(uid(), label(), false, value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error(),description()); } @NonNull @Override public FieldViewModel withError(@NonNull String error) { - return new AutoValue_UnsupportedViewModel(uid(), label(), false, value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error); + return new AutoValue_UnsupportedViewModel(uid(), label(), false, value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning(), error,description()); } @NonNull @Override public FieldViewModel withWarning(@NonNull String warning) { - return new AutoValue_UnsupportedViewModel(uid(), label(), false, value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning, error()); + return new AutoValue_UnsupportedViewModel(uid(), label(), false, value(), programStageSection(), allowFutureDate(), editable(), optionSet(), warning, error(),description()); } } diff --git a/app/src/main/java/org/dhis2/data/user/UserComponent.java b/app/src/main/java/org/dhis2/data/user/UserComponent.java index edfbd83189..de2965352c 100644 --- a/app/src/main/java/org/dhis2/data/user/UserComponent.java +++ b/app/src/main/java/org/dhis2/data/user/UserComponent.java @@ -13,6 +13,12 @@ import org.dhis2.data.service.ServiceModule; import org.dhis2.usescases.about.AboutComponent; import org.dhis2.usescases.about.AboutModule; +import org.dhis2.usescases.dataset.dataSetPeriod.DataSetPeriodComponent; +import org.dhis2.usescases.dataset.dataSetPeriod.DataSetPeriodModule; +import org.dhis2.usescases.datasets.datasetDetail.DataSetDetailComponent; +import org.dhis2.usescases.datasets.datasetDetail.DataSetDetailModule; +import org.dhis2.usescases.datasets.datasetInitial.DataSetInitialComponent; +import org.dhis2.usescases.datasets.datasetInitial.DataSetInitialModule; import org.dhis2.usescases.eventsWithoutRegistration.eventInitial.EventInitialComponent; import org.dhis2.usescases.eventsWithoutRegistration.eventInitial.EventInitialModule; import org.dhis2.usescases.eventsWithoutRegistration.eventSummary.EventSummaryComponent; @@ -116,4 +122,13 @@ public interface UserComponent { @NonNull AboutComponent plus(AboutModule aboutModule); + + @NonNull + DataSetPeriodComponent plus(DataSetPeriodModule dataSetPeriodModule); + + @NonNull + DataSetDetailComponent plus(DataSetDetailModule dataSetDetailModel); + + @NonNull + DataSetInitialComponent plus(DataSetInitialModule dataSetInitialModule); } diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodActivity.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodActivity.java new file mode 100644 index 0000000000..2160634ca6 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodActivity.java @@ -0,0 +1,35 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +import android.databinding.DataBindingUtil; +import android.os.Bundle; + +import org.dhis2.App; +import org.dhis2.R; +import org.dhis2.databinding.ActivityDataSetPeriodBinding; +import org.dhis2.usescases.general.ActivityGlobalAbstract; + +import org.hisp.dhis.android.core.dataset.DataSet; + +import javax.inject.Inject; + +public class DataSetPeriodActivity extends ActivityGlobalAbstract implements DataSetPeriodContract.View { + + ActivityDataSetPeriodBinding binding; + @Inject + DataSetPeriodContract.Presenter presenter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ((App) getApplicationContext()).userComponent().plus(new DataSetPeriodModule()).inject(this); + binding = DataBindingUtil.setContentView(this, R.layout.activity_data_set_period); + binding.setPresenter(presenter); + String dataSetId = getIntent().getStringExtra("DATASET_UID"); + presenter.init(this, dataSetId); + } + + @Override + public void setDataSet(DataSet dataSet) { + binding.setName(dataSet.name()); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodAdapter.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodAdapter.java new file mode 100644 index 0000000000..238d533536 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodAdapter.java @@ -0,0 +1,4 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +public class DataSetPeriodAdapter { +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodComponent.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodComponent.java new file mode 100644 index 0000000000..0e5a8c6aaa --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodComponent.java @@ -0,0 +1,14 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +import org.dhis2.data.dagger.PerActivity; + +import dagger.Subcomponent; + +/** + * Created by frodriguez on 7/20/2018. + */ +@PerActivity +@Subcomponent(modules = DataSetPeriodModule.class) +public interface DataSetPeriodComponent { + void inject(DataSetPeriodActivity activity); +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodContract.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodContract.java new file mode 100644 index 0000000000..05ab09d530 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodContract.java @@ -0,0 +1,23 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +import org.dhis2.usescases.general.AbstractActivityContracts; + +import org.hisp.dhis.android.core.dataset.DataSet; + +/** + * Created by frodriguez on 7/20/2018. + */ +public class DataSetPeriodContract { + + public interface View extends AbstractActivityContracts.View { + + void setDataSet(DataSet dataSet); + } + + public interface Presenter { + void init(View view, String dataSetId); + void onBackClick(); + void showFilter(); + void addDataSet(); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodHolder.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodHolder.java new file mode 100644 index 0000000000..00d01419a0 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodHolder.java @@ -0,0 +1,4 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +public class DataSetPeriodHolder { +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodModel.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodModel.java new file mode 100644 index 0000000000..b5091979a8 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodModel.java @@ -0,0 +1,9 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +import org.hisp.dhis.android.core.period.PeriodModel; + +public class DataSetPeriodModel { + + private PeriodModel period; + +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodModule.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodModule.java new file mode 100644 index 0000000000..f585d9b77d --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodModule.java @@ -0,0 +1,34 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +import com.squareup.sqlbrite2.BriteDatabase; + +import org.dhis2.data.dagger.PerActivity; + +import dagger.Module; +import dagger.Provides; + +/** + * Created by frodriguez on 7/20/2018. + */ +@Module +public class DataSetPeriodModule { + + @Provides + @PerActivity + DataSetPeriodContract.View providesView(DataSetPeriodActivity activity) { + return activity; + } + + @Provides + @PerActivity + DataSetPeriodContract.Presenter providesPresenter(DataSetPeriodRepository repository) { + return new DataSetPeriodPresenter(repository); + } + + @Provides + @PerActivity + DataSetPeriodRepository providesRepository(BriteDatabase briteDatabase) { + return new DataSetPeriodRepositoryImpl(briteDatabase); + } + +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodPresenter.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodPresenter.java new file mode 100644 index 0000000000..aa6ae67fa3 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodPresenter.java @@ -0,0 +1,49 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +/** + * Created by frodriguez on 7/20/2018. + */ +public class DataSetPeriodPresenter implements DataSetPeriodContract.Presenter { + + private DataSetPeriodRepository repository; + private DataSetPeriodContract.View view; + private CompositeDisposable disposable; + + private String dataSetId; + + DataSetPeriodPresenter(DataSetPeriodRepository repository){ + this.repository = repository; + } + + @Override + public void init(DataSetPeriodContract.View view, String dataSetId) { + this.view = view; + this.dataSetId = dataSetId; + + disposable.add( + repository.getDataSet(dataSetId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(view::setDataSet, Timber::d) + ); + } + + @Override + public void onBackClick() { + view.displayMessage("back back"); + view.back(); + } + + @Override + public void showFilter() { + } + + @Override + public void addDataSet() { + } +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodRepository.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodRepository.java new file mode 100644 index 0000000000..d9a1f88831 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodRepository.java @@ -0,0 +1,19 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + +import org.hisp.dhis.android.core.dataset.DataSet; +import org.hisp.dhis.android.core.period.PeriodModel; + +import java.util.List; + +import io.reactivex.Observable; +import io.reactivex.annotations.NonNull; + +public interface DataSetPeriodRepository { + + @NonNull + Observable getDataSet(String dataSetId); + + Observable> getDataSetPeriods(String dataSetId); + + +} diff --git a/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodRepositoryImpl.java new file mode 100644 index 0000000000..8008cff6f1 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/dataset/dataSetPeriod/DataSetPeriodRepositoryImpl.java @@ -0,0 +1,37 @@ +package org.dhis2.usescases.dataset.dataSetPeriod; + + +import com.squareup.sqlbrite2.BriteDatabase; + +import org.hisp.dhis.android.core.dataset.DataSet; +import org.hisp.dhis.android.core.dataset.DataSetModel; +import org.hisp.dhis.android.core.period.PeriodModel; + +import java.util.List; + +import io.reactivex.Observable; + +public class DataSetPeriodRepositoryImpl implements DataSetPeriodRepository { + + private final BriteDatabase briteDatabase; + + public DataSetPeriodRepositoryImpl(BriteDatabase briteDatabase) { + this.briteDatabase = briteDatabase; + } + + private String DATASET_BY_ID = "SELECT * " + + "FROM DataSet " + + "WHERE DataSet.uid = ?"; + + @Override + public Observable getDataSet(String dataSetId) { + return null; + /*return briteDatabase.createQuery(DataSetModel.TABLE, DATASET_BY_ID, dataSetId) + .mapToOne(DataSetModel::create);*/ + } + + @Override + public Observable> getDataSetPeriods(String dataSetId) { + return null; + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailActivity.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailActivity.java new file mode 100644 index 0000000000..b320ab9038 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailActivity.java @@ -0,0 +1,437 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.annotation.SuppressLint; +import android.app.DatePickerDialog; +import android.databinding.DataBindingUtil; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.os.Bundle; +import android.support.v4.content.ContextCompat; +import android.support.v4.content.res.ResourcesCompat; +import android.support.v7.widget.DividerItemDecoration; +import android.view.Gravity; +import android.view.View; +import android.widget.AdapterView; + +import com.unnamed.b.atv.model.TreeNode; +import com.unnamed.b.atv.view.AndroidTreeView; + +import org.dhis2.App; +import org.dhis2.R; +import org.dhis2.databinding.ActivityDatasetDetailBinding; +import org.dhis2.usescases.general.ActivityGlobalAbstract; +import org.dhis2.usescases.main.program.OrgUnitHolder; +import org.dhis2.utils.CatComboAdapter; +import org.dhis2.utils.CustomViews.RxDateDialog; +import org.dhis2.utils.DateUtils; +import org.dhis2.utils.Period; +import org.hisp.dhis.android.core.category.CategoryComboModel; +import org.hisp.dhis.android.core.category.CategoryOptionComboModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; + +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Date; +import java.util.List; +import java.util.Locale; + +import javax.inject.Inject; + +import io.reactivex.Flowable; +import io.reactivex.processors.PublishProcessor; +import timber.log.Timber; + +import static org.dhis2.utils.Period.DAILY; +import static org.dhis2.utils.Period.MONTHLY; +import static org.dhis2.utils.Period.NONE; +import static org.dhis2.utils.Period.WEEKLY; +import static org.dhis2.utils.Period.YEARLY; + +public class DataSetDetailActivity extends ActivityGlobalAbstract implements DataSetDetailContract.View { + + private ActivityDatasetDetailBinding binding; + private ArrayList chosenDateWeek = new ArrayList<>(); + private ArrayList chosenDateMonth = new ArrayList<>(); + private ArrayList chosenDateYear = new ArrayList<>(); + private String dataSetUid; + private Period currentPeriod = Period.NONE; + private StringBuilder orgUnitFilter = new StringBuilder(); + private SimpleDateFormat monthFormat = new SimpleDateFormat("yyyy-MM", Locale.getDefault()); + private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy", Locale.getDefault()); + private Date chosenDateDay = new Date(); + private TreeNode treeNode; + private AndroidTreeView treeView; + private boolean isFilteredByCatCombo = false; + @Inject + DataSetDetailContract.Presenter presenter; + + private static PublishProcessor currentPage; + DataSetDetailAdapter adapter; + + @Override + protected void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + ((App) getApplicationContext()).userComponent().plus(new DataSetDetailModule()).inject(this); + binding = DataBindingUtil.setContentView(this, R.layout.activity_dataset_detail); + + chosenDateWeek.add(new Date()); + chosenDateMonth.add(new Date()); + chosenDateYear.add(new Date()); + + dataSetUid = getIntent().getStringExtra("DATASET_UID"); + binding.setPresenter(presenter); + + adapter = new DataSetDetailAdapter(presenter); + + currentPage = PublishProcessor.create(); + } + + @Override + protected void onResume() { + super.onResume(); + presenter.init(this); + + presenter.getDataSetWithDates(null, currentPeriod, orgUnitFilter.toString()); + } + + @Override + protected void onPause() { + presenter.onDettach(); + super.onPause(); + binding.treeViewContainer.removeAllViews(); + } + + @Override + public void setData(List datasets) { + if (binding.recycler.getAdapter() == null) { + binding.recycler.setAdapter(adapter); + binding.recycler.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL)); + } + adapter.setDatasets(datasets); + } + + @Override + public void addTree(TreeNode treeNode) { + this.treeNode = treeNode; + binding.treeViewContainer.removeAllViews(); + binding.orgUnitApply.setOnClickListener(view -> apply()); + treeView = new AndroidTreeView(getContext(), treeNode); + + treeView.setDefaultContainerStyle(R.style.TreeNodeStyle, false); + treeView.setSelectionModeEnabled(true); + + binding.treeViewContainer.addView(treeView.getView()); + if (presenter.getOrgUnits().size() < 25) + treeView.expandAll(); + + treeView.setDefaultNodeClickListener((node, value) -> { + if (treeView.getSelected().size() == 1 && !node.isSelected()) { + ((OrgUnitHolder) node.getViewHolder()).update(); + binding.buttonOrgUnit.setText(String.format(getString(R.string.org_unit_filter), treeView.getSelected().size())); + } else if (treeView.getSelected().size() > 1) { + ((OrgUnitHolder) node.getViewHolder()).update(); + binding.buttonOrgUnit.setText(String.format(getString(R.string.org_unit_filter), treeView.getSelected().size())); + } + }); + + binding.buttonOrgUnit.setText(String.format(getString(R.string.org_unit_filter), treeView.getSelected().size())); + } + + @Override + public void openDrawer() { + if (!binding.drawerLayout.isDrawerOpen(Gravity.END)) + binding.drawerLayout.openDrawer(Gravity.END); + else + binding.drawerLayout.closeDrawer(Gravity.END); + } + + @Override + public void showTimeUnitPicker() { + + Drawable drawable = null; + String textToShow = ""; + + switch (currentPeriod) { + case NONE: + currentPeriod = DAILY; + drawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_view_day); + break; + case DAILY: + currentPeriod = WEEKLY; + drawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_view_week); + break; + case WEEKLY: + currentPeriod = MONTHLY; + drawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_view_month); + break; + case MONTHLY: + currentPeriod = YEARLY; + drawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_view_year); + break; + case YEARLY: + currentPeriod = NONE; + drawable = ContextCompat.getDrawable(getContext(), R.drawable.ic_view_none); + break; + } + binding.buttonTime.setImageDrawable(drawable); + + switch (currentPeriod) { + case NONE: + // TODO + presenter.getDataSetWithDates(null, currentPeriod, orgUnitFilter.toString()); + textToShow = getString(R.string.period); + break; + case DAILY: + ArrayList datesD = new ArrayList<>(); + datesD.add(chosenDateDay); + if (!datesD.isEmpty()) + textToShow = DateUtils.getInstance().formatDate(datesD.get(0)); + if (!datesD.isEmpty() && datesD.size() > 1) textToShow += "... "; + // TODO + presenter.getDataSetWithDates(datesD, currentPeriod, orgUnitFilter.toString()); + break; + case WEEKLY: + if (!chosenDateWeek.isEmpty()) { + String week = getString(R.string.week); + SimpleDateFormat weeklyFormat = new SimpleDateFormat("'" + week + "' w", Locale.getDefault()); + textToShow = weeklyFormat.format(chosenDateWeek.get(0)) + ", " + yearFormat.format(chosenDateWeek.get(0)); + } + if (!chosenDateWeek.isEmpty() && chosenDateWeek.size() > 1) textToShow += "... "; + // TODO + presenter.getDataSetWithDates(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); + break; + case MONTHLY: + if (!chosenDateMonth.isEmpty()) { + String dateFormatted = monthFormat.format(chosenDateMonth.get(0)); + textToShow = dateFormatted.substring(0, 1).toUpperCase() + dateFormatted.substring(1); + } + if (!chosenDateMonth.isEmpty() && chosenDateMonth.size() > 1) textToShow += "... "; + // TODO + presenter.getDataSetWithDates(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); + break; + case YEARLY: + if (!chosenDateYear.isEmpty()) + textToShow = yearFormat.format(chosenDateYear.get(0)); + if (!chosenDateYear.isEmpty() && chosenDateYear.size() > 1) textToShow += "... "; + // TODO + presenter.getDataSetWithDates(chosenDateYear, currentPeriod, orgUnitFilter.toString()); + break; + } + + binding.buttonPeriodText.setText(textToShow); + } + + @SuppressLint({"RxLeakedSubscription", "CheckResult"}) + @Override + public void showRageDatePicker() { + Calendar calendar = Calendar.getInstance(); + calendar.setMinimalDaysInFirstWeek(7); + + String week = getString(R.string.week); + SimpleDateFormat weeklyFormat = new SimpleDateFormat("'" + week + "' w", Locale.getDefault()); + + if (currentPeriod != DAILY && currentPeriod != NONE) { + new RxDateDialog(getAbstractActivity(), currentPeriod).create().show().subscribe(selectedDates -> { + if (!selectedDates.isEmpty()) { + String textToShow; + if (currentPeriod == WEEKLY) { + textToShow = weeklyFormat.format(selectedDates.get(0)) + ", " + yearFormat.format(selectedDates.get(0)); + chosenDateWeek = (ArrayList) selectedDates; + if (selectedDates.size() > 1) + textToShow += "... " /*+ weeklyFormat.format(selectedDates.get(1))*/; + } else if (currentPeriod == MONTHLY) { + textToShow = monthFormat.format(selectedDates.get(0)); + chosenDateMonth = (ArrayList) selectedDates; + if (selectedDates.size() > 1) + textToShow += "... " /*+ monthFormat.format(selectedDates.get(1))*/; + } else { + textToShow = yearFormat.format(selectedDates.get(0)); + chosenDateYear = (ArrayList) selectedDates; + if (selectedDates.size() > 1) + textToShow += "... " /*+ yearFormat.format(selectedDates.get(1))*/; + + } + binding.buttonPeriodText.setText(textToShow); + + // TODO + presenter.getDataSetWithDates(selectedDates, currentPeriod, orgUnitFilter.toString()); + + } else { + ArrayList date = new ArrayList<>(); + date.add(new Date()); + + String text = ""; + + switch (currentPeriod) { + case WEEKLY: + text = weeklyFormat.format(date.get(0)) + ", " + yearFormat.format(date.get(0)); + chosenDateWeek = date; + break; + case MONTHLY: + text = monthFormat.format(date.get(0)); + chosenDateMonth = date; + break; + case YEARLY: + text = yearFormat.format(date.get(0)); + chosenDateYear = date; + break; + default: + break; + } + binding.buttonPeriodText.setText(text); + + // TODO + presenter.getDataSetWithDates(date, currentPeriod, orgUnitFilter.toString()); + } + }, + Timber::d); + } else if (currentPeriod == DAILY) { + Calendar cal = Calendar.getInstance(); + cal.setTime(chosenDateDay); + DatePickerDialog pickerDialog; + pickerDialog = new DatePickerDialog(getContext(), (datePicker, year, monthOfYear, dayOfMonth) -> { + calendar.set(year, monthOfYear, dayOfMonth); + Date[] dates = DateUtils.getInstance().getDateFromDateAndPeriod(calendar.getTime(), currentPeriod); + ArrayList day = new ArrayList<>(); + day.add(dates[0]); + // TODO + presenter.getDataSetWithDates(day, currentPeriod, orgUnitFilter.toString()); + binding.buttonPeriodText.setText(DateUtils.getInstance().formatDate(dates[0])); + chosenDateDay = dates[0]; + }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)); + pickerDialog.show(); + } + } + + @Override + public void setCatComboOptions(CategoryComboModel catCombo, List catComboList) { + if (catCombo.uid().equals(CategoryComboModel.DEFAULT_UID) || catComboList == null || catComboList.isEmpty()) { + binding.catCombo.setVisibility(View.GONE); + binding.catCombo.setVisibility(View.GONE); + } else { + binding.catCombo.setVisibility(View.VISIBLE); + CatComboAdapter adapter = new CatComboAdapter(this, + R.layout.spinner_layout, + R.id.spinner_text, + catComboList, + catCombo.displayName(), + R.color.white_faf); + + binding.catCombo.setVisibility(View.VISIBLE); + binding.catCombo.setAdapter(adapter); + + binding.catCombo.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (position == 0) { + isFilteredByCatCombo = false; + presenter.clearCatComboFilters(orgUnitFilter.toString()); + } else { + isFilteredByCatCombo = true; + presenter.onCatComboSelected(adapter.getItem(position - 1), orgUnitFilter.toString()); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + isFilteredByCatCombo = false; + presenter.clearCatComboFilters(orgUnitFilter.toString()); + } + }); + } + } + + @Override + public void setOrgUnitFilter(StringBuilder orgUnitFilter) { + this.orgUnitFilter = orgUnitFilter; + } + + @Override + public void showHideFilter() { + binding.filterLayout.setVisibility(binding.filterLayout.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); + checkFilterEnabled(); + } + + @Override + public void apply() { + binding.drawerLayout.closeDrawers(); + orgUnitFilter = new StringBuilder(); + for (int i = 0; i < treeView.getSelected().size(); i++) { + orgUnitFilter.append("'"); + orgUnitFilter.append(((OrganisationUnitModel) treeView.getSelected().get(i).getValue()).uid()); + orgUnitFilter.append("'"); + if (i < treeView.getSelected().size() - 1) + orgUnitFilter.append(", "); + } + + + switch (currentPeriod) { + case NONE: + // TODO + presenter.getDataSetWithDates(null, currentPeriod, orgUnitFilter.toString()); + break; + case DAILY: + ArrayList datesD = new ArrayList<>(); + datesD.add(chosenDateDay); + // TODO + presenter.getDataSetWithDates(datesD, currentPeriod, orgUnitFilter.toString()); + break; + case WEEKLY: + // TODO + presenter.getDataSetWithDates(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); + break; + case MONTHLY: + // TODO + presenter.getDataSetWithDates(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); + break; + case YEARLY: + // TODO + presenter.getDataSetWithDates(chosenDateYear, currentPeriod, orgUnitFilter.toString()); + break; + } + } + + @Override + public void setWritePermission(Boolean canWrite) { + binding.addDatasetButton.setVisibility(canWrite ? View.VISIBLE : View.GONE); + } + + @Override + public Flowable dataSetPage() { + return currentPage; + } + + @Override + public String dataSetUid() { + return dataSetUid; + } + + private void checkFilterEnabled() { + if (binding.filterLayout.getVisibility() == View.VISIBLE) { + binding.filter.setBackgroundColor(getPrimaryColor()); + binding.filter.setColorFilter(getResources().getColor(R.color.white), PorterDuff.Mode.SRC_IN); + binding.filter.setBackgroundResource(0); + } + // when filter layout is hidden + else { + // not applied period filter + if (currentPeriod == Period.NONE && areAllOrgUnitsSelected() && !isFilteredByCatCombo) { + binding.filter.setBackgroundColor(getPrimaryColor()); + binding.filter.setColorFilter(getResources().getColor(R.color.white), PorterDuff.Mode.SRC_IN); + binding.filter.setBackgroundResource(0); + } + // applied period filter + else { + binding.filter.setBackgroundColor(ResourcesCompat.getColor(getResources(), R.color.white, getTheme())); + binding.filter.setColorFilter(getPrimaryColor(), PorterDuff.Mode.SRC_IN); + binding.filter.setBackgroundResource(R.drawable.white_circle); + } + } + } + + private boolean areAllOrgUnitsSelected() { + return treeNode != null && treeNode.getChildren().size() == treeView.getSelected().size(); + } + +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailAdapter.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailAdapter.java new file mode 100644 index 0000000000..9e5279ff9e --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailAdapter.java @@ -0,0 +1,53 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.databinding.DataBindingUtil; +import android.support.annotation.NonNull; +import android.support.v7.util.DiffUtil; +import android.support.v7.widget.RecyclerView; +import android.view.LayoutInflater; +import android.view.ViewGroup; + +import org.dhis2.R; +import org.dhis2.databinding.ItemDatasetBinding; +import org.hisp.dhis.android.core.dataset.DataSetModel; + +import java.util.ArrayList; +import java.util.List; + +public class DataSetDetailAdapter extends RecyclerView.Adapter { + + private DataSetDetailContract.Presenter presenter; + private List datasets; + + public DataSetDetailAdapter(DataSetDetailContract.Presenter presenter) { + this.presenter = presenter; + this.datasets = new ArrayList<>(); + } + + @NonNull + @Override + public DataSetDetailViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) { + LayoutInflater inflater = LayoutInflater.from(parent.getContext()); + ItemDatasetBinding binding = DataBindingUtil.inflate(inflater, R.layout.item_dataset, parent, false); + + return new DataSetDetailViewHolder(binding); + } + + @Override + public void onBindViewHolder(@NonNull DataSetDetailViewHolder holder, int position) { + DataSetDetailModel dataSetModel = datasets.get(position); + holder.bind(presenter, dataSetModel); + } + + @Override + public int getItemCount() { + return datasets.size(); + } + + public void setDatasets(List datasets){ + DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new DataSetDiffCallback(this.datasets, datasets)); + this.datasets.clear(); + this.datasets.addAll(datasets); + diffResult.dispatchUpdatesTo(this); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailComponent.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailComponent.java new file mode 100644 index 0000000000..46ec0c2cf7 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailComponent.java @@ -0,0 +1,12 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import org.dhis2.data.dagger.PerActivity; + +import dagger.Subcomponent; + + +@Subcomponent (modules = DataSetDetailModule.class) +@PerActivity +public interface DataSetDetailComponent { + void inject(DataSetDetailActivity activity); +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailContract.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailContract.java new file mode 100644 index 0000000000..0d6802829b --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailContract.java @@ -0,0 +1,76 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import com.unnamed.b.atv.model.TreeNode; + +import org.dhis2.usescases.general.AbstractActivityContracts; +import org.dhis2.utils.Period; +import org.hisp.dhis.android.core.category.CategoryComboModel; +import org.hisp.dhis.android.core.category.CategoryOptionComboModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; + +import java.util.Date; +import java.util.List; + +import io.reactivex.Flowable; + +public class DataSetDetailContract { + + public interface View extends AbstractActivityContracts.View { + void setData(List dataSetDetailModels); + + void addTree(TreeNode treeNode); + + void openDrawer(); + + void showTimeUnitPicker(); + + void showRageDatePicker(); + + void renderError(String message); + + void setCatComboOptions(CategoryComboModel catCombo, List catComboList); + + void setOrgUnitFilter(StringBuilder orgUnitFilter); + + void showHideFilter(); + + void apply(); + + void setWritePermission(Boolean aBoolean); + + Flowable dataSetPage(); + + String dataSetUid(); + } + + public interface Presenter extends AbstractActivityContracts.Presenter { + void init(View view); + + void onTimeButtonClick(); + + void onDateRangeButtonClick(); + + void onOrgUnitButtonClick(); + + void addDataSet(); + + void onBackClick(); + + void onCatComboSelected(CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery); + + void clearCatComboFilters(String orgUnitQuery); + + void onDataSetClick(String eventId, String orgUnit); + + List getOrgUnits(); + + void showFilter(); + + void getDataSets(Date fromDate, Date toDate, String orgUnitQuery); + + void getOrgUnits(Date date); + + void getDataSetWithDates(List dates, Period period, String orgUnitQuery); + + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailModel.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailModel.java new file mode 100644 index 0000000000..fe4107f257 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailModel.java @@ -0,0 +1,38 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.support.annotation.NonNull; + +import com.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.common.State; + +@AutoValue +public abstract class DataSetDetailModel { + + @NonNull + public abstract String orgUnitUid(); + + @NonNull + public abstract String catOptionComboUid(); + + @NonNull + public abstract String periodId(); + + @NonNull + public abstract String nameOrgUnit(); + + @NonNull + public abstract String nameCatCombo(); + + @NonNull + public abstract String namePeriod(); + + @NonNull + public abstract State state(); + + @NonNull + public static DataSetDetailModel create(@NonNull String orgUnitUid, @NonNull String catOptionComboUid, @NonNull String periodId, @NonNull String orgUnitName, String nameCatCombo, String namePeriod, State state) { + return new AutoValue_DataSetDetailModel(orgUnitUid, catOptionComboUid, periodId, orgUnitName, nameCatCombo, namePeriod, state); + } + +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailModule.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailModule.java new file mode 100644 index 0000000000..2417a4db1c --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailModule.java @@ -0,0 +1,37 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import org.dhis2.data.dagger.PerActivity; +import org.dhis2.data.metadata.MetadataRepository; +import org.dhis2.usescases.programDetail.ProgramRepository; +import org.dhis2.usescases.programDetail.ProgramRepositoryImpl; +import org.dhis2.usescases.programEventDetail.ProgramEventDetailRepository; +import org.dhis2.usescases.programEventDetail.ProgramEventDetailRepositoryImpl; +import com.squareup.sqlbrite2.BriteDatabase; + +import dagger.Module; +import dagger.Provides; + +@PerActivity +@Module +public class DataSetDetailModule { + + + @Provides + @PerActivity + DataSetDetailContract.View provideView(DataSetDetailActivity activity) { + return activity; + } + + @Provides + @PerActivity + DataSetDetailContract.Presenter providesPresenter(DataSetDetailRepository dataSetDetailRepository, + MetadataRepository metadataRepository) { + return new DataSetDetailPresenter(dataSetDetailRepository, metadataRepository); + } + + @Provides + @PerActivity + DataSetDetailRepository eventDetailRepository(BriteDatabase briteDatabase) { + return new DataSetDetailRepositoryImpl(briteDatabase); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailPeriodEnum.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailPeriodEnum.java new file mode 100644 index 0000000000..57c6ed6b31 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailPeriodEnum.java @@ -0,0 +1,207 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import org.joda.time.DateTime; +import org.joda.time.DateTimeConstants; +import org.joda.time.LocalDate; +import org.joda.time.format.DateTimeFormat; + +import java.util.ArrayList; +import java.util.List; + +public enum DataSetDetailPeriodEnum { + + DAILY("Daily") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + List listDataSet = new ArrayList<>(); + /*if(year <= maxDate().year().get()){ + + for(int month = 1; month<= maxDate().monthOfYear().get(); month++){ + DateTime currentMonth = new DateTime().withMonthOfYear(month); + for(int day = 1; day< currentMonth.dayOfMonth().withMaximumValue().getDayOfMonth(); day++){ + if(month == maxDate().monthOfYear().get() && day == maxDate().dayOfMonth().get()){ + break; + } + + listDataSet.add(new DataSetDetailModel(dataset.getUidDataSet(), + dataset.getNameOrgUnit(),dataset.getNameCatCombo(), + new LocalDate( year, month, day).toString())); + } + + } + }*/ + + return listDataSet; + } + }, + + WEEKLY("Weekly") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsWeeks(year, dataset, DateTimeConstants.MONDAY); + } + }, + + WEEKLYWEDNESDAY("WeeklyWednesday") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsWeeks(year, dataset, DateTimeConstants.WEDNESDAY); + } + }, + + WEEKLYTHURSDAY("WeeklyThursday") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsWeeks(year, dataset, DateTimeConstants.THURSDAY); + } + }, + + WEEKLYSATURDAY("WeeklySaturday") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsWeeks(year, dataset, DateTimeConstants.SATURDAY); + } + }, + + WEEKLYSUNDAY("WeeklySunday") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsWeeks(year, dataset, DateTimeConstants.SUNDAY); + } + }, + + BIWEEKLY("BiWeekly"){ + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + List listDataSet = new ArrayList<>(); + /*if(year <= maxDate().year().get()){ + int biweek = 1; + for(int week = 1; week < maxDate().getWeekOfWeekyear(); week++){ + DateTime currentWeek = new DateTime().withWeekOfWeekyear(week); + String date = "W"+ biweek + " " + new LocalDate(year, currentWeek.getMonthOfYear(),currentWeek.getDayOfMonth()).toString() + " - " + + new LocalDate(year, currentWeek.plusWeeks(2).getMonthOfYear(),currentWeek.plusWeeks(2).getDayOfMonth()-1).toString(); + if(currentWeek.plusWeeks(2).getWeekOfWeekyear() >= maxDate().getWeekOfWeekyear()){ + break; + } + listDataSet.add(new DataSetDetailModel(dataset.getUidDataSet(), + dataset.getNameOrgUnit(),dataset.getNameCatCombo(), + date)); + biweek ++; + week ++; + } + }*/ + return listDataSet; + } + }, + + MONTHLY("Monthly") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsMonth(year, dataset, DateTimeConstants.JANUARY, 0); + } + }, + + BIMONTHLY("BiMonthly") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsMonth(year, dataset, DateTimeConstants.JANUARY, 1); + } + }, + QUATERLY("Quarterly") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsMonth(year, dataset, DateTimeConstants.JANUARY, 2); + } + }, + SIXMONTHLY("SixMonthly") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsMonth(year, dataset, DateTimeConstants.JANUARY, 5); + } + }, + SIXMONTHLYAPRIL("SixMonthlyApril") { + public List getListDataSetWithPeriods(int year, DataSetDetailModel dataset){ + return getListDataSetWithPeriodsMonth(year, dataset, DateTimeConstants.APRIL, 5); + } + }, + YEARLY("Yearly"){ + public List getListDataSetWithPeriods(int currentYear, DataSetDetailModel dataset){ + List listDataSet = new ArrayList<>(); + /* if(currentYear <= maxDate().year().get()){ + + for(int year = currentYear; year > 2008; year--){ + DateTime currentMonth = new DateTime().withYear(year); + String date = currentMonth.toString(DateTimeFormat.forPattern("yyyy")); + listDataSet.add(new DataSetDetailModel(dataset.getUidDataSet(), + dataset.getNameOrgUnit(),dataset.getNameCatCombo(), + date)); + + } + }*/ + return listDataSet; + } + } + ; + + + + public abstract List getListDataSetWithPeriods(int year, DataSetDetailModel dataset); + + public static List getListDataSetWithPeriodsWeeks(int year, DataSetDetailModel dataset, int day){ + List listDataSet = new ArrayList<>(); + /*if(year <= maxDate().year().get()){ + + for(int week = 1; week < maxDate().getWeekOfWeekyear(); week++){ + DateTime currentWeek = new DateTime().withWeekOfWeekyear(week).withDayOfWeek(day); + String date = "W"+ week + " " + new LocalDate(year, currentWeek.getMonthOfYear(),currentWeek.getDayOfMonth()).toString() + " - " + + new LocalDate(year, currentWeek.plusDays(6).getMonthOfYear(),currentWeek.plusDays(6).getDayOfMonth()).toString(); + if(currentWeek.plusDays(6).getWeekOfWeekyear() == maxDate().getWeekOfWeekyear()){ + break; + } + listDataSet.add(new DataSetDetailModel(dataset.getUidDataSet(), + dataset.getNameOrgUnit(),dataset.getNameCatCombo(), + date)); + } + }*/ + return listDataSet; + } + + public static List getListDataSetWithPeriodsMonth(int year, DataSetDetailModel dataset, int startMonth, int period){ + List listDataSet = new ArrayList<>(); + /*if(year <= maxDate().year().get()){ + + for(int month = 1; month< maxDate().monthOfYear().get(); month++){ + DateTime currentMonth = new DateTime().withMonthOfYear(month).withYear(year).withMonthOfYear(startMonth + month - 1); + String date = currentMonth.toString("MMMM") + " - " + + currentMonth.plusMonths(period).toString(DateTimeFormat.forPattern("MMMM yyyy")); + date = period == 0 ? currentMonth.plusMonths(period).toString(DateTimeFormat.forPattern("MMMM yyyy")): date; + if(currentMonth.plusMonths(period).monthOfYear().get() >= maxDate().monthOfYear().get()){ + break; + } + listDataSet.add(new DataSetDetailModel(dataset.getUidDataSet(), + dataset.getNameOrgUnit(),dataset.getNameCatCombo(), + date)); + + month = month+period; + } + }*/ + + return listDataSet; + } + + private String periodTypeName; + + DataSetDetailPeriodEnum(String periodTypeName) { + this.periodTypeName = periodTypeName; + } + + public String getPeriodTypeName() { + return periodTypeName; + } + + public static DataSetDetailPeriodEnum getDataSetPeriod(String namePeriod){ + + for(DataSetDetailPeriodEnum dataSetDetailPeriodEnum: DataSetDetailPeriodEnum.values()){ + if(dataSetDetailPeriodEnum.getPeriodTypeName().equals(namePeriod)){ + return dataSetDetailPeriodEnum; + } + } + + return null; + } + + private static DateTime maxDate(){ + return new LocalDate().toDateTimeAtCurrentTime(); + } + + +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailPresenter.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailPresenter.java new file mode 100644 index 0000000000..c21fe544c5 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailPresenter.java @@ -0,0 +1,203 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.annotation.SuppressLint; +import android.os.Bundle; +import android.support.annotation.IntDef; + +import org.dhis2.data.metadata.MetadataRepository; +import org.dhis2.usescases.datasets.datasetInitial.DataSetInitialActivity; +import org.dhis2.utils.Constants; +import org.dhis2.utils.OrgUnitUtils; +import org.dhis2.utils.Period; +import org.hisp.dhis.android.core.category.CategoryComboModel; +import org.hisp.dhis.android.core.category.CategoryOptionComboModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; +import org.hisp.dhis.android.core.period.PeriodType; + +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.util.Date; +import java.util.List; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + + +public class DataSetDetailPresenter implements DataSetDetailContract.Presenter { + + private DataSetDetailRepository dataSetDetailRepository; + private DataSetDetailContract.View view; + private CategoryOptionComboModel categoryOptionComboModel; + private MetadataRepository metadataRepository; + private int lastSearchType; + private Date fromDate; + private Date toDate; + private Period period; + private List dates; + private CompositeDisposable compositeDisposable; + private List orgUnits; + private CategoryComboModel mCatCombo; + private List selectedOrgUnits; + private PeriodType selectedPeriodType; + + @Retention(RetentionPolicy.SOURCE) + @IntDef({LastSearchType.DATES, LastSearchType.DATE_RANGES}) + public @interface LastSearchType { + int DATES = 1; + int DATE_RANGES = 32; + } + + public DataSetDetailPresenter(DataSetDetailRepository dataSetDetailRepository, MetadataRepository metadataRepository) { + this.dataSetDetailRepository = dataSetDetailRepository; + this.metadataRepository = metadataRepository; + compositeDisposable = new CompositeDisposable(); + } + + @Override + public void init(DataSetDetailContract.View view) { + this.view = view; + getOrgUnits(null); + compositeDisposable.add( + view.dataSetPage() + .startWith(0) + .flatMap(page -> dataSetDetailRepository.dataSetGroups(view.dataSetUid(), selectedOrgUnits, selectedPeriodType, page)) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + view::setData, + Timber::d + ) + ); + } + + @Override + public void onTimeButtonClick() { + view.showTimeUnitPicker(); + } + + @Override + public void onDateRangeButtonClick() { + view.showRageDatePicker(); + } + + @Override + public void onOrgUnitButtonClick() { + view.openDrawer(); + } + + + @Override + public void addDataSet() { + Bundle bundle = new Bundle(); + bundle.putString(Constants.DATA_SET_UID, view.dataSetUid()); + + view.startActivity(DataSetInitialActivity.class,bundle,false,false,null); + } + + @Override + public void onBackClick() { + if (view != null) + view.back(); + } + + @Override + public void onCatComboSelected(CategoryOptionComboModel categoryOptionComboModel, String + orgUnitQuery) { + updateFilters(categoryOptionComboModel, orgUnitQuery); + } + + @Override + public void clearCatComboFilters(String orgUnitQuery) { + updateFilters(null, orgUnitQuery); + } + + @Override + public void onDataSetClick(String eventId, String orgUnit) { + + } + + @Override + public List getOrgUnits() { + return this.orgUnits; + } + + @Override + public void showFilter() { + view.showHideFilter(); + } + + @SuppressLint("CheckResult") + @Override + public void getDataSets(Date fromDate, Date toDate, String orgUnitQuery) { + this.fromDate = fromDate; + this.toDate = toDate; + lastSearchType = LastSearchType.DATES; + /*Observable.just(dataSetDetailRepository.filteredDataSet(programId, + DateUtils.getInstance().formatDate(fromDate), + DateUtils.getInstance().formatDate(toDate), + categoryOptionComboModel) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + list ->view.setData(getPeriodFromType(list)), + Timber::e));*/ + } + + @Override + public void getOrgUnits(Date date) { + compositeDisposable.add(dataSetDetailRepository.orgUnits() + .map(orgUnits -> { + this.orgUnits = orgUnits; + return OrgUnitUtils.renderTree(view.getContext(), orgUnits, true); + }) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + treeNode -> view.addTree(treeNode), + throwable -> view.renderError(throwable.getMessage()) + )); + } + + private void updateFilters(CategoryOptionComboModel categoryOptionComboModel, String + orgUnitQuery) { + this.categoryOptionComboModel = categoryOptionComboModel; + switch (lastSearchType) { + case LastSearchType.DATES: + getDataSets(this.fromDate, this.toDate, orgUnitQuery); + break; + case LastSearchType.DATE_RANGES: + getDataSetWithDates(this.dates, this.period, orgUnitQuery); + break; + default: + getDataSetWithDates(null, this.period, orgUnitQuery); + break; + } + } + + @Override + public void getDataSetWithDates(List dates, Period period, String orgUnitQuery) { + this.dates = dates; + this.period = period; + lastSearchType = LastSearchType.DATE_RANGES; + //FIXME cuando haya datos para dataset hay que cambiarlo + //ahora falla por que se va a hacer la select y no puede + /* compositeDisposable.add(dataSetDetailRepository.filteredDataSet(programId,"","", categoryOptionComboModel) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + list ->view.setData(getPeriodFromType(list)), + throwable -> view.renderError(throwable.getMessage())));*/ + } + + @Override + public void onDettach() { + compositeDisposable.clear(); + } + + @Override + public void displayMessage(String message) { + view.displayMessage(message); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailRepository.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailRepository.java new file mode 100644 index 0000000000..66ef3b8cf1 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailRepository.java @@ -0,0 +1,19 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.support.annotation.NonNull; + +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; +import org.hisp.dhis.android.core.period.PeriodType; + +import java.util.List; + +import io.reactivex.Flowable; +import io.reactivex.Observable; + +public interface DataSetDetailRepository { + + @NonNull + Observable> orgUnits(); + + Flowable> dataSetGroups(String dataSetUid, List selectedOrgUnit, PeriodType selectedPeriodType, int page); +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailRepositoryImpl.java new file mode 100644 index 0000000000..d1ba8c6edf --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailRepositoryImpl.java @@ -0,0 +1,133 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.database.Cursor; +import android.support.annotation.NonNull; + +import com.squareup.sqlbrite2.BriteDatabase; + +import org.dhis2.utils.DateUtils; +import org.hisp.dhis.android.core.common.State; +import org.hisp.dhis.android.core.datavalue.DataValueModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; +import org.hisp.dhis.android.core.period.PeriodModel; +import org.hisp.dhis.android.core.period.PeriodType; + +import java.util.List; + +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; +import io.reactivex.Observable; + +public class DataSetDetailRepositoryImpl implements DataSetDetailRepository { + + private final static String GET_DATA_SETS = "SELECT " + + "DataValue.organisationUnit, " + + "DataValue.period, " + + "DataValue.attributeOptionCombo " + + "FROM DataValue " + + "JOIN DataSetDataElementLink " + + "ON DataSetDataElementLink.dataElement = DataValue.dataElement " + + "WHERE DataSetDataElementLink.dataSet = ? %s " + + "GROUP BY DataValue.period,DataValue.organisationUnit,DataValue.categoryOptionCombo"; + + private final static String DATA_SETS_ORG_UNIT_FILTER = "AND DataValue.organisationUnit IN (%s) "; + private final static String DATA_SETS_PERIOD_FILTER = "AND DataValue.period = ? "; + + private final BriteDatabase briteDatabase; + + public DataSetDetailRepositoryImpl(BriteDatabase briteDatabase) { + this.briteDatabase = briteDatabase; + } + + + @NonNull + @Override + public Observable> orgUnits() { + String SELECT_ORG_UNITS = "SELECT * FROM " + OrganisationUnitModel.TABLE; + return briteDatabase.createQuery(OrganisationUnitModel.TABLE, SELECT_ORG_UNITS) + .mapToList(OrganisationUnitModel::create); + } + + @Override + public Flowable> dataSetGroups(String dataSetUid, List orgUnits, PeriodType selectedPeriodType, int page) { + String SQL = GET_DATA_SETS; + String orgUnitFilter = ""; + if (orgUnits != null && !orgUnits.isEmpty()) { + StringBuilder orgUnitUids = new StringBuilder(""); + for (int i = 0; i < orgUnits.size(); i++) { + orgUnitUids.append(orgUnits.get(i)); + if (i != orgUnits.size() - 1) + orgUnitUids.append(","); + } + + orgUnitFilter = String.format(DATA_SETS_ORG_UNIT_FILTER, orgUnitFilter); + } + + SQL = String.format(SQL, orgUnitFilter); + + return briteDatabase.createQuery(DataValueModel.TABLE, SQL, dataSetUid) + .mapToList(cursor -> { + String organisationUnitUid = cursor.getString(0); + String period = cursor.getString(1); + String categoryOptionCombo = cursor.getString(2); + + String orgUnitName = ""; + String periodName = ""; + String catOptCombName = ""; + State state = State.SYNCED; + Cursor orgUnitCursor = briteDatabase.query("SELECT OrganisationUnit.displayName FROM OrganisationUnit WHERE uid = ?", organisationUnitUid); + if (orgUnitCursor != null && orgUnitCursor.moveToFirst()) { + orgUnitName = orgUnitCursor.getString(0); + orgUnitCursor.close(); + } + + Cursor periodCursor = briteDatabase.query("SELECT Period.* FROM Period WHERE Period.periodId = ?", period); + if (periodCursor != null && periodCursor.moveToFirst()) { + PeriodModel periodModel = PeriodModel.create(periodCursor); + periodName = DateUtils.getInstance().getPeriodUIString(periodModel.periodType(), periodModel.startDate()); + periodCursor.close(); + } + + Cursor catOptCombCursor = briteDatabase.query("SELECT CategoryOptionCombo.displayName FROM CategoryOptionCombo WHERE uid = ?", categoryOptionCombo); + if (catOptCombCursor != null && catOptCombCursor.moveToFirst()) { + catOptCombName = catOptCombCursor.getString(0); + catOptCombCursor.close(); + } + + Cursor stateCursor = briteDatabase.query("SELECT DataValue.state FROM DataValue " + + "WHERE period = ? AND organisationUnit = ? AND attributeOptionCombo = ? " + + "AND state != 'SYNCED'", + period, organisationUnitUid, categoryOptionCombo); + if (stateCursor != null && stateCursor.moveToFirst()) { + State errorState = null; + State toPost = null; + State toUpdate = null; + for (int i = 0; i < cursor.getCount(); i++) { + State stateValue = State.valueOf(cursor.getString(0)); + switch (stateValue) { + case ERROR: + errorState = State.ERROR; + break; + case TO_POST: + toPost = State.TO_POST; + break; + case TO_UPDATE: + toUpdate = State.TO_UPDATE; + break; + } + cursor.moveToNext(); + } + stateCursor.close(); + + if (errorState != null) + state = errorState; + else if (toUpdate != null) + state = toUpdate; + else if (toPost != null) + state = toPost; + } + + return DataSetDetailModel.create(organisationUnitUid, categoryOptionCombo, period, orgUnitName, catOptCombName, periodName, state); + }).toFlowable(BackpressureStrategy.LATEST); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailViewHolder.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailViewHolder.java new file mode 100644 index 0000000000..80e287877e --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDetailViewHolder.java @@ -0,0 +1,49 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.support.v7.widget.RecyclerView; + +import com.android.databinding.library.baseAdapters.BR; +import org.dhis2.databinding.ItemDatasetBinding; + +import org.hisp.dhis.android.core.dataset.DataSetModel; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +public class DataSetDetailViewHolder extends RecyclerView.ViewHolder{ + + private ItemDatasetBinding binding; + private CompositeDisposable disposable; + + public DataSetDetailViewHolder(ItemDatasetBinding binding) { + super(binding.getRoot()); + this.binding = binding; + disposable = new CompositeDisposable(); + } + + public void bind(DataSetDetailContract.Presenter presenter, DataSetDetailModel dataset) { + binding.setVariable(BR.presenter, presenter); + binding.setVariable(BR.dataset, dataset); + binding.executePendingBindings(); + //FIXME revisar para que sirve esto + /*disposable.add(presenter.getDataSetDataValueNew(dataset) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + values -> { + StringBuilder stringBuilder = new StringBuilder(""); + int valuesSize = values.size() > 3 ? 3 : values.size(); + for (int i = 0; i < valuesSize; i++) { + if (values.get(i) != null) + stringBuilder.append(values.get(i)).append("\n"); + } + binding.dataValue.setText(stringBuilder); + }, + Timber::d + ));*/ + +// itemView.setOnClickListener(view -> presenter.onDataSetClick(dataset.getUidDataSet(),null)); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDiffCallback.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDiffCallback.java new file mode 100644 index 0000000000..58b32f65d9 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetDetail/DataSetDiffCallback.java @@ -0,0 +1,38 @@ +package org.dhis2.usescases.datasets.datasetDetail; + +import android.support.v7.util.DiffUtil; + +import java.util.List; + +public class DataSetDiffCallback extends DiffUtil.Callback { + + private List oldList; + private List newList; + + public DataSetDiffCallback(List oldList, List newList) { + this.oldList = oldList; + this.newList = newList; + } + + @Override + public int getOldListSize() { + return oldList.size(); + } + + @Override + public int getNewListSize() { + return newList.size(); + } + + @Override + public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) { + return oldList.get(oldItemPosition) + .equals(newList.get(newItemPosition)); + } + + @Override + public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) { + return oldList.get(oldItemPosition) + .equals(newList.get(newItemPosition)); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialActivity.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialActivity.java new file mode 100644 index 0000000000..d26b7b4530 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialActivity.java @@ -0,0 +1,157 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import android.databinding.DataBindingUtil; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.design.widget.TextInputEditText; +import android.view.Gravity; +import android.view.Menu; +import android.view.View; +import android.widget.PopupMenu; + +import org.dhis2.App; +import org.dhis2.R; +import org.dhis2.databinding.ActivityDatasetInitialBinding; +import org.dhis2.databinding.ItemCategoryComboBinding; +import org.dhis2.usescases.general.ActivityGlobalAbstract; +import org.dhis2.utils.Constants; +import org.dhis2.utils.CustomViews.OrgUnitDialog; +import org.dhis2.utils.CustomViews.PeriodDialog; +import org.dhis2.utils.DateUtils; +import org.hisp.dhis.android.core.category.CategoryModel; +import org.hisp.dhis.android.core.category.CategoryOptionModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; +import org.hisp.dhis.android.core.period.PeriodType; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; + +import javax.inject.Inject; + +public class DataSetInitialActivity extends ActivityGlobalAbstract implements DataSetInitialContract.View { + + private ActivityDatasetInitialBinding binding; + View selectedView; + @Inject + DataSetInitialContract.Presenter presenter; + + private HashMap selectedCatOptions; + private OrganisationUnitModel selectedOrgUnit; + private Date selectedPeriod; + + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + String dataSetUid = getIntent().getStringExtra(Constants.DATA_SET_UID); + ((App) getApplicationContext()).userComponent().plus(new DataSetInitialModule(dataSetUid)).inject(this); + + binding = DataBindingUtil.setContentView(this, R.layout.activity_dataset_initial); + binding.setPresenter(presenter); + } + + @Override + protected void onResume() { + super.onResume(); + presenter.init(this); + } + + @Override + protected void onPause() { + presenter.onDettach(); + super.onPause(); + } + + @Override + public void setAccessDataWrite(Boolean canWrite) { + + } + + @Override + public void setData(DataSetInitialModel dataSetInitialModel) { + binding.setDataSetModel(dataSetInitialModel); + binding.catComboContainer.removeAllViews(); + selectedCatOptions = new HashMap<>(); + for (CategoryModel categoryModel : dataSetInitialModel.categories()) { + selectedCatOptions.put(categoryModel.uid(), null); + ItemCategoryComboBinding categoryComboBinding = ItemCategoryComboBinding.inflate(getLayoutInflater(), binding.catComboContainer, false); + categoryComboBinding.inputLayout.setHint(categoryModel.displayName()); + categoryComboBinding.inputEditText.setOnClickListener(view -> { + selectedView = view; + presenter.onCatOptionClick(categoryModel.uid()); + }); + binding.catComboContainer.addView(categoryComboBinding.getRoot()); + } + checkActionVisivbility(); + } + + /** + * When changing orgUnit, date must be cleared + */ + @Override + public void showOrgUnitDialog(List data) { + OrgUnitDialog orgUnitDialog = OrgUnitDialog.newInstace(false); + orgUnitDialog.setOrgUnits(data); + orgUnitDialog.setTitle(getString(R.string.org_unit)) + .setPossitiveListener(v -> { + if (orgUnitDialog.getSelectedOrgUnit() != null) { + selectedOrgUnit = orgUnitDialog.getSelectedOrgUnitModel(); + binding.dataSetOrgUnitEditText.setText(selectedOrgUnit.displayName()); + binding.dataSetPeriodEditText.setText(""); + } + checkActionVisivbility(); + orgUnitDialog.dismiss(); + }) + .setNegativeListener(v -> orgUnitDialog.dismiss()); + orgUnitDialog.show(getSupportFragmentManager(), OrgUnitDialog.class.getSimpleName()); + } + + @Override + public void showPeriodSelector(PeriodType periodType) { + new PeriodDialog() + .setPeriod(periodType) +// .setMinDate() TODO: Depends on dataSet expiration settings and orgUnit Opening date +// .setMaxDate() TODO: Depends on dataSet open Future settings. Default: TODAY + .setMaxDate(DateUtils.getInstance().getCalendar().getTime()) + .setPossitiveListener(selectedDate -> { + this.selectedPeriod = selectedDate; + binding.dataSetPeriodEditText.setText(DateUtils.getInstance().getPeriodUIString(periodType, selectedDate)); + checkActionVisivbility(); + }) + .show(getSupportFragmentManager(), PeriodDialog.class.getSimpleName()); + } + + @Override + public void showCatComboSelector(String catOptionUid, List data) { + PopupMenu menu = new PopupMenu(this, selectedView, Gravity.BOTTOM); +// menu.getMenu().add(Menu.NONE, Menu.NONE, 0, viewModel.label()); Don't show label + for (CategoryOptionModel optionModel : data) + menu.getMenu().add(Menu.NONE, Menu.NONE, data.indexOf(optionModel), optionModel.displayName()); + + menu.setOnDismissListener(menu1 -> selectedView = null); + menu.setOnMenuItemClickListener(item -> { + if (selectedCatOptions == null) + selectedCatOptions = new HashMap<>(); + selectedCatOptions.put(catOptionUid, data.get(item.getOrder())); + ((TextInputEditText) selectedView).setText(data.get(item.getOrder()).displayName()); + checkActionVisivbility(); + return false; + }); + menu.show(); + } + + private void checkActionVisivbility() { + boolean visible = true; + if (selectedOrgUnit == null) + visible = false; + if (selectedPeriod == null) + visible = false; + for (String key : selectedCatOptions.keySet()) { + if (selectedCatOptions.get(key) == null) + visible = false; + } + + binding.actionButton.setVisibility(visible ? View.VISIBLE : View.GONE); + + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialComponent.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialComponent.java new file mode 100644 index 0000000000..dce235cdfb --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialComponent.java @@ -0,0 +1,15 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import org.dhis2.data.dagger.PerActivity; + +import dagger.Subcomponent; + +/** + * QUADRAM. Created by ppajuelo on 24/09/2018. + */ + +@Subcomponent(modules = DataSetInitialModule.class) +@PerActivity +public interface DataSetInitialComponent { + void inject(DataSetInitialActivity activity); +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialContract.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialContract.java new file mode 100644 index 0000000000..ce224d330d --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialContract.java @@ -0,0 +1,52 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import org.dhis2.usescases.general.AbstractActivityContracts; +import org.hisp.dhis.android.core.category.CategoryOptionModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; +import org.hisp.dhis.android.core.period.PeriodType; + +import java.util.List; + +public class DataSetInitialContract { + + public enum Action { + ACTION_CREATE, + ACTION_UPDATE, + ACTION_CHECK + } + + public static final int ACTION_CREATE = 0; + public static final int ACTION_UPDATE = 1; + public static final int ACTION_CHECK = 2; + + + public interface View extends AbstractActivityContracts.View { + + void setAccessDataWrite(Boolean canWrite); + + void setData(DataSetInitialModel dataSetInitialModel); + + void showOrgUnitDialog(List data); + + void showPeriodSelector(PeriodType periodType); + + void showCatComboSelector(String catOptionUid, List data); + } + + public interface Presenter extends AbstractActivityContracts.Presenter { + void init(View view); + + /*Actions*/ + void onBackClick(); + + void onOrgUnitSelectorClick(); + + void onReportPeriodClick(PeriodType periodType); + + void onCatOptionClick(String catOptionUid); + + void onActionButtonClick(Action action); + } + + +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialModel.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialModel.java new file mode 100644 index 0000000000..1df6e8ef43 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialModel.java @@ -0,0 +1,48 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +import com.google.auto.value.AutoValue; + +import org.hisp.dhis.android.core.category.CategoryModel; +import org.hisp.dhis.android.core.period.PeriodType; + +import java.util.Collections; +import java.util.List; + +@AutoValue +public abstract class DataSetInitialModel { + + @NonNull + public abstract String displayName(); + + @Nullable + public abstract String description(); + + @NonNull + public abstract String categoryCombo(); + + @NonNull + public abstract String categoryComboName(); + + @NonNull + public abstract PeriodType periodType(); + + @NonNull + public abstract List categoryModels(); + + @NonNull + public static DataSetInitialModel create(@NonNull String displayName, + @Nullable String description, + @NonNull String categoryCombo, + @NonNull String categoryComboName, + @NonNull PeriodType periodType, + @NonNull List categoryModels) { + return new AutoValue_DataSetInitialModel(displayName, description, categoryCombo, categoryComboName, periodType, categoryModels); + } + + public final List categories() { + return Collections.unmodifiableList(categoryModels()); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialModule.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialModule.java new file mode 100644 index 0000000000..ea6cd274ef --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialModule.java @@ -0,0 +1,39 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import com.squareup.sqlbrite2.BriteDatabase; + +import org.dhis2.data.dagger.PerActivity; + +import dagger.Module; +import dagger.Provides; + +/** + * QUADRAM. Created by ppajuelo on 24/09/2018. + */ +@PerActivity +@Module +public class DataSetInitialModule { + private final String dataSetUid; + + DataSetInitialModule(String dataSetUid) { + this.dataSetUid = dataSetUid; + } + + @Provides + @PerActivity + DataSetInitialContract.View provideView(DataSetInitialActivity activity) { + return activity; + } + + @Provides + @PerActivity + DataSetInitialContract.Presenter providesPresenter(DataSetInitialRepository dataSetInitialRepository) { + return new DataSetInitialPresenter(dataSetInitialRepository); + } + + @Provides + @PerActivity + DataSetInitialRepository dataSetInitialRepository(BriteDatabase briteDatabase) { + return new DataSetInitialRepositoryImpl(briteDatabase, dataSetUid); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialPresenter.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialPresenter.java new file mode 100644 index 0000000000..d110f6686f --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialPresenter.java @@ -0,0 +1,86 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import org.hisp.dhis.android.core.period.PeriodType; + +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; + +public class DataSetInitialPresenter implements DataSetInitialContract.Presenter { + + private CompositeDisposable compositeDisposable; + private DataSetInitialRepository dataSetInitialRepository; + private DataSetInitialContract.View view; + + public DataSetInitialPresenter(DataSetInitialRepository dataSetInitialRepository) { + this.dataSetInitialRepository = dataSetInitialRepository; + } + + + @Override + public void init(DataSetInitialContract.View view) { + this.view = view; + compositeDisposable = new CompositeDisposable(); + compositeDisposable.add( + dataSetInitialRepository.dataSet() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + view::setData, + Timber::d + )); + } + + @Override + public void onBackClick() { + view.back(); + } + + @Override + public void onOrgUnitSelectorClick() { + compositeDisposable.add( + dataSetInitialRepository.orgUnits() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + data -> view.showOrgUnitDialog(data), + Timber::d + ) + ); + } + + @Override + public void onReportPeriodClick(PeriodType periodType) { + view.showPeriodSelector(periodType); + } + + @Override + public void onCatOptionClick(String catOptionUid) { + compositeDisposable.add( + dataSetInitialRepository.catCombo(catOptionUid) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + data -> view.showCatComboSelector(catOptionUid,data), + Timber::d + ) + ); + } + + @Override + public void onActionButtonClick(DataSetInitialContract.Action action) { + + } + + + @Override + public void onDettach() { + compositeDisposable.clear(); + } + + @Override + public void displayMessage(String message) { + view.displayMessage(message); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialRepository.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialRepository.java new file mode 100644 index 0000000000..eeffbd7f85 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialRepository.java @@ -0,0 +1,22 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import android.support.annotation.NonNull; + +import org.hisp.dhis.android.core.category.CategoryOptionModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; + +import java.util.List; + +import io.reactivex.Observable; + +public interface DataSetInitialRepository { + + @NonNull + Observable dataSet(); + + @NonNull + Observable> orgUnits(); + + @NonNull + Observable> catCombo(String categoryUid); +} diff --git a/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialRepositoryImpl.java new file mode 100644 index 0000000000..902f233f8b --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/datasets/datasetInitial/DataSetInitialRepositoryImpl.java @@ -0,0 +1,102 @@ +package org.dhis2.usescases.datasets.datasetInitial; + +import android.database.Cursor; +import android.support.annotation.NonNull; + +import com.squareup.sqlbrite2.BriteDatabase; + +import org.hisp.dhis.android.core.category.CategoryModel; +import org.hisp.dhis.android.core.category.CategoryOptionModel; +import org.hisp.dhis.android.core.dataset.DataSetModel; +import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; +import org.hisp.dhis.android.core.period.PeriodType; + +import java.util.ArrayList; +import java.util.List; + +import io.reactivex.Observable; + +public class DataSetInitialRepositoryImpl implements DataSetInitialRepository { + + private static final String GET_DATA_SET_INFO = "SELECT " + + "DataSet.displayName, " + + "DataSet.description, " + + "DataSet.categoryCombo, " + + "DataSet.periodType, " + + "CategoryCombo.displayName " + + "FROM DataSet JOIN CategoryCombo ON CategoryCombo.uid = DataSet.categoryCombo " + + "WHERE DataSet.uid = ? LIMIT 1"; + + private static final String GET_ORG_UNITS = "SELECT OrganisationUnit.* FROM OrganisationUnit " + + "JOIN DataSetOrganisationUnitLink ON DataSetOrganisationUnitLink.organisationUnit = OrganisationUnit.uid " + + "WHERE DataSetOrganisationUnitLink.dataSet = ?"; + + private static final String GET_CATEGORIES = "SELECT Category.* FROM Category " + + "JOIN CategoryCategoryComboLink ON CategoryCategoryComboLink.category = Category.uid " + + "WHERE CategoryCategoryComboLink.categoryCombo = ?"; + private static final String GET_CATEGORY_OPTION = "SELECT CategoryOption.* FROM CategoryOption " + + "JOIN CategoryCategoryOptionLink ON CategoryCategoryOptionLink.categoryOption = CategoryOption.uid " + + "WHERE CategoryCategoryOptionLink.category = ? ORDER BY CategoryOption.displayName ASC"; + + + private final BriteDatabase briteDatabase; + private final String dataSetUid; + + public DataSetInitialRepositoryImpl(com.squareup.sqlbrite2.BriteDatabase briteDatabase, String dataSetUid) { + this.briteDatabase = briteDatabase; + this.dataSetUid = dataSetUid; + } + + @NonNull + @Override + public Observable dataSet() { + return briteDatabase.createQuery(DataSetModel.TABLE, GET_DATA_SET_INFO, dataSetUid) + .mapToOne(cursor -> { + + String displayName = cursor.getString(0); + String description = cursor.getString(1); + String categoryComboUid = cursor.getString(2); + PeriodType periodType = PeriodType.valueOf(cursor.getString(3)); + String categoryComboName = cursor.getString(4); + + List categoryModels = getCategoryModels(categoryComboUid); + + return DataSetInitialModel.create( + displayName, + description, + categoryComboUid, + categoryComboName, + periodType, + categoryModels + ); + }); + } + + private List getCategoryModels(String categoryComboUid) { + Cursor cursor = briteDatabase.query(GET_CATEGORIES, categoryComboUid); + List categoryModelList = new ArrayList<>(); + if (cursor != null && cursor.moveToFirst()) { + for (int i = 0; i < cursor.getCount(); i++) { + categoryModelList.add(CategoryModel.create(cursor)); + cursor.moveToNext(); + } + cursor.close(); + } + + return categoryModelList; + } + + @NonNull + @Override + public Observable> orgUnits() { + return briteDatabase.createQuery(OrganisationUnitModel.TABLE, GET_ORG_UNITS, dataSetUid) + .mapToList(OrganisationUnitModel::create); + } + + @NonNull + @Override + public Observable> catCombo(String categoryUid) { + return briteDatabase.createQuery(CategoryOptionModel.TABLE, GET_CATEGORY_OPTION, categoryUid) + .mapToList(CategoryOptionModel::create); + } +} diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialActivity.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialActivity.java index 55d04bafcf..a31b20d9ef 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialActivity.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialActivity.java @@ -16,6 +16,7 @@ import android.view.View; import android.widget.AdapterView; import android.widget.DatePicker; +import android.widget.PopupMenu; import com.unnamed.b.atv.model.TreeNode; import com.unnamed.b.atv.view.AndroidTreeView; @@ -33,10 +34,12 @@ import org.dhis2.usescases.qrCodes.eventsworegistration.QrEventsWORegistrationActivity; import org.dhis2.utils.CatComboAdapter2; import org.dhis2.utils.Constants; +import org.dhis2.utils.CustomViews.CustomDialog; import org.dhis2.utils.CustomViews.OrgUnitDialog; import org.dhis2.utils.CustomViews.PeriodDialog; import org.dhis2.utils.CustomViews.ProgressBarAnimation; import org.dhis2.utils.DateUtils; +import org.dhis2.utils.DialogClickListener; import org.dhis2.utils.HelpManager; import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.category.CategoryOptionComboModel; @@ -47,6 +50,8 @@ import org.hisp.dhis.android.core.program.ProgramModel; import org.hisp.dhis.android.core.program.ProgramStageModel; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.text.ParseException; import java.util.ArrayList; import java.util.Calendar; @@ -412,6 +417,8 @@ private boolean isCompleted(String field) { @Override public void setProgram(@NonNull ProgramModel program) { this.program = program; + this.periodType = program.expiryPeriodType(); + String activityTitle; if (eventCreationType.equals(REFERRAL)) { activityTitle = program.displayName() + " - " + getString(R.string.referral); @@ -420,9 +427,13 @@ public void setProgram(@NonNull ProgramModel program) { } binding.setName(activityTitle); - if (periodType == null) + Calendar now = Calendar.getInstance(); + if (periodType == null) { + selectedDateString = String.format(Locale.getDefault(), "%s-%02d-%02d", now.get(Calendar.YEAR), now.get(Calendar.MONTH), now.get(Calendar.DAY_OF_MONTH)); binding.date.setOnClickListener(v -> presenter.onDateClick(EventInitialActivity.this)); - else + } else { + now.setTime(DateUtils.getInstance().getNextPeriod(periodType, now.getTime(), 0)); + selectedDateString = String.format(Locale.getDefault(), "%s-%02d-%02d", now.get(Calendar.YEAR), now.get(Calendar.MONTH) + 1, now.get(Calendar.DAY_OF_MONTH)); binding.date.setOnClickListener(v -> new PeriodDialog() .setPeriod(periodType) @@ -432,8 +443,11 @@ public void setProgram(@NonNull ProgramModel program) { if (!fixedOrgUnit) binding.orgUnit.setText(""); presenter.filterOrgUnits(DateUtils.uiDateFormat().format(selectedDate)); - } ) + }) .show(getSupportFragmentManager(), PeriodDialog.class.getSimpleName())); + } + + binding.date.setText(selectedDateString); if (program.captureCoordinates()) { binding.coordinatesLayout.setVisibility(View.VISIBLE); @@ -515,9 +529,7 @@ public void setEvent(EventModel event) { if (event.eventDate() != null) - binding.setEventDate(DateUtils.uiDateFormat().format(event.eventDate())); - else - binding.setEventDate(""); + binding.date.setText(DateUtils.uiDateFormat().format(event.eventDate())); if (event.latitude() != null && event.longitude() != null) { runOnUiThread(() -> { @@ -862,4 +874,65 @@ public void setTutorial() { }, 500); } + + @Override + public void showMoreOptions(View view) { + PopupMenu popupMenu = new PopupMenu(this, view, Gravity.BOTTOM); + try { + Field[] fields = popupMenu.getClass().getDeclaredFields(); + for (Field field : fields) { + if ("mPopup".equals(field.getName())) { + field.setAccessible(true); + Object menuPopupHelper = field.get(popupMenu); + Class classPopupHelper = Class.forName(menuPopupHelper.getClass().getName()); + Method setForceIcons = classPopupHelper.getMethod("setForceShowIcon", boolean.class); + setForceIcons.invoke(menuPopupHelper, true); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + popupMenu.getMenuInflater().inflate(R.menu.event_menu, popupMenu.getMenu()); + popupMenu.setOnMenuItemClickListener(item -> { + switch (item.getItemId()) { + case R.id.showHelp: + showTutorial(false); + break; + case R.id.menu_delete: + confirmDeleteEvent(); + break; + } + return false; + }); + popupMenu.show(); + } + + public void confirmDeleteEvent() { + new CustomDialog( + this, + getString(R.string.delete_event), + getString(R.string.confirm_delete_event), + getString(R.string.delete), + getString(R.string.cancel), + 0, + new DialogClickListener() { + @Override + public void onPositive() { + presenter.deleteEvent(); + } + + @Override + public void onNegative() { + // dismiss + } + } + ).show(); + } + + @Override + public void showEventWasDeleted() { + showToast(getString(R.string.event_was_deleted)); + finish(); + } } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialContract.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialContract.java index ebed26032c..3172d085c8 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialContract.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialContract.java @@ -71,6 +71,8 @@ public interface View extends AbstractActivityContracts.View { void showOrgUnitSelector(List orgUnits); void showQR(); + + void showEventWasDeleted(); } public interface Presenter extends AbstractActivityContracts.Presenter { @@ -122,6 +124,8 @@ void editEvent(String programStageModel, String eventUid, String date, String or List getOrgUnits(); void onShareClick(android.view.View mView); + + void deleteEvent(); } } diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialPresenter.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialPresenter.java index 18a4f9e8f3..eb067eca70 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialPresenter.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialPresenter.java @@ -11,11 +11,16 @@ import android.text.TextUtils; import android.view.View; +import com.google.android.gms.location.FusedLocationProviderClient; +import com.google.android.gms.location.LocationServices; + import org.dhis2.Bindings.Bindings; import org.dhis2.data.forms.dataentry.fields.FieldViewModel; import org.dhis2.data.forms.dataentry.fields.edittext.EditTextViewModel; import org.dhis2.data.metadata.MetadataRepository; import org.dhis2.data.schedulers.SchedulerProvider; +import org.dhis2.data.tuples.Quartet; +import org.dhis2.data.tuples.Trio; import org.dhis2.usescases.eventsWithoutRegistration.eventSummary.EventSummaryActivity; import org.dhis2.usescases.eventsWithoutRegistration.eventSummary.EventSummaryRepository; import org.dhis2.usescases.map.MapSelectorActivity; @@ -23,9 +28,6 @@ import org.dhis2.utils.DateUtils; import org.dhis2.utils.OrgUnitUtils; import org.dhis2.utils.Result; -import com.google.android.gms.location.FusedLocationProviderClient; -import com.google.android.gms.location.LocationServices; - import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; import org.hisp.dhis.android.core.period.PeriodType; @@ -42,6 +44,7 @@ import java.util.List; import java.util.Map; +import io.reactivex.BackpressureStrategy; import io.reactivex.Flowable; import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; @@ -94,66 +97,46 @@ public void init(EventInitialContract.View mview, String programId, String event mFusedLocationClient = LocationServices.getFusedLocationProviderClient(view.getContext()); - if (eventId != null) + if (eventId != null) { compositeDisposable.add( - eventInitialRepository.event(eventId) - .flatMap( - eventModel -> { - view.setEvent(eventModel); - return metadataRepository.getProgramWithId(programId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - ) - .flatMap( - programModel -> { - this.programModel = programModel; - view.setProgram(programModel); - return metadataRepository.getCategoryComboWithId(programModel.categoryCombo()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - ) - .flatMap( - catCombo -> { - this.catCombo = catCombo; - return eventInitialRepository.catCombo(programModel.categoryCombo()) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()); - } - ) + Flowable.zip( + eventInitialRepository.event(eventId).toFlowable(BackpressureStrategy.LATEST), + metadataRepository.getProgramWithId(programId).toFlowable(BackpressureStrategy.LATEST), + eventInitialRepository.catComboModel(programId).toFlowable(BackpressureStrategy.LATEST), + eventInitialRepository.catCombo(programId).toFlowable(BackpressureStrategy.LATEST), + Quartet::create + ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - catComboOptions -> view.setCatComboOptions(catCombo, catComboOptions), - error -> Timber.log(1, error) - ) + .subscribe(quartetFlowable -> { + this.programModel = quartetFlowable.val1(); + this.catCombo = quartetFlowable.val2(); + view.setEvent(quartetFlowable.val0()); + view.setProgram(quartetFlowable.val1()); + view.setCatComboOptions(catCombo, quartetFlowable.val3()); + }, Timber::d) ); - else + + } else compositeDisposable.add( - metadataRepository.getProgramWithId(programId) - .flatMap( - programModel -> { - this.programModel = programModel; - view.setProgram(programModel); - return metadataRepository.getCategoryComboWithId(programModel.categoryCombo()); - } - ) - .flatMap( - catCombo -> { - this.catCombo = catCombo; - return eventInitialRepository.catCombo(programModel.categoryCombo()); - } - ) + Flowable.zip( + metadataRepository.getProgramWithId(programId).toFlowable(BackpressureStrategy.LATEST), + eventInitialRepository.catComboModel(programId).toFlowable(BackpressureStrategy.LATEST), + eventInitialRepository.catCombo(programId).toFlowable(BackpressureStrategy.LATEST), + Trio::create + ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - catComboOptions -> view.setCatComboOptions(catCombo, catComboOptions), - Timber::d - ) + .subscribe(trioFlowable -> { + this.programModel = trioFlowable.val0(); + this.catCombo = trioFlowable.val1(); + view.setProgram(trioFlowable.val0()); + view.setCatComboOptions(catCombo, trioFlowable.val2()); + }, Timber::d) ); + getOrgUnits(programId); - if(TextUtils.isEmpty(programStageId)) + if (TextUtils.isEmpty(programStageId)) getProgramStages(programId); else getProgramStage(programStageId); @@ -229,6 +212,15 @@ public void onShareClick(View mView) { view.showQR(); } + @Override + public void deleteEvent() { + if (eventId != null) { + eventInitialRepository.deleteEvent(eventId); + view.showEventWasDeleted(); + } else + view.displayMessage("This event has not been created yet"); + } + @Override public void getProgramStage(String programStageUid) { compositeDisposable.add(eventInitialRepository.programStageWithId(programStageUid) @@ -401,8 +393,8 @@ public void getSectionCompletion(@Nullable String sectionUid) { Flowable> viewModelsFlowable = Flowable.zip(fieldsFlowable, ruleEffectFlowable, this::applyEffects); compositeDisposable.add(viewModelsFlowable - .subscribeOn(schedulerProvider.io()) - .observeOn(schedulerProvider.ui()) + .subscribeOn(AndroidSchedulers.mainThread()) + .observeOn(AndroidSchedulers.mainThread()) .subscribe(view.showFields(sectionUid), throwable -> { throw new OnErrorNotImplementedException(throwable); })); diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepository.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepository.java index f9f649e96f..ccd59c6019 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepository.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepository.java @@ -4,6 +4,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.category.CategoryOptionComboModel; import org.hisp.dhis.android.core.event.EventModel; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; @@ -26,6 +27,9 @@ public interface EventInitialRepository { @NonNull Observable> orgUnits(String programId); + @NonNull + Observable catComboModel(String programUid); + @NonNull Observable> catCombo(String programUid); @@ -62,4 +66,6 @@ Observable scheduleEvent(String enrollmentUid, @Nullable String trackedE Observable> getEventsFromProgramStage(String programUid, String enrollmentUid, String programStageUid); Observable accessDataWrite(String programId); + + void deleteEvent(String eventId); } diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java index b2f44de694..5c27e92bdd 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventInitial/EventInitialRepositoryImpl.java @@ -7,10 +7,11 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import org.dhis2.utils.CodeGenerator; -import org.dhis2.utils.DateUtils; import com.squareup.sqlbrite2.BriteDatabase; +import org.dhis2.utils.CodeGenerator; +import org.dhis2.utils.DateUtils; +import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.category.CategoryOptionComboCategoryOptionLinkModel; import org.hisp.dhis.android.core.category.CategoryOptionComboModel; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; @@ -87,10 +88,19 @@ public Observable> orgUnits(String programId) { @NonNull @Override - public Observable> catCombo(String categoryComboUid) { + public Observable catComboModel(String programUid) { + String catComboQuery = "SELECT * FROM CategoryCombo JOIN Program ON Program.categoryCombo = CategoryCombo.uid WHERE Program.uid = ?"; + return briteDatabase.createQuery(CategoryComboModel.TABLE, catComboQuery, programUid).mapToOne(CategoryComboModel::create); + } + + @NonNull + @Override + public Observable> catCombo(String programUid) { String SELECT_CATEGORY_COMBO = String.format("SELECT * FROM %s WHERE %s.%s = ?", CategoryOptionComboModel.TABLE, CategoryOptionComboModel.TABLE, CategoryOptionComboModel.Columns.CATEGORY_COMBO); - return briteDatabase.createQuery(CategoryOptionComboModel.TABLE, SELECT_CATEGORY_COMBO, categoryComboUid == null ? "" : categoryComboUid) + String catComboQuery = "SELECT * FROM CategoryOptionCombo JOIN CategoryCombo ON CategoryCombo.uid= CategoryOptionCombo.categoryCombo " + + "JOIN Program ON Program.categoryCombo = CategoryCombo.uid WHERE program.uid = ?"; + return briteDatabase.createQuery(CategoryOptionComboModel.TABLE, catComboQuery,programUid) .mapToList(CategoryOptionComboModel::create); } @@ -370,6 +380,25 @@ public Observable accessDataWrite(String programId) { .mapToOne(cursor -> cursor.getInt(0) == 1) .flatMap(programStageAccessDataWrite -> briteDatabase.createQuery(ProgramModel.TABLE, PROGRAM_WRITE_PERMISSION, programId == null ? "" : programId) - .mapToOne(cursor -> (cursor.getInt(0) == 1) && programStageAccessDataWrite)); + .mapToOne(cursor -> (cursor.getInt(0) == 1) && programStageAccessDataWrite)); + } + + @Override + public void deleteEvent(String eventId) { + Cursor eventCursor = briteDatabase.query("SELECT Event.* FROM Event WHERE Event.uid = ?", eventId); + if (eventCursor != null && eventCursor.moveToNext()) { + EventModel eventModel = EventModel.create(eventCursor); + if (eventModel.state() == State.TO_POST) { + String DELETE_WHERE = String.format( + "%s.%s = ?", + EventModel.TABLE, EventModel.Columns.UID + ); + briteDatabase.delete(EventModel.TABLE, DELETE_WHERE, eventId); + } else { + ContentValues contentValues = eventModel.toContentValues(); + contentValues.put(EventModel.Columns.STATE, State.TO_DELETE.name()); + briteDatabase.update(EventModel.TABLE, contentValues, EventModel.Columns.UID + " = ?", eventId); + } + } } } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryActivity.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryActivity.java index 220b080546..97cc85d2a8 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryActivity.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryActivity.java @@ -91,6 +91,12 @@ protected void onResume() { } + @Override + protected void onPause() { + presenter.onDettach(); + super.onPause(); + } + @Override public void setProgram(@NonNull ProgramModel program) { binding.setName(program.displayName()); diff --git a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryRepositoryImpl.java index 963e72c403..ec73c13a22 100644 --- a/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/eventsWithoutRegistration/eventSummary/EventSummaryRepositoryImpl.java @@ -6,6 +6,8 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import com.squareup.sqlbrite2.BriteDatabase; + import org.dhis2.R; import org.dhis2.data.forms.FormRepository; import org.dhis2.data.forms.FormSectionViewModel; @@ -14,8 +16,6 @@ import org.dhis2.data.forms.dataentry.fields.FieldViewModelFactoryImpl; import org.dhis2.utils.DateUtils; import org.dhis2.utils.Result; -import com.squareup.sqlbrite2.BriteDatabase; - import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.common.ValueType; @@ -84,11 +84,14 @@ public class EventSummaryRepositoryImpl implements EventSummaryRepository { " Field.mandatory,\n" + " Field.optionSet,\n" + " Value.value,\n" + - " Option.name,\n" + + " Option.displayName,\n" + " Field.section,\n" + " Field.allowFutureDate,\n" + " Event.status,\n" + - " Field.formLabel\n" + + " Field.formLabel,\n" + + " Field.displayDescription,\n" + + " Field.formOrder,\n" + + " Field.sectionOrder\n" + "FROM Event\n" + " LEFT OUTER JOIN (\n" + " SELECT\n" + @@ -100,10 +103,13 @@ public class EventSummaryRepositoryImpl implements EventSummaryRepository { " ProgramStageDataElement.sortOrder AS formOrder,\n" + " ProgramStageDataElement.programStage AS stage,\n" + " ProgramStageDataElement.compulsory AS mandatory,\n" + - " ProgramStageDataElement.programStageSection AS section,\n" + - " ProgramStageDataElement.allowFutureDate AS allowFutureDate\n" + + " ProgramStageSectionDataElementLink.programStageSection AS section,\n" + + " ProgramStageDataElement.allowFutureDate AS allowFutureDate,\n" + + " DataElement.displayDescription AS displayDescription,\n" + + " ProgramStageSectionDataElementLink.sortOrder AS sectionOrder\n" + " FROM ProgramStageDataElement\n" + " INNER JOIN DataElement ON DataElement.uid = ProgramStageDataElement.dataElement\n" + + " LEFT JOIN ProgramStageSectionDataElementLink ON ProgramStageSectionDataElementLink.dataElement = ProgramStageDataElement.dataElement\n" + " ) AS Field ON (Field.stage = Event.programStage)\n" + " LEFT OUTER JOIN TrackedEntityDataValue AS Value ON (\n" + " Value.event = Event.uid AND Value.dataElement = Field.id\n" + @@ -112,7 +118,10 @@ public class EventSummaryRepositoryImpl implements EventSummaryRepository { " Field.optionSet = Option.optionSet AND Value.value = Option.code\n" + " )\n" + " %s " + - "ORDER BY Field.formOrder ASC;"; + "ORDER BY CASE" + + " WHEN Field.sectionOrder IS NULL THEN Field.formOrder" + + " WHEN Field.sectionOrder IS NOT NULL THEN Field.sectionOrder" + + " END ASC;"; private static final String QUERY_EVENT = "SELECT Event.uid,\n" + @@ -214,6 +223,7 @@ private FieldViewModel transform(@NonNull Cursor cursor) { String optionCodeName = cursor.getString(6); EventStatus eventStatus = EventStatus.valueOf(cursor.getString(9)); String formName = cursor.getString(10); + String description = cursor.getString(11); if (!isEmpty(optionCodeName)) { dataValue = optionCodeName; } @@ -221,7 +231,7 @@ private FieldViewModel transform(@NonNull Cursor cursor) { return fieldFactory.create(cursor.getString(0), formName == null ? cursor.getString(1) : formName, ValueType.valueOf(cursor.getString(2)), cursor.getInt(3) == 1, cursor.getString(4), dataValue, cursor.getString(7), cursor.getInt(8) == 1, - eventStatus == EventStatus.ACTIVE, null); + eventStatus == EventStatus.ACTIVE, null, description); } @NonNull @@ -305,7 +315,7 @@ public Flowable getEvent(String eventId) { public Observable accessDataWrite(String eventId) { return briteDatabase.createQuery(ProgramStageModel.TABLE, ACCESS_QUERY, eventId == null ? "" : eventId) .mapToOne(cursor -> cursor.getInt(0) == 1) - .flatMap(programStageAccessDataWrite ->briteDatabase.createQuery(ProgramModel.TABLE, PROGRAM_ACCESS_QUERY, eventId == null ? "" : eventId) + .flatMap(programStageAccessDataWrite -> briteDatabase.createQuery(ProgramModel.TABLE, PROGRAM_ACCESS_QUERY, eventId == null ? "" : eventId) .mapToOne(cursor -> (cursor.getInt(0) == 1) && programStageAccessDataWrite)); } @@ -319,7 +329,7 @@ private Flowable queryEvent(@NonNull List dataValues) String programStage = cursor.getString(6); RuleEvent.Status status = RuleEvent.Status.valueOf(cursor.getString(2)); return RuleEvent.create(cursor.getString(0), cursor.getString(1), - status, eventDate, dueDate,dataValues); + status, eventDate, dueDate, dataValues); }).toFlowable(BackpressureStrategy.LATEST); } diff --git a/app/src/main/java/org/dhis2/usescases/general/ActivityGlobalAbstract.java b/app/src/main/java/org/dhis2/usescases/general/ActivityGlobalAbstract.java index 4845d3aa70..f9eddd6915 100644 --- a/app/src/main/java/org/dhis2/usescases/general/ActivityGlobalAbstract.java +++ b/app/src/main/java/org/dhis2/usescases/general/ActivityGlobalAbstract.java @@ -39,6 +39,7 @@ import org.dhis2.utils.ColorUtils; import org.dhis2.utils.Constants; import org.dhis2.utils.CustomViews.CoordinatesView; +import org.dhis2.utils.CustomViews.CustomDialog; import org.dhis2.utils.HelpManager; import java.lang.reflect.Field; @@ -307,15 +308,15 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { @Override public void showDescription(String description) { - showInfoDialog("Description", description); - /* LayoutInflater inflater = getLayoutInflater(); - View layout = inflater.inflate(R.layout.toast, findViewById(R.id.custom_toast_layout_id)); - ((TextView) layout.findViewById(R.id.toast_message)).setText(description); - Toast toast = new Toast(this); - toast.setView(layout); - toast.setGravity(Gravity.BOTTOM, 0, 0); - toast.setDuration(Toast.LENGTH_SHORT); - toast.show();*/ + new CustomDialog( + getAbstracContext(), + getString(R.string.info), + description, + getString(R.string.action_accept), + null, + Constants.DESCRIPTION_DIALOG, + null + ).show(); } protected int getPrimaryColor() { diff --git a/app/src/main/java/org/dhis2/usescases/login/LoginActivity.java b/app/src/main/java/org/dhis2/usescases/login/LoginActivity.java index c8cd2d59b9..e155cc7933 100644 --- a/app/src/main/java/org/dhis2/usescases/login/LoginActivity.java +++ b/app/src/main/java/org/dhis2/usescases/login/LoginActivity.java @@ -52,7 +52,7 @@ public class LoginActivity extends ActivityGlobalAbstract implements LoginContra private boolean isPinScreenVisible = false; enum SyncState { - METADATA, EVENTS, TEI + METADATA, EVENTS, TEI, RESERVED_VALUES } @Override diff --git a/app/src/main/java/org/dhis2/usescases/login/LoginContracts.java b/app/src/main/java/org/dhis2/usescases/login/LoginContracts.java index d2c13f459b..db202bded5 100644 --- a/app/src/main/java/org/dhis2/usescases/login/LoginContracts.java +++ b/app/src/main/java/org/dhis2/usescases/login/LoginContracts.java @@ -103,13 +103,13 @@ public interface Presenter { void syncReservedValues(); + void syncAggregatesData(); + } public interface Interactor { - - } } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/usescases/login/LoginPresenter.java b/app/src/main/java/org/dhis2/usescases/login/LoginPresenter.java index 03c6df1cb1..5dfa9f94aa 100644 --- a/app/src/main/java/org/dhis2/usescases/login/LoginPresenter.java +++ b/app/src/main/java/org/dhis2/usescases/login/LoginPresenter.java @@ -191,10 +191,13 @@ public void syncNext(LoginActivity.SyncState syncState, SyncResult syncResult) { syncEvents(); break; case EVENTS: - syncReservedValues(); +// syncAggregatesData(); syncTrackedEntities(); break; case TEI: + syncReservedValues(); + break; + case RESERVED_VALUES: Intent intent = new Intent(view.getContext(), MainActivity.class); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); view.getContext().startActivity(intent); @@ -323,10 +326,7 @@ public void syncTrackedEntities() { disposable.add(trackerData() .subscribeOn(Schedulers.io()) - .map(response -> { -// userManager.getD2().syncAllTrackedEntityAttributeReservedValues(); - return SyncResult.success(); - }) + .map(response -> SyncResult.success()) .observeOn(AndroidSchedulers.mainThread()) .onErrorReturn(throwable -> SyncResult.failure( throwable.getMessage() == null ? "" : throwable.getMessage())) @@ -349,15 +349,26 @@ public void syncReservedValues() { userManager.getD2().syncAllTrackedEntityAttributeReservedValues(); return true; }) + .map(response -> SyncResult.success()) .subscribeOn(Schedulers.io()) .observeOn(Schedulers.io()) .subscribe( - data -> Timber.log(1, "DONE"), + update(LoginActivity.SyncState.RESERVED_VALUES), Timber::d ) ); } + @Override + public void syncAggregatesData() { + disposable.add(aggregatesData() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(data -> Timber.log(1, "AGGREGATE DONE"), + throwable -> view.displayMessage(throwable.getMessage()) + )); + } + @NonNull private Consumer update(LoginActivity.SyncState syncState) { return result -> { @@ -391,4 +402,9 @@ private Observable> events() { return Observable.defer(() -> Observable.fromCallable(userManager.getD2().downloadSingleEvents(eventLimit, limityByOU))); } + @NonNull + private Observable aggregatesData() { + return Observable.defer(() -> Observable.fromCallable(userManager.getD2().syncAggregatedData())); + } + } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/usescases/main/program/HomeRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/main/program/HomeRepositoryImpl.java index 21b044af3b..0c3555dc2d 100644 --- a/app/src/main/java/org/dhis2/usescases/main/program/HomeRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/main/program/HomeRepositoryImpl.java @@ -68,7 +68,21 @@ class HomeRepositoryImpl implements HomeRepository { "Program.trackedEntityType," + "Program.description " + "FROM Program LEFT JOIN ObjectStyle ON ObjectStyle.uid = Program.uid " + - "JOIN OrganisationUnitProgramLink ON OrganisationUnitProgramLink.program = Program.uid GROUP BY Program.uid"; + "JOIN OrganisationUnitProgramLink ON OrganisationUnitProgramLink.program = Program.uid GROUP BY Program.uid "/* + + "UNION " + + "SELECT DataSet.uid, " + + "DataSet.displayName, " + + "null, " + + "null, " + + "'', " + + "'', " + + "DataSet.description " + + "FROM DataSet " + + "JOIN DataSetOrganisationUnitLink ON DataSetOrganisationUnitLink.dataSet = DataSet.uid GROUP BY DataSet.uid"*/; + + private final static String AGGREGATE_FROM_DATASET = "SELECT * FROM DataSetDataElementLink " + + "WHERE dataSet = ? "; + private static final String[] TABLE_NAMES = new String[]{ProgramModel.TABLE, ObjectStyleModel.TABLE,OrganisationUnitProgramLinkModel.TABLE}; private static final Set TABLE_SET = new HashSet<>(Arrays.asList(TABLE_NAMES)); @@ -117,34 +131,38 @@ public Flowable> programModels(List dates, Period p String description = cursor.getString(6); //QUERYING Program EVENTS - dates filter - StringBuilder dateQuery = new StringBuilder(""); - if (dates != null && !dates.isEmpty()) { - String queryFormat = "(%s BETWEEN '%s' AND '%s') "; - for (int i = 0; i < dates.size(); i++) { - Date[] datesToQuery = DateUtils.getInstance().getDateFromDateAndPeriod(dates.get(i), period); - dateQuery.append(String.format(queryFormat, "Event.eventDate", DateUtils.databaseDateFormat().format(datesToQuery[0]), DateUtils.databaseDateFormat().format(datesToQuery[1]))); - if (i < dates.size() - 1) - dateQuery.append("OR "); + String queryFinal; + if(!programType.isEmpty()){ + StringBuilder dateQuery = new StringBuilder(""); + if (dates != null && !dates.isEmpty()) { + String queryFormat = "(%s BETWEEN '%s' AND '%s') "; + for (int i = 0; i < dates.size(); i++) { + Date[] datesToQuery = DateUtils.getInstance().getDateFromDateAndPeriod(dates.get(i), period); + dateQuery.append(String.format(queryFormat, "Event.eventDate", DateUtils.databaseDateFormat().format(datesToQuery[0]), DateUtils.databaseDateFormat().format(datesToQuery[1]))); + if (i < dates.size() - 1) + dateQuery.append("OR "); + } } - } - //QUERYING Program Events - orgUnit filter - String orgQuery = ""; - if (orgUnitsId != null) - orgQuery = String.format("Event.organisationUnit IN (%s)", orgUnitsId); + //QUERYING Program Events - orgUnit filter + String orgQuery = ""; + if (orgUnitsId != null) + orgQuery = String.format("Event.organisationUnit IN (%s)", orgUnitsId); - String queryFinal; - String filter = ""; - if (!dateQuery.toString().isEmpty() && !orgQuery.isEmpty()) - filter = dateQuery.toString() + " AND " + orgQuery + " AND "; - else if (!dateQuery.toString().isEmpty() || !orgQuery.isEmpty()) - filter = dateQuery.toString() + orgQuery + " AND "; + String filter = ""; + if (!dateQuery.toString().isEmpty() && !orgQuery.isEmpty()) + filter = dateQuery.toString() + " AND " + orgQuery + " AND "; + else if (!dateQuery.toString().isEmpty() || !orgQuery.isEmpty()) + filter = dateQuery.toString() + orgQuery + " AND "; - if (programType.equals(ProgramType.WITH_REGISTRATION.name())) { - queryFinal = String.format(SELECT_TEIS, filter); + if (programType.equals(ProgramType.WITH_REGISTRATION.name())) { + queryFinal = String.format(SELECT_TEIS, filter); + } else { + queryFinal = String.format(SELECT_EVENTS, filter); + } } else { - queryFinal = String.format(SELECT_EVENTS, filter); + queryFinal = AGGREGATE_FROM_DATASET; } Cursor countCursor = briteDatabase.query(queryFinal, uid); @@ -156,13 +174,17 @@ else if (!dateQuery.toString().isEmpty() || !orgQuery.isEmpty()) //QUERYING Tracker name - String typeName = "Events"; + String typeName = ""; if (programType.equals(ProgramType.WITH_REGISTRATION.name())) { Cursor typeCursor = briteDatabase.query(TRACKED_ENTITY_TYPE_NAME, teiType); if (typeCursor != null && typeCursor.moveToFirst()) { typeName = typeCursor.getString(0); typeCursor.close(); } + } else if (programType.equals(ProgramType.WITHOUT_REGISTRATION.name())){ + typeName = "Events"; + } else { + typeName = "DataSets"; } return ProgramViewModel.create(uid, displayName, color, icon, count, teiType, typeName, programType, description,true,true); diff --git a/app/src/main/java/org/dhis2/usescases/main/program/ProgramModelHolder.java b/app/src/main/java/org/dhis2/usescases/main/program/ProgramModelHolder.java index f0fbc39139..bb5db720d2 100644 --- a/app/src/main/java/org/dhis2/usescases/main/program/ProgramModelHolder.java +++ b/app/src/main/java/org/dhis2/usescases/main/program/ProgramModelHolder.java @@ -44,9 +44,5 @@ public void bind(ProgramContract.Presenter presenter, ProgramViewModel programVi binding.programImage.setBackgroundColor(color); - /* SpannableStringBuilder sb = new SpannableStringBuilder(String.format("%s %s", programViewModel.count(), programViewModel.typeName())); - sb.setSpan(new AbsoluteSizeSpan(18,true), 0, programViewModel.count().toString().length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - sb.setSpan(new StyleSpan(Typeface.BOLD), 0, programViewModel.count().toString().length(), Spanned.SPAN_INCLUSIVE_INCLUSIVE); - binding.eventsNumber.setText(sb);*/ } } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/usescases/main/program/ProgramPresenter.java b/app/src/main/java/org/dhis2/usescases/main/program/ProgramPresenter.java index 5fae8ee92a..5d53bbdf7c 100644 --- a/app/src/main/java/org/dhis2/usescases/main/program/ProgramPresenter.java +++ b/app/src/main/java/org/dhis2/usescases/main/program/ProgramPresenter.java @@ -7,13 +7,13 @@ import org.dhis2.R; import org.dhis2.data.tuples.Pair; import org.dhis2.data.tuples.Trio; +import org.dhis2.usescases.datasets.datasetDetail.DataSetDetailActivity; import org.dhis2.usescases.programEventDetail.ProgramEventDetailActivity; import org.dhis2.usescases.searchTrackEntity.SearchTEActivity; import org.dhis2.utils.ColorUtils; import org.dhis2.utils.Constants; import org.dhis2.utils.OrgUnitUtils; import org.dhis2.utils.Period; - import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; import org.hisp.dhis.android.core.program.ProgramModel; import org.hisp.dhis.android.core.program.ProgramType; @@ -31,6 +31,8 @@ import io.reactivex.processors.PublishProcessor; import io.reactivex.schedulers.Schedulers; +import static android.text.TextUtils.isEmpty; + /** * Created by ppajuelo on 18/10/2017.f */ @@ -114,8 +116,17 @@ public void dispose() { public void onItemClick(ProgramViewModel programModel, Period currentPeriod) { Bundle bundle = new Bundle(); - bundle.putString("PROGRAM_UID", programModel.id()); - bundle.putString("TRACKED_ENTITY_UID", programModel.type()); + String idTag; + if (!isEmpty(programModel.type())) { + bundle.putString("TRACKED_ENTITY_UID", programModel.type()); + } + + if (programModel.typeName().equals("DataSets")) + idTag = "DATASET_UID"; + else + idTag = "PROGRAM_UID"; + + bundle.putString(idTag, programModel.id()); switch (currentPeriod) { case NONE: @@ -150,8 +161,10 @@ public void onItemClick(ProgramViewModel programModel, Period currentPeriod) { if (programModel.programType().equals(ProgramType.WITH_REGISTRATION.name())) { view.startActivity(SearchTEActivity.class, bundle, false, false, null); - } else { + } else if (programModel.programType().equals(ProgramType.WITHOUT_REGISTRATION.name())) { view.startActivity(ProgramEventDetailActivity.class, bundle, false, false, null); + } else { + view.startActivity(DataSetDetailActivity.class, bundle, false, false, null); } } diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.java index 66b2ca546a..223313bc0c 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailActivity.java @@ -15,10 +15,14 @@ import android.support.v4.widget.DrawerLayout; import android.support.v7.app.AlertDialog; import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.RecyclerView; import android.view.Gravity; import android.view.View; import android.widget.AdapterView; +import com.unnamed.b.atv.model.TreeNode; +import com.unnamed.b.atv.view.AndroidTreeView; + import org.dhis2.App; import org.dhis2.R; import org.dhis2.databinding.ActivityProgramEventDetailBinding; @@ -28,11 +32,9 @@ import org.dhis2.utils.Constants; import org.dhis2.utils.CustomViews.RxDateDialog; import org.dhis2.utils.DateUtils; +import org.dhis2.utils.EndlessRecyclerViewScrollListener; import org.dhis2.utils.HelpManager; import org.dhis2.utils.Period; -import com.unnamed.b.atv.model.TreeNode; -import com.unnamed.b.atv.view.AndroidTreeView; - import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.category.CategoryOptionComboModel; import org.hisp.dhis.android.core.event.EventModel; @@ -48,6 +50,8 @@ import javax.inject.Inject; +import io.reactivex.Flowable; +import io.reactivex.processors.PublishProcessor; import me.toptas.fancyshowcase.FancyShowCaseView; import timber.log.Timber; @@ -71,7 +75,6 @@ public class ProgramEventDetailActivity extends ActivityGlobalAbstract implement @Inject ProgramEventDetailAdapter adapter; private Period currentPeriod = Period.NONE; - ProgramModel programModel; private Date chosenDateDay = new Date(); private ArrayList chosenDateWeek = new ArrayList<>(); @@ -84,6 +87,8 @@ public class ProgramEventDetailActivity extends ActivityGlobalAbstract implement private StringBuilder orgUnitFilter = new StringBuilder(); private boolean isFilteredByCatCombo = false; private String programId; + private static PublishProcessor pageProcessor; + private EndlessRecyclerViewScrollListener endlessScrollListener; @Override public void onCreate(@Nullable Bundle savedInstanceState) { @@ -101,14 +106,20 @@ public void onCreate(@Nullable Bundle savedInstanceState) { binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); + pageProcessor = PublishProcessor.create(); + + endlessScrollListener = new EndlessRecyclerViewScrollListener(binding.recycler.getLayoutManager(), 2, 0) { + @Override + public void onLoadMore(int page, int totalItemsCount, RecyclerView view) { + pageProcessor.onNext(page); + } + }; } @Override protected void onResume() { super.onResume(); presenter.init(this, programId, currentPeriod); - - presenter.getProgramEventsWithDates(null, currentPeriod, orgUnitFilter.toString()); } @Override @@ -122,26 +133,18 @@ protected void onPause() { public void setData(List events) { if (binding.recycler.getAdapter() == null) { binding.recycler.setAdapter(adapter); + binding.recycler.addOnScrollListener(endlessScrollListener); binding.recycler.addItemDecoration(new DividerItemDecoration(getContext(), DividerItemDecoration.VERTICAL)); } - adapter.setEvents(events); + adapter.setEvents(events, endlessScrollListener.getCurrentPage()); - if(!HelpManager.getInstance().isTutorialReadyForScreen(getClass().getName())) + if (!HelpManager.getInstance().isTutorialReadyForScreen(getClass().getName())) setTutorial(); } @Override public void setProgram(ProgramModel program) { - this.programModel = program; - presenter.setProgram(program); binding.setName(program.displayName()); - - if (!program.accessDataWrite()){ - binding.addEventButton.setVisibility(View.GONE); - } - else { - binding.addEventButton.setVisibility(View.VISIBLE); - } } @Override @@ -149,7 +152,7 @@ public void openDrawer() { if (!binding.drawerLayout.isDrawerOpen(Gravity.END)) { binding.drawerLayout.openDrawer(Gravity.END); binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_OPEN); - }else { + } else { binding.drawerLayout.closeDrawer(Gravity.END); binding.drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED); } @@ -187,8 +190,10 @@ public void showRageDatePicker() { } binding.buttonPeriodText.setText(textToShow); - // TODO - presenter.getProgramEventsWithDates(selectedDates, currentPeriod, orgUnitFilter.toString()); + presenter.setFilters(selectedDates, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(selectedDates, currentPeriod, orgUnitFilter.toString()); } else { ArrayList date = new ArrayList<>(); @@ -214,8 +219,10 @@ public void showRageDatePicker() { } binding.buttonPeriodText.setText(text); - // TODO - presenter.getProgramEventsWithDates(date, currentPeriod, orgUnitFilter.toString()); + presenter.setFilters(date, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(date, currentPeriod, orgUnitFilter.toString()); } }, Timber::d); @@ -228,8 +235,11 @@ public void showRageDatePicker() { Date[] dates = DateUtils.getInstance().getDateFromDateAndPeriod(calendar.getTime(), currentPeriod); ArrayList day = new ArrayList<>(); day.add(dates[0]); - // TODO - presenter.getProgramEventsWithDates(day, currentPeriod, orgUnitFilter.toString()); + + presenter.setFilters(day, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(day, currentPeriod, orgUnitFilter.toString()); binding.buttonPeriodText.setText(DateUtils.getInstance().formatDate(dates[0])); chosenDateDay = dates[0]; }, cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH)); @@ -270,8 +280,10 @@ public void showTimeUnitPicker() { switch (currentPeriod) { case NONE: - // TODO - presenter.getProgramEventsWithDates(null, currentPeriod, orgUnitFilter.toString()); + presenter.setFilters(null, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(null, currentPeriod, orgUnitFilter.toString()); textToShow = getString(R.string.period); break; case DAILY: @@ -280,8 +292,11 @@ public void showTimeUnitPicker() { if (!datesD.isEmpty()) textToShow = DateUtils.getInstance().formatDate(datesD.get(0)); if (!datesD.isEmpty() && datesD.size() > 1) textToShow += "... "; - // TODO - presenter.getProgramEventsWithDates(datesD, currentPeriod, orgUnitFilter.toString()); + + presenter.setFilters(datesD, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(datesD, currentPeriod, orgUnitFilter.toString()); break; case WEEKLY: if (!chosenDateWeek.isEmpty()) { @@ -290,8 +305,11 @@ public void showTimeUnitPicker() { textToShow = weeklyFormat.format(chosenDateWeek.get(0)) + ", " + yearFormat.format(chosenDateWeek.get(0)); } if (!chosenDateWeek.isEmpty() && chosenDateWeek.size() > 1) textToShow += "... "; - // TODO - presenter.getProgramEventsWithDates(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); + + presenter.setFilters(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); break; case MONTHLY: if (!chosenDateMonth.isEmpty()) { @@ -299,15 +317,21 @@ public void showTimeUnitPicker() { textToShow = dateFormatted.substring(0, 1).toUpperCase() + dateFormatted.substring(1); } if (!chosenDateMonth.isEmpty() && chosenDateMonth.size() > 1) textToShow += "... "; - // TODO - presenter.getProgramEventsWithDates(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); + + presenter.setFilters(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); break; case YEARLY: if (!chosenDateYear.isEmpty()) textToShow = yearFormat.format(chosenDateYear.get(0)); if (!chosenDateYear.isEmpty() && chosenDateYear.size() > 1) textToShow += "... "; - // TODO - presenter.getProgramEventsWithDates(chosenDateYear, currentPeriod, orgUnitFilter.toString()); + + presenter.setFilters(chosenDateYear, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(chosenDateYear, currentPeriod, orgUnitFilter.toString()); break; } @@ -372,15 +396,15 @@ public void renderError(String message) { @Override public void setCatComboOptions(CategoryComboModel catCombo, List catComboList) { ArrayList catComboListFinal = new ArrayList<>(); - if (catComboList != null){ - for (CategoryOptionComboModel categoryOptionComboModel : catComboList){ + if (catComboList != null) { + for (CategoryOptionComboModel categoryOptionComboModel : catComboList) { if (!"default".equals(categoryOptionComboModel.displayName()) && !categoryOptionComboModel.uid().equals(CategoryComboModel.DEFAULT_UID)) { catComboListFinal.add(categoryOptionComboModel); } } } - if ("default".equals(catCombo.displayName()) || catCombo.uid().equals(CategoryComboModel.DEFAULT_UID) || catComboListFinal.isEmpty()) { + if (catCombo.isDefault() || "default".equals(catCombo.displayName()) || catCombo.uid().equals(CategoryComboModel.DEFAULT_UID) || catComboListFinal.isEmpty()) { binding.catCombo.setVisibility(View.GONE); } else { binding.catCombo.setVisibility(View.VISIBLE); @@ -415,11 +439,6 @@ public void onNothingSelected(AdapterView parent) { } } - @Override - public void setOrgUnitFilter(StringBuilder orgUnitFilter) { - this.orgUnitFilter = orgUnitFilter; - } - @Override public void showHideFilter() { binding.filterLayout.setVisibility(binding.filterLayout.getVisibility() == View.VISIBLE ? View.GONE : View.VISIBLE); @@ -475,26 +494,37 @@ public void apply() { switch (currentPeriod) { case NONE: - // TODO - presenter.getProgramEventsWithDates(null, currentPeriod, orgUnitFilter.toString()); + + presenter.setFilters(null, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(null, currentPeriod, orgUnitFilter.toString()); break; case DAILY: ArrayList datesD = new ArrayList<>(); datesD.add(chosenDateDay); - // TODO - presenter.getProgramEventsWithDates(datesD, currentPeriod, orgUnitFilter.toString()); + presenter.setFilters(datesD, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(datesD, currentPeriod, orgUnitFilter.toString()); break; case WEEKLY: - // TODO - presenter.getProgramEventsWithDates(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); + presenter.setFilters(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(chosenDateWeek, currentPeriod, orgUnitFilter.toString()); break; case MONTHLY: - // TODO - presenter.getProgramEventsWithDates(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); + presenter.setFilters(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(chosenDateMonth, currentPeriod, orgUnitFilter.toString()); break; case YEARLY: - // TODO - presenter.getProgramEventsWithDates(chosenDateYear, currentPeriod, orgUnitFilter.toString()); + presenter.setFilters(chosenDateYear, currentPeriod, orgUnitFilter.toString()); + endlessScrollListener.resetState(0); + pageProcessor.onNext(0); +// presenter.getProgramEventsWithDates(chosenDateYear, currentPeriod, orgUnitFilter.toString()); break; } } @@ -538,4 +568,9 @@ public void setTutorial() { }, 500); } + + @Override + public Flowable currentPage() { + return pageProcessor; + } } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailAdapter.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailAdapter.java index ba66f6b844..fc7399aa8e 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailAdapter.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailAdapter.java @@ -9,15 +9,13 @@ import org.dhis2.R; import org.dhis2.databinding.ItemProgramEventBinding; - import org.hisp.dhis.android.core.event.EventModel; import java.util.ArrayList; import java.util.List; /** - * Created by Cristian on 13/02/2018. - * + * QUADRAM. Created by Cristian on 13/02/2018. */ public class ProgramEventDetailAdapter extends RecyclerView.Adapter { @@ -49,13 +47,15 @@ public int getItemCount() { return events != null ? events.size() : 0; } - public void setEvents(List events){ - /* Collections.sort(this.events, (ob1, ob2) -> ob2.lastUpdated().compareTo(ob1.lastUpdated())); - Collections.sort(events, (ob1, ob2) -> ob2.lastUpdated().compareTo(ob1.lastUpdated()));*/ + public void setEvents(List events, int currentPage) { - DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new ProgramEventDiffCallback(this.events, events)); - this.events.clear(); + if (currentPage == 0) + this.events = new ArrayList<>(); +// DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(new ProgramEventDiffCallback(this.events, newList)); this.events.addAll(events); - diffResult.dispatchUpdatesTo(this); + + notifyDataSetChanged(); + +// diffResult.dispatchUpdatesTo(this); } } diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailContract.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailContract.java index 0259f570f7..e9d8b31f6e 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailContract.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailContract.java @@ -14,6 +14,7 @@ import java.util.Date; import java.util.List; +import io.reactivex.Flowable; import io.reactivex.Observable; /** @@ -39,13 +40,13 @@ public interface View extends AbstractActivityContracts.View { void setCatComboOptions(CategoryComboModel catCombo, List catComboList); - void setOrgUnitFilter(StringBuilder orgUnitFilter); - void showHideFilter(); void apply(); void setWritePermission(Boolean aBoolean); + + Flowable currentPage(); } public interface Presenter extends AbstractActivityContracts.Presenter { @@ -73,24 +74,10 @@ public interface Presenter extends AbstractActivityContracts.Presenter { void showFilter(); - void getProgramEventsWithDates(List dates, Period period, String orgUnitQuery); + void getProgramEventsWithDates(); List getOrgUnits(); - } - - public interface Interactor extends AbstractActivityContracts.Interactor { - void init(View view, String programId, Period period); - - void getEvents(String programId, Date fromDate, Date toDate, String orgUnitQuery); - void getOrgUnits(Date date); - - void updateFilters(CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery); - - Observable> getEventDataValueNew(EventModel event); - - void getProgramEventsWithDates(String programId, List dates, Period period, String orgUnitQuery); - - List getOrgUnits(); + void setFilters(List selectedDates, Period currentPeriod, String orgUnits); } } diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailInteractor.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailInteractor.java deleted file mode 100644 index 3c12ccc7ad..0000000000 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailInteractor.java +++ /dev/null @@ -1,188 +0,0 @@ -package org.dhis2.usescases.programEventDetail; - -import android.annotation.SuppressLint; -import android.support.annotation.IntDef; - -import org.dhis2.Bindings.Bindings; -import org.dhis2.data.metadata.MetadataRepository; -import org.dhis2.utils.DateUtils; -import org.dhis2.utils.OrgUnitUtils; -import org.dhis2.utils.Period; - -import org.hisp.dhis.android.core.category.CategoryComboModel; -import org.hisp.dhis.android.core.category.CategoryOptionComboModel; -import org.hisp.dhis.android.core.event.EventModel; -import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; -import org.hisp.dhis.android.core.program.ProgramModel; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueModel; - -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.Date; -import java.util.List; - -import io.reactivex.Observable; -import io.reactivex.android.schedulers.AndroidSchedulers; -import io.reactivex.disposables.CompositeDisposable; -import io.reactivex.schedulers.Schedulers; -import timber.log.Timber; - -/** - * QUADRAM. Created by Cristian on 13/02/2018. - */ - -public class ProgramEventDetailInteractor implements ProgramEventDetailContract.Interactor { - - private final MetadataRepository metadataRepository; - private final ProgramEventDetailRepository programEventDetailRepository; - private ProgramEventDetailContract.View view; - private String programId; - private CompositeDisposable compositeDisposable; - private CategoryOptionComboModel categoryOptionComboModel; - - private Date fromDate; - private Date toDate; - - private List dates; - private Period period; - - private @LastSearchType - int lastSearchType; - private CategoryComboModel mCatCombo; - private List orgUnits; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({LastSearchType.DATES, LastSearchType.DATE_RANGES}) - public @interface LastSearchType { - int DATES = 1; - int DATE_RANGES = 32; - } - - ProgramEventDetailInteractor(ProgramEventDetailRepository programEventDetailRepository, MetadataRepository metadataRepository) { - this.metadataRepository = metadataRepository; - this.programEventDetailRepository = programEventDetailRepository; - Bindings.setMetadataRepository(metadataRepository); - compositeDisposable = new CompositeDisposable(); - } - - @Override - public void init(ProgramEventDetailContract.View view, String programId, Period period) { - this.view = view; - this.programId = programId; - getProgram(); - getOrgUnits(null); - - compositeDisposable.add( - programEventDetailRepository.writePermission(programId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - view::setWritePermission, - Timber::e - ) - ); - } - - @Override - public void getOrgUnits(Date date) { - compositeDisposable.add(programEventDetailRepository.orgUnits() - .map(orgUnits -> { - this.orgUnits = orgUnits; - return OrgUnitUtils.renderTree(view.getContext(), orgUnits, true); - }) - .subscribeOn(Schedulers.computation()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - treeNode -> view.addTree(treeNode), - throwable -> view.renderError(throwable.getMessage()) - )); - } - - @Override - public Observable> getEventDataValueNew(EventModel event) { - return programEventDetailRepository.eventDataValuesNew(event); - } - - - private void getProgram() { - compositeDisposable.add(metadataRepository.getProgramWithId(programId) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - programModel -> { - view.setProgram(programModel); - getCatCombo(programModel); - }, - Timber::d) - ); - } - - private void getCatCombo(ProgramModel programModel) { - compositeDisposable.add(metadataRepository.getCategoryComboWithId(programModel.categoryCombo()) - .filter(categoryComboModel -> categoryComboModel != null && !categoryComboModel.uid().equals(CategoryComboModel.DEFAULT_UID)) - .flatMap(catCombo -> { - mCatCombo = catCombo; - return programEventDetailRepository.catCombo(programModel.categoryCombo()); - }) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(catComboOptions -> view.setCatComboOptions(mCatCombo, catComboOptions), Timber::d) - ); - } - - @Override - public void onDettach() { - compositeDisposable.clear(); - } - - @Override - public void updateFilters(CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery) { - this.categoryOptionComboModel = categoryOptionComboModel; - switch (lastSearchType) { - case LastSearchType.DATES: - getEvents(programId, this.fromDate, this.toDate, orgUnitQuery); - break; - case LastSearchType.DATE_RANGES: - getProgramEventsWithDates(programId, this.dates, this.period, orgUnitQuery); - break; - default: - getProgramEventsWithDates(programId, null, this.period, orgUnitQuery); - break; - } - } - - @Override - public void getProgramEventsWithDates(String programId, List dates, Period period, String orgUnitQuery) { - this.dates = dates; - this.period = period; - lastSearchType = LastSearchType.DATE_RANGES; - compositeDisposable.add(programEventDetailRepository.filteredProgramEvents(programId, dates, period, categoryOptionComboModel, orgUnitQuery) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - view::setData, - throwable -> view.renderError(throwable.getMessage()))); - } - - @Override - public List getOrgUnits() { - return this.orgUnits; - } - - @SuppressLint("CheckResult") - @Override - public void getEvents(String programId, Date fromDate, Date toDate, String orgUnitQuery) { - this.fromDate = fromDate; - this.toDate = toDate; - lastSearchType = LastSearchType.DATES; - Observable.just(programEventDetailRepository.filteredProgramEvents(programId, - DateUtils.getInstance().formatDate(fromDate), - DateUtils.getInstance().formatDate(toDate), - categoryOptionComboModel, orgUnitQuery) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - view::setData, - Timber::e)); - } -} diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailModel.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailModel.java deleted file mode 100644 index aa6a364406..0000000000 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailModel.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.dhis2.usescases.programEventDetail; - -import org.hisp.dhis.android.core.event.EventModel; -import org.hisp.dhis.android.core.program.ProgramTrackedEntityAttributeModel; - -import java.util.List; - -/** - * Created by Cristian on 22/02/2018. - * - */ - -public class ProgramEventDetailModel { - private List events; - private List attributesToShow; -} diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailModule.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailModule.java index 8eb639b9b9..ae9b9419cf 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailModule.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailModule.java @@ -28,15 +28,9 @@ ProgramEventDetailContract.View provideView(ProgramEventDetailActivity activity) @Provides @PerActivity - ProgramEventDetailContract.Presenter providesPresenter(ProgramEventDetailContract.Interactor interactor) { - return new ProgramEventDetailPresenter(interactor); - } - - @Provides - @PerActivity - ProgramEventDetailContract.Interactor provideInteractor(@NonNull ProgramEventDetailRepository programEventDetailRepository, - @NonNull MetadataRepository metadataRepository) { - return new ProgramEventDetailInteractor(programEventDetailRepository, metadataRepository); + ProgramEventDetailContract.Presenter providesPresenter(@NonNull ProgramEventDetailRepository programEventDetailRepository, + @NonNull MetadataRepository metadataRepository) { + return new ProgramEventDetailPresenter(programEventDetailRepository,metadataRepository); } @Provides diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailPresenter.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailPresenter.java index 535e590a7f..3f0e120da7 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailPresenter.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailPresenter.java @@ -1,20 +1,27 @@ package org.dhis2.usescases.programEventDetail; import android.os.Bundle; +import android.support.annotation.NonNull; +import org.dhis2.data.metadata.MetadataRepository; import org.dhis2.usescases.eventsWithoutRegistration.eventInitial.EventInitialActivity; import org.dhis2.utils.Constants; +import org.dhis2.utils.OrgUnitUtils; import org.dhis2.utils.Period; +import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.category.CategoryOptionComboModel; import org.hisp.dhis.android.core.event.EventModel; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; import org.hisp.dhis.android.core.program.ProgramModel; -import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueModel; import java.util.Date; import java.util.List; import io.reactivex.Observable; +import io.reactivex.android.schedulers.AndroidSchedulers; +import io.reactivex.disposables.CompositeDisposable; +import io.reactivex.schedulers.Schedulers; +import timber.log.Timber; import static org.dhis2.utils.Constants.NEW_EVENT; import static org.dhis2.utils.Constants.ORG_UNIT; @@ -27,21 +34,73 @@ public class ProgramEventDetailPresenter implements ProgramEventDetailContract.Presenter { + private final ProgramEventDetailRepository eventRepository; + private final MetadataRepository metaRepository; private ProgramEventDetailContract.View view; - private final ProgramEventDetailContract.Interactor interactor; public ProgramModel program; public String programId; + private CompositeDisposable compositeDisposable; + private CategoryOptionComboModel categoryOptionComboModel; + private List orgUnits; - ProgramEventDetailPresenter(ProgramEventDetailContract.Interactor interactor) { - this.interactor = interactor; + //Search fields + private CategoryComboModel mCatCombo; + private List dates; + private Period period; + private String orgUnitQuery; + + ProgramEventDetailPresenter( + @NonNull ProgramEventDetailRepository programEventDetailRepository, + @NonNull MetadataRepository metadataRepository) { + this.eventRepository = programEventDetailRepository; + this.metaRepository = metadataRepository; } @Override public void init(ProgramEventDetailContract.View mview, String programId, Period period) { view = mview; + compositeDisposable = new CompositeDisposable(); this.programId = programId; - interactor.init(view, programId, period); + compositeDisposable.add(metaRepository.getProgramWithId(programId) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + programModel -> { + view.setProgram(programModel); + view.setWritePermission(programModel.accessDataWrite()); + getCatCombo(programModel); + }, + Timber::d) + ); + + compositeDisposable.add(eventRepository.orgUnits() + .map(orgUnits -> { + this.orgUnits = orgUnits; + return OrgUnitUtils.renderTree(view.getContext(), orgUnits, true); + }) + .subscribeOn(Schedulers.computation()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + treeNode -> view.addTree(treeNode), + throwable -> view.renderError(throwable.getMessage()) + )); + + getProgramEventsWithDates(); + + } + + private void getCatCombo(ProgramModel programModel) { + compositeDisposable.add(metaRepository.getCategoryComboWithId(programModel.categoryCombo()) + .filter(categoryComboModel -> categoryComboModel != null && !categoryComboModel.isDefault() && !categoryComboModel.uid().equals(CategoryComboModel.DEFAULT_UID)) + .flatMap(catCombo -> { + this.mCatCombo = catCombo; + return eventRepository.catCombo(programModel.categoryCombo()); + }) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(catComboOptions -> view.setCatComboOptions(mCatCombo, catComboOptions), Timber::d) + ); } @Override @@ -66,23 +125,40 @@ public void setProgram(ProgramModel program) { } @Override - public void getProgramEventsWithDates(List dates, Period period, String orgUnitQuery) { - interactor.getProgramEventsWithDates(programId, dates, period, orgUnitQuery); + public void getProgramEventsWithDates() { + compositeDisposable.add( + view.currentPage() + .startWith(0) + .flatMap(page -> eventRepository.filteredProgramEvents(programId, dates, period, categoryOptionComboModel, orgUnitQuery, page).distinctUntilChanged()) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + view::setData, + throwable -> view.renderError(throwable.getMessage()))); } @Override public List getOrgUnits() { - return interactor.getOrgUnits(); + return this.orgUnits; + } + + @Override + public void setFilters(List selectedDates, Period currentPeriod, String orgUnits) { + this.dates = selectedDates; + this.period = currentPeriod; + this.orgUnitQuery = orgUnits; } @Override public void onCatComboSelected(CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery) { - interactor.updateFilters(categoryOptionComboModel, orgUnitQuery); + this.categoryOptionComboModel = categoryOptionComboModel; + } @Override public void clearCatComboFilters(String orgUnitQuery) { - interactor.updateFilters(null, orgUnitQuery); + this.categoryOptionComboModel = null; + } @Override @@ -97,7 +173,7 @@ public void onEventClick(String eventId, String orgUnit) { @Override public Observable> getEventDataValueNew(EventModel event) { - return interactor.getEventDataValueNew(event); + return eventRepository.eventDataValuesNew(event); } public void addEvent() { @@ -114,7 +190,7 @@ public void onBackClick() { @Override public void onDettach() { - interactor.onDettach(); + compositeDisposable.clear(); } @Override diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepository.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepository.java index 42b85e2ad1..b30d48d30d 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepository.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepository.java @@ -12,6 +12,7 @@ import java.util.Date; import java.util.List; +import io.reactivex.Flowable; import io.reactivex.Observable; /** @@ -21,11 +22,11 @@ public interface ProgramEventDetailRepository { - @NonNull - Observable> filteredProgramEvents(String programUid, String fromDate, String toDate, CategoryOptionComboModel categoryOptionComboModel,String orgUnitQuery); + /* @NonNull + Observable> filteredProgramEvents(String programUid, String fromDate, String toDate, CategoryOptionComboModel categoryOptionComboModel,String orgUnitQuery);*/ @NonNull - Observable> filteredProgramEvents(String programUid, List dates, Period period, CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery); + Flowable> filteredProgramEvents(String programUid, List dates, Period period, CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery, int page); @NonNull Observable> orgUnits(); diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepositoryImpl.java index 6935c2f3e0..6274744fb0 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailRepositoryImpl.java @@ -8,9 +8,11 @@ import org.dhis2.utils.DateUtils; import org.dhis2.utils.Period; +import org.dhis2.utils.ValueUtils; import org.hisp.dhis.android.core.category.CategoryComboModel; import org.hisp.dhis.android.core.category.CategoryOptionComboModel; import org.hisp.dhis.android.core.common.State; +import org.hisp.dhis.android.core.common.ValueType; import org.hisp.dhis.android.core.event.EventModel; import org.hisp.dhis.android.core.event.EventStatus; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; @@ -20,7 +22,10 @@ import java.util.ArrayList; import java.util.Date; import java.util.List; +import java.util.Locale; +import io.reactivex.BackpressureStrategy; +import io.reactivex.Flowable; import io.reactivex.Observable; /** @@ -29,15 +34,34 @@ public class ProgramEventDetailRepositoryImpl implements ProgramEventDetailRepository { - private final String EVENT_DATA_VALUES_NEW = "SELECT TrackedEntityDataValue.value, DataElement.optionSet FROM TrackedEntityDataValue " + + private final String EVENT_DATA_VALUES_NEW = "SELECT " + + "TrackedEntityDataValue.*, " + + "DataElement.valueType, " + + "DataElement.optionSet " + + "FROM TrackedEntityDataValue " + "JOIN DataElement ON DataElement.uid = TrackedEntityDataValue.dataElement WHERE TrackedEntityDataValue.event = ?\n" + "AND TrackedEntityDataValue.dataElement IN\n" + "(SELECT ProgramStageDataElement.dataElement FROM ProgramStageDataElement\n" + "WHERE ProgramStageDataElement.displayInReports = '1'\n" + - "ORDER BY ProgramStageDataElement.sortOrder ASC\n" + + "ORDER BY ProgramStageDataElement.sortOrder ASC LIMIT 3\n" + ")"; - private final String OPTION = "SELECT Option.displayName FROM Option WHERE Option.uid = ?"; + private final String EVENT_DATA_VALUES = "SELECT " + + "DE.uid, " + + "DE.displayName, " + + "DE.valueType, " + + "DE.optionSet, " + + "TrackedEntityDataValue.value " + + "FROM TrackedEntityDataValue " + + "JOIN (" + + "SELECT DataElement.uid AS uid, " + + "DataElement.displayName AS displayName, " + + "DataElement.valueType AS valueType, " + + "DataElement.optionSet AS optionSet " + + "FROM ProgramStageDataElement " + + "JOIN DataElement ON DataElement.uid = ProgramStageDataElement.dataElement " + + "WHERE ProgramStageDataElement.displayInReports = 1 GROUP BY DataElement.uid) AS DE ON DE.uid = TrackedEntityDataValue.dataElement " + + "WHERE TrackedEntityDataValue.event = ?"; private final BriteDatabase briteDatabase; @@ -46,34 +70,9 @@ public class ProgramEventDetailRepositoryImpl implements ProgramEventDetailRepos } @NonNull - private Observable> programEvents(String programUid, String fromDate, String toDate) { - String SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES = "SELECT * FROM " + EventModel.TABLE + " WHERE " + EventModel.Columns.PROGRAM + "='%s' AND " + EventModel.Columns.EVENT_DATE + " BETWEEN '%s' and '%s' " + - "AND " + EventModel.TABLE + "." + EventModel.Columns.STATE + " != '" + State.TO_DELETE + "' " + - "ORDER BY " + EventModel.TABLE + "." + EventModel.Columns.EVENT_DATE + " DESC"; - return briteDatabase.createQuery(EventModel.TABLE, - String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES, - programUid == null ? "" : programUid, - fromDate == null ? "" : fromDate, - toDate == null ? "" : toDate)) - .mapToList(cursor -> { - EventModel eventModel = EventModel.create(cursor); + private Flowable> programEvents(String programUid, List dates, Period period, String orgUnitQuery, int page) { + String pageQuery = String.format(Locale.US, " LIMIT %d,%d", page * 20, 20); - Cursor program = briteDatabase.query("SELECT * FROM Program WHERE uid = ?", programUid); - if (program != null && program.moveToFirst()) { - ProgramModel programModel = ProgramModel.create(program); - if (DateUtils.getInstance().hasExpired(eventModel, programModel.expiryDays(),programModel.completeEventsExpiryDays(),programModel.expiryPeriodType())){ - ContentValues contentValues = eventModel.toContentValues(); - contentValues.put(EventModel.Columns.STATUS, EventStatus.SKIPPED.toString()); - briteDatabase.update(EventModel.TABLE,contentValues,"uid = ?",eventModel.uid()); - } - program.close(); - } - return eventModel; - }); - } - - @NonNull - private Observable> programEvents(String programUid, List dates, Period period, String orgUnitQuery) { if (dates != null) { String SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES = "SELECT * FROM " + EventModel.TABLE + " WHERE " + EventModel.Columns.PROGRAM + "='%s' AND (%s) " + "AND " + EventModel.TABLE + "." + EventModel.Columns.STATE + " != '" + State.TO_DELETE + "'"; @@ -92,10 +91,10 @@ private Observable> programEvents(String programUid, List SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES += " ORDER BY " + EventModel.TABLE + "." + EventModel.Columns.EVENT_DATE + " DESC"; - return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES, + return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES + pageQuery, programUid == null ? "" : programUid, dateQuery == null ? "" : dateQuery)) - .mapToList(EventModel::create); + .mapToList(EventModel::create).toFlowable(BackpressureStrategy.LATEST); } else { String SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES = "SELECT * FROM " + EventModel.TABLE + " WHERE " + EventModel.Columns.PROGRAM + "='%s' " + "AND " + EventModel.TABLE + "." + EventModel.Columns.STATE + " != '" + State.TO_DELETE + "'"; @@ -105,26 +104,26 @@ private Observable> programEvents(String programUid, List SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES += " ORDER BY " + EventModel.TABLE + "." + EventModel.Columns.EVENT_DATE + " DESC"; - return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES, programUid == null ? "" : programUid)) + return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES + pageQuery, programUid == null ? "" : programUid)) .mapToList(cursor -> { EventModel eventModel = EventModel.create(cursor); Cursor program = briteDatabase.query("SELECT * FROM Program WHERE uid = ?", programUid); if (program != null && program.moveToFirst()) { ProgramModel programModel = ProgramModel.create(program); - if (DateUtils.getInstance().hasExpired(eventModel, programModel.expiryDays(),programModel.completeEventsExpiryDays(),programModel.expiryPeriodType())){ + if (DateUtils.getInstance().hasExpired(eventModel, programModel.expiryDays(), programModel.completeEventsExpiryDays(), programModel.expiryPeriodType())) { ContentValues contentValues = eventModel.toContentValues(); contentValues.put(EventModel.Columns.STATUS, EventStatus.SKIPPED.toString()); - briteDatabase.update(EventModel.TABLE,contentValues,"uid = ?",eventModel.uid()); + briteDatabase.update(EventModel.TABLE, contentValues, "uid = ?", eventModel.uid()); } program.close(); } return eventModel; - }); + }).toFlowable(BackpressureStrategy.LATEST); } } - @NonNull + /*@NonNull @Override public Observable> filteredProgramEvents(String programUid, String fromDate, String toDate, CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery) { if (categoryOptionComboModel == null) { @@ -148,22 +147,29 @@ public Observable> filteredProgramEvents(String programUid, Str Cursor program = briteDatabase.query("SELECT * FROM Program WHERE uid = ?", programUid); if (program != null && program.moveToFirst()) { ProgramModel programModel = ProgramModel.create(program); - if (DateUtils.getInstance().hasExpired(eventModel, programModel.expiryDays(),programModel.completeEventsExpiryDays(),programModel.expiryPeriodType())){ + if (DateUtils.getInstance().hasExpired(eventModel, programModel.expiryDays(), programModel.completeEventsExpiryDays(), programModel.expiryPeriodType())) { ContentValues contentValues = eventModel.toContentValues(); contentValues.put(EventModel.Columns.STATUS, EventStatus.SKIPPED.toString()); - briteDatabase.update(EventModel.TABLE,contentValues,"uid = ?",eventModel.uid()); + briteDatabase.update(EventModel.TABLE, contentValues, "uid = ?", eventModel.uid()); } program.close(); } return eventModel; }); - } + }*/ @NonNull @Override - public Observable> filteredProgramEvents(String programUid, List dates, Period period, CategoryOptionComboModel categoryOptionComboModel, String orgUnitQuery) { + public Flowable> filteredProgramEvents(String programUid, List dates, + Period period, + CategoryOptionComboModel categoryOptionComboModel, + String orgUnitQuery, + int page) { + + String pageQuery = String.format(Locale.US, " LIMIT %d,%d", page * 20, 20); + if (categoryOptionComboModel == null) { - return programEvents(programUid, dates, period, orgUnitQuery); + return programEvents(programUid, dates, period, orgUnitQuery, page); } if (dates != null) { String SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO = "SELECT * FROM " + EventModel.TABLE + " WHERE " + EventModel.Columns.PROGRAM + "='%s' AND " + EventModel.Columns.ATTRIBUTE_OPTION_COMBO + "='%s' AND (%s) " + @@ -181,11 +187,11 @@ public Observable> filteredProgramEvents(String programUid, Lis SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO += " AND " + EventModel.TABLE + "." + EventModel.Columns.ORGANISATION_UNIT + " IN (" + orgUnitQuery + ")"; String id = categoryOptionComboModel == null || categoryOptionComboModel.uid() == null ? "" : categoryOptionComboModel.uid(); - return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO, + return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO + pageQuery, programUid == null ? "" : programUid, id, dateQuery)) - .mapToList(EventModel::create); + .mapToList(EventModel::create).toFlowable(BackpressureStrategy.LATEST); } else { String SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO = "SELECT * FROM " + EventModel.TABLE + " WHERE " + EventModel.Columns.PROGRAM + "='%s' AND " + EventModel.Columns.ATTRIBUTE_OPTION_COMBO + "='%s' " + "AND " + EventModel.TABLE + "." + EventModel.Columns.STATE + " != '" + State.TO_DELETE + "'"; @@ -194,7 +200,7 @@ public Observable> filteredProgramEvents(String programUid, Lis SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO += " AND " + EventModel.TABLE + "." + EventModel.Columns.ORGANISATION_UNIT + " IN (" + orgUnitQuery + ")"; String id = categoryOptionComboModel == null || categoryOptionComboModel.uid() == null ? "" : categoryOptionComboModel.uid(); - return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO, + return briteDatabase.createQuery(EventModel.TABLE, String.format(SELECT_EVENT_WITH_PROGRAM_UID_AND_DATES_AND_CAT_COMBO + pageQuery, programUid == null ? "" : programUid, id)) .mapToList(cursor -> { @@ -203,15 +209,15 @@ public Observable> filteredProgramEvents(String programUid, Lis Cursor program = briteDatabase.query("SELECT * FROM Program WHERE uid = ?", programUid); if (program != null && program.moveToFirst()) { ProgramModel programModel = ProgramModel.create(program); - if (DateUtils.getInstance().hasExpired(eventModel, programModel.expiryDays(),programModel.completeEventsExpiryDays(),programModel.expiryPeriodType())){ + if (DateUtils.getInstance().hasExpired(eventModel, programModel.expiryDays(), programModel.completeEventsExpiryDays(), programModel.expiryPeriodType())) { ContentValues contentValues = eventModel.toContentValues(); contentValues.put(EventModel.Columns.STATUS, EventStatus.SKIPPED.toString()); - briteDatabase.update(EventModel.TABLE,contentValues,"uid = ?",eventModel.uid()); + briteDatabase.update(EventModel.TABLE, contentValues, "uid = ?", eventModel.uid()); } program.close(); } return eventModel; - }); + }).toFlowable(BackpressureStrategy.LATEST); } } @@ -238,14 +244,16 @@ public Observable> catCombo(String categoryComboU public Observable> eventDataValuesNew(EventModel eventModel) { List values = new ArrayList<>(); String id = eventModel == null || eventModel.uid() == null ? "" : eventModel.uid(); - Cursor cursor = briteDatabase.query(EVENT_DATA_VALUES_NEW, id); + Cursor cursor = briteDatabase.query(EVENT_DATA_VALUES, id); if (cursor != null && cursor.moveToFirst()) { for (int i = 0; i < cursor.getCount(); i++) { - String value = cursor.getString(0); + String value = cursor.getString(cursor.getColumnIndex("value")); + if(cursor.getString(cursor.getColumnIndex("optionSet"))!=null) + value = ValueUtils.optionSetCodeToDisplayName(briteDatabase,cursor.getString(cursor.getColumnIndex("optionSet")),value); + else if(cursor.getString(cursor.getColumnIndex("valueType")).equals(ValueType.ORGANISATION_UNIT.name())) + value = ValueUtils.orgUnitUidToDisplayName(briteDatabase,value); values.add(value); - - cursor.moveToNext(); } cursor.close(); diff --git a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailViewHolder.java b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailViewHolder.java index 14002096be..6cd170f90a 100644 --- a/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailViewHolder.java +++ b/app/src/main/java/org/dhis2/usescases/programEventDetail/ProgramEventDetailViewHolder.java @@ -3,8 +3,8 @@ import android.support.v7.widget.RecyclerView; import com.android.databinding.library.baseAdapters.BR; -import org.dhis2.databinding.ItemProgramEventBinding; +import org.dhis2.databinding.ItemProgramEventBinding; import org.hisp.dhis.android.core.event.EventModel; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -41,7 +41,9 @@ public void bind(ProgramEventDetailContract.Presenter presenter, EventModel even int valuesSize = values.size() > 3 ? 3 : values.size(); for (int i = 0; i < valuesSize; i++) { if (values.get(i) != null) - stringBuilder.append(values.get(i)).append("\n"); + stringBuilder.append(values.get(i)); + if (i != valuesSize - 1) + stringBuilder.append("\n"); } binding.dataValue.setText(stringBuilder); }, diff --git a/app/src/main/java/org/dhis2/usescases/programStageSelection/ProgramStageSelectionRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/programStageSelection/ProgramStageSelectionRepositoryImpl.java index fd66bb3a45..aa43e724c1 100644 --- a/app/src/main/java/org/dhis2/usescases/programStageSelection/ProgramStageSelectionRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/programStageSelection/ProgramStageSelectionRepositoryImpl.java @@ -3,14 +3,15 @@ import android.database.Cursor; import android.support.annotation.NonNull; +import com.squareup.sqlbrite2.BriteDatabase; + import org.dhis2.data.forms.RulesRepository; import org.dhis2.utils.DateUtils; import org.dhis2.utils.Result; -import com.squareup.sqlbrite2.BriteDatabase; - import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.enrollment.EnrollmentModel; import org.hisp.dhis.android.core.event.EventModel; +import org.hisp.dhis.android.core.event.EventStatus; import org.hisp.dhis.android.core.program.ProgramStageModel; import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueModel; import org.hisp.dhis.android.core.trackedentity.TrackedEntityDataValueModel; @@ -109,7 +110,7 @@ public class ProgramStageSelectionRepositoryImpl implements ProgramStageSelectio this.cachedRuleEngineFlowable = Flowable.zip( rulesRepository.rulesNew(programUid), - rulesRepository.ruleVariables(programUid), + rulesRepository.ruleVariablesProgramStages(programUid), ruleEvents(enrollmentUid), (rules, variables, ruleEvents) -> RuleEngineContext.builder(evaluator) @@ -130,7 +131,13 @@ private Flowable> ruleEvents(String enrollmentUid) { Date dueDate = cursor.isNull(4) ? eventDate : DateUtils.databaseDateFormat().parse(cursor.getString(4)); String orgUnit = cursor.getString(5); String programStage = cursor.getString(6); - RuleEvent.Status status = RuleEvent.Status.valueOf(cursor.getString(2)); + String eventStatus; + if (cursor.getString(2).equals(EventStatus.VISITED.name())) + eventStatus = EventStatus.ACTIVE.name(); + else + eventStatus = cursor.getString(2); + + RuleEvent.Status status = RuleEvent.Status.valueOf(eventStatus); Cursor dataValueCursor = briteDatabase.query(QUERY_VALUES, eventUid == null ? "" : eventUid); if (dataValueCursor != null && dataValueCursor.moveToFirst()) { diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java index 45ca3e7ecc..1d32ec42c1 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchRepositoryImpl.java @@ -11,6 +11,7 @@ import org.dhis2.usescases.searchTrackEntity.adapters.SearchTeiModel; import org.dhis2.utils.CodeGenerator; import org.dhis2.utils.Constants; +import org.dhis2.utils.ValueUtils; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.common.ObjectStyleModel; import org.hisp.dhis.android.core.common.State; @@ -84,19 +85,21 @@ public class SearchRepositoryImpl implements SearchRepository { private final String SEARCH_ATTR = " JOIN (ATTR_QUERY) tabla ON tabla.trackedEntityInstance = TrackedEntityInstance.uid"; private final String PROGRAM_TRACKED_ENTITY_ATTRIBUTES_VALUES_PROGRAM_QUERY = String.format( - "SELECT %s.* FROM %s " + + "SELECT %s.*, %s.%s, %s.%s FROM %s " + + "JOIN %s ON %s.%s = %s.%s " + "JOIN %s ON %s.%s = %s.%s " + "WHERE %s.%s = ? AND %s.%s = ? AND " + "%s.%s = 1 " + "ORDER BY %s.%s ASC", - TrackedEntityAttributeValueModel.TABLE, TrackedEntityAttributeValueModel.TABLE, + TrackedEntityAttributeValueModel.TABLE, TrackedEntityAttributeModel.TABLE, TrackedEntityAttributeModel.Columns.VALUE_TYPE, TrackedEntityAttributeModel.TABLE, TrackedEntityAttributeModel.Columns.OPTION_SET, TrackedEntityAttributeValueModel.TABLE, ProgramTrackedEntityAttributeModel.TABLE, ProgramTrackedEntityAttributeModel.TABLE, ProgramTrackedEntityAttributeModel.Columns.TRACKED_ENTITY_ATTRIBUTE, TrackedEntityAttributeValueModel.TABLE, TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_ATTRIBUTE, + TrackedEntityAttributeModel.TABLE, TrackedEntityAttributeModel.TABLE, TrackedEntityAttributeModel.Columns.UID, TrackedEntityAttributeValueModel.TABLE, TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_ATTRIBUTE, ProgramTrackedEntityAttributeModel.TABLE, ProgramTrackedEntityAttributeModel.Columns.PROGRAM, TrackedEntityAttributeValueModel.TABLE, TrackedEntityAttributeValueModel.Columns.TRACKED_ENTITY_INSTANCE, ProgramTrackedEntityAttributeModel.TABLE, ProgramTrackedEntityAttributeModel.Columns.DISPLAY_IN_LIST, ProgramTrackedEntityAttributeModel.TABLE, ProgramTrackedEntityAttributeModel.Columns.SORT_ORDER); private final String PROGRAM_TRACKED_ENTITY_ATTRIBUTES_VALUES_QUERY = String.format( - "SELECT %s.* FROM %s " + + "SELECT %s.*, TrackedEntityAttribute.valueType, TrackedEntityAttribute.optionSet FROM %s " + "JOIN %s ON %s.%s = %s.%s " + "WHERE %s.%s = ? AND %s.%s <> '0' ORDER BY %s.%s ASC", TrackedEntityAttributeValueModel.TABLE, TrackedEntityAttributeValueModel.TABLE, @@ -447,7 +450,7 @@ public Flowable> transformIntoModel(List te if (attributes != null) { attributes.moveToFirst(); for (int i = 0; i < attributes.getCount(); i++) { - tei.addAttributeValues(TrackedEntityAttributeValueModel.create(attributes)); + tei.addAttributeValues(ValueUtils.transform(briteDatabase, attributes)); attributes.moveToNext(); } attributes.close(); @@ -485,7 +488,6 @@ public Flowable> transformIntoModel(List te .toList().toFlowable(); } - private void updateProgramTable(Date lastUpdated, String programUid) { /*ContentValues program = new ContentValues();TODO: Crash if active program.put(EnrollmentModel.Columns.LAST_UPDATED, BaseIdentifiableObject.DATE_FORMAT.format(lastUpdated)); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java index 468c1a2677..d415f46647 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/SearchTEActivity.java @@ -26,6 +26,7 @@ import android.view.Window; import android.view.WindowManager; import android.widget.AdapterView; +import android.widget.Spinner; import org.dhis2.App; import org.dhis2.BuildConfig; @@ -45,10 +46,10 @@ import org.dhis2.utils.EndlessRecyclerViewScrollListener; import org.dhis2.utils.HelpManager; import org.dhis2.utils.NetworkUtils; - import org.hisp.dhis.android.core.program.ProgramModel; import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeModel; +import java.lang.reflect.Field; import java.util.ArrayList; import java.util.HashMap; import java.util.List; @@ -246,7 +247,7 @@ public void setTutorial() { HelpManager.getInstance().setScreenHelp(getClass().getName(), steps); - if (!prefs.getBoolean("TUTO_SEARCH_SHOWN", false)&& !BuildConfig.DEBUG) { + if (!prefs.getBoolean("TUTO_SEARCH_SHOWN", false) && !BuildConfig.DEBUG) { HelpManager.getInstance().showHelp();/* getAbstractActivity().fancyShowCaseQueue.show();*/ prefs.edit().putBoolean("TUTO_SEARCH_SHOWN", true).apply(); } @@ -301,11 +302,23 @@ public void setPrograms(List programModels) { setInitialProgram(programModels); else binding.programSpinner.setSelection(0); + try { + Field popup = Spinner.class.getDeclaredField("mPopup"); + popup.setAccessible(true); + + // Get private mPopup member variable and try cast to ListPopupWindow + android.widget.ListPopupWindow popupWindow = (android.widget.ListPopupWindow) popup.get(binding.programSpinner); + + // Set popupWindow height to 500px + popupWindow.setHeight(500); + } catch (NoClassDefFoundError | ClassCastException | NoSuchFieldException | IllegalAccessException e) { + // silently fail... + } binding.programSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() { @Override public void onItemSelected(AdapterView adapterView, View view, int pos, long id) { if (pos > 0) { - ProgramModel selectedProgram = (ProgramModel) adapterView.getItemAtPosition(pos -1); + ProgramModel selectedProgram = (ProgramModel) adapterView.getItemAtPosition(pos - 1); setProgramColor(presenter.getProgramColor(selectedProgram.uid())); presenter.setProgram((ProgramModel) adapterView.getItemAtPosition(pos - 1)); binding.enrollmentButton.setVisibility(View.VISIBLE); @@ -336,7 +349,6 @@ public void setProgramColor(String color) { int programColor = ColorUtils.getColorFrom(this, color); - SharedPreferences prefs = getAbstracContext().getSharedPreferences( Constants.SHARE_PREFS, Context.MODE_PRIVATE); if (programTheme != -1) { @@ -346,15 +358,33 @@ public void setProgramColor(String color) { binding.appbatlayout.setBackgroundColor(programColor); } else { prefs.edit().remove(Constants.PROGRAM_THEME).apply(); - binding.enrollmentButton.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(this, R.color.colorPrimary))); - binding.mainToolbar.setBackgroundColor(ContextCompat.getColor(this, R.color.colorPrimary)); - binding.appbatlayout.setBackgroundColor(ContextCompat.getColor(this, R.color.colorPrimary)); + int colorPrimary; + switch (prefs.getInt(Constants.THEME, R.style.AppTheme)) { + case R.style.AppTheme: + colorPrimary = R.color.colorPrimary; + break; + case R.style.RedTheme: + colorPrimary = R.color.colorPrimaryRed; + break; + case R.style.OrangeTheme: + colorPrimary = R.color.colorPrimaryOrange; + break; + case R.style.GreenTheme: + colorPrimary = R.color.colorPrimaryGreen; + break; + default: + colorPrimary = R.color.colorPrimary; + break; + } + binding.enrollmentButton.setBackgroundTintList(ColorStateList.valueOf(ContextCompat.getColor(this, colorPrimary))); + binding.mainToolbar.setBackgroundColor(ContextCompat.getColor(this, colorPrimary)); + binding.appbatlayout.setBackgroundColor(ContextCompat.getColor(this, colorPrimary)); } binding.executePendingBindings(); setTheme(prefs.getInt(Constants.PROGRAM_THEME, prefs.getInt(Constants.THEME, R.style.AppTheme))); - if(Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP) { Window window = getWindow(); window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS); TypedValue typedValue = new TypedValue(); diff --git a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/adapters/FormAdapter.java b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/adapters/FormAdapter.java index 8a05d28fe7..78677e9320 100644 --- a/app/src/main/java/org/dhis2/usescases/searchTrackEntity/adapters/FormAdapter.java +++ b/app/src/main/java/org/dhis2/usescases/searchTrackEntity/adapters/FormAdapter.java @@ -113,7 +113,7 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi ValueType.DATE, null, null, - holder.getAdapterPosition() == 0 ? programModel.selectEnrollmentDatesInFuture() : programModel.selectIncidentDatesInFuture(), true); + holder.getAdapterPosition() == 0 ? programModel.selectEnrollmentDatesInFuture() : programModel.selectIncidentDatesInFuture(), true,null); } else { TrackedEntityAttributeModel attr = attributeList.get(holder.getAdapterPosition() - programData); @@ -121,36 +121,38 @@ public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int positi String label = attr.displayName(); switch (holder.getItemViewType()) { case EDITTEXT: - viewModel = EditTextViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), label, 1, attr.valueType(), null, !attr.generated()); + viewModel = EditTextViewModel.create(attr.uid(), label, false, + queryData.get(attr.uid()), label, 1, attr.valueType(), null, !attr.generated(), + attr.displayDescription()); break; case BUTTON: - viewModel = FileViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null); + viewModel = FileViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null,attr.displayDescription()); break; case CHECKBOX: case YES_NO: - viewModel = RadioButtonViewModel.fromRawValue(attr.uid(), label, attr.valueType(), false, queryData.get(attr.uid()), null, true); + viewModel = RadioButtonViewModel.fromRawValue(attr.uid(), label, attr.valueType(), false, queryData.get(attr.uid()), null, true,attr.displayDescription()); break; case SPINNER: - viewModel = SpinnerViewModel.create(attr.uid(), label, "", false, attr.optionSet(), queryData.get(attr.uid()), null, true); + viewModel = SpinnerViewModel.create(attr.uid(), label, "", false, attr.optionSet(), queryData.get(attr.uid()), null, true,attr.displayDescription()); break; case COORDINATES: - viewModel = CoordinateViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null, true); + viewModel = CoordinateViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null, true,attr.displayDescription()); break; case TIME: case DATE: case DATETIME: - viewModel = DateTimeViewModel.create(attr.uid(), label, false, attr.valueType(), queryData.get(attr.uid()), null, true, true); + viewModel = DateTimeViewModel.create(attr.uid(), label, false, attr.valueType(), queryData.get(attr.uid()), null, true, true,attr.displayDescription()); break; case AGEVIEW: - viewModel = AgeViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null, true); + viewModel = AgeViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null, true,attr.displayDescription()); break; case ORG_UNIT: - viewModel = OrgUnitViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null, true); + viewModel = OrgUnitViewModel.create(attr.uid(), label, false, queryData.get(attr.uid()), null, true,attr.displayDescription()); break; default: Crashlytics.log("Unsupported viewType " + "source type: " + holder.getItemViewType()); - viewModel = EditTextViewModel.create(attr.uid(), "UNSUPORTED", false, null, "UNSUPPORTED", 1, attr.valueType(), null, false); + viewModel = EditTextViewModel.create(attr.uid(), "UNSUPORTED", false, null, "UNSUPPORTED", 1, attr.valueType(), null, false,attr.displayDescription()); break; } } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.java index 2616f986ae..599a9fe6d4 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/DashboardRepositoryImpl.java @@ -11,6 +11,7 @@ import org.dhis2.data.tuples.Trio; import org.dhis2.utils.CodeGenerator; import org.dhis2.utils.DateUtils; +import org.dhis2.utils.ValueUtils; import org.hisp.dhis.android.core.common.BaseIdentifiableObject; import org.hisp.dhis.android.core.common.State; import org.hisp.dhis.android.core.common.ValueType; @@ -103,7 +104,7 @@ public class DashboardRepositoryImpl implements DashboardRepository { "ON %s.%s = %s.%s " + "WHERE %s.%s = ? " + "AND %s.%s = ? " + - "AND " + EventModel.TABLE + "." + EventModel.Columns.STATE + " != '" + State.TO_DELETE + "'" + + "AND " + EventModel.TABLE + "." + EventModel.Columns.STATE + " != '" + State.TO_DELETE + "' " + "ORDER BY CASE WHEN %s.%s > %s.%s " + "THEN %s.%s ELSE %s.%s END DESC", EventModel.TABLE, EnrollmentModel.TABLE, @@ -132,7 +133,7 @@ public class DashboardRepositoryImpl implements DashboardRepository { private static final Set EVENTS_PROGRAM_STAGE_TABLE = new HashSet<>(Arrays.asList(EventModel.TABLE, EnrollmentModel.TABLE, ProgramStageModel.TABLE)); private final String ATTRIBUTE_VALUES_QUERY = String.format( - "SELECT TrackedEntityAttributeValue.*, TrackedEntityAttribute.valueType FROM %s " + + "SELECT TrackedEntityAttributeValue.*, TrackedEntityAttribute.valueType, TrackedEntityAttribute.optionSet FROM %s " + "JOIN %s ON %s.%s = %s.%s " + "JOIN %s ON %s.%s = %s.%s " + "WHERE %s.%s = ? " + @@ -147,7 +148,7 @@ public class DashboardRepositoryImpl implements DashboardRepository { ProgramTrackedEntityAttributeModel.TABLE, ProgramTrackedEntityAttributeModel.Columns.DISPLAY_IN_LIST, ProgramTrackedEntityAttributeModel.TABLE, ProgramTrackedEntityAttributeModel.Columns.SORT_ORDER); private final String ATTRIBUTE_VALUES_NO_PROGRAM_QUERY = String.format( - "SELECT %s.*, TrackedEntityAttribute.valueType FROM %s " + + "SELECT %s.*, TrackedEntityAttribute.valueType, TrackedEntityAttribute.optionSet FROM %s " + "JOIN %s ON %s.%s = %s.%s " + "JOIN %s ON %s.%s = %s.%s " + "WHERE %s.%s = ? GROUP BY %s.%s", @@ -437,48 +438,10 @@ public void updateTeiState() { public Observable> getTEIAttributeValues(String programUid, String teiUid) { if (programUid != null) return briteDatabase.createQuery(ATTRIBUTE_VALUES_TABLE, ATTRIBUTE_VALUES_QUERY, programUid == null ? "" : programUid, teiUid == null ? "" : teiUid) - .mapToList(cursor -> { - TrackedEntityAttributeValueModel teAttrValue = TrackedEntityAttributeValueModel.create(cursor); - int valueTypeIndex = cursor.getColumnIndex("valueType"); - if (cursor.getString(valueTypeIndex).equals(ValueType.ORGANISATION_UNIT.name())) { - String orgUnitUid = cursor.getString(cursor.getColumnIndex("value")); - Cursor orgUnitCursor = briteDatabase.query("SELECT OrganisationUnit.displayName FROM OrganisationUnit WHERE uid = ?", orgUnitUid); - if (orgUnitCursor != null && orgUnitCursor.moveToFirst()) { - String orgUnitName = orgUnitCursor.getString(0); - teAttrValue = TrackedEntityAttributeValueModel.builder() - .trackedEntityInstance(teAttrValue.trackedEntityInstance()) - .lastUpdated(teAttrValue.lastUpdated()) - .created(teAttrValue.created()) - .trackedEntityAttribute(teAttrValue.trackedEntityAttribute()) - .value(orgUnitName) - .build(); - orgUnitCursor.close(); - } - } - return teAttrValue; - }); + .mapToList(cursor -> ValueUtils.transform(briteDatabase,cursor)); else return briteDatabase.createQuery(ATTRIBUTE_VALUES_TABLE, ATTRIBUTE_VALUES_NO_PROGRAM_QUERY, teiUid == null ? "" : teiUid) - .mapToList(cursor -> { - TrackedEntityAttributeValueModel teAttrValue = TrackedEntityAttributeValueModel.create(cursor); - int valueTypeIndex = cursor.getColumnIndex("valueType"); - if (cursor.getString(valueTypeIndex).equals(ValueType.ORGANISATION_UNIT.name())) { - String orgUnitUid = cursor.getString(cursor.getColumnIndex("value")); - Cursor orgUnitCursor = briteDatabase.query("SELECT OrganisationUnit.displayName FROM OrganisationUnit WHERE uid = ?", orgUnitUid); - if (orgUnitCursor != null && orgUnitCursor.moveToFirst()) { - String orgUnitName = orgUnitCursor.getString(0); - teAttrValue = TrackedEntityAttributeValueModel.builder() - .trackedEntityInstance(teAttrValue.trackedEntityInstance()) - .lastUpdated(teAttrValue.lastUpdated()) - .created(teAttrValue.created()) - .trackedEntityAttribute(teAttrValue.trackedEntityAttribute()) - .value(orgUnitName) - .build(); - orgUnitCursor.close(); - } - } - return teAttrValue; - }); + .mapToList(cursor -> ValueUtils.transform(briteDatabase,cursor)); } @Override diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardActivity.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardActivity.java index 2f79072f67..167a3c0def 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardActivity.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardActivity.java @@ -2,6 +2,8 @@ import android.os.Bundle; import android.support.annotation.Nullable; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentStatePagerAdapter; import org.dhis2.App; import org.dhis2.usescases.general.ActivityGlobalAbstract; @@ -19,7 +21,6 @@ public class TeiDashboardActivity extends ActivityGlobalAbstract implements TeiD public TeiDashboardContracts.Presenter presenter; public DashboardProgramModel programModel; - public DashboardPagerAdapter adapter; public String teiUid; public String programUid; @@ -53,7 +54,7 @@ public String getToolbarTitle() { } @Override - public DashboardPagerAdapter getAdapter() { + public FragmentStatePagerAdapter getAdapter() { return null; } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardContracts.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardContracts.java index 0d4872a72a..fb5e48e876 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardContracts.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardContracts.java @@ -1,6 +1,7 @@ package org.dhis2.usescases.teiDashboard; import android.os.Bundle; +import android.support.v4.app.FragmentStatePagerAdapter; import android.widget.TextView; import org.dhis2.data.tuples.Pair; @@ -39,7 +40,7 @@ public interface View extends AbstractActivityContracts.View { String getToolbarTitle(); - DashboardPagerAdapter getAdapter(); + FragmentStatePagerAdapter getAdapter(); void showQR(); diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardPresenter.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardPresenter.java index 5721b0cfdc..480776ee03 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardPresenter.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/TeiDashboardPresenter.java @@ -157,7 +157,7 @@ public void getTEIEvents(TEIDataFragment teiFragment) { public void areEventsCompleted(TEIDataFragment teiDataFragment) { compositeDisposable.add( dashboardRepository.getEnrollmentEventsWithDisplay(programUid, teUid) - .flatMap(events -> events.isEmpty() ? dashboardRepository.getTEIEnrollmentEvents(programUid, teUid) : Observable.empty()) + .flatMap(events -> events.isEmpty() ? dashboardRepository.getTEIEnrollmentEvents(programUid, teUid) : Observable.just(events)) .map(events -> Observable.fromIterable(events).all(event -> event.status() == EventStatus.COMPLETED)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/DashboardPagerAdapter.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/DashboardPagerAdapter.java index f1421498e3..2a97424d11 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/DashboardPagerAdapter.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/DashboardPagerAdapter.java @@ -4,60 +4,57 @@ import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentStatePagerAdapter; -import org.dhis2.usescases.teiDashboard.DashboardProgramModel; import org.dhis2.usescases.teiDashboard.dashboardfragments.IndicatorsFragment; import org.dhis2.usescases.teiDashboard.dashboardfragments.NotesFragment; import org.dhis2.usescases.teiDashboard.dashboardfragments.RelationshipFragment; import org.dhis2.usescases.teiDashboard.dashboardfragments.TEIDataFragment; -import java.util.ArrayList; - /** * QUADRAM. Created by ppajuelo on 29/11/2017. */ public class DashboardPagerAdapter extends FragmentStatePagerAdapter { - private boolean isTablet; - private DashboardProgramModel dashboardProgram; + private static final int MOVILE_DASHBOARD_SIZE = 4; + private String currentProgram; - private ArrayList pagerFragments = new ArrayList<>(); - private ArrayList pagerFragmentsTitle = new ArrayList<>(); - public DashboardPagerAdapter(FragmentManager fm, DashboardProgramModel program, boolean isTablet) { + public DashboardPagerAdapter(FragmentManager fm, String program) { super(fm); - this.isTablet = isTablet; - this.dashboardProgram = program; - - if (!isTablet) { - pagerFragments.add(TEIDataFragment.createInstance()); - pagerFragmentsTitle.add("Overview"); - } - if (program.getCurrentProgram() != null) { - pagerFragments.add(RelationshipFragment.createInstance()); - pagerFragmentsTitle.add("Relationships"); - - pagerFragments.add(IndicatorsFragment.createInstance()); - pagerFragmentsTitle.add("Indicators"); - - pagerFragments.add(NotesFragment.createInstance()); - pagerFragmentsTitle.add("Notes"); - } - + this.currentProgram = program; } @Override public Fragment getItem(int position) { - return pagerFragments.get(position); + switch (position) { + default: + return TEIDataFragment.getInstance(); + case 1: + return RelationshipFragment.getInstance(); + case 2: + return IndicatorsFragment.getInstance(); + case 3: + return NotesFragment.getInstance(); + + } } @Override public int getCount() { - return pagerFragments.size(); + return currentProgram != null ? MOVILE_DASHBOARD_SIZE : 1; } @Override public CharSequence getPageTitle(int position) { - return pagerFragmentsTitle.get(position); + switch (position) { + default: + return "Overview"; + case 1: + return "Relationships"; + case 2: + return "Indicators"; + case 3: + return "Notes"; + } } } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/DashboardPagerTabletAdapter.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/DashboardPagerTabletAdapter.java new file mode 100644 index 0000000000..df362595e4 --- /dev/null +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/DashboardPagerTabletAdapter.java @@ -0,0 +1,57 @@ +package org.dhis2.usescases.teiDashboard.adapters; + +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentStatePagerAdapter; + +import org.dhis2.usescases.teiDashboard.dashboardfragments.IndicatorsFragment; +import org.dhis2.usescases.teiDashboard.dashboardfragments.NotesFragment; +import org.dhis2.usescases.teiDashboard.dashboardfragments.RelationshipFragment; + +/** + * QUADRAM. Created by ppajuelo on 29/11/2017. + */ + +public class DashboardPagerTabletAdapter extends FragmentStatePagerAdapter { + + private static final int MOVILE_DASHBOARD_SIZE = 3; + private String currentProgram; + + + public DashboardPagerTabletAdapter(FragmentManager fm, String program) { + super(fm); + this.currentProgram = program; + } + + @Override + public Fragment getItem(int position) { + switch (position) { + default: + return RelationshipFragment.getInstance(); + case 1: + return IndicatorsFragment.getInstance(); + case 2: + return NotesFragment.getInstance(); + + } + } + + @Override + public int getCount() { + return currentProgram != null ? MOVILE_DASHBOARD_SIZE : 1; + } + + @Override + public CharSequence getPageTitle(int position) { + switch (position) { + default: + return "Overview"; + case 1: + return "Relationships"; + case 2: + return "Indicators"; + case 3: + return "Notes"; + } + } +} diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/IndicatorViewHolder.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/IndicatorViewHolder.java index c23bea1e6e..bb6bd5af9e 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/IndicatorViewHolder.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/IndicatorViewHolder.java @@ -1,12 +1,16 @@ package org.dhis2.usescases.teiDashboard.adapters; import android.graphics.Color; +import android.support.annotation.NonNull; import android.support.v7.widget.RecyclerView; import org.dhis2.BR; +import org.dhis2.R; import org.dhis2.data.tuples.Trio; import org.dhis2.databinding.ItemIndicatorBinding; +import org.dhis2.utils.Constants; +import org.dhis2.utils.CustomViews.CustomDialog; import org.hisp.dhis.android.core.program.ProgramIndicatorModel; /** @@ -26,5 +30,19 @@ public void bind(Trio programIndicatorMod binding.setVariable(BR.value, programIndicatorModel.val1()); binding.setVariable(BR.colorBg, programIndicatorModel.val2().isEmpty() ? -1 : Color.parseColor(programIndicatorModel.val2())); binding.executePendingBindings(); + + binding.descriptionLabel.setOnClickListener(view->showDescription(programIndicatorModel.val0())); + } + + private void showDescription(@NonNull ProgramIndicatorModel programIndicatorModel) { + new CustomDialog( + itemView.getContext(), + programIndicatorModel.displayName(), + programIndicatorModel.displayDescription(), + itemView.getContext().getString(R.string.action_accept), + null, + Constants.DESCRIPTION_DIALOG, + null + ).show(); } } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/NotesViewholder.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/NotesViewholder.java index ae973b51c3..2727166348 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/NotesViewholder.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/adapters/NotesViewholder.java @@ -1,6 +1,7 @@ package org.dhis2.usescases.teiDashboard.adapters; import android.support.v7.widget.RecyclerView; +import android.text.TextUtils; import org.dhis2.databinding.ItemNotesBinding; import org.dhis2.utils.DateUtils; @@ -27,5 +28,14 @@ public void bind(NoteModel note) { binding.noteText.setText(note.value()); binding.storeBy.setText(note.storedBy()); binding.executePendingBindings(); + itemView.setOnClickListener(view->{ + if(binding.noteText.getMaxLines() == 1) { + binding.noteText.setMaxLines(Integer.MAX_VALUE); + binding.noteText.setEllipsize(null); + }else{ + binding.noteText.setMaxLines(1); + binding.noteText.setEllipsize(TextUtils.TruncateAt.END); + } + }); } } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/NotesFragment.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/NotesFragment.java index b8c5d41842..fd418686fc 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/NotesFragment.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/NotesFragment.java @@ -7,6 +7,7 @@ import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.view.LayoutInflater; +import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; @@ -17,7 +18,6 @@ import org.dhis2.usescases.teiDashboard.TeiDashboardContracts; import org.dhis2.usescases.teiDashboard.adapters.NotesAdapter; import org.dhis2.usescases.teiDashboard.mobile.TeiDashboardMobileActivity; - import org.hisp.dhis.android.core.enrollment.note.NoteModel; import java.util.List; @@ -59,6 +59,17 @@ public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup c binding.notesRecycler.setAdapter(noteAdapter); binding.buttonAdd.setOnClickListener(this::addNote); binding.buttonDelete.setOnClickListener(this::clearNote); + binding.editNote.setOnTouchListener((v, event) -> { + if (v.getId() == R.id.edit_note) { + v.getParent().requestDisallowInterceptTouchEvent(true); + switch (event.getAction() & MotionEvent.ACTION_MASK) { + case MotionEvent.ACTION_UP: + v.getParent().requestDisallowInterceptTouchEvent(false); + break; + } + } + return false; + }); return binding.getRoot(); } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/TEIDataFragment.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/TEIDataFragment.java index c04bfa302f..4e43abdb2b 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/TEIDataFragment.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/dashboardfragments/TEIDataFragment.java @@ -84,9 +84,9 @@ public static TEIDataFragment getInstance() { return instance; } - public static TEIDataFragment createInstance() { + /* public static TEIDataFragment createInstance() { return instance = new TEIDataFragment(); - } + }*/ @Override public void onAttach(Context context) { @@ -225,7 +225,7 @@ public Consumer> setEvents() { public Consumer displayGenerateEvent() { return programStageModel -> { this.programStageFromEvent = programStageModel; - if (programStageModel.displayGenerateEventBox()) { + if (programStageModel.displayGenerateEventBox() || programStageModel.allowGenerateNextVisit()) { dialog = new CustomDialog( getContext(), getString(R.string.dialog_generate_new_event), diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailActivity.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailActivity.java index 7f71d3c17b..823c95eaaa 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailActivity.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailActivity.java @@ -10,7 +10,9 @@ import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.view.Gravity; import android.view.View; +import android.widget.PopupMenu; import org.dhis2.App; import org.dhis2.R; @@ -25,12 +27,13 @@ import org.dhis2.utils.DateUtils; import org.dhis2.utils.DialogClickListener; import org.dhis2.utils.HelpManager; - import org.hisp.dhis.android.core.event.EventModel; import org.hisp.dhis.android.core.event.EventStatus; import org.hisp.dhis.android.core.organisationunit.OrganisationUnitModel; import org.hisp.dhis.android.core.program.ProgramModel; +import java.lang.reflect.Field; +import java.lang.reflect.Method; import java.util.ArrayList; import javax.inject.Inject; @@ -80,9 +83,9 @@ public void setData(EventDetailModel eventDetailModel, MetadataRepository metada setDataEditable(); binding.orgUnit.setText(eventDetailModel.getOrgUnitName()); - if(eventDetailModel.getOptionComboList().isEmpty()){ + if (eventDetailModel.getOptionComboList().isEmpty()) { binding.categoryComboLayout.setVisibility(View.GONE); - }else{ + } else { binding.categoryComboLayout.setVisibility(View.VISIBLE); binding.categoryComboLayout.setHint(eventDetailModel.getCatComboName()); binding.categoryCombo.setText(eventDetailModel.getEventCatComboOptionName()); @@ -106,7 +109,7 @@ public void setData(EventDetailModel eventDetailModel, MetadataRepository metada false, true), "EVENT_DATA_ENTRY") .commit(); - if(!HelpManager.getInstance().isTutorialReadyForScreen(getClass().getName())) + if (!HelpManager.getInstance().isTutorialReadyForScreen(getClass().getName())) setTutorial(); } @@ -222,7 +225,7 @@ public void setTutorial() { new Handler().postDelayed(() -> { FancyShowCaseView tuto1 = new FancyShowCaseView.Builder(getAbstractActivity()) .title(getString(R.string.tuto_tei_event_1)) - .focusOn(getAbstractActivity().findViewById(R.id.toolbarDelete)) + .focusOn(getAbstractActivity().findViewById(R.id.moreOptions)) .closeOnTouch(true) .build(); FancyShowCaseView tuto2 = new FancyShowCaseView.Builder(getAbstractActivity()) @@ -247,4 +250,37 @@ public void setTutorial() { }, 500); } + + @Override + public void showMoreOptions(View view) { + PopupMenu popupMenu = new PopupMenu(this, view, Gravity.BOTTOM); + try { + Field[] fields = popupMenu.getClass().getDeclaredFields(); + for (Field field : fields) { + if ("mPopup".equals(field.getName())) { + field.setAccessible(true); + Object menuPopupHelper = field.get(popupMenu); + Class classPopupHelper = Class.forName(menuPopupHelper.getClass().getName()); + Method setForceIcons = classPopupHelper.getMethod("setForceShowIcon", boolean.class); + setForceIcons.invoke(menuPopupHelper, true); + break; + } + } + } catch (Exception e) { + e.printStackTrace(); + } + popupMenu.getMenuInflater().inflate(R.menu.event_menu, popupMenu.getMenu()); + popupMenu.setOnMenuItemClickListener(item -> { + switch (item.getItemId()) { + case R.id.showHelp: + showTutorial(false); + break; + case R.id.menu_delete: + presenter.confirmDeleteEvent(); + break; + } + return false; + }); + popupMenu.show(); + } } diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailModel.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailModel.java index da08bab463..a40a61352f 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailModel.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/eventDetail/EventDetailModel.java @@ -26,7 +26,7 @@ public class EventDetailModel extends BaseObservable { private final List dataElemets; private final List stageSections; - private final HashMap> fieldsElements; +// private final HashMap> fieldsElements; private final ProgramStageModel programStage; private final List optionComboList; private final ProgramModel programModel; @@ -43,17 +43,17 @@ public class EventDetailModel extends BaseObservable { this.dataElemets = programStageDataElementModelList; this.stageSections = programStageSectionModelList; this.programStage = programStage; - fieldsElements = new HashMap<>(); +// fieldsElements = new HashMap<>(); this.orgUnit = orgUnit; this.catComboName = optionComboList.val0(); this.optionComboList = optionComboList.val1(); this.programModel = programModel; - setUpFields(); +// setUpFields(); } - private void setUpFields() { + /* private void setUpFields() { ArrayList sectionDataElements = new ArrayList<>(); for (ProgramStageDataElementModel de : dataElemets) { @@ -69,12 +69,12 @@ private void setUpFields() { sectionDataElements.add(de); fieldsElements.put(section.uid(), sectionDataElements); } - } + }*/ EventModel getEventModel() { return eventModel; } - +/* List getStageSections() { return stageSections; } @@ -92,7 +92,7 @@ String getValueForDE(String dataelementUid) { return trackedEntityDataValueModel.value(); } return null; - } + }*/ public ProgramStageModel getProgramStage() { return programStage; diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/mobile/TeiDashboardMobileActivity.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/mobile/TeiDashboardMobileActivity.java index 1b6409debb..e1cfc33c06 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/mobile/TeiDashboardMobileActivity.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/mobile/TeiDashboardMobileActivity.java @@ -9,6 +9,7 @@ import android.os.Handler; import android.support.annotation.Nullable; import android.support.design.widget.TabLayout; +import android.support.v4.app.FragmentStatePagerAdapter; import android.text.TextUtils; import android.view.Gravity; import android.view.View; @@ -23,6 +24,7 @@ import org.dhis2.usescases.teiDashboard.TeiDashboardActivity; import org.dhis2.usescases.teiDashboard.TeiDashboardContracts; import org.dhis2.usescases.teiDashboard.adapters.DashboardPagerAdapter; +import org.dhis2.usescases.teiDashboard.adapters.DashboardPagerTabletAdapter; import org.dhis2.usescases.teiDashboard.dashboardfragments.RelationshipFragment; import org.dhis2.usescases.teiDashboard.dashboardfragments.TEIDataFragment; import org.dhis2.usescases.teiDashboard.teiProgramList.TeiProgramListActivity; @@ -41,6 +43,7 @@ public class TeiDashboardMobileActivity extends TeiDashboardActivity implements TeiDashboardContracts.View { ActivityDashboardMobileBinding binding; + public FragmentStatePagerAdapter adapter; @Override protected void onCreate(@Nullable Bundle savedInstanceState) { @@ -57,11 +60,22 @@ protected void onCreate(@Nullable Bundle savedInstanceState) { binding.tabLayout.setTabMode(TabLayout.MODE_SCROLLABLE); binding.toolbarTitle.setLines(1); binding.toolbarTitle.setEllipsize(TextUtils.TruncateAt.END); + } + @Override protected void onResume() { super.onResume(); + + if (adapter == null) { + if (!getResources().getBoolean(R.bool.is_tablet)) + adapter = new DashboardPagerAdapter(getSupportFragmentManager(), programUid); + else + adapter = new DashboardPagerTabletAdapter(getSupportFragmentManager(), programUid); + binding.teiPager.setAdapter(adapter); + } + init(teiUid, programUid); } @@ -78,10 +92,6 @@ public void init(String teiUid, String programUid) { @Override public void setData(DashboardProgramModel program) { - if (adapter == null) { - adapter = new DashboardPagerAdapter(getSupportFragmentManager(), program, getResources().getBoolean(R.bool.is_tablet)); - binding.teiPager.setAdapter(adapter); - } if (getResources().getBoolean(R.bool.is_tablet)) getSupportFragmentManager().beginTransaction() @@ -106,10 +116,7 @@ public void setData(DashboardProgramModel program) { @Override public void setDataWithOutProgram(DashboardProgramModel program) { - if (adapter == null) { - adapter = new DashboardPagerAdapter(getSupportFragmentManager(), program, getResources().getBoolean(R.bool.is_tablet)); - binding.teiPager.setAdapter(adapter); - } + binding.setDashboardModel(program); binding.setTrackEntity(program.getTei()); String title = program.getAttributeBySortOrder(1) + " " + program.getAttributeBySortOrder(2); @@ -123,7 +130,7 @@ public void setDataWithOutProgram(DashboardProgramModel program) { } @Override - public DashboardPagerAdapter getAdapter() { + public FragmentStatePagerAdapter getAdapter() { return adapter; } @@ -160,7 +167,7 @@ protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (data.hasExtra("CHANGE_PROGRAM")) { programUid = data.getStringExtra("CHANGE_PROGRAM"); - init(teiUid, data.getStringExtra("CHANGE_PROGRAM")); + adapter = null; } } super.onActivityResult(requestCode, resultCode, data); diff --git a/app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListEnrollmentViewHolder.java b/app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListEnrollmentViewHolder.java index a94b3449aa..83a69b5bbf 100644 --- a/app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListEnrollmentViewHolder.java +++ b/app/src/main/java/org/dhis2/usescases/teiDashboard/teiProgramList/TeiProgramListEnrollmentViewHolder.java @@ -6,8 +6,9 @@ import android.support.v4.content.ContextCompat; import android.support.v7.widget.RecyclerView; import android.widget.ImageView; +import android.widget.RelativeLayout; -import com.android.databinding.library.baseAdapters.BR; +import org.dhis2.BR; import org.dhis2.R; import org.dhis2.databinding.ItemTeiProgramsEnrollmentBinding; import org.dhis2.databinding.ItemTeiProgramsEnrollmentInactiveBinding; @@ -36,10 +37,14 @@ public void bind(TeiProgramListContract.Presenter presenter, EnrollmentViewModel if (enrollment != null) { ImageView programImage; - if (binding instanceof ItemTeiProgramsEnrollmentBinding) + RelativeLayout iconBg; + if (binding instanceof ItemTeiProgramsEnrollmentBinding) { programImage = ((ItemTeiProgramsEnrollmentBinding) binding).programImage; - else + iconBg = ((ItemTeiProgramsEnrollmentBinding) binding).iconBg; + } else { programImage = ((ItemTeiProgramsEnrollmentInactiveBinding) binding).programImage; + iconBg = ((ItemTeiProgramsEnrollmentInactiveBinding) binding).iconBg; + } int color = enrollment != null ? ColorUtils.getColorFrom(itemView.getContext(), enrollment.color()) : ColorUtils.getPrimaryColor(itemView.getContext(), ColorUtils.ColorType.PRIMARY_LIGHT); int icon; @@ -56,14 +61,20 @@ public void bind(TeiProgramListContract.Presenter presenter, EnrollmentViewModel programImage.setImageDrawable(ColorUtils.tintDrawableReosurce(iconImage, color)); Drawable bgImage = ContextCompat.getDrawable(itemView.getContext(), R.drawable.photo_temp_gray); - programImage.setBackground(ColorUtils.tintDrawableWithColor(bgImage, color)); + iconBg.setBackground(ColorUtils.tintDrawableWithColor(bgImage, color)); } else if (programModel != null) { ImageView programImage; - if (binding instanceof ItemTeiProgramsProgramsBinding) + RelativeLayout iconBg; + if (binding instanceof ItemTeiProgramsProgramsBinding) { programImage = ((ItemTeiProgramsProgramsBinding) binding).programImage; - else + iconBg = ((ItemTeiProgramsProgramsBinding) binding).iconBg; + + } else { programImage = ((ItemTeiProgramsEnrollmentInactiveBinding) binding).programImage; + iconBg = ((ItemTeiProgramsEnrollmentInactiveBinding) binding).iconBg; + + } int color = programModel != null ? ColorUtils.getColorFrom(itemView.getContext(), programModel.color()) : ColorUtils.getPrimaryColor(itemView.getContext(), ColorUtils.ColorType.PRIMARY_LIGHT); int icon; @@ -80,7 +91,7 @@ public void bind(TeiProgramListContract.Presenter presenter, EnrollmentViewModel programImage.setImageDrawable(ColorUtils.tintDrawableReosurce(iconImage, color)); Drawable bgImage = ContextCompat.getDrawable(itemView.getContext(), R.drawable.photo_temp_gray); - programImage.setBackground(ColorUtils.tintDrawableWithColor(bgImage, color)); + iconBg.setBackground(ColorUtils.tintDrawableWithColor(bgImage, color)); } binding.executePendingBindings(); diff --git a/app/src/main/java/org/dhis2/utils/Constants.java b/app/src/main/java/org/dhis2/utils/Constants.java index 60dcf33999..3a15d4f36c 100644 --- a/app/src/main/java/org/dhis2/utils/Constants.java +++ b/app/src/main/java/org/dhis2/utils/Constants.java @@ -49,6 +49,8 @@ public class Constants { public static final String PROGRAM_THEME = "PROGRAM_THEME"; public static final String SERVER = "SERVER"; public static final String THEME = "THEME"; + public static final String DATA_SET_UID = "DATA_SET_UID"; + public static final int DESCRIPTION_DIALOG = 111; public static String LAST_DATA_SYNC = "last_data_sync"; public static String LAST_DATA_SYNC_STATUS = "last_data_sync_status"; diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/AgeView.java b/app/src/main/java/org/dhis2/utils/CustomViews/AgeView.java index 6db0d7fe8d..b030242fcd 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/AgeView.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/AgeView.java @@ -42,6 +42,7 @@ public class AgeView extends RelativeLayout implements View.OnClickListener, Vie private OnAgeSet listener; private LayoutInflater inflater; private String label; + private String description; public AgeView(Context context) { super(context); @@ -64,12 +65,16 @@ private void init(Context context) { } - public void setLabel(String label) { + public void setLabel(String label,String description) { this.label = label; - if (binding instanceof AgeCustomViewAccentBinding) + this.description = description; + if (binding instanceof AgeCustomViewAccentBinding) { ((AgeCustomViewAccentBinding) binding).setLabel(label); - else + ((AgeCustomViewAccentBinding) binding).setDescription(description); + }else { ((AgeCustomViewBinding) binding).setLabel(label); + ((AgeCustomViewBinding) binding).setDescription(description); + } } public void setWarningOrError(String warningOrError) { diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/CoordinatesView.java b/app/src/main/java/org/dhis2/utils/CustomViews/CoordinatesView.java index 1b20d8e0c8..3f355a4975 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/CoordinatesView.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/CoordinatesView.java @@ -95,6 +95,13 @@ public void setLabel(String label) { ((FormCoordinatesAccentBinding) binding).setLabel(label); } + public void setDescription(String description) { + if (binding instanceof FormCoordinatesBinding) + ((FormCoordinatesBinding) binding).setDescription(description); + else + ((FormCoordinatesAccentBinding) binding).setDescription(description); + } + public void setInitialValue(String initialValue) { this.latLong.setText(initialValue.replace("[", "").replace("]", "")); } diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/CustomDialog.java b/app/src/main/java/org/dhis2/utils/CustomViews/CustomDialog.java index 204b1123d3..23c991b858 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/CustomDialog.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/CustomDialog.java @@ -33,7 +33,7 @@ public CustomDialog(@NonNull Context context, @NonNull String title, @NonNull String message, @NonNull String positiveText, - @NonNull String negativeText, + @Nullable String negativeText, int requestCode, @Nullable DialogClickListener listener) { super(context); @@ -87,14 +87,14 @@ public void onClick(View view) { case R.id.negative: if (listener != null) { listener.onNegative(); - dismiss(); } + dismiss(); break; case R.id.possitive: if (listener != null) { listener.onPositive(); - dismiss(); } + dismiss(); break; default: break; diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/DateAdapter.java b/app/src/main/java/org/dhis2/utils/CustomViews/DateAdapter.java index ce5528986c..a24b32800e 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/DateAdapter.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/DateAdapter.java @@ -8,6 +8,7 @@ import org.dhis2.R; import org.dhis2.databinding.ItemDateBinding; +import org.dhis2.utils.DateUtils; import org.dhis2.utils.Period; import java.text.SimpleDateFormat; @@ -36,7 +37,7 @@ public class DateAdapter extends RecyclerView.Adapter { public DateAdapter(Period period) { currentPeriod = period; - Calendar calendar = Calendar.getInstance(); + Calendar calendar = DateUtils.getInstance().getCalendar(); calendar.add(Calendar.YEAR, 1); //let's the user select dates in the next year int year = calendar.get(Calendar.YEAR); diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/DateTimeView.java b/app/src/main/java/org/dhis2/utils/CustomViews/DateTimeView.java index 6c55521def..44f184a0a7 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/DateTimeView.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/DateTimeView.java @@ -62,6 +62,10 @@ public void setLabel(String label) { binding.setLabel(label); binding.executePendingBindings(); } + public void setDescription(String description) { + binding.setDescription(description); + binding.executePendingBindings(); + } public void initData(String data) { if (data != null) { diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/DateView.java b/app/src/main/java/org/dhis2/utils/CustomViews/DateView.java index d3cc321269..57129462fa 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/DateView.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/DateView.java @@ -40,6 +40,7 @@ public class DateView extends RelativeLayout implements View.OnClickListener { private String label; private boolean allowFutureDates; + private String description; public DateView(Context context) { super(context); @@ -87,6 +88,12 @@ public void setLabel(String label) { binding.executePendingBindings(); } + public void setDescription(String description) { + this.description = description; + binding.setVariable(BR.description, description); + binding.executePendingBindings(); + } + public void setAllowFutureDates(boolean allowFutureDates) { this.allowFutureDates = allowFutureDates; } diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/TimeView.java b/app/src/main/java/org/dhis2/utils/CustomViews/TimeView.java index 0a7ccbc9a6..6da8482689 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/TimeView.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/TimeView.java @@ -38,6 +38,7 @@ public class TimeView extends RelativeLayout implements View.OnClickListener { private OnDateSelected listener; private String label; + private String description; public TimeView(Context context) { super(context); @@ -80,6 +81,12 @@ public void setLabel(String label) { binding.executePendingBindings(); } + public void setDescription(String description) { + this.description = description; + binding.setVariable(BR.description, description); + binding.executePendingBindings(); + } + public void initData(String data) { if (data != null) { Date date = null; diff --git a/app/src/main/java/org/dhis2/utils/CustomViews/YesNoView.java b/app/src/main/java/org/dhis2/utils/CustomViews/YesNoView.java index 6536ccd097..dc33baed68 100644 --- a/app/src/main/java/org/dhis2/utils/CustomViews/YesNoView.java +++ b/app/src/main/java/org/dhis2/utils/CustomViews/YesNoView.java @@ -65,6 +65,11 @@ public void setLabel(String label) { binding.executePendingBindings(); } + public void setDescription(String description) { + binding.setVariable(BR.description, description); + binding.executePendingBindings(); + } + @Override public void onCheckedChanged(RadioGroup radioGroup, int i) { diff --git a/app/src/main/java/org/dhis2/utils/DateUtils.java b/app/src/main/java/org/dhis2/utils/DateUtils.java index b416f31ab9..0058d74b25 100644 --- a/app/src/main/java/org/dhis2/utils/DateUtils.java +++ b/app/src/main/java/org/dhis2/utils/DateUtils.java @@ -4,6 +4,7 @@ import android.support.annotation.Nullable; import org.hisp.dhis.android.core.event.EventModel; +import org.hisp.dhis.android.core.event.EventStatus; import org.hisp.dhis.android.core.period.PeriodType; import java.text.SimpleDateFormat; @@ -259,6 +260,11 @@ public Calendar getCalendar() { public boolean hasExpired(@NonNull EventModel event, int expiryDays, int completeEventExpiryDays, @Nullable PeriodType expiryPeriodType) { Calendar expiredDate = Calendar.getInstance(); + if(event.status() == EventStatus.COMPLETED){ + if (completeEventExpiryDays == 0) + return false; + } + if (event.completedDate() != null) expiredDate.setTime(event.completedDate()); else { diff --git a/app/src/main/java/org/dhis2/utils/EndlessRecyclerViewScrollListener.java b/app/src/main/java/org/dhis2/utils/EndlessRecyclerViewScrollListener.java index 9e99fafd71..fa77a5867c 100644 --- a/app/src/main/java/org/dhis2/utils/EndlessRecyclerViewScrollListener.java +++ b/app/src/main/java/org/dhis2/utils/EndlessRecyclerViewScrollListener.java @@ -38,7 +38,7 @@ public EndlessRecyclerViewScrollListener(StaggeredGridLayoutManager layoutManage visibleThreshold = visibleThreshold * layoutManager.getSpanCount(); } - public EndlessRecyclerViewScrollListener(RecyclerView.LayoutManager layoutManager,int visibleThreshold,int initialPage) { + public EndlessRecyclerViewScrollListener(RecyclerView.LayoutManager layoutManager, int visibleThreshold, int initialPage) { this.mLayoutManager = layoutManager; this.visibleThreshold = visibleThreshold; this.currentPage = initialPage; @@ -125,7 +125,7 @@ public void resetState() { this.loading = true; } - public void resetState(Integer initialPage){ + public void resetState(Integer initialPage) { this.currentPage = initialPage; this.previousTotalItemCount = 0; this.loading = true; @@ -134,4 +134,8 @@ public void resetState(Integer initialPage){ // Defines the process for actually loading more data based on page public abstract void onLoadMore(int page, int totalItemsCount, RecyclerView view); + public int getCurrentPage() { + return currentPage; + } + } \ No newline at end of file diff --git a/app/src/main/java/org/dhis2/utils/RulesUtilsProviderImpl.java b/app/src/main/java/org/dhis2/utils/RulesUtilsProviderImpl.java index 10b66070ae..0cb82387ff 100644 --- a/app/src/main/java/org/dhis2/utils/RulesUtilsProviderImpl.java +++ b/app/src/main/java/org/dhis2/utils/RulesUtilsProviderImpl.java @@ -55,8 +55,8 @@ public void applyRuleEffects(Map fieldViewModels, Result RuleActionDisplayText displayText = (RuleActionDisplayText) ruleAction; EditTextViewModel textViewModel = EditTextViewModel.create(uid, displayText.content(), false, displayText.data(), "Information", 1, ValueType.TEXT, - fieldViewModels.get(0).programStageSection() != null ? fieldViewModels.get(0).programStageSection() : null, - false); + fieldViewModels.get(0).programStageSection(), + false,null); fieldViewModels.put(uid, textViewModel); } else if (ruleAction instanceof RuleActionDisplayKeyValuePair) { String uid = codeGenerator.generate(); @@ -65,7 +65,7 @@ public void applyRuleEffects(Map fieldViewModels, Result (RuleActionDisplayKeyValuePair) ruleAction; EditTextViewModel textViewModel = EditTextViewModel.create(uid, - displayText.content(), false, displayText.data(), displayText.content(), 1, ValueType.TEXT, null, false); + displayText.content(), false, displayText.data(), displayText.content(), 1, ValueType.TEXT, null, false,null); fieldViewModels.put(uid, textViewModel); } else if (ruleAction instanceof RuleActionHideSection) { diff --git a/app/src/main/java/org/dhis2/utils/ValueUtils.java b/app/src/main/java/org/dhis2/utils/ValueUtils.java new file mode 100644 index 0000000000..976a78e5d3 --- /dev/null +++ b/app/src/main/java/org/dhis2/utils/ValueUtils.java @@ -0,0 +1,84 @@ +package org.dhis2.utils; + +import android.database.Cursor; + +import com.squareup.sqlbrite2.BriteDatabase; + +import org.hisp.dhis.android.core.common.ValueType; +import org.hisp.dhis.android.core.option.OptionModel; +import org.hisp.dhis.android.core.trackedentity.TrackedEntityAttributeValueModel; + +/** + * QUADRAM. Created by ppajuelo on 25/09/2018. + */ + +public class ValueUtils { + + /** + * @param briteDatabase access to database + * @param cursor cursor of the original TEAV + * @return Returns a trackedEntityAttributeValueModel which value has been parse for valueType orgunit uid or optionSet code/name + */ + public static TrackedEntityAttributeValueModel transform(BriteDatabase briteDatabase, Cursor cursor) { + TrackedEntityAttributeValueModel teAttrValue = TrackedEntityAttributeValueModel.create(cursor); + int valueTypeIndex = cursor.getColumnIndex("valueType"); + int optionSetIndex = cursor.getColumnIndex("optionSet"); + if (cursor.getString(valueTypeIndex).equals(ValueType.ORGANISATION_UNIT.name())) { + String orgUnitUid = cursor.getString(cursor.getColumnIndex("value")); + Cursor orgUnitCursor = briteDatabase.query("SELECT OrganisationUnit.displayName FROM OrganisationUnit WHERE OrganisationUnit.uid = ?", orgUnitUid); + if (orgUnitCursor != null && orgUnitCursor.moveToFirst()) { + String orgUnitName = orgUnitCursor.getString(0); + teAttrValue = TrackedEntityAttributeValueModel.builder() + .trackedEntityInstance(teAttrValue.trackedEntityInstance()) + .lastUpdated(teAttrValue.lastUpdated()) + .created(teAttrValue.created()) + .trackedEntityAttribute(teAttrValue.trackedEntityAttribute()) + .value(orgUnitName) + .build(); + orgUnitCursor.close(); + } + } else if (cursor.getString(optionSetIndex) != null) { + String optionSet = cursor.getString(optionSetIndex); + String optionCode = cursor.getString(cursor.getColumnIndex("value")); + Cursor optionsCursor = briteDatabase.query("SELECT * FROM Option WHERE optionSet = ?", optionSet); + if (optionsCursor != null && optionsCursor.moveToFirst()) { + for (int i = 0; i < optionsCursor.getCount(); i++) { + OptionModel optionModel = OptionModel.create(optionsCursor); + if (optionModel.code().equals(optionCode) || optionModel.name().equals(optionCode)) { + teAttrValue = TrackedEntityAttributeValueModel.builder() + .trackedEntityInstance(teAttrValue.trackedEntityInstance()) + .lastUpdated(teAttrValue.lastUpdated()) + .created(teAttrValue.created()) + .trackedEntityAttribute(teAttrValue.trackedEntityAttribute()) + .value(optionModel.displayName()) + .build(); + } + optionsCursor.moveToNext(); + } + optionsCursor.close(); + } + } + return teAttrValue; + } + + public static String optionSetCodeToDisplayName(BriteDatabase briteDatabase, String optionSet, String optionSetCode) { + String displayName = optionSetCode; + Cursor optionsCursor = briteDatabase.query("SELECT * FROM Option WHERE optionSet = ? AND code = ? LIMIT 1", optionSet, optionSetCode); + if (optionsCursor != null && optionsCursor.moveToFirst()) { + OptionModel optionModel = OptionModel.create(optionsCursor); + displayName = optionModel.displayName(); + optionsCursor.close(); + } + return displayName; + } + + public static String orgUnitUidToDisplayName(BriteDatabase briteDatabase, String value) { + String displayName = value; + Cursor orgUnitCursor = briteDatabase.query("SELECT OrganisationUnit.displayName FROM OrganisationUnit WHERE OrganisationUnit.uid = ?", value); + if (orgUnitCursor != null && orgUnitCursor.moveToFirst()) { + displayName = orgUnitCursor.getString(0); + orgUnitCursor.close(); + } + return displayName; + } +} diff --git a/app/src/main/res/drawable/button_round_20.xml b/app/src/main/res/drawable/button_round_20.xml index d65f08685b..6802676e0c 100644 --- a/app/src/main/res/drawable/button_round_20.xml +++ b/app/src/main/res/drawable/button_round_20.xml @@ -5,8 +5,8 @@ + android:color="@color/colorGreyDefault" /> + android:color="@color/colorGreyDefault" /> \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_20_pressed.xml b/app/src/main/res/drawable/button_round_20_pressed.xml index a0453ea425..5b7df3680c 100644 --- a/app/src/main/res/drawable/button_round_20_pressed.xml +++ b/app/src/main/res/drawable/button_round_20_pressed.xml @@ -5,8 +5,8 @@ + android:color="@color/colorGreyDefault"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_7.xml b/app/src/main/res/drawable/button_round_7.xml index 78d2bba301..adefb6fe6c 100644 --- a/app/src/main/res/drawable/button_round_7.xml +++ b/app/src/main/res/drawable/button_round_7.xml @@ -4,8 +4,8 @@ + android:color="@color/colorGreyDefault"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/button_round_7_pressed.xml b/app/src/main/res/drawable/button_round_7_pressed.xml index 3e8adea4ee..d79c00f9b3 100644 --- a/app/src/main/res/drawable/button_round_7_pressed.xml +++ b/app/src/main/res/drawable/button_round_7_pressed.xml @@ -5,8 +5,8 @@ + android:color="@color/colorGreyDefault"/> \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_add_circle.xml b/app/src/main/res/drawable/ic_add_circle.xml index 0a3425b8fd..a4bbf9a74b 100644 --- a/app/src/main/res/drawable/ic_add_circle.xml +++ b/app/src/main/res/drawable/ic_add_circle.xml @@ -4,6 +4,6 @@ android:viewportWidth="24.0" android:viewportHeight="24.0"> diff --git a/app/src/main/res/drawable/ic_delete.xml b/app/src/main/res/drawable/ic_delete.xml index 8bed121aa6..fa54d9c8b3 100644 --- a/app/src/main/res/drawable/ic_delete.xml +++ b/app/src/main/res/drawable/ic_delete.xml @@ -1,5 +1,9 @@ - - + + diff --git a/app/src/main/res/drawable/photo_temp.xml b/app/src/main/res/drawable/photo_temp.xml index cc5a9a6081..5fed9ad64c 100644 --- a/app/src/main/res/drawable/photo_temp.xml +++ b/app/src/main/res/drawable/photo_temp.xml @@ -2,12 +2,9 @@ - - + + android:height="50dp" /> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_data_set_period.xml b/app/src/main/res/layout/activity_data_set_period.xml new file mode 100644 index 0000000000..ea963ee19b --- /dev/null +++ b/app/src/main/res/layout/activity_data_set_period.xml @@ -0,0 +1,229 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_dataset_detail.xml b/app/src/main/res/layout/activity_dataset_detail.xml new file mode 100644 index 0000000000..233b319021 --- /dev/null +++ b/app/src/main/res/layout/activity_dataset_detail.xml @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +