(targetsCount);
@@ -153,7 +157,7 @@ public void onCompleted(Response response) {
}
}
- Uri webFallbackUrl = getWebFallbackUriFromJson(uri, urlData);
+ Uri webFallbackUrl = getWebFallbackUriFromJson(uri, appLinkData);
AppLink appLink = new AppLink(uri, targets, webFallbackUrl);
appLinkResults.put(uri, appLink);
diff --git a/facebook/src/main/java/com/facebook/FacebookBroadcastReceiver.java b/facebook/src/main/java/com/facebook/FacebookBroadcastReceiver.java
index ec2556c..1704cda 100644
--- a/facebook/src/main/java/com/facebook/FacebookBroadcastReceiver.java
+++ b/facebook/src/main/java/com/facebook/FacebookBroadcastReceiver.java
@@ -4,7 +4,6 @@
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
-import android.util.Log;
import com.facebook.internal.NativeProtocol;
/**
diff --git a/facebook/src/main/java/com/facebook/FacebookRequestError.java b/facebook/src/main/java/com/facebook/FacebookRequestError.java
index 937e2b5..954d0ec 100644
--- a/facebook/src/main/java/com/facebook/FacebookRequestError.java
+++ b/facebook/src/main/java/com/facebook/FacebookRequestError.java
@@ -58,6 +58,9 @@ public final class FacebookRequestError {
private static final String ERROR_SUB_CODE_KEY = "error_subcode";
private static final String ERROR_MSG_KEY = "error_msg";
private static final String ERROR_REASON_KEY = "error_reason";
+ private static final String ERROR_USER_TITLE_KEY = "error_user_title";
+ private static final String ERROR_USER_MSG_KEY = "error_user_msg";
+ private static final String ERROR_IS_TRANSIENT_KEY = "is_transient";
private static class Range {
private final int start, end;
@@ -98,6 +101,9 @@ boolean contains(int value) {
private final int subErrorCode;
private final String errorType;
private final String errorMessage;
+ private final String errorUserTitle;
+ private final String errorUserMessage;
+ private final boolean errorIsTransient;
private final JSONObject requestResult;
private final JSONObject requestResultBody;
private final Object batchRequestResult;
@@ -105,9 +111,9 @@ boolean contains(int value) {
private final FacebookException exception;
private FacebookRequestError(int requestStatusCode, int errorCode,
- int subErrorCode, String errorType, String errorMessage, JSONObject requestResultBody,
- JSONObject requestResult, Object batchRequestResult, HttpURLConnection connection,
- FacebookException exception) {
+ int subErrorCode, String errorType, String errorMessage, String errorUserTitle, String errorUserMessage,
+ boolean errorIsTransient, JSONObject requestResultBody, JSONObject requestResult, Object batchRequestResult,
+ HttpURLConnection connection, FacebookException exception) {
this.requestStatusCode = requestStatusCode;
this.errorCode = errorCode;
this.subErrorCode = subErrorCode;
@@ -117,6 +123,9 @@ private FacebookRequestError(int requestStatusCode, int errorCode,
this.requestResult = requestResult;
this.batchRequestResult = batchRequestResult;
this.connection = connection;
+ this.errorUserTitle = errorUserTitle;
+ this.errorUserMessage = errorUserMessage;
+ this.errorIsTransient = errorIsTransient;
boolean isLocalException = false;
if (exception != null) {
@@ -172,28 +181,32 @@ private FacebookRequestError(int requestStatusCode, int errorCode,
}
}
+ // Notify user when error_user_msg is present
+ shouldNotify = errorUserMessage!= null && errorUserMessage.length() > 0;
+
this.category = errorCategory;
this.userActionMessageId = messageId;
this.shouldNotifyUser = shouldNotify;
}
private FacebookRequestError(int requestStatusCode, int errorCode,
- int subErrorCode, String errorType, String errorMessage, JSONObject requestResultBody,
- JSONObject requestResult, Object batchRequestResult, HttpURLConnection connection) {
- this(requestStatusCode, errorCode, subErrorCode, errorType, errorMessage,
- requestResultBody, requestResult, batchRequestResult, connection, null);
+ int subErrorCode, String errorType, String errorMessage, String errorUserTitle, String errorUserMessage,
+ boolean errorIsTransient, JSONObject requestResultBody, JSONObject requestResult, Object batchRequestResult,
+ HttpURLConnection connection) {
+ this(requestStatusCode, errorCode, subErrorCode, errorType, errorMessage, errorUserTitle, errorUserMessage,
+ errorIsTransient, requestResultBody, requestResult, batchRequestResult, connection, null);
}
FacebookRequestError(HttpURLConnection connection, Exception exception) {
this(INVALID_HTTP_STATUS_CODE, INVALID_ERROR_CODE, INVALID_ERROR_CODE,
- null, null, null, null, null, connection,
+ null, null, null, null, false, null, null, null, connection,
(exception instanceof FacebookException) ?
(FacebookException) exception : new FacebookException(exception));
}
public FacebookRequestError(int errorCode, String errorType, String errorMessage) {
this(INVALID_HTTP_STATUS_CODE, errorCode, INVALID_ERROR_CODE, errorType, errorMessage,
- null, null, null, null, null);
+ null, null, false, null, null, null, null, null);
}
/**
@@ -279,6 +292,36 @@ public String getErrorMessage() {
}
}
+ /**
+ * A message suitable for display to the user, describing a user action necessary to enable Facebook functionality.
+ * Not all Facebook errors yield a message suitable for user display; however in all cases where
+ * shouldNotifyUser() returns true, this method returns a non-null message suitable for display.
+ *
+ * @return the error message returned from Facebook
+ */
+ public String getErrorUserMessage() {
+ return errorUserMessage;
+ }
+
+ /**
+ * A short summary of the error suitable for display to the user.
+ * Not all Facebook errors yield a title/message suitable for user display; however in all cases where
+ * getErrorUserTitle() returns valid String - user should be notified.
+ *
+ * @return the error message returned from Facebook
+ */
+ public String getErrorUserTitle() {
+ return errorUserTitle;
+ }
+
+ /**
+ * @return true if given error is transient and may succeed if the initial action is retried as-is.
+ * Application may use this information to display a "Retry" button, if user should be notified about this error.
+ */
+ public boolean getErrorIsTransient() {
+ return errorIsTransient;
+ }
+
/**
* Returns the body portion of the response corresponding to the request from Facebook.
*
@@ -359,6 +402,9 @@ static FacebookRequestError checkResponseAndCreateError(JSONObject singleResult,
// with several sub-properties, or else one or more top-level fields containing error info.
String errorType = null;
String errorMessage = null;
+ String errorUserMessage = null;
+ String errorUserTitle = null;
+ boolean errorIsTransient = false;
int errorCode = INVALID_ERROR_CODE;
int errorSubCode = INVALID_ERROR_CODE;
@@ -371,6 +417,9 @@ static FacebookRequestError checkResponseAndCreateError(JSONObject singleResult,
errorMessage = error.optString(ERROR_MESSAGE_FIELD_KEY, null);
errorCode = error.optInt(ERROR_CODE_FIELD_KEY, INVALID_ERROR_CODE);
errorSubCode = error.optInt(ERROR_SUB_CODE_KEY, INVALID_ERROR_CODE);
+ errorUserMessage = error.optString(ERROR_USER_MSG_KEY, null);
+ errorUserTitle = error.optString(ERROR_USER_TITLE_KEY, null);
+ errorIsTransient = error.optBoolean(ERROR_IS_TRANSIENT_KEY, false);
hasError = true;
} else if (jsonBody.has(ERROR_CODE_KEY) || jsonBody.has(ERROR_MSG_KEY)
|| jsonBody.has(ERROR_REASON_KEY)) {
@@ -383,14 +432,15 @@ static FacebookRequestError checkResponseAndCreateError(JSONObject singleResult,
if (hasError) {
return new FacebookRequestError(responseCode, errorCode, errorSubCode,
- errorType, errorMessage, jsonBody, singleResult, batchResult, connection);
+ errorType, errorMessage, errorUserTitle, errorUserMessage, errorIsTransient, jsonBody,
+ singleResult, batchResult, connection);
}
}
// If we didn't get error details, but we did get a failure response code, report it.
if (!HTTP_RANGE_SUCCESS.contains(responseCode)) {
return new FacebookRequestError(responseCode, INVALID_ERROR_CODE,
- INVALID_ERROR_CODE, null, null,
+ INVALID_ERROR_CODE, null, null, null, null, false,
singleResult.has(BODY_KEY) ?
(JSONObject) Utility.getStringPropertyAsJSON(
singleResult, BODY_KEY, Response.NON_JSON_RESPONSE_PROPERTY) : null,
diff --git a/facebook/src/main/java/com/facebook/FacebookSdkVersion.java b/facebook/src/main/java/com/facebook/FacebookSdkVersion.java
index 4e0e5ea..7067b76 100644
--- a/facebook/src/main/java/com/facebook/FacebookSdkVersion.java
+++ b/facebook/src/main/java/com/facebook/FacebookSdkVersion.java
@@ -17,5 +17,5 @@
package com.facebook;
final class FacebookSdkVersion {
- public static final String BUILD = "3.16.0";
+ public static final String BUILD = "3.17.0";
}
diff --git a/facebook/src/main/java/com/facebook/FacebookTimeSpentData.java b/facebook/src/main/java/com/facebook/FacebookTimeSpentData.java
new file mode 100644
index 0000000..08c2920
--- /dev/null
+++ b/facebook/src/main/java/com/facebook/FacebookTimeSpentData.java
@@ -0,0 +1,226 @@
+package com.facebook;
+
+import android.os.Bundle;
+import android.text.format.DateUtils;
+
+import com.facebook.internal.Logger;
+
+import java.io.Serializable;
+
+class FacebookTimeSpentData implements Serializable {
+ // Constants
+ private static final long serialVersionUID = 1L;
+ private static final String TAG = AppEventsLogger.class.getCanonicalName();
+ private static final long FIRST_TIME_LOAD_RESUME_TIME = -1;
+ private static final long INTERRUPTION_THRESHOLD_MILLISECONDS = 1000;
+ private static final long NUM_MILLISECONDS_IDLE_TO_BE_NEW_SESSION =
+ 60 * DateUtils.SECOND_IN_MILLIS;
+ private static final long APP_ACTIVATE_SUPPRESSION_PERIOD_IN_MILLISECONDS =
+ 5 * DateUtils.MINUTE_IN_MILLIS;
+
+ // Should be kept in sync with the iOS sdk
+ private static final long[] INACTIVE_SECONDS_QUANTA =
+ new long[] {
+ 5 * DateUtils.MINUTE_IN_MILLIS,
+ 15 * DateUtils.MINUTE_IN_MILLIS,
+ 30 * DateUtils.MINUTE_IN_MILLIS,
+ 1 * DateUtils.HOUR_IN_MILLIS,
+ 6 * DateUtils.HOUR_IN_MILLIS,
+ 12 * DateUtils.HOUR_IN_MILLIS,
+ 1 * DateUtils.DAY_IN_MILLIS,
+ 2 * DateUtils.DAY_IN_MILLIS,
+ 3 * DateUtils.DAY_IN_MILLIS,
+ 7 * DateUtils.DAY_IN_MILLIS,
+ 14 * DateUtils.DAY_IN_MILLIS,
+ 21 * DateUtils.DAY_IN_MILLIS,
+ 28 * DateUtils.DAY_IN_MILLIS,
+ 60 * DateUtils.DAY_IN_MILLIS,
+ 90 * DateUtils.DAY_IN_MILLIS,
+ 120 * DateUtils.DAY_IN_MILLIS,
+ 150 * DateUtils.DAY_IN_MILLIS,
+ 180 * DateUtils.DAY_IN_MILLIS,
+ 365 * DateUtils.DAY_IN_MILLIS,
+ };
+
+ private boolean isWarmLaunch;
+ private boolean isAppActive;
+ private long lastActivateEventLoggedTime;
+
+ // Member data that's persisted to disk
+ private long lastResumeTime;
+ private long lastSuspendTime;
+ private long millisecondsSpentInSession;
+ private int interruptionCount;
+
+ /**
+ * Serialization proxy for the FacebookTimeSpentData class. This is version 1 of
+ * serialization. Future serializations may differ in format. This
+ * class should not be modified. If serializations formats change,
+ * create a new class SerializationProxyVx.
+ */
+ private static class SerializationProxyV1 implements Serializable {
+ private static final long serialVersionUID = 6L;
+
+ private final long lastResumeTime;
+ private final long lastSuspendTime;
+ private final long millisecondsSpentInSession;
+ private final int interruptionCount;
+
+ SerializationProxyV1(
+ long lastResumeTime,
+ long lastSuspendTime,
+ long millisecondsSpentInSession,
+ int interruptionCount
+ ) {
+ this.lastResumeTime = lastResumeTime;
+ this.lastSuspendTime = lastSuspendTime;
+ this.millisecondsSpentInSession = millisecondsSpentInSession;
+ this.interruptionCount = interruptionCount;
+ }
+
+ private Object readResolve() {
+ return new FacebookTimeSpentData(
+ lastResumeTime,
+ lastSuspendTime,
+ millisecondsSpentInSession,
+ interruptionCount);
+ }
+ }
+
+ FacebookTimeSpentData() {
+ resetSession();
+ }
+
+ /**
+ * Constructor to be used for V1 serialization only, DO NOT CHANGE.
+ */
+ private FacebookTimeSpentData(
+ long lastResumeTime,
+ long lastSuspendTime,
+ long millisecondsSpentInSession,
+ int interruptionCount
+ ) {
+ resetSession();
+ this.lastResumeTime = lastResumeTime;
+ this.lastSuspendTime = lastSuspendTime;
+ this.millisecondsSpentInSession = millisecondsSpentInSession;
+ this.interruptionCount = interruptionCount;
+ }
+
+ private Object writeReplace() {
+ return new SerializationProxyV1(
+ lastResumeTime,
+ lastSuspendTime,
+ millisecondsSpentInSession,
+ interruptionCount
+ );
+ }
+
+ void onSuspend(AppEventsLogger logger, long eventTime) {
+ if (!isAppActive) {
+ Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Suspend for inactive app");
+ return;
+ }
+
+ long now = eventTime;
+ long delta = (now - lastResumeTime);
+ if (delta < 0) {
+ Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Clock skew detected");
+ delta = 0;
+ }
+ millisecondsSpentInSession += delta;
+ lastSuspendTime = now;
+ isAppActive = false;
+ }
+
+ void onResume(AppEventsLogger logger, long eventTime) {
+ long now = eventTime;
+
+ // Retain old behavior for activated app event - log the event if the event hasn't
+ // been logged in the previous suppression interval or this is a cold launch.
+ // If this is a cold launch, always log the event. Otherwise, use the last
+ // event log time to determine if the app activate should be suppressed or not.
+ if (isColdLaunch() ||
+ ((now - lastActivateEventLoggedTime) > APP_ACTIVATE_SUPPRESSION_PERIOD_IN_MILLISECONDS)) {
+ logger.logEvent(AppEventsConstants.EVENT_NAME_ACTIVATED_APP);
+ lastActivateEventLoggedTime = now;
+ }
+
+ // If this is an application that's not calling onSuspend yet, log and return. We can't
+ // track time spent for this application as there are no calls to onSuspend.
+ if (isAppActive) {
+ Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Resume for active app");
+ return;
+ }
+
+ long interruptionDurationMillis = wasSuspendedEver() ? now - lastSuspendTime : 0;
+ if (interruptionDurationMillis < 0) {
+ Logger.log(LoggingBehavior.APP_EVENTS, TAG, "Clock skew detected");
+ interruptionDurationMillis = 0;
+ }
+
+ // If interruption duration is > new session threshold, then log old session
+ // event and start a new session.
+ if (interruptionDurationMillis > NUM_MILLISECONDS_IDLE_TO_BE_NEW_SESSION) {
+ logAppDeactivatedEvent(logger, interruptionDurationMillis);
+ } else {
+ // We're not logging this resume event - check to see if this should count
+ // as an interruption
+ if (interruptionDurationMillis > INTERRUPTION_THRESHOLD_MILLISECONDS) {
+ interruptionCount++;
+ }
+ }
+
+ lastResumeTime = now;
+ isAppActive = true;
+ }
+
+ private void logAppDeactivatedEvent(AppEventsLogger logger, long interruptionDurationMillis) {
+ // Log the old session information and clear the data
+ Bundle eventParams = new Bundle();
+ eventParams.putInt(
+ AppEventsConstants.EVENT_NAME_SESSION_INTERRUPTIONS,
+ interruptionCount);
+ eventParams.putInt(
+ AppEventsConstants.EVENT_NAME_TIME_BETWEEN_SESSIONS,
+ getQuantaIndex(interruptionDurationMillis));
+ logger.logEvent(
+ AppEventsConstants.EVENT_NAME_DEACTIVATED_APP,
+ (millisecondsSpentInSession/DateUtils.SECOND_IN_MILLIS),
+ eventParams);
+ resetSession();
+ }
+
+ private static int getQuantaIndex(long timeBetweenSessions) {
+ int quantaIndex = 0;
+
+ while (
+ quantaIndex < INACTIVE_SECONDS_QUANTA.length &&
+ INACTIVE_SECONDS_QUANTA[quantaIndex] < timeBetweenSessions
+ ) {
+ ++quantaIndex;
+ }
+
+ return quantaIndex;
+ }
+
+ private void resetSession() {
+ isAppActive = false;
+ lastResumeTime = FIRST_TIME_LOAD_RESUME_TIME;
+ lastSuspendTime = FIRST_TIME_LOAD_RESUME_TIME;
+ interruptionCount = 0;
+ millisecondsSpentInSession = 0;
+ }
+
+ private boolean wasSuspendedEver() {
+ return lastSuspendTime != FIRST_TIME_LOAD_RESUME_TIME;
+ }
+
+ private boolean isColdLaunch() {
+ // On the very first call in the process lifecycle, this will always
+ // return true. After that, it will always return false.
+ boolean result = !isWarmLaunch;
+ isWarmLaunch = true;
+ return result;
+ }
+}
diff --git a/facebook/src/main/java/com/facebook/GetTokenClient.java b/facebook/src/main/java/com/facebook/GetTokenClient.java
index 23cb31b..c0f1a70 100644
--- a/facebook/src/main/java/com/facebook/GetTokenClient.java
+++ b/facebook/src/main/java/com/facebook/GetTokenClient.java
@@ -18,7 +18,7 @@
import android.content.Context;
-import android.os.*;
+import android.os.Bundle;
import com.facebook.internal.NativeProtocol;
import com.facebook.internal.PlatformServiceClient;
diff --git a/facebook/src/main/java/com/facebook/NativeAppCallAttachmentStore.java b/facebook/src/main/java/com/facebook/NativeAppCallAttachmentStore.java
index dc41086..3704be2 100644
--- a/facebook/src/main/java/com/facebook/NativeAppCallAttachmentStore.java
+++ b/facebook/src/main/java/com/facebook/NativeAppCallAttachmentStore.java
@@ -24,7 +24,10 @@
import java.io.*;
import java.net.URLEncoder;
-import java.util.*;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.UUID;
/**
* This class works in conjunction with {@link NativeAppCallContentProvider} to allow apps to attach binary
diff --git a/facebook/src/main/java/com/facebook/NativeAppCallContentProvider.java b/facebook/src/main/java/com/facebook/NativeAppCallContentProvider.java
index bde165f..374547a 100644
--- a/facebook/src/main/java/com/facebook/NativeAppCallContentProvider.java
+++ b/facebook/src/main/java/com/facebook/NativeAppCallContentProvider.java
@@ -24,8 +24,9 @@
import android.util.Log;
import android.util.Pair;
-import java.io.*;
-import java.util.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.UUID;
/**
*
Implements a
diff --git a/facebook/src/main/java/com/facebook/Request.java b/facebook/src/main/java/com/facebook/Request.java
index 1bf5bc9..9b95938 100644
--- a/facebook/src/main/java/com/facebook/Request.java
+++ b/facebook/src/main/java/com/facebook/Request.java
@@ -42,15 +42,13 @@
import java.util.regex.Pattern;
/**
- * A single request to be sent to the Facebook Platform through either the Graph API or REST API. The Request class provides functionality
+ * A single request to be sent to the Facebook Platform through the Graph API. The Request class provides functionality
* relating to serializing and deserializing requests and responses, making calls in batches (with a single round-trip
* to the service) and making calls asynchronously.
*
- * The particular service endpoint that a request targets is determined by either a graph path (see the
- * {@link #setGraphPath(String) setGraphPath} method) or a REST method name (see the {@link #setRestMethod(String)
- * setRestMethod} method); a single request may not target both.
+ * The particular service endpoint that a request targets is determined by a graph path (see the
+ * {@link #setGraphPath(String) setGraphPath} method).
*
* A Request can be executed either anonymously or representing an authenticated user. In the former case, no Session
* needs to be specified, while in the latter, a Session that is in an opened state must be provided. If requests are
@@ -118,7 +116,6 @@ public class Request {
private HttpMethod httpMethod;
private String graphPath;
private GraphObject graphObject;
- private String restMethod;
private String batchEntryName;
private String batchEntryDependsOn;
private boolean batchEntryOmitResultOnSuccess = true;
@@ -266,26 +263,6 @@ public static Request newPostRequest(Session session, String graphPath, GraphObj
return request;
}
- /**
- * Creates a new Request configured to make a call to the Facebook REST API.
- *
- * @param session
- * the Session to use, or null; if non-null, the session must be in an opened state
- * @param restMethod
- * the method in the Facebook REST API to execute
- * @param parameters
- * additional parameters to pass along with the Graph API request; parameters must be Strings, Numbers,
- * Bitmaps, Dates, or Byte arrays.
- * @param httpMethod
- * the HTTP method to use for the request; must be one of GET, POST, or DELETE
- * @return a Request that is ready to execute
- */
- public static Request newRestRequest(Session session, String restMethod, Bundle parameters, HttpMethod httpMethod) {
- Request request = new Request(session, null, parameters, httpMethod);
- request.setRestMethod(restMethod);
- return request;
- }
-
/**
* Creates a new Request configured to retrieve a user's own profile.
*
@@ -543,6 +520,7 @@ public static Request newStatusUpdateRequest(Session session, String message, Gr
* A `null` ID will be provided into the callback if a) there is no native Facebook app, b) no one is logged into
* it, or c) the app has previously called
* {@link Settings#setLimitEventAndDataUsage(android.content.Context, boolean)} with `true` for this user.
+ * You must call this method from a background thread for it to work properly.
*
* @param session
* the Session to issue the Request on, or null; if non-null, the session must be in an opened state.
@@ -577,6 +555,7 @@ public static Request newCustomAudienceThirdPartyIdRequest(Session session, Cont
* A `null` ID will be provided into the callback if a) there is no native Facebook app, b) no one is logged into
* it, or c) the app has previously called
* {@link Settings#setLimitEventAndDataUsage(android.content.Context, boolean)} ;} with `true` for this user.
+ * You must call this method from a background thread for it to work properly.
*
* @param session
* the Session to issue the Request on, or null; if non-null, the session must be in an opened state.
@@ -875,7 +854,7 @@ public final String getGraphPath() {
}
/**
- * Sets the graph path of this request. A graph path may not be set if a REST method has been specified.
+ * Sets the graph path of this request.
*
* @param graphPath
* the graph path for this request
@@ -945,25 +924,6 @@ public final void setParameters(Bundle parameters) {
this.parameters = parameters;
}
- /**
- * Returns the REST method to call for this request.
- *
- * @return the REST method
- */
- public final String getRestMethod() {
- return this.restMethod;
- }
-
- /**
- * Sets the REST method to call for this request. A REST method may not be set if a graph path has been specified.
- *
- * @param restMethod
- * the REST method to call
- */
- public final void setRestMethod(String restMethod) {
- this.restMethod = restMethod;
- }
-
/**
* Returns the Session associated with this request.
*
@@ -1139,30 +1099,6 @@ public static RequestAsyncTask executePostRequestAsync(Session session, String g
return newPostRequest(session, graphPath, graphObject, callback).executeAsync();
}
- /**
- * Starts a new Request configured to make a call to the Facebook REST API.
- *
- * This should only be called from the UI thread.
- *
- * This method is deprecated. Prefer to call Request.newRestRequest(...).executeAsync();
- *
- * @param session
- * the Session to use, or null; if non-null, the session must be in an opened state
- * @param restMethod
- * the method in the Facebook REST API to execute
- * @param parameters
- * additional parameters to pass along with the Graph API request; parameters must be Strings, Numbers,
- * Bitmaps, Dates, or Byte arrays.
- * @param httpMethod
- * the HTTP method to use for the request; must be one of GET, POST, or DELETE
- * @return a RequestAsyncTask that is executing the request
- */
- @Deprecated
- public static RequestAsyncTask executeRestRequestAsync(Session session, String restMethod, Bundle parameters,
- HttpMethod httpMethod) {
- return newRestRequest(session, restMethod, parameters, httpMethod).executeAsync();
- }
-
/**
* Starts a new Request configured to retrieve a user's own profile.
*
@@ -1392,10 +1328,6 @@ public static HttpURLConnection toHttpConnection(Collection requests) {
*/
public static HttpURLConnection toHttpConnection(RequestBatch requests) {
- for (Request request : requests) {
- request.validate();
- }
-
URL url = null;
try {
if (requests.size() == 1) {
@@ -1702,8 +1634,8 @@ public static RequestAsyncTask executeConnectionAsync(Handler callbackHandler, H
@Override
public String toString() {
return new StringBuilder().append("{Request: ").append(" session: ").append(session).append(", graphPath: ")
- .append(graphPath).append(", graphObject: ").append(graphObject).append(", restMethod: ")
- .append(restMethod).append(", httpMethod: ").append(httpMethod).append(", parameters: ")
+ .append(graphPath).append(", graphObject: ").append(graphObject)
+ .append(", httpMethod: ").append(httpMethod).append(", parameters: ")
.append(parameters).append("}").toString();
}
@@ -1813,13 +1745,7 @@ final String getUrlForBatchedRequest() {
throw new FacebookException("Can't override URL for a batch request");
}
- String baseUrl;
- if (this.restMethod != null) {
- baseUrl = getRestPathWithVersion();
- } else {
- baseUrl = getGraphPathWithVersion();
- }
-
+ String baseUrl = getGraphPathWithVersion();
addCommonParameters();
return appendParametersToBaseUrl(baseUrl);
}
@@ -1829,18 +1755,13 @@ final String getUrlForSingleRequest() {
return overriddenURL.toString();
}
- String baseUrl;
- if (this.restMethod != null) {
- baseUrl = String.format("%s/%s", ServerProtocol.getRestUrlBase(), getRestPathWithVersion());
+ String graphBaseUrlBase;
+ if (this.getHttpMethod() == HttpMethod.POST && graphPath != null && graphPath.endsWith(VIDEOS_SUFFIX)) {
+ graphBaseUrlBase = ServerProtocol.getGraphVideoUrlBase();
} else {
- String graphBaseUrlBase;
- if (this.getHttpMethod() == HttpMethod.POST && graphPath != null && graphPath.endsWith(VIDEOS_SUFFIX)) {
- graphBaseUrlBase = ServerProtocol.getGraphVideoUrlBase();
- } else {
- graphBaseUrlBase = ServerProtocol.getGraphUrlBase();
- }
- baseUrl = String.format("%s/%s", graphBaseUrlBase, getGraphPathWithVersion());
+ graphBaseUrlBase = ServerProtocol.getGraphUrlBase();
}
+ String baseUrl = String.format("%s/%s", graphBaseUrlBase, getGraphPathWithVersion());
addCommonParameters();
return appendParametersToBaseUrl(baseUrl);
@@ -1854,14 +1775,6 @@ private String getGraphPathWithVersion() {
return String.format("%s/%s", this.version, this.graphPath);
}
- private String getRestPathWithVersion() {
- Matcher matcher = versionPattern.matcher(this.restMethod);
- if (matcher.matches()) {
- return this.restMethod;
- }
- return String.format("%s/%s/%s", this.version, ServerProtocol.REST_METHOD_BASE, this.restMethod);
- }
-
private static class Attachment {
private final Request request;
private final Object value;
@@ -1933,12 +1846,6 @@ public void writeString(String key, String value) throws IOException {
batch.put(batchEntry);
}
- private void validate() {
- if (graphPath != null && restMethod != null) {
- throw new IllegalArgumentException("Only one of a graph path or REST method may be specified per request.");
- }
- }
-
private static boolean hasOnProgressCallbacks(RequestBatch requests) {
for (RequestBatch.Callback callback : requests.getCallbacks()) {
if (callback instanceof RequestBatch.OnProgressCallback) {
diff --git a/facebook/src/main/java/com/facebook/Response.java b/facebook/src/main/java/com/facebook/Response.java
index a5e408d..f562025 100644
--- a/facebook/src/main/java/com/facebook/Response.java
+++ b/facebook/src/main/java/com/facebook/Response.java
@@ -17,7 +17,10 @@
package com.facebook;
import android.content.Context;
-import com.facebook.internal.*;
+import com.facebook.internal.CacheableRequestBatch;
+import com.facebook.internal.FileLruCache;
+import com.facebook.internal.Logger;
+import com.facebook.internal.Utility;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphObjectList;
import org.json.JSONArray;
@@ -52,6 +55,9 @@ public class Response {
*/
public static final String NON_JSON_RESPONSE_PROPERTY = "FACEBOOK_NON_JSON_RESULT";
+ // From v2.1 of the Graph API, write endpoints will now return valid JSON with the result as the value for the "success" key
+ public static final String SUCCESS_KEY = "success";
+
private static final int INVALID_SESSION_FACEBOOK_ERROR_CODE = 190;
private static final String CODE_KEY = "code";
diff --git a/facebook/src/main/java/com/facebook/Session.java b/facebook/src/main/java/com/facebook/Session.java
index d0d2b56..4fe2311 100644
--- a/facebook/src/main/java/com/facebook/Session.java
+++ b/facebook/src/main/java/com/facebook/Session.java
@@ -16,6 +16,7 @@
package com.facebook;
+import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.*;
import android.content.pm.ResolveInfo;
@@ -24,7 +25,10 @@
import android.support.v4.content.LocalBroadcastManager;
import android.text.TextUtils;
import android.util.Log;
-import com.facebook.internal.*;
+import com.facebook.internal.NativeProtocol;
+import com.facebook.internal.SessionAuthorizationType;
+import com.facebook.internal.Utility;
+import com.facebook.internal.Validate;
import com.facebook.model.GraphMultiResult;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphObjectList;
@@ -124,8 +128,6 @@ public class Session implements Serializable {
private static final String PUBLISH_PERMISSION_PREFIX = "publish";
private static final String MANAGE_PERMISSION_PREFIX = "manage";
- private static final String BASIC_INFO_PERMISSION = "basic_info";
-
@SuppressWarnings("serial")
private static final Set OTHER_PUBLISH_PERMISSIONS = new HashSet() {{
add("ads_management");
@@ -190,6 +192,7 @@ private Object readResolve() {
* class should not be modified. If serializations formats change,
* create a new class SerializationProxyVx.
*/
+ @SuppressWarnings("UnusedDeclaration")
private static class SerializationProxyV2 implements Serializable {
private static final long serialVersionUID = 7663436173185080064L;
private final String applicationId;
diff --git a/facebook/src/main/java/com/facebook/SessionDefaultAudience.java b/facebook/src/main/java/com/facebook/SessionDefaultAudience.java
index 2fdac3d..3f1aea8 100644
--- a/facebook/src/main/java/com/facebook/SessionDefaultAudience.java
+++ b/facebook/src/main/java/com/facebook/SessionDefaultAudience.java
@@ -51,7 +51,7 @@ private SessionDefaultAudience(String protocol) {
nativeProtocolAudience = protocol;
}
- String getNativeProtocolAudience() {
+ public String getNativeProtocolAudience() {
return nativeProtocolAudience;
}
}
diff --git a/facebook/src/main/java/com/facebook/Settings.java b/facebook/src/main/java/com/facebook/Settings.java
index b4608a4..c08376c 100644
--- a/facebook/src/main/java/com/facebook/Settings.java
+++ b/facebook/src/main/java/com/facebook/Settings.java
@@ -30,13 +30,16 @@
import com.facebook.android.BuildConfig;
import com.facebook.internal.AttributionIdentifiers;
import com.facebook.internal.Utility;
-import com.facebook.model.GraphObject;
import com.facebook.internal.Validate;
+import com.facebook.model.GraphObject;
import org.json.JSONException;
import org.json.JSONObject;
import java.lang.reflect.Field;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
@@ -58,6 +61,7 @@ public final class Settings {
private static volatile String facebookDomain = FACEBOOK_COM;
private static AtomicLong onProgressThreshold = new AtomicLong(65536);
private static volatile boolean platformCompatibilityEnabled;
+ private static volatile boolean isLoggingEnabled = BuildConfig.DEBUG;
private static final int DEFAULT_CORE_POOL_SIZE = 5;
private static final int DEFAULT_MAXIMUM_POOL_SIZE = 128;
@@ -180,10 +184,26 @@ public static final void clearLoggingBehaviors() {
*/
public static final boolean isLoggingBehaviorEnabled(LoggingBehavior behavior) {
synchronized (loggingBehaviors) {
- return BuildConfig.DEBUG && loggingBehaviors.contains(behavior);
+ return Settings.isLoggingEnabled() && loggingBehaviors.contains(behavior);
}
}
+ /**
+ * Indicates if logging is enabled.
+ */
+ public static final boolean isLoggingEnabled() {
+ return isLoggingEnabled;
+ }
+
+ /**
+ * Used to enable or disable logging, defaults to BuildConfig.DEBUG.
+ * @param enabled
+ * Logging is enabled if true, disabled if false.
+ */
+ public static final void setIsLoggingEnabled(boolean enabled) {
+ isLoggingEnabled = enabled;
+ }
+
/**
* Returns the Executor used by the SDK for non-AsyncTask background work.
*
@@ -364,7 +384,8 @@ static Response publishInstallAndWaitForResponse(
} else {
return new Response(null, null, null, graphObject, true);
}
- } else if (identifiers.getAndroidAdvertiserId() == null && identifiers.getAttributionId() == null) {
+ } else if (identifiers == null ||
+ (identifiers.getAndroidAdvertiserId() == null && identifiers.getAttributionId() == null)) {
throw new FacebookException("No attribution id available to send to server.");
} else {
if (!Utility.queryAppSettings(applicationId, false).supportsAttribution()) {
diff --git a/facebook/src/main/java/com/facebook/TestSession.java b/facebook/src/main/java/com/facebook/TestSession.java
index 821c1f9..ef8b7dd 100644
--- a/facebook/src/main/java/com/facebook/TestSession.java
+++ b/facebook/src/main/java/com/facebook/TestSession.java
@@ -20,13 +20,12 @@
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
-import com.facebook.model.GraphObject;
-import com.facebook.model.GraphObjectList;
import com.facebook.internal.Logger;
import com.facebook.internal.Utility;
import com.facebook.internal.Validate;
-import org.json.JSONException;
-import org.json.JSONObject;
+import com.facebook.model.GraphObject;
+import com.facebook.model.GraphObjectList;
+import com.facebook.model.GraphUser;
import java.util.*;
@@ -232,70 +231,48 @@ private static synchronized void retrieveTestAccountsForAppIfNeeded() {
appTestAccounts = new HashMap();
- // The data we need is split across two different FQL tables. We construct two queries, submit them
+ // The data we need is split across two different graph API queries. We construct two queries, submit them
// together (the second one refers to the first one), then cross-reference the results.
- // Get the test accounts for this app.
- String testAccountQuery = String.format("SELECT id,access_token FROM test_account WHERE app_id = %s",
- testApplicationId);
- // Get the user names for those accounts.
- String userQuery = "SELECT uid,name FROM user WHERE uid IN (SELECT id FROM #test_accounts)";
+ Request.setDefaultBatchApplicationId(testApplicationId);
Bundle parameters = new Bundle();
-
- // Build a JSON string that contains our queries and pass it as the 'q' parameter of the query.
- JSONObject multiquery;
- try {
- multiquery = new JSONObject();
- multiquery.put("test_accounts", testAccountQuery);
- multiquery.put("users", userQuery);
- } catch (JSONException exception) {
- throw new FacebookException(exception);
- }
- parameters.putString("q", multiquery.toString());
-
- // We need to authenticate as this app.
parameters.putString("access_token", getAppAccessToken());
- Request request = new Request(null, "fql", parameters, null);
- Response response = request.executeAndWait();
+ Request requestTestUsers = new Request(null, "app/accounts/test-users", parameters, null);
+ requestTestUsers.setBatchEntryName("testUsers");
+ requestTestUsers.setBatchEntryOmitResultOnSuccess(false);
- if (response.getError() != null) {
- throw response.getError().getException();
- }
+ Bundle testUserNamesParam = new Bundle();
+ testUserNamesParam.putString("access_token", getAppAccessToken());
+ testUserNamesParam.putString("ids", "{result=testUsers:$.data.*.id}");
+ testUserNamesParam.putString("fields", "name");
- FqlResponse fqlResponse = response.getGraphObjectAs(FqlResponse.class);
+ Request requestTestUserNames = new Request(null, "", testUserNamesParam, null);
+ requestTestUserNames.setBatchEntryDependsOn("testUsers");
- GraphObjectList fqlResults = fqlResponse.getData();
- if (fqlResults == null || fqlResults.size() != 2) {
- throw new FacebookException("Unexpected number of results from FQL query");
+ List responses = Request.executeBatchAndWait(requestTestUsers, requestTestUserNames);
+ if (responses == null || responses.size() != 2) {
+ throw new FacebookException("Unexpected number of results from TestUsers batch query");
}
- // We get back two sets of results. The first is from the test_accounts query, the second from the users query.
- Collection testAccounts = fqlResults.get(0).getFqlResultSet().castToListOf(TestAccount.class);
- Collection userAccounts = fqlResults.get(1).getFqlResultSet().castToListOf(UserAccount.class);
+ TestAccountsResponse testAccountsResponse = responses.get(0).getGraphObjectAs(TestAccountsResponse.class);
+ GraphObjectList testAccounts = testAccountsResponse.getData();
- // Use both sets of results to populate our static array of accounts.
- populateTestAccounts(testAccounts, userAccounts);
+ // Response should contain a map of test accounts: { id's => { GraphUser } }
+ GraphObject userAccountsMap = responses.get(1).getGraphObject();
+ populateTestAccounts(testAccounts, userAccountsMap);
return;
}
private static synchronized void populateTestAccounts(Collection testAccounts,
- Collection userAccounts) {
- // We get different sets of data from each of these queries. We want to combine them into a single data
- // structure. We have added a Name property to the TestAccount interface, even though we don't really get
- // a name back from the service from that query. We stick the Name from the corresponding UserAccount in it.
+ GraphObject userAccountsMap) {
for (TestAccount testAccount : testAccounts) {
+ GraphUser testUser = userAccountsMap.getPropertyAs(testAccount.getId(), GraphUser.class);
+ testAccount.setName(testUser.getName());
storeTestAccount(testAccount);
}
-
- for (UserAccount userAccount : userAccounts) {
- TestAccount testAccount = appTestAccounts.get(userAccount.getUid());
- if (testAccount != null) {
- testAccount.setName(userAccount.getName());
- }
- }
}
private static synchronized void storeTestAccount(TestAccount testAccount) {
@@ -442,7 +419,8 @@ private void deleteTestAccount(String testAccountId, String appAccessToken) {
GraphObject graphObject = response.getGraphObject();
if (error != null) {
Log.w(LOG_TAG, String.format("Could not delete test account %s: %s", testAccountId, error.getException().toString()));
- } else if (graphObject.getProperty(Response.NON_JSON_RESPONSE_PROPERTY) == (Boolean) false) {
+ } else if (graphObject.getProperty(Response.NON_JSON_RESPONSE_PROPERTY) == (Boolean) false
+ || graphObject.getProperty(Response.SUCCESS_KEY) == (Boolean) false) {
Log.w(LOG_TAG, String.format("Could not delete test account %s: unknown reason", testAccountId));
}
}
@@ -484,27 +462,14 @@ private interface TestAccount extends GraphObject {
String getAccessToken();
- // Note: We don't actually get Name from our FQL query. We fill it in by correlating with UserAccounts.
- String getName();
-
- void setName(String name);
- }
-
- private interface UserAccount extends GraphObject {
- String getUid();
-
+ // Note: We don't actually get Name from our accounts/test-users query. We fill it in by correlating with GraphUser.
String getName();
void setName(String name);
}
- private interface FqlResult extends GraphObject {
- GraphObjectList getFqlResultSet();
-
- }
-
- private interface FqlResponse extends GraphObject {
- GraphObjectList getData();
+ private interface TestAccountsResponse extends GraphObject {
+ GraphObjectList getData();
}
private static final class TestTokenCachingStrategy extends TokenCachingStrategy {
diff --git a/facebook/src/main/java/com/facebook/UiLifecycleHelper.java b/facebook/src/main/java/com/facebook/UiLifecycleHelper.java
index d1656b5..8d5b730 100644
--- a/facebook/src/main/java/com/facebook/UiLifecycleHelper.java
+++ b/facebook/src/main/java/com/facebook/UiLifecycleHelper.java
@@ -269,14 +269,7 @@ private boolean handleFacebookDialogActivityResult(int requestCode, int resultCo
return true;
}
- String callIdString = data.getStringExtra(NativeProtocol.EXTRA_PROTOCOL_CALL_ID);
- UUID callId = null;
- if (callIdString != null) {
- try {
- callId = UUID.fromString(callIdString);
- } catch (IllegalArgumentException exception) {
- }
- }
+ UUID callId = NativeProtocol.getCallIdFromIntent(data);
// Was this result for the call we are waiting on?
if (callId != null && pendingFacebookDialogCall.getCallId().equals(callId)) {
diff --git a/facebook/src/main/java/com/facebook/android/FbDialog.java b/facebook/src/main/java/com/facebook/android/FbDialog.java
index 603e692..e35bbaa 100644
--- a/facebook/src/main/java/com/facebook/android/FbDialog.java
+++ b/facebook/src/main/java/com/facebook/android/FbDialog.java
@@ -18,7 +18,9 @@
import android.content.Context;
import android.os.Bundle;
-import com.facebook.*;
+import com.facebook.FacebookDialogException;
+import com.facebook.FacebookException;
+import com.facebook.FacebookOperationCanceledException;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.widget.WebDialog;
diff --git a/facebook/src/main/java/com/facebook/internal/AttributionIdentifiers.java b/facebook/src/main/java/com/facebook/internal/AttributionIdentifiers.java
index 1c8f630..4efe7a3 100644
--- a/facebook/src/main/java/com/facebook/internal/AttributionIdentifiers.java
+++ b/facebook/src/main/java/com/facebook/internal/AttributionIdentifiers.java
@@ -19,8 +19,11 @@
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
+import android.os.Looper;
import android.util.Log;
+import com.facebook.FacebookException;
+
import java.lang.reflect.Method;
/**
@@ -51,6 +54,11 @@ public class AttributionIdentifiers {
private static AttributionIdentifiers getAndroidId(Context context) {
AttributionIdentifiers identifiers = new AttributionIdentifiers();
try {
+ // We can't call getAdvertisingIdInfo on the main thread or the app will potentially
+ // freeze, if this is the case throw:
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new FacebookException("getAndroidId cannot be called on the main thread.");
+ }
Method isGooglePlayServicesAvailable = Utility.getMethodQuietly(
"com.google.android.gms.common.GooglePlayServicesUtil",
"isGooglePlayServicesAvailable",
diff --git a/facebook/src/main/java/com/facebook/internal/ImageDownloader.java b/facebook/src/main/java/com/facebook/internal/ImageDownloader.java
index dc55fb9..69bea7f 100644
--- a/facebook/src/main/java/com/facebook/internal/ImageDownloader.java
+++ b/facebook/src/main/java/com/facebook/internal/ImageDownloader.java
@@ -30,7 +30,8 @@
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
-import java.util.*;
+import java.util.HashMap;
+import java.util.Map;
public class ImageDownloader {
private static final int DOWNLOAD_QUEUE_MAX_CONCURRENT = WorkQueue.DEFAULT_MAX_CONCURRENT;
diff --git a/facebook/src/main/java/com/facebook/internal/NativeProtocol.java b/facebook/src/main/java/com/facebook/internal/NativeProtocol.java
index 7a7186c..a2cf019 100644
--- a/facebook/src/main/java/com/facebook/internal/NativeProtocol.java
+++ b/facebook/src/main/java/com/facebook/internal/NativeProtocol.java
@@ -25,7 +25,10 @@
import android.os.Build;
import android.os.Bundle;
import android.text.TextUtils;
-import com.facebook.*;
+import com.facebook.FacebookException;
+import com.facebook.FacebookOperationCanceledException;
+import com.facebook.SessionDefaultAudience;
+import com.facebook.Settings;
import java.util.*;
@@ -57,12 +60,26 @@ public final class NativeProtocol {
public static final int PROTOCOL_VERSION_20131107 = 20131107;
public static final int PROTOCOL_VERSION_20140204 = 20140204;
public static final int PROTOCOL_VERSION_20140324 = 20140324;
+ public static final int PROTOCOL_VERSION_20140701 = 20140701;
public static final String EXTRA_PROTOCOL_VERSION = "com.facebook.platform.protocol.PROTOCOL_VERSION";
public static final String EXTRA_PROTOCOL_ACTION = "com.facebook.platform.protocol.PROTOCOL_ACTION";
public static final String EXTRA_PROTOCOL_CALL_ID = "com.facebook.platform.protocol.CALL_ID";
public static final String EXTRA_GET_INSTALL_DATA_PACKAGE = "com.facebook.platform.extra.INSTALLDATA_PACKAGE";
+ public static final String EXTRA_PROTOCOL_BRIDGE_ARGS =
+ "com.facebook.platform.protocol.BRIDGE_ARGS";
+
+ public static final String EXTRA_PROTOCOL_METHOD_ARGS =
+ "com.facebook.platform.protocol.METHOD_ARGS";
+
+ public static final String EXTRA_PROTOCOL_METHOD_RESULTS =
+ "com.facebook.platform.protocol.RESULT_ARGS";
+
+ public static final String BRIDGE_ARG_APP_NAME_STRING = "app_name";
+ public static final String BRIDGE_ARG_ACTION_ID_STRING = "action_id";
+ public static final String BRIDGE_ARG_ERROR_BUNDLE = "error";
+
// Messages supported by PlatformService:
public static final int MESSAGE_GET_ACCESS_TOKEN_REQUEST = 0x10000;
public static final int MESSAGE_GET_ACCESS_TOKEN_REPLY = 0x10001;
@@ -126,12 +143,27 @@ public final class NativeProtocol {
public static final String EXTRA_DATA_FAILURES_FATAL = "com.facebook.platform.extra.DATA_FAILURES_FATAL";
public static final String EXTRA_PHOTOS = "com.facebook.platform.extra.PHOTOS";
+ public static final String METHOD_ARGS_PLACE_TAG = "PLACE";
+ public static final String METHOD_ARGS_FRIEND_TAGS = "FRIENDS";
+ public static final String METHOD_ARGS_LINK = "LINK";
+ public static final String METHOD_ARGS_IMAGE = "IMAGE";
+ public static final String METHOD_ARGS_TITLE = "TITLE";
+ public static final String METHOD_ARGS_SUBTITLE = "SUBTITLE";
+ public static final String METHOD_ARGS_DESCRIPTION = "DESCRIPTION";
+ public static final String METHOD_ARGS_REF = "REF";
+ public static final String METHOD_ARGS_DATA_FAILURES_FATAL = "DATA_FAILURES_FATAL";
+ public static final String METHOD_ARGS_PHOTOS = "PHOTOS";
+
// Extras supported for ACTION_OGACTIONPUBLISH_DIALOG:
public static final String EXTRA_ACTION = "com.facebook.platform.extra.ACTION";
public static final String EXTRA_ACTION_TYPE = "com.facebook.platform.extra.ACTION_TYPE";
public static final String EXTRA_PREVIEW_PROPERTY_NAME =
"com.facebook.platform.extra.PREVIEW_PROPERTY_NAME";
+ public static final String METHOD_ARGS_ACTION = "ACTION";
+ public static final String METHOD_ARGS_ACTION_TYPE = "ACTION_TYPE";
+ public static final String METHOD_ARGS_PREVIEW_PROPERTY_NAME = "PREVIEW_PROPERTY_NAME";
+
// OG objects will have this key to set to true if they should be created as part of OG Action publish
public static final String OPEN_GRAPH_CREATE_OBJECT_KEY = "fbsdk:create_object";
// Determines whether an image is user generated
@@ -157,9 +189,9 @@ public final class NativeProtocol {
public static final String ERROR_PERMISSION_DENIED = "PermissionDenied";
public static final String ERROR_SERVICE_DISABLED = "ServiceDisabled";
- public static final String AUDIENCE_ME = "SELF";
- public static final String AUDIENCE_FRIENDS = "ALL_FRIENDS";
- public static final String AUDIENCE_EVERYONE = "EVERYONE";
+ public static final String AUDIENCE_ME = "only_me";
+ public static final String AUDIENCE_FRIENDS = "friends";
+ public static final String AUDIENCE_EVERYONE = "everyone";
// Request codes for different categories of native protocol calls.
public static final int DIALOG_REQUEST_CODE = 0xfacf;
@@ -170,9 +202,6 @@ public final class NativeProtocol {
// Columns returned by PlatformProvider
private static final String PLATFORM_PROVIDER_VERSION_COLUMN = "version";
- // Broadcast action for asynchronously-executing AppCalls
- private static final String PLATFORM_ASYNC_APPCALL_ACTION = "com.facebook.platform.AppCallResultBroadcast";
-
private static abstract class NativeAppInfo {
abstract protected String getPackage();
@@ -308,7 +337,7 @@ static Intent validateServiceIntent(Context context, Intent intent, NativeAppInf
}
public static Intent createProxyAuthIntent(Context context, String applicationId, List permissions,
- String e2e, boolean isRerequest) {
+ String e2e, boolean isRerequest, SessionDefaultAudience defaultAudience) {
for (NativeAppInfo appInfo : facebookAppInfoList) {
Intent intent = new Intent()
.setClassName(appInfo.getPackage(), FACEBOOK_PROXY_AUTH_ACTIVITY)
@@ -323,6 +352,7 @@ public static Intent createProxyAuthIntent(Context context, String applicationId
intent.putExtra(ServerProtocol.DIALOG_PARAM_RESPONSE_TYPE, ServerProtocol.DIALOG_RESPONSE_TYPE_TOKEN);
intent.putExtra(ServerProtocol.DIALOG_PARAM_RETURN_SCOPES, ServerProtocol.DIALOG_RETURN_SCOPES_TRUE);
+ intent.putExtra(ServerProtocol.DIALOG_PARAM_DEFAULT_AUDIENCE, defaultAudience.getNativeProtocolAudience());
if (!Settings.getPlatformCompatibilityEnabled()) {
// Override the API Version for Auth
@@ -360,6 +390,7 @@ public static Intent createTokenRefreshIntent(Context context) {
// Note: be sure this stays sorted in descending order; add new versions at the beginning
private static final List KNOWN_PROTOCOL_VERSIONS =
Arrays.asList(
+ PROTOCOL_VERSION_20140701,
PROTOCOL_VERSION_20140324,
PROTOCOL_VERSION_20140204,
PROTOCOL_VERSION_20131107,
@@ -389,15 +420,43 @@ private static Intent findActivityIntent(Context context, String activityAction,
return intent;
}
- public static Intent createPlatformActivityIntent(Context context, String action, int version, Bundle extras) {
+ public static boolean isVersionCompatibleWithBucketedIntent(int version) {
+ return KNOWN_PROTOCOL_VERSIONS.contains(version) && version >= PROTOCOL_VERSION_20140701;
+ }
+
+ public static Intent createPlatformActivityIntent(
+ Context context,
+ String callId,
+ String action,
+ int version,
+ String applicationName,
+ Bundle extras) {
Intent intent = findActivityIntent(context, INTENT_ACTION_PLATFORM_ACTIVITY, action);
if (intent == null) {
return null;
}
- intent.putExtras(extras)
- .putExtra(EXTRA_PROTOCOL_VERSION, version)
- .putExtra(EXTRA_PROTOCOL_ACTION, action);
+ String applicationId = Utility.getMetadataApplicationId(context);
+
+ intent.putExtra(EXTRA_PROTOCOL_VERSION, version)
+ .putExtra(EXTRA_PROTOCOL_ACTION, action)
+ .putExtra(EXTRA_APPLICATION_ID, applicationId);
+
+ if (isVersionCompatibleWithBucketedIntent(version)) {
+ // This is a bucketed intent
+ Bundle bridgeArguments = new Bundle();
+ bridgeArguments.putString(BRIDGE_ARG_ACTION_ID_STRING, callId);
+ bridgeArguments.putString(BRIDGE_ARG_APP_NAME_STRING, applicationName);
+ intent.putExtra(EXTRA_PROTOCOL_BRIDGE_ARGS, bridgeArguments);
+
+ Bundle methodArguments = extras == null ? new Bundle() : extras;
+ intent.putExtra(EXTRA_PROTOCOL_METHOD_ARGS, methodArguments);
+ } else {
+ // This is the older flat intent
+ intent.putExtra(EXTRA_PROTOCOL_CALL_ID, callId);
+ intent.putExtra(EXTRA_APPLICATION_NAME, applicationName);
+ intent.putExtras(extras);
+ }
return intent;
}
@@ -415,8 +474,58 @@ public static Intent createPlatformServiceIntent(Context context) {
return null;
}
+ public static int getProtocolVersionFromIntent(Intent intent) {
+ return intent.getIntExtra(EXTRA_PROTOCOL_VERSION, 0);
+ }
+
+ public static UUID getCallIdFromIntent(Intent intent) {
+ int version = getProtocolVersionFromIntent(intent);
+ String callIdString = null;
+ if (isVersionCompatibleWithBucketedIntent(version)) {
+ Bundle bridgeArgs = intent.getBundleExtra(EXTRA_PROTOCOL_BRIDGE_ARGS);
+ if (bridgeArgs != null) {
+ callIdString = bridgeArgs.getString(BRIDGE_ARG_ACTION_ID_STRING);
+ }
+ } else {
+ callIdString = intent.getStringExtra(EXTRA_PROTOCOL_CALL_ID);
+ }
+
+ UUID callId = null;
+ if (callIdString != null) {
+ try {
+ callId = UUID.fromString(callIdString);
+ } catch (IllegalArgumentException exception) {
+ }
+ }
+ return callId;
+ }
+
+ public static Bundle getBridgeArgumentsFromIntent(Intent intent) {
+ int version = getProtocolVersionFromIntent(intent);
+ if (!isVersionCompatibleWithBucketedIntent(version)) {
+ return null;
+ }
+
+ return intent.getBundleExtra(EXTRA_PROTOCOL_BRIDGE_ARGS);
+ }
+
+ public static Bundle getSuccessResultsFromIntent(Intent resultIntent) {
+ int version = getProtocolVersionFromIntent(resultIntent);
+ Bundle extras = resultIntent.getExtras();
+ if (!isVersionCompatibleWithBucketedIntent(version) || extras == null) {
+ return extras;
+ }
+
+ return extras.getBundle(EXTRA_PROTOCOL_METHOD_RESULTS);
+ }
+
public static boolean isErrorResult(Intent resultIntent) {
- return resultIntent.hasExtra(STATUS_ERROR_TYPE);
+ Bundle bridgeArgs = getBridgeArgumentsFromIntent(resultIntent);
+ if (bridgeArgs != null) {
+ return bridgeArgs.containsKey(BRIDGE_ARG_ERROR_BUNDLE);
+ } else {
+ return resultIntent.hasExtra(STATUS_ERROR_TYPE);
+ }
}
public static Exception getErrorFromResult(Intent resultIntent) {
@@ -424,12 +533,27 @@ public static Exception getErrorFromResult(Intent resultIntent) {
return null;
}
- String type = resultIntent.getStringExtra(STATUS_ERROR_TYPE);
- String description = resultIntent.getStringExtra(STATUS_ERROR_DESCRIPTION);
+ Bundle bridgeArgs = getBridgeArgumentsFromIntent(resultIntent);
+ if (bridgeArgs != null) {
+ Bundle errorBundle = bridgeArgs.getBundle(BRIDGE_ARG_ERROR_BUNDLE);
+ if (errorBundle != null) {
+ return getErrorFromResult(errorBundle);
+ }
+ }
- if (type.equalsIgnoreCase(ERROR_USER_CANCELED)) {
+ return getErrorFromResult(resultIntent.getExtras());
+ }
+
+ public static Exception getErrorFromResult(Bundle errorBundle) {
+ // TODO This is not going to work for JS dialogs, where the keys are not STATUS_ERROR_TYPE etc.
+ // TODO However, it should keep existing dialogs functional
+ String type = errorBundle.getString(STATUS_ERROR_TYPE);
+ String description = errorBundle.getString(STATUS_ERROR_DESCRIPTION);
+
+ if (type != null && type.equalsIgnoreCase(ERROR_USER_CANCELED)) {
return new FacebookOperationCanceledException(description);
}
+
/* TODO parse error values and create appropriate exception class */
return new FacebookException(description);
}
diff --git a/facebook/src/main/java/com/facebook/internal/ServerProtocol.java b/facebook/src/main/java/com/facebook/internal/ServerProtocol.java
index 06155ce..bc9beec 100644
--- a/facebook/src/main/java/com/facebook/internal/ServerProtocol.java
+++ b/facebook/src/main/java/com/facebook/internal/ServerProtocol.java
@@ -39,6 +39,7 @@ public final class ServerProtocol {
public static final String DIALOG_PARAM_RESPONSE_TYPE = "response_type";
public static final String DIALOG_PARAM_RETURN_SCOPES = "return_scopes";
public static final String DIALOG_PARAM_SCOPE = "scope";
+ public static final String DIALOG_PARAM_DEFAULT_AUDIENCE = "default_audience";
public static final String DIALOG_REREQUEST_AUTH_TYPE = "rerequest";
public static final String DIALOG_RESPONSE_TYPE_TOKEN = "token";
public static final String DIALOG_RETURN_SCOPES_TRUE = "true";
@@ -46,9 +47,7 @@ public final class ServerProtocol {
// URL components
private static final String GRAPH_VIDEO_URL_FORMAT = "https://graph-video.%s";
private static final String GRAPH_URL_FORMAT = "https://graph.%s";
- private static final String REST_URL_FORMAT = "https://api.%s";
- public static final String REST_METHOD_BASE = "method";
- public static final String GRAPH_API_VERSION = "v2.0";
+ public static final String GRAPH_API_VERSION = "v2.1";
private static final String LEGACY_API_VERSION = "v1.0";
@@ -69,10 +68,6 @@ public static final String getGraphVideoUrlBase() {
return String.format(GRAPH_VIDEO_URL_FORMAT, Settings.getFacebookDomain());
}
- public static final String getRestUrlBase() {
- return String.format(REST_URL_FORMAT, Settings.getFacebookDomain());
- }
-
public static final String getAPIVersion() {
if (Settings.getPlatformCompatibilityEnabled()) {
return LEGACY_API_VERSION;
diff --git a/facebook/src/main/java/com/facebook/internal/Utility.java b/facebook/src/main/java/com/facebook/internal/Utility.java
index 41fd4e3..19a8f40 100644
--- a/facebook/src/main/java/com/facebook/internal/Utility.java
+++ b/facebook/src/main/java/com/facebook/internal/Utility.java
@@ -17,27 +17,19 @@
package com.facebook.internal;
import android.content.Context;
-import android.content.Intent;
-import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.net.Uri;
-import android.os.BatteryManager;
-import android.os.Build;
import android.os.Bundle;
-import android.os.Environment;
import android.os.Parcelable;
-import android.os.StatFs;
import android.provider.Settings.Secure;
-import android.telephony.TelephonyManager;
import android.text.TextUtils;
-import android.util.DisplayMetrics;
import android.util.Log;
-import android.view.Display;
-import android.view.WindowManager;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
-import com.facebook.*;
+import com.facebook.FacebookException;
+import com.facebook.Request;
+import com.facebook.Settings;
import com.facebook.android.BuildConfig;
import com.facebook.model.GraphObject;
import org.json.JSONArray;
@@ -85,12 +77,6 @@ public final class Utility {
private static Map fetchedAppSettings =
new ConcurrentHashMap();
- private static int sNumCPUCores = 0;
- private static long sTotalExternalStorageBytes = -1;
- private static long sAvailableExternalStorageBytes = -1;
- private static String sCarrierName = null;
-
-
public static class FetchedAppSettings {
private boolean supportsAttribution;
private boolean supportsImplicitLogging;
@@ -366,13 +352,13 @@ public static void clearFacebookCookies(Context context) {
}
public static void logd(String tag, Exception e) {
- if (BuildConfig.DEBUG && tag != null && e != null) {
+ if (Settings.isLoggingEnabled() && tag != null && e != null) {
Log.d(tag, e.getClass().getSimpleName() + ": " + e.getMessage());
}
}
public static void logd(String tag, String msg) {
- if (BuildConfig.DEBUG && tag != null && msg != null) {
+ if (Settings.isLoggingEnabled() && tag != null && msg != null) {
Log.d(tag, msg);
}
}
@@ -516,14 +502,6 @@ public static void setAppEventExtendedDeviceInfoParameters(GraphObject params, C
params.setProperty("extinfo", extraInfoArray.toString());
}
- private static void silentJSONObjectPut(JSONObject object, String key, T data) {
- try {
- object.put(key, data);
- } catch (JSONException e) {
- // Swallow
- }
- }
-
public static Method getMethodQuietly(Class> clazz, String methodName, Class>... parameterTypes) {
try {
return clazz.getMethod(methodName, parameterTypes);
diff --git a/facebook/src/main/java/com/facebook/widget/FacebookDialog.java b/facebook/src/main/java/com/facebook/widget/FacebookDialog.java
index 1322228..226e120 100644
--- a/facebook/src/main/java/com/facebook/widget/FacebookDialog.java
+++ b/facebook/src/main/java/com/facebook/widget/FacebookDialog.java
@@ -29,7 +29,10 @@
import com.facebook.internal.NativeProtocol;
import com.facebook.internal.Utility;
import com.facebook.internal.Validate;
-import com.facebook.model.*;
+import com.facebook.model.GraphObject;
+import com.facebook.model.GraphObjectList;
+import com.facebook.model.OpenGraphAction;
+import com.facebook.model.OpenGraphObject;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
@@ -341,9 +344,11 @@ public static boolean handleActivityResult(Context context, PendingCall appCall,
if (callback != null) {
if (NativeProtocol.isErrorResult(data)) {
Exception error = NativeProtocol.getErrorFromResult(data);
+
+ // TODO - data.getExtras() doesn't work for the bucketed protocol.
callback.onError(appCall, error, data.getExtras());
} else {
- callback.onComplete(appCall, data.getExtras());
+ callback.onComplete(appCall, NativeProtocol.getSuccessResultsFromIntent(data));
}
}
@@ -480,7 +485,14 @@ static private String getEventName(String action, boolean hasPhotos) {
return eventName;
}
- abstract static class Builder> {
+ /**
+ * Provides a base class for various FacebookDialog builders. This is public primarily to allow its use elsewhere
+ * in the Android SDK; developers are discouraged from constructing their own FacebookDialog builders as the
+ * internal API may change.
+ *
+ * @param The concrete base class of the builder.
+ */
+ public abstract static class Builder> {
final protected Activity activity;
final protected String applicationId;
final protected PendingCall appCall;
@@ -489,7 +501,12 @@ abstract static class Builder> {
protected HashMap imageAttachments = new HashMap();
protected HashMap imageAttachmentFiles = new HashMap();
- Builder(Activity activity) {
+ /**
+ * Constructor.
+ *
+ * @param activity the Activity which is presenting the native Share dialog; must not be null
+ */
+ public Builder(Activity activity) {
Validate.notNull(activity, "activity");
this.activity = activity;
@@ -549,16 +566,26 @@ public CONCRETE setFragment(Fragment fragment) {
public FacebookDialog build() {
validate();
- Bundle extras = new Bundle();
- putExtra(extras, NativeProtocol.EXTRA_APPLICATION_ID, applicationId);
- putExtra(extras, NativeProtocol.EXTRA_APPLICATION_NAME, applicationName);
- extras = setBundleExtras(extras);
-
String action = getActionForFeatures(getDialogFeatures());
int protocolVersion = getProtocolVersionForNativeDialog(activity, action,
getMinVersionForFeatures(getDialogFeatures()));
- Intent intent = NativeProtocol.createPlatformActivityIntent(activity, action, protocolVersion, extras);
+ Bundle extras = null;
+ if (NativeProtocol.isVersionCompatibleWithBucketedIntent(protocolVersion)) {
+ // Facebook app supports the new bucketed protocol
+ extras = getMethodArguments();
+ } else {
+ // Facebook app only supports the old flat protocol
+ extras = setBundleExtras(new Bundle());
+ }
+
+ Intent intent = NativeProtocol.createPlatformActivityIntent(
+ activity,
+ appCall.getCallId().toString(),
+ action,
+ protocolVersion,
+ applicationName,
+ extras);
if (intent == null) {
logDialogActivity(activity, fragment,
getEventName(action, extras.containsKey(NativeProtocol.EXTRA_PHOTOS)),
@@ -567,6 +594,7 @@ public FacebookDialog build() {
throw new FacebookException(
"Unable to create Intent; this likely means the Facebook app is not installed.");
}
+
appCall.setRequestIntent(intent);
return new FacebookDialog(activity, fragment, appCall, getOnPresentCallback());
@@ -636,9 +664,11 @@ List getImageAttachmentNames() {
return new ArrayList(imageAttachments.keySet());
}
- abstract Bundle setBundleExtras(Bundle extras);
+ protected abstract Bundle setBundleExtras(Bundle extras);
- void putExtra(Bundle extras, String key, String value) {
+ protected abstract Bundle getMethodArguments();
+
+ protected void putExtra(Bundle extras, String key, String value) {
if (value != null) {
extras.putString(key, value);
}
@@ -800,7 +830,7 @@ public CONCRETE setDataErrorsFatal(boolean dataErrorsFatal) {
}
@Override
- Bundle setBundleExtras(Bundle extras) {
+ protected Bundle setBundleExtras(Bundle extras) {
putExtra(extras, NativeProtocol.EXTRA_APPLICATION_ID, applicationId);
putExtra(extras, NativeProtocol.EXTRA_APPLICATION_NAME, applicationName);
putExtra(extras, NativeProtocol.EXTRA_TITLE, name);
@@ -818,6 +848,27 @@ Bundle setBundleExtras(Bundle extras) {
}
return extras;
}
+
+ @Override
+ protected Bundle getMethodArguments() {
+ Bundle methodArguments = new Bundle();
+
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_TITLE, name);
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_SUBTITLE, caption);
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_DESCRIPTION, description);
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_LINK, link);
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_IMAGE, picture);
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_PLACE_TAG, place);
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_TITLE, name);
+ putExtra(methodArguments, NativeProtocol.METHOD_ARGS_REF, ref);
+
+ methodArguments.putBoolean(NativeProtocol.METHOD_ARGS_DATA_FAILURES_FATAL, dataErrorsFatal);
+ if (!Utility.isNullOrEmpty(friends)) {
+ methodArguments.putStringArrayList(NativeProtocol.METHOD_ARGS_FRIEND_TAGS, friends);
+ }
+
+ return methodArguments;
+ }
}
/**
@@ -937,7 +988,7 @@ void validate() {
}
@Override
- Bundle setBundleExtras(Bundle extras) {
+ protected Bundle setBundleExtras(Bundle extras) {
putExtra(extras, NativeProtocol.EXTRA_APPLICATION_ID, applicationId);
putExtra(extras, NativeProtocol.EXTRA_APPLICATION_NAME, applicationName);
putExtra(extras, NativeProtocol.EXTRA_PLACE_TAG, place);
@@ -948,6 +999,20 @@ Bundle setBundleExtras(Bundle extras) {
}
return extras;
}
+
+ @Override
+ protected Bundle getMethodArguments() {
+ Bundle methodArgs = new Bundle();
+
+ putExtra(methodArgs, NativeProtocol.METHOD_ARGS_PLACE_TAG, place);
+ methodArgs.putStringArrayList(NativeProtocol.METHOD_ARGS_PHOTOS, imageAttachmentUrls);
+
+ if (!Utility.isNullOrEmpty(friends)) {
+ methodArgs.putStringArrayList(NativeProtocol.METHOD_ARGS_FRIEND_TAGS, friends);
+ }
+
+ return methodArgs;
+ }
}
/**
@@ -1399,7 +1464,7 @@ void updateObjectAttachmentUrls(String objectProperty, List attachmentUr
}
@Override
- Bundle setBundleExtras(Bundle extras) {
+ protected Bundle setBundleExtras(Bundle extras) {
putExtra(extras, NativeProtocol.EXTRA_PREVIEW_PROPERTY_NAME, previewPropertyName);
putExtra(extras, NativeProtocol.EXTRA_ACTION_TYPE, actionType);
extras.putBoolean(NativeProtocol.EXTRA_DATA_FAILURES_FATAL, dataErrorsFatal);
@@ -1413,6 +1478,23 @@ Bundle setBundleExtras(Bundle extras) {
return extras;
}
+ @Override
+ protected Bundle getMethodArguments() {
+ Bundle methodArgs = new Bundle();
+
+ putExtra(methodArgs, NativeProtocol.METHOD_ARGS_PREVIEW_PROPERTY_NAME, previewPropertyName);
+ putExtra(methodArgs, NativeProtocol.METHOD_ARGS_ACTION_TYPE, actionType);
+ methodArgs.putBoolean(NativeProtocol.METHOD_ARGS_DATA_FAILURES_FATAL, dataErrorsFatal);
+
+ JSONObject jsonAction = action.getInnerJSONObject();
+ jsonAction = flattenChildrenOfGraphObject(jsonAction);
+
+ String jsonString = jsonAction.toString();
+ putExtra(methodArgs, NativeProtocol.METHOD_ARGS_ACTION, jsonString);
+
+ return methodArgs;
+ }
+
private JSONObject flattenChildrenOfGraphObject(JSONObject graphObject) {
try {
// Clone the existing object to avoid modifying it from under the caller.
@@ -1577,7 +1659,6 @@ private PendingCall(Parcel in) {
private void setRequestIntent(Intent requestIntent) {
this.requestIntent = requestIntent;
- this.requestIntent.putExtra(NativeProtocol.EXTRA_PROTOCOL_CALL_ID, callId.toString());
}
/**
diff --git a/facebook/src/main/java/com/facebook/widget/GraphObjectAdapter.java b/facebook/src/main/java/com/facebook/widget/GraphObjectAdapter.java
index 285ddee..bc0adbf 100644
--- a/facebook/src/main/java/com/facebook/widget/GraphObjectAdapter.java
+++ b/facebook/src/main/java/com/facebook/widget/GraphObjectAdapter.java
@@ -23,7 +23,7 @@
import android.view.ViewGroup;
import android.view.ViewStub;
import android.widget.*;
-import com.facebook.*;
+import com.facebook.FacebookException;
import com.facebook.android.R;
import com.facebook.internal.ImageDownloader;
import com.facebook.internal.ImageRequest;
diff --git a/facebook/src/main/java/com/facebook/widget/GraphObjectPagingLoader.java b/facebook/src/main/java/com/facebook/widget/GraphObjectPagingLoader.java
index 17d6cb6..0f100ed 100644
--- a/facebook/src/main/java/com/facebook/widget/GraphObjectPagingLoader.java
+++ b/facebook/src/main/java/com/facebook/widget/GraphObjectPagingLoader.java
@@ -20,9 +20,9 @@
import android.os.Handler;
import android.support.v4.content.Loader;
import com.facebook.*;
+import com.facebook.internal.CacheableRequestBatch;
import com.facebook.model.GraphObject;
import com.facebook.model.GraphObjectList;
-import com.facebook.internal.CacheableRequestBatch;
class GraphObjectPagingLoader extends Loader> {
private final Class graphObjectClass;
diff --git a/facebook/src/main/java/com/facebook/widget/LoginButton.java b/facebook/src/main/java/com/facebook/widget/LoginButton.java
index 828b21d..6f548f4 100644
--- a/facebook/src/main/java/com/facebook/widget/LoginButton.java
+++ b/facebook/src/main/java/com/facebook/widget/LoginButton.java
@@ -33,17 +33,18 @@
import android.view.Gravity;
import android.view.View;
import android.widget.Button;
-
import com.facebook.*;
import com.facebook.android.R;
import com.facebook.internal.AnalyticsEvents;
-import com.facebook.model.GraphUser;
import com.facebook.internal.SessionAuthorizationType;
import com.facebook.internal.SessionTracker;
import com.facebook.internal.Utility;
import com.facebook.internal.Utility.FetchedAppSettings;
+import com.facebook.model.GraphUser;
-import java.util.*;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.List;
/**
* A Log In/Log Out button that maintains session state and logs
diff --git a/facebook/src/main/java/com/facebook/widget/PickerFragment.java b/facebook/src/main/java/com/facebook/widget/PickerFragment.java
index 780fdfa..4ef6cbd 100644
--- a/facebook/src/main/java/com/facebook/widget/PickerFragment.java
+++ b/facebook/src/main/java/com/facebook/widget/PickerFragment.java
@@ -32,10 +32,13 @@
import android.view.ViewStub;
import android.view.animation.AlphaAnimation;
import android.widget.*;
-import com.facebook.*;
+import com.facebook.FacebookException;
+import com.facebook.Request;
+import com.facebook.Session;
+import com.facebook.SessionState;
import com.facebook.android.R;
-import com.facebook.model.GraphObject;
import com.facebook.internal.SessionTracker;
+import com.facebook.model.GraphObject;
import java.util.*;
diff --git a/facebook/src/main/java/com/facebook/widget/PlacePickerFragment.java b/facebook/src/main/java/com/facebook/widget/PlacePickerFragment.java
index 58b2775..0437001 100644
--- a/facebook/src/main/java/com/facebook/widget/PlacePickerFragment.java
+++ b/facebook/src/main/java/com/facebook/widget/PlacePickerFragment.java
@@ -35,9 +35,9 @@
import com.facebook.*;
import com.facebook.android.R;
import com.facebook.internal.AnalyticsEvents;
-import com.facebook.model.GraphPlace;
import com.facebook.internal.Logger;
import com.facebook.internal.Utility;
+import com.facebook.model.GraphPlace;
import java.util.*;
diff --git a/facebook/src/main/java/com/facebook/widget/ToolTipPopup.java b/facebook/src/main/java/com/facebook/widget/ToolTipPopup.java
index 58f64f3..0622416 100644
--- a/facebook/src/main/java/com/facebook/widget/ToolTipPopup.java
+++ b/facebook/src/main/java/com/facebook/widget/ToolTipPopup.java
@@ -16,11 +16,6 @@
package com.facebook.widget;
-import java.lang.ref.WeakReference;
-
-import com.facebook.android.R;
-import com.facebook.widget.LoginButton.ToolTipMode;
-
import android.app.Activity;
import android.content.Context;
import android.view.LayoutInflater;
@@ -31,6 +26,9 @@
import android.widget.ImageView;
import android.widget.PopupWindow;
import android.widget.TextView;
+import com.facebook.android.R;
+
+import java.lang.ref.WeakReference;
public class ToolTipPopup {
diff --git a/facebook/src/main/java/com/facebook/widget/WebDialog.java b/facebook/src/main/java/com/facebook/widget/WebDialog.java
index 84be24e..9150f50 100644
--- a/facebook/src/main/java/com/facebook/widget/WebDialog.java
+++ b/facebook/src/main/java/com/facebook/widget/WebDialog.java
@@ -37,7 +37,8 @@
import android.widget.ImageView;
import android.widget.LinearLayout;
import com.facebook.*;
-import com.facebook.android.*;
+import com.facebook.android.R;
+import com.facebook.android.Util;
import com.facebook.internal.Logger;
import com.facebook.internal.ServerProtocol;
import com.facebook.internal.Utility;
@@ -51,7 +52,6 @@
public class WebDialog extends Dialog {
private static final String LOG_TAG = Logger.LOG_TAG_BASE + "WebDialog";
private static final String DISPLAY_TOUCH = "touch";
- private static final String USER_AGENT = "user_agent";
static final String REDIRECT_URI = "fbconnect://success";
static final String CANCEL_URI = "fbconnect://cancel";
static final boolean DISABLE_SSL_CHECK_FOR_TESTING = false;