Skip to content

Commit 81e0ce3

Browse files
authored
SDK 18 tidying: enable strict explicit-api mode in Pref Center and Feature Flag modules (#1451)
* Enable explicit API mode for Pref Center * Enable explicit API mode for Feature Flags
1 parent c2b06ab commit 81e0ce3

33 files changed

+320
-309
lines changed

urbanairship-feature-flag/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,6 @@ android {
4040

4141
kotlinOptions {
4242
jvmTarget = JavaVersion.VERSION_1_8
43-
// TODO(SDK18): freeCompilerArgs = ["-Xexplicit-api=strict"]
43+
freeCompilerArgs = ["-Xexplicit-api=strict"]
4444
}
4545
}

urbanairship-feature-flag/src/main/java/com/urbanairship/featureflag/FeatureFlag.kt

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,41 +13,43 @@ import com.urbanairship.json.requireField
1313
/**
1414
* Airship Feature flag.
1515
*/
16-
class FeatureFlag private constructor(
16+
public class FeatureFlag private constructor(
1717

1818
/**
1919
* Flag name. Will be empty if the flag was created through the deprecated constructor.
2020
*/
21-
val name: String,
21+
public val name: String,
2222

2323
/**
2424
* Indicates whether the device is eligible or not for the flag.
2525
*/
26-
val isEligible: Boolean,
26+
public val isEligible: Boolean,
2727

2828
/**
2929
* Indicates whether the flag exists in the current flag listing or not
3030
*/
31-
val exists: Boolean,
31+
public val exists: Boolean,
3232

3333
/**
34-
* Optional reportingInfo. Will be missing if the flag is created through the
34+
* Optional reporting info. Will be `null` if the flag is created through the
3535
* deprecated constructor or if it does not exist.
3636
*/
3737
internal val reportingInfo: ReportingInfo?,
3838

3939
/**
4040
* Optional variables associated with the flag
4141
*/
42-
val variables: JsonMap?,
42+
public val variables: JsonMap?,
4343
) : JsonSerializable {
4444

4545
/**
4646
* Public constructor.
47+
*
4748
* @deprecated Applications should not create a flag directly, instead request a flag
48-
* through `FeatureFlagManager`.
49+
* via [FeatureFlagManager.flag] or [FeatureFlagManager.flagAsPendingResult].
4950
*/
50-
@Deprecated("Flags should be accessed through `FeatureFlagManager`")
51+
@Deprecated("Applications should not create a flag directly, instead request a flag " +
52+
"via FeatureFlagManager.flag or FeatureFlagManager.flagAsPendingResult")
5153
public constructor(isEligible: Boolean, exists: Boolean, variables: JsonMap?) :
5254
this("", isEligible, exists, null, variables)
5355

@@ -75,6 +77,7 @@ class FeatureFlag private constructor(
7577
return result
7678
}
7779

80+
@Throws(JsonException::class)
7881
override fun toJsonValue(): JsonValue = jsonMapOf(
7982
KEY_NAME to name,
8083
KEY_EXISTS to exists,
@@ -160,6 +163,7 @@ class FeatureFlag private constructor(
160163
}
161164
}
162165

166+
@Throws(JsonException::class)
163167
override fun toJsonValue() = jsonMapOf(
164168
KEY_REPORTING_METADATA to reportingMetadata,
165169
KEY_CHANNEL_ID to channelId,

urbanairship-feature-flag/src/main/java/com/urbanairship/featureflag/FeatureFlagAnalytics.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,10 +4,12 @@ import com.urbanairship.UALog
44
import com.urbanairship.analytics.AirshipEventFeed
55
import com.urbanairship.analytics.Analytics
66

7-
class FeatureFlagAnalytics(
7+
/** Analytics helper that handles tracking of feature flag interactions. */
8+
internal class FeatureFlagAnalytics(
89
private val analytics: Analytics,
910
) {
1011

12+
/** Tracks an interaction with the given feature [flag]. */
1113
fun trackInteraction(flag: FeatureFlag) {
1214
if (!flag.exists) {
1315
UALog.e { "Flag does not exist, unable to track interaction: $flag" }

urbanairship-feature-flag/src/main/java/com/urbanairship/featureflag/FeatureFlagException.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,11 +5,11 @@ package com.urbanairship.featureflag
55
/**
66
* Thrown when a feature flag data is stale or outdated.
77
*/
8-
sealed class FeatureFlagException(override val message: String) : Exception(message) {
9-
class FailedToFetch() : FeatureFlagException("failed to fetch data")
8+
public sealed class FeatureFlagException(override val message: String) : Exception(message) {
9+
public class FailedToFetch() : FeatureFlagException("failed to fetch data")
1010
}
1111

12-
sealed class FeatureFlagEvaluationException(override val message: String) : Exception(message) {
12+
internal sealed class FeatureFlagEvaluationException(override val message: String) : Exception(message) {
1313
class ConnectionError() : FeatureFlagEvaluationException("Unable to fetch data")
1414
class OutOfDate() : FeatureFlagEvaluationException("Remote data is outdated")
1515
class StaleNotAllowed() : FeatureFlagEvaluationException("Stale data is not allowed")

urbanairship-feature-flag/src/main/java/com/urbanairship/featureflag/FeatureFlagInfo.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import java.text.ParseException
2121
internal sealed class FeatureFlagVariables: JsonSerializable {
2222
data class Fixed(val data: JsonMap?) : FeatureFlagVariables() {
2323

24+
@Throws(JsonException::class)
2425
override fun toJsonValue(): JsonValue {
2526
return jsonMapOf(
2627
KEY_TYPE to VariableType.FIXED.jsonValue,
@@ -31,6 +32,7 @@ internal sealed class FeatureFlagVariables: JsonSerializable {
3132

3233
data class Variant(val variantVariables: List<VariablesVariant>) : FeatureFlagVariables() {
3334

35+
@Throws(JsonException::class)
3436
override fun toJsonValue(): JsonValue {
3537
return jsonMapOf(
3638
KEY_TYPE to VariableType.VARIANTS.jsonValue,
@@ -97,6 +99,7 @@ internal data class VariablesVariant(
9799
}
98100
}
99101

102+
@Throws(JsonException::class)
100103
override fun toJsonValue(): JsonValue {
101104
return jsonMapOf(
102105
KEY_ID to id,
@@ -247,14 +250,16 @@ internal data class EvaluationOptions(
247250
private const val KEY_STALE_DATA_FLAG = "disallow_stale_value"
248251
private const val KEY_TTL = "ttl"
249252

250-
fun fromJson(json: JsonMap): EvaluationOptions? {
253+
@Throws(JsonException::class)
254+
fun fromJson(json: JsonMap): EvaluationOptions {
251255
return EvaluationOptions(
252256
disallowStaleValues = json.optionalField(KEY_STALE_DATA_FLAG),
253257
ttl = json.optionalField(KEY_TTL)
254258
)
255259
}
256260
}
257261

262+
@Throws(JsonException::class)
258263
override fun toJsonValue(): JsonValue {
259264
return jsonMapOf(
260265
KEY_STALE_DATA_FLAG to disallowStaleValues, KEY_TTL to ttl?.toLong()

urbanairship-feature-flag/src/main/java/com/urbanairship/featureflag/FeatureFlagManager.kt

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ import kotlinx.coroutines.launch
2727
* Airship Feature Flags manager.
2828
*/
2929
@OpenForTesting
30-
public class FeatureFlagManager
31-
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) internal constructor(
30+
public class FeatureFlagManager internal constructor(
3231
context: Context,
3332
dataStore: PreferenceDataStore,
3433
private val audienceEvaluator: AudienceEvaluator,
@@ -41,15 +40,15 @@ public class FeatureFlagManager
4140
private val privacyManager: PrivacyManager
4241
) : AirshipComponent(context, dataStore) {
4342

44-
companion object {
43+
public companion object {
4544

4645
/**
4746
* Gets the shared `FeatureFlagManager` instance.
4847
*
4948
* @return an instance of `FeatureFlagManager`.
5049
*/
5150
@JvmStatic
52-
fun shared(): FeatureFlagManager =
51+
public fun shared(): FeatureFlagManager =
5352
UAirship.shared().requireComponent(FeatureFlagManager::class.java)
5453
}
5554

@@ -72,7 +71,7 @@ public class FeatureFlagManager
7271
* @param name The flag name
7372
* @return an instance of `PendingResult<FeatureFlag>`.
7473
*/
75-
fun flagAsPendingResult(name: String): PendingResult<FeatureFlag> {
74+
public fun flagAsPendingResult(name: String): PendingResult<FeatureFlag> {
7675
val result = PendingResult<FeatureFlag>()
7776
pendingResultScope.launch {
7877
result.result = flag(name).getOrNull()
@@ -86,7 +85,7 @@ public class FeatureFlagManager
8685
* @param name The flag name
8786
* @return an instance of `Result<FeatureFlag>`.
8887
*/
89-
suspend fun flag(name: String): Result<FeatureFlag> {
88+
public suspend fun flag(name: String): Result<FeatureFlag> {
9089
return flag(name = name, allowRefresh = true)
9190
}
9291

@@ -153,7 +152,7 @@ public class FeatureFlagManager
153152
* Tracks an interaction on a [FeatureFlag]. The [PrivacyManager.Feature.FEATURE_FLAGS]
154153
* must be enabled or this method will no-op.
155154
*/
156-
fun trackInteraction(flag: FeatureFlag) {
155+
public fun trackInteraction(flag: FeatureFlag) {
157156
if (!privacyManager.isEnabled(PrivacyManager.Feature.FEATURE_FLAGS)) {
158157
UALog.w { "Feature flags are disabled, unable to track interaction" }
159158
return

urbanairship-feature-flag/src/main/java/com/urbanairship/featureflag/FeatureFlagsModuleFactoryImpl.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ package com.urbanairship.featureflag
44

55
import android.content.Context
66
import androidx.annotation.Keep
7+
import androidx.annotation.RestrictTo
78
import com.urbanairship.BuildConfig
89
import com.urbanairship.PreferenceDataStore
910
import com.urbanairship.PrivacyManager
@@ -16,8 +17,10 @@ import com.urbanairship.modules.Module
1617
import com.urbanairship.modules.featureflag.FeatureFlagsModuleFactory
1718
import com.urbanairship.remotedata.RemoteData
1819

20+
/** @hide */
1921
@Keep
20-
class FeatureFlagsModuleFactoryImpl : FeatureFlagsModuleFactory {
22+
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
23+
public class FeatureFlagsModuleFactoryImpl : FeatureFlagsModuleFactory {
2124

2225
override fun build(
2326
context: Context,

urbanairship-feature-flag/src/main/java/com/urbanairship/featureflag/FlagDeferredResolver.kt

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
package com.urbanairship.featureflag
44

55
import com.urbanairship.AirshipDispatchers
6+
import com.urbanairship.UALog
67
import com.urbanairship.cache.AirshipCache
78
import com.urbanairship.deferred.DeferredRequest
89
import com.urbanairship.deferred.DeferredResolver
@@ -51,7 +52,13 @@ internal class FlagDeferredResolver(
5152
}
5253

5354
private suspend fun resolve(request: DeferredRequest, flagInfo: FeatureFlagInfo, requestId: String): Result<DeferredFlag> {
54-
val cached = cache.getCached(requestId, DeferredFlag::fromJson)
55+
val cached = try {
56+
cache.getCached(requestId, DeferredFlag::fromJson)
57+
} catch (e: JsonException) {
58+
UALog.w(e) { "Failed to parse cached deferred flag!" }
59+
null
60+
}
61+
5562
if (cached != null) {
5663
return Result.success(cached)
5764
}
@@ -82,7 +89,14 @@ internal class FlagDeferredResolver(
8289
backOffIntervals.remove(requestId)
8390
}
8491

85-
when (val result = resolver.resolve(request, DeferredFlagInfo::fromJson)) {
92+
val result = try {
93+
resolver.resolve(request, DeferredFlagInfo::fromJson)
94+
} catch (e: JsonException) {
95+
UALog.w(e) { "Failed to parse resolved deferred flag info!" }
96+
null
97+
}
98+
99+
when (result) {
86100
is DeferredResult.Success<DeferredFlagInfo> -> {
87101
return Result.success(DeferredFlag.Found(result.result))
88102
}
@@ -149,13 +163,14 @@ internal sealed class DeferredFlag: JsonSerializable {
149163

150164
data class Found(val flagInfo: DeferredFlagInfo): DeferredFlag() {
151165

166+
@Throws(JsonException::class)
152167
override fun toJsonValue(): JsonValue {
153168
return jsonMapOf(KEY_TYPE to ResultType.FOUND.jsonValue, KEY_FLAG to flagInfo).toJsonValue()
154169
}
155170
}
156171

157172
data object NotFound: DeferredFlag() {
158-
173+
@Throws(JsonException::class)
159174
override fun toJsonValue(): JsonValue {
160175
return jsonMapOf(KEY_TYPE to ResultType.NOT_FOUND.jsonValue).toJsonValue()
161176
}
@@ -183,6 +198,7 @@ internal data class DeferredFlagInfo(
183198
}
184199
}
185200

201+
@Throws(JsonException::class)
186202
override fun toJsonValue(): JsonValue {
187203
return jsonMapOf(
188204
KEY_IS_ELIGIBLE to isEligible,

urbanairship-feature-flag/src/test/java/com/urbanairship/featureflag/EvaluationOptionsTest.kt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,17 +6,17 @@ import org.junit.Test
66
import org.junit.runner.RunWith
77

88
@RunWith(AndroidJUnit4::class)
9-
class EvaluationOptionsTest {
9+
public class EvaluationOptionsTest {
1010

1111
@Test
12-
fun testParse() {
12+
public fun testParse() {
1313
val json = jsonMapOf(
1414
"disallow_stale_value" to true,
1515
"ttl" to 1800000
1616
)
1717

1818
val fromJson = EvaluationOptions.fromJson(json)
19-
assert(fromJson?.disallowStaleValues == true)
20-
assert(fromJson?.ttl == 1800000.toULong())
19+
assert(fromJson.disallowStaleValues == true)
20+
assert(fromJson.ttl == 1800000.toULong())
2121
}
2222
}

urbanairship-feature-flag/src/test/java/com/urbanairship/featureflag/FeatureFlagAnalyticsTest.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,14 +16,14 @@ import org.junit.Test
1616
import org.junit.runner.RunWith
1717

1818
@RunWith(AndroidJUnit4::class)
19-
class FeatureFlagAnalyticsTest {
19+
public class FeatureFlagAnalyticsTest {
2020

2121
private val analytics: Analytics = mockk()
2222
private val feed: AirshipEventFeed = mockk(relaxed = true)
2323
private val featureFlagAnalytics = FeatureFlagAnalytics(analytics)
2424

2525
@Test
26-
fun testTrackInteraction(): TestResult = runTest {
26+
public fun testTrackInteraction(): TestResult = runTest {
2727
coEvery { analytics.addEvent(any()) } returns true
2828

2929
val flag = FeatureFlag.createFlag("some-flag", true, generateReportingInfo())
@@ -38,7 +38,7 @@ class FeatureFlagAnalyticsTest {
3838
}
3939

4040
@Test
41-
fun testTrackInteractionFailed(): TestResult = runTest {
41+
public fun testTrackInteractionFailed(): TestResult = runTest {
4242
coEvery { analytics.addEvent(any()) } returns false
4343

4444
val flag = FeatureFlag.createFlag("some-flag", true, generateReportingInfo())
@@ -53,7 +53,7 @@ class FeatureFlagAnalyticsTest {
5353
}
5454

5555
@Test
56-
fun testTrackInteractionFlagDoesNotExist(): TestResult = runTest {
56+
public fun testTrackInteractionFlagDoesNotExist(): TestResult = runTest {
5757
val flag = FeatureFlag.createMissingFlag("some-flag")
5858
featureFlagAnalytics.trackInteraction(flag)
5959

@@ -63,7 +63,7 @@ class FeatureFlagAnalyticsTest {
6363
}
6464

6565
@Test
66-
fun testTrackInteractionMissingReportingInfo(): TestResult = runTest {
66+
public fun testTrackInteractionMissingReportingInfo(): TestResult = runTest {
6767
val flag = FeatureFlag(true, true, null)
6868
featureFlagAnalytics.trackInteraction(flag)
6969

0 commit comments

Comments
 (0)