package com.stanleycen.facebookanalytics;
import android.R;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.Bundle;
import android.os.Debug;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.os.SystemClock;
import android.support.v4.app.NotificationCompat;
import android.util.Log;
import com.facebook.FacebookRequestError;
import com.facebook.HttpMethod;
import com.facebook.Request;
import com.facebook.Response;
import com.facebook.Session;
import org.joda.time.DateTime;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
/**
* Created by scen on 8/29/13.
*/
public class DataDownloaderService extends Service {
private final static String TAG = "DataDownloader";
private static final int ONGOING_NOTIFICATION_ID = 42;
public static final String EXTRA_MESSENGER = "com.stanleycen.facebookanalytics.DataDownloaderService.EXTRA_MESSENGER";
enum MessageType {
UPDATE_PROGRESSBAR,
FINISHED_DOWNLOAD
}
;
private Messenger messenger = null;
private boolean startedDownload = false;
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
Log.d(TAG, "onStartCommand");
if (startedDownload) return START_NOT_STICKY;
startedDownload = true;
Bundle extras = intent.getExtras();
if (extras != null) {
messenger = (Messenger) extras.get(EXTRA_MESSENGER);
} else {
Log.wtf(TAG, "extras null");
}
updateDownloadProgress("Retrieving conversation list", 0, 100, true);
Thread t;
if (GlobalApp.get().fb.fbData.collectionMethod == FBData.CollectionMethod.UNIFIED_API) {
t = new Thread(new Runnable() {
@Override
public void run() {
// Download thread list
FBData newFbData = new FBData();
long lastTimestamp = UnifiedMessaging.LARGE_TIMESTAMP;
int totMessageCount = 0;
// int howMany = 20;
outer:
while (true) {
String threadFQL = UnifiedMessaging.getThreadFQL(lastTimestamp);
Bundle opts = new Bundle();
opts.putString("q", threadFQL);
Request req = new Request(Session.getActiveSession(), "/fql",
opts, HttpMethod.GET);
Response res = req.executeAndWait();
if (res.getError() != null) {
if (handleFBResponseError(res.getError())) continue;
}
JSONObject jobj = res.getGraphObject().getInnerJSONObject();
try {
JSONArray data = jobj.getJSONArray("data");
if (data == null || data.length() == 0) break;
for (int i = 0; i < data.length(); i++) {
JSONObject jcurThread = data.getJSONObject(i);
FBThread fbThread = new FBThread();
long timestamp = jcurThread.getLong("timestamp");
fbThread.lastUpdate = new DateTime(timestamp);
fbThread.messageCount = jcurThread.getInt("num_messages");
totMessageCount += fbThread.messageCount;
fbThread.title = jcurThread.getString("title");
Log.d(TAG, fbThread.title);
fbThread.id = jcurThread.getString("thread_id");
fbThread.isGroupConversation = jcurThread.getBoolean("is_group_conversation");
JSONArray participants = jcurThread.getJSONArray("participants");
for (int j = 0; j < participants.length(); j++) {
JSONObject curp = participants.getJSONObject(j);
String uid = curp.getString("user_id");
FBUser user = new FBUser(uid, curp.getString("name"));
newFbData.userMap.put(uid, user);
fbThread.participants.add(newFbData.userMap.get(uid));
}
JSONArray formerParticipants = jcurThread.optJSONArray("former_participants");
if (formerParticipants != null)
for (int j = 0; j < formerParticipants.length(); j++) {
JSONObject curp = formerParticipants.getJSONObject(j);
String uid = curp.getString("user_id");
FBUser user = new FBUser(uid, curp.getString("name"));
newFbData.userMap.put(uid, user);
fbThread.participants.add(newFbData.userMap.get(uid));
}
newFbData.threads.add(fbThread);
lastTimestamp = timestamp;
// if (--howMany <= 0) break outer;
}
} catch (JSONException e) {
e.printStackTrace();
}
SystemClock.sleep(UnifiedMessaging.API_WAIT);
}
int curIdx = 1;
int tot = newFbData.threads.size();
int messagesDownloaded = 0;
for (FBThread fbThread : newFbData.threads) {
int curThreadMessagesDownloaded = 0;
lastTimestamp = 0;
try {
while (true) {
updateDownloadProgress("Conversation " + curIdx + " of " + tot + " (" + curThreadMessagesDownloaded + " / " + fbThread.messageCount + ")",
messagesDownloaded, totMessageCount, false);
String messagesFQL = UnifiedMessaging.getMessagesFQL(fbThread.id, lastTimestamp);
Bundle opts = new Bundle();
opts.putString("q", messagesFQL);
Request req = new Request(Session.getActiveSession(), "/fql",
opts, HttpMethod.GET);
Log.v(TAG, "Sent batch");
Response res = req.executeAndWait();
if (res.getError() != null) {
if (handleFBResponseError(res.getError())) continue;
}
JSONObject jobj = res.getGraphObject().getInnerJSONObject();
JSONArray data = jobj.optJSONArray("data");
if (data == null || data.length() == 0) break;
Log.v(TAG, "" + data.length());
for (int i = 0; i < data.length(); i++) {
JSONObject curMessage = data.getJSONObject(i);
FBMessage fbMessage = new FBMessage();
long ts = curMessage.getLong("timestamp");
fbMessage.timestamp = new DateTime(ts);
lastTimestamp = ts;
if (!curMessage.isNull("coordinates")) {
JSONObject coordinates = curMessage.optJSONObject("coordinates");
if (coordinates != null) {
fbMessage.hasCoordinates = true;
fbMessage.latitude = (float) coordinates.getDouble("latitude");
fbMessage.longitude = (float) coordinates.getDouble("longitude");
}
}
fbMessage.body = curMessage.getString("body");
JSONObject curp = curMessage.optJSONObject("sender");
if (curp != null) {
String uid = curp.getString("user_id");
FBUser user = new FBUser(uid, curp.getString("name"));
fbMessage.from = user;
newFbData.userMap.put(uid, user);
}
fbMessage.id = curMessage.getString("message_id");
JSONArray attachments = curMessage.optJSONArray("attachments");
if (attachments != null && attachments.length() > 0) {
JSONObject attachmentMap = curMessage.optJSONObject("attachment_map");
if (attachmentMap != null) {
for (int j = 0; j < attachments.length(); j++) {
String id = attachments.getString(j);
JSONObject attachmentObj = attachmentMap.optJSONObject(id);
if (attachmentObj != null) {
JSONObject imageData = attachmentObj.optJSONObject("image_data");
if (imageData != null) {
FBAttachment fbAttachment = new FBAttachment();
try {
fbAttachment.height = imageData.getInt("height");
fbAttachment.width = imageData.getInt("width");
fbAttachment.mimeType = attachmentObj.getString("mime_type");
fbAttachment.url = imageData.getString("url");
fbAttachment.previewUrl = imageData.getString("preview_url");
fbAttachment.id = id;
fbAttachment.type = FBAttachment.Type.IMAGE;
fbAttachment.message = fbMessage.id;
fbAttachment.thread = fbThread.id;
fbMessage.attachments.add(fbAttachment);
Log.d(TAG, fbAttachment.url);
} catch (JSONException e) {
}
}
}
}
}
}
fbMessage.source = FBMessage.Source.OTHER;
JSONArray tags = curMessage.optJSONArray("tags");
if (tags != null && tags.length() > 0) {
for (int k = 0; k < tags.length(); k++) {
String tag = tags.getString(k);
if (tag.startsWith("source:mobile")) {
fbMessage.source = FBMessage.Source.MOBILE;
} else if (tag.startsWith("source:chat")) {
fbMessage.source = FBMessage.Source.WEB;
}
}
}
JSONArray shares = curMessage.optJSONArray("shares");
if (shares != null && shares.length() > 0) {
if (!curMessage.isNull("share_map")) {
// Log.d(TAG, curMessage.toString(2));
JSONObject shareMap = curMessage.optJSONObject("share_map");
if (shareMap != null) {
for (int k = 0; k < shares.length(); k++) {
String id = shares.getString(k);
JSONObject shareObj = shareMap.optJSONObject(id);
if (shareObj != null) {
if (!shareObj.isNull("sticker_id")) {
FBAttachment sticker = new FBAttachment();
sticker.type = FBAttachment.Type.STICKER;
sticker.url = shareObj.getString("href");
sticker.message = fbMessage.id;
sticker.thread = fbThread.id;
Log.v(TAG, sticker.url);
fbMessage.attachments.add(sticker);
}
}
}
}
}
}
fbMessage.thread = fbThread.id;
fbThread.messages.add(fbMessage);
++messagesDownloaded;
++curThreadMessagesDownloaded;
}
SystemClock.sleep(UnifiedMessaging.API_WAIT);
}
} catch (JSONException e) {
e.printStackTrace();
}
fbThread.messageCount = fbThread.messages.size();
++curIdx;
}
newFbData.lastUpdate = DateTime.now();
updateDownloadProgress("Committing data", 100, 100, true);
UnifiedMessaging.commitData(newFbData);
updateDownloadProgress("Crunching data", 100, 100, true);
GlobalApp.get().fb.fbData.computeHighLevelThreadStats();
notifyFinish();
stopSelf();
}
});
} else {
t = new Thread(new Runnable() {
@Override
public void run() {
notifyFinish();
stopSelf();
}
});
}
t.start();
return START_NOT_STICKY;
}
private boolean handleFBResponseError(FacebookRequestError error) {
if (error.getErrorMessage() != null) Log.e(TAG, error.getErrorMessage());
if (error.getErrorType() != null) Log.e(TAG, error.getErrorType());
Log.e(TAG, "Error code: " + error.getErrorCode());
int code = error.getErrorCode();
switch (code) {
case 1:
case 2:
case 4:
case 17:
SystemClock.sleep(UnifiedMessaging.API_TIMEOUT_WAIT);
return true;
default:
Debug.waitForDebugger();
}
return false;
}
private void notifyFinish() {
Message msg = Message.obtain();
msg.what = MessageType.FINISHED_DOWNLOAD.ordinal();
msg.obj = GlobalApp.get().fb.me.getId();
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
void updateDownloadProgress(String content, int progress, int mx, boolean ongoing) {
updateOngoingNotification(content, progress, mx, ongoing);
Message msg = Message.obtain();
msg.what = MessageType.UPDATE_PROGRESSBAR.ordinal();
ProgressBarUpdate progressBarUpdate = new ProgressBarUpdate();
progressBarUpdate.content = content;
progressBarUpdate.progress = progress;
progressBarUpdate.mx = mx;
progressBarUpdate.ongoing = ongoing;
msg.obj = progressBarUpdate;
try {
messenger.send(msg);
} catch (RemoteException e) {
e.printStackTrace();
}
}
private void updateOngoingNotification(String content, int progress, int mx, boolean ongoing) {
Intent i = new Intent(this, MainActivity.class);
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_SINGLE_TOP);
PendingIntent pi = PendingIntent.getActivity(this, 0, i, 0);
NotificationCompat.Builder b = new NotificationCompat.Builder(this);
b.setContentTitle("Facebook message download")
.setContentText(content)
.setTicker("Starting Facebook message download")
.setSmallIcon(R.drawable.stat_sys_download)
.setProgress(mx, progress, ongoing)
.setContentIntent(pi);
Notification n = b.build();
startForeground(ONGOING_NOTIFICATION_ID, n);
}
@Override
public void onDestroy() {
Log.d(TAG, "onDestroy");
stopForeground(true);
}
@Override
public IBinder onBind(Intent intent) {
return null;
}
public class ProgressBarUpdate {
String content;
int progress;
int mx;
boolean ongoing;
}
}