package io.scal.secureshareui.controller;
import timber.log.Timber;
import io.scal.secureshareui.login.FacebookLoginActivity;
import io.scal.secureshareui.model.Account;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
import java.util.ArrayList;
import java.util.HashMap;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.text.TextUtils;
import android.util.Log;
import com.facebook.HttpMethod;
import com.facebook.Request;
import com.facebook.Response;
import com.facebook.Session;
import com.facebook.model.GraphUser;
import com.google.api.client.util.IOUtils;
import org.apache.http.HttpEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import org.json.JSONException;
import org.json.JSONObject;
public class FacebookSiteController extends SiteController {
public static final String SITE_NAME = "Facebook";
public static final String SITE_KEY = "facebook";
private static final String TAG = "FacebookSiteController";
public static final String PHOTO_SET_KEY = "PhotoSetPaths";
public static final String POST_NOT_PUBLIC = "PostNotPublic";
private String postId = null;
private String title = null;
private String album = null;
private ArrayList<String> photosToUpload = null;
private int pendingUploads = 0;
public FacebookSiteController(Context context, Handler handler, String jobId) {
super(context, handler, jobId);
// TODO Auto-generated constructor stub
}
@Override
public void startAuthentication(Account account) {
Intent intent = new Intent(mContext, FacebookLoginActivity.class);
intent.putExtra(SiteController.EXTRAS_KEY_CREDENTIALS, account.getCredentials());
((Activity) mContext).startActivityForResult(intent, SiteController.CONTROLLER_REQUEST_CODE); // FIXME not a safe cast, context might be a service
}
@Override
public void upload(Account account, HashMap<String, String> valueMap) {
Timber.d("Upload file: Entering upload");
title = valueMap.get(VALUE_KEY_TITLE);
String body = valueMap.get(VALUE_KEY_BODY);
// check for photo set upload paths
String mediaPath = "";
if (valueMap.keySet().contains(PHOTO_SET_KEY)) {
mediaPath = valueMap.get(PHOTO_SET_KEY);
} else {
mediaPath = valueMap.get(VALUE_KEY_MEDIA_PATH);
}
boolean useTor = (valueMap.get(VALUE_KEY_USE_TOR).equals("true")) ? true : false;
Session session = Session.openActiveSessionFromCache(mContext);
// setup callback
Request.Callback uploadMediaRequestCallback = new Request.OnProgressCallback() {
@Override
public void onCompleted(Response response) {
// privacy check callback
// ckeck to see if a video post is public
Request.Callback privacyCheckRequestCallback = new Request.Callback() {
@Override
public void onCompleted(Response response) {
Timber.d("PRIVACY CHECK RESPONSE: " + response.toString());
// request will fail unless app has user_videos permission
// since this feature is incomplete, just continue
if (response.getError() != null) {
Timber.e("PRIVACY CHECK, ERROR: " + response.getError() + " (IGNORED)");
jobSucceeded(postId);
return;
}
JSONObject graphResponse = response.getGraphObject().getInnerJSONObject();
try {
String privacyValue = graphResponse.getString("privacy");
Timber.d("PRIVACY CHECK, FOUND: " + privacyValue);
if ((privacyValue != null) && (privacyValue.equals("everyone"))) {
// post is public, ok to publish
jobSucceeded(postId);
} else {
// post is not public, need to handle this somehow
jobSucceeded(POST_NOT_PUBLIC);
}
} catch (JSONException je) {
Timber.e("FAILED TO EXTRACT PRIVACY SETTINGS FROM RESPONSE: " + je.getMessage() + " (IGNORED)");
// currently unable to find privacy field in video responses, so i'm letting this through
jobSucceeded(postId);
}
}
};
// post fail
if (response.getError() != null) {
Timber.d("media upload problem. Error= " + response.getError());
jobFailed(null, 1, response.getError().toString());
return;
}
Object graphResponse = response.getGraphObject().getProperty("id");
// upload fail
if (graphResponse == null || !(graphResponse instanceof String)
|| TextUtils.isEmpty((String) graphResponse)) {
Timber.d("failed media upload/no response");
jobFailed(null, 0, "failed media upload/no response");
}
// upload success
else {
postId = (String) graphResponse;
Timber.d("LOOKING FOR POST ID " + postId);
Session session = Session.openActiveSessionFromCache(mContext);
Bundle parameters = null;
Request request = new Request(session, postId, parameters, HttpMethod.GET, privacyCheckRequestCallback);
request.executeAsync();
}
}
@Override
public void onProgress(long current, long max) {
float percent = ((float) current) / ((float) max);
jobProgress(percent, "Facebook uploading...");
}
};
// photo album callback
// ckeck to see if album already exists
/*
Request.Callback checkAlbumRequestCallback = new Request.Callback() {
@Override
public void onCompleted(Response response) {
Timber.d("ALBUM CHECK RESPONSE: " + response.toString());
JSONObject graphResponse = response.getGraphObject().getInnerJSONObject();
try {
JSONArray albumArray = graphResponse.getJSONArray("data");
for (int i = 0; i < albumArray.length(); i++) {
JSONObject albumObject = albumArray.getJSONObject(i);
Timber.d("ALBUM CHECK, FOUND: " + albumObject.getString("name") + "/" + albumObject.getString("id"));
}
} catch (JSONException je) {
Timber.e("FAILED TO EXTRACT ALBUM LIST FROM RESPONSE: " + je.getMessage());
jobFailed(null, 0, "An error occurred while creating the album");
}
}
};
*/
// photo album callback
Request.Callback uploadAlbumRequestCallback = new Request.OnProgressCallback() {
@Override
public void onCompleted(Response response) {
Timber.d("ALBUM CREATION RESPONSE: " + response.toString());
JSONObject graphResponse = response.getGraphObject().getInnerJSONObject();
try {
album = graphResponse.getString("id");
Timber.d("NEW ALBUM ID: " + album);
// photo callback
Request.Callback uploadPhotoRequestCallback = new Request.OnProgressCallback() {
@Override
public void onCompleted(Response response) {
Timber.d("PHOTO UPLOAD RESPONSE: " + response.toString());
JSONObject graphResponse = response.getGraphObject().getInnerJSONObject();
try {
Timber.d("NEW PHOTO ID: " + graphResponse.getString("id"));
pendingUploads--;
// privacy check callback
// ckeck to see if an album post is public
Request.Callback privacyCheckRequestCallback = new Request.Callback() {
@Override
public void onCompleted(Response response) {
Timber.d("PRIVACY CHECK RESPONSE: " + response.toString());
JSONObject graphResponse = response.getGraphObject().getInnerJSONObject();
try {
String privacyValue = graphResponse.getString("privacy");
Timber.d("PRIVACY CHECK, FOUND: " + privacyValue);
if ((privacyValue != null) && (privacyValue.equals("everyone"))) {
// post is public, ok to publish
jobSucceeded(album);
} else {
// post is not public, need to handle this somehow
jobSucceeded(POST_NOT_PUBLIC);
}
} catch (JSONException je) {
Timber.e("FAILED TO EXTRACT PRIVACY SETTINGS FROM RESPONSE: " + je.getMessage());
// could not determine privacy setting, assume the worst
jobSucceeded(POST_NOT_PUBLIC);
}
}
};
if (pendingUploads == 0) {
Timber.d("ALL UPLOADS COMPLETE");
Session session = Session.openActiveSessionFromCache(mContext);
Bundle parameters = null;
Request request = new Request(session, album, parameters, HttpMethod.GET, privacyCheckRequestCallback);
request.executeAsync();
// jobSucceeded(album);
} else {
Timber.d(pendingUploads + " UPLOADS REMAINING");
}
} catch (JSONException je) {
Timber.e("FAILED TO EXTRACT PHOTO ID FROM RESPONSE: " + je.getMessage());
jobFailed(null, 0, "An error occurred while uploading the photo");
}
}
@Override
public void onProgress(long current, long max) {
float percent = ((float) current) / ((float) max);
jobProgress(percent, "uploading photo...");
}
};
// upload photo to album
Timber.d("PHOTO UPLOAD");
pendingUploads = photosToUpload.size();
int photoNumber = 1;
for (String photoToUpload : photosToUpload) {
try {
FileInputStream inStream = new FileInputStream(photoToUpload);
ByteArrayOutputStream outStream = new ByteArrayOutputStream();
byte[] photoBuffer = new byte[1024];
for (int i; (i = inStream.read(photoBuffer)) != -1; ) {
outStream.write(photoBuffer, 0, i);
}
byte[] photoBytes = outStream.toByteArray();
Session session = Session.openActiveSessionFromCache(mContext);
Bundle parameters = null;
Request request = new Request(session, album + "/photos", parameters, HttpMethod.POST, uploadPhotoRequestCallback);
//Timber.d("GOT REQUEST (" + photoNumber + ")");
parameters = request.getParameters();
parameters.putByteArray("source", photoBytes);
parameters.putString("name", title + "_" + photoNumber);
//Timber.d("GOT PARAMETERS (" + photoNumber + ")");
//Timber.d("EXECUTING REQUEST (" + photoNumber + ")...");
request.executeAsync();
} catch (FileNotFoundException fnfe) {
// can't find photo file
Timber.e("COULD NOT FIND FILE " + photoToUpload + " -> " + fnfe.getMessage());
} catch (IOException ioe) {
// can't read photo file
Timber.e("COULD NOT READ FILE " + photoToUpload + " -> " + ioe.getMessage());
}
photoNumber++;
}
// TEMP
// jobSucceeded(album);
} catch (JSONException je) {
Timber.e("FAILED TO EXTRACT ALBUM ID FROM RESPONSE: " + je.getMessage());
jobFailed(null, 0, "An error occurred while creating the album");
}
}
@Override
public void onProgress(long current, long max) {
float percent = ((float) current) / ((float) max);
jobProgress(percent, "creating album...");
}
};
Bundle parameters = null;
Request request = null;
Timber.d("MEDIA PATH: " + mediaPath);
if (mediaPath.contains(";")) {
// upload multiple photos
Timber.d("MULTIPLE FILE UPLOAD");
String[] photoPaths = mediaPath.split(";");
photosToUpload = new ArrayList<String>();
for (int i = 0; i < photoPaths.length; i++) {
photosToUpload.add(photoPaths[i]);
}
// doing this to hit the photos api
// may restructure to check for existing album
// Request requestTwo = null;
// requestTwo = new Request(session, "me/albums", parameters, HttpMethod.GET, checkAlbumRequestCallback);
// requestTwo.executeAsync();
request = new Request(session, "me/albums", parameters, HttpMethod.POST, uploadAlbumRequestCallback);
//Timber.d("GOT REQUEST");
parameters = request.getParameters();
parameters.putString("name", title);
//Timber.d("GOT PARAMETERS");
} else {
// upload single photo or video
Timber.d("SINGLE FILE UPLOAD");
File mediaFile = new File(mediaPath);
try {
if (super.isVideoFile(mediaFile)) {
request = Request.newUploadVideoRequest(session, mediaFile, uploadMediaRequestCallback);
if (torCheck(useTor, mContext)) {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ORBOT_HOST, ORBOT_HTTP_PORT));
request.setProxy(proxy);
}
parameters = request.getParameters();
//video params
parameters.putString("title", title);
parameters.putString("description", body);
} else if (super.isImageFile(mediaFile)) {
request = Request.newUploadPhotoRequest(session, mediaFile, uploadMediaRequestCallback);
if (torCheck(useTor, mContext)) {
Proxy proxy = new Proxy(Proxy.Type.HTTP, new InetSocketAddress(ORBOT_HOST, ORBOT_HTTP_PORT));
request.setProxy(proxy);
}
parameters = request.getParameters();
//image params
parameters.putString("name", title);
} else {
Timber.d("media type not supported");
return;
}
request.setParameters(parameters);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
//Timber.d("EXECUTING REQUEST...");
request.executeAsync();
}
static String userId; // FIXME we should be caching this at login
public static String getUserId(){
final Session session = Session.getActiveSession();
if (session != null && session.isOpened()) {
Request request = Request.newMeRequest(session, new Request.GraphUserCallback() {
@Override
public void onCompleted(GraphUser user, Response response) {
if (session == Session.getActiveSession()) {
if (user != null) {
userId = user.getId();
}
}
}
});
Request.executeAndWait(request);
return userId;
}else{
return null;
}
}
@Override
public void startMetadataActivity(Intent intent) {
return; // nop
}
}