Skip to content

Add getUtmParameters api method to PendingDynamicLinkData #2445

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Feb 18, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions firebase-dynamic-links/api.txt
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,7 @@ package com.google.firebase.dynamiclinks {
method @Nullable public android.net.Uri getLink();
method public int getMinimumAppVersion();
method @Nullable public android.content.Intent getUpdateAppIntent(@NonNull android.content.Context);
method @NonNull public android.os.Bundle getUtmParameters();
}

public interface ShortDynamicLink {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,13 @@
import com.google.android.gms.common.GooglePlayServicesUtil;
import com.google.android.gms.common.util.DefaultClock;
import com.google.firebase.dynamiclinks.internal.DynamicLinkData;
import com.google.firebase.dynamiclinks.internal.DynamicLinkUTMParams;

/** Provides accessor methods to dynamic links data. */
public class PendingDynamicLinkData {

private final DynamicLinkData dynamicLinkData;
@Nullable private final DynamicLinkUTMParams dynamicLinkUTMParams;

/**
* Create a dynamic link from parameters.
Expand All @@ -40,13 +42,15 @@ public class PendingDynamicLinkData {
public PendingDynamicLinkData(DynamicLinkData dynamicLinkData) {
if (dynamicLinkData == null) {
this.dynamicLinkData = null;
this.dynamicLinkUTMParams = null;
return;
}
if (dynamicLinkData.getClickTimestamp() == 0L) {
long now = DefaultClock.getInstance().currentTimeMillis();
dynamicLinkData.setClickTimestamp(now);
}
this.dynamicLinkData = dynamicLinkData;
this.dynamicLinkUTMParams = new DynamicLinkUTMParams(dynamicLinkData);
}

/**
Expand All @@ -61,6 +65,7 @@ protected PendingDynamicLinkData(
@Nullable String deepLink, int minVersion, long clickTimestamp, @Nullable Uri redirectUrl) {
dynamicLinkData =
new DynamicLinkData(null, deepLink, minVersion, clickTimestamp, null, redirectUrl);
dynamicLinkUTMParams = new DynamicLinkUTMParams(dynamicLinkData);
}

/**
Expand Down Expand Up @@ -100,6 +105,21 @@ public Uri getLink() {
return null;
}

/**
* Return the {@link Bundle} which contains utm parameters associated with the firebase dynamic
* link.
*
* @return Bundle of utm parameters associated with firebase dynamic link.
*/
@NonNull
public Bundle getUtmParameters() {
if (dynamicLinkUTMParams == null) {
return Bundle.EMPTY;
}

return dynamicLinkUTMParams.asBundle();
}

/**
* The minimum app version requested to process the dynamic link that can be compared directly
* with {@link android.content.pm.PackageInfo#versionCode}. If the minimum version code is higher
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.dynamiclinks.internal;

import android.os.Bundle;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.VisibleForTesting;

/**
* Class to extract UTM parameters from firebase dynamic link.
*
* @hide
*/
public class DynamicLinkUTMParams {

@VisibleForTesting public static final String KEY_CAMPAIGN_BUNDLE = "_cmp";
@VisibleForTesting public static final String KEY_SCION_DATA_BUNDLE = "scionData";
@VisibleForTesting public static final String KEY_MEDIUM = "medium";
@VisibleForTesting public static final String KEY_SOURCE = "source";
@VisibleForTesting public static final String KEY_CAMPAIGN = "campaign";

/** Key to retrieve utm_medium from utm params bundle returned by {@link #asBundle()} */
public static final String KEY_UTM_MEDIUM = "utm_medium";
/** Key to retrieve utm_source from utm params bundle returned by {@link #asBundle()} */
public static final String KEY_UTM_SOURCE = "utm_source";
/** Key to retrieve utm_campaign from utm params bundle returned by {@link #asBundle()} */
public static final String KEY_UTM_CAMPAIGN = "utm_campaign";

private final DynamicLinkData dynamicLinkData;
@NonNull private final Bundle utmParamsBundle;

public DynamicLinkUTMParams(DynamicLinkData dynamicLinkData) {
this.dynamicLinkData = dynamicLinkData;
this.utmParamsBundle = initUTMParamsBundle();
}

@NonNull
public Bundle asBundle() {
return new Bundle(utmParamsBundle);
}

private Bundle initUTMParamsBundle() {
if (dynamicLinkData == null || dynamicLinkData.getExtensionBundle() == null) {
return Bundle.EMPTY;
}

Bundle scionBundle = dynamicLinkData.getExtensionBundle().getBundle(KEY_SCION_DATA_BUNDLE);

if (scionBundle == null) {
return Bundle.EMPTY;
}

Bundle campaignBundle = scionBundle.getBundle(KEY_CAMPAIGN_BUNDLE);
if (campaignBundle == null) {
return Bundle.EMPTY;
}

Bundle bundle = new Bundle();
checkAndAdd(KEY_MEDIUM, KEY_UTM_MEDIUM, campaignBundle, bundle);
checkAndAdd(KEY_SOURCE, KEY_UTM_SOURCE, campaignBundle, bundle);
checkAndAdd(KEY_CAMPAIGN, KEY_UTM_CAMPAIGN, campaignBundle, bundle);
return bundle;
}

/*
* Checks and adds the value from source bundle to the destination bundle based on the source
* key and destination key.
*/
private void checkAndAdd(
@NonNull String sourceKey,
@NonNull String destKey,
@NonNull Bundle source,
@NonNull Bundle dest) {
String value = source.getString(sourceKey);
if (!TextUtils.isEmpty(value)) {
dest.putString(destKey, value);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertNotSame;
import static junit.framework.Assert.assertNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
Expand All @@ -30,6 +31,7 @@
import android.net.Uri;
import android.os.Bundle;
import com.google.firebase.dynamiclinks.internal.DynamicLinkData;
import com.google.firebase.dynamiclinks.internal.DynamicLinkUTMParams;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
Expand Down Expand Up @@ -189,6 +191,39 @@ public void testGetUpdateAppIntent_NameNotFoundException() throws Exception {
assertNull(intent);
}

@Test
public void testGetUtmParameters_WithEmptyUTMParams() {
PendingDynamicLinkData pendingDynamicLinkData =
new PendingDynamicLinkData(createDynamicLinkDataExtensions());
assertNotNull(pendingDynamicLinkData.getUtmParameters());
assertEquals(pendingDynamicLinkData.getUtmParameters().size(), 0);
}

@Test
public void testGetUtmParameters_WithNonEmptyUTMParams() {
Bundle scionBundle = new Bundle();
Bundle campaignBundle = new Bundle();
scionBundle.putBundle(DynamicLinkUTMParams.KEY_CAMPAIGN_BUNDLE, campaignBundle);
campaignBundle.putString(DynamicLinkUTMParams.KEY_MEDIUM, "m");
campaignBundle.putString(DynamicLinkUTMParams.KEY_SOURCE, "s");
campaignBundle.putString(DynamicLinkUTMParams.KEY_CAMPAIGN, "c");

DynamicLinkData dynamicLinkData = createDynamicLinkDataExtensions();
dynamicLinkData
.getExtensionBundle()
.putBundle(DynamicLinkUTMParams.KEY_SCION_DATA_BUNDLE, scionBundle);
PendingDynamicLinkData pendingDynamicLinkData = new PendingDynamicLinkData(dynamicLinkData);

Bundle utmParamsBundle = pendingDynamicLinkData.getUtmParameters();
assertNotNull(utmParamsBundle);
assertNotSame(utmParamsBundle.size(), 0);

// Comparing Utm params
assertEquals(utmParamsBundle.getString(DynamicLinkUTMParams.KEY_UTM_MEDIUM), "m");
assertEquals(utmParamsBundle.getString(DynamicLinkUTMParams.KEY_UTM_SOURCE), "s");
assertEquals(utmParamsBundle.getString(DynamicLinkUTMParams.KEY_UTM_CAMPAIGN), "c");
}

private DynamicLinkData createDynamicLinkData() {
return new DynamicLinkData(
DYNAMIC_LINK, DEEP_LINK, MINIMUM_VERSION, CLICK_TIMESTAMP, null, updateAppUri);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
// Copyright 2021 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package com.google.firebase.dynamiclinks.internal;

import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertFalse;
import static junit.framework.Assert.assertNotNull;
import static junit.framework.Assert.assertTrue;

import android.os.Bundle;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;

@RunWith(RobolectricTestRunner.class)
public class DynamicLinkUTMParamsTest {

@Test
public void testAsBundle_WithNullExtensions() {
DynamicLinkUTMParams dynamicLinkUTMParams = new DynamicLinkUTMParams(getDynamicLinkData(null));
assertNonNullEmptyBundle(dynamicLinkUTMParams.asBundle());
}

@Test
public void testAsBundle_WithExtensionsButNullScionBundle() {
Bundle extensions = getExtensions();
extensions.remove(DynamicLinkUTMParams.KEY_SCION_DATA_BUNDLE);
DynamicLinkUTMParams dynamicLinkUTMParams =
new DynamicLinkUTMParams(getDynamicLinkData(extensions));
assertNonNullEmptyBundle(dynamicLinkUTMParams.asBundle());
}

@Test
public void testAsBundle_WithExtensionsButNullCampaignBundle() {
Bundle extensions = getExtensions();
extensions
.getBundle(DynamicLinkUTMParams.KEY_SCION_DATA_BUNDLE)
.remove(DynamicLinkUTMParams.KEY_CAMPAIGN_BUNDLE);
DynamicLinkUTMParams dynamicLinkUTMParams =
new DynamicLinkUTMParams(getDynamicLinkData(extensions));
assertNonNullEmptyBundle(dynamicLinkUTMParams.asBundle());
}

@Test
public void testAsBundle_WithExtensionsButNullUtmParams() {
DynamicLinkUTMParams dynamicLinkUTMParams =
new DynamicLinkUTMParams(getDynamicLinkData(getExtensions()));
assertNonNullEmptyBundle(dynamicLinkUTMParams.asBundle());
}

@Test
public void testAsBundle_WithExtensionsButEmptyUtmParams() {
DynamicLinkUTMParams dynamicLinkUTMParams =
new DynamicLinkUTMParams(getDynamicLinkData(getExtensions("", "", "")));
assertNonNullEmptyBundle(dynamicLinkUTMParams.asBundle());
}

@Test
public void testAsBundle_WithExtensionsContainingUtmParams() {
DynamicLinkUTMParams dynamicLinkUTMParams =
new DynamicLinkUTMParams(getDynamicLinkData(getExtensions("m", "s", "c")));
// Non empty check
assertFalse(isEmptyBundle(dynamicLinkUTMParams.asBundle()));

// Comparing Utm params
assertEquals(
dynamicLinkUTMParams.asBundle().getString(DynamicLinkUTMParams.KEY_UTM_MEDIUM), "m");
assertEquals(
dynamicLinkUTMParams.asBundle().getString(DynamicLinkUTMParams.KEY_UTM_SOURCE), "s");
assertEquals(
dynamicLinkUTMParams.asBundle().getString(DynamicLinkUTMParams.KEY_UTM_CAMPAIGN), "c");
}

private void assertNonNullEmptyBundle(Bundle bundle) {
assertNotNull(bundle);
assertTrue(isEmptyBundle(bundle));
}

private boolean isEmptyBundle(Bundle bundle) {
return bundle.size() == 0;
}

private Bundle getExtensions() {
return getExtensions(null, null, null);
}

private Bundle getExtensions(String medium, String source, String campaign) {
Bundle bundle = new Bundle();
Bundle scionBundle = new Bundle();
Bundle campaignBundle = new Bundle();

bundle.putBundle(DynamicLinkUTMParams.KEY_SCION_DATA_BUNDLE, scionBundle);
scionBundle.putBundle(DynamicLinkUTMParams.KEY_CAMPAIGN_BUNDLE, campaignBundle);

campaignBundle.putString(DynamicLinkUTMParams.KEY_MEDIUM, medium);
campaignBundle.putString(DynamicLinkUTMParams.KEY_SOURCE, source);
campaignBundle.putString(DynamicLinkUTMParams.KEY_CAMPAIGN, campaign);

return bundle;
}

private DynamicLinkData getDynamicLinkData(Bundle extensions) {
return new DynamicLinkData(null, null, 0, 0L, extensions, null);
}
}