Android Advanced Features
Create Content Reference
- The Branch Universal Object encapsulates the thing you want to share (content or user)
- Uses the Universal Object Properties
val buo = BranchUniversalObject()
.setCanonicalIdentifier("content/12345")
.setTitle("My Content Title")
.setContentDescription("My Content Description")
.setContentImageUrl("https://lorempixel.com/400/400")
.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setContentMetadata(ContentMetadata().addCustomMetadata("key1", "value1"))
BranchUniversalObject buo = new BranchUniversalObject()
.setCanonicalIdentifier("content/12345")
.setTitle("My Content Title")
.setContentDescription("My Content Description")
.setContentImageUrl("https://lorempixel.com/400/400")
.setContentIndexingMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setLocalIndexMode(BranchUniversalObject.CONTENT_INDEX_MODE.PUBLIC)
.setContentMetadata(new ContentMetadata().addCustomMetadata("key1", "value1"));
Create Deep Link
- Creates a deep link URL with encapsulated data
- Needs a Branch Universal Object
- Uses Deep Link Properties
- Validate with the Branch Dashboard
val lp = LinkProperties()
.setChannel("facebook")
.setFeature("sharing")
.setCampaign("content 123 launch")
.setStage("new user")
.addControlParameter("$desktop_url", "http://example.com/home")
.addControlParameter("custom", "data")
.addControlParameter("custom_random", Long.toString(Calendar.getInstance().getTimeInMillis()))
buo.generateShortUrl(this, lp, BranchLinkCreateListener { url?, error? ->
if (error == null) {
Log.i("BRANCH SDK", "got my Branch link to share: " + url)
}
})
LinkProperties lp = new LinkProperties()
.setChannel("facebook")
.setFeature("sharing")
.setCampaign("content 123 launch")
.setStage("new user")
.addControlParameter("$desktop_url", "https://example.com/home")
.addControlParameter("custom", "data")
.addControlParameter("custom_random", Long.toString(Calendar.getInstance().getTimeInMillis()));
buo.generateShortUrl(this, lp, new Branch.BranchLinkCreateListener() {
@Override
public void onLinkCreate(String url, BranchError error) {
if (error == null) {
Log.i("BRANCH SDK", "got my Branch link to share: " + url);
}
}
});
Share Deep Link
- Will generate a Branch deep link and tag it with the channel the user selects
- Needs a Branch Universal Object
- Uses Deep Link Properties
var lp = LinkProperties()
.setChannel("facebook")
.setFeature("sharing")
.setCampaign("content 123 launch")
.setStage("new user")
.addControlParameter("$desktop_url", "http://example.com/home")
.addControlParameter("custom", "data")
.addControlParameter("custom_random", Long.toString(Calendar.getInstance().getTimeInMillis()))
val ss = ShareSheetStyle(this@MainActivity, "Check this out!", "This stuff is awesome: ")
.setCopyUrlStyle(resources.getDrawable(this, android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
.setMoreOptionStyle(resources.getDrawable(this, android.R.drawable.ic_menu_search), "Show more")
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.EMAIL)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.MESSAGE)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.HANGOUT)
.setAsFullWidthStyle(true)
.setSharingTitle("Share With")
buo.showShareSheet(this, lp, ss, object : Branch.BranchLinkShareListener {
override fun onShareLinkDialogLaunched() {}
override fun onShareLinkDialogDismissed() {}
override fun onLinkShareResponse(sharedLink: String?, sharedChannel: String?, error: BranchError?) {}
override fun onChannelSelected(channelName: String) {}
})
LinkProperties lp = new LinkProperties()
.setChannel("facebook")
.setFeature("sharing")
.setCampaign("content 123 launch")
.setStage("new user")
.addControlParameter("$desktop_url", "https://example.com/home")
.addControlParameter("custom", "data")
.addControlParameter("custom_random", Long.toString(Calendar.getInstance().getTimeInMillis()));
ShareSheetStyle ss = new ShareSheetStyle(MainActivity.this, "Check this out!", "This stuff is awesome: ")
.setCopyUrlStyle(ContextCompat.getDrawable(this, android.R.drawable.ic_menu_send), "Copy", "Added to clipboard")
.setMoreOptionStyle(ContextCompat.getDrawable(this, android.R.drawable.ic_menu_search), "Show more")
.addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.EMAIL)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.MESSAGE)
.addPreferredSharingOption(SharingHelper.SHARE_WITH.HANGOUT)
.setAsFullWidthStyle(true)
.setSharingTitle("Share With");
buo.showShareSheet(this, lp, ss, new Branch.BranchLinkShareListener() {
@Override
public void onShareLinkDialogLaunched() {
}
@Override
public void onShareLinkDialogDismissed() {
}
@Override
public void onLinkShareResponse(String sharedLink, String sharedChannel, BranchError error) {
}
@Override
public void onChannelSelected(String channelName) {
}
});
Read Deep Link
- Retrieve Branch data from a deep link
- Best practice to receive data from the
listener
(to prevent a race condition) - Returns deep link properties
// listener (within Main Activity's onStart)
Branch.sessionBuilder(this).withCallback(object : BranchReferralInitListener {
override fun onInitFinished(referringParams: JSONObject?, error: BranchError?) {
if (error == null) {
Log.i("BRANCH SDK", referringParams.toString())
} else {
Log.e("BRANCH SDK", error.message)
}
}
}).withData(this.intent.data).init()
// latest
val sessionParams = Branch.getInstance().latestReferringParams
// first
val installParams = Branch.getInstance().firstReferringParams
// listener (within Main Activity's onStart)
Branch.sessionBuilder(this).withCallback(new Branch.BranchReferralInitListener() {
@Override
public void onInitFinished(JSONObject referringParams, BranchError error) {
if (error == null) {
Log.i("BRANCH SDK", referringParams.toString());
} else {
Log.i("BRANCH SDK", error.getMessage());
}
}
}).withData(this.getIntent().getData()).init();
// latest
JSONObject sessionParams = Branch.getInstance().getLatestReferringParams();
// first
JSONObject installParams = Branch.getInstance().getFirstReferringParams();
Create QR Code
- Create a BranchQRCode object and set the properties
- Create a BranchUniversalObject and BranchLinkProperties
- Use getQRCodeAsImage() or getQRCodeAsData() to get your QR code
val qrCode = BranchQRCode()
.setCodeColor("#a4c639")
.setBackgroundColor(Color.WHITE)
.setMargin(1)
.setWidth(512)
.setImageFormat(BranchQRCode.BranchImageFormat.JPEG)
.setCenterLogo("https://cdn.branch.io/branch-assets/1598575682753-og_image.png")
val buo = BranchUniversalObject()
.setCanonicalIdentifier("content/12345")
.setTitle("My Content Title")
.setContentDescription("My Content Description")
.setContentImageUrl("https://lorempixel.com/400/400")
val lp = LinkProperties()
.setChannel("facebook")
.setFeature("sharing")
.setCampaign("content 123 launch")
.setStage("new user")
qrCode.getQRCodeAsImage(this@MainActivity, buo, lp, object : BranchQRCodeImageHandler<Any?> {
override fun onSuccess(qrCodeImage: Bitmap) {
//Do something with your QR code here.
}
override fun onFailure(e: Exception) {
Log.d("Failed to get QR code", e.toString())
}
})
BranchQRCode qrCode = new BranchQRCode() //All QR code settings are optional
.setCodeColor("#a4c639")
.setBackgroundColor(Color.WHITE)
.setMargin(1)
.setWidth(512)
.setImageFormat(BranchQRCode.BranchImageFormat.PNG)
.setCenterLogo("https://cdn.branch.io/branch-assets/1598575682753-og_image.png");
BranchUniversalObject buo = new BranchUniversalObject()
.setCanonicalIdentifier("content/12345")
.setTitle("My QR Code");
LinkProperties lp = new LinkProperties()
.setChannel("facebook")
.setFeature("qrCode")
.setCampaign("content 123 launch");
qrCode.getQRCodeAsImage(MainActivity.this, buo, lp, new BranchQRCode.BranchQRCodeImageHandler() {
@Override
public void onSuccess(Bitmap qrCodeImage) {
//Do something with the QR code here.
}
@Override
public void onFailure(Exception e) {
Log.d("Failed to get QR code", String.valueOf(e));
}
});
Navigate to Content
- Do stuff with the Branch deep link data
// Listener (within Main Activity's onStart)
Branch.sessionBuilder(this).withCallback(object : BranchReferralInitListener {
override fun onInitFinished(referringParams: JSONObject?, error: BranchError?) {
if (error == null) {
// Option 1: log data
Log.i("BRANCH SDK", referringParams.toString())
// Option 2: save data to be used later
val preferences = getSharedPreferences("MyPreferences", Context.MODE_PRIVATE)
preferences.edit().putString("branchData", referringParams.toString()).apply();
// Option 3: navigate to page
val intent = Intent(this@MainActivity, MainActivity2::class.java)
intent.putExtra("branchData", referringParams.toString())
startActivity(intent)
// Option 4: display data
Toast.makeText(this@MainActivity, referringParams.toString(), Toast.LENGTH_SHORT).show()
} else {
Log.e("BRANCH SDK", error.message)
}
}
}).withData(this.intent.data).init()
// Listener (within Main Activity's onStart)
Branch.sessionBuilder(this).withCallback(new Branch.BranchReferralInitListener() {
@Override
public void onInitFinished(JSONObject referringParams, BranchError error) {
if (error == null) {
// Option 1: log data
Log.i("BRANCH SDK", referringParams.toString());
// Option 2: save data to be used later
SharedPreferences preferences = MainActivity.this.getSharedPreferences("MyPreferences", Context.MODE_PRIVATE);
preferences.edit().putString("branchData", referringParams.toString()).apply();
// Option 3: navigate to page
Intent intent = new Intent(MainActivity.this, OtherActivity.class);
startActivity(intent);
// Option 4: display data
Toast.makeText(MainActivity.this, referringParams.toString(), Toast.LENGTH_LONG).show();
} else {
Log.i("BRANCH SDK", error.getMessage());
}
}
}).withData(this.getIntent().getData()).init();
- Needs a Branch Universal Object
- Needs
build.gradle
library
Track Content
- Track how many times a piece of content is viewed
- Needs a Branch Universal Object
- Uses Track content properties
- Validate with the Branch Dashboard
BranchEvent(BRANCH_STANDARD_EVENT.VIEW_ITEM).addContentItems(buo).logEvent(context)
new BranchEvent(BRANCH_STANDARD_EVENT.VIEW_ITEM).addContentItems(buo).logEvent(context);
Track Users
Sending PII
Be sure to not send any PII through this method. For additional details, please view our guide on Best Practices to Avoid Sending PII to Branch
- Sets the identity of a user (ID, UUID, etc) for events, deep links, and referrals
127
character max for user id- Validate with the Branch Dashboard
// login
Branch.getInstance().setIdentity("your_user_id");
// logout
Branch.getInstance().logout();
// login
Branch.getInstance().setIdentity("your_user_id");
// logout
Branch.getInstance().logout();
Track Events
- All events related to a customer purchasing are bucketed into a "Commerce" class of data items
- All events related to users interacting with your in-app content are bucketed to a "Content" class of data items.
- All events related to users progressing in your app are bucketed to a "Lifecycle" class of data items.
- To track custom events - not found in the table below - please see Track Custom Events
- Validate with the Branch Dashboard
Use the table below to quickly find the event you want to track.
Event Name | Event Category | Android |
---|---|---|
Add To Cart | Commerce Event | BRANCH_STANDARD_EVENT.ADD_TO_CART |
Add To Wishlist | Commerce Event | BRANCH_STANDARD_EVENT.ADD_TO_WISHLIST |
View Cart | Commerce Event | BRANCH_STANDARD_EVENT.VIEW_CART |
Initiate Purchase | Commerce Event | BRANCH_STANDARD_EVENT.INITIATE_PURCHASE |
Add Payment Info | Commerce Event | BRANCH_STANDARD_EVENT.ADD_PAYMENT_INFO |
Purchase | Commerce Event | BRANCH_STANDARD_EVENT.PURCHASE |
Spend Credits | Commerce Event | BRANCH_STANDARD_EVENT.SPEND_CREDITS |
Search | Content Event | BRANCH_STANDARD_EVENT.SEARCH |
View Item | Content Event | BRANCH_STANDARD_EVENT.VIEW_ITEM |
View Items | Content Event | BRANCH_STANDARD_EVENT.VIEW_ITEMS |
Rate | Content Event | BRANCH_STANDARD_EVENT.RATE |
Share | Content Event | BRANCH_STANDARD_EVENT.SHARE |
Complete Registration | Lifecycle Event | BRANCH_STANDARD_EVENT.COMPLETE_REGISTRATION |
Complete Tutorial | Lifecycle Event | BRANCH_STANDARD_EVENT.COMPLETE_TUTORIAL |
Achieve Level | Lifecycle Event | BRANCH_STANDARD_EVENT.ACHIEVE_LEVEL |
Unlock Achievement | Lifecycle Event | BRANCH_STANDARD_EVENT.UNLOCK_ACHIEVEMENT |
Handle Push Notifications
- Deep link to content from push notifications just by adding a Branch link to your result intent
val resultIntent = Intent(this, TargetClass::class.java)
resultIntent.putExtra("branch", "http://xxxx.app.link/testlink")
resultIntent.putExtra("branch_force_new_session", true)
val resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT)
Intent resultIntent = new Intent(this, TargetActivity.class);
resultIntent.putExtra("branch","https://xxxx.app.link/testlink");
resultIntent.putExtra("branch_force_new_session",true);
PendingIntent resultPendingIntent = PendingIntent.getActivity(this, 0, resultIntent, PendingIntent.FLAG_UPDATE_CURRENT);
- To handle situations where
TargetActivity
is in the foreground when push notification is clicked, don't forget to callsessionBuilder()...reInit()
fromonNewIntent
insideTargetActivity
:
override fun onNewIntent(intent: Intent) {
super.onNewIntent(intent)
this.intent = intent
// Branch reinit (in case Activity is already visible when Branch link is clicked)
// will re-initialize only if "branch_force_new_session=true" intent extra is set
Branch.sessionBuilder(this).withCallback(branchListener).reInit()
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
// if activity is in foreground (or in backstack but partially visible) launching the same
// activity will skip onStart, handle this case with reInitSession
if (intent != null &&
intent.hasExtra("branch_force_new_session") &&
intent.getBooleanExtra("branch_force_new_session")) {
Branch.sessionBuilder(this).withCallback(branchReferralInitListener).reInit();
}
}
Handling a new deep link via sessionBuilder()....reInit()
Handling a new deep link via
sessionBuilder()....reInit()
will clear the current session data, if there is any, and a new referred "open" will be attributed.
Handle Links in Your Own App
- Allows you to deep link into your own app from your app itself by launching a Chrome intent
val intent = Intent(this, ActivityToLaunch::class.java)
intent.putExtra("branch", "http://xxxx.app.link/testlink")
intent.putExtra("branch_force_new_session", true)
startActivity(intent)
Intent intent = new Intent(this, ActivityToLaunch.class);
intent.putExtra("branch","https://xxxx.app.link/testlink");
intent.putExtra("branch_force_new_session",true);
startActivity(intent);
- Replace "https://xxxx.app.link/testlink" with your own link URL
Handling a new deep link
Handling a new deep link in your app will clear the current session data and a new referred "open" will be attributed.
Linking to the currently open activity
Similarly to handling push notifications, linking to the currently open activity or an activity that's in the backstack and partially visible, must be handled via
sessionBuilder()....reInit()
.
Enable 100% Matching
- Uses Chrome Tabs to increase attribute matching success.
- Add
implementation 'androidx.browser:browser:1.5.0'
to yourbuild.gradle
. - If you need the custom tabs library but do not wish to enable 100% matching you can call
Branch.enableCookieBasedMatching(null)
prior to session initialization.
Delay Session Initialization
Sometimes, users may need to delay session initialization, for example to make an async call to retrieve some data that needs to be passed to Branch as request metadata.
In this case, you may run into an error "SDK already initialized", which happens due to Branch self-initializing the session when Activity enters RESUMED state. To avoid that, you can disable auto-session-initialization manually and initialize the session yourself after the async call finishes.
Note: expectDelayedSessionInitialization()
must be called before establishing the Branch singleton within your application class onCreate()
.
package com.eneff.branch.example.android
import android.app.Application
import io.branch.referral.Branch
class CustomApplicationClass : Application() {
override fun onCreate() {
super.onCreate()
// Delay Session Initialization
Branch.expectDelayedSessionInitialization()
// Branch object initialization
Branch.getAutoInstance(this)
}
}
package com.eneff.branch.example.android;
import android.app.Application;
import io.branch.referral.Branch;
public class CustomApplicationClass extends Application {
@Override
public void onCreate() {
super.onCreate();
// Delay Session Initialization
Branch.expectDelayedSessionInitialization();
// Branch object initialization
Branch.getAutoInstance(this);
}
}
Enable / Disable User Tracking
If you need to comply with a user's request to not be tracked for GDPR purposes, or otherwise determine that a user should not be tracked, utilize this field to prevent Branch from sending network requests. This setting can also be enabled across all users for a particular link, or across your Branch links.
Branch.getInstance().disableTracking(true)
Branch.getInstance().disableTracking(true);
You can choose to call this throughout the lifecycle of the app. Once called, network requests will not be sent from the SDKs. Link generation will continue to work, but will not contain identifying information about the user. In addition, deep linking will continue to work, but will not track analytics for the user.
Set initialization metadata
Data Integration Only
If you are using a 3rd Party Data Integration Partner that requires setting certain identifiers before initializing the Branch SDK, you should add this code snippet:
//Before you initialize the session.
Branch.getInstance().setRequestMetadata("{ANALYTICS_ID}", […]);
Replace {ANALYTICS_ID}
with your Data Integration Partner's key.
Control How Branch Handles Network Requests
By default, Branch Android SDK uses Java's javax.net.ssl.HttpsURLConnection
for network requests. There are known issues with that interface and typically applications use third party networking libraries, such as OkHttp, to customize their networking. Branch SDK provides an interface, BranchRemoteInterface
, to apply your networking customization to how Branch handles network requests. Use the below API to set your implementation of BranchRemoteInterface
to handle Branch requests.
setBranchRemoteInterface(remoteInterface: BranchRemoteInterface)
setBranchRemoteInterface(@NonNull BranchRemoteInterface remoteInterface)
Below is an example of an implementation of BranchRemoteInterface
using OkHttp.
public class OkhttpBranchNetworkInterface extends BranchRemoteInterface {
private OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(3, TimeUnit.SECONDS).writeTimeout(3, TimeUnit.SECONDS).connectTimeout(3, TimeUnit.SECONDS).build();
@Override
public BranchResponse doRestfulGet(String url) throws BranchRemoteException {
Request request = new Request.Builder().url(url).build();
return handleNetworkRequest(request);
}
@Override
public BranchResponse doRestfulPost(String url, JSONObject payload) throws BranchRemoteException {
Request request = new Request.Builder().url(url).post(RequestBody.create(MediaType.parse("application/json"), payload.toString())).build();
return handleNetworkRequest(request);
}
private BranchResponse handleNetworkRequest(Request request) throws BranchRemoteException {
try {
Response response = okHttpClient.newCall(request).execute();
ResponseBody rb = response.body();
if (rb != null) throw new BranchRemoteException(BranchError.ERR_BRANCH_NO_CONNECTIVITY);
return new BranchResponse(rb.string(), response.code());
} catch(Exception exception) {
if (exception instanceof SocketTimeoutException) {
// add desired retry logic, then eventually throw BranchError.ERR_BRANCH_REQ_TIMED_OUT
throw new BranchRemoteException(BranchError.ERR_BRANCH_REQ_TIMED_OUT);
} else {
// handle other failures as needed, throw BranchError.ERR_BRANCH_NO_CONNECTIVITY as a default
throw new BranchRemoteException(BranchError.ERR_BRANCH_NO_CONNECTIVITY);
}
}
}
}
public class OkhttpBranchNetworkInterface extends BranchRemoteInterface {
private OkHttpClient okHttpClient = new OkHttpClient.Builder().readTimeout(3, TimeUnit.SECONDS).writeTimeout(3, TimeUnit.SECONDS).connectTimeout(3, TimeUnit.SECONDS).build();
@Override
public BranchResponse doRestfulGet(String url) throws BranchRemoteException {
Request request = new Request.Builder().url(url).build();
return handleNetworkRequest(request);
}
@Override
public BranchResponse doRestfulPost(String url, JSONObject payload) throws BranchRemoteException {
Request request = new Request.Builder().url(url).post(RequestBody.create(MediaType.parse("application/json"), payload.toString())).build();
return handleNetworkRequest(request);
}
private BranchResponse handleNetworkRequest(Request request) throws BranchRemoteException {
try {
Response response = okHttpClient.newCall(request).execute();
ResponseBody rb = response.body();
if (rb != null) throw new BranchRemoteException(BranchError.ERR_BRANCH_NO_CONNECTIVITY);
return new BranchResponse(rb.string(), response.code());
} catch(Exception exception) {
if (exception instanceof SocketTimeoutException) {
// add desired retry logic, then eventually throw BranchError.ERR_BRANCH_REQ_TIMED_OUT
throw new BranchRemoteException(BranchError.ERR_BRANCH_REQ_TIMED_OUT);
} else {
// handle other failures as needed, throw BranchError.ERR_BRANCH_NO_CONNECTIVITY as a default
throw new BranchRemoteException(BranchError.ERR_BRANCH_NO_CONNECTIVITY);
}
}
}
}
Set Hashed Information for Facebook Advanced Matching
You can pass additional hashed information to the SDK before init()
for Facebook Advanced Matching.
Branch branch = Branch.getAutoInstance(getApplicationContext())
branch.addFacebookPartnerParameterWithName("em", "194b86d986ad041666822dad7602f1a7bac1d9e286273e86141666ffb4b1909b")
Branch branch = Branch.getAutoInstance(getApplicationContext());
branch.addFacebookPartnerParameterWithName("em", "194b86d986ad041666822dad7602f1a7bac1d9e286273e86141666ffb4b1909b");
Detect Screenshots
When one of your users takes a screenshot within your app, you have the option to surface a pop-up that encourages them to share the content directly instead. A Branch Deep Link is generated, which they can easily copy and share using the pop-up.
var lp = LinkProperties().setChannel(“facebook”).setFeature(“sharing”).setCampaign(“content 123 launch”).setStage(“new user”).addControlParameter("$desktop_url", "http://example.com/home").addControlParameter("custom", "data").addControlParameter("custom_random", Long.toString(Calendar.getInstance().getTimeInMillis()))
val ss = ShareSheetStyle(this@MainActivity, "Check this out!", "This stuff is awesome: ").setCopyUrlStyle(resources.getDrawable(this, android.R.drawable.ic_menu_send), "Copy", "Added to clipboard").setMoreOptionStyle(resources.getDrawable(this, android.R.drawable.ic_menu_search), "Show more").addPreferredSharingOption(SharingHelper.SHARE_WITH.FACEBOOK).addPreferredSharingOption(SharingHelper.SHARE_WITH.EMAIL).addPreferredSharingOption(SharingHelper.SHARE_WITH.MESSAGE).addPreferredSharingOption(SharingHelper.SHARE_WITH.HANGOUT).setAsFullWidthStyle(true).setSharingTitle("Share With")
buo.showShareSheet(this, lp, ss, object : Branch.BranchLinkShareListener {
override fun onShareLinkDialogLaunched() {}
override fun onShareLinkDialogDismissed() {}
override fun onLinkShareResponse(sharedLink: String?, sharedChannel: String?, error: BranchError?) {}
override fun onChannelSelected(channelName: String) {}
})
Updated 2 months ago