Skip to main content

7.0.0

Overview

The RuStore In-app Updates SDK ensures that users have the most up-to-date version of your application on their devices. This helps users experience the latest features, benefit from performance improvements, and receive bug fixes.

Implementation Example

Use the RuStore In-app Updates SDK to implement various update methods. Currently supported methods include: deferred, silent (without RuStore UI), and forced updates.

Refer to the example application to learn how to properly integrate the Updates SDK.

User Scenarios

Integrating into Your Project

Add the repository as shown in the example below.

build.gradle
repositories {
maven {
url = uri("https://artifactory-external.vkpartner.ru/artifactory/maven")
}
}

Using the BOM file allows you to manage multiple RuStore SDK versions.

Advantages of using the BOM file for configuration:

  1. Unified Version Management:

    • With the BOM, you can control the versions of all dependencies from a single file. This is especially helpful when using multiple libraries that need to be compatible with each other.
    • For instance, if you have several RuStore libraries like ru.rustore.sdk:billingclient and ru.rustore.sdk:pushclient, you can use the BOM to ensure that they are all compatible.
  2. Simplified Updates:

    • Updating dependencies becomes simpler because you only need to change the version in one place — in the BOM file. This reduces the risk of missing an update for a dependency and avoids version conflicts.
    • For example, if a new version of the BOM contains updated versions of all libraries, you only need to update the BOM file, not each dependency individually.
  3. Enhanced Compatibility:

    • Using the BOM helps to avoid version conflicts between different libraries. This is particularly important when libraries have dependencies on each other.
    • For instance, if two libraries depend on different versions of the same library, this can cause conflicts. The BOM helps to avoid these situations by ensuring that all dependencies are compatible.
build.gradle
dependencies {
implementation(platform("ru.rustore.sdk:bom:7.0.0"))
implementation("ru.rustore.sdk:appupdate")
}

Creating an Update Manager

Before calling the library methods, you must create an update manager.

val updateManager = RuStoreAppUpdateManagerFactory.create(context)

Checking for Updates

Before requesting an update, verify if an update is available for your app. To check for updates, call the getAppUpdateInfo() method. This method checks the following conditions:

  • The user's device has the latest version of RuStore installed.
  • The user and the application must not be blocked in RuStore.
  • The RuStore app is authorized to install applications.
  • The user is logged into RuStore.

This method returns an AppUpdateInfo object containing information about the need for an update. Request this object in advance and cache it to prompt the user to start downloading the update without delay and at a time convenient for the user.

ruStoreAppUpdateManager
.getAppUpdateInfo()
.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.updateAvailability == UpdateAvailability.UPDATE_AVAILABLE) {
// An update is available (you can register a listener and start the download here)
}
}
.addOnFailureListener { throwable ->
Log.e(TAG, "getAppUpdateInfo error", throwable)
}

The AppUpdateInfo object includes a set of parameters needed to determine update availability.

updateAvailability — update availability:

  • UNKNOWN (int == 0) — default;
  • UPDATE_NOT_AVAILABLE (int == 1) — no update needed;
  • UPDATE_AVAILABLE (int == 2) — update required to download or update already downloaded on the user's device;
  • DEVELOPER_TRIGGERED_UPDATE_IN_PROGRESS (int == 3) — update is already downloading or installation has already started.

installStatus — the status of the update installation if the user is already installing the update at the current time:

  • UNKNOWN (int == 0) — default;
  • DOWNLOADED (int == 1) — downloaded;
  • DOWNLOADING (int == 2) — downloading;
  • FAILED (int == 3) — error;
  • PENDING (int == 5) — pending.
Note

Starting the update download is only possible if the updateAvailability field contains the value UPDATE_AVAILABLE.

Downloading and Installing Updates

Using a Listener

After confirming the availability of the update (AppUpdateInfo), you can request the download status of the update - for this, start a listener for the download status.

Checking the Download Status

Use the registerListener() method.

ruStoreAppUpdateManager.registerListener { state ->
when (state.installStatus) {
InstallStatus.DOWNLOADED -> {
// Update is ready to be installed
}
InstallStatus.DOWNLOADING -> {
val totalBytes = state.totalBytesToDownload
val bytesDownloaded = state.bytesDownloaded
// Here you can display the download progress
}
InstallStatus.FAILED -> {
Log.e(TAG, "Downloading error")
}
}
}

The state object describes the current download status. The content of the object is presented below.

installStatus — the status of the update installation if the user is already installing the update at the current time:

  • UNKNOWN (int == 0) — default;
  • DOWNLOADED (int == 1) — downloaded;
  • DOWNLOADING (int == 2) — downloading;
  • FAILED (int == 3) — error;
  • PENDING (int == 5) — pending;
IMPORTANT

The Updates SDK does not have a specific status for the situation when the user has canceled the update download. If the user interrupted the update at the download stage, installStatus returns the original status UNKNOWN (0) with the Download button.

If the user has already downloaded the update but canceled the installation, then installStatus will return the value DOWNLOADED (1).

Consider the following options:

The user started downloading the update but canceled the download — in this case:

  • updateAvailability — UPDATE_AVAILABLE (2);
  • installStatus — UNKNOWN (0).
  • The user downloaded the update file but did not install it — in this case:
  • updateAvailability — UPDATE_AVAILABLE (2);
  • installStatus — DOWNLOADED (1).
  • bytesDownloaded — the number of bytes downloaded;
  • totalBytesToDownload — the total number of bytes to download;
  • installErrorCode — the error code during download. Error codes are described in the Error Handling section.

Removing the Listener

If you no longer need the listener, use the unregisterListener() method to remove the listener, passing the previously registered listener to the method.

ruStoreAppUpdateManager.unregisterListener(listener)

Starting the Update Download

Deferred Update

Starting the Update Scenario

To start downloading the app update, call the startUpdateFlow() method.

info

The AppUpdateInfo object becomes invalid after a single use. To call the startUpdateFlow() method again, request AppUpdateInfo again using the getAppUpdateInfo() method.

ruStoreAppUpdateManager
.startUpdateFlow(appUpdateInfo, AppUpdateOptions.Builder().build())
.addOnSuccessListener { resultCode ->
if (resultCode == Activity.RESULT_CANCELED) {
// User cancelled the download
}
}
.addOnFailureListener { throwable ->
Log.e(TAG, "startUpdateFlow error", throwable)
}

If the user confirmed the update download, then resultCode = Activity.RESULT_OK; if they declined, then resultCode = Activity.RESULT_CANCEL.

After receiving the DOWNLOADED status, you can call the update installation method completeUpdate().

It is recommended to notify the user that the update is ready to be installed.

The method may return an error.

Forced Update

Starting the Update Scenario

After receiving the AppUpdateInfo, you can check the availability of a forced update.

if (appUpdateInfo.isUpdateTypeAllowed(IMMEDIATE)) {
// Forced update is available
}

It is recommended to use the result of the isUpdateTypeAllowed function to decide whether to start a forced update, but this result does not affect the ability to start the scenario. The need to start the update scenario can occur according to your internal logic.

To start the update scenario, use the startUpdateFlow() method.

ruStoreAppUpdateManager
.startUpdateFlow(appUpdateInfo, AppUpdateOptions.Builder().appUpdateType(IMMEDIATE).build())
.addOnSuccessListener { resultCode ->

}
.addOnFailureListener { throwable ->

}

resultCode (Int):

  • Activity.RESULT_OK (-1) — the update is complete, the code may not be received because the application is terminated at the time of the update.
  • Activity.RESULT_CANCELED (0) — the flow was interrupted by the user or an error occurred. It is assumed that upon receiving this code, the application should be terminated.
  • ActivityResult.ACTIVITY_NOT_FOUND (2) — RuStore is not installed, or the installed version does not support forced updates (RuStore versionCode < 191).

throwable — an error starting the update scenario.

No further action is required upon successful update.

Silent Update

Starting the Update Scenario

To start downloading the application update, you need to call the startUpdateFlow() method with the AppUpdateInfo argument, obtained in the getAppUpdateInfo() method, and set the update type in AppUpdateOptions to SILENT.

ruStoreAppUpdateManager
.startUpdateFlow(appUpdateInfo, AppUpdateOptions.Builder().appUpdateType(SILENT).build())
.addOnSuccessListener { resultCode ->

}
.addOnFailureListener { throwable ->

}

When calling onSuccessListener with resultCode = Activity.RESULT_OK, a task to download the update will be registered.

In this scenario, only onSuccessListener with resultCode = Activity.RESULT_OK or onFailureListener can be called.

After calling the method, you can monitor the update download status in the listener.

After receiving the DOWNLOADED status, you can call the update installation method completeUpdate(). It is recommended to notify the user that the update is ready to be installed.

tip

It is recommended to implement your own interface for silent updates.

Installing the Update

To start the update installation, call the completeUpdate() method.

To start the update installation, call the completeUpdate(appUpdateOptions: AppUpdateOptions) method. You can only pass two types of installation completion to the method: FLEXIBLE and SILENT, deferred and silent updates, respectively.

var type: Int = AppUpdateType.FLEXIBLE

ruStoreAppUpdateManager
.completeUpdate(AppUpdateOptions.Builder().appUpdateType(type).build())
.addOnFailureListener { throwable ->
Log.e(TAG, "update error", throwable)
}

type - the update type AppUpdateType.

If you pass the update type FLEXIBLE, the application will restart. An example of a user flow is shown below.

img

If you pass the update type SILENT, the application will close without restarting. An example of a user flow is shown below. Update without UI from RuStore:

  1. The UI dialog for completing the update will not be shown.
  2. If the update is successful, the application will be closed.

Error Handling

tip

If you receive onFailure in response, it is not recommended to display the error to the user yourself. Displaying the error can negatively affect the user experience.

Possible Errors

  • RuStoreNotInstalledException — RuStore is not installed on the user's device;
  • RuStoreOutdatedException — the version of RuStore installed on the user's device does not support this SDK;
  • RuStoreUserUnauthorizedException — the user is not authorized in RuStore;
  • RuStoreException — the basic RuStore error from which other errors inherit;
  • RuStoreInstallException(public val code: Int) — download and installation error.
  • ERROR_UNKNOWN(Int = 4001) — unknown error.
  • ERROR_DOWNLOAD(Int = 4002) — download error.
  • ERROR_BLOCKED(Int = 4003) — installation blocked by the system.
  • ERROR_INVALID_APK(Int = 4004) — incorrect update APK.
  • ERROR_CONFLICT(Int = 4005) — conflict with the current version of the application.
  • ERROR_STORAGE(Int = 4006) — not enough memory on the device.
  • ERROR_INCOMPATIBLE(Int = 4007) — incompatible with the device.
  • ERROR_APP_NOT_OWNED(Int = 4008) — the application has not been purchased.
  • ERROR_INTERNAL_ERROR(Int = 4009) — internal error.
  • ERROR_ABORTED(Int = 4010) — the user has canceled the update installation.
  • ERROR_APK_NOT_FOUND(Int = 4011) — APK not found to start the installation.
  • ERROR_EXTERNAL_SOURCE_DENIED(Int = 4012) — update start denied. For example, the first method returned a response that the update is not available, but the user calls the second method.
  • ERROR_ACTIVITY_SEND_INTENT(Int = 9901) — error sending intent to open the activity.
  • ERROR_ACTIVITY_UNKNOWN(Int = 9902) — unknown error opening activity.