Class Invites
Invite a friend, and follow the invitation through to what it caused.
Mint an invite, share it, and on the friend's device recover the invite
that produced the install. Once attribution resolves it is written as
persistent analytics dimensions, so every later event -- including the
purchase event the framework already emits -- carries the campaign and
the referrer, and revenue per campaign comes out of the reports you have.
Sending
Invite invite = Invites.create(InviteRequest.create()
.campaign("spring")
.channel("share_sheet")
.build());
Invites.share(invite, "Come and try this with me");
create(InviteRequest) returns immediately and works with no network, so the share
sheet never waits on a server. Registration with the link service is
retried in the background.
Receiving
Invites.setInviteListener(new InviteListener() {
public void inviteReceived(InviteAttribution attribution) {
// attribution.getCode(), getCampaign(), getPayload()
}
public void attributionUnavailable(String reason) {
}
});
Invites.checkForInvite();
Call checkForInvite() from your start() method. It is a pull rather
than a callback on purpose: Android delivers a link by replacing the
activity intent and iOS by setting a property, and reading the launch
argument is the one path that behaves the same on both.
Consent, and what is on the device before it
Everything reported here is gated on the analytics consent category of
Analytics, and nothing is transmitted until consent is granted.
Nothing about the device is collected, before consent or after it. An earlier design wrote a coarse profile -- operating system version, hardware model, language, screen size -- to local storage on first launch, because an iOS install could then be matched to a click statistically. App Clips removed the need: the clip is launched by the invite link and is handed the code itself, so there is nothing to match and nothing to keep.
What is stored locally is the invite code and the bookkeeping around it --
a state, a deadline, an attempt count -- and the code is only ever one the
person produced by tapping an invite. setAttributionWindow(long) with 0
switches deferred attribution off entirely.
How exact the answer is
InviteAttribution.getMatchType() says how the attribution was made, and
every one of them is exact. MATCH_DIRECT is a link opening an
application that was already installed; MATCH_REFERRER is a code that
made the whole trip through the Play store; MATCH_APP_CLIP is a code an
iOS App Clip received from the link itself and handed to the application it
installed.
There used to be a statistical match here as well, because the App Store carries no referrer of its own and an iOS install could only be guessed at. It was occasionally wrong, it could not say which times, and it required collecting a hashed profile of people who installed nothing. App Clips made it unnecessary and it is gone.
-
Field Summary
FieldsModifier and TypeFieldDescriptionstatic final StringThe analytics category every invite event is reported under.static final longThe default attribution window: how long after a first launch a deferred invite may still be resolved.static final StringDimension carrying the campaign the invite belonged to.static final StringDimension carrying the channel the invite was sent through.static final StringDimension carrying the matched invite code.static final StringDimension carrying how the attribution was made.static final StringAn iOS App Clip received the invite link, kept the code, and handed it to the application the person then installed.static final StringThe link opened an application that was already installed.static final StringThe invite code made the whole trip through the application store and came back verbatim.static final StringAnalytics consent was refused, so attribution was abandoned.static final StringThe attribution window closed before an answer arrived.static final StringNo invite matched.static final StringThis platform cannot recover a deferred invite.static final intAttribution was abandoned because analytics consent was refused.static final intNothing has been attributed and nothing is outstanding.static final intNo invite will be attributed to this install.static final intAn invite is being resolved; the answer has not arrived yet.static final intThis install has been attributed to an invite. -
Method Summary
Modifier and TypeMethodDescriptionstatic booleanLooks for an invite: first in the launch argument, then, when this looks like a fresh install, by asking the link service.static voidconversion(String action) Reports that the invited user reached the outcome the invite existed for -- signed up, joined the room, completed onboarding.static voidconversion(String action, double value, String currency) Reports a conversion carrying a value, so revenue can be attributed to the campaign and the referrer.static Invitecreate(InviteRequest request) Mints an invite and returns it immediately.static voidflush()Retries anything queued: unregistered invites, and an outstanding deferred match.static InviteAttributionThe attribution for this install, or null when there is none yet.static longThe attribution window in milliseconds.static InviteListenerThe registered listener, or null.static StringThe link service base address in use.static intgetState()Where attribution has got to: one of theSTATE_constants.static booleanOffers a url to the invite machinery directly, for applications that consume the launch argument themselves or route it throughcom.codename1.router.static booleanWhether last touch attribution is enabled.static booleanisRegistered(Invite invite) Whether the link service has acknowledged this invite.static voidRegisters the platform hook that reads the invite code an iOS App Clip left for this application.static voidRegisters the platform hook that reads the application store's install referrer.static voidreportShareResult(Invite invite, ShareResult result) Reports the outcome of a share your application performed itself, rather than throughshare(Invite, String).static voidreset()Forgets every trace of invite attribution on this device: the pending lookup, the resolved attribution and the referral dimensions.static voidsetAttributionWindow(long millis) How long after a first launch a deferred invite may still be resolved.static voidRegisters the listener that receives the invite behind this install.static voidsetLinkBase(String url) Points the invite machinery at a different link service.static voidsetReattribution(boolean value) Whether a later invite replaces an earlier attribution.static voidShares an invite through the native share sheet.static voidshare(Invite invite, String message, Rectangle sourceRect, ShareResultListener resultListener) Shares an invite through the native share sheet and reports the outcome.
-
Field Details
-
STATE_NONE
public static final int STATE_NONENothing has been attributed and nothing is outstanding.- See Also:
-
STATE_PENDING
public static final int STATE_PENDINGAn invite is being resolved; the answer has not arrived yet.- See Also:
-
STATE_RESOLVED
public static final int STATE_RESOLVEDThis install has been attributed to an invite.- See Also:
-
STATE_NONE_FOUND
public static final int STATE_NONE_FOUNDNo invite will be attributed to this install.- See Also:
-
STATE_DECLINED
public static final int STATE_DECLINEDAttribution was abandoned because analytics consent was refused.- See Also:
-
MATCH_DIRECT
The link opened an application that was already installed. Exact.- See Also:
-
MATCH_REFERRER
The invite code made the whole trip through the application store and came back verbatim. Exact.- See Also:
-
MATCH_APP_CLIP
An iOS App Clip received the invite link, kept the code, and handed it to the application the person then installed. Exact.- See Also:
-
REASON_NO_MATCH
No invite matched. The ordinary outcome for an uninvited install.- See Also:
-
REASON_EXPIRED
The attribution window closed before an answer arrived.- See Also:
-
REASON_CONSENT_DENIED
Analytics consent was refused, so attribution was abandoned.- See Also:
-
REASON_UNSUPPORTED
This platform cannot recover a deferred invite.- See Also:
-
CATEGORY
The analytics category every invite event is reported under.- See Also:
-
DIMENSION_CODE
-
DIMENSION_CAMPAIGN
Dimension carrying the campaign the invite belonged to.- See Also:
-
DIMENSION_CHANNEL
Dimension carrying the channel the invite was sent through.- See Also:
-
DIMENSION_MATCH
Dimension carrying how the attribution was made.- See Also:
-
DEFAULT_ATTRIBUTION_WINDOW
public static final long DEFAULT_ATTRIBUTION_WINDOWThe default attribution window: how long after a first launch a deferred invite may still be resolved.- See Also:
-
-
Method Details
-
registerInstallReferrerSource
Registers the platform hook that reads the application store's install referrer. The Codename One build calls this before the application starts on platforms that have one; an application does not.
Parameters
source: the platform source, or null to remove it
-
registerAppClipHandoffSource
Registers the platform hook that reads the invite code an iOS App Clip left for this application. The Codename One build calls this before the application starts on platforms that have one; an application does not.
Parameters
source: the platform source, or null to remove it
-
create
Mints an invite and returns it immediately.
This never blocks and never fails for want of a network. The code is generated on the device, so
Invite.getUrl()is usable at once; registration with the link service is queued and retried until it lands. A link clicked before that registration arrives is still attributed, because the server records the click against the code and joins it when the registration turns up.Parameters
request: what to mint, must not be null
Returns
the invite, never null
Throws
IllegalStateException: when the device cannot supply secure randomness. The code is the digest of a secret and that secret is what proves who minted it, so a guessable one is a forgeable proof -- an invite anybody it is shared with could register as their own. Failing here is visible on the broken device; minting anyway is invisible on every device the link reaches.
-
setInviteListener
Registers the listener that receives the invite behind this install.
An answer that arrived before the listener was registered -- which happens routinely on a cold launch from a link, because the platform delivers the link before the application starts -- is delivered as soon as this is called.
Parameters
l: the listener, or null to remove it
-
getInviteListener
The registered listener, or null.
Returns
the listener
-
checkForInvite
public static boolean checkForInvite()Looks for an invite: first in the launch argument, then, when this looks like a fresh install, by asking the link service.
Safe and cheap to call on every start; it will not attribute twice and will not report twice.
Call it on every start rather than only the first. A lookup that ended with "not yet" -- the invite exists but the inviter minted it offline and their registration has not reached the service -- is retried here, at most once per retry interval, so an invite that becomes claimable during the session is picked up in the session rather than on the next cold start.
flush()does the same for an application that knows it has just regained connectivity.Returns
true when the launch argument carried an invite link
-
handleUrl
Offers a url to the invite machinery directly, for applications that consume the launch argument themselves or route it through
com.codename1.router.Parameters
url: the url to inspect, may be null
Returns
true when the url carried an invite code
-
getAttribution
The attribution for this install, or null when there is none yet.
Returns
the attribution
-
getState
public static int getState()Where attribution has got to: one of the
STATE_constants.Returns
the current state
-
conversion
Reports that the invited user reached the outcome the invite existed for -- signed up, joined the room, completed onboarding. No-op unless this install was attributed.
Parameters
action: what the user did
-
conversion
Reports a conversion carrying a value, so revenue can be attributed to the campaign and the referrer. No-op unless this install was attributed.
Parameters
-
action: what the user did -
value: the value of the conversion -
currency: the currency code, or null
-
-
setLinkBase
Points the invite machinery at a different link service. Defaults to the Codename One cloud, honouring the
cloudServerURLdisplay property.Set the
invite.domainbuild hint to the same host. This changes where links are MINTED and nothing else. The Android intent filter and the iOS associated-domain entitlement are written at BUILD time from that hint, so a host set only here is a host the installed app does not claim: every invite link opens the browser instead of the app, and neither the OS nor the framework reports anything. A mismatch is logged once, because it cannot be refused -- pointing at a staging service and accepting the browser is a legitimate thing to do.A bare host is accepted and read as
https://. Anything else that is not HTTPS is REFUSED: Invite.getUrl() promises an absolute https url, and the generated Android filter and iOS associated domain match nothing else, so an http:// base mints links that always open the browser -- and it would pass the host check below, which compares hosts and not schemes.Parameters
url: the base address, with no trailing path
Throws
IllegalArgumentException: when the address is not HTTPS
-
getLinkBase
The link service base address in use.
Returns
the base address, never null
-
setAttributionWindow
public static void setAttributionWindow(long millis) How long after a first launch a deferred invite may still be resolved. Clamped to at most 30 days. Zero switches deferred attribution off, which is the supported way to ship without the statistical match.
Parameters
millis: the window in milliseconds
-
getAttributionWindow
public static long getAttributionWindow()The attribution window in milliseconds.
Returns
the window
-
setReattribution
public static void setReattribution(boolean value) Whether a later invite replaces an earlier attribution. Off by default: first touch stands, so a user's cohort does not change underneath the reports.
Parameters
value: true for last touch
-
isReattribution
public static boolean isReattribution()Whether last touch attribution is enabled.
Returns
true when a later invite replaces an earlier one
-
flush
public static void flush()Retries anything queued: unregistered invites, and an outstanding deferred match. Called for you on the paths that matter; exposed for an application that knows it has just regained connectivity. -
reset
public static void reset()Forgets every trace of invite attribution on this device: the pending lookup, the resolved attribution and the referral dimensions.
Analytics.resetClientId()triggers this for you, because an erasure that left the referral dimensions behind would re-link the fresh identity to the same inviter. -
isRegistered
Whether the link service has acknowledged this invite.
An unacknowledged invite is still shareable and still attributes -- registration is retried until it lands -- so this is a diagnostic rather than a gate.
Parameters
invite: the invite to ask about, may be null
Returns
true once the server has acknowledged it
-