Skip to content

Commit

Permalink
Update and implementation of UMP SDK to 2.2.0/2.4.0 (#1062)
Browse files Browse the repository at this point in the history
* Updated UMP dependency to 2.2.0

* Added UMP 2.2.0 Android implementation

* Added UMP 2.2.0 iOS implementation

* Added Android unit tests

* Added iOS unit tests
  • Loading branch information
LTPhantom authored May 2, 2024
1 parent aabe81a commit d4071e1
Show file tree
Hide file tree
Showing 21 changed files with 797 additions and 3 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/google_mobile_ads.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ jobs:
flutter clean
flutter pub get
pod install
xcodebuild -configuration Debug -resultBundlePath TestResults VERBOSE_SCRIPT_LOGGING=YES -workspace Runner.xcworkspace -scheme Runner -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 14 Pro,OS=16.2' test
xcodebuild -configuration Debug -resultBundlePath TestResults VERBOSE_SCRIPT_LOGGING=YES -workspace Runner.xcworkspace -scheme Runner -sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 15,OS=17.5' test
- uses: actions/upload-artifact@v4
if: failure()
with:
Expand Down
4 changes: 4 additions & 0 deletions packages/google_mobile_ads/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## Next Version
* Adds support for APIs from the [Android](https://developers.google.com/admob/android/privacy/release-notes) UMP SDK version 2.2.0.
* Adds support for APIs from the [iOS](https://developers.google.com/admob/ios/privacy/download#release_notes) UMP SDK version 2.4.0.

## 5.0.0
* Adds `MediationExtras` class to include parameters when using mediation through the implementation of `FlutterMediationExtras` in Android and `FlutterMediationExtras` in iOS.
* Deprecates `MediationNetworkExtrasProvider` and `FLTMediationNetworkExtrasProvider`.
Expand Down
2 changes: 1 addition & 1 deletion packages/google_mobile_ads/android/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ android {
}
dependencies {
api 'com.google.android.gms:play-services-ads:23.0.0'
implementation 'com.google.android.ump:user-messaging-platform:2.1.0'
implementation 'com.google.android.ump:user-messaging-platform:2.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-process:2.6.2'
implementation 'com.google.errorprone:error_prone_annotations:2.16'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.google.android.ump.ConsentForm;
import com.google.android.ump.FormError;
import io.flutter.plugin.common.StandardMessageCodec;
import java.io.ByteArrayOutputStream;
import java.nio.ByteBuffer;
Expand All @@ -31,6 +32,7 @@ public class UserMessagingCodec extends StandardMessageCodec {
private static final byte VALUE_CONSENT_REQUEST_PARAMETERS = (byte) 129;
private static final byte VALUE_CONSENT_DEBUG_SETTINGS = (byte) 130;
private static final byte VALUE_CONSENT_FORM = (byte) 131;
private static final byte VALUE_FORM_ERROR = (byte) 132;

private final Map<Integer, ConsentForm> consentFormMap;

Expand All @@ -53,6 +55,11 @@ protected void writeValue(@NonNull ByteArrayOutputStream stream, @NonNull Object
} else if (value instanceof ConsentForm) {
stream.write(VALUE_CONSENT_FORM);
writeValue(stream, value.hashCode());
} else if (value instanceof FormError) {
stream.write(VALUE_FORM_ERROR);
FormError formError = (FormError) value;
writeValue(stream, formError.getErrorCode());
writeValue(stream, formError.getMessage());
} else {
super.writeValue(stream, value);
}
Expand Down Expand Up @@ -96,6 +103,12 @@ protected Object readValueOfType(byte type, @NonNull ByteBuffer buffer) {
Integer hash = (Integer) readValueOfType(buffer.get(), buffer);
return consentFormMap.get(hash);
}
case VALUE_FORM_ERROR:
{
Integer errorCode = (Integer) readValueOfType(buffer.get(), buffer);
String errorMessage = (String) readValueOfType(buffer.get(), buffer);
return new FormError(errorCode, errorMessage);
}
default:
return super.readValueOfType(type, buffer);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,35 @@ public void onConsentInfoUpdateFailure(FormError error) {
});
break;
}
case "ConsentInformation#canRequestAds":
result.success(getConsentInformation().canRequestAds());
break;
case "ConsentInformation#getPrivacyOptionsRequirementStatus":
switch (getConsentInformation().getPrivacyOptionsRequirementStatus()) {
case NOT_REQUIRED:
result.success(0);
break;
case REQUIRED:
result.success(1);
break;
default:
result.success(2);
}
break;
case "UserMessagingPlatform#loadAndShowConsentFormIfRequired":
if (activity == null) {
result.error(
INTERNAL_ERROR_CODE,
"UserMessagingPlatform#loadAndShowConsentFormIfRequired called before plugin has been registered to an activity.",
null);
break;
}
UserMessagingPlatform.loadAndShowConsentFormIfRequired(
activity,
loadAndShowError -> {
result.success(loadAndShowError);
});
break;
case "UserMessagingPlatform#loadConsentForm":
UserMessagingPlatform.loadConsentForm(
context,
Expand All @@ -149,6 +178,20 @@ public void onConsentFormLoadFailure(FormError formError) {
}
});
break;
case "UserMessagingPlatform#showPrivacyOptionsForm":
if (activity == null) {
result.error(
INTERNAL_ERROR_CODE,
"UserMessagingPlatform#showPrivacyOptionsForm called before plugin has been registered to an activity.",
null);
break;
}
UserMessagingPlatform.showPrivacyOptionsForm(
activity,
loadAndShowError -> {
result.success(loadAndShowError);
});
break;
case "ConsentInformation#isConsentFormAvailable":
{
result.success(getConsentInformation().isConsentFormAvailable());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import static org.mockito.Mockito.mock;

import com.google.android.ump.ConsentForm;
import com.google.android.ump.FormError;
import java.nio.ByteBuffer;
import java.util.Collections;
import java.util.List;
Expand Down Expand Up @@ -130,4 +131,16 @@ public void testConsentForm() {
decoded = (ConsentForm) codec.decodeMessage((ByteBuffer) message.position(0));
assertNull(decoded);
}

@Test
public void testFormError() {
FormError formError = new FormError(123, "testMessage");

final ByteBuffer message = codec.encodeMessage(formError);
FormError decoded = (FormError) codec.decodeMessage((ByteBuffer) message.position(0));

assert decoded != null;
assertEquals(formError.getErrorCode(), decoded.getErrorCode());
assertEquals(formError.getMessage(), decoded.getMessage());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
import com.google.android.ump.ConsentInformation.ConsentStatus;
import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateFailureListener;
import com.google.android.ump.ConsentInformation.OnConsentInfoUpdateSuccessListener;
import com.google.android.ump.ConsentInformation.PrivacyOptionsRequirementStatus;
import com.google.android.ump.ConsentRequestParameters;
import com.google.android.ump.FormError;
import com.google.android.ump.UserMessagingPlatform;
Expand Down Expand Up @@ -170,6 +171,59 @@ public void testConsentInformation_isConsentFormAvailable() {
verify(result).success(eq(false));
}

@Test
public void testConsentInformation_canRequestAds() {
doReturn(true).when(mockConsentInformation).canRequestAds();
MethodCall methodCall = new MethodCall("ConsentInformation#canRequestAds", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

verify(result).success(eq(true));
}

@Test
public void testConsentInformation_getPrivacyOptionsRequirementStatus_notRequiredReturns0() {
doReturn(PrivacyOptionsRequirementStatus.NOT_REQUIRED)
.when(mockConsentInformation)
.getPrivacyOptionsRequirementStatus();
MethodCall methodCall =
new MethodCall("ConsentInformation#getPrivacyOptionsRequirementStatus", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

verify(result).success(eq(0));
}

@Test
public void testConsentInformation_getPrivacyOptionsRequirementStatus_requiredReturns1() {
doReturn(PrivacyOptionsRequirementStatus.REQUIRED)
.when(mockConsentInformation)
.getPrivacyOptionsRequirementStatus();
MethodCall methodCall =
new MethodCall("ConsentInformation#getPrivacyOptionsRequirementStatus", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

verify(result).success(eq(1));
}

@Test
public void testConsentInformation_getPrivacyOptionsRequirementStatus_requiredReturns2() {
doReturn(PrivacyOptionsRequirementStatus.UNKNOWN)
.when(mockConsentInformation)
.getPrivacyOptionsRequirementStatus();
MethodCall methodCall =
new MethodCall("ConsentInformation#getPrivacyOptionsRequirementStatus", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

verify(result).success(eq(2));
}

@Test
public void testUserMessagingPlatform_loadConsentFormAndDispose() {
MethodCall methodCall = new MethodCall("UserMessagingPlatform#loadConsentForm", null);
Expand Down Expand Up @@ -204,6 +258,78 @@ public void testUserMessagingPlatform_loadConsentFormAndDispose() {
verify(result).success(null);
}

@Test
public void testUserMessagingPlatform_loadAndShowConsentFormIfRequired() {
manager.setActivity(activity);
MethodCall methodCall =
new MethodCall("UserMessagingPlatform#loadAndShowConsentFormIfRequired", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

ArgumentCaptor<OnConsentFormDismissedListener> listenerCaptor =
ArgumentCaptor.forClass(OnConsentFormDismissedListener.class);
mockedUmp.verify(
() ->
UserMessagingPlatform.loadAndShowConsentFormIfRequired(
eq(activity), listenerCaptor.capture()));
listenerCaptor.getValue().onConsentFormDismissed(null);
verify(result).success(isNull());
}

@Test
public void testUserMessagingPlatform_loadAndShowConsentFormIfRequired_withFormError() {
manager.setActivity(activity);
FormError mockFormError = mock(FormError.class);
MethodCall methodCall =
new MethodCall("UserMessagingPlatform#loadAndShowConsentFormIfRequired", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

ArgumentCaptor<OnConsentFormDismissedListener> listenerCaptor =
ArgumentCaptor.forClass(OnConsentFormDismissedListener.class);
mockedUmp.verify(
() ->
UserMessagingPlatform.loadAndShowConsentFormIfRequired(
eq(activity), listenerCaptor.capture()));
listenerCaptor.getValue().onConsentFormDismissed(mockFormError);
verify(result).success(mockFormError);
}

@Test
public void testUserMessagingPlatform_showPrivacyOptionsForm() {
manager.setActivity(activity);
MethodCall methodCall = new MethodCall("UserMessagingPlatform#showPrivacyOptionsForm", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

ArgumentCaptor<OnConsentFormDismissedListener> listenerCaptor =
ArgumentCaptor.forClass(OnConsentFormDismissedListener.class);
mockedUmp.verify(
() -> UserMessagingPlatform.showPrivacyOptionsForm(eq(activity), listenerCaptor.capture()));
listenerCaptor.getValue().onConsentFormDismissed(null);
verify(result).success(isNull());
}

@Test
public void testUserMessagingPlatform_showPrivacyOptionsForm_withFormError() {
manager.setActivity(activity);
FormError mockFormError = mock(FormError.class);
MethodCall methodCall = new MethodCall("UserMessagingPlatform#showPrivacyOptionsForm", null);
Result result = mock(Result.class);

manager.onMethodCall(methodCall, result);

ArgumentCaptor<OnConsentFormDismissedListener> listenerCaptor =
ArgumentCaptor.forClass(OnConsentFormDismissedListener.class);
mockedUmp.verify(
() -> UserMessagingPlatform.showPrivacyOptionsForm(eq(activity), listenerCaptor.capture()));
listenerCaptor.getValue().onConsentFormDismissed(mockFormError);
verify(result).success(mockFormError);
}

@Test
public void testConsentForm_show() {
manager.setActivity(activity);
Expand Down
Loading

0 comments on commit d4071e1

Please sign in to comment.