Report the installation of the Android application back to facebook without using their api

From my Android app, I would like to publish its installation back to facebook to track conversions for my new mobile app install ads, but I would like to do this without using their api.

So, instead of doing

com.facebook.Settings.publishInstall(context, appId); 

I would just send an HTTP request with the required parameters to some URL.

EDIT:

I registered two requests that are sent to facebook to publish the installation of the application, 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 

Answer:

 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 

Answer:

 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 have included a cropped version of facebook api in my application that cannot do anything, but just send these two requests. I will try and talk about how it works.

Optimally, I would like to send requests from the server, and not from the phone at all.

+5
android facebook
source share
3 answers

You can do it with a simple call

 FacebookHelper.appInstall(this.getApplicationContext(), "<com.your.package>", "<your facebook app id>"); 

and below.

Remember that you can track the application only if the facebook application is installed - then you have attribution. If you want to send this request to the server, you need to send the attribution 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 uses guava checkArgument and checkNotNull, if you are not using guava, you can simply skip these lines.

+3
source share

This cannot be done without integrating our SDK. For App Install ads, the SDK needs to integrate with your application. If you go to the app’s toolbar> Promote> Android feed, a message appears saying:

Note. This only works if you integrated the Android SDK into Android. You will pay every time someone sees your ad.

+1
source share

I ended up posting the installation back to facebook from my application using this stripped-down version of my 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; } } 
0
source share

All Articles