Class AggregateQuery

java.lang.Object
com.codename1.health.AggregateQuery

public final class AggregateQuery extends Object

Describes a bucketed summary read against HealthStore.

AggregateQuery q = new AggregateQuery()
        .addType(HealthDataType.STEPS)
        .addMetric(AggregateMetric.TOTAL)
        .setTimeRange(HealthTimeRange.calendarDays(7, ZoneId.systemDefault()))
        .setBucket(HealthInterval.calendarDays(1, ZoneId.systemDefault()));
Overlapping sources are counted twice, on every platform

When a phone and a watch both record steps for the same walk, the store holds two overlapping sets of samples, and a total over them counts the walk twice.

This includes iOS. HealthKit's statistics engine does de-duplicate overlapping sources -- but no port uses it in this release. Every metric here is computed by shared code from raw samples read back through an ordinary query, so that the bucket arithmetic has one implementation rather than one per platform that can drift. iOS therefore double-counts exactly as Android does. A port that grows a native aggregate path would change that, and this note with it.

This API does not paper over the overlap with a heuristic de-duplicator -- guessing which of two overlapping sources is authoritative is exactly the kind of silent wrongness health data cannot afford. Use addSource(String) to pin the query to the source you trust, and tell the user which device a figure came from.

  • Constructor Details

    • AggregateQuery

      public AggregateQuery()
  • Method Details

    • addType

      public AggregateQuery addType(HealthDataType type)
      Adds a type to summarize. At least one is required.
    • getTypes

      public List<HealthDataType> getTypes()
      The types this query summarizes.
    • addMetric

      public AggregateQuery addMetric(AggregateMetric metric)
      Adds a metric to compute. At least one is required.
    • getMetrics

      public List<AggregateMetric> getMetrics()
      The metrics this query computes.
    • setDeduplicateSources

      public AggregateQuery setDeduplicateSources(boolean deduplicate)

      Restricts the summary to one writing app -- see the double-counting warning on this class. Asks the platform to de-duplicate overlapping sources.

      Off by default, because the shared rollup cannot do it and most stores therefore cannot honour it. Check HealthStore.isSourceDeduplicationSupported() first: asking a store that cannot fails the aggregate with HealthError.NOT_SUPPORTED rather than silently returning the double-counted answer.

      Where it is honoured -- HealthKit, whose statistics engine does this natively -- a walk recorded by both a phone and a watch is counted once instead of twice.

    • isDeduplicateSources

      public boolean isDeduplicateSources()
      Whether this query asked for source de-duplication.
    • addSource

      public AggregateQuery addSource(String bundleId)
    • getSources

      public List<String> getSources()
      The source filter, empty when unrestricted.
    • setTimeRange

      public AggregateQuery setTimeRange(HealthTimeRange timeRange)
      The span to summarize. Required.
    • getTimeRange

      public HealthTimeRange getTimeRange()
      The span this query summarizes.
    • setBucket

      public AggregateQuery setBucket(HealthInterval bucket)

      Splits the range into buckets of this width. Leave unset for a single bucket covering the whole range.

      Prefer HealthInterval.calendarDays(int,java.time.ZoneId) over a fixed 24-hour width whenever the buckets are labelled with dates in your UI -- see HealthInterval.

    • getBucket

      public HealthInterval getBucket()
      The bucket width, or null for one bucket over the whole range.
    • setUnit

      public AggregateQuery setUnit(HealthUnit unit)
      Returns aggregated values in unit rather than each type's canonical unit.
    • getUnit

      public HealthUnit getUnit()
      The requested unit, or null for canonical units.
    • validate

      public void validate() throws HealthException

      Validates the query and throws if it cannot be run.

      Throws
      Throws:
      HealthException
    • isMeaningful

      public static boolean isMeaningful(HealthDataType type, AggregateMetric metric)

      Whether metric says anything true about type.

      AggregateMetric.COUNT and AggregateMetric.DURATION apply to everything. Summing a discrete series -- the total of every body mass ever recorded -- and averaging a cumulative one -- the mean of arbitrarily-chunked step totals -- are both meaningless, so they are rejected rather than answered.