Skip to content

Commit 520581f

Browse files
authored
SDK 18 Migration Guide Updates (#1453)
* 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
1 parent cc8a4e9 commit 520581f

File tree

12 files changed

+164
-7
lines changed

12 files changed

+164
-7
lines changed

documentation/migration/migration-guide-17-18.md

Lines changed: 146 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -60,13 +60,13 @@ public void updateAdvertisingId(@NonNull Context context){
6060
if (adInfo == null) {
6161
return;
6262
}
63-
63+
6464
advertisingId = adInfo.getId();
6565
limitedAdTrackingEnabled = adInfo.isLimitAdTrackingEnabled();
6666
if (advertisingId != null && limitedAdTrackingEnabled != null) {
6767
UAirship.shared().getAnalytics().editAssociatedIdentifiers()
68-
.setAdvertisingId(advertisingId, limitedAdTrackingEnabled)
69-
.apply();
68+
.setAdvertisingId(advertisingId, limitedAdTrackingEnabled)
69+
.apply();
7070
}
7171
} catch (IOException | GooglePlayServicesNotAvailableException | GooglePlayServicesRepairableException e) {
7272
Log.e(TAG, "Failed to retrieve and update advertising ID!", e);
@@ -97,3 +97,146 @@ import com.urbanairship.messagecenter.R
9797
// Preference Center resources:
9898
import com.urbanairship.preferencecenter.R
9999
```
100+
101+
## Privacy Manager
102+
103+
### Feature Constants
104+
105+
Integer constants for features have been moved to a class, `PrivacyManager.Feature`.
106+
107+
| 17.x | 18.x |
108+
|----------------------------------------------|----------------------------------------------|
109+
| `PrivacyManager.FEATURE_ALL` | `PrivacyManager.Feature.ALL` |
110+
| `PrivacyManager.FEATURE_NONE` | `PrivacyManager.Feature.NONE` |
111+
| `PrivacyManager.FEATURE_MESSAGE_CENTER` | `PrivacyManager.Feature.MESSAGE_CENTER` |
112+
| `PrivacyManager.FEATURE_TAGS_AND_ATTRIBUTES` | `PrivacyManager.Feature.TAGS_AND_ATTRIBUTES` |
113+
| `PrivacyManager.FEATURE_IN_APP_AUTOMATION` | `PrivacyManager.Feature.IN_APP_AUTOMATION` |
114+
| `PrivacyManager.FEATURE_CONTACTS` | `PrivacyManager.Feature.CONTACTS` |
115+
| `PrivacyManager.FEATURE_ANALYTICS` | `PrivacyManager.Feature.ANALYTICS` |
116+
| `PrivacyManager.FEATURE_PUSH` | `PrivacyManager.Feature.PUSH` |
117+
118+
### Methods
119+
120+
Methods on `PrivacyManager` that previously accepted an integer feature mask now accept a `PrivacyManager.Feature` object.
121+
122+
| 17.x | 18.x |
123+
|-------------------------------------------------------|-------------------------------------------------------|
124+
| `disable(FEATURE_PUSH)` | `disable(Feature.PUSH)` |
125+
| `enable(FEATURE_PUSH)` | `enable(Feature.PUSH)` |
126+
| `isEnabled(FEATURE_PUSH)` | `isEnabled(Feature.PUSH)` |
127+
| `isAnyEnabled(FEATURE_PUSH, FEATURE_ANALYTICS)` | `isAnyEnabled(Feature.PUSH, Feature.ANALYTICS)` |
128+
| `setEnabledFeatures(FEATURE_PUSH, FEATURE_ANALYTICS)` | `setEnabledFeatures(Feature.PUSH, Feature.ANALYTICS)` |
129+
130+
A new value, `PrivacyManager.Feature.FEATURE_FLAGS`, has also been added to control the enabled state of Airship Feature Flags.
131+
132+
## Contact
133+
134+
### Conflict Events
135+
136+
The signature of `ConflictEvent.associatedChannels` was changed from `List<AssociatedChannel>` to `List<ConflictEvent.ChannelInfo>` and the `AssociatedChannel` class has been removed.
137+
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.
138+
139+
## Image Loading
140+
141+
The ability to provide a custom image loader via `UAirship.setImageLoader(...)` has been removed. The SDK now uses Glide internally to handle image loading.
142+
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.
143+
144+
## Automation
145+
146+
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,
147+
this update maybe more extensive. See below for more info about custom display adapter migration.
148+
149+
### Accessors
150+
151+
The accessors for InAppMessaging and LegacyInAppMessaging have moved.
152+
153+
| 17.x | 18.x |
154+
|------------------------------------------------|-------------------------------------------------|
155+
| `InAppAutomation.shared().inAppMessageManager` | `InAppAutomation.shared().inAppMessaging` |
156+
| `LegacyInAppMessageManager.shared()` | `InAppAutomation.shared().legacyInAppMessaging` |
157+
158+
### Cache Management
159+
160+
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.
161+
162+
### Display Coordinators
163+
164+
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.
165+
166+
### Extending Messages
167+
168+
InAppMessages are no longer extendable when displaying. If this is needed in your application, please file a GitHub issue with your use case.
169+
170+
### Custom Display Adapters
171+
172+
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.
173+
174+
| 17.x `InAppMessageAdapter` | 18.x `CustomDisplayAdapter` |
175+
|--------------------------------------|------------------------------------------------------------------------------------------------|
176+
| `Factory.createAdapter(message)` | No mapping. A factory is no longer required. |
177+
| `onDisplay(context, displayHandler)` | `SuspendingAdapter.display(context)` or `CallbackAdapter.display(context, callback)` |
178+
| `onPrepare(context, assets)` | Use the `isReady` Flow on `SuspendingAdapter` to indicate when the adapter is ready to display |
179+
| `isReady(context)` | Use the `SuspendingAdapter.isReady` Flow |
180+
181+
#### Custom display adapter example:
182+
183+
```kotlin
184+
class MyCustomDisplayAdapter(
185+
private val context: Context,
186+
private val message: InAppMessage,
187+
private val assets: AirshipCachedAssets
188+
) : CustomDisplayAdapter.SuspendingAdapter {
189+
190+
// Implement the isReady property, which can be used to signal when the adapter is ready to display the message.
191+
// If this adapter does not need to wait for anything before displaying the message,
192+
// you can return a StateFlow with an initial value of true to indicate that it is always ready.
193+
override val isReady: StateFlow<Boolean> = MutableStateFlow(true)
194+
195+
override suspend fun display(context: Context): CustomDisplayResolution {
196+
197+
// Display the message...
198+
199+
// Return a result after the message has been displayed.
200+
return CustomDisplayResolution.UserDismissed
201+
}
202+
203+
companion object {
204+
fun register() {
205+
InAppAutomation.shared().inAppMessaging.setAdapterFactoryBlock(
206+
type = CustomDisplayAdapterType.BANNER,
207+
factoryBlock = { context, message, assets ->
208+
MyCustomDisplayAdapter(context, message, assets)
209+
}
210+
)
211+
}
212+
}
213+
}
214+
```
215+
216+
Then, register the custom display adapter in your `Autopilot` implementation:
217+
218+
```kotlin
219+
class MyAutopilot : Autopilot() {
220+
override fun onAirshipReady(airship: UAirship) {
221+
// Other Airship setup...
222+
223+
// Register the custom display adapter
224+
MyCustomDisplayAdapter.register()
225+
}
226+
}
227+
```
228+
229+
## Live Update
230+
231+
### Changes to Live Update Handlers
232+
233+
The `LiveUpdateNotificationHandler` or `LiveUpdateCustomHandler` classes that were previously marked
234+
as deprecated have been removed. The new handler interfaces / abstract classes are:
235+
236+
* `CallbackLiveUpdateNotificationHandler`
237+
* `SuspendLiveUpdateNotificationHandler`
238+
* `CallbackLiveUpdateCustomHandler`
239+
* `SuspendLiveUpdateCustomHandler`
240+
241+
Apps that were using `LiveUpdateNotificationHandler` or `LiveUpdateCustomHandler` should update to
242+
the suspend or callback versions, as appropriate.

urbanairship-automation/src/main/java/com/urbanairship/iam/adapter/CustomDisplayAdapter.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -170,15 +170,15 @@ public sealed class CustomDisplayResolution {
170170
/**
171171
* Message tap
172172
*/
173-
public class MessageTap(): CustomDisplayResolution()
173+
public data object MessageTap: CustomDisplayResolution()
174174

175175
/**
176176
* User dismissed
177177
*/
178-
public class UserDismissed(): CustomDisplayResolution()
178+
public data object UserDismissed: CustomDisplayResolution()
179179

180180
/**
181181
* Timed out
182182
*/
183-
public class TimedOut(): CustomDisplayResolution()
183+
public data object TimedOut: CustomDisplayResolution()
184184
}

urbanairship-core/src/main/java/com/urbanairship/audience/DeviceInfoProvider.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ public interface DeviceInfoProvider {
3636
public val installDateMilliseconds: Long
3737
public val locale: Locale
3838

39+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
3940
public companion object {
4041
@JvmStatic
4142
public fun newProvider(contactId: String? = null): DeviceInfoProvider {

urbanairship-core/src/main/java/com/urbanairship/audience/DeviceTagSelector.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ public class DeviceTagSelector
3131
internal val selectors: List<DeviceTagSelector> = emptyList()
3232
) : JsonSerializable {
3333

34+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
3435
public companion object {
3536
public fun and(@Size(min = 1) selectors: List<DeviceTagSelector>): DeviceTagSelector {
3637
return DeviceTagSelector(Type.AND, selectors = selectors)

urbanairship-core/src/main/java/com/urbanairship/config/AirshipRuntimeConfig.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -171,6 +171,7 @@ public class AirshipRuntimeConfig internal constructor (
171171
configObserver.updateRemoteConfig(config)
172172
}
173173

174+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
174175
public companion object {
175176
private fun determineUrlFallback(configOptions: AirshipConfigOptions): Boolean {
176177
if ("huawei".equals(Build.MANUFACTURER, ignoreCase = true)) {

urbanairship-core/src/main/java/com/urbanairship/experiment/Experiment.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,10 @@ public enum class ResolutionType(public val jsonValue: String) {
3232
DEFERRED("deferred"),
3333
STATIC("static");
3434

35+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
3536
public companion object {
3637
public fun from(value: String): ResolutionType? {
37-
return values().firstOrNull { it.jsonValue == value }
38+
return entries.firstOrNull { it.jsonValue == value }
3839
}
3940
}
4041
}
@@ -49,6 +50,7 @@ public class ExperimentResult(
4950
public val allEvaluatedExperimentsMetadata: List<JsonMap>
5051
) : JsonSerializable {
5152

53+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
5254
public companion object {
5355
private const val KEY_CHANNEL_ID = "channelId"
5456
private const val KEY_CONTACT_ID = "contactId"

urbanairship-core/src/main/java/com/urbanairship/experiment/ExperimentManager.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ public class ExperimentManager internal constructor(
3232

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

35+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
3536
public companion object {
3637
internal const val PAYLOAD_TYPE = "experiments"
3738
}

urbanairship-core/src/main/java/com/urbanairship/experiment/TimeCriteria.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public data class TimeCriteria(
1818
private val end: Long?,
1919
) : JsonSerializable {
2020

21+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
2122
public companion object {
2223
private const val KEY_START = "start_timestamp"
2324
private const val KEY_END = "end_timestamp"

urbanairship-core/src/main/java/com/urbanairship/remoteconfig/RemoteConfig.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ public data class RemoteConfig @JvmOverloads constructor(
3434
IAA_CONFIG to iaaConfig
3535
).toJsonValue()
3636

37+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
3738
public companion object {
3839
private const val AIRSHIP_CONFIG_KEY = "airship_config"
3940
private const val METERED_USAGE_CONFIG_KEY = "metered_usage"
@@ -97,6 +98,7 @@ public data class RemoteAirshipConfig @JvmOverloads constructor(
9798
meteredUsageUrl = jsonValue.optMap().optionalField(METERED_USAGE_URL_KEY)
9899
)
99100

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

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

151+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
148152
public companion object {
149153
private const val IS_ENABLED_KEY = "enabled"
150154
private const val INITIAL_DELAY_MS_KEY = "initial_delay_ms"

urbanairship-core/src/main/java/com/urbanairship/remotedata/RemoteDataPayload.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ public data class RemoteDataPayload(
1616
val data: JsonMap,
1717
val remoteDataInfo: RemoteDataInfo? = null
1818
) {
19+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
1920
public companion object {
2021
@JvmStatic
2122
public fun emptyPayload(type: String): RemoteDataPayload {

0 commit comments

Comments
 (0)