Skip to content

Commit 125a493

Browse files
authored
Make ABT Dynamic-Module compliant. (#2524)
* Make ABT Dynamic-Module compliant. Changes the dependency on analytics to an optional Provider. * Rename and improve lint test that verifies fall positives are gone. * Address review comments.
1 parent 8b5b455 commit 125a493

File tree

6 files changed

+51
-20
lines changed

6 files changed

+51
-20
lines changed

firebase-abt/src/main/java/com/google/firebase/abt/FirebaseABTesting.java

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
import androidx.annotation.WorkerThread;
2424
import com.google.firebase.analytics.connector.AnalyticsConnector;
2525
import com.google.firebase.analytics.connector.AnalyticsConnector.ConditionalUserProperty;
26+
import com.google.firebase.inject.Provider;
2627
import java.lang.annotation.Retention;
2728
import java.lang.annotation.RetentionPolicy;
2829
import java.util.ArrayDeque;
@@ -56,7 +57,7 @@ public class FirebaseABTesting {
5657
static final String ORIGIN_LAST_KNOWN_START_TIME_KEY_FORMAT = "%s_lastKnownExperimentStartTime";
5758

5859
/** The App's Firebase Analytics client. */
59-
private final AnalyticsConnector analyticsConnector;
60+
private final Provider<AnalyticsConnector> analyticsConnector;
6061

6162
/** The name of an ABT client. */
6263
private final String originService;
@@ -89,7 +90,7 @@ public class FirebaseABTesting {
8990
*/
9091
public FirebaseABTesting(
9192
Context unusedAppContext,
92-
AnalyticsConnector analyticsConnector,
93+
Provider<AnalyticsConnector> analyticsConnector,
9394
@OriginService String originService) {
9495
this.analyticsConnector = analyticsConnector;
9596
this.originService = originService;
@@ -333,11 +334,11 @@ private static List<AbtExperimentInfo> convertMapsToExperimentInfos(
333334
}
334335

335336
private void addExperimentToAnalytics(ConditionalUserProperty experiment) {
336-
analyticsConnector.setConditionalUserProperty(experiment);
337+
analyticsConnector.get().setConditionalUserProperty(experiment);
337338
}
338339

339340
private void throwAbtExceptionIfAnalyticsIsNull() throws AbtException {
340-
if (analyticsConnector == null) {
341+
if (analyticsConnector.get() == null) {
341342
throw new AbtException(
342343
"The Analytics SDK is not available. "
343344
+ "Please check that the Analytics SDK is included in your app dependencies.");
@@ -350,14 +351,16 @@ private void throwAbtExceptionIfAnalyticsIsNull() throws AbtException {
350351
* breaking, or if the underlying Analytics clear method is failing.
351352
*/
352353
private void removeExperimentFromAnalytics(String experimentId) {
353-
analyticsConnector.clearConditionalUserProperty(
354-
experimentId, /*clearEventName=*/ null, /*clearEventParams=*/ null);
354+
analyticsConnector
355+
.get()
356+
.clearConditionalUserProperty(
357+
experimentId, /*clearEventName=*/ null, /*clearEventParams=*/ null);
355358
}
356359

357360
@WorkerThread
358361
private int getMaxUserPropertiesInAnalytics() {
359362
if (maxUserProperties == null) {
360-
maxUserProperties = analyticsConnector.getMaxUserProperties(originService);
363+
maxUserProperties = analyticsConnector.get().getMaxUserProperties(originService);
361364
}
362365
return maxUserProperties;
363366
}
@@ -370,7 +373,8 @@ private int getMaxUserPropertiesInAnalytics() {
370373
*/
371374
@WorkerThread
372375
private List<ConditionalUserProperty> getAllExperimentsInAnalytics() {
373-
return analyticsConnector.getConditionalUserProperties(
374-
originService, /*propertyNamePrefix=*/ "");
376+
return analyticsConnector
377+
.get()
378+
.getConditionalUserProperties(originService, /*propertyNamePrefix=*/ "");
375379
}
376380
}

firebase-abt/src/main/java/com/google/firebase/abt/component/AbtComponent.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import com.google.firebase.abt.FirebaseABTesting;
2121
import com.google.firebase.abt.FirebaseABTesting.OriginService;
2222
import com.google.firebase.analytics.connector.AnalyticsConnector;
23+
import com.google.firebase.inject.Provider;
2324
import java.util.HashMap;
2425
import java.util.Map;
2526

@@ -37,11 +38,11 @@ public class AbtComponent {
3738
private final Map<String, FirebaseABTesting> abtOriginInstances = new HashMap<>();
3839

3940
private final Context appContext;
40-
private final AnalyticsConnector analyticsConnector;
41+
private final Provider<AnalyticsConnector> analyticsConnector;
4142

4243
/** Firebase ABT Component constructor. */
4344
@VisibleForTesting(otherwise = VisibleForTesting.PACKAGE_PRIVATE)
44-
protected AbtComponent(Context appContext, AnalyticsConnector analyticsConnector) {
45+
protected AbtComponent(Context appContext, Provider<AnalyticsConnector> analyticsConnector) {
4546
this.appContext = appContext;
4647
this.analyticsConnector = analyticsConnector;
4748
}

firebase-abt/src/main/java/com/google/firebase/abt/component/AbtRegistrar.java

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,11 +38,12 @@ public List<Component<?>> getComponents() {
3838
return Arrays.asList(
3939
Component.builder(AbtComponent.class)
4040
.add(Dependency.required(Context.class))
41-
.add(Dependency.optional(AnalyticsConnector.class))
41+
.add(Dependency.optionalProvider(AnalyticsConnector.class))
4242
.factory(
4343
container ->
4444
new AbtComponent(
45-
container.get(Context.class), container.get(AnalyticsConnector.class)))
45+
container.get(Context.class),
46+
container.getProvider(AnalyticsConnector.class)))
4647
.build(),
4748
LibraryVersionComponent.create("fire-abt", BuildConfig.VERSION_NAME));
4849
}

firebase-abt/src/test/java/com/google/firebase/abt/FirebaseABTestingTest.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ public void setUp() {
7676
firebaseAbt =
7777
new FirebaseABTesting(
7878
/*unusedAppContext=*/ null,
79-
/*analyticsConnector=*/ mockAnalyticsConnector,
79+
/*analyticsConnector=*/ () -> mockAnalyticsConnector,
8080
ORIGIN_SERVICE);
8181

8282
when(mockAnalyticsConnector.getMaxUserProperties(ORIGIN_SERVICE))
@@ -189,7 +189,7 @@ public void replaceAllExperiments_totalExperimentsExceedsAnalyticsLimit_oldExper
189189
public void replaceAllExperiments_analyticsSdkUnavailable_throwsAbtException() {
190190
firebaseAbt =
191191
new FirebaseABTesting(
192-
/*unusedAppContext=*/ null, /*analyticsConnector=*/ null, ORIGIN_SERVICE);
192+
/*unusedAppContext=*/ null, /*analyticsConnector=*/ () -> null, ORIGIN_SERVICE);
193193

194194
AbtException actualException =
195195
assertThrows(
@@ -243,7 +243,7 @@ public void removeAllExperiments_existExperimentsInAnalytics_experimentsClearedF
243243
public void removeAllExperiments_analyticsSdkUnavailable_throwsAbtException() {
244244
firebaseAbt =
245245
new FirebaseABTesting(
246-
/*unusedAppContext=*/ null, /*analyticsConnector=*/ null, ORIGIN_SERVICE);
246+
/*unusedAppContext=*/ null, /*analyticsConnector=*/ () -> null, ORIGIN_SERVICE);
247247

248248
AbtException actualException =
249249
assertThrows(AbtException.class, () -> firebaseAbt.removeAllExperiments());
@@ -281,7 +281,7 @@ public void getAllExperiments_existExperimentsInAnalytics_returnAllExperiments()
281281
public void getAllExperiments_analyticsSdkUnavailable_throwsAbtException() {
282282
firebaseAbt =
283283
new FirebaseABTesting(
284-
/*unusedAppContext=*/ null, /*analyticsConnector=*/ null, ORIGIN_SERVICE);
284+
/*unusedAppContext=*/ null, /*analyticsConnector=*/ () -> null, ORIGIN_SERVICE);
285285

286286
AbtException actualException =
287287
assertThrows(AbtException.class, () -> firebaseAbt.getAllExperiments());

tools/lint/src/main/kotlin/ProviderAssignmentDetector.kt

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,17 @@ class ProviderAssignmentDetector : Detector(), SourceCodeScanner {
3939
if (!isProviderGet(method)) {
4040
return
4141
}
42-
val assignmentTarget = node.getParentOfType<JavaUAssignmentExpression>(
43-
JavaUAssignmentExpression::class.java,
44-
true)?.leftOperand as? UReferenceExpression ?: return
42+
val assignmentExpression = node
43+
.getParentOfType<JavaUAssignmentExpression>(
44+
JavaUAssignmentExpression::class.java, true)
45+
val assignmentTarget = assignmentExpression?.leftOperand as? UReferenceExpression ?: return
46+
47+
// This would only be true if assigning the result of get(),
48+
// in cases like foo = p.get().someMethod() there would be an intermediate parent
49+
// and we don't want to trigger in such cases.
50+
if (assignmentExpression != node.uastParent?.uastParent) {
51+
return
52+
}
4553
assignmentTarget.resolve()?.let {
4654
if (it is PsiField) {
4755
context.report(

tools/lint/src/test/kotlin/ProviderAssignmentDetectorTests.kt

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,21 @@ class ProviderAssignmentDetectorTests : LintDetectorTest() {
8787
.run()
8888
.expectClean()
8989
}
90+
91+
fun test_assignmentOfAPropertyOrMethodOfTheProvidedObject_shouldSucceed() {
92+
lint().files(java(providerSource()), java("""
93+
import com.google.firebase.inject.Provider;
94+
95+
class Foo {
96+
private final int length;
97+
private final String s;
98+
Foo(Provider<String> p) {
99+
length = p.get().length;
100+
s = p.get().toString();
101+
}
102+
}
103+
""".trimIndent()))
104+
.run()
105+
.expectClean()
106+
}
90107
}

0 commit comments

Comments
 (0)