Skip to content

Commit

Permalink
SDK 18 Migration Guide Updates (#1453)
Browse files Browse the repository at this point in the history
* Update migration-guide-17-18.md

* added conflict events, stubbed out more of automation section

* Flesh out automation section

* Tweak custom display adapter resolutions to use data object

* Add @RestrictTo on companions of classes with @RestrictTo
  • Loading branch information
jyaganeh authored Jun 13, 2024
1 parent cc8a4e9 commit 520581f
Show file tree
Hide file tree
Showing 12 changed files with 164 additions and 7 deletions.
149 changes: 146 additions & 3 deletions documentation/migration/migration-guide-17-18.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,13 @@ public void updateAdvertisingId(@NonNull Context context){
if (adInfo == null) {
return;
}

advertisingId = adInfo.getId();
limitedAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled();
if (advertisingId != null && limitedAdTrackingEnabled != null) {
UAirship.shared().getAnalytics().editAssociatedIdentifiers()
.setAdvertisingId(advertisingId, limitedAdTrackingEnabled)
.apply();
.setAdvertisingId(advertisingId, limitedAdTrackingEnabled)
.apply();
}
} catch (IOException | GooglePlayServicesNotAvailableException | GooglePlayServicesRepairableException e) {
Log.e(TAG, "Failed to retrieve and update advertising ID!", e);
Expand Down Expand Up @@ -97,3 +97,146 @@ import com.urbanairship.messagecenter.R
// Preference Center resources:
import com.urbanairship.preferencecenter.R
```

## Privacy Manager

### Feature Constants

Integer constants for features have been moved to a class, `PrivacyManager.Feature`.

| 17.x | 18.x |
|----------------------------------------------|----------------------------------------------|
| `PrivacyManager.FEATURE_ALL` | `PrivacyManager.Feature.ALL` |
| `PrivacyManager.FEATURE_NONE` | `PrivacyManager.Feature.NONE` |
| `PrivacyManager.FEATURE_MESSAGE_CENTER` | `PrivacyManager.Feature.MESSAGE_CENTER` |
| `PrivacyManager.FEATURE_TAGS_AND_ATTRIBUTES` | `PrivacyManager.Feature.TAGS_AND_ATTRIBUTES` |
| `PrivacyManager.FEATURE_IN_APP_AUTOMATION` | `PrivacyManager.Feature.IN_APP_AUTOMATION` |
| `PrivacyManager.FEATURE_CONTACTS` | `PrivacyManager.Feature.CONTACTS` |
| `PrivacyManager.FEATURE_ANALYTICS` | `PrivacyManager.Feature.ANALYTICS` |
| `PrivacyManager.FEATURE_PUSH` | `PrivacyManager.Feature.PUSH` |

### Methods

Methods on `PrivacyManager` that previously accepted an integer feature mask now accept a `PrivacyManager.Feature` object.

| 17.x | 18.x |
|-------------------------------------------------------|-------------------------------------------------------|
| `disable(FEATURE_PUSH)` | `disable(Feature.PUSH)` |
| `enable(FEATURE_PUSH)` | `enable(Feature.PUSH)` |
| `isEnabled(FEATURE_PUSH)` | `isEnabled(Feature.PUSH)` |
| `isAnyEnabled(FEATURE_PUSH, FEATURE_ANALYTICS)` | `isAnyEnabled(Feature.PUSH, Feature.ANALYTICS)` |
| `setEnabledFeatures(FEATURE_PUSH, FEATURE_ANALYTICS)` | `setEnabledFeatures(Feature.PUSH, Feature.ANALYTICS)` |

A new value, `PrivacyManager.Feature.FEATURE_FLAGS`, has also been added to control the enabled state of Airship Feature Flags.

## Contact

### Conflict Events

The signature of `ConflictEvent.associatedChannels` was changed from `List<AssociatedChannel>` to `List<ConflictEvent.ChannelInfo>` and the `AssociatedChannel` class has been removed.
Apps that have implemented a custom `ContactConflictListener` may need to make minor adjustments to update to the new type. The `ChannelInfo` class provides the same fields as the removed `AssociatedChannel` class.

## Image Loading

The ability to provide a custom image loader via `UAirship.setImageLoader(...)` has been removed. The SDK now uses Glide internally to handle image loading.
Additionally, the `ImageLoader` and `ImageRequestOptions` classes has been removed. Apps that were using these class should migrate to using another image loading library, like Coil or Glide, directly.

## Automation

The `urbanairship-automation` module has been rewritten in Kotlin and now uses Kotlin coroutines. For most apps, this will be a trivial update, but if your app uses custom display adapters,
this update maybe more extensive. See below for more info about custom display adapter migration.

### Accessors

The accessors for InAppMessaging and LegacyInAppMessaging have moved.

| 17.x | 18.x |
|------------------------------------------------|-------------------------------------------------|
| `InAppAutomation.shared().inAppMessageManager` | `InAppAutomation.shared().inAppMessaging` |
| `LegacyInAppMessageManager.shared()` | `InAppAutomation.shared().legacyInAppMessaging` |

### Cache Management

The `PrepareAssetsDelegate`, `CachePolicyDelegate`, and `AssetManager` classes have been removed and are no longer available to extend. These APIs were difficult to use and often times lead to unintended consequences. The Airship SDK will now manage its own assets. External assets required by the App that need to be fetched before hand should happen outside of Airship. If assets are needed and can be fetched at display time, use the `isReady` Flow in a custom implementation of `CustomDisplayAdapter.SuspendingAdapter` to indicate when the adapter has finished fetching assets and is ready to display by emitting a `true` value.

### Display Coordinators

Display coordinators was another difficult to use API that has been removed. Instead, use the `InAppMessageDisplayDelegate.isMessageReadyToDisplay(message, scheduleId)` method to prevent messages from displaying, and `InAppAutomation.shared().inAppMessaging.notifyDisplayConditionsChanged()` to notify when the message should be tried again. If a use case is not able to be solved with the replacement methods, please file a GitHub issue with your use case.

### Extending Messages

InAppMessages are no longer extendable when displaying. If this is needed in your application, please file a GitHub issue with your use case.

### Custom Display Adapters

The `InAppMessageAdapter` interface has been replaced with a sealed interface, `CustomDisplayAdapter`, that contains `SuspendingAdapter` and `CallbackAdapter` subtypes. The new interface provides roughly the same functionality as before just with a different structure.

| 17.x `InAppMessageAdapter` | 18.x `CustomDisplayAdapter` |
|--------------------------------------|------------------------------------------------------------------------------------------------|
| `Factory.createAdapter(message)` | No mapping. A factory is no longer required. |
| `onDisplay(context, displayHandler)` | `SuspendingAdapter.display(context)` or `CallbackAdapter.display(context, callback)` |
| `onPrepare(context, assets)` | Use the `isReady` Flow on `SuspendingAdapter` to indicate when the adapter is ready to display |
| `isReady(context)` | Use the `SuspendingAdapter.isReady` Flow |

#### Custom display adapter example:

```kotlin
class MyCustomDisplayAdapter(
private val context: Context,
private val message: InAppMessage,
private val assets: AirshipCachedAssets
) : CustomDisplayAdapter.SuspendingAdapter {

// Implement the isReady property, which can be used to signal when the adapter is ready to display the message.
// If this adapter does not need to wait for anything before displaying the message,
// you can return a StateFlow with an initial value of true to indicate that it is always ready.
override val isReady: StateFlow<Boolean> = MutableStateFlow(true)

override suspend fun display(context: Context): CustomDisplayResolution {

// Display the message...

// Return a result after the message has been displayed.
return CustomDisplayResolution.UserDismissed
}

companion object {
fun register() {
InAppAutomation.shared().inAppMessaging.setAdapterFactoryBlock(
type = CustomDisplayAdapterType.BANNER,
factoryBlock = { context, message, assets ->
MyCustomDisplayAdapter(context, message, assets)
}
)
}
}
}
```

Then, register the custom display adapter in your `Autopilot` implementation:

```kotlin
class MyAutopilot : Autopilot() {
override fun onAirshipReady(airship: UAirship) {
// Other Airship setup...

// Register the custom display adapter
MyCustomDisplayAdapter.register()
}
}
```

## Live Update

### Changes to Live Update Handlers

The `LiveUpdateNotificationHandler` or `LiveUpdateCustomHandler` classes that were previously marked
as deprecated have been removed. The new handler interfaces / abstract classes are:

* `CallbackLiveUpdateNotificationHandler`
* `SuspendLiveUpdateNotificationHandler`
* `CallbackLiveUpdateCustomHandler`
* `SuspendLiveUpdateCustomHandler`

Apps that were using `LiveUpdateNotificationHandler` or `LiveUpdateCustomHandler` should update to
the suspend or callback versions, as appropriate.
Original file line number Diff line number Diff line change
Expand Up @@ -170,15 +170,15 @@ public sealed class CustomDisplayResolution {
/**
* Message tap
*/
public class MessageTap(): CustomDisplayResolution()
public data object MessageTap: CustomDisplayResolution()

/**
* User dismissed
*/
public class UserDismissed(): CustomDisplayResolution()
public data object UserDismissed: CustomDisplayResolution()

/**
* Timed out
*/
public class TimedOut(): CustomDisplayResolution()
public data object TimedOut: CustomDisplayResolution()
}
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ public interface DeviceInfoProvider {
public val installDateMilliseconds: Long
public val locale: Locale

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
@JvmStatic
public fun newProvider(contactId: String? = null): DeviceInfoProvider {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ public class DeviceTagSelector
internal val selectors: List<DeviceTagSelector> = emptyList()
) : JsonSerializable {

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
public fun and(@Size(min = 1) selectors: List<DeviceTagSelector>): DeviceTagSelector {
return DeviceTagSelector(Type.AND, selectors = selectors)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,7 @@ public class AirshipRuntimeConfig internal constructor (
configObserver.updateRemoteConfig(config)
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
private fun determineUrlFallback(configOptions: AirshipConfigOptions): Boolean {
if ("huawei".equals(Build.MANUFACTURER, ignoreCase = true)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,10 @@ public enum class ResolutionType(public val jsonValue: String) {
DEFERRED("deferred"),
STATIC("static");

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
public fun from(value: String): ResolutionType? {
return values().firstOrNull { it.jsonValue == value }
return entries.firstOrNull { it.jsonValue == value }
}
}
}
Expand All @@ -49,6 +50,7 @@ public class ExperimentResult(
public val allEvaluatedExperimentsMetadata: List<JsonMap>
) : JsonSerializable {

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
private const val KEY_CHANNEL_ID = "channelId"
private const val KEY_CONTACT_ID = "contactId"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ public class ExperimentManager internal constructor(

private val scope = CoroutineScope(AirshipDispatchers.IO + SupervisorJob())

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
internal const val PAYLOAD_TYPE = "experiments"
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ public data class TimeCriteria(
private val end: Long?,
) : JsonSerializable {

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
private const val KEY_START = "start_timestamp"
private const val KEY_END = "end_timestamp"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public data class RemoteConfig @JvmOverloads constructor(
IAA_CONFIG to iaaConfig
).toJsonValue()

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
private const val AIRSHIP_CONFIG_KEY = "airship_config"
private const val METERED_USAGE_CONFIG_KEY = "metered_usage"
Expand Down Expand Up @@ -97,6 +98,7 @@ public data class RemoteAirshipConfig @JvmOverloads constructor(
meteredUsageUrl = jsonValue.optMap().optionalField(METERED_USAGE_URL_KEY)
)

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
private const val REMOTE_DATA_URL_KEY = "remote_data_url"
private const val DEVICE_API_URL_KEY = "device_api_url"
Expand All @@ -120,6 +122,7 @@ public data class ContactConfig @JvmOverloads constructor(
CHANNEL_REGISTRATION_MAX_RESOLVE_AGE_MS_KEY to channelRegistrationMaxResolveAgeMs
).toJsonValue()

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
private const val FOREGROUND_INTERVAL_MS_KEY = "foreground_resolve_interval_ms"
private const val CHANNEL_REGISTRATION_MAX_RESOLVE_AGE_MS_KEY = "max_cra_resolve_age_ms"
Expand All @@ -145,6 +148,7 @@ public data class MeteredUsageConfig internal constructor(
INTERVAL_MS_KEY to intervalMs
).toJsonValue()

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
private const val IS_ENABLED_KEY = "enabled"
private const val INITIAL_DELAY_MS_KEY = "initial_delay_ms"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ public data class RemoteDataPayload(
val data: JsonMap,
val remoteDataInfo: RemoteDataInfo? = null
) {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
@JvmStatic
public fun emptyPayload(type: String): RemoteDataPayload {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ public class TaskSleeper {
}
}

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
public val default: TaskSleeper = TaskSleeper()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import com.urbanairship.json.requireField
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public sealed class ViewInfo : View {

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
public companion object {
@JvmStatic
@Throws(JsonException::class)
Expand Down

0 comments on commit 520581f

Please sign in to comment.