> ## Documentation Index
> Fetch the complete documentation index at: https://documentation.qonversion.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Android 4.+ migration guide

## Upgrading version

Increase the dependency version in your app *build.gradle* file to upgrade your Qonversion SDK to the latest version

<CodeGroup>
  ```groovy build.gradle theme={null}
  dependencies {    
       implementation 'io.qonversion.android.sdk:sdk:4.+'
  }
  ```
</CodeGroup>

## Initialization

Qonversion Android SDK 4 contains significant changes in how the library is initialized. We are moving from a singletons approach to the instance-based one. Before, you initialized Qonversion using the `launch` call:

<CodeGroup>
  ```kotlin Kotlin theme={null}
  class App : Application() {
      override fun onCreate() {
          super.onCreate()
          Qonversion.launch(this, "projectKey", false)
      }
  }
  ```

  ```java Java theme={null}
  public class App extends Application {
      @Override
      public void onCreate() {
          super.onCreate();
          Qonversion.launch(this, "projectKey", false);
      }
  }
  ```
</CodeGroup>

Now, instead, you should create a `QonversionConfig` object using nested `Builder` and provide it to initialization method as follows:

<CodeGroup>
  ```kotlin Kotlin theme={null}
  class App : Application() {
      override fun onCreate() {
          super.onCreate()
          val qonversionConfig = QonversionConfig.Builder(
              this,
              "projectKey",
              QLaunchMode.SubscriptionManagement
          ).build()
          Qonversion.initialize(qonversionConfig)
      }
  }
  ```

  ```java Java theme={null}
  public class App extends Application {
      @Override
      public void onCreate() {
          super.onCreate();
          final QonversionConfig qonversionConfig = new QonversionConfig.Builder(
                  this,
                  "projectKey",
                  QLaunchMode.Infrastructure
          ).build();
          Qonversion.initialize(qonversionConfig);
      }
  }
  ```
</CodeGroup>

Note that instead of providing the `observeMode` flag to the `launch` call, you should provide a concrete value from the `QLaunchMode` enum, depending on which mode you use Qonversion. Also, we've renamed our modes to make them more transparent for users:

* "Observe" mode becomes "Analytics" mode,
* "Infrastructure" mode becomes "Subscription Management" mode.

After the initialization, you can access the Qonversion instance whenever you want as follows:

<CodeGroup>
  ```kotlin Kotlin theme={null}
  Qonversion.shared
  ```

  ```java Java theme={null}
  Qonversion.getSharedInstance();
  ```
</CodeGroup>

So you should replace all your Qonversion calls with the construction above.

Also, if you were using `Qonversion.setDebugMode()` for testing purposes, you should now call the `setEnvironment(QEnvironment.Sandbox)` method of the `QonversionConfig.Builder`.

As no `launch` method is available anymore, you won't get `QLaunchResult` as a result. The good news is that there are analogues for all the fields you might have been using from there:

* for `uid`, call `userInfo()` and get the `QUser.qonversionId` from the result,
* for `products` call `products()`,
* for `offerings` call `offerings()`,
* for `permissions` call `checkEntitlements()`,
* `experiments` field has no analogue, as the `experiments` method was removed for now.

## Entitlements

We are on the way to renaming permissions to entitlements as this naming suits more what it is used for. So, the following objects and methods were renamed in this release:

| Version `<4`                  | Version 4+                     |
| ----------------------------- | ------------------------------ |
| QPermission                   | QEntitlement                   |
| QonversionPermissionsCallback | QonversionEntitlementsCallback |
| QProductRenewState            | QEntitlementRenewState         |
| QPermissionSource             | QEntitlementSource             |
| QPermissionsCacheLifetime     | QEntitlementsCacheLifetime     |
| checkPermissions              | checkEntitlements              |
| UpdatedPurchasesListener      | QEntitlementsUpdateListener    |

The `QEntitlement` class contains the same information as the `QPermission` with small renamings.

* `permissionsID` was renamed to `id`,
* `productID` was renamed to `productId`,
* `isActive` became a Kotlin property, so it should now be accessed as a property.

There is no `setUpdatedPurchasesListener` method in Qonversion. You should provide `UpdatedEntitlementsListener` to `QonversionConfig.Builder` during the initialization using the `setUpdatedEntitlementsListener` method. The same change is made to the `setPermissionsCacheLifetime` method. Now you can set the required lifetime using `QonversionConfig.Builder.setEntitlementsCacheLifetime` while Qonversion initialization.

<CodeGroup>
  ```kotlin Kotlin theme={null}
  val qonversionConfig = QonversionConfig.Builder(
      this,
      "projectKey",
      QLaunchMode.SubscriptionManagement
  )
      .setEntitlementsUpdateListener(object : EntitlementsUpdateListener {
          override fun onEntitlementsUpdated(entitlements: Map<String, QEntitlement>) {
              // handle updated entitlements
          }
      })
      .setEntitlementsCacheLifetime(QEntitlementsCacheLifetime.Year)
      .build()
  Qonversion.initialize(qonversionConfig)
  ```

  ```java Java theme={null}
  final QonversionConfig qonversionConfig = new QonversionConfig.Builder(
          this,
          "projectKey",
          QLaunchMode.Infrastructure
  )
          .setEntitlementsCacheLifetime(QEntitlementsCacheLifetime.Year)
          .setEntitlementsUpdateListener(entitlements -> {/* handle updated entitlements */})
          .build();
  Qonversion.initialize(qonversionConfig);
  ```
</CodeGroup>

## Automation changes

We've also changed the way the `Automations` is used. As in Qonversion, you should use `Automations` via the shared instance. On the first access, it will be initialized and returned. Then the initialized instance will be used.

<Info>
  ### You should access the shared instance of `Automations` strictly after you initialize Qonversion, else an exception will be thrown.
</Info>

<CodeGroup>
  ```kotlin Kotlin theme={null}
  public class App : Application {
      override fun onCreate() {
          super.onCreate()
          val qonversionConfig = QonversionConfig.Builder(
              this,
              "projectKey",
              QLaunchMode.SubscriptionManagement
          ).build()
          Qonversion.initialize(qonversionConfig)
          Automations.shared.setDelegate(...)
      }
  }
  ```

  ```java Java theme={null}
  public class App extends Application {
      @Override
      public void onCreate() {
          super.onCreate();
          final QonversionConfig qonversionConfig = new QonversionConfig.Builder(
                  this,
                  "projectKey",
                  QLaunchMode.Infrastructure
          ).build();
          Qonversion.initialize(qonversionConfig);
          Automations.getSharedInstance().setDelegate(...);
      }
  }
  ```
</CodeGroup>

Also, the methods for working with push notifications were moved from `Qonversion` to `Automations`, so if you were using the following methods:

* `setNotificationsToken`,
* `handleNotification`,
* `getNotificationCustomPayload`, make sure to make calls from the Automations instance instead of the Qonversion one.

## Rest of the changes

Along with the changes described above, there are several technical improvements and other changes in the new major release:

* all the internal classes and extensions are now marked as `internal`, so they won't be accessible from your code and won't pollute your project namespace (note that they are still accessible in java because of interoperability issues, but you should prevent using them as well). All these classes and extensions were moved to the `internal` package to make the library file structure more readable;
* the deprecated methods `resetUser`, `setUserId`, and `handleNotification(RemoteMessage)` were removed. `resetUser` was deprecated for a long time, and it did nothing, so there is nothing to replace this call with, remove the method if you were still using it for some reason. `setUserId` should be replaced with the `setProperty` call with `QUserProperties.CustomUserId` parameter. Instead of using the `handleNotification` method, accepting `RemoteMessage`, call the method of the same name, accepting the data map (`RemoteMessage.data`). Also note that the `handleNotification(Map<String, String>)` method was moved to the `Automations` class, as described above;
* the `firebase-messaging` dependency causing resolution collisions was removed;
* the `experiments` method was removed - we are now working on a new design of A/B experiments;
* `QEntitlementsCacheLifetime` enum values are rewritten in CamelCase to match the rest code style;
* the `AttributionSource` enum was renamed to `QAttributionProvider` and moved to the `dto` package;
* the `UserProperties` enum was renamed to the `QUserProperty` enum and moved to the `dto` package;
* the `checkTrialIntroEligibilityForProductIds` method was shortened to `checkTrialIntroEligibility`;
* added the new method `userInfo`, which returns the information about the current Qonversion user. Now it contains internal Qonversion and identity identifiers. The user info may be extended in future releases;
* added new enum values - `QOfferingTag.Unknown` and `QTrialDuration.Unknown`, which are used when parsing fails. `QTrialDuration.Unknown` is now a default value for the `QProduct.trialDuration` field, which is no longer nullable.

***

[iOS 3.+ migration guide](ios-3-migration-guide)

[Flutter 5.+ migration guide](flutter-5-migration-guide)
