public final class Invites
- Object
- 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 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 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.
Fields
Methods
public static void registerInstallReferrerSource(InstallReferrerSource source) | Registers the platform hook that reads the application store’s install referrer. |
public static void registerAppClipHandoffSource(AppClipHandoffSource source) | Registers the platform hook that reads the invite code an iOS App Clip left for this application. |
public static Invite create(InviteRequest request) | Mints an invite and returns it immediately. |
public static void share(Invite invite, String message) | Shares an invite through the native share sheet. |
public static void share(Invite invite, String message, Rectangle sourceRect, ShareResultListener resultListener) | Shares an invite through the native share sheet and reports the outcome. |
public static void reportShareResult(Invite invite, ShareResult result) | Reports the outcome of a share your application performed itself, rather than through share. |
public static void setInviteListener(InviteListener l) | Registers the listener that receives the invite behind this install. |
public static InviteListener getInviteListener() | The registered listener, or null. |
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. |
public static boolean handleUrl(String url) | Offers a url to the invite machinery directly, for applications that consume the launch argument themselves or route it through com.codename1.router. |
public static InviteAttribution getAttribution() | The attribution for this install, or null when there is none yet. |
public static int getState() | Where attribution has got to: one of the STATE_ constants. |
public static void conversion(String action) | Reports that the invited user reached the outcome the invite existed for – signed up, joined the room, completed onboarding. |
public static void conversion(String action, double value, String currency) | Reports a conversion carrying a value, so revenue can be attributed to the campaign and the referrer. |
public static void setLinkBase(String url) | Points the invite machinery at a different link service. |
public static String getLinkBase() | The link service base address in use. |
public static void setAttributionWindow(long millis) | How long after a first launch a deferred invite may still be resolved. |
public static long getAttributionWindow() | The attribution window in milliseconds. |
public static void setReattribution(boolean value) | Whether a later invite replaces an earlier attribution. |
public static boolean isReattribution() | Whether last touch attribution is enabled. |
public static void flush() | Retries anything queued: unregistered invites, and an outstanding deferred match. |
public static void reset() | Forgets every trace of invite attribution on this device: the pending lookup, the resolved attribution and the referral dimensions. |
public static boolean isRegistered(Invite invite) | Whether the link service has acknowledged this invite. |
Inherited methods
Field details
STATE_NONE
public static final int STATE_NONE = 0STATE_PENDING
public static final int STATE_PENDING = 1STATE_RESOLVED
public static final int STATE_RESOLVED = 2STATE_NONE_FOUND
public static final int STATE_NONE_FOUND = 3STATE_DECLINED
public static final int STATE_DECLINED = 4MATCH_DIRECT
public static final String MATCH_DIRECT = "direct"MATCH_REFERRER
public static final String MATCH_REFERRER = "referrer"MATCH_APP_CLIP
public static final String MATCH_APP_CLIP = "app_clip"REASON_NO_MATCH
public static final String REASON_NO_MATCH = "no_match"REASON_EXPIRED
public static final String REASON_EXPIRED = "expired"REASON_CONSENT_DENIED
public static final String REASON_CONSENT_DENIED = "consent_denied"REASON_UNSUPPORTED
public static final String REASON_UNSUPPORTED = "unsupported"CATEGORY
public static final String CATEGORY = "referral"DIMENSION_CODE
public static final String DIMENSION_CODE = "cn1_invite_code"DIMENSION_CAMPAIGN
public static final String DIMENSION_CAMPAIGN = "cn1_campaign"DIMENSION_CHANNEL
public static final String DIMENSION_CHANNEL = "cn1_channel"DIMENSION_MATCH
public static final String DIMENSION_MATCH = "cn1_invite_match"DEFAULT_ATTRIBUTION_WINDOW
public static final long DEFAULT_ATTRIBUTION_WINDOW = 604800000LMethod details
registerInstallReferrerSource
public static void registerInstallReferrerSource(InstallReferrerSource source)Parameters
sourceInstallReferrerSource- the platform source, or null to remove it
registerAppClipHandoffSource
public static void registerAppClipHandoffSource(AppClipHandoffSource source)Parameters
sourceAppClipHandoffSource- the platform source, or null to remove it
create
public static Invite create(InviteRequest request)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
requestInviteRequest- what to mint, must not be null
Returns
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
public static void setInviteListener(InviteListener l)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
lInviteListener- the listener, or null to remove it
getInviteListener
public static InviteListener getInviteListener()Returns
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
handleUrl
public static boolean handleUrl(String url)com.codename1.router.Parameters
urlString- the url to inspect, may be null
Returns
getAttribution
public static InviteAttribution getAttribution()Returns
getState
public static int getState()STATE_ constants.Returns
conversion
public static void conversion(String action)Parameters
actionString- what the user did
conversion
public static void conversion(String action, double value, String currency)Parameters
actionString- what the user did
valuedouble- the value of the conversion
currencyString- the currency code, or null
setLinkBase
public static void setLinkBase(String url)Points the invite machinery at a different link service. Defaults to
the Codename One cloud, honouring the cloudServerURL display
property.
Set the invite.domain build 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
urlString- the base address, with no trailing path
Throws
IllegalArgumentException- when the address is not HTTPS
getLinkBase
public static String getLinkBase()Returns
setAttributionWindow
public static void setAttributionWindow(long millis)Parameters
millislong- the window in milliseconds
getAttributionWindow
public static long getAttributionWindow()Returns
setReattribution
public static void setReattribution(boolean value)Parameters
valueboolean- true for last touch
isReattribution
public static boolean isReattribution()Returns
flush
public static void flush()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
public static boolean isRegistered(Invite invite)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
inviteInvite- the invite to ask about, may be null