audienceFraction static method

double? audienceFraction(
  1. PlayReleaseStatus? status, {
  2. required double? userFraction,
})

The fraction of the track's audience a release in status has been given.

Play omits its own userFraction exactly when it means one, and that is the hole this fills. Google sets the field only for inProgress and halted, so a completed rollout carries no fraction at all, and a caller reading Play's number alone gets null for the release that reached everybody, then has to learn from Google's documentation that null-beside-completed is what 100% looks like. That is the deferral serving exists to end, one field down.

Where the range comes from, because the values below lean on it. Google's own words for userFraction, reaching this package through googleapis 16.0.0's dartdoc, which discoveryapis_generator writes from the Android Publisher v3 discovery document:

Fraction of users who are eligible for a staged release. 0 < fraction < 1. Can only be set when status is "inProgress" or "halted".

Two honest caveats about that sentence. It is phrased as a constraint on what may be set, so it binds a write directly and a read only by inference. And nothing in this repository has measured it — there is no observation of a live account here, unlike the App Store states next door.

So the values below do not depend on it for correctness, and that is the second reason PlayReleaseEntry.userFraction is carried rather than folded away. If Play ever answered 1.0 for an inProgress release, this would return 1.0 — the same number it infers for completed — and the two would still be distinguishable, because this is a function of PlayReleaseEntry.status and PlayReleaseEntry.userFraction and the document carries both of its inputs. A caller reading status sees which branch produced the number: completed means the 1.0 is ours, inProgress means it is Play's.

The status is the discriminator, and not the raw field's nullness — which is the tempting answer and is only true while the quoted sentence holds in its second half. Were Play to set userFraction on a completed release, ours and a full rollout would both read 1.0 beside 1.0, and nullness would have stopped separating them. Nullness is a convenience for the common case; status is the guarantee.

Reading PlayReleaseEntry.audienceFraction alone is what the range buys. Reading it beside status needs nothing documented about the value.

1.0 for completed and 0.0 for draft — both stated by Play's status rather than measured by it. PlayReleaseEntry.userFraction for inProgress and for halted: a halted release's fraction is the one it stopped at, and the users who already took it keep it.

Null for statusUnspecified, for unknown and for an absent status, on the same terms as every other derived answer here — and null for an inProgress or halted release Play sent no fraction for, which is Play contradicting its own documentation and not a case to guess at.

It measures who has it, not what the rollout is doing, which is why it is safe to read beside serving in either order: a halted release is serving: false with a non-zero fraction, and both are true at once.

Implementation

static double? audienceFraction(
  PlayReleaseStatus? status, {
  required double? userFraction,
}) => switch (status) {
  completed => 1.0,
  draft => 0.0,
  inProgress || halted => userFraction,
  statusUnspecified || unknown || null => null,
};