Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Report Android app install back to facebook without using their api

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.

like image 1000
Twilite Avatar asked Oct 06 '22 04:10

Twilite


1 Answers

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.

like image 134
Jacek Marchwicki Avatar answered Oct 10 '22 10:10

Jacek Marchwicki