Reputation: 873
From my Android app, I'd like to publish its install back to facebook to allow for conversion tracking for their new mobile app install ads, but I'd like to do it without using their api.
So instead of doing
com.facebook.Settings.publishInstall(context, appId);
I'd like to just send a HTTP request with the required parameters to some URL.
EDIT:
I logged the two requests that get sent to facebook to publish the app install and they look like this:
Request:
GET /[app id]?format=json&sdk=android&fields=supports_attribution HTTP/1.1
User-Agent: FBAndroidSDK.3.0.0.b
Content-Type: multipart/form-data; boundary=3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Host: graph.facebook.com
Connection: Keep-Alive
Accept-Encoding: gzip
Response:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: private, no-cache, no-store, must-revalidate
Content-Type: text/javascript; charset=UTF-8
ETag: "24ea6554744eece05b90dd2e65af63277cdcaf53"
Expires: Sat, 01 Jan 2000 00:00:00 GMT
Pragma: no-cache
X-FB-Rev: 658994
X-FB-Debug: P2GE3fDVAnRJh62rBS5WXD4ce1hTy8Pwvjq5rT/I+TI=
Date: Tue, 30 Oct 2012 11:37:09 GMT
Connection: keep-alive
Content-Length: 52
{"supports_attribution":true,"id":"[app id]"}
Request:
POST /[app id]/activities?format=json&sdk=android&migration_bundle=fbsdk%3A20120913 HTTP/1.1
User-Agent: FBAndroidSDK.3.0.0.b
Content-Type: multipart/form-data; boundary=3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Host: graph.facebook.com
Connection: Keep-Alive
Transfer-Encoding: chunked
Accept-Encoding: gzip
261
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Content-Disposition: form-data; name="format"
json
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Content-Disposition: form-data; name="sdk"
android
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Content-Disposition: form-data; name="migration_bundle"
fbsdk:20120913
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Content-Disposition: form-data; name="attribution"
ab175007-2725-464f-a111-b8b1a92bf1dd
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
Content-Disposition: form-data; name="event"
MOBILE_APP_INSTALL
--3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f
0
Response:
HTTP/1.1 200 OK
Access-Control-Allow-Origin: *
Cache-Control: private, no-cache, no-store, must-revalidate
Content-Type: text/javascript; charset=UTF-8
Expires: Sat, 01 Jan 2000 00:00:00 GMT
Pragma: no-cache
X-FB-Rev: 658994
X-FB-Debug: +0GWQ4cu+tFeAg3QEuwYGx+HAt7t37itzxEYBaTZF8U=
Date: Tue, 30 Oct 2012 11:38:33 GMT
Connection: keep-alive
Content-Length: 4
true
I've included a trimmed down version of the facebook api in my app that cannot do anything else, but just send those two requests. I'll try it out and report back on how it works.
Optimally, I'd like to send the requests from a server and not from the phone at all.
Upvotes: 5
Views: 2608
Reputation: 1673
You can do this with simple call
FacebookHelper.appInstall(this.getApplicationContext(), "<com.your.package>",
"<your facebook app id>");
and library below.
Remember that you can track application only if facebook app is installed - then you have attributionId. If you would like to send this request on server you have to send attributionId to your server.
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import org.apache.http.HttpResponse;
import org.apache.http.HttpStatus;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpEntityEnclosingRequestBase;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.client.methods.HttpRequestBase;
import org.apache.http.entity.mime.HttpMultipartMode;
import org.apache.http.entity.mime.MultipartEntity;
import org.apache.http.entity.mime.content.StringBody;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.protocol.HTTP;
import java.io.IOException;
import java.nio.charset.Charset;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
public class FacebookHelper {
private static final Uri ATTRIBUTION_ID_CONTENT_URI =
Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");
private static final String ATTRIBUTION_ID_COLUMN_NAME = "aid";
private static final String USER_AGENT = "FBAndroidSDK/3.5.0";
private final DefaultHttpClient mHttpClient;
private final String mApplicationPackage;
private final String mApplicationId;
private FacebookHelper(String applicationPackage, String applicationId) {
checkNotNull(applicationPackage);
checkNotNull(applicationId);
mApplicationPackage = applicationPackage;
mApplicationId = applicationId;
mHttpClient = new DefaultHttpClient();
}
private static String getAttributionId(ContentResolver cr) {
checkNotNull(cr);
final String [] projection = {ATTRIBUTION_ID_COLUMN_NAME};
final Cursor cursor = cr.query(ATTRIBUTION_ID_CONTENT_URI, projection,
null, null, null);
if (cursor == null) {
return null;
}
try {
if (!cursor.moveToFirst()) {
return null;
}
final int attributionColumnIndex = cursor.getColumnIndex(ATTRIBUTION_ID_COLUMN_NAME);
if (attributionColumnIndex < 0) {
return null;
}
return cursor.getString(attributionColumnIndex);
} finally {
cursor.close();
}
}
public static void appInstall(Context applicationContext, String applicationPackage,
String applicationId) throws IOException {
checkNotNull(applicationContext);
checkNotNull(applicationPackage);
checkNotNull(applicationId);
final ContentResolver cr = applicationContext.getContentResolver();
final String attributionId = getAttributionId(cr);
if (attributionId == null) {
// we can not send anything if facebook app is not installed
return;
}
final FacebookHelper facebookHelper = new FacebookHelper(applicationPackage, applicationId);
facebookHelper.appInstall(attributionId);
}
private void appInstall(String attribution) throws IOException {
checkNotNull(attribution);
String url = String.format("https://graph.facebook.com/%s/activities", mApplicationId);
MultipartEntity entity = new MultipartEntity(
HttpMultipartMode.BROWSER_COMPATIBLE);
Charset charset = Charset.forName(HTTP.UTF_8);
entity.addPart("sdk", new StringBody("android", charset));
entity.addPart("format", new StringBody("json", charset));
entity.addPart("event", new StringBody("MOBILE_APP_INSTALL", charset));
entity.addPart("attribution", new StringBody(attribution, charset));
entity.addPart("auto_publish", new StringBody("false", charset));
entity.addPart("application_tracking_enabled", new StringBody("true", charset));
entity.addPart("migration_bundle", new StringBody("fbsdk:20130708", charset));
entity.addPart("application_package_name", new StringBody(mApplicationPackage, charset));
HttpEntityEnclosingRequestBase request = new HttpPost(url);
setupDefaultHeaders(request);
request.setEntity(entity);
HttpResponse response = getHttpClient().execute(request);
validateResponseCode(response, HttpStatus.SC_OK);
}
private void validateResponseCode(HttpResponse response,
int expectedStatusCode) throws IOException {
checkNotNull(response);
checkArgument(expectedStatusCode != HttpStatus.SC_UNPROCESSABLE_ENTITY);
checkArgument(expectedStatusCode != HttpStatus.SC_NOT_FOUND);
int statusCode = response.getStatusLine().getStatusCode();
if (statusCode != expectedStatusCode) {
throw new IOException("Wrong response code: "+
"expected <" +expectedStatusCode +"> was <" +statusCode+">");
}
}
private HttpClient getHttpClient() {
return mHttpClient;
}
private void setupDefaultHeaders(HttpRequestBase request) {
checkNotNull(request);
request.setHeader("Accept", "application/json");
request.setHeader("User-Agent", USER_AGENT);
}
}
this library use guava checkArgument and checkNotNull if you do not use guava you can just skip those lines.
Upvotes: 3
Reputation: 873
I ended up publishing the install back to facebook from my app, using this stripped down version of their api:
package de.techunity.fancy.facebook.publishinstall;
import org.json.JSONException;
import android.content.ContentResolver;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import com.facebook.android.Util;
public class InstallPublisher {
private static final Uri ATTRIBUTION_ID_CONTENT_URI =
Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");
private static final String ATTRIBUTION_ID_COLUMN_NAME = "aid";
private static final String ATTRIBUTION_PREFERENCES = "com.facebook.sdk.attributionTracking";
private static final String PUBLISH_ACTIVITY_PATH = "%s/activities";
private static final String MOBILE_INSTALL_EVENT = "MOBILE_APP_INSTALL";
private static final String SUPPORTS_ATTRIBUTION = "supports_attribution";
private static final String APPLICATION_FIELDS = "fields";
private static final String ANALYTICS_EVENT = "event";
private static final String ATTRIBUTION_KEY = "attribution";
/**
* Manually publish install attribution to the facebook graph. Internally handles tracking repeat calls to prevent
* multiple installs being published to the graph.
* @param context
* @return returns false on error. Applications should retry until true is returned. Safe to call again after
* true is returned.
*/
public static boolean publishInstall(final Context context, final String applicationId) {
try {
if (applicationId == null) {
return false;
}
final String attributionId = InstallPublisher.getAttributionId(context.getContentResolver());
final SharedPreferences preferences = context.getSharedPreferences(ATTRIBUTION_PREFERENCES, Context.MODE_PRIVATE);
final String pingKey = applicationId+"ping";
long lastPing = preferences.getLong(pingKey, 0);
if (lastPing == 0 && attributionId != null) {
new Thread(new Runnable() {
@Override
public void run() {
try {
Bundle supportsAttributionParams = new Bundle();
supportsAttributionParams.putString(APPLICATION_FIELDS, SUPPORTS_ATTRIBUTION);
Request pingRequest = Request.newGraphPathRequest(applicationId, null);
pingRequest.setParameters(supportsAttributionParams);
GraphObject supportResponse = pingRequest.executeAndWait().getGraphObject();
Object doesSupportAttribution = supportResponse.getProperty(SUPPORTS_ATTRIBUTION);
if (!(doesSupportAttribution instanceof Boolean)) {
throw new JSONException(String.format(
"%s contains %s instead of a Boolean", SUPPORTS_ATTRIBUTION, doesSupportAttribution));
}
if ((Boolean)doesSupportAttribution) {
GraphObject publishParams = (GraphObject) GraphObjectWrapper.createGraphObject();
publishParams.setProperty(ANALYTICS_EVENT, MOBILE_INSTALL_EVENT);
publishParams.setProperty(ATTRIBUTION_KEY, attributionId);
String publishUrl = String.format(PUBLISH_ACTIVITY_PATH, applicationId);
Request publishRequest = Request.newPostRequest(publishUrl, publishParams, null);
publishRequest.executeAndWait();
// denote success since no error threw from the post.
SharedPreferences.Editor editor = preferences.edit();
editor.putLong(pingKey, System.currentTimeMillis());
editor.commit();
}
}
catch (Exception e) {
Log.d("fancy", "Couldn't publish publish install to facebook", e);
}
finally {
Log.d("fancy", "Publish install to facebook thread completed");
}
}
}, "facebook publish install thread").start();
}
return true;
} catch (Exception e) {
// if there was an error, fall through to the failure case.
Util.logd("Facebook-publish", e.getMessage());
}
return false;
}
public static String getAttributionId(ContentResolver contentResolver) {
String [] projection = {ATTRIBUTION_ID_COLUMN_NAME};
Cursor c = contentResolver.query(ATTRIBUTION_ID_CONTENT_URI, projection, null, null, null);
if (c == null || !c.moveToFirst()) {
return null;
}
String attributionId = c.getString(c.getColumnIndex(ATTRIBUTION_ID_COLUMN_NAME));
c.close();
return attributionId;
}
}
Upvotes: 0
Reputation: 4928
It's not possible to do it without integrating our SDK. For App Install ads, the SDK must be integrated with your app. If you go to your app dashboard > Promote > Android Feed, there is a message that states:
Note: This is only effective if you integrated the Facebook SDK for Android. You'll be charged every time someone sees your ad.
Upvotes: 1