# Android v3.0.X

### Introduction

#### SDK overview

Welcome to the Flagship Android SDK documentation!

The following article will guide you through the steps to get Flagship up and running on your native Android app using our client library with preconfigured methods to implement the Decision API.

Feel free to [contact us](mailto:product.feedback@abtasty.com?subject=Flagship%20Developer%20Documentation) if you have any questions regarding this documentation.

#### Release notes

[See here](doc:android-sdk)

#### SDK features

This SDK version will help you:

* Set a [visitor ID](doc:glossary)
* Update [user context](doc:glossary.)
* Assign campaigns via the [Decision API](doc:decision-api-v2) or [Bucketing](doc:bucketing)
* Get flags
* Activate campaigns
* Send hits to our [Universal Collect](doc:universal-collect-documentation)

#### Prerequisites

* Your app must be a native Android app written in Kotlin or Java
* Your app must at least be compatible with Android API version 21 (Android 5.0) **OR** version 16 (Android 4.1) if you use the compatibility version of our library. [See dependencies](#dependency)

  > The `minSdkVersion` value in your app module's build.gradle file must be greater than or equal to **21**:

```groovy
android {
    compileSdkVersion 28
    defaultConfig {
        minSdkVersion 21
        ...
    }
}
```

* Your app must have the necessary permissions to use the Internet

  > Internet permission must be enabled in the \<application> tag of your app's AndroidManifest.xml file:

```xml
<?xml version="1.0" encoding="utf-8"?>
<manifest>
    <uses-permission android:name="android.permission.INTERNET" />
    <!-- [...] -->
</manifest>
```

* Your app must have AndroidX enabled

#### Good to know

* [GitHub repository](https://github.com/flagship-io/flagship-android)
* Weight: 112ko
* Kotlin and Java code supported
* [Open source demo app](https://github.com/abtasty/flagship-demo-android)

### Getting Started

#### Installation

To install the library, start by adding the following repository in the **main build.gradle** file of your Android project so that Gradle can find and build with the library:

Our SDK is available on Maven central, be sure that you added it in your dependency manager.

```groovy
allprojects {
    repositories {
        maven {
            mavenCentral()
        }
    }
}
```

Then add the following library dependency in the **build.gradle** file of your **app module**:

**`com.abtasty:flagship-android:x.x.x`**

> 📘
>
> Replace `x.x.x` with the desired version of the Flagship SDK.

```groovy
dependencies {
    implementation 'com.abtasty:flagship-android:3.0.0
}
```

> 🚧
>
> If you need to target Android versions from Android 4.1+ API level 16+, use the following code, starting at version 1.2.1 of the Flagship Android SDK:
>
> **`com.abtasty:flagship-android-compat:x.x.x`**
>
> This version ensures TLS 1.2 compatibility between our servers and Android 4.1+, but we do not recommand using it due to lack of TLS 1.2 support by these platforms.

#### Initialization

To initialize and start the library, just call the `start()` function of the `Flagship` class in the most appropriate location for your application.

```kotlin
class MyActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        Flagship.start(getApplication(),"_ENV_ID_", "_API_KEY_", FlagshipConfig.DecisionApi()
    }
}
```

```java
```

**`fun start(application : Application, envId: String, apiKey: String, config: FlagshipConfig<*>)`**

| Parameter   | Type               | Description                                  |
| ----------- | ------------------ | -------------------------------------------- |
| application | Application        | your Android application base class.         |
| envId       | String             | Environment id provided by Flagship.         |
| apiKey      | String             | Api authentication key provided by Flagship. |
| config      | FlagshipConfig<\*> | Flagship configuration to use.               |

> 📘
>
> You can find your `apiKey` and your `environmentId` on your Flagship account, in Parameters > Environment & Security.

**Flagship configuration : FlagshipConfig.**

This class aims to help you to configure the SDK via the following two available config implementations: DecisionApi and Bucketing. **See**[**Decision Mode**](https://github.com/flagship-io/Gitbook/blob/main/sdks/Archived%20SDK%20Versions/archived-android/v3.0#decision-mode) **section**.

```kotlin
//Start the Flagship SDK in Api mode.
Flagship.start(getApplication(), "_ENV_ID_", "_API_KEY_", FlagshipConfig.DecisionApi()
            .withStatusListener { status -> }
            .withLogLevel(LogManager.Level.ALL)
            .withTimeout(500)
            .withCacheManager(customCacheManager)
            .withLogManager(customLogManager)
        )


//Start the Flagship SDK in Bucketing mode.
Flagship.start(getApplication(), "_ENV_ID_", "_API_KEY_", FlagshipConfig.Bucketing()
            .withStatusListener { status -> }
            .withLogLevel(LogManager.Level.ALL)
            .withTimeout(500)
            .withCacheManager(customCacheManager)
            .withLogManager(customLogManager)
            .withPollingIntervals(5, TimeUnit.MINUTES)
        )
```

```java
```

\\

* **`fun withStatusListener(listener: ((Flagship.Status) -> Unit)): T`**

  ```
  This method defines a new lambda in order to get a callback when the SDK status has changed.

  Parameter | Type    | Description
  --------- | ------- | -----------
  listener     | Flagship.Status -> Unit   |  lambda to invoke when SDK status have changed. **See [SDK status](./v3.0#status) section**.
  ```

  \\
* **`fun withLogLevel(level: LogManager.Level): T`**

  Specifies a log level to filter logs emitted by the SDK.

  | Parameter | Type                   | Description                                                                                                  |
  | --------- | ---------------------- | ------------------------------------------------------------------------------------------------------------ |
  | level     | LogManager.Level level | The levels in ascending order are : NONE(0), EXCEPTIONS(1), ERROR(2), WARNING(3), DEBUG(4), INFO(5), ALL(6). |

\\

* **`fun withTimeout(timeout: Int): T`**

  Specifies timeout for api requests.

  | Parameter | Type | Description                                                     |
  | --------- | ---- | --------------------------------------------------------------- |
  | timeout   | int  | Milliseconds for connect and read timeouts. Default is 2000 ms. |

\\

* **`fun withCacheManager(customCacheManager: CacheManager): T`**

  Specifies a custom cache manager implementation. **See**[**Cache management**](https://github.com/flagship-io/Gitbook/blob/main/sdks/Archived%20SDK%20Versions/archived-android/v3.0#cache-management) **section**\
  Parameter | Type | Description\
  \--------- | ------- | -----------\
  customCacheManager | CacheManager | Custom implementation.

\\

* **`fun withLogManager(customLogManager: LogManager): T`**

  Specifies a custom implementation of LogManager in order to receive logs from the SDK.

  | Parameter        | Type       | Description                          |
  | ---------------- | ---------- | ------------------------------------ |
  | customLogManager | LogManager | Custom implementation of LogManager. |

\\

***Only available for Bucketing Mode:***

* **`fun withPollingIntervals(time: Long, timeUnit: TimeUnit): Bucketing`**

  Define time interval between two bucketing updates. Default is 60 seconds. MICROSECONDS and NANOSECONDS Unit are ignored.

  | Parameter | Type     | Description                                         |
  | --------- | -------- | --------------------------------------------------- |
  | time      | Long     | time value.                                         |
  | timeUnit  | TimeUnit | time unit. Must be greater or equal to MILLISECONDS |

\\

**Decision Mode**

**`DecisionApi`Mode**

When the SDK is running in `DecisionApi` mode, the campaign assignments and targeting validation take place server-side. In this mode, each call to the `fetchFlags` method to refresh the flags will create an HTTP request.

**`Bucketing`Mode**

When the SDK is running in `Bucketing` mode, the SDK downloads all the campaigns configurations at once in a single bucketing file so that variation assignment can be computed client-side by the SDK. This bucketing file is stored in cache and will only be downloaded again when campaign configurations are modified in the Flagship interface. [**Learn more**](doc:bucketing)

\\

**SDK Status**

List of the possible SDK status :

| Status           | Description                                                                                                                              |
| ---------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
| NOT\_INITIALIZED | Flagship SDK has not been started or initialized successfully.                                                                           |
| STARTING         | Flagship SDK is starting.                                                                                                                |
| POLLING          | Flagship SDK has been started successfully but is still polling campaigns. (Only when Bucketing mode is used)                            |
| PANIC            | Flagship SDK is ready but is running in Panic mode: All visitor's features are disabled except 'fetchFlags' which refreshes this status. |
| READY            | Flagship SDK is ready to use.                                                                                                            |

\\

It is possible to get the current status via the method getStatus() from the Flagship class.

* **`fun getStatus(): Status`**

  Return the current SDK status

\\

### Create a new visitor

\\

The `visitor` instance is a helper object that lets you manage the context and campaigns for a user identified by a unique ID.

The user context is a property dataset that defines the current user of your app. This dataset is sent and used by the Flagship Decision API as targeting criteria for campaign assignment.

For example, if you want to enable or disable a specific feature based on VIP status, you would pass this attribute as a key-value pair in the user context so that the Decision API can enable or disable the corresponding feature flag for the user.

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1")
            .context(hashMapOf("age" to "32", "isVIP" to true))
            .hasConsented(true)
            .isAuthenticated(true)
            .build()
```

\\

* **`fun newVisitor(visitorId: String, instanceType: Visitor.Instance = Visitor.Instance.SINGLE_INSTANCE): Visitor.Builder`**

  Return a Visitor Builder class which aims to help creating new visitors.

  | Parameter    | Type             | Description                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  |
  | ------------ | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
  | visitorId    | String           | Unique visitor identifier.                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                   |
  | instanceType | Visitor.Instance | <p>How Flagship SDK should handle the newly created visitor instance. (Default is SINGLE\_INSTANCE)<br><br>SINGLE\_INSTANCE :<br>The newly created visitor instance will be returned and saved into the Flagship singleton. Call <code>Flagship.getVisitor()</code> to retrieve the instance. This option should be adopted on applications that handle only one visitor at the same time.<br><br>NEW\_INSTANCE:<br>The newly created visitor instance wont be saved and will simply be returned. Any previous visitor instance will have to be recreated. This option should be adopted on applications that handle multiple visitors at the same time.</p> |

  ```
    
  ```

**Visitor Builder methods :**

* **`fun isAuthenticated(isAuthenticated: Boolean): Builder`**

  Specify if the visitor is authenticated or anonymous. Default value is false.

| Parameter       | Type    | Default Value | Description                                       |
| --------------- | ------- | ------------- | ------------------------------------------------- |
| isAuthenticated | Boolean | false         | True for authenticated user, false for anonymous. |

* **`fun hasConsented(hasConsented: Boolean): Builder`**

  Specify if the visitor has consented to personal data usage. When false some features will be deactivated, cache will be deactivated and cleared. Default value is True.

| Parameter    | Type    | Default Value | Description                                        |
| ------------ | ------- | ------------- | -------------------------------------------------- |
| hasConsented | Boolean | true          | True when user has given consent, false otherwise. |

* **`fun context(context: HashMap<String, Any>): Builder`**

  Specifies visitor initial context key / values used for targeting.\
  Context keys must be String, and values types must be one of the following: Number, Boolean, String.

  | Parameter | Type                  | Description      |
  | --------- | --------------------- | ---------------- |
  | context   | HashMap\<String, Any> | Initial context. |

\\

> 🚧
>
> * User context keys must have a type of `String`
> * User context values must have a type of `String`, `Boolean`, `Number`.

\\

* **`public Visitor build()`**

  Return the newly created visitor.

\
\\

### Updating the visitor context

\\

The user context is a property dataset that defines the current user of your app. This dataset is sent and used by the Flagship Decision API as targeting criteria for campaign assignment.

The following method from the `Visitor` instance allows you to set new context values matching the given keys.

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1")
            .context(hashMapOf("age" to "32", "isVIP" to true))
            .hasConsented(true)
            .isAuthenticated(true)
            .build()

visitor1.updateContext("lastPurchaseDate", 1615384464)
```

\\

**`fun <T> updateContext(key: String, value: T)`**

Upsert the visitor context values, matching the given keys, used for targeting. Only String, Boolean, Number typed values are accepted.

| Parameter | Type   | Description    |
| --------- | ------ | -------------- |
| key       | String | Context key.   |
| value     | T      | Context value. |

\\

**`fun updateContext(context: HashMap<String, Any>)`**

Upsert the visitor context values, matching the given keys, used for targeting. Only String, Boolean, Number typed values are accepted.

| Parameter | Type                     | Description              |
| --------- | ------------------------ | ------------------------ |
| context   | HashMap\<String, Object> | HashMap of keys, values. |

\\

> 🚧
>
> * User context keys must have a type of `String`
> * User context values must have a type of `String`, `Boolean`, `Number`.

\\

**`fun <T> updateContext(flagshipContext: FlagshipContext<T>, value: T)`**

Upsert the visitor context values with Flagship predefined context key. \*\*[See FlagshipContext](https://github.com/flagship-io/Gitbook/blob/main/sdks/Archived%20SDK%20Versions/archived-android/v3.0#predefined-user-context-keys--flagshipcontext)

| Parameter       | Type            | Description            |
| --------------- | --------------- | ---------------------- |
| flagshipContext | FlagshipContext | Predefined context key |
| value           | T               | value to add.          |

\
\\

**`fun clearContext()`**

Clear all the visitor context values used for targeting.

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1")
            .context(hashMapOf("age" to "32", "isVIP" to true))
            .hasConsented(true)
            .isAuthenticated(true)
            .build()

visitor1.clearContext()
```

\\

**`getContext(): HashMap<String, Any>`**

Get visitor current context key / values.

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1")
            .context(hashMapOf("age" to "32", "isVIP" to true))
            .hasConsented(true)
            .isAuthenticated(true)
            .build()

val context = visitor1.getContext()
```

\
\\

### Managing visitor campaigns and their flags

\\

#### Fetching Flags

The `fetchFlags()` method of the `visitor` instance automatically updates the campaign assignments according to the current user context and retrieves applicable flags.

These flags are then stored in the SDK and updated asynchronously when `fetchFlags()` is called.

\\

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1").build()

visitor1.updateContext("postcode", "31200");

visitor1.fetchFlags().await()
```

\\

**`fun fetchFlags(): Deferred<Unit>`**

This function will call the decision api and update all the campaigns flags from the server according to the visitor context.

| Return           | Description                                  |
| ---------------- | -------------------------------------------- |
| `Deferred<Unit>` | Return a Deferred to manage sync/async call. |

\\

#### Getting flags

Once the campaign has been **assigned** and **fetched**, all the flags are stored in the SDK. You can retrieve these flags using the following functions from the `Visitor` instance:

\\

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1").build()
visitor1.updateContext("isVip", true)
visitor1.fetchFlags().invokeOnCompletion { 
	val displayVipFeatureFlag = visitor1.getFlag("displayVipFeature", false)
}
```

\\

**`fun <T> getFlag(key: String, defaultValue: T?): Flag`**

Retrieve a Flag object by its key. If no flag match the given key an empty flag will be returned. Call exists() to check if the flag has been found.

| Parameter    | Type   | Description                 |
| ------------ | ------ | --------------------------- |
| key          | String | key associated to the flag. |
| defaultValue | T      | flag default value.         |

\\

> 🚧
>
> * Default value must be one of the following type : `String`, `Boolean`, `Number`, `JSONArray`, 'JSONObject'.

#### Getting flags current values

To retrieve flag current value, simply call value() method of the Flag object.

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1").build()
visitor1.updateContext("isVip", true)
visitor1.fetchFlags().invokeOnCompletion { 
	val displayVipFeatureFlag = visitor1.getFlag("displayVipFeature", false).value()
        // display or not the vip feature
}
```

**`fun value(userExposed: Boolean = true): T?`**

Returns the value from the assigned campaign variation or the Flag default value if the Flag does not exist, or if types are different.

| Field       | Type    | Default value                                                                                                                                                                                                                                                         | Description |
| ----------- | ------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ----------- |
| userExposed | Boolean | Tells Flagship the user have been exposed and have seen this flag. This will increment the visits for the current variation on your campaign reporting. If needed it is possible to set this param to false and call `userExposed()` afterward when the user sees it. |             |

#### Getting flags campaigns metadata

You may need to send campaign IDs or variation IDs to a third party for reporting and/or analytics purposes. It is possible to retrieve campaigns metadata for a specific Flag.

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1").build()
visitor1.updateContext("isVip", true)
visitor1.fetchFlags().invokeOnCompletion { 
  val campaignMetadata = visitor1.getFlag("displayVipFeature", false).metadata()
}
```

**`fun metadata() : FlagMetadata`**

Return the campaign information metadata or an empty object if the Flag doesn't exist or if the default value type does not correspond to the Flag type in Flagship.

**FlagMetadata**

| Field            | Type    | Description                                     |
| ---------------- | ------- | ----------------------------------------------- |
| campaignId       | String  | Campaign identifier the flag belongs to.        |
| variationGroupId | String  | Variation group identifier the flag belongs to. |
| variationId      | String  | Variation identifier the flag belongs to.       |
| isReference      | Boolean | Is the flag from the original variation.        |
| campaignType     | String  | Type of the flag campaign.                      |
| slug             | String  | Campaign custom id.                             |

#### Report a Flag exposition

By default when the method value() is called, The SDK considers that the user have seen the effets of your Flag, unless you pass false to value(). In this case you will have to call userExposed().

There are two options for exposing a user to a flag:

1. Pass an `userExposed=true` parameter to the **value()** method.
2. Use the following **userExposed()** method from the Flag instance.

\\

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1").build()
visitor1.updateContext("isVip", true)
visitor1.fetchFlags().invokeOnCompletion { 
	val displayVipFeature = visitor1.getFlag("displayVipFeature", true).value() // userExposed true by default
  
    // or
 
	val displayVipFeature = visitor1.getFlag("displayVipFeature", false).value(false)
  visitor1.userExposed();
	
}
```

\\

**`fun userExposed()`**

Tells Flagship the user have been exposed and have seen this flag. This will increment the visits for the current variation on your campaign reporting. No user exposition will be sent if the Flag doesn't exist or if the default value type do not correspond to the Flag type in Flagship.

\\

#### Check if a Flag exists

**`fun exists()`**

This method will return true if a Flag exists in Flagship

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1").build()
visitor1.updateContext("isVip", true)
visitor1.fetchFlags().invokeOnCompletion { 
  val isDisplayVipFeatureExists = visitor1.getFlag("do_not_exists", false).exists()
}
```

\
\\

### Managing visitor consent

The Visitor class provides a method to let you manage visitor consent for data privacy usage. When False, ***campaign activation*** and ***hits*** will be disabled and cache cleared.

**`fun setConsent(hasConsented: Boolean)`**

| Parameter    | Type    | Default Value | Description                                                                                                                      |
| ------------ | ------- | ------------- | -------------------------------------------------------------------------------------------------------------------------------- |
| hasConsented | Boolean | true          | Set visitor consent for private data usage. When false some features will be deactivated, cache will be deactivated and cleared. |

```kotlin
val visitor1 = Flagship.newVisitor("visitor_1").build()
  visitor1.setConsent(false);
```

> 🚧
>
> When consent is not given: Hits, Activations will be deactivated and all the cached visitor data will be cleared until consent is given again.\
> Only consent tracking requests will enabled in order to clear server-side cached data.

\
\\

### Managing visitor cache

By default the Flagship Android SDK provide a default cache manager implementation. This helps to cache visitors data, to cache non sent hits due to internet failures or server errors, to cache campaign assignations for offline mode and to prevent reallocation in Bucketing mode. Indeed as the assignation is made on local device in bucketing mode, changing campaign allocation in the platform would make visitors to see different campaigns.

The default cache manager use a local database.

It is possible to provide a custom cache implementation using the following abstract class and the specific interfaces :

```kotlin
Flagship.start(getApplication(), "_ENV_ID_", "_API_KEY_", FlagshipConfig.DecisionApi()
            .withCacheManager(CacheManager.Builder()
                .withVisitorCacheLookupTimeout(200)
                .withHitCacheLookupTimeout(200)
                .withVisitorCacheImplementation(object : IVisitorCacheImplementation {
                    override fun cacheVisitor(visitorId: String, data: JSONObject) {
                        // Upsert in your database
                    }

                    override fun lookupVisitor(visitorId: String): JSONObject {
                        // Load & delete from your database
                        return JSONObject()
                    }

                    override fun flushVisitor(visitorId: String) {
                        // Clear from your database
                    }
                })
                .withHitCacheImplementation(object : IHitCacheImplementation {
                    override fun cacheHit(visitorId: String, data: JSONObject) {
                        // Insert in your database
                    }

                    override fun lookupHits(visitorId: String): JSONArray {
                        // Delete and load from your database
                        return JSONArray()
                    }

                    override fun flushHits(visitorId: String) {
                        // Clear from your database
                    }

                })
                .build())
        )
```

\
\\

**`interface IVisitorCacheImplementation`**

\\

This interface specifies the methods to implement in order to cache visitors information.

\\

**fun cacheVisitor(visitorId : String, data : JSONObject)**

This method is called when the SDK needs to cache visitor information in your database.

| Parameter | Type       | Description                                                  |
| --------- | ---------- | ------------------------------------------------------------ |
| visitorId | String     | Visitor unique identifier from which data need to be cached. |
| data      | JSONObject | data to cache                                                |

\\

**fun lookupVisitor(visitorId: String) : JSONObject**

This method is called when the SDK needs to get visitor information from your database. This method will block the current thread so it will be called with a configurable timeout. **See**[**Cache format**](https://github.com/flagship-io/Gitbook/blob/main/sdks/Archived%20SDK%20Versions/archived-android/v3.0##cache-data-format) **section**.

| Parameter | Type   | Description                                                  |
| --------- | ------ | ------------------------------------------------------------ |
| visitorId | String | Visitor unique identifier from which data need to be loaded. |

Return : visitor information json respecting the expected format.

\\

**fun flushVisitor(visitorId: String)**

This method is called when the SDK needs to flush visitor information in your database. For example when the user hasn't given his consent this method will be called.

| Parameter | Type   | Description                                                   |
| --------- | ------ | ------------------------------------------------------------- |
| visitorId | String | Visitor unique identifier from which data need to be cleared. |

\
\\

**`interface IHitCacheImplementation`**

\\

This interface specifies the methods to implement in order to cache visitors hits when they failed to be sent.

\\

**fun cacheHit(visitorId : String, data : JSONObject)**

This method is called when the SDK needs to cache visitor hits in your database.

| Parameter | Type       | Description                                                  |
| --------- | ---------- | ------------------------------------------------------------ |
| visitorId | String     | Visitor unique identifier from which hits need to be cached. |
| data      | JSONObject | data to cache                                                |

\\

**fun lookupHits(visitorId: String) : JSONObject**

This method is called when the SDK needs to get visitor hits from your database in order to send them again. This method will block the current thread so it will be called with a configurable timeout. **See**[**Cache format**](https://github.com/flagship-io/Gitbook/blob/main/sdks/Archived%20SDK%20Versions/archived-android/v3.0##cache-data-format) **section**.

| Parameter | Type   | Description                                                  |
| --------- | ------ | ------------------------------------------------------------ |
| visitorId | String | Visitor unique identifier from which hits need to be loaded. |

Return : visitor hits json array respecting the expected format.

> 🚧 Hits must be deleted
>
> Cached hits must be deleted before being returned from `lookupHits` so it prevents hits cache duplication if they failed to be sent again.

\\

**fun flushHits(visitorId: String)**

This method is called when the SDK needs to flush visitor hits in your database. For example when the user hasn't given his consent this method will be called.

| Parameter | Type   | Description                                                   |
| --------- | ------ | ------------------------------------------------------------- |
| visitorId | String | Visitor unique identifier from which hits need to be cleared. |

\
\\

### Experience Continuity

Dealing with anonymous and logged-in users, experience continuity allows you to maintain consistency between sessions and devices.

> 🚧
>
> Make sure that the experience continuity option is enabled on the flagship platform before using those methods.

#### Authenticate

There are 2 ways to authenticate a visitor:

1. Set key isAuthenticated to true when creating a new visitor
2. Use authenticate method of `Visitor` instance

Authenticate anonymous visitor

**`fun authenticate(visitorId: String)`**

| Parameter | Type   | Description                          |
| --------- | ------ | ------------------------------------ |
| visitorId | String | id of the new authenticated visitor. |

> 🚧
>
> Because we have changed the visitor data, we have to call the **fetchFlags method** after calling this one to update the decision from Flagship.
>
> The targeting / Flags could be different for the visitor.

#### Unauthenticate

This function change authenticated Visitor to anonymous visitor

**`fun unauthenticate()`**

> 🚧
>
> Because we have changed the visitor datas, we have to call the **fetchFlags method** after calling this one to update the decision from Flagship.
>
> The targeting / Flags could be different for the visitor.

#### Code example

Let's assume basic scenario to understand how things work:

**1. Your visitor arrives on your app for the first time.**

We need to initialize the visitor but as we don't know anything about this visitor, we'll create a random visitor id or let the SDK do it for us. You can also specify some visitor context if necessary.

```kotlin
val visitor = Flagship.newVisitor("anonymous_001")
            .context(hashMapOf("key" to "value"))
            .build()

visitor.fetchFlags()
```

Here we set an anonymous visitorId property so the SDK or an empty string for auto-generated id for our visitor.

Regardless of how it has been set, the actual visitor id will be what we call the **anonymous id**.

**2. Your visitor is signing in.**

To tell the SDK about this status modification, you'll have to call the authenticate function which takes the required visitor id as argument.

```kotlin
// Once you fetch the visitorId from your DB

visitor.authenticate(visitorId)

// Since your visitor has changed (is now logged-in)
// You have to check if the proper targeting and flags are set

visitor.fetchFlags()
```

The visitor is updated as authenticated, keeping the previous variations from campaigns that are still matched and thus gives you same flags as before being logged in.

> 📘
>
> Keep in mind that if the visitor also has its context changed, you might still have changes on flags as your visitor might target new campaigns.

**3. Your visitor decides to sign out.**

If you want to keep the same visitor experience, then you should do:

```kotlin
visitor.unauthenticate()

// Since your visitor has changed (is now logged-out)
// You have to check if the proper targeting and flags are set

visitor.fetchFlags()
```

#### Final implementation example

```kotlin
//The current visitor is anonymous

val visitor = Flagship.newVisitor("anonymous_001")
            .context(hashMapOf("key" to "value"))
            .build()

visitor.fetchFlags()


//Once the visitor logs in, and you can get his unique id:

visitor.authenticate(visitorId)
visitor.fetchFlags()

//And When he logs out:

visitor.unauthenticate()
visitor.fetchFlags()
```

### Hit Tracking

\\

This section helps you track your users in your application and learn how to build hits in order to feed campaign goals. For more information about our measurement protocol, read our [Universal Collect documentation](doc:universal-collect-documentation).

There are five different types of Hits available:

* Page
* Screen
* Transaction
* Item
* Event

**They must all be built and sent with the following function from the`Visitor` instance:**

\\

```kotlin
visitor1.sendHit(Page("https://www.my_domain_com/my_page"))
```

\\

**`fun <T> sendHit(hit: Hit<T>)`**

Send hits as objectives in your campaign reporting.

| Parameter | Type | Description  |
| --------- | ---- | ------------ |
| hit       | Hit  | Hit to send. |

\\

#### Hit common optional parameters

\\

```java
val screen = Screen("screen location")
                .withResolution(200, 100)
                .withLocale("fr_FR")
                .withIp("127.0.0.1")
                .withSessionNumber(2)
 visitor.sendHit(screen)
```

\\

| Parameter         | Type     | Description                  |
| ----------------- | -------- | ---------------------------- |
| withIp            | String   | Optional. User IP            |
| withResolution    | int, int | Optional. Screen resolution. |
| withLocale        | String   | Optional. User language      |
| withSessionNumber | int      | Optional. Session number     |

\\

#### Page

\\

This hit should be sent each time a visitor visits a new local or web page in an embedded web view.

```kotlin
val page = Page("https://www.my_domain_com/my_page")

visitor1.sendHit(page);
```

\\

**\`class Page(location : String)**

| Builder Parameter | Type   | Description |
| ----------------- | ------ | ----------- |
| location          | String | Valid url.  |

\\

#### Screen

\\

This hit should be sent each time a visitor arrives on an interface on the client side.

```kotlin
val screen = Screen("your_screen_name")

visitor1.sendHit(screen)
```

\\

**`class Screen(location : String)`**

| Builder Parameter | Type   | Description     |
| ----------------- | ------ | --------------- |
| location          | String | Interface name. |

\\

#### Transaction

\\

This hit should be sent when a user complete a Transaction.

```kotlin
val transaction = Transaction("#12345", "affiliation")
                .withCouponCode("code")
                .withCurrency("EUR")
                .withItemCount(1)
                .withPaymentMethod("creditcard")
                .withShippingCosts(9.99f)
                .withTaxes(19.99f)
                .withTotalRevenue(199.99f)
                .withShippingMethod("1day")
visitor1.sendHit(transaction)
```

\\

**`class Transaction(transactionId : String, affiliation : String)`**

| Builder Parameter  | Type   | Description                                                                                                                        |
| ------------------ | ------ | ---------------------------------------------------------------------------------------------------------------------------------- |
| transactionId      | String | Unique identifier for your transaction.                                                                                            |
| affiliation        | String | The name of the KPI that you will have inside your reporting. [**Learn more**](doc:glossary#kpi)                                   |
| withTotalRevenue   | float  | (optional) Specifies the total revenue associated with the transaction. This value should include any shipping and/or tax amounts. |
| withShippingCosts  | float  | (optional) The total shipping cost of your transaction.                                                                            |
| withShippingMethod | String | (optional) The shipping method for your transaction.                                                                               |
| withTaxes          | float  | (optional) Specifies the total amount of taxes in your transaction.                                                                |
| withCurrency       | String | (optional) Specifies the currency of your transaction. NOTE: This value should be a valid ISO 4217 currency code.                  |
| withPaymentMethod  | String | (optional) Specifies the payment method used for your transaction.                                                                 |
| withItemCount      | int    | (optional) Specifies the number of items in your transaction.                                                                      |
| withCouponCode     | String | (optional) Specifies the coupon code used by the customer in your transaction.                                                     |

\\

#### Item

\\

This hit is used to link an item with a transaction. It must be sent after the corresponding transaction hit.

```kotlin
val item = Item("#12345", "product", "sku123")
                .withItemCategory("test")
                .withItemPrice(199.99f)
                .withItemQuantity(1)
visitor1.sendHit(item)
```

\\

**`class Item(transactionId : String, productName : String, productSku : String)`**

| Builder Parameter | Type   | Description                                                 |
| ----------------- | ------ | ----------------------------------------------------------- |
| transactionId     | String | Unique identifier for your transaction.                     |
| productName       | String | Name of your item.                                          |
| productSku        | String | Specifies the SKU or item code.                             |
| withItemCategory  | String | (optional) Specifies the category that the item belongs to. |
| withItemPrice     | float  | (optional) Specifies the price for a single item/unit.      |
| withItemQuantity  | int    | (optional) Specifies the number of items purchased.         |

\\

> 📘
>
> The `Item` hit isn't available yet in the Flagship reporting view.

\\

#### Event

This hit can be used for any event (e.g. Add To Cart click, newsletter subscription).

\\

```kotlin
val event = Event(Event.EventCategory.USER_ENGAGEMENT, "action")
                .withEventLabel("label")
                .withEventValue(100)
visitor1.sendHit(event)
```

\\

**`class Event(val category: EventCategory, val action : String)`**

| Builder Parameter | Type          | Description                                                                                                                                                                                     |
| ----------------- | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| category          | EventCategory | Specifies the category of your event. NOTE: This value must be either `'ACTION_TRACKING'` or `'USER_ENGAGEMENT'`.                                                                               |
| action            | String        | Event name that will also serve as the KPI that you will have inside your reporting. [**Learn more**](doc:glossary#kpi)                                                                         |
| withEventLabel    | String        | (optional) Additional description of your event.                                                                                                                                                |
| withEventValue    | Int           | (optional) Specifies the monetary value associated with an event (e.g. you earn 10 to 100 euros depending on the quality of lead generated). NOTE: this value must be non-negative integer > 0. |

\\

### Appendix

#### Predefined user context keys

The Flagship SDK contains predefined user context keys.

The keys marked as **Yes** in the **Auto-set by SDK** column will be automatically set, while the ones marked as **No** need to be set by the client.

> 📘
>
> Check the values sent by the SDK by enabling the logs.

You can overwrite these keys at any time. The key-value pairs will be sent to the server in the user context and can be edited in the [Persona](doc:glossary#persona) section of the Flagship platform.

| SDK Variable Name              | Description                                            | Context variable name                 | Type    | Auto-set by SDK | Example                                      |
| ------------------------------ | ------------------------------------------------------ | ------------------------------------- | ------- | --------------- | -------------------------------------------- |
| FIRST\_TIME\_INIT              | First init of the app                                  | sdk\_firstTimeInit                    | Boolean | Yes             | TRUE (FALSE if the init isn't the first one) |
| LOCALE                         | Language of the device                                 | sdk\_deviceLanguage                   | String  | Yes             | fr\_FR                                       |
| DEVICE\_TYPE                   | Tablette / Mobile                                      | sdk\_deviceType                       | String  | Yes             | mobile                                       |
| DEVICE\_MODEL                  | Model of the device                                    | sdk\_deviceModel                      | String  | Yes             | samsung E1200                                |
| LOCATION\_CITY                 | City geolocation                                       | sdk\_city                             | String  | No              | toulouse                                     |
| LOCATION\_REGION               | Region geolocation                                     | sdk\_region                           | String  | No              | occitanie                                    |
| LOCATION\_COUNTRY              | Country geolocation                                    | sdk\_country                          | String  | No              | France                                       |
| LOCATION\_LAT                  | Current Latitude                                       | sdk\_lat                              | Double  | No              | 43.623647                                    |
| LOCATION\_LONG                 | Current Longitude                                      | sdk\_long                             | Double  | No              | 1.445397                                     |
| IP                             | IP of the device                                       | sdk\_ip                               | String  | No              | 127.0.0.1                                    |
| OS\_NAME                       | Name of the OS                                         | sdk\_osName                           | String  | Yes             | android / iOS                                |
| OS\_VERSION                    | Version of the OS                                      | sdk\_osVersion                        | String  | Yes             | pie                                          |
| API\_LEVEL                     | Version of the API                                     | sdk\_apiLevel                         | Integer | Yes             | 24                                           |
| ANDROID\_VERSION / IOS VERSION | Version of Android                                     | sdk\_androidVersion / sdk\_iOSVersion | String  | Yes             | 9                                            |
| MVNO / carrierName             | Name of the carrier or mobile virtual network operator | sdk\_carrierName                      | String  | Yes             | orange                                       |
| DEV\_MODE                      | Is the app in debug mode?                              | sdk\_devMode                          | Boolean | Yes             | TRUE                                         |
| VISITOR\_ID                    | Visitor\_id of the user                                | sdk\_visitorId                        | String  | Yes             | toto2000                                     |
| INTERNET\_CONNECTION           | What is the internet connection                        | sdk\_internetConnection               | String  | No              | 3g                                           |
| APP\_VERSION\_NAME             | Version name of the app                                | sdk\_versionName                      | String  | No              | 1.1.2-beta                                   |
| APP\_VERSION\_CODE             | Version code of the app                                | sdk\_versionCode                      | Integer | No              | 40                                           |
| FLAGSHIP\_VERSION              | Version of the Flagship SDK                            | sdk\_fsVersion                        | String  | Yes             | 1.1.2                                        |
| INTERFACE\_NAME                | Name of the interface                                  | sdk\_interfaceName                    | String  | No              | ProductPage                                  |

> 📘
>
> To overwrite the keys, use the [`updateContext`](#update-context-with-predefined-keys-of-context) method

\\

#### Cache data format

**IVisitorCacheImplementation lookup format**

```json
{
  "version": 1,
  "data": {
    "visitorId": "visitor_1",
    "consent": true,
    "context": {
      "fs_users": "visitor_1",
      "sdk_osVersionCode": 31,
      "sdk_deviceModel": "Google Pixel 3",
      "fs_client": "android",
      "fs_version": "3.0.0",
      "sdk_carrierName": "Free",
      "sdk_osVersionName": "Android 12 S",
      "sdk_deviceType": "mobile",
      "plan": "enterprise",
      "sdk_deviceLanguage": "fra",
      "sdk_osName": "Android"
    },
    "assignmentsHistory": {
      "xxxxxxxxxxxxxxxxxxxx": "xxxxxxxxxxxxxxxxxxxx",
      "yyyyyyyyyyyyyyyyyyyy": "yyyyyyyyyyyyyyyyyyyy"
    },
    "campaigns": [
      {
        "campaignId": "xxxxxxxxxxxxxxxxxxxx",
        "variationGroupId": "xxxxxxxxxxxxxxxxxxxx",
        "variationId": "xxxxxxxxxxxxxxxxxxxx",
        "isReference": false,
        "type": "",
        "activated": false,
        "flags": {
          "key": "value"
        }
      },
      {
        "campaignId": "xxxxxxxxxxxxxxxxxxxx",
        "variationGroupId": "xxxxxxxxxxxxxxxxxxxx",
        "variationId": "xxxxxxxxxxxxxxxxxxxx",
        "isReference": false,
        "type": "",
        "activated": false,
        "flags": {
          "myAwesomeFeature": 20
        }
      }
    ]
  }
}
```

**IHitCacheImplementation lookup format**

```json
{
  "version": 1,
  "data": {
    "time": 1637606817507,
    "visitorId": "visitor_1",
    "anonymousId": "e436e87f-cc61-44c6-ab40-fba15aec0af5",
    "type": "ACTIVATION",
    "content": {
      "cid": "xxxxxxxxxxxxxxxxxxxx",
      "caid": "xxxxxxxxxxxxxxxxxxxx",
      "vaid": "xxxxxxxxxxxxxxxxxxxx",
      "aid": "e436e87f-cc61-44c6-ab40-fba15aec0af5",
      "vid": "visitor_1"
    }
  }
}
```
