diff --git a/.gitignore b/.gitignore index 23ee352e..ff7f2830 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,6 @@ build #Windows *.db + +# Last commit file +app/src/main/res/raw/lastcommit.txt \ No newline at end of file diff --git a/api/build.gradle b/api/build.gradle index 14b5bbb3..4c76294e 100755 --- a/api/build.gradle +++ b/api/build.gradle @@ -1,5 +1,6 @@ apply plugin: 'com.android.library' apply plugin: 'com.neenbedankt.android-apt' +apply plugin: 'jacoco-android' android { compileSdkVersion 25 @@ -17,8 +18,20 @@ android { targetCompatibility JavaVersion.VERSION_1_7 } + packagingOptions { + exclude 'META-INF/LICENSE' + exclude 'META-INF/NOTICE' + } + lintOptions { - disable 'RtlSymmetry', 'RtlHardcoded' + disable 'RtlSymmetry', 'RtlHardcoded', 'RestrictedApi' + abortOnError false + } + + buildTypes { + debug { + testCoverageEnabled true + } } } @@ -47,4 +60,7 @@ dependencies { // Other compile 'joda-time:joda-time:2.9.2' + + // Java test dependencies + testCompile "junit:junit:4.12" } diff --git a/api/src/main/AndroidManifest.xml b/api/src/main/AndroidManifest.xml index 2dbf51c3..b04cd600 100755 --- a/api/src/main/AndroidManifest.xml +++ b/api/src/main/AndroidManifest.xml @@ -3,6 +3,7 @@ package="org.hisp.dhis.android.dashboard.api"> + diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/controllers/DashboardController.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/controllers/DashboardController.java index a27634b4..76819c47 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/controllers/DashboardController.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/controllers/DashboardController.java @@ -28,6 +28,13 @@ package org.hisp.dhis.android.dashboard.api.controllers; +import static org.hisp.dhis.android.dashboard.api.models.BaseIdentifiableObject.merge; +import static org.hisp.dhis.android.dashboard.api.models.BaseIdentifiableObject.toListIds; +import static org.hisp.dhis.android.dashboard.api.models.BaseIdentifiableObject.toMap; +import static org.hisp.dhis.android.dashboard.api.utils.NetworkUtils.findLocationHeader; +import static org.hisp.dhis.android.dashboard.api.utils.NetworkUtils.handleApiException; +import static org.hisp.dhis.android.dashboard.api.utils.NetworkUtils.unwrapResponse; + import android.net.Uri; import com.raizlabs.android.dbflow.sql.builder.Condition; @@ -61,13 +68,6 @@ import retrofit.client.Header; import retrofit.client.Response; -import static org.hisp.dhis.android.dashboard.api.models.BaseIdentifiableObject.merge; -import static org.hisp.dhis.android.dashboard.api.models.BaseIdentifiableObject.toListIds; -import static org.hisp.dhis.android.dashboard.api.models.BaseIdentifiableObject.toMap; -import static org.hisp.dhis.android.dashboard.api.utils.NetworkUtils.findLocationHeader; -import static org.hisp.dhis.android.dashboard.api.utils.NetworkUtils.handleApiException; -import static org.hisp.dhis.android.dashboard.api.utils.NetworkUtils.unwrapResponse; - final class DashboardController { final DhisApi mDhisApi; @@ -96,6 +96,11 @@ private static List queryDashboards() { .queryList(); } + public static List queryAllDashboardElement() { + return new Select().from(DashboardElement.class) + .queryList(); + } + private static List queryDashboardItems(Dashboard dashboard) { Where where = new Select().from(DashboardItem.class) .where(Condition.column(DashboardItem$Table @@ -164,7 +169,8 @@ private List updateDashboards(DateTime lastUpdated) throws APIExcepti "]"); if (lastUpdated != null) { - QUERY_MAP_FULL.put("filter", "lastUpdated:gt:" + lastUpdated.toString()); + QUERY_MAP_FULL.put("filter", + "lastUpdated:gt:" + lastUpdated.toLocalDateTime().toString()); } // List of dashboards with UUIDs (without content). This list is used @@ -183,8 +189,11 @@ private List updateDashboards(DateTime lastUpdated) throws APIExcepti continue; } + int i=0; for (DashboardItem item : dashboard.getDashboardItems()) { item.setDashboard(dashboard); + item.setOrderPosition(i); + i++; } } } @@ -213,7 +222,7 @@ private List updateDashboardItems(List dashboards, Dat QUERY_MAP_BASIC.put("fields", "id,created,lastUpdated,shape"); if (lastUpdated != null) { - QUERY_MAP_BASIC.put("filter", "lastUpdated:gt:" + lastUpdated.toString()); + QUERY_MAP_BASIC.put("filter", "lastUpdated:gt:" + lastUpdated.toLocalDateTime().toString()); } // List of actual dashboard items. @@ -628,6 +637,8 @@ private List updateApiResources(DateTime lastUpdated) thro DashboardItemContent.TYPE_REPORTS, lastUpdated)); dashboardItemContent.addAll(updateApiResourceByType( DashboardItemContent.TYPE_RESOURCES, lastUpdated)); + dashboardItemContent.addAll(updateApiResourceByType( + DashboardItemContent.TYPE_MESSAGES, lastUpdated)); return dashboardItemContent; } @@ -682,6 +693,8 @@ private List getApiResourceByType(String type, Map createOperations(List oldModels continue; } - if (newModel.getLastUpdated().isAfter(oldModel.getLastUpdated())) { + if (DateTime.parse(newModel.getLastUpdated()).isAfter(DateTime.parse(oldModel.getLastUpdated()))) { newModel.setId(oldModel.getId()); ops.add(DbOperation.update(newModel)); } @@ -573,6 +573,11 @@ private static List queryInterpretations() { .queryList(); } + public static List queryAllInterpretationElements() { + return new Select().from(InterpretationElement.class) + .queryList(); + } + private static List queryInterpretationUsers() { return new Select().from(User.class).queryList(); } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/controllers/PullImageController.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/controllers/PullImageController.java new file mode 100644 index 00000000..319c2ef8 --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/controllers/PullImageController.java @@ -0,0 +1,101 @@ +package org.hisp.dhis.android.dashboard.api.controllers; + +import android.content.Context; + +import com.squareup.picasso.MemoryPolicy; +import com.squareup.picasso.NetworkPolicy; + +import org.hisp.dhis.android.dashboard.api.models.DashboardElement; +import org.hisp.dhis.android.dashboard.api.models.DashboardItemContent; +import org.hisp.dhis.android.dashboard.api.models.Interpretation; +import org.hisp.dhis.android.dashboard.api.models.InterpretationElement; +import org.hisp.dhis.android.dashboard.api.network.APIException; +import org.hisp.dhis.android.dashboard.api.utils.PicassoProvider; + +import java.util.ArrayList; +import java.util.List; + + +final class PullImageController { + + static Context mContext; + + public PullImageController(Context context) { + mContext = context; + } + + public void pullDashboardImages(DhisController.ImageNetworkPolicy imageNetworkPolicy) throws APIException { + List requestList = new ArrayList<>(); + requestList = downloadDashboardImages(requestList); + downloadImages(imageNetworkPolicy, requestList, mContext); + } + + public void pullInterpretationImages(DhisController.ImageNetworkPolicy imageNetworkPolicy) throws APIException { + List requestList = new ArrayList<>(); + requestList = downloadInterpretationImages(requestList); + downloadImages(imageNetworkPolicy, requestList, mContext); + } + + public static List downloadInterpretationImages(List requestList) { + for (InterpretationElement interpretationElement : InterpretationController + .queryAllInterpretationElements()) { + if (interpretationElement == null || interpretationElement.getType() == null) { + continue; + } + if (Interpretation.TYPE_CHART.equals(interpretationElement.getType())) { + requestList.add( + DhisController.buildImageUrl("charts", interpretationElement.getUId(), + mContext)); + } else if (Interpretation.TYPE_MAP.equals(interpretationElement.getType())) { + requestList.add(DhisController.buildImageUrl("maps", interpretationElement.getUId(), + mContext)); + } + } + return requestList; + } + + public static List downloadDashboardImages(List requestList) { + for (DashboardElement element : DashboardController.queryAllDashboardElement()) { + if (element.getDashboardItem() == null + || element.getDashboardItem().getType() == null) { + continue; + } + + switch (element.getDashboardItem().getType()) { + case DashboardItemContent.TYPE_CHART: { + requestList.add( + DhisController.buildImageUrl("charts", element.getUId(), mContext)); + break; + } + case DashboardItemContent.TYPE_EVENT_CHART: { + requestList.add(DhisController.buildImageUrl("eventCharts", element.getUId(), + mContext)); + break; + } + case DashboardItemContent.TYPE_MAP: { + requestList.add( + DhisController.buildImageUrl("maps", element.getUId(), mContext)); + break; + } + } + } + return requestList; + } + + private static void downloadImages(DhisController.ImageNetworkPolicy imageNetworkPolicy, + final List requestUrlList, final Context context) { + + for (int i = 0; i < requestUrlList.size(); i++) { + final String request = requestUrlList.get(i); + + if (imageNetworkPolicy == DhisController.ImageNetworkPolicy.NO_CACHE) { + PicassoProvider.getInstance(context, false) + .load(request).networkPolicy(NetworkPolicy.NO_CACHE) + .memoryPolicy(MemoryPolicy.NO_CACHE).fetch(); + } else { + PicassoProvider.getInstance(context, false) + .load(request).fetch(); + } + } + } +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Access.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Access.java index bc2c182f..8fc263f9 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Access.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Access.java @@ -58,7 +58,7 @@ public final class Access { * * @return new Access object. */ - static Access provideDefaultAccess() { + public static Access provideDefaultAccess() { Access access = new Access(); access.setManage(true); access.setExternalize(true); diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/AttributeDimension.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/AttributeDimension.java new file mode 100644 index 00000000..0a9570cc --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/AttributeDimension.java @@ -0,0 +1,26 @@ +package org.hisp.dhis.android.dashboard.api.models; + +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import org.hisp.dhis.android.dashboard.api.models.meta.DbDhis; + +@Table(databaseName = DbDhis.NAME) +public class AttributeDimension extends BaseModel { + + @Column(name = "id") + @PrimaryKey(autoincrement = true) + long id; + + UIDObject attribute; + + public UIDObject getAttribute() { + return attribute; + } + + public void setAttribute(UIDObject attribute) { + this.attribute = attribute; + } +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/BaseIdentifiableObject.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/BaseIdentifiableObject.java index f2dcd5ef..be4bd076 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/BaseIdentifiableObject.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/BaseIdentifiableObject.java @@ -36,6 +36,7 @@ import org.hisp.dhis.android.dashboard.api.utils.StringUtils; import org.joda.time.DateTime; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; @@ -45,6 +46,10 @@ @JsonIgnoreProperties(ignoreUnknown = true) public class BaseIdentifiableObject extends BaseModel implements IdentifiableObject { + public static final String TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss"; + + public static final SimpleDateFormat LONG_DATE_FORMAT = new SimpleDateFormat(TIMESTAMP_PATTERN); + @JsonIgnore @Column(name = "id") @PrimaryKey(autoincrement = true) @@ -64,11 +69,11 @@ public class BaseIdentifiableObject extends BaseModel implements IdentifiableObj @JsonProperty("created") @Column(name = "created") - DateTime created; + String created; @JsonProperty("lastUpdated") @Column(name = "lastUpdated") - DateTime lastUpdated; + String lastUpdated; @JsonProperty("access") @Column(name = "access") @@ -115,22 +120,22 @@ public void setDisplayName(String displayName) { } @Override - public DateTime getCreated() { + public String getCreated() { return created; } @Override - public void setCreated(DateTime created) { + public void setCreated(String created) { this.created = created; } @Override - public DateTime getLastUpdated() { + public String getLastUpdated() { return lastUpdated; } @Override - public void setLastUpdated(DateTime lastUpdated) { + public void setLastUpdated(String lastUpdated) { this.lastUpdated = lastUpdated; } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Dashboard.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Dashboard.java index 28dc6679..5bb92432 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Dashboard.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Dashboard.java @@ -86,8 +86,8 @@ public static Dashboard createDashboard(String name) { dashboard.setState(State.TO_POST); dashboard.setName(name); dashboard.setDisplayName(name); - dashboard.setCreated(lastUpdatedDateTime); - dashboard.setLastUpdated(lastUpdatedDateTime); + dashboard.setCreated(LONG_DATE_FORMAT.format(lastUpdatedDateTime.toDate())); + dashboard.setLastUpdated(LONG_DATE_FORMAT.format(lastUpdatedDateTime.toDate())); dashboard.setAccess(Access.provideDefaultAccess()); return dashboard; @@ -231,7 +231,8 @@ private boolean isItemContentTypeEmbedded(DashboardItemContent content) { } case DashboardItemContent.TYPE_USERS: case DashboardItemContent.TYPE_REPORTS: - case DashboardItemContent.TYPE_RESOURCES: { + case DashboardItemContent.TYPE_RESOURCES: + case DashboardItemContent.TYPE_MESSAGES: { return false; } } @@ -274,4 +275,14 @@ public int getDashboardItemCount() { = queryRelatedDashboardItems(); return items == null ? 0 : items.size(); } + + @Override + public void setCreated(String created) { + this.created=created; + } + + @Override + public void setLastUpdated(String lastUpdated) { + this.lastUpdated = lastUpdated; + } } \ No newline at end of file diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/DashboardItem.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/DashboardItem.java index 8749f701..a1198960 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/DashboardItem.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/DashboardItem.java @@ -108,6 +108,10 @@ public final class DashboardItem extends BaseIdentifiableObject { @JsonProperty("messages") boolean messages; + @JsonIgnore + @Column + int orderPosition; + public DashboardItem() { state = State.SYNCED; shape = SHAPE_NORMAL; @@ -127,8 +131,8 @@ public static DashboardItem createDashboardItem(Dashboard dashboard, .getLastUpdated(ResourceType.DASHBOARDS); DashboardItem item = new DashboardItem(); - item.setCreated(lastUpdatedDateTime); - item.setLastUpdated(lastUpdatedDateTime); + item.setCreated(LONG_DATE_FORMAT.format(lastUpdatedDateTime.toDate())); + item.setLastUpdated(LONG_DATE_FORMAT.format(lastUpdatedDateTime.toDate())); item.setState(State.TO_POST); item.setDashboard(dashboard); item.setAccess(Access.provideDefaultAccess()); @@ -425,4 +429,12 @@ public State getState() { public void setState(State state) { this.state = state; } + + public int getOrderPosition() { + return orderPosition; + } + + public void setOrderPosition(int orderPosition) { + this.orderPosition = orderPosition; + } } \ No newline at end of file diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/DataElementDimension.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/DataElementDimension.java new file mode 100644 index 00000000..14da5b36 --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/DataElementDimension.java @@ -0,0 +1,54 @@ +package org.hisp.dhis.android.dashboard.api.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import org.hisp.dhis.android.dashboard.api.models.meta.DbDhis; + +@Table(databaseName = DbDhis.NAME) +public class DataElementDimension extends BaseModel { + @JsonIgnore + @Column + @PrimaryKey(autoincrement = true) + long id; + + String filter; + + UIDObject dataElement; + + UIDObject legendSet; + + public DataElementDimension() { + } + + public DataElementDimension(UIDObject dataElement) { + this.dataElement = dataElement; + } + + public UIDObject getDataElement() { + return dataElement; + } + + public void setDataElement(UIDObject dataElement) { + this.dataElement = dataElement; + } + + public String getFilter() { + return filter; + } + + public void setFilter(String filter) { + this.filter = filter; + } + + public UIDObject getLegendSet() { + return legendSet; + } + + public void setLegendSet(UIDObject legendSet) { + this.legendSet = legendSet; + } +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/EventReport.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/EventReport.java new file mode 100644 index 00000000..961042a3 --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/EventReport.java @@ -0,0 +1,194 @@ +package org.hisp.dhis.android.dashboard.api.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.raizlabs.android.dbflow.annotation.Table; + +import org.hisp.dhis.android.dashboard.api.models.meta.DbDhis; + +import java.util.List; + +@Table(databaseName = DbDhis.NAME) +public final class EventReport extends BaseIdentifiableObject { + @JsonIgnore + static final String AGGREGATED_VALUES_TYPE = "AGGREGATED_VALUES"; + @JsonIgnore + static final String EVENTS_TYPE = "EVENTS"; + @JsonIgnore + static final String OU_KEY = "ou"; + @JsonIgnore + static final String PE_KEY = "pe"; + @JsonIgnore + static final String AGGREGATE_KEY = "aggregate"; + @JsonIgnore + static final String QUERY_KEY = "query"; + + + UIDObject program; + UIDObject programStage; + List organisationUnits; + RelativePeriod relativePeriods; + List dataElementDimensions; + UIDObject dataElementValueDimension; + String aggregationType; + String outputType; + String dataType; + List attributeDimensions; + List filters; + List columns; + + public UIDObject getProgram() { + return program; + } + + public void setProgram(UIDObject program) { + this.program = program; + } + + public UIDObject getProgramStage() { + return programStage; + } + + public void setProgramStage(UIDObject programStage) { + this.programStage = programStage; + } + + public List getOrganisationUnits() { + return organisationUnits; + } + + public void setOrganisationUnits( + List organisationUnits) { + this.organisationUnits = organisationUnits; + } + + public RelativePeriod getRelativePeriods() { + return relativePeriods; + } + + public void setRelativePeriods(RelativePeriod relativePeriods) { + this.relativePeriods = relativePeriods; + } + + public List getDataElementDimensions() { + return dataElementDimensions; + } + + public void setDataElementDimensions( + List dataElementDimensions) { + this.dataElementDimensions = dataElementDimensions; + } + + public UIDObject getDataElementValueDimension() { + return dataElementValueDimension; + } + + public void setDataElementValueDimension( + UIDObject dataElementValueDimension) { + this.dataElementValueDimension = dataElementValueDimension; + } + + public String getAggregationType() { + return aggregationType; + } + + public void setAggregationType(String aggregationType) { + this.aggregationType = aggregationType; + } + + public String getOutputType() { + return outputType; + } + + public void setOutputType(String outputType) { + this.outputType = outputType; + } + + public String getDataType() { + return dataType; + } + + public void setDataType(String dataType) { + this.dataType = dataType; + } + + public List getAttributeDimensions() { + return attributeDimensions; + } + + public void setAttributeDimensions( + List attributeDimensions) { + this.attributeDimensions = attributeDimensions; + } + + public List getFilters() { + return filters; + } + + public void setFilters(List filters) { + this.filters = filters; + } + + public List getColumns() { + return columns; + } + + public void setColumns(List columns) { + this.columns = columns; + } + + public String getOUDimensionFilter() { + String ouDimensions = "ou:"; + boolean firstOu = true; + for (UIDObject organizationUnit : organisationUnits) { + ouDimensions += + firstOu ? organizationUnit.getuId() : ";" + organizationUnit.getuId(); + firstOu = false; + } + return ouDimensions; + } + + public String getDimensionFilter(DataElementDimension dimension) { + String dimensionUID = ""; + dimensionUID += dimension.getDataElement().getuId(); + if (dimension.getLegendSet() != null) { + dimensionUID += "-" + dimension.getLegendSet().getuId(); + } + if (dimension.getFilter() != null && !dimension.getFilter().isEmpty()) { + dimensionUID += ":" + dimension.getFilter(); + } + return dimensionUID; + } + + public boolean isOUInFilters() { + return isInFilters(OU_KEY); + } + + public boolean isPEInFilters() { + return isInFilters(PE_KEY); + } + + public boolean isInFilters(String key){ + for (UIDObject filter : filters) { + if (filter.getuId().equals(key)) { + return true; + } + } + return false; + } + + public String getDataTypeString() { + return (dataType.equals(AGGREGATED_VALUES_TYPE)) ? AGGREGATE_KEY : QUERY_KEY; + } + + public boolean isValidColumn(UIDObject column) { + if (column.getuId().equals(PE_KEY) || column.getuId().equals(OU_KEY)) { + return false; + } + for (DataElementDimension dimension : dataElementDimensions) { + if (dimension.getDataElement().getuId().equals(column.getuId())) { + return false; + } + } + return true; + } +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/IdentifiableObject.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/IdentifiableObject.java index e5a2986a..26e1fd2d 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/IdentifiableObject.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/IdentifiableObject.java @@ -51,13 +51,13 @@ public interface IdentifiableObject { void setDisplayName(String displayName); - DateTime getCreated(); + String getCreated(); - void setCreated(DateTime created); + void setCreated(String created); - DateTime getLastUpdated(); + String getLastUpdated(); - void setLastUpdated(DateTime lastUpdated); + void setLastUpdated(String lastUpdated); Access getAccess(); @@ -80,13 +80,21 @@ class CreatedComparator implements Comparator { @Override public int compare(IdentifiableObject first, IdentifiableObject second) { - if (first != null && first.getCreated() != null - && second != null && second.getCreated() != null) { - if (first.getCreated().isAfter(second.getCreated())) { + DateTime firstDate = null; + if(first!=null && first.getCreated()!=null) { + firstDate=DateTime.parse(first.getCreated()); + } + DateTime secondDate = null; + if(second!=null && second.getCreated()!=null) { + secondDate=DateTime.parse(second.getCreated()); + } + if (first != null && firstDate != null + && second != null && secondDate != null) { + if (firstDate.isAfter(secondDate)) { return 1; } - if (second.getCreated().isAfter(first.getCreated())) { + if (secondDate.isAfter(firstDate)) { return -1; } } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Interpretation.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Interpretation.java index 938d83b4..856d15c7 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Interpretation.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/Interpretation.java @@ -119,8 +119,8 @@ public static InterpretationComment addComment(Interpretation interpretation, Us .getLastUpdated(ResourceType.INTERPRETATIONS); InterpretationComment comment = new InterpretationComment(); - comment.setCreated(lastUpdated); - comment.setLastUpdated(lastUpdated); + comment.setCreated(LONG_DATE_FORMAT.format(lastUpdated.toDate())); + comment.setLastUpdated(LONG_DATE_FORMAT.format(lastUpdated.toDate())); comment.setAccess(Access.provideDefaultAccess()); comment.setText(text); comment.setState(State.TO_POST); @@ -146,8 +146,8 @@ public static Interpretation createInterpretation(DashboardItem item, User user, .getLastUpdated(ResourceType.INTERPRETATIONS); Interpretation interpretation = new Interpretation(); - interpretation.setCreated(lastUpdated); - interpretation.setLastUpdated(lastUpdated); + interpretation.setCreated(LONG_DATE_FORMAT.format(lastUpdated.toDate())); + interpretation.setLastUpdated(LONG_DATE_FORMAT.format(lastUpdated.toDate())); interpretation.setAccess(Access.provideDefaultAccess()); interpretation.setText(text); interpretation.setState(State.TO_POST); diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/RelativePeriod.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/RelativePeriod.java new file mode 100644 index 00000000..c329b883 --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/RelativePeriod.java @@ -0,0 +1,399 @@ +package org.hisp.dhis.android.dashboard.api.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import org.hisp.dhis.android.dashboard.api.models.meta.DbDhis; + +@Table(databaseName = DbDhis.NAME) +public final class RelativePeriod extends BaseModel { + + @JsonIgnore + private static final String[] periodsStrings = + {"THIS_YEAR", "QUARTERS_LAST_YEAR", "LAST_52_WEEKS", "THIS_WEEK", "LAST_MONTH", + "LAST_14_DAYS", "MONTHS_THIS_YEAR", "LAST_2_SIXMONTHS", "YESTERDAY", + "THIS_QUARTER", "LAST_12_MONTHS", "LAST_5_FINANCIAL_YEARS", "THIS_SIX_MONTH", + "LAST_QUARTER", "THIS_FINANCIAL_YEAR", "LAST_4_WEEKS", "LAST_3_MONTHS", + "THIS_DAY", "THIS_MONTH", "LAST_5_YEARS", "LAST_6_BIMONTHS", + "LAST_FINANCIAL_YEAR", "LAST_6_MONTHS", "LAST_3_DAYS", "QUARTERS_THIS_YEAR", + "MONTHS_LAST_YEAR", "LAST_WEEK", "LAST_7_DAYS", "THIS_BIMONTH", "LAST_BIMONTH", + "LAST_SIX_MONTH", "LAST_YEAR", "LAST_12_WEEKS", "LAST_4_QUARTERS", + "BIMONTHS_THIS_YEAR", "WEEKS_THIS_YEAR"}; + + @JsonIgnore + @Column(name = "id") + @PrimaryKey(autoincrement = true) + long id; + + boolean thisYear; + boolean quartersLastYear; + boolean last52Weeks; + boolean thisWeek; + boolean lastMonth; + boolean last14Days; + boolean monthsThisYear; + boolean last2SixMonths; + boolean yesterday; + boolean thisQuarter; + boolean last12Months; + boolean last5FinancialYears; + boolean thisSixMonth; + boolean lastQuarter; + boolean thisFinancialYear; + boolean last4Weeks; + boolean last3Months; + boolean thisDay; + boolean thisMonth; + boolean last5Years; + boolean last6BiMonths; + boolean lastFinancialYear; + boolean last6Months; + boolean last3Days; + boolean quartersThisYear; + boolean monthsLastYear; + boolean lastWeek; + boolean last7Days; + boolean thisBimonth; + boolean lastBimonth; + boolean lastSixMonth; + boolean lastYear; + boolean last12Weeks; + boolean last4Quarters; + boolean biMonthsThisYear; + boolean weeksThisYear; + + @JsonIgnore + private boolean[] periodsList; + + + public RelativePeriod() { + } + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public boolean[] getPeriodsList() { + return periodsList; + } + + public void setPeriodsList(boolean[] periodsList) { + this.periodsList = periodsList; + } + + public String getRelativePeriodString() { + String result = "pe:"; + periodsList = new boolean[]{thisYear, quartersLastYear, last52Weeks, thisWeek, lastMonth, + last14Days, monthsThisYear, last2SixMonths, yesterday, thisQuarter, last12Months, + last5FinancialYears, thisSixMonth, lastQuarter, thisFinancialYear, last4Weeks, + last3Months, thisDay, thisMonth, last5Years, last6BiMonths, lastFinancialYear, + last6Months, last3Days, quartersThisYear, monthsLastYear, lastWeek, last7Days, + thisBimonth, lastBimonth, lastSixMonth, lastYear, last12Weeks, last4Quarters, + biMonthsThisYear, weeksThisYear}; + for (int i = 0; i < periodsList.length; i++) { + if (periodsList[i]) { + if (i == 0) { + result += periodsStrings[i]; + } else { + result += ";" + periodsStrings[i]; + } + } + } + return result; + } + + public boolean isThisYear() { + return thisYear; + } + + public void setThisYear(boolean thisYear) { + this.thisYear = thisYear; + } + + public boolean isQuartersLastYear() { + return quartersLastYear; + } + + public void setQuartersLastYear(boolean quartersLastYear) { + this.quartersLastYear = quartersLastYear; + } + + public boolean isLast52Weeks() { + return last52Weeks; + } + + public void setLast52Weeks(boolean last52Weeks) { + this.last52Weeks = last52Weeks; + } + + public boolean isThisWeek() { + return thisWeek; + } + + public void setThisWeek(boolean thisWeek) { + this.thisWeek = thisWeek; + } + + public boolean isLastMonth() { + return lastMonth; + } + + public void setLastMonth(boolean lastMonth) { + this.lastMonth = lastMonth; + } + + public boolean isLast14Days() { + return last14Days; + } + + public void setLast14Days(boolean last14Days) { + this.last14Days = last14Days; + } + + public boolean isMonthsThisYear() { + return monthsThisYear; + } + + public void setMonthsThisYear(boolean monthsThisYear) { + this.monthsThisYear = monthsThisYear; + } + + public boolean isLast2SixMonths() { + return last2SixMonths; + } + + public void setLast2SixMonths(boolean last2SixMonths) { + this.last2SixMonths = last2SixMonths; + } + + public boolean isYesterday() { + return yesterday; + } + + public void setYesterday(boolean yesterday) { + this.yesterday = yesterday; + } + + public boolean isThisQuarter() { + return thisQuarter; + } + + public void setThisQuarter(boolean thisQuarter) { + this.thisQuarter = thisQuarter; + } + + public boolean isLast12Months() { + return last12Months; + } + + public void setLast12Months(boolean last12Months) { + this.last12Months = last12Months; + } + + public boolean isLast5FinancialYears() { + return last5FinancialYears; + } + + public void setLast5FinancialYears(boolean last5FinancialYears) { + this.last5FinancialYears = last5FinancialYears; + } + + public boolean isThisSixMonth() { + return thisSixMonth; + } + + public void setThisSixMonth(boolean thisSixMonth) { + this.thisSixMonth = thisSixMonth; + } + + public boolean isLastQuarter() { + return lastQuarter; + } + + public void setLastQuarter(boolean lastQuarter) { + this.lastQuarter = lastQuarter; + } + + public boolean isThisFinancialYear() { + return thisFinancialYear; + } + + public void setThisFinancialYear(boolean thisFinancialYear) { + this.thisFinancialYear = thisFinancialYear; + } + + public boolean isLast4Weeks() { + return last4Weeks; + } + + public void setLast4Weeks(boolean last4Weeks) { + this.last4Weeks = last4Weeks; + } + + public boolean isLast3Months() { + return last3Months; + } + + public void setLast3Months(boolean last3Months) { + this.last3Months = last3Months; + } + + public boolean isThisDay() { + return thisDay; + } + + public void setThisDay(boolean thisDay) { + this.thisDay = thisDay; + } + + public boolean isThisMonth() { + return thisMonth; + } + + public void setThisMonth(boolean thisMonth) { + this.thisMonth = thisMonth; + } + + public boolean isLast5Years() { + return last5Years; + } + + public void setLast5Years(boolean last5Years) { + this.last5Years = last5Years; + } + + public boolean isLast6BiMonths() { + return last6BiMonths; + } + + public void setLast6BiMonths(boolean last6BiMonths) { + this.last6BiMonths = last6BiMonths; + } + + public boolean isLastFinancialYear() { + return lastFinancialYear; + } + + public void setLastFinancialYear(boolean lastFinancialYear) { + this.lastFinancialYear = lastFinancialYear; + } + + public boolean isLast6Months() { + return last6Months; + } + + public void setLast6Months(boolean last6Months) { + this.last6Months = last6Months; + } + + public boolean isLast3Days() { + return last3Days; + } + + public void setLast3Days(boolean last3Days) { + this.last3Days = last3Days; + } + + public boolean isQuartersThisYear() { + return quartersThisYear; + } + + public void setQuartersThisYear(boolean quartersThisYear) { + this.quartersThisYear = quartersThisYear; + } + + public boolean isMonthsLastYear() { + return monthsLastYear; + } + + public void setMonthsLastYear(boolean monthsLastYear) { + this.monthsLastYear = monthsLastYear; + } + + public boolean isLastWeek() { + return lastWeek; + } + + public void setLastWeek(boolean lastWeek) { + this.lastWeek = lastWeek; + } + + public boolean isLast7Days() { + return last7Days; + } + + public void setLast7Days(boolean last7Days) { + this.last7Days = last7Days; + } + + public boolean isThisBimonth() { + return thisBimonth; + } + + public void setThisBimonth(boolean thisBimonth) { + this.thisBimonth = thisBimonth; + } + + public boolean isLastBimonth() { + return lastBimonth; + } + + public void setLastBimonth(boolean lastBimonth) { + this.lastBimonth = lastBimonth; + } + + public boolean isLastSixMonth() { + return lastSixMonth; + } + + public void setLastSixMonth(boolean lastSixMonth) { + this.lastSixMonth = lastSixMonth; + } + + public boolean isLastYear() { + return lastYear; + } + + public void setLastYear(boolean lastYear) { + this.lastYear = lastYear; + } + + public boolean isLast12Weeks() { + return last12Weeks; + } + + public void setLast12Weeks(boolean last12Weeks) { + this.last12Weeks = last12Weeks; + } + + public boolean isLast4Quarters() { + return last4Quarters; + } + + public void setLast4Quarters(boolean last4Quarters) { + this.last4Quarters = last4Quarters; + } + + public boolean isBiMonthsThisYear() { + return biMonthsThisYear; + } + + public void setBiMonthsThisYear(boolean biMonthsThisYear) { + this.biMonthsThisYear = biMonthsThisYear; + } + + public boolean isWeeksThisYear() { + return weeksThisYear; + } + + public void setWeeksThisYear(boolean weeksThisYear) { + this.weeksThisYear = weeksThisYear; + } +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/UIDObject.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/UIDObject.java new file mode 100644 index 00000000..d4cfc892 --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/UIDObject.java @@ -0,0 +1,46 @@ +package org.hisp.dhis.android.dashboard.api.models; + +import com.fasterxml.jackson.annotation.JsonIgnore; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.raizlabs.android.dbflow.annotation.Column; +import com.raizlabs.android.dbflow.annotation.PrimaryKey; +import com.raizlabs.android.dbflow.annotation.Table; +import com.raizlabs.android.dbflow.structure.BaseModel; + +import org.hisp.dhis.android.dashboard.api.models.meta.DbDhis; + +@Table(databaseName = DbDhis.NAME) +public class UIDObject extends BaseModel { + @JsonIgnore + @Column(name = "id") + @PrimaryKey(autoincrement = true) + long id; + + @JsonProperty("id") + @Column(name = "uId") + String uId; + + public UIDObject() { + } + + public UIDObject(String uId) { + this.uId = uId; + } + + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public String getuId() { + return uId; + } + + public void setuId(String uId) { + this.uId = uId; + } +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/UserAccount.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/UserAccount.java index 182185cf..e65db0e9 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/UserAccount.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/UserAccount.java @@ -68,11 +68,11 @@ public final class UserAccount extends BaseModel implements IdentifiableObject { @JsonProperty("created") @Column(name = "created") - DateTime created; + String created; @JsonProperty("lastUpdated") @Column(name = "lastUpdated") - DateTime lastUpdated; + String lastUpdated; @JsonProperty("access") @Column(name = "access") @@ -151,7 +151,7 @@ public static User toUser(UserAccount userAccount) { User user = new User(); user.setUId(userAccount.getUId()); user.setAccess(userAccount.getAccess()); - user.setCreated(user.getCreated()); + user.setCreated(userAccount.getCreated()); user.setLastUpdated(userAccount.getLastUpdated()); user.setName(userAccount.getName()); user.setDisplayName(userAccount.getDisplayName()); @@ -208,25 +208,25 @@ public void setDisplayName(String displayName) { @JsonIgnore @Override - public DateTime getCreated() { + public String getCreated() { return created; } @JsonIgnore @Override - public void setCreated(DateTime created) { + public void setCreated(String created) { this.created = created; } @JsonIgnore @Override - public DateTime getLastUpdated() { + public String getLastUpdated() { return lastUpdated; } @JsonIgnore @Override - public void setLastUpdated(DateTime lastUpdated) { + public void setLastUpdated(String lastUpdated) { this.lastUpdated = lastUpdated; } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/meta/DbDhis.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/meta/DbDhis.java index 0d8492e9..bfc838c6 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/meta/DbDhis.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/meta/DbDhis.java @@ -34,5 +34,5 @@ @Database(name = DbDhis.NAME, version = DbDhis.VERSION) public final class DbDhis { public static final String NAME = "dhis"; - public static final int VERSION = 3; + public static final int VERSION = 4; } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/meta/migrations/MigrationAddOrderPosToDashboardItem.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/meta/migrations/MigrationAddOrderPosToDashboardItem.java new file mode 100644 index 00000000..ba34e06c --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/models/meta/migrations/MigrationAddOrderPosToDashboardItem.java @@ -0,0 +1,21 @@ +package org.hisp.dhis.android.dashboard.api.models.meta.migrations; + +import com.raizlabs.android.dbflow.annotation.Migration; +import com.raizlabs.android.dbflow.sql.migration.AlterTableMigration; + +import org.hisp.dhis.android.dashboard.api.models.DashboardItem; +import org.hisp.dhis.android.dashboard.api.models.meta.DbDhis; + +@Migration(version = 4, databaseName = DbDhis.NAME) +public class MigrationAddOrderPosToDashboardItem extends AlterTableMigration { + + + public MigrationAddOrderPosToDashboardItem() { + super(DashboardItem.class); + } + + @Override + public void onPreMigrate() { + addColumn(Integer.class, "orderPosition"); + } +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/DhisApi.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/DhisApi.java index c7a77540..a5901953 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/DhisApi.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/DhisApi.java @@ -31,6 +31,7 @@ import org.hisp.dhis.android.dashboard.api.models.Dashboard; import org.hisp.dhis.android.dashboard.api.models.DashboardItem; import org.hisp.dhis.android.dashboard.api.models.DashboardItemContent; +import org.hisp.dhis.android.dashboard.api.models.EventReport; import org.hisp.dhis.android.dashboard.api.models.Interpretation; import org.hisp.dhis.android.dashboard.api.models.SystemInfo; import org.hisp.dhis.android.dashboard.api.models.UserAccount; @@ -141,6 +142,22 @@ Response deleteDashboardItemContent(@Path("dashboardUid") String dashboardUid, @GET("/reportTables/{id}/data.html") Response getReportTableData(@Path("id") String id); + @Headers("Accept: application/json") + @GET("/eventReports/{id}") + EventReport getEventReport(@Path("id") String id); + + @Headers("Accept: application/text") + @GET("/analytics/events/{dataType}/{program}" + + ".html+css?displayProperty=NAME") + Response getEventReportTableData(@Path("program") String program, + @Query("stage") String programStage, + @Query("dimension") List dimensions, + @Query("outputType") String outputType, + @Query("aggregationType") String aggregationType, + @Query("value") String value, + @Path("dataType") String dataType, + @Query("filter") List filter); + @GET("/eventReports?paging=false") @Headers("Accept: application/json") Map> getEventReports(@QueryMap Map queryParams); @@ -207,4 +224,5 @@ Response putInterpretationComment(@Path("interpretationUid") String interpretati @DELETE("/interpretations/{interpretationUid}/comments/{commentUid}") Response deleteInterpretationComment(@Path("interpretationUid") String interpretationUid, @Path("commentUid") String commentUid); + } \ No newline at end of file diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/RepoManager.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/RepoManager.java index 17cf63af..c06a851c 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/RepoManager.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/network/RepoManager.java @@ -28,6 +28,13 @@ package org.hisp.dhis.android.dashboard.api.network; +import static com.squareup.okhttp.Credentials.basic; + +import android.content.Context; +import android.net.ConnectivityManager; +import android.net.NetworkInfo; + +import com.squareup.okhttp.Cache; import com.squareup.okhttp.HttpUrl; import com.squareup.okhttp.Interceptor; import com.squareup.okhttp.OkHttpClient; @@ -38,19 +45,19 @@ import org.hisp.dhis.android.dashboard.api.models.meta.Credentials; import org.hisp.dhis.android.dashboard.api.utils.ObjectMapperProvider; +import java.io.File; import java.io.IOException; import java.net.HttpURLConnection; import java.util.concurrent.TimeUnit; import retrofit.ErrorHandler; +import retrofit.RequestInterceptor; import retrofit.RestAdapter; import retrofit.RetrofitError; import retrofit.client.OkClient; import retrofit.converter.Converter; import retrofit.converter.JacksonConverter; -import static com.squareup.okhttp.Credentials.basic; - public final class RepoManager { static final int DEFAULT_CONNECT_TIMEOUT_MILLIS = 15 * 1000; // 15s @@ -61,13 +68,15 @@ private RepoManager() { // no instances } - public static DhisApi createService(HttpUrl serverUrl, Credentials credentials) { + public static DhisApi createService(HttpUrl serverUrl, Credentials credentials, + final Context context) { RestAdapter restAdapter = new RestAdapter.Builder() .setEndpoint(provideServerUrl(serverUrl)) .setConverter(provideJacksonConverter()) - .setClient(provideOkClient(credentials)) + .setClient(provideOkClient(credentials, context)) .setErrorHandler(new RetrofitErrorHandler()) .setLogLevel(RestAdapter.LogLevel.BASIC) + .setRequestInterceptor(new ConnectionInterceptor(context)) .build(); return restAdapter.create(DhisApi.class); } @@ -82,19 +91,28 @@ private static Converter provideJacksonConverter() { return new JacksonConverter(ObjectMapperProvider.getInstance()); } - private static OkClient provideOkClient(Credentials credentials) { - return new OkClient(provideOkHttpClient(credentials)); + private static OkClient provideOkClient(Credentials credentials, Context context) { + return new OkClient(provideOkHttpClient(credentials, context)); } - public static OkHttpClient provideOkHttpClient(Credentials credentials) { + public static OkHttpClient provideOkHttpClient(Credentials credentials, Context context) { + OkHttpClient client = new OkHttpClient(); client.interceptors().add(provideInterceptor(credentials)); client.setConnectTimeout(DEFAULT_CONNECT_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setReadTimeout(DEFAULT_READ_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); client.setWriteTimeout(DEFAULT_WRITE_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); + client.setCache(provideCache(context)); return client; } + private static Cache provideCache(Context context) { + File httpCacheDirectory = new File(context.getCacheDir(), "responses"); + Cache cache = null; + cache = new Cache(httpCacheDirectory, 10 * 1024 * 1024); + return cache; + } + private static Interceptor provideInterceptor(Credentials credentials) { return new AuthInterceptor(credentials.getUsername(), credentials.getPassword()); } @@ -132,4 +150,33 @@ public Throwable handleError(RetrofitError cause) { return APIException.fromRetrofitError(cause); } } + + private static boolean isOnline(Context context) { + ConnectivityManager cm = + (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); + NetworkInfo netInfo = cm.getActiveNetworkInfo(); + return netInfo != null && netInfo.isConnectedOrConnecting(); + } + + private static class ConnectionInterceptor implements RequestInterceptor { + private Context mContext; + + public ConnectionInterceptor(Context context) { + mContext = context; + } + + @Override + public void intercept(RequestFacade request) { + request.addHeader("Accept", "application/json;versions=1"); + if (isOnline(mContext)) { + int maxAge = 0; // no read cache if there is internet + request.addHeader("Cache-Control", "public, max-age=" + maxAge); + } else { + int maxStale = 60 * 60 * 24 * 365; // tolerate 1 year state + request.addHeader("Cache-Control", + "public, only-if-cached, max-stale=" + maxStale); + } + } + } + } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/persistence/preferences/ResourceType.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/persistence/preferences/ResourceType.java index 943a094a..aefdc8d4 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/persistence/preferences/ResourceType.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/persistence/preferences/ResourceType.java @@ -4,5 +4,5 @@ * @author Araz Abishov . */ public enum ResourceType { - DASHBOARDS_CONTENT, DASHBOARDS, INTERPRETATIONS, USERS, + DASHBOARDS_CONTENT, DASHBOARDS, INTERPRETATIONS, USERS, INTERPRETATION_IMAGES, DASHBOARD_IMAGES } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/persistence/preferences/SettingsManager.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/persistence/preferences/SettingsManager.java new file mode 100644 index 00000000..55c78041 --- /dev/null +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/persistence/preferences/SettingsManager.java @@ -0,0 +1,38 @@ +package org.hisp.dhis.android.dashboard.api.persistence.preferences; + +import android.content.Context; +import android.content.SharedPreferences; + +public class SettingsManager { + public static final String CHART_WIDTH = "key:chart_width"; + public static final String CHART_HEIGHT = "key:chart_height"; + public static final String MINIMUM_WIDTH = "480"; + public static final String MINIMUM_HEIGHT = "320"; + private static final String PREFERENCES = "preferences:settings"; + public static final String MAXIMUM_WIDTH = "1920"; + public static final String MAXIMUM_HEIGHT = "1080"; + private static SettingsManager mSettingsManager = null; + private SharedPreferences mPrefs; + + public SettingsManager(Context context) { + mPrefs = context.getSharedPreferences(SettingsManager.PREFERENCES, + Context.MODE_PRIVATE); + } + + public static SettingsManager getInstance(Context context) { + if (mSettingsManager == null) { + mSettingsManager = new SettingsManager(context); + } + return mSettingsManager; + } + + public void setPreference(String key, String value) { + mPrefs.edit().putString(key, value).commit(); + } + + public String getPreference(String key, String defaultValue) { + return mPrefs.getString(key, defaultValue); + } + + +} diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/DbUtils.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/DbUtils.java index 6d830bd2..6235b397 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/DbUtils.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/DbUtils.java @@ -94,14 +94,11 @@ public static List createOperati continue; } - // if the last updated field in up to date model is after the same - // field in persisted model, it means we need to update it. - if (newModel.getLastUpdated().isAfter(oldModel.getLastUpdated())) { + //always updating to save changes not from the server // note, we need to pass database primary id to updated model // in order to avoid creation of new object. newModel.setId(oldModel.getId()); ops.add(DbOperation.update(newModel)); - } // as we have processed given old (persisted) model, // we can remove it from map of new models. diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/NetworkUtils.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/NetworkUtils.java index 0316a38a..a1802d71 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/NetworkUtils.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/NetworkUtils.java @@ -67,6 +67,7 @@ public static Header findLocationHeader(List
headers) { } public static void handleApiException(APIException apiException) throws APIException { + apiException.printStackTrace(); handleApiException(apiException, null); } diff --git a/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/PicassoProvider.java b/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/PicassoProvider.java index 03fb8567..f5d868a3 100755 --- a/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/PicassoProvider.java +++ b/api/src/main/java/org/hisp/dhis/android/dashboard/api/utils/PicassoProvider.java @@ -44,14 +44,15 @@ public final class PicassoProvider { private PicassoProvider() { } - public static Picasso getInstance(Context context) { - if (mPicasso == null) { + public static Picasso getInstance(Context context, boolean changeCredentials) { + if (mPicasso == null || changeCredentials) { OkHttpClient client = RepoManager.provideOkHttpClient( - DhisController.getInstance().getUserCredentials()); - + DhisController.getInstance().getUserCredentials(), context); mPicasso = new Picasso.Builder(context) .downloader(new OkHttpDownloader(client)) .build(); + mPicasso.setIndicatorsEnabled(false); + mPicasso.setLoggingEnabled(false); } return mPicasso; diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/PreconditionsTests.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/PreconditionsTests.java new file mode 100644 index 00000000..deaf35cc --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/PreconditionsTests.java @@ -0,0 +1,18 @@ +package org.hisp.dhis.android.dashboard.api; + +import org.hisp.dhis.android.dashboard.api.utils.Preconditions; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class PreconditionsTests { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void precondition_null_exception() { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("test_message"); + Preconditions.isNull(null, "test_message"); + } +} diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/DateTestUtils.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/DateTestUtils.java new file mode 100644 index 00000000..d69740ca --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/DateTestUtils.java @@ -0,0 +1,27 @@ +package org.hisp.dhis.android.dashboard.api.commons; + +import org.joda.time.DateTime; + +import java.text.ParseException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.TimeZone; + +public class DateTestUtils { + public final static String DHIS2_GMT_NEW_DATE_FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSS"; + + private static Date parseDate(String date, String format) { + try { + SimpleDateFormat sdf = new SimpleDateFormat(format); + sdf.setTimeZone(TimeZone.getTimeZone("UTC")); + return sdf.parse(date); + } catch (ParseException e) { + e.printStackTrace(); + return null; + } + } + + public static boolean compareParsedDateWithStringDate(DateTime date, String isDate) { + return date.toDate().getTime() == (parseDate(isDate, DHIS2_GMT_NEW_DATE_FORMAT).getTime()); + } +} diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/FileReader.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/FileReader.java new file mode 100644 index 00000000..90dc5bcc --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/FileReader.java @@ -0,0 +1,28 @@ +package org.hisp.dhis.android.dashboard.api.commons; + +import java.io.BufferedReader; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStreamReader; +import java.net.URL; + +public class FileReader { + private File getFile(String filename) { + ClassLoader classLoader = getClass().getClassLoader(); + URL resource = classLoader.getResource(filename); + return new File(resource.getPath()); + } + + public String getStringFromFile(String filename) throws IOException { + FileInputStream inputStream = new FileInputStream(getFile(filename)); + InputStreamReader isr = new InputStreamReader(inputStream); + BufferedReader bufferedReader = new BufferedReader(isr); + StringBuilder sb = new StringBuilder(); + String line; + while ((line = bufferedReader.readLine()) != null) { + sb.append(line); + } + return sb.toString(); + } +} diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/JsonParser.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/JsonParser.java new file mode 100644 index 00000000..3512c207 --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/commons/JsonParser.java @@ -0,0 +1,15 @@ +package org.hisp.dhis.android.dashboard.api.commons; + +import org.hisp.dhis.android.dashboard.api.utils.ObjectMapperProvider; + +import java.io.IOException; + +public class JsonParser { + + + public static Object getModelFromJson(Class modelClass, String json) throws IOException { + return ObjectMapperProvider.getInstance() + .readValue(json, modelClass); + } + +} \ No newline at end of file diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/AccessConverterTests.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/AccessConverterTests.java new file mode 100644 index 00000000..b64e79d4 --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/AccessConverterTests.java @@ -0,0 +1,48 @@ +package org.hisp.dhis.android.dashboard.api.converters; + +import static junit.framework.Assert.assertTrue; + +import org.hisp.dhis.android.dashboard.api.commons.FileReader; +import org.hisp.dhis.android.dashboard.api.models.Access; +import org.hisp.dhis.android.dashboard.api.persistence.converters.AccessConverter; +import org.junit.Test; + +import java.io.IOException; + +public class AccessConverterTests { + public static final String ACCESS_ALL_FALSE_STRING_TXT = "access_all_false_string.json"; + public static final String ACCESS_ALL_TRUE_STRING_TXT = "access_all_true_string.json"; + AccessConverter accessConverter = new AccessConverter(); + + @Test + public void convert_access_object_to_database_string() throws Exception { + String access = getAccessFromJson(ACCESS_ALL_TRUE_STRING_TXT); + assertTrue(accessConverter.getDBValue(Access.provideDefaultAccess()).equals(access)); + } + + @Test + public void convert_access_all_true_database_string_to_model() throws IOException { + Access access = accessConverter.getModelValue( + getAccessFromJson(ACCESS_ALL_TRUE_STRING_TXT)); + assertTrue(access.isDelete()); + assertTrue(access.isRead()); + assertTrue(access.isWrite()); + assertTrue(access.isManage()); + assertTrue(access.isExternalize()); + } + + @Test + public void convert_access_all_false_database_string_to_model() throws IOException { + Access access = accessConverter.getModelValue( + getAccessFromJson(ACCESS_ALL_FALSE_STRING_TXT)); + assertTrue(!access.isDelete()); + assertTrue(!access.isRead()); + assertTrue(!access.isWrite()); + assertTrue(!access.isManage()); + assertTrue(!access.isExternalize()); + } + + private String getAccessFromJson(String json) throws IOException { + return new FileReader().getStringFromFile(json).replaceAll(" ", "").replace("\t", ""); + } +} diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/DateTimeConverterTests.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/DateTimeConverterTests.java new file mode 100644 index 00000000..4820d504 --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/DateTimeConverterTests.java @@ -0,0 +1,46 @@ +package org.hisp.dhis.android.dashboard.api.converters; + +import static junit.framework.Assert.assertTrue; + +import org.hisp.dhis.android.dashboard.api.commons.DateTestUtils; +import org.hisp.dhis.android.dashboard.api.persistence.converters.DateTimeConverter; +import org.joda.time.DateTime; +import org.junit.Test; + +import java.util.Calendar; +import java.util.TimeZone; + +public class DateTimeConverterTests { + + public static final String DATETIME_AS_STRING = "2016-04-21T15:37:07.740Z"; + + DateTimeConverter dateTimeConverter = new DateTimeConverter(); + + @Test + public void convert_datetime_string_to_object() throws Exception { + DateTime convertedDate = dateTimeConverter.getModelValue(DATETIME_AS_STRING); + + assertTrue(DateTestUtils.compareParsedDateWithStringDate(convertedDate, DATETIME_AS_STRING)); + } + + @Test + public void convert_datetime_object_to_string() throws Exception { + String converterDate = dateTimeConverter.getDBValue(createStubDateTime()); + + assertTrue(DateTestUtils.compareParsedDateWithStringDate(createStubDateTime(), converterDate)); + } + + + private DateTime createStubDateTime(){ + Calendar calendar = Calendar.getInstance(); + calendar.setTimeZone(TimeZone.getTimeZone("UTC")); + calendar.set(Calendar.YEAR, 2016); + calendar.set(Calendar.MONTH, 04); + calendar.set(Calendar.DAY_OF_MONTH, 21); + calendar.set(Calendar.HOUR_OF_DAY, 15); + calendar.set(Calendar.MINUTE, 37); + calendar.set(Calendar.SECOND, 07); + calendar.set(Calendar.MILLISECOND, 740); + return new DateTime(calendar); + } +} diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/StateConverterTests.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/StateConverterTests.java new file mode 100644 index 00000000..fbe2fa59 --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/converters/StateConverterTests.java @@ -0,0 +1,56 @@ +package org.hisp.dhis.android.dashboard.api.converters; + +import static junit.framework.Assert.assertTrue; + +import org.hisp.dhis.android.dashboard.api.models.meta.State; +import org.hisp.dhis.android.dashboard.api.persistence.converters.StateConverter; +import org.junit.Test; + +public class StateConverterTests { + public static final String STATE_SYNCED = "SYNCED"; + public static final String STATE_TO_POST = "TO_POST"; + public static final String STATE_TO_UPDATE = "TO_UPDATE"; + public static final String STATE_TO_DELETE = "TO_DELETE"; + + StateConverter stateConverter = new StateConverter(); + + @Test + public void convert_state_to_post_string_to_object() throws Exception { + assertTrue(stateConverter.getModelValue(STATE_TO_POST).equals(State.TO_POST)); + } + + @Test + public void convert_state_to_post_object_to_string() throws Exception { + assertTrue(stateConverter.getDBValue(State.TO_POST).equals(STATE_TO_POST)); + } + + @Test + public void convert_state_to_delete_string_to_object() throws Exception { + assertTrue(stateConverter.getModelValue(STATE_TO_DELETE).equals(State.TO_DELETE)); + } + + @Test + public void convert_state_to_delete_object_to_string() throws Exception { + assertTrue(stateConverter.getDBValue(State.TO_DELETE).equals(STATE_TO_DELETE)); + } + + @Test + public void convert_state_to_update_string_to_object() throws Exception { + assertTrue(stateConverter.getModelValue(STATE_TO_UPDATE).equals(State.TO_UPDATE)); + } + + @Test + public void convert_state_to_update_object_to_string() throws Exception { + assertTrue(stateConverter.getDBValue(State.TO_UPDATE).equals(STATE_TO_UPDATE)); + } + + @Test + public void convert_state_synced_string_to_object() throws Exception { + assertTrue(stateConverter.getModelValue(STATE_SYNCED).equals(State.SYNCED)); + } + + @Test + public void convert_state_synced_object_to_string() throws Exception { + assertTrue(stateConverter.getDBValue(State.SYNCED).equals(STATE_SYNCED)); + } +} diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/meta/CredentialsTests.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/meta/CredentialsTests.java new file mode 100644 index 00000000..f35115eb --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/meta/CredentialsTests.java @@ -0,0 +1,27 @@ +package org.hisp.dhis.android.dashboard.api.meta; + +import org.hisp.dhis.android.dashboard.api.models.meta.Credentials; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +public class CredentialsTests { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void throw_exception_if_username_is_not_provided() throws Exception { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Username must not be null"); + + new Credentials(null, "pwd"); + } + + @Test + public void throw_exception_if_password_is_not_provided() throws Exception { + thrown.expect(IllegalArgumentException.class); + thrown.expectMessage("Password must not be null"); + + new Credentials("user", null); + } +} diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/network/ApiExceptionTests.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/network/ApiExceptionTests.java new file mode 100644 index 00000000..7451f712 --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/network/ApiExceptionTests.java @@ -0,0 +1,74 @@ +package org.hisp.dhis.android.dashboard.api.network; + +import static org.junit.Assert.assertTrue; + +import org.junit.Test; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import retrofit.RetrofitError; +import retrofit.client.Header; +import retrofit.client.Response; +import retrofit.converter.ConversionException; +import retrofit.converter.Converter; + +public class ApiExceptionTests { + + Response response; + Converter converter; + java.lang.reflect.Type type; + + @Test + public void retrofit_network_exception_map_to_api_exception() { + APIException apiException = getNetworkExceptionFromRetrofit(); + assertTrue(apiException.getKind().equals(APIException.Kind.NETWORK)); + assertTrue(apiException.getUrl().equals("test_message")); + } + + @Test + public void retrofit_conversion_exception_map_to_api_exception() { + APIException apiException = getConversionExceptionFromRetrofit(); + assertTrue(apiException.getKind().equals(APIException.Kind.CONVERSION)); + assertTrue(apiException.getUrl().equals("test_message")); + } + + @Test + public void retrofit_unexpected_exception_map_to_api_exception() { + APIException apiException = getUnexpectedExceptionFromRetrofit(); + assertTrue(apiException.getKind().equals(APIException.Kind.UNEXPECTED)); + assertTrue(apiException.getUrl().equals("test_message")); + } + + @Test + public void retrofit_http_error_exception_map_to_api_exception() { + APIException apiException = getHttpErrorExceptionFromRetrofit(); + assertTrue(apiException.getKind().equals(APIException.Kind.HTTP)); + assertTrue(apiException.getUrl().equals("test_message")); + } + + + private APIException getNetworkExceptionFromRetrofit() { + return APIException.fromRetrofitError( + RetrofitError.networkError("test_message", new IOException())); + } + + private APIException getConversionExceptionFromRetrofit() { + return APIException.fromRetrofitError( + RetrofitError.conversionError("test_message", response, converter, + type, new ConversionException("test_message"))); + } + + private APIException getUnexpectedExceptionFromRetrofit() { + return APIException.fromRetrofitError( + RetrofitError.unexpectedError("test_message", new IOException())); + } + + private APIException getHttpErrorExceptionFromRetrofit() { + List
headerList = new ArrayList<>(); + Response response = new Response("test_url", 404, "Not found", headerList, null); + return APIException.fromRetrofitError( + RetrofitError.httpError("test_message", response, converter, type)); + } +} \ No newline at end of file diff --git a/api/src/test/java/org/hisp/dhis/android/dashboard/api/user/UserTests.java b/api/src/test/java/org/hisp/dhis/android/dashboard/api/user/UserTests.java new file mode 100644 index 00000000..a4ff8ad8 --- /dev/null +++ b/api/src/test/java/org/hisp/dhis/android/dashboard/api/user/UserTests.java @@ -0,0 +1,39 @@ +package org.hisp.dhis.android.dashboard.api.user; + +import static org.junit.Assert.assertTrue; + +import org.hisp.dhis.android.dashboard.api.commons.DateTestUtils; +import org.hisp.dhis.android.dashboard.api.commons.FileReader; +import org.hisp.dhis.android.dashboard.api.commons.JsonParser; +import org.hisp.dhis.android.dashboard.api.models.User; +import org.hisp.dhis.android.dashboard.api.models.UserAccount; +import org.joda.time.DateTime; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; + +import java.io.IOException; + +public class UserTests { + @Rule + public ExpectedException thrown = ExpectedException.none(); + + @Test + public void userAccount_conversion_to_user() throws IOException { + UserAccount userAccount = getUserAccountFromJson(); + User user = UserAccount.toUser(userAccount); + assertTrue(user.getUId().equals("xE7jOejl9FI")); + assertTrue(user.getName().equals("John Traore")); + assertTrue(user.getDisplayName().equals("John Traore")); + assertTrue(DateTestUtils.compareParsedDateWithStringDate(DateTime.parse(user.getCreated()), + "2013-04-18T17:15:08.407")); + assertTrue(DateTestUtils.compareParsedDateWithStringDate(DateTime.parse(user.getLastUpdated()), + "2017-05-02T17:02:37.817")); + assertTrue(user.getAccess() == null); + } + + private UserAccount getUserAccountFromJson() throws IOException { + return (UserAccount) JsonParser.getModelFromJson(UserAccount.class, new FileReader().getStringFromFile( + "userAccount.json")); + } +} \ No newline at end of file diff --git a/api/src/test/resources/access_all_false_string.json b/api/src/test/resources/access_all_false_string.json new file mode 100644 index 00000000..1cd2bc71 --- /dev/null +++ b/api/src/test/resources/access_all_false_string.json @@ -0,0 +1,8 @@ +{ + "manage": false, + "externalize": false, + "write": false, + "read": false, + "update": false, + "delete": false +} \ No newline at end of file diff --git a/api/src/test/resources/access_all_true_string.json b/api/src/test/resources/access_all_true_string.json new file mode 100644 index 00000000..75d73647 --- /dev/null +++ b/api/src/test/resources/access_all_true_string.json @@ -0,0 +1,8 @@ +{ + "manage": true, + "externalize": true, + "write": true, + "read": true, + "update": true, + "delete": true +} \ No newline at end of file diff --git a/api/src/test/resources/userAccount.json b/api/src/test/resources/userAccount.json new file mode 100644 index 00000000..0fbfc01e --- /dev/null +++ b/api/src/test/resources/userAccount.json @@ -0,0 +1,21 @@ +{ + "created": "2013-04-18T17:15:08.407", + "lastUpdated": "2017-05-02T17:02:37.817", + "name": "John Traore", + "id": "xE7jOejl9FI", + "birthday": "1971-04-08T00:00:00.000", + "education": "Master of super using", + "gender": "gender_male", + "languages": "English", + "displayName": "John Traore", + "jobTitle": "Super user", + "firstName": "John", + "surname": "Traore", + "employer": "DHIS", + "interests": "Football, swimming, singing, dancing", + "introduction": "I am the super user of DHIS 2", + "email": "someone@dhis2.org", + "organisationUnits": [{ + "id": "ImspTQPwCqd" + }] +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 59656a3e..982c2a6d 100755 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,4 +1,5 @@ apply plugin: 'com.android.application' +apply plugin: 'jacoco-android' android { compileSdkVersion 25 @@ -8,8 +9,8 @@ android { applicationId "org.hisp.dhis.android.dashboard" minSdkVersion 15 targetSdkVersion 25 - versionCode 5 - versionName "0.6.5" + versionCode 7 + versionName "0.6.7" } compileOptions { @@ -23,7 +24,14 @@ android { } lintOptions { - disable 'RtlSymmetry', 'RtlHardcoded', 'ContentDescription' + disable 'RtlSymmetry', 'RtlHardcoded', 'ContentDescription', 'RestrictedApi' + abortOnError false + } + + buildTypes { + debug { + testCoverageEnabled true + } } } @@ -43,4 +51,7 @@ dependencies { // Other compile 'com.jakewharton:butterknife:7.0.1' compile 'com.github.chrisbanes.photoview:library:1.2.4' + + // Java test dependencies + testCompile "junit:junit:4.10" } diff --git a/app/src/main/ic_event_report-web.png b/app/src/main/ic_event_report-web.png new file mode 100644 index 00000000..243af003 Binary files /dev/null and b/app/src/main/ic_event_report-web.png differ diff --git a/app/src/main/ic_pivot_table-web.png b/app/src/main/ic_pivot_table-web.png new file mode 100644 index 00000000..42ecc34f Binary files /dev/null and b/app/src/main/ic_pivot_table-web.png differ diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/DhisService.java b/app/src/main/java/org/hisp/dhis/android/dashboard/DhisService.java index 60e6bb55..5ebf5125 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/DhisService.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/DhisService.java @@ -27,6 +27,7 @@ package org.hisp.dhis.android.dashboard; import android.app.Service; +import android.content.Context; import android.content.Intent; import android.os.Binder; import android.os.IBinder; @@ -54,6 +55,8 @@ public final class DhisService extends Service { public static final int SYNC_DASHBOARDS = 5; public static final int SYNC_DASHBOARD_CONTENT = 6; public static final int SYNC_INTERPRETATIONS = 7; + public static final int PULL_INTERPRETATION_IMAGES = 8; + public static final int PULL_DASHBOARD_IMAGES = 9; private final IBinder mBinder = new ServiceBinder(); private DhisController mDhisController; @@ -165,6 +168,28 @@ public Object execute() throws APIException { }); } + public void pullInterpretationImages(final DhisController.ImageNetworkPolicy imageNetworkPolicy, final Context context) { + JobExecutor.enqueueJob(new NetworkJob(PULL_INTERPRETATION_IMAGES, + ResourceType.INTERPRETATION_IMAGES) { + @Override + public Object execute() throws APIException { + mDhisController.pullInterpretationImages(imageNetworkPolicy,context); + return new Object(); + } + }); + } + + public void pullDashboardImages(final DhisController.ImageNetworkPolicy imageNetworkPolicy, final Context context) { + JobExecutor.enqueueJob(new NetworkJob(PULL_DASHBOARD_IMAGES, + ResourceType.DASHBOARD_IMAGES) { + @Override + public Object execute() throws APIException { + mDhisController.pullDashboardImages(imageNetworkPolicy,context); + return new Object(); + } + }); + } + public boolean isJobRunning(int jobId) { return JobExecutor.isJobRunning(jobId); } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/DashboardElementDetailActivity.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/DashboardElementDetailActivity.java index 8ad0094c..5f909347 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/DashboardElementDetailActivity.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/DashboardElementDetailActivity.java @@ -29,6 +29,7 @@ package org.hisp.dhis.android.dashboard.ui.activities; import android.app.Activity; +import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.support.v4.app.Fragment; @@ -72,13 +73,6 @@ public static Intent newIntentForInterpretationElement(Activity activity, long i return intent; } - private static String buildImageUrl(String resource, String id) { - return DhisController.getInstance().getServerUrl().newBuilder() - .addPathSegment("api").addPathSegment(resource).addPathSegment(id).addPathSegment("data.png") - .addQueryParameter("width", "480").addQueryParameter("height", "320") - .toString(); - } - private long getDashboardElementId() { return getIntent().getLongExtra(DASHBOARD_ELEMENT_ID, -1); } @@ -114,7 +108,7 @@ protected void onPostCreate(Bundle savedInstanceState) { .where(Condition.column(DashboardElement$Table.ID) .is(getDashboardElementId())) .querySingle(); - handleDashboardElement(element); + handleDashboardElement(element, getApplicationContext()); } if (interpretationElementId > 0) { @@ -123,11 +117,11 @@ protected void onPostCreate(Bundle savedInstanceState) { .where(Condition.column(InterpretationElement$Table .ID).is(interpretationElementId)) .querySingle(); - handleInterpretationElement(element); + handleInterpretationElement(element, getApplicationContext()); } } - private void handleDashboardElement(DashboardElement element) { + private void handleDashboardElement(DashboardElement element, Context context) { if (element == null || element.getDashboardItem() == null) { return; @@ -136,29 +130,31 @@ private void handleDashboardElement(DashboardElement element) { mToolbar.setTitle(element.getDisplayName()); switch (element.getDashboardItem().getType()) { case DashboardItemContent.TYPE_CHART: { - String request = buildImageUrl("charts", element.getUId()); + String request = DhisController.getInstance().buildImageUrl("charts", element.getUId(), context); attachFragment(ImageViewFragment.newInstance(request)); break; } case DashboardItemContent.TYPE_EVENT_CHART: { - String request = buildImageUrl("eventCharts", element.getUId()); + String request = DhisController.getInstance().buildImageUrl("eventCharts", element.getUId(), context); attachFragment(ImageViewFragment.newInstance(request)); break; } case DashboardItemContent.TYPE_MAP: { - String request = buildImageUrl("maps", element.getUId()); + String request = DhisController.getInstance().buildImageUrl("maps", element.getUId(), context); attachFragment(ImageViewFragment.newInstance(request)); break; } - case DashboardItemContent.TYPE_REPORT_TABLE: { + case DashboardItemContent.TYPE_REPORT_TABLE: + case DashboardItemContent.TYPE_EVENT_REPORT: { String elementId = element.getUId(); - attachFragment(WebViewFragment.newInstance(elementId)); + attachFragment(WebViewFragment.newInstance(elementId, + element.getDashboardItem().getType())); break; } } } - private void handleInterpretationElement(InterpretationElement element) { + private void handleInterpretationElement(InterpretationElement element, Context context) { if (element == null || element.getInterpretation() == null) { return; } @@ -166,18 +162,18 @@ private void handleInterpretationElement(InterpretationElement element) { mToolbar.setTitle(element.getDisplayName()); switch (element.getInterpretation().getType()) { case Interpretation.TYPE_CHART: { - String request = buildImageUrl("charts", element.getUId()); + String request = DhisController.getInstance().buildImageUrl("charts", element.getUId(), context); attachFragment(ImageViewFragment.newInstance(request)); break; } case Interpretation.TYPE_MAP: { - String request = buildImageUrl("maps", element.getUId()); + String request = DhisController.getInstance().buildImageUrl("maps", element.getUId(), context); attachFragment(ImageViewFragment.newInstance(request)); break; } case Interpretation.TYPE_REPORT_TABLE: { String elementId = element.getUId(); - attachFragment(WebViewFragment.newInstance(elementId)); + attachFragment(WebViewFragment.newInstance(elementId, element.getType())); break; } case Interpretation.TYPE_DATA_SET_REPORT: { diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/LoginActivity.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/LoginActivity.java index 69e5c6df..fa570087 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/LoginActivity.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/LoginActivity.java @@ -28,6 +28,8 @@ package org.hisp.dhis.android.dashboard.ui.activities; +import static org.hisp.dhis.android.dashboard.utils.TextUtils.isEmpty; + import android.content.Intent; import android.os.Bundle; import android.support.annotation.Nullable; @@ -46,6 +48,7 @@ import org.hisp.dhis.android.dashboard.api.models.meta.Credentials; import org.hisp.dhis.android.dashboard.api.models.meta.ResponseHolder; import org.hisp.dhis.android.dashboard.api.persistence.preferences.ResourceType; +import org.hisp.dhis.android.dashboard.api.utils.PicassoProvider; import butterknife.Bind; import butterknife.ButterKnife; @@ -53,8 +56,6 @@ import butterknife.OnTextChanged; import fr.castorflex.android.circularprogressbar.CircularProgressBar; -import static org.hisp.dhis.android.dashboard.utils.TextUtils.isEmpty; - public class LoginActivity extends BaseActivity { private static final String IS_LOADING = "state:isLoading"; @@ -136,6 +137,7 @@ public void onResultReceived(NetworkJob.NetworkJobResult jobResult) ResponseHolder responseHolder = jobResult.getResponseHolder(); if (responseHolder.getApiException() == null) { + PicassoProvider.getInstance(this, true); startActivity(new Intent(this, MenuActivity.class)); finish(); } else { diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/MenuActivity.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/MenuActivity.java index e1d56975..c9c477b4 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/MenuActivity.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/activities/MenuActivity.java @@ -45,6 +45,7 @@ import android.view.View; import android.widget.TextView; +import org.hisp.dhis.android.dashboard.ui.fragments.AboutUsFragment; import org.hisp.dhis.android.dashboard.R; import org.hisp.dhis.android.dashboard.api.models.UserAccount; import org.hisp.dhis.android.dashboard.api.persistence.loaders.DbLoader; @@ -131,6 +132,10 @@ public boolean onNavigationItemSelected(MenuItem menuItem) { attachFragmentDelayed(new SettingsFragment()); break; } + case R.id.menu_about_app: { + attachFragmentDelayed(new AboutUsFragment()); + break; + } /* case R.id.menu_about_item: { getDhisController().invalidateSession(); startActivity(new Intent(this, LauncherActivity.class)); diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardAdapter.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardAdapter.java index 05a322c8..4c20f103 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardAdapter.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardAdapter.java @@ -28,11 +28,14 @@ package org.hisp.dhis.android.dashboard.ui.adapters; +import static org.hisp.dhis.android.dashboard.ui.fragments.dashboard.DashboardFragment.newInstance; + import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentPagerAdapter; import org.hisp.dhis.android.dashboard.api.models.Dashboard; +import org.hisp.dhis.android.dashboard.ui.fragments.SyncingController; import org.hisp.dhis.android.dashboard.ui.fragments.dashboard.DashboardFragment; import java.util.List; @@ -40,16 +43,22 @@ public class DashboardAdapter extends FragmentPagerAdapter { private static final String EMPTY_TITLE = ""; private List mDashboards; + private SyncingController syncingController; - public DashboardAdapter(FragmentManager fm) { + public DashboardAdapter(FragmentManager fm,SyncingController syncingController) { super(fm); + this.syncingController = syncingController; } @Override public Fragment getItem(int position) { if (mDashboards != null && mDashboards.size() > 0) { - return DashboardFragment + DashboardFragment dashboardFragment = DashboardFragment .newInstance(getDashboard(position)); + + dashboardFragment.setSyncingController(syncingController); + + return dashboardFragment; } else { return null; } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardItemAdapter.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardItemAdapter.java index d1c47311..4d0b3d66 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardItemAdapter.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/adapters/DashboardItemAdapter.java @@ -39,6 +39,8 @@ import android.widget.LinearLayout; import android.widget.TextView; +import com.squareup.picasso.MemoryPolicy; +import com.squareup.picasso.NetworkPolicy; import com.squareup.picasso.Picasso; import org.hisp.dhis.android.dashboard.R; @@ -90,6 +92,7 @@ public class DashboardItemAdapter extends AbsAdapter. */ @@ -77,7 +79,7 @@ public CommentViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { @Override public void onBindViewHolder(CommentViewHolder holder, int position) { InterpretationComment comment = getItem(position); - DateTime lastUpdated = comment.getLastUpdated(); + DateTime lastUpdated = DateTime.parse(comment.getLastUpdated()); User user = comment.getUser(); String name = EMPTY_FIELD; diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/AboutUsFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/AboutUsFragment.java new file mode 100644 index 00000000..46832f16 --- /dev/null +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/AboutUsFragment.java @@ -0,0 +1,115 @@ +package org.hisp.dhis.android.dashboard.ui.fragments; + +import android.content.Context; +import android.os.Bundle; +import android.support.annotation.Nullable; +import android.support.v7.widget.Toolbar; +import android.text.Html; +import android.text.SpannableString; +import android.text.Spanned; +import android.text.util.Linkify; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +import org.hisp.dhis.android.dashboard.BuildConfig; +import org.hisp.dhis.android.dashboard.R; +import org.hisp.dhis.android.dashboard.ui.views.FontTextView; + +import java.io.BufferedReader; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; + +import butterknife.Bind; +import butterknife.ButterKnife; + +public class AboutUsFragment extends BaseFragment { + + @Bind(R.id.toolbar) + Toolbar mToolbar; + + public static String getAppVersion() { + return String.valueOf(BuildConfig.VERSION_NAME); + } + + public static String getCommitHash(Context context) { + String stringCommit; + //Check if lastcommit.txt file exist, and if not exist show as unavailable. + int layoutId = context.getResources().getIdentifier("lastcommit", "raw", + context.getPackageName()); + if (layoutId == 0) { + stringCommit = context.getString(R.string.unavailable); + } else { + InputStream commit = context.getResources().openRawResource(layoutId); + stringCommit = convertFromInputStreamToString(commit).toString(); + } + return stringCommit; + } + + private SpannableString getDescriptionMessage(Context context) { + InputStream message = context.getResources().openRawResource(R.raw.description); + String stringMessage = convertFromInputStreamToString(message).toString(); + final SpannableString linkedMessage = new SpannableString(Html.fromHtml(stringMessage)); + Linkify.addLinks(linkedMessage, Linkify.EMAIL_ADDRESSES | Linkify.WEB_URLS); + return linkedMessage; + } + + private static StringBuilder convertFromInputStreamToString(InputStream inputStream) { + StringBuilder stringBuilder = new StringBuilder(); + + try { + BufferedReader r = new BufferedReader(new InputStreamReader(inputStream, "UTF-8")); + String line; + while ((line = r.readLine()) != null) { + stringBuilder.append(line + "\n"); + } + } catch (IOException e) { + e.printStackTrace(); + } + + return stringBuilder; + } + + public static Spanned getCommitMessage(Context context) { + String stringCommit = getCommitHash(context); + + if (stringCommit.contains(context.getString(R.string.unavailable))) { + stringCommit = String.format(context.getString(R.string.last_commit), stringCommit); + stringCommit = stringCommit + " " + context.getText(R.string.lastcommit_unavailable); + } else { + stringCommit = String.format(context.getString(R.string.last_commit), stringCommit); + } + + return Html.fromHtml(stringCommit); + } + + public static String getVersionMessage(Context context) { + String version = getAppVersion(); + return String.format(context.getString(R.string.app_version), version); + } + + @Nullable + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_about_us, container, false); + } + + @Override + public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { + ButterKnife.bind(this, view); + + ((FontTextView) view.findViewById(R.id.app_version)).setText(getVersionMessage(getContext())); + ((FontTextView) view.findViewById(R.id.commit_hash)).setText(getCommitMessage(getContext())); + ((FontTextView) view.findViewById(R.id.description)).setText(getDescriptionMessage(getContext())); + mToolbar.setNavigationIcon(R.mipmap.ic_menu); + mToolbar.setTitle(R.string.about_this_app); + mToolbar.setNavigationOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View v) { + toggleNavigationDrawer(); + } + }); + } +} diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/AccountFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/AccountFragment.java index 784b9476..0fae6a12 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/AccountFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/AccountFragment.java @@ -88,7 +88,7 @@ public void onClick(View v) { }); mAdapter = new AccountFieldAdapter(getActivity().getApplicationContext(), - getLayoutInflater(savedInstanceState)); + (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)); LinearLayoutManager linearLayoutManager = new LinearLayoutManager(getActivity()); diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/BaseFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/BaseFragment.java index 9f6c823a..d0ca83bf 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/BaseFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/BaseFragment.java @@ -28,10 +28,17 @@ package org.hisp.dhis.android.dashboard.ui.fragments; +import static android.R.attr.editable; + import android.app.Activity; +import android.content.Context; import android.support.v4.app.Fragment; +import android.view.View; +import android.widget.ProgressBar; +import android.widget.Toast; import org.hisp.dhis.android.dashboard.DhisService; +import org.hisp.dhis.android.dashboard.R; import org.hisp.dhis.android.dashboard.api.utils.EventBusProvider; import org.hisp.dhis.android.dashboard.ui.activities.BaseActivity; import org.hisp.dhis.android.dashboard.ui.activities.INavigationCallback; diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/ImageViewFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/ImageViewFragment.java index 68d54850..12888664 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/ImageViewFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/ImageViewFragment.java @@ -28,6 +28,8 @@ package org.hisp.dhis.android.dashboard.ui.fragments; +import static org.hisp.dhis.android.dashboard.api.utils.Preconditions.isNull; + import android.os.Bundle; import android.support.annotation.Nullable; import android.view.LayoutInflater; @@ -35,13 +37,14 @@ import android.view.ViewGroup; import android.widget.ImageView; +import com.squareup.picasso.MemoryPolicy; +import com.squareup.picasso.NetworkPolicy; + import org.hisp.dhis.android.dashboard.R; import org.hisp.dhis.android.dashboard.api.utils.PicassoProvider; import uk.co.senab.photoview.PhotoViewAttacher; -import static org.hisp.dhis.android.dashboard.api.utils.Preconditions.isNull; - public class ImageViewFragment extends BaseFragment { private static final String IMAGE_URL = "arg:imageUrl"; @@ -60,7 +63,8 @@ public static ImageViewFragment newInstance(String imageUrl) { } private String getImageUrl() { - return getArguments().getString(IMAGE_URL); + String imageSimplePath = getArguments().getString(IMAGE_URL); + return imageSimplePath; } @Nullable @Override @@ -75,8 +79,10 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { mAttacher = new PhotoViewAttacher(mImageView); mAttacher.update(); - PicassoProvider.getInstance(getActivity().getApplicationContext()) + PicassoProvider.getInstance(getActivity().getApplicationContext(), false) .load(getImageUrl()) + .networkPolicy(NetworkPolicy.NO_STORE, NetworkPolicy.OFFLINE) + .memoryPolicy(MemoryPolicy.NO_STORE) .placeholder(R.mipmap.ic_stub_dashboard_item) .into(mImageView); } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/SettingsFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/SettingsFragment.java index 24ef3431..c9748dec 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/SettingsFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/SettingsFragment.java @@ -4,15 +4,20 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.widget.Toolbar; +import android.text.Editable; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.widget.EditText; import com.squareup.otto.Subscribe; import org.hisp.dhis.android.dashboard.R; +import org.hisp.dhis.android.dashboard.api.persistence.preferences.SettingsManager; import org.hisp.dhis.android.dashboard.ui.activities.LauncherActivity; import org.hisp.dhis.android.dashboard.ui.events.UiEvent; +import org.hisp.dhis.android.dashboard.ui.views.FontEditText; import butterknife.Bind; import butterknife.ButterKnife; @@ -22,14 +27,22 @@ * Created by arazabishov on 7/27/15. */ public final class SettingsFragment extends BaseFragment { - @Bind(R.id.toolbar) Toolbar mToolbar; + FontEditText widthEditText; + FontEditText heightEditText; + public static final String MINIMUM_WIDTH = "480"; + public static final String MINIMUM_HEIGHT = "320"; @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { - return inflater.inflate(R.layout.fragment_settings, container, false); + View view = inflater.inflate(R.layout.fragment_settings, container, false); + widthEditText = (FontEditText) view.findViewById(R.id.update_width_edit); + heightEditText =(FontEditText) view.findViewById(R.id.update_height_edit); + widthEditText.addTextChangedListener(new CustomTextWatcher(SettingsManager.CHART_WIDTH, SettingsManager.MINIMUM_WIDTH, SettingsManager.MAXIMUM_WIDTH, widthEditText)); + heightEditText.addTextChangedListener(new CustomTextWatcher(SettingsManager.CHART_HEIGHT, SettingsManager.MINIMUM_HEIGHT, SettingsManager.MAXIMUM_HEIGHT, heightEditText)); + return view; } @Override @@ -44,6 +57,11 @@ public void onClick(View v) { toggleNavigationDrawer(); } }); + String width = SettingsManager.getInstance(getContext()).getPreference((SettingsManager.CHART_WIDTH), SettingsManager.MINIMUM_WIDTH); + String height = SettingsManager.getInstance(getContext()).getPreference((SettingsManager.CHART_HEIGHT), SettingsManager.MINIMUM_HEIGHT); + widthEditText.setText(width); + heightEditText.setText(height); + } @OnClick(R.id.delete_and_log_out_button) @@ -62,4 +80,40 @@ public void onLogOut(UiEvent event) { getActivity().finish(); } } + + private class CustomTextWatcher implements TextWatcher{ + + final String preference; + final int minimumValue; + final int maximumValue; + final EditText mEditText; + public CustomTextWatcher(String preference, String minimumValue, String maximumValue, EditText editText){ + this.preference = preference; + this.minimumValue = Integer.parseInt(minimumValue); + this.maximumValue = Integer.parseInt(maximumValue); + this.mEditText = editText; + } + @Override + public void afterTextChanged(Editable s) {} + + @Override + public void beforeTextChanged(CharSequence s, int start, + int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, + int before, int count) { + if(s.toString().length()>0) { + if (Integer.parseInt(s.toString())>= minimumValue && Integer.parseInt(s.toString()) <= maximumValue) { + mEditText.setError(null); + SettingsManager.getInstance(getContext()).setPreference(preference, + s.toString()); + }else{ + mEditText.setError(String.format(getContext().getString(R.string.invalid_value), minimumValue, maximumValue)); + } + } + } + } + } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/SyncingController.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/SyncingController.java new file mode 100644 index 00000000..652ad183 --- /dev/null +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/SyncingController.java @@ -0,0 +1,5 @@ +package org.hisp.dhis.android.dashboard.ui.fragments; + +public interface SyncingController{ + boolean isSyncing(); +} \ No newline at end of file diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/WebViewFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/WebViewFragment.java index 52e7f30f..6d52d864 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/WebViewFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/WebViewFragment.java @@ -28,6 +28,11 @@ package org.hisp.dhis.android.dashboard.ui.fragments; +import static android.text.TextUtils.isEmpty; + +import static org.hisp.dhis.android.dashboard.api.models.DashboardItemContent.TYPE_REPORT_TABLE; + +import android.content.Context; import android.os.Bundle; import android.view.LayoutInflater; import android.view.View; @@ -39,6 +44,10 @@ import org.hisp.dhis.android.dashboard.api.controllers.DhisController; import org.hisp.dhis.android.dashboard.api.job.Job; import org.hisp.dhis.android.dashboard.api.job.JobExecutor; +import org.hisp.dhis.android.dashboard.api.models.AttributeDimension; +import org.hisp.dhis.android.dashboard.api.models.DataElementDimension; +import org.hisp.dhis.android.dashboard.api.models.EventReport; +import org.hisp.dhis.android.dashboard.api.models.UIDObject; import org.hisp.dhis.android.dashboard.api.models.meta.ResponseHolder; import org.hisp.dhis.android.dashboard.api.network.APIException; import org.hisp.dhis.android.dashboard.api.network.DhisApi; @@ -48,15 +57,17 @@ import java.io.IOException; import java.io.InputStreamReader; import java.lang.ref.WeakReference; +import java.util.ArrayList; +import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; import retrofit.mime.TypedInput; -import static android.text.TextUtils.isEmpty; - public class WebViewFragment extends BaseFragment { private static final String DASHBOARD_ELEMENT_ID = "arg:dashboardElementId"; + private Context mContext; + private static final String DASHBOARD_TYPE = "dashboardType"; @Bind(R.id.web_view_content) WebView mWebView; @@ -64,9 +75,10 @@ public class WebViewFragment extends BaseFragment { @Bind(R.id.container_layout_progress_bar) View mProgressBarContainer; - public static WebViewFragment newInstance(String id) { + public static WebViewFragment newInstance(String id, String dashboardType) { Bundle args = new Bundle(); args.putString(DASHBOARD_ELEMENT_ID, id); + args.putString(DASHBOARD_TYPE, dashboardType); WebViewFragment fragment = new WebViewFragment(); fragment.setArguments(args); @@ -74,7 +86,9 @@ public static WebViewFragment newInstance(String id) { return fragment; } - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { + mContext = getContext(); return inflater.inflate(R.layout.fragment_web_view, container, false); } @@ -84,9 +98,16 @@ public void onViewCreated(View view, Bundle savedInstanceState) { mWebView.getSettings().setBuiltInZoomControls(true); if (getArguments() != null && !isEmpty(getArguments() - .getString(DASHBOARD_ELEMENT_ID))) { - JobExecutor.enqueueJob(new GetReportTableJob(this, getArguments() - .getString(DASHBOARD_ELEMENT_ID))); + .getString(DASHBOARD_ELEMENT_ID)) && !isEmpty(getArguments() + .getString(DASHBOARD_TYPE))) { + if (getArguments() + .getString(DASHBOARD_TYPE).equals(TYPE_REPORT_TABLE)) { + JobExecutor.enqueueJob(new GetReportTableJob(this, getArguments() + .getString(DASHBOARD_ELEMENT_ID), mContext)); + } else { + JobExecutor.enqueueJob(new GetEventReportTableJob(this, getArguments() + .getString(DASHBOARD_ELEMENT_ID), mContext)); + } } } @@ -108,12 +129,15 @@ static class GetReportTableJob extends Job> { final WeakReference mFragmentRef; final String mDashboardElementId; + Context mContext; - public GetReportTableJob(WebViewFragment fragment, String dashboardElementId) { + public GetReportTableJob(WebViewFragment fragment, String dashboardElementId, + Context context) { super(JOB_ID); mFragmentRef = new WeakReference<>(fragment); mDashboardElementId = dashboardElementId; + mContext = context; } static String readInputStream(TypedInput in) { @@ -142,9 +166,12 @@ public ResponseHolder inBackground() { ResponseHolder responseHolder = new ResponseHolder<>(); try { - DhisApi dhisApi = RepoManager.createService(DhisController.getInstance().getServerUrl(), - DhisController.getInstance().getUserCredentials()); - responseHolder.setItem(readInputStream(dhisApi.getReportTableData(mDashboardElementId).getBody())); + DhisApi dhisApi = RepoManager.createService(DhisController.getInstance() + .getServerUrl(), + DhisController.getInstance().getUserCredentials(), + mContext); + responseHolder.setItem( + readInputStream(dhisApi.getReportTableData(mDashboardElementId).getBody())); } catch (APIException exception) { responseHolder.setApiException(exception); } @@ -159,4 +186,88 @@ public void onFinish(ResponseHolder result) { } } } + + static class GetEventReportTableJob extends GetReportTableJob { + + Context mContext; + + public GetEventReportTableJob(WebViewFragment fragment, String dashboardElementId, + Context context) { + super(fragment, dashboardElementId, context); + + mContext = context; + } + + @Override + public ResponseHolder inBackground() { + ResponseHolder responseHolder = new ResponseHolder<>(); + EventReport eventReport; + + try { + DhisApi dhisApi = RepoManager.createService( + DhisController.getInstance().getServerUrl(), + DhisController.getInstance().getUserCredentials(), mContext); + eventReport = dhisApi.getEventReport(mDashboardElementId); + responseHolder.setItem(readInputStream( + dhisApi.getEventReportTableData(eventReport.getProgram().getuId(), + eventReport.getProgramStage().getuId(), + getDimensions(eventReport), + eventReport.getOutputType(), + eventReport.getAggregationType(), + eventReport.getDataElementValueDimension() != null + ? eventReport.getDataElementValueDimension().getuId() + : null, + eventReport.getDataTypeString(), + getFilters(eventReport)) + .getBody())); + } catch (APIException exception) { + responseHolder.setApiException(exception); + } + + return responseHolder; + } + + private List getDimensions(EventReport eventReport) { + List dimensions = new ArrayList<>(); + if (!eventReport.isPEInFilters()) { + dimensions.add(eventReport.getRelativePeriods().getRelativePeriodString()); + } + if (!eventReport.isOUInFilters()) { + dimensions.add(eventReport.getOUDimensionFilter()); + } + for (DataElementDimension dimension : eventReport.getDataElementDimensions()) { + if (!eventReport.isInFilters(dimension.getDataElement().getuId())) { + dimensions.add(eventReport.getDimensionFilter(dimension)); + } + } + for (UIDObject column : eventReport.getColumns()) { + if (eventReport.isValidColumn(column)) { + dimensions.add(column.getuId()); + } + } + + for (AttributeDimension attributeDimension : eventReport.getAttributeDimensions()) { + dimensions.add(attributeDimension.getAttribute().getuId()); + } + return dimensions; + } + + private List getFilters(EventReport eventReport) { + List filters = new ArrayList<>(); + if (eventReport.isOUInFilters()) { + filters.add(eventReport.getOUDimensionFilter()); + } + if (eventReport.isPEInFilters()) { + filters.add(eventReport.getRelativePeriods().getRelativePeriodString()); + } + + for (DataElementDimension dimension : eventReport.getDataElementDimensions()) { + if (eventReport.isInFilters(dimension.getDataElement().getuId())) { + filters.add(eventReport.getDimensionFilter(dimension)); + } + } + + return filters; + } + } } \ No newline at end of file diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardEmptyFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardEmptyFragment.java index d12a13a2..cd58687f 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardEmptyFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardEmptyFragment.java @@ -29,15 +29,18 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import com.squareup.otto.Subscribe; import org.hisp.dhis.android.dashboard.DhisService; import org.hisp.dhis.android.dashboard.R; +import org.hisp.dhis.android.dashboard.api.controllers.DhisController; import org.hisp.dhis.android.dashboard.api.job.NetworkJob; import org.hisp.dhis.android.dashboard.api.network.SessionManager; import org.hisp.dhis.android.dashboard.api.persistence.preferences.ResourceType; @@ -63,6 +66,9 @@ public class DashboardEmptyFragment extends BaseFragment implements View.OnClick @Bind(R.id.progress_bar) SmoothProgressBar mProgressBar; + private DhisController.ImageNetworkPolicy mImageNetworkPolicy = + DhisController.ImageNetworkPolicy.CACHE; + @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -117,8 +123,16 @@ public void onClick(View v) { } public boolean onMenuItemClicked(MenuItem item) { + if (mProgressBar.getVisibility() == View.VISIBLE) { + Toast.makeText(getContext(), + getString(R.string.action_not_allowed_during_sync), + Toast.LENGTH_SHORT).show(); + return false; + } + switch (item.getItemId()) { case R.id.refresh: { + mImageNetworkPolicy = DhisController.ImageNetworkPolicy.NO_CACHE; syncDashboards(); return true; } @@ -133,7 +147,7 @@ public boolean onMenuItemClicked(MenuItem item) { private void syncDashboards() { if (isDhisServiceBound()) { - getDhisService().syncDashboardsAndContent(); + getDhisService().syncDashboards(); mProgressBar.setVisibility(View.VISIBLE); } } @@ -141,8 +155,21 @@ private void syncDashboards() { @Subscribe @SuppressWarnings("unused") public void onResponseReceived(NetworkJob.NetworkJobResult result) { + Log.d(TAG, "Received" + result.getResourceType()); if (result.getResourceType() == ResourceType.DASHBOARDS) { + getDhisService().syncDashboardContents(); + } + if (result.getResourceType() == ResourceType.DASHBOARDS_CONTENT) { + getDhisService().pullDashboardImages(mImageNetworkPolicy,getContext()); + } + if (result.getResourceType() == ResourceType.INTERPRETATIONS) { + getDhisService().pullInterpretationImages(mImageNetworkPolicy,getContext()); + } + if (result.getResourceType() == ResourceType.DASHBOARD_IMAGES) { mProgressBar.setVisibility(View.INVISIBLE); + getDhisService().syncInterpretations(); } } + + } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardFragment.java index 680dec39..bc935552 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardFragment.java @@ -36,6 +36,7 @@ import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -44,8 +45,10 @@ import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; +import com.squareup.otto.Subscribe; import org.hisp.dhis.android.dashboard.R; +import org.hisp.dhis.android.dashboard.api.job.NetworkJob; import org.hisp.dhis.android.dashboard.api.models.Access; import org.hisp.dhis.android.dashboard.api.models.Dashboard; import org.hisp.dhis.android.dashboard.api.models.DashboardElement; @@ -55,11 +58,13 @@ import org.hisp.dhis.android.dashboard.api.models.meta.State; import org.hisp.dhis.android.dashboard.api.persistence.loaders.DbLoader; import org.hisp.dhis.android.dashboard.api.persistence.loaders.Query; +import org.hisp.dhis.android.dashboard.api.persistence.preferences.ResourceType; import org.hisp.dhis.android.dashboard.api.utils.EventBusProvider; import org.hisp.dhis.android.dashboard.ui.activities.DashboardElementDetailActivity; import org.hisp.dhis.android.dashboard.ui.adapters.DashboardItemAdapter; import org.hisp.dhis.android.dashboard.ui.events.UiEvent; import org.hisp.dhis.android.dashboard.ui.fragments.BaseFragment; +import org.hisp.dhis.android.dashboard.ui.fragments.SyncingController; import org.hisp.dhis.android.dashboard.ui.fragments.interpretation.InterpretationCreateFragment; import org.hisp.dhis.android.dashboard.ui.views.GridDividerDecoration; @@ -76,6 +81,7 @@ public class DashboardFragment extends BaseFragment private static final String WRITE = "arg:write"; private static final String MANAGE = "arg:manage"; private static final String EXTERNALIZE = "arg:externalize"; + public static final String TAG = DashboardFragment.class.getSimpleName(); ViewSwitcher mViewSwitcher; @@ -83,6 +89,8 @@ public class DashboardFragment extends BaseFragment DashboardItemAdapter mAdapter; + private SyncingController syncingController; + public static DashboardFragment newInstance(Dashboard dashboard) { DashboardFragment fragment = new DashboardFragment(); Access access = dashboard.getAccess(); @@ -100,6 +108,10 @@ public static DashboardFragment newInstance(Dashboard dashboard) { return fragment; } + public void setSyncingController(SyncingController syncingController){ + this.syncingController = syncingController; + } + private static Access getAccessFromBundle(Bundle args) { Access access = new Access(); @@ -120,6 +132,8 @@ public View onCreateView(LayoutInflater inflater, ViewGroup group, Bundle bundle @Override public void onViewCreated(View view, Bundle savedInstanceState) { + + mViewSwitcher = (ViewSwitcher) view; mRecyclerView = (RecyclerView) view.findViewById(R.id.recycler_view); @@ -168,6 +182,14 @@ public Loader> onCreateLoader(int id, Bundle args) { return null; } + @Subscribe + @SuppressWarnings("unused") + public void onResponseReceived(NetworkJob.NetworkJobResult result) { + Log.d(TAG, "Received " + result.getResourceType()); + if (result.getResourceType().equals(ResourceType.DASHBOARD_IMAGES)) { + mAdapter.updateImages(); + } + } @Override public void onLoadFinished(Loader> loader, List dashboardItems) { @@ -199,7 +221,8 @@ public void onContentClick(DashboardElement element) { case DashboardItemContent.TYPE_CHART: case DashboardItemContent.TYPE_EVENT_CHART: case DashboardItemContent.TYPE_MAP: - case DashboardItemContent.TYPE_REPORT_TABLE: { + case DashboardItemContent.TYPE_REPORT_TABLE: + case DashboardItemContent.TYPE_EVENT_REPORT: { Intent intent = DashboardElementDetailActivity .newIntentForDashboardElement(getActivity(), element.getId()); startActivity(intent); @@ -216,6 +239,7 @@ public void onContentClick(DashboardElement element) { @Override public void onContentDeleteClick(DashboardElement element) { + if (element != null) { element.deleteDashboardElement(); @@ -228,7 +252,12 @@ public void onContentDeleteClick(DashboardElement element) { @Override public void onItemDeleteClick(DashboardItem item) { - if (item != null) { + + if (syncingController != null && syncingController.isSyncing()){ + Toast.makeText(getContext(), + getString(R.string.action_not_allowed_during_sync), + Toast.LENGTH_SHORT).show(); + }else if (item != null) { item.deleteDashboardItem(); if (isDhisServiceBound()) { @@ -267,7 +296,9 @@ public List query(Context context) { DashboardItemContent.TYPE_EVENT_REPORT, DashboardItemContent.TYPE_USERS, DashboardItemContent.TYPE_REPORTS, - DashboardItemContent.TYPE_RESOURCES)) + DashboardItemContent.TYPE_RESOURCES, + DashboardItemContent.TYPE_MESSAGES) + ).orderBy(DashboardItem$Table.ORDERPOSITION) .queryList(); if (dashboardItems != null && !dashboardItems.isEmpty()) { for (DashboardItem dashboardItem : dashboardItems) { diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardItemAddFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardItemAddFragment.java index d97d9bc5..227ec4dd 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardItemAddFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardItemAddFragment.java @@ -36,6 +36,7 @@ import android.support.v4.content.Loader; import android.support.v7.widget.PopupMenu; import android.text.Editable; +import android.view.Gravity; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -141,7 +142,7 @@ public void onViewCreated(View view, Bundle savedInstanceState) { mListView.setAdapter(mAdapter); mDialogLabel.setText(getString(R.string.add_dashboard_item)); - mResourcesMenu = new PopupMenu(getActivity(), mFilterResources); + mResourcesMenu = new PopupMenu(getActivity(),mFilter, Gravity.END); mResourcesMenu.inflate(R.menu.menu_filter_resources); mResourcesMenu.setOnMenuItemClickListener(this); } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardViewPagerFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardViewPagerFragment.java index 1b8e5815..33f7f6c0 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardViewPagerFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/dashboard/DashboardViewPagerFragment.java @@ -35,11 +35,13 @@ import android.support.v4.content.Loader; import android.support.v4.view.ViewPager; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import com.raizlabs.android.dbflow.sql.builder.Condition; import com.raizlabs.android.dbflow.sql.language.Select; @@ -47,6 +49,7 @@ import org.hisp.dhis.android.dashboard.DhisService; import org.hisp.dhis.android.dashboard.R; +import org.hisp.dhis.android.dashboard.api.controllers.DhisController; import org.hisp.dhis.android.dashboard.api.job.NetworkJob; import org.hisp.dhis.android.dashboard.api.models.Access; import org.hisp.dhis.android.dashboard.api.models.Dashboard; @@ -59,6 +62,7 @@ import org.hisp.dhis.android.dashboard.ui.adapters.DashboardAdapter; import org.hisp.dhis.android.dashboard.ui.events.UiEvent; import org.hisp.dhis.android.dashboard.ui.fragments.BaseFragment; +import org.hisp.dhis.android.dashboard.ui.fragments.SyncingController; import java.util.Arrays; import java.util.Collections; @@ -70,7 +74,7 @@ public class DashboardViewPagerFragment extends BaseFragment implements LoaderCallbacks>, View.OnClickListener, - ViewPager.OnPageChangeListener { + ViewPager.OnPageChangeListener, SyncingController { static final String TAG = DashboardViewPagerFragment.class.getSimpleName(); static final String IS_LOADING = "state:isLoading"; @@ -90,6 +94,9 @@ public class DashboardViewPagerFragment extends BaseFragment DashboardAdapter mDashboardAdapter; + private DhisController.ImageNetworkPolicy mImageNetworkPolicy = + DhisController.ImageNetworkPolicy.CACHE; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_dashboards, parent, false); @@ -99,7 +106,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle saved public void onViewCreated(View view, Bundle savedInstanceState) { ButterKnife.bind(this, view); - mDashboardAdapter = new DashboardAdapter(getChildFragmentManager()); + mDashboardAdapter = new DashboardAdapter(getChildFragmentManager(),this); mViewPager.setAdapter(mDashboardAdapter); mViewPager.addOnPageChangeListener(this); @@ -121,7 +128,11 @@ public boolean onMenuItemClick(MenuItem item) { } boolean isLoading = isDhisServiceBound() && - getDhisService().isJobRunning(DhisService.SYNC_DASHBOARDS); + (getDhisService().isJobRunning(DhisService.SYNC_DASHBOARDS) + || + getDhisService().isJobRunning(DhisService.SYNC_DASHBOARD_CONTENT) + || + getDhisService().isJobRunning(DhisService.PULL_DASHBOARD_IMAGES)); if ((savedInstanceState != null && savedInstanceState.getBoolean(IS_LOADING)) || isLoading) { mProgressBar.setVisibility(View.VISIBLE); @@ -211,6 +222,10 @@ public void onUiEventReceived(UiEvent uiEvent) { } private void setDashboards(List dashboards) { + if (dashboards != null) { + mDashboardAdapter = new DashboardAdapter(getChildFragmentManager(), this); + mViewPager.setAdapter(mDashboardAdapter); + } mDashboardAdapter.swapData(dashboards); mTabs.removeAllTabs(); @@ -220,6 +235,13 @@ private void setDashboards(List dashboards) { } public boolean onMenuItemClicked(MenuItem item) { + if (isSyncing()) { + Toast.makeText(getContext(), + getString(R.string.action_not_allowed_during_sync), + Toast.LENGTH_SHORT).show(); + return false; + } + switch (item.getItemId()) { case R.id.add_dashboard_item: { long dashboardId = mDashboardAdapter @@ -230,6 +252,7 @@ public boolean onMenuItemClicked(MenuItem item) { return true; } case R.id.refresh: { + mImageNetworkPolicy = DhisController.ImageNetworkPolicy.NO_CACHE; syncDashboards(); return true; } @@ -260,11 +283,27 @@ private void syncDashboards() { @Subscribe @SuppressWarnings("unused") public void onResponseReceived(NetworkJob.NetworkJobResult result) { + Log.d(TAG, "Received " + result.getResourceType()); if (result.getResourceType() == ResourceType.DASHBOARDS) { + getDhisService().syncDashboardContents(); + } + if (result.getResourceType() == ResourceType.DASHBOARDS_CONTENT) { + getDhisService().pullDashboardImages(mImageNetworkPolicy,getContext()); + } + if (result.getResourceType() == ResourceType.INTERPRETATIONS) { + getDhisService().pullInterpretationImages(mImageNetworkPolicy,getContext()); + } + if (result.getResourceType() == ResourceType.DASHBOARD_IMAGES) { mProgressBar.setVisibility(View.INVISIBLE); + getDhisService().syncInterpretations(); } } + @Override + public boolean isSyncing() { + return mProgressBar.getVisibility() == View.VISIBLE; + } + private static class DashboardQuery implements Query> { @Override diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationEmptyFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationEmptyFragment.java index d690e21d..19e20dd4 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationEmptyFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationEmptyFragment.java @@ -3,15 +3,18 @@ import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; +import android.widget.Toast; import com.squareup.otto.Subscribe; import org.hisp.dhis.android.dashboard.DhisService; import org.hisp.dhis.android.dashboard.R; +import org.hisp.dhis.android.dashboard.api.controllers.DhisController; import org.hisp.dhis.android.dashboard.api.job.NetworkJob; import org.hisp.dhis.android.dashboard.api.network.SessionManager; import org.hisp.dhis.android.dashboard.api.persistence.preferences.ResourceType; @@ -34,9 +37,13 @@ public class InterpretationEmptyFragment extends BaseFragment implements View.On @Bind(R.id.progress_bar) SmoothProgressBar mProgressBar; + private DhisController.ImageNetworkPolicy mImageNetworkPolicy = + DhisController.ImageNetworkPolicy.CACHE; + @Nullable @Override - public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + public View onCreateView(LayoutInflater inflater, ViewGroup container, + Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_interpretations_empty, container, false); } @@ -87,8 +94,16 @@ public void onSaveInstanceState(Bundle outState) { } public boolean onMenuItemClicked(MenuItem item) { + if (mProgressBar.getVisibility() == View.VISIBLE) { + Toast.makeText(getContext(), + getString(R.string.action_not_allowed_during_sync), + Toast.LENGTH_SHORT).show(); + return false; + } + switch (item.getItemId()) { case R.id.refresh: { + mImageNetworkPolicy = DhisController.ImageNetworkPolicy.NO_CACHE; syncInterpretations(); return true; } @@ -106,8 +121,12 @@ private void syncInterpretations() { @Subscribe @SuppressWarnings("unused") public void onResponseReceived(NetworkJob.NetworkJobResult result) { + Log.d(TAG, "Received " + result.getResourceType()); if (result.getResourceType() == ResourceType.INTERPRETATIONS) { + getDhisService().pullInterpretationImages(mImageNetworkPolicy, getContext()); + } else if (result.getResourceType() == ResourceType.INTERPRETATION_IMAGES) { mProgressBar.setVisibility(View.INVISIBLE); } } + } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationFragment.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationFragment.java index 2bc6f0f4..1f7c2d12 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationFragment.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/fragments/interpretation/InterpretationFragment.java @@ -36,6 +36,7 @@ import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; +import android.util.Log; import android.view.LayoutInflater; import android.view.MenuItem; import android.view.View; @@ -49,6 +50,7 @@ import org.hisp.dhis.android.dashboard.DhisService; import org.hisp.dhis.android.dashboard.R; +import org.hisp.dhis.android.dashboard.api.controllers.DhisController; import org.hisp.dhis.android.dashboard.api.job.NetworkJob; import org.hisp.dhis.android.dashboard.api.models.Interpretation; import org.hisp.dhis.android.dashboard.api.models.Interpretation$Table; @@ -97,6 +99,9 @@ public final class InterpretationFragment extends BaseFragment InterpretationAdapter mAdapter; + private DhisController.ImageNetworkPolicy mImageNetworkPolicy = + DhisController.ImageNetworkPolicy.CACHE; + @Nullable @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { @@ -108,7 +113,7 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { ButterKnife.bind(this, view); mAdapter = new InterpretationAdapter(getActivity(), - getLayoutInflater(savedInstanceState), this); + (LayoutInflater)getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE), this); final int spanCount = getResources().getInteger(R.integer.column_nums); @@ -127,7 +132,16 @@ public void onViewCreated(View view, @Nullable Bundle savedInstanceState) { mToolbar.setOnMenuItemClickListener(new Toolbar.OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { + + if (mProgressBar.getVisibility() == View.VISIBLE) { + Toast.makeText(getContext(), + getString(R.string.action_not_allowed_during_sync), + Toast.LENGTH_SHORT).show(); + return false; + } + if (item.getItemId() == R.id.refresh) { + mImageNetworkPolicy = DhisController.ImageNetworkPolicy.NO_CACHE; syncInterpretations(); return true; } @@ -234,6 +248,13 @@ public void onInterpretationTextClick(Interpretation interpretation) { @Override public void onInterpretationDeleteClick(Interpretation interpretation) { + if (mProgressBar.getVisibility() == View.VISIBLE) { + Toast.makeText(getContext(), + getString(R.string.action_not_allowed_during_sync), + Toast.LENGTH_SHORT).show(); + return; + } + int position = mAdapter.getData().indexOf(interpretation); if (!(position < 0)) { mAdapter.getData().remove(position); @@ -256,6 +277,13 @@ public void onInterpretationDeleteClick(Interpretation interpretation) { @Override public void onInterpretationEditClick(Interpretation interpretation) { + if (mProgressBar.getVisibility() == View.VISIBLE) { + Toast.makeText(getContext(), + getString(R.string.action_not_allowed_during_sync), + Toast.LENGTH_SHORT).show(); + return; + } + InterpretationTextEditFragment .newInstance(interpretation.getId()) .show(getChildFragmentManager()); @@ -271,8 +299,12 @@ public void onInterpretationCommentsClick(Interpretation interpretation) { @Subscribe @SuppressWarnings("unused") public void onResponseReceived(NetworkJob.NetworkJobResult result) { + Log.d(TAG, "Received " + result.getResourceType()); if (result.getResourceType() == ResourceType.INTERPRETATIONS) { + getDhisService().pullInterpretationImages(mImageNetworkPolicy,getContext()); + } else if (result.getResourceType() == ResourceType.INTERPRETATION_IMAGES) { mProgressBar.setVisibility(View.INVISIBLE); + mAdapter.updateImages(); } } @@ -281,7 +313,8 @@ public void onResponseReceived(NetworkJob.NetworkJobResult result) { public void onUiEventReceived(UiEvent uiEvent) { if (uiEvent.getEventType() == UiEvent.UiEventType.SYNC_INTERPRETATIONS) { boolean isLoading = isDhisServiceBound() && - getDhisService().isJobRunning(DhisService.SYNC_INTERPRETATIONS); + getDhisService().isJobRunning(DhisService.SYNC_INTERPRETATIONS) + || getDhisService().isJobRunning(DhisService.PULL_INTERPRETATION_IMAGES); if (isLoading) { mProgressBar.setVisibility(View.VISIBLE); } else { diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontButton.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontButton.java index 48d0e288..be33d74e 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontButton.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontButton.java @@ -38,7 +38,7 @@ import org.hisp.dhis.android.dashboard.R; import org.hisp.dhis.android.dashboard.utils.TypefaceManager; -public class FontButton extends Button { +public class FontButton extends android.support.v7.widget.AppCompatButton { public FontButton(Context context) { super(context); diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontCheckBox.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontCheckBox.java index 4cff7b05..55a46e87 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontCheckBox.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontCheckBox.java @@ -33,12 +33,11 @@ import android.graphics.Paint; import android.graphics.Typeface; import android.util.AttributeSet; -import android.widget.CheckBox; import org.hisp.dhis.android.dashboard.R; import org.hisp.dhis.android.dashboard.utils.TypefaceManager; -public class FontCheckBox extends CheckBox { +public class FontCheckBox extends android.support.v7.widget.AppCompatCheckBox { public FontCheckBox(Context context) { super(context); } diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontEditText.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontEditText.java index ebb5dec3..090980f5 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontEditText.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontEditText.java @@ -38,7 +38,7 @@ import org.hisp.dhis.android.dashboard.R; import org.hisp.dhis.android.dashboard.utils.TypefaceManager; -public class FontEditText extends EditText { +public class FontEditText extends android.support.v7.widget.AppCompatEditText { public FontEditText(Context context) { super(context); diff --git a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontTextView.java b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontTextView.java index 1e1054a4..5a82ce9a 100755 --- a/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontTextView.java +++ b/app/src/main/java/org/hisp/dhis/android/dashboard/ui/views/FontTextView.java @@ -38,7 +38,7 @@ import org.hisp.dhis.android.dashboard.R; import org.hisp.dhis.android.dashboard.utils.TypefaceManager; -public class FontTextView extends TextView { +public class FontTextView extends android.support.v7.widget.AppCompatTextView { public FontTextView(Context context) { super(context); diff --git a/app/src/main/res/drawable-hdpi/ic_event_report.png b/app/src/main/res/drawable-hdpi/ic_event_report.png new file mode 100644 index 00000000..63156d16 Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_event_report.png differ diff --git a/app/src/main/res/drawable-hdpi/ic_pivot_table.png b/app/src/main/res/drawable-hdpi/ic_pivot_table.png new file mode 100644 index 00000000..7aedbf0a Binary files /dev/null and b/app/src/main/res/drawable-hdpi/ic_pivot_table.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_event_report.png b/app/src/main/res/drawable-mdpi/ic_event_report.png new file mode 100644 index 00000000..c0915ccf Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_event_report.png differ diff --git a/app/src/main/res/drawable-mdpi/ic_pivot_table.png b/app/src/main/res/drawable-mdpi/ic_pivot_table.png new file mode 100644 index 00000000..26852e62 Binary files /dev/null and b/app/src/main/res/drawable-mdpi/ic_pivot_table.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_event_report.png b/app/src/main/res/drawable-xhdpi/ic_event_report.png new file mode 100644 index 00000000..c542b41b Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_event_report.png differ diff --git a/app/src/main/res/drawable-xhdpi/ic_pivot_table.png b/app/src/main/res/drawable-xhdpi/ic_pivot_table.png new file mode 100644 index 00000000..c56b1c1c Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/ic_pivot_table.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_event_report.png b/app/src/main/res/drawable-xxhdpi/ic_event_report.png new file mode 100644 index 00000000..fab51321 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_event_report.png differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_pivot_table.png b/app/src/main/res/drawable-xxhdpi/ic_pivot_table.png new file mode 100644 index 00000000..5bf700b9 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/ic_pivot_table.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_event_report.png b/app/src/main/res/drawable-xxxhdpi/ic_event_report.png new file mode 100644 index 00000000..69fd8bd6 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_event_report.png differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_pivot_table.png b/app/src/main/res/drawable-xxxhdpi/ic_pivot_table.png new file mode 100644 index 00000000..42b4fa52 Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_pivot_table.png differ diff --git a/app/src/main/res/layout/fragment_about_us.xml b/app/src/main/res/layout/fragment_about_us.xml new file mode 100644 index 00000000..6ce814c7 --- /dev/null +++ b/app/src/main/res/layout/fragment_about_us.xml @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_settings.xml b/app/src/main/res/layout/fragment_settings.xml index df6b7975..4445be8c 100755 --- a/app/src/main/res/layout/fragment_settings.xml +++ b/app/src/main/res/layout/fragment_settings.xml @@ -53,13 +53,80 @@ + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/menu/menu_drawer.xml b/app/src/main/res/menu/menu_drawer.xml index 4d8276ae..9d86ec69 100755 --- a/app/src/main/res/menu/menu_drawer.xml +++ b/app/src/main/res/menu/menu_drawer.xml @@ -46,6 +46,10 @@ android:id="@+id/menu_settings_item" android:icon="@mipmap/ic_settings" android:title="@string/settings"/> + @@ -113,4 +114,31 @@ Phone number + + + "About this app" + App version: %s + "Commit hash: %s" + "Unavailable" + This option requires some changes in the git repository.

+

Steps:

+

1. Please open or create the .git/hooks/post-commit and .git/hooks/post-checkout files.

+

2. If the file does not exist, add the following lines:

+

#!/bin/sh

+

gitPath=$(git rev-parse --show-toplevel)

+

sh ${gitPath}/generate_last_commit.sh

+

2. If the file already exists, you only need add the following lines at the end:

+

gitPath=$(git rev-parse --show-toplevel)

+

sh ${gitPath}/generate_last_commit.sh

+

3. Run "git checkout".

]]> +
+ + + + "Images size" + "Width:" + "Height:" + The value is not valid, please enter a value between %1$d and %2$d + "The App is synchronizing with the server, please try again after it's finished" diff --git a/app/src/test/java/TextUtilsTest.java b/app/src/test/java/TextUtilsTest.java new file mode 100644 index 00000000..8fff2c09 --- /dev/null +++ b/app/src/test/java/TextUtilsTest.java @@ -0,0 +1,17 @@ +import static org.junit.Assert.assertTrue; + +import org.hisp.dhis.android.dashboard.utils.TextUtils; +import org.junit.Test; + +public class TextUtilsTest { + + @Test + public void test_empty_string_is_empty() { + assertTrue(TextUtils.isEmpty("")); + } + + @Test + public void test_null_string_is_empty() { + assertTrue(TextUtils.isEmpty(null)); + } +} diff --git a/buddybuild_postbuild.sh b/buddybuild_postbuild.sh new file mode 100644 index 00000000..507c8e47 --- /dev/null +++ b/buddybuild_postbuild.sh @@ -0,0 +1,9 @@ +#!/usr/bin/env bash + +# Upload CodeCov report +echo "ConnectedCheck: " +./gradlew connectedCheck + +echo "Send report to CodeCov" +bash <(curl -s https://codecov.io/bash) -t ${CODECOV_TOKEN} + diff --git a/buddybuild_postclone.sh b/buddybuild_postclone.sh new file mode 100644 index 00000000..83e8e1f0 --- /dev/null +++ b/buddybuild_postclone.sh @@ -0,0 +1,10 @@ +#!/usr/bin/env bash + +# Definitions +gitPath=$(git rev-parse --show-toplevel) + +# Generate last commit +sh ${gitPath}/generate_last_commit.sh + +echo "Generate Test Coverage Report:" +./gradlew build jacocoTestReport assembleAndroidTest diff --git a/build.gradle b/build.gradle index b1ecb178..9bc13480 100755 --- a/build.gradle +++ b/build.gradle @@ -6,6 +6,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:2.3.0' classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4' + classpath 'com.dicedmelon.gradle:jacoco-android:0.1.1' } } diff --git a/generate_last_commit.sh b/generate_last_commit.sh new file mode 100644 index 00000000..08506669 --- /dev/null +++ b/generate_last_commit.sh @@ -0,0 +1,8 @@ +#!/bin/sh +gitPath=$(git rev-parse --show-toplevel) +filePath="${gitPath}/app/src/main/res/raw/lastcommit.txt" +echo "Last commit path $filePath" +commit=$(git log -1 HEAD --format=%H) +echo "Saving last commit: ${commit}" +echo ${commit} > $filePath +echo "Done."