diff --git a/Branch-SDK-Automation-TestBed/src/main/res/values/strings.xml b/Branch-SDK-Automation-TestBed/src/main/res/values/strings.xml index bf0b6d7f5..40842263a 100644 --- a/Branch-SDK-Automation-TestBed/src/main/res/values/strings.xml +++ b/Branch-SDK-Automation-TestBed/src/main/res/values/strings.xml @@ -253,6 +253,7 @@ INITIATE_PURCHASE ADD_PAYMENT_INFO PURCHASE + SPEND_CREDITS SEARCH VIEW_ITEM VIEW_ITEMS diff --git a/Branch-SDK/src/androidTest/java/io/branch/referral/test/mock/MockRemoteInterface.java b/Branch-SDK/src/androidTest/java/io/branch/referral/test/mock/MockRemoteInterface.java index 296f5bbce..f44fdd577 100644 --- a/Branch-SDK/src/androidTest/java/io/branch/referral/test/mock/MockRemoteInterface.java +++ b/Branch-SDK/src/androidTest/java/io/branch/referral/test/mock/MockRemoteInterface.java @@ -1,20 +1,23 @@ package io.branch.referral.test.mock; -import static io.branch.referral.Defines.RequestPath.GetCPID; -import static io.branch.referral.Defines.RequestPath.GetURL; -import static io.branch.referral.Defines.RequestPath.IdentifyUser; -import static io.branch.referral.Defines.RequestPath.QRCode; -import static io.branch.referral.Defines.RequestPath.RegisterInstall; -import static io.branch.referral.Defines.RequestPath.RegisterOpen; - import org.json.JSONObject; import java.util.UUID; +import io.branch.referral.Branch; import io.branch.referral.BranchTest; import io.branch.referral.PrefHelper; import io.branch.referral.network.BranchRemoteInterface; +import static io.branch.referral.Defines.RequestPath.GetCPID; +import static io.branch.referral.Defines.RequestPath.QRCode; +import static io.branch.referral.Defines.RequestPath.GetCreditHistory; +import static io.branch.referral.Defines.RequestPath.GetCredits; +import static io.branch.referral.Defines.RequestPath.GetURL; +import static io.branch.referral.Defines.RequestPath.IdentifyUser; +import static io.branch.referral.Defines.RequestPath.RegisterInstall; +import static io.branch.referral.Defines.RequestPath.RegisterOpen; + public class MockRemoteInterface extends BranchRemoteInterface { private final static String TAG = "MockRemoteInterface"; diff --git a/Branch-SDK/src/main/java/io/branch/referral/Branch.java b/Branch-SDK/src/main/java/io/branch/referral/Branch.java index 41d28fd0c..e4cd4222a 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/Branch.java +++ b/Branch-SDK/src/main/java/io/branch/referral/Branch.java @@ -291,6 +291,8 @@ public class Branch implements BranchViewHandler.IBranchViewEvents, SystemObserv private static boolean disableDeviceIDFetch_; + private boolean enableFacebookAppLinkCheck_ = false; + static boolean bypassWaitingForIntent_ = false; private static boolean bypassCurrentActivityIntentState_ = false; @@ -835,6 +837,24 @@ public void setDeepLinkDebugMode(JSONObject debugParams) { public void disableAppList() { // Do nothing } + + /** + *

+ * Enable Facebook app link check operation during Branch initialisation. + *

+ */ + public void enableFacebookAppLinkCheck() { + enableFacebookAppLinkCheck_ = true; + } + + /** + * Enables or disables app tracking with Branch or any other third parties that Branch use internally + * + * @param isLimitFacebookTracking {@code true} to limit app tracking + */ + public void setLimitFacebookTracking(boolean isLimitFacebookTracking) { + prefHelper_.setLimitFacebookTracking(isLimitFacebookTracking); + } /** *

Add key value pairs to all requests

@@ -1843,6 +1863,29 @@ private void initializeSession(ServerRequestInitSession initRequest, int delay) PrefHelper.Debug("Warning: You are using your test app's Branch Key. Remember to change it to live Branch Key during deployment."); } + if (initState_ == SESSION_STATE.UNINITIALISED && getSessionReferredLink() == null && enableFacebookAppLinkCheck_) { + // Check if opened by facebook with deferred install data + boolean appLinkRqSucceeded = DeferredAppLinkDataHandler.fetchDeferredAppLinkData( + context_, new DeferredAppLinkDataHandler.AppLinkFetchEvents() { + @Override + public void onAppLinkFetchFinished(String nativeAppLinkUrl) { + prefHelper_.setIsAppLinkTriggeredInit(true); // callback returns when app link fetch finishes with success or failure. Report app link checked in both cases + if (nativeAppLinkUrl != null) { + Uri appLinkUri = Uri.parse(nativeAppLinkUrl); + String bncLinkClickId = appLinkUri.getQueryParameter(Defines.Jsonkey.LinkClickID.getKey()); + if (!TextUtils.isEmpty(bncLinkClickId)) { + prefHelper_.setLinkClickIdentifier(bncLinkClickId); + } + } + requestQueue_.unlockProcessWait(ServerRequest.PROCESS_WAIT_LOCK.FB_APP_LINK_WAIT_LOCK); + processNextQueueItem(); + } + }); + if (appLinkRqSucceeded) { + initRequest.addProcessWaitLock(ServerRequest.PROCESS_WAIT_LOCK.FB_APP_LINK_WAIT_LOCK); + } + } + if (delay > 0) { initRequest.addProcessWaitLock(ServerRequest.PROCESS_WAIT_LOCK.USER_SET_WAIT_LOCK); new Handler().postDelayed(new Runnable() { diff --git a/Branch-SDK/src/main/java/io/branch/referral/DeferredAppLinkDataHandler.java b/Branch-SDK/src/main/java/io/branch/referral/DeferredAppLinkDataHandler.java new file mode 100644 index 000000000..2909b2ef6 --- /dev/null +++ b/Branch-SDK/src/main/java/io/branch/referral/DeferredAppLinkDataHandler.java @@ -0,0 +1,76 @@ +package io.branch.referral; + +import android.content.Context; +import android.os.Bundle; +import android.text.TextUtils; + +import java.lang.reflect.InvocationHandler; +import java.lang.reflect.Method; +import java.lang.reflect.Proxy; + +/** + * Created by sojanpr on 5/19/16. + * Class for handling deferred app links + */ +class DeferredAppLinkDataHandler { + private static final String NATIVE_URL_KEY = "com.facebook.platform.APPLINK_NATIVE_URL"; + + public static Boolean fetchDeferredAppLinkData(Context context, final AppLinkFetchEvents callback) { + boolean isRequestSucceeded = true; + try { + // Init FB SDK + Class FacebookSdkClass = Class.forName("com.facebook.FacebookSdk"); + Method initSdkMethod = FacebookSdkClass.getMethod("sdkInitialize", Context.class); + initSdkMethod.invoke(null, context); + + final Class AppLinkDataClass = Class.forName("com.facebook.applinks.AppLinkData"); + Class AppLinkDataCompletionHandlerClass = Class.forName("com.facebook.applinks.AppLinkData$CompletionHandler"); + Method fetchDeferredAppLinkDataMethod = AppLinkDataClass.getMethod("fetchDeferredAppLinkData", Context.class, String.class, AppLinkDataCompletionHandlerClass); + + InvocationHandler ALDataCompletionHandler = new InvocationHandler() { + @Override + public Object invoke(Object proxy, Method method, Object[] args) throws Exception { + if (method.getName().equals("onDeferredAppLinkDataFetched") && args[0] != null) { + String appLinkUrl = null; + Object appLinkDataClass = AppLinkDataClass.cast(args[0]); + Method getArgumentBundleMethod = AppLinkDataClass.getMethod("getArgumentBundle"); + Bundle appLinkDataBundle = Bundle.class.cast(getArgumentBundleMethod.invoke(appLinkDataClass)); + + if (appLinkDataBundle != null) { + appLinkUrl = appLinkDataBundle.getString(NATIVE_URL_KEY); + } + + if (callback != null) { + callback.onAppLinkFetchFinished(appLinkUrl); + } + + } else { + if (callback != null) { + callback.onAppLinkFetchFinished(null); + } + } + return null; + } + }; + + Object completionListenerInterface = Proxy.newProxyInstance(AppLinkDataCompletionHandlerClass.getClassLoader() + , new Class[]{AppLinkDataCompletionHandlerClass} + , ALDataCompletionHandler); + + String fbAppID = context.getString(context.getResources().getIdentifier("facebook_app_id", "string", context.getPackageName())); + if (TextUtils.isEmpty(fbAppID)) { + isRequestSucceeded = false; + } else { + fetchDeferredAppLinkDataMethod.invoke(null, context, fbAppID, completionListenerInterface); + } + + } catch (Exception ex) { + isRequestSucceeded = false; + } + return isRequestSucceeded; + } + + public interface AppLinkFetchEvents { + void onAppLinkFetchFinished(String nativeAppLinkUrl); + } +} diff --git a/Branch-SDK/src/main/java/io/branch/referral/Defines.java b/Branch-SDK/src/main/java/io/branch/referral/Defines.java index c3488e0c8..49cda941a 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/Defines.java +++ b/Branch-SDK/src/main/java/io/branch/referral/Defines.java @@ -21,6 +21,7 @@ public enum Jsonkey { ReferrerGclid("referrer_gclid"), //Key APIOpen expects for gclid in event ReferringUrlQueryParameters("bnc_referringUrlQueryParameters"), InstallBeginTimeStamp("install_begin_ts"), + FaceBookAppLinkChecked("facebook_app_link_checked"), @Deprecated BranchLinkUsed("branch_used"), //use Defines.IntentKeys.BranchLinkUsed ReferringBranchIdentity("referring_branch_identity"), BranchIdentity("branch_identity"), @@ -254,6 +255,8 @@ public enum RequestPath { RegisterInstall("v1/install"), RegisterClose("v1/close"), RegisterOpen("v1/open"), + @Deprecated GetCredits("v1/credits/"), + @Deprecated GetCreditHistory("v1/credithistory"), CompletedAction("v1/event"), IdentifyUser("v1/profile"), Logout("v1/logout"), diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java index 94d135b45..93975d3b4 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java +++ b/Branch-SDK/src/main/java/io/branch/referral/ServerRequest.java @@ -46,7 +46,7 @@ public abstract class ServerRequest { // Various process wait locks for Branch server request enum PROCESS_WAIT_LOCK { - SDK_INIT_WAIT_LOCK, GAID_FETCH_WAIT_LOCK, INTENT_PENDING_WAIT_LOCK, USER_SET_WAIT_LOCK, + SDK_INIT_WAIT_LOCK, FB_APP_LINK_WAIT_LOCK, GAID_FETCH_WAIT_LOCK, INTENT_PENDING_WAIT_LOCK, USER_SET_WAIT_LOCK, HUAWEI_INSTALL_REFERRER_FETCH_WAIT_LOCK, GOOGLE_INSTALL_REFERRER_FETCH_WAIT_LOCK, SAMSUNG_INSTALL_REFERRER_FETCH_WAIT_LOCK, @@ -157,6 +157,16 @@ boolean isPersistable() { return true; } + /** + * Specifies whether this request should add the limit app tracking value + * + * @return {@code true} to add the limit app tracking value to the request else false. + * {@code false} by default. Should override for requests that need limited app tracking value. + */ + protected boolean shouldUpdateLimitFacebookTracking() { + return false; + } + /** *

Provides the path to server for this request. * see {@link Defines.RequestPath}

@@ -522,6 +532,22 @@ private void updateRequestMetadata() { PrefHelper.Debug("Could not merge metadata, ignoring user metadata."); } } + + /* + * Update the the limit app tracking value to the request + */ + private void updateLimitFacebookTracking() { + JSONObject updateJson = getBranchRemoteAPIVersion() == BRANCH_API_VERSION.V1 ? params_ : params_.optJSONObject(Defines.Jsonkey.UserData.getKey()); + if (updateJson != null) { + boolean isLimitFacebookTracking = prefHelper_.isAppTrackingLimited(); // Currently only FB app tracking + if (isLimitFacebookTracking) { + try { + updateJson.putOpt(Defines.Jsonkey.limitFacebookTracking.getKey(), isLimitFacebookTracking); + } catch (JSONException ignore) { + } + } + } + } private void updateDisableAdNetworkCallouts() { JSONObject updateJson = getBranchRemoteAPIVersion() == BRANCH_API_VERSION.V1 ? params_ : params_.optJSONObject(Defines.Jsonkey.UserData.getKey()); @@ -552,6 +578,9 @@ private void removePreinstallData(JSONObject params) { void doFinalUpdateOnMainThread() { updateRequestMetadata(); + if (shouldUpdateLimitFacebookTracking()) { + updateLimitFacebookTracking(); + } } void doFinalUpdateOnBackgroundThread() { diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetCPID.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetCPID.java index 33d6405e4..e29133033 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetCPID.java +++ b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetCPID.java @@ -69,6 +69,11 @@ public BRANCH_API_VERSION getBranchRemoteAPIVersion() { return BRANCH_API_VERSION.V1_CPID; } + @Override + protected boolean shouldUpdateLimitFacebookTracking() { + return true; + } + public interface BranchCrossPlatformIdListener { void onDataFetched(BranchCPID branchCPID, BranchError error); } diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetLATD.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetLATD.java index 56721642f..7bdfae930 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetLATD.java +++ b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestGetLATD.java @@ -74,6 +74,11 @@ public BRANCH_API_VERSION getBranchRemoteAPIVersion() { return BRANCH_API_VERSION.V1_LATD; } + @Override + protected boolean shouldUpdateLimitFacebookTracking() { + return true; + } + public interface BranchLastAttributedTouchDataListener { void onDataFetched(JSONObject jsonObject, BranchError error); } diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestInitSession.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestInitSession.java index 14e24afc1..d257f7cab 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestInitSession.java +++ b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestInitSession.java @@ -55,6 +55,7 @@ protected void setPost(JSONObject post) throws JSONException { if(!TextUtils.isEmpty(prefHelper_.getInitialReferrer()) && !prefHelper_.getInitialReferrer().equals(PrefHelper.NO_STRING_VALUE)) { post.put(Defines.Jsonkey.InitialReferrer.getKey(), prefHelper_.getInitialReferrer()); } + post.put(Defines.Jsonkey.FaceBookAppLinkChecked.getKey(), prefHelper_.getIsAppLinkTriggeredInit()); updateInstallStateAndTimestamps(post); updateEnvironment(context_, post); @@ -66,6 +67,11 @@ protected void setPost(JSONObject post) throws JSONException { } } + @Override + protected boolean shouldUpdateLimitFacebookTracking() { + return true; + } + public abstract String getRequestActionName(); static boolean isInitSessionAction(String actionName) { @@ -141,8 +147,8 @@ void updateLinkReferrerParams() { if (!linkIdentifier.equals(PrefHelper.NO_STRING_VALUE)) { try { getPost().put(Defines.Jsonkey.LinkIdentifier.getKey(), linkIdentifier); - } - catch (JSONException ignore) { + getPost().put(Defines.Jsonkey.FaceBookAppLinkChecked.getKey(), prefHelper_.getIsAppLinkTriggeredInit()); + } catch (JSONException ignore) { } } // Add Google search install referrer if present @@ -285,6 +291,7 @@ protected boolean prepareExecuteWithoutTracking() { post.remove(Defines.Jsonkey.RandomizedDeviceToken.getKey()); post.remove(Defines.Jsonkey.RandomizedBundleToken.getKey()); + post.remove(Defines.Jsonkey.FaceBookAppLinkChecked.getKey()); post.remove(Defines.Jsonkey.External_Intent_Extra.getKey()); post.remove(Defines.Jsonkey.External_Intent_URI.getKey()); post.remove(Defines.Jsonkey.FirstInstallTime.getKey()); diff --git a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogEvent.java b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogEvent.java index b5ea7c15d..8177464cf 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogEvent.java +++ b/Branch-SDK/src/main/java/io/branch/referral/ServerRequestLogEvent.java @@ -87,6 +87,11 @@ public BRANCH_API_VERSION getBranchRemoteAPIVersion() { return BRANCH_API_VERSION.V2; //This is a v2 event } + @Override + protected boolean shouldUpdateLimitFacebookTracking() { + return true; + } + public boolean shouldRetryOnFail() { return true; // Branch event need to be retried on failure. } diff --git a/Branch-SDK/src/main/java/io/branch/referral/util/BRANCH_STANDARD_EVENT.java b/Branch-SDK/src/main/java/io/branch/referral/util/BRANCH_STANDARD_EVENT.java index 1ca330eba..243f15629 100644 --- a/Branch-SDK/src/main/java/io/branch/referral/util/BRANCH_STANDARD_EVENT.java +++ b/Branch-SDK/src/main/java/io/branch/referral/util/BRANCH_STANDARD_EVENT.java @@ -14,6 +14,7 @@ public enum BRANCH_STANDARD_EVENT { INITIATE_PURCHASE("INITIATE_PURCHASE"), ADD_PAYMENT_INFO("ADD_PAYMENT_INFO"), PURCHASE("PURCHASE"), + SPEND_CREDITS("SPEND_CREDITS"), // Content events SEARCH("SEARCH"),