/**
* Copyright 2010-present Facebook
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.facebook.android;
import android.Manifest;
import android.app.Activity;
import android.content.*;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.net.Uri;
import android.os.*;
import com.facebook.*;
import com.facebook.Session.StatusCallback;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.lang.ref.WeakReference;
import java.net.MalformedURLException;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
/**
* THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
* <p/>
* All public members of this class are intentionally deprecated.
* New code should instead use
* {@link Session} to manage session state,
* {@link Request} to make API requests, and
* {@link com.facebook.widget.WebDialog} to make dialog requests.
* <p/>
* Adding @Deprecated to this class causes warnings in other deprecated classes
* that reference this one. That is the only reason this entire class is not
* deprecated.
*
* @devDocDeprecated
*/
public class Facebook {
// Strings used in the authorization flow
@Deprecated
public static final String REDIRECT_URI = "fbconnect://success";
@Deprecated
public static final String CANCEL_URI = "fbconnect://cancel";
@Deprecated
public static final String TOKEN = "access_token";
@Deprecated
public static final String EXPIRES = "expires_in";
@Deprecated
public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled";
@Deprecated
public static final Uri ATTRIBUTION_ID_CONTENT_URI =
Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider");
@Deprecated
public static final String ATTRIBUTION_ID_COLUMN_NAME = "aid";
@Deprecated
public static final int FORCE_DIALOG_AUTH = -1;
private static final String LOGIN = "oauth";
// Used as default activityCode by authorize(). See authorize() below.
private static final int DEFAULT_AUTH_ACTIVITY_CODE = 32665;
// Facebook server endpoints: may be modified in a subclass for testing
@Deprecated
protected static String DIALOG_BASE_URL = "https://m.facebook.com/dialog/";
@Deprecated
protected static String GRAPH_BASE_URL = "https://graph.facebook.com/";
@Deprecated
protected static String RESTSERVER_URL = "https://api.facebook.com/restserver.php";
private final Object lock = new Object();
private String accessToken = null;
private long accessExpiresMillisecondsAfterEpoch = 0;
private long lastAccessUpdateMillisecondsAfterEpoch = 0;
private String mAppId;
private Activity pendingAuthorizationActivity;
private String[] pendingAuthorizationPermissions;
private Session pendingOpeningSession;
private volatile Session session; // must synchronize this.sync to write
private boolean sessionInvalidated; // must synchronize this.sync to access
private SetterTokenCachingStrategy tokenCache;
private volatile Session userSetSession;
// If the last time we extended the access token was more than 24 hours ago
// we try to refresh the access token again.
final private static long REFRESH_TOKEN_BARRIER = 24L * 60L * 60L * 1000L;
/**
* Constructor for Facebook object.
*
* @param appId
* Your Facebook application ID. Found at
* www.facebook.com/developers/apps.php.
*/
@Deprecated
public Facebook(String appId) {
if (appId == null) {
throw new IllegalArgumentException("You must specify your application ID when instantiating "
+ "a Facebook object. See README for details.");
}
mAppId = appId;
}
/**
* Default authorize method. Grants only basic permissions.
* <p/>
* See authorize() below for @params.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*/
@Deprecated
public void authorize(Activity activity, final DialogListener listener) {
authorize(activity, new String[]{}, DEFAULT_AUTH_ACTIVITY_CODE, SessionLoginBehavior.SSO_WITH_FALLBACK,
listener);
}
/**
* Authorize method that grants custom permissions.
* <p/>
* See authorize() below for @params.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*/
@Deprecated
public void authorize(Activity activity, String[] permissions, final DialogListener listener) {
authorize(activity, permissions, DEFAULT_AUTH_ACTIVITY_CODE, SessionLoginBehavior.SSO_WITH_FALLBACK, listener);
}
/**
* Full authorize method.
* <p/>
* Starts either an Activity or a dialog which prompts the user to log in to
* Facebook and grant the requested permissions to the given application.
* <p/>
* This method will, when possible, use Facebook's single sign-on for
* Android to obtain an access token. This involves proxying a call through
* the Facebook for Android stand-alone application, which will handle the
* authentication flow, and return an OAuth access token for making API
* calls.
* <p/>
* Because this process will not be available for all users, if single
* sign-on is not possible, this method will automatically fall back to the
* OAuth 2.0 User-Agent flow. In this flow, the user credentials are handled
* by Facebook in an embedded WebView, not by the client application. As
* such, the dialog makes a network request and renders HTML content rather
* than a native UI. The access token is retrieved from a redirect to a
* special URL that the WebView handles.
* <p/>
* Note that User credentials could be handled natively using the OAuth 2.0
* Username and Password Flow, but this is not supported by this SDK.
* <p/>
* See http://developers.facebook.com/docs/authentication/ and
* http://wiki.oauth.net/OAuth-2 for more details.
* <p/>
* Note that this method is asynchronous and the callback will be invoked in
* the original calling thread (not in a background thread).
* <p/>
* Also note that requests may be made to the API without calling authorize
* first, in which case only public information is returned.
* <p/>
* IMPORTANT: Note that single sign-on authentication will not function
* correctly if you do not include a call to the authorizeCallback() method
* in your onActivityResult() function! Please see below for more
* information. single sign-on may be disabled by passing FORCE_DIALOG_AUTH
* as the activityCode parameter in your call to authorize().
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param activity
* The Android activity in which we want to display the
* authorization dialog.
* @param permissions
* A list of permissions required for this application: e.g.
* "read_stream", "publish_stream", "offline_access", etc. see
* http://developers.facebook.com/docs/authentication/permissions
* This parameter should not be null -- if you do not require any
* permissions, then pass in an empty String array.
* @param activityCode
* Single sign-on requires an activity result to be called back
* to the client application -- if you are waiting on other
* activities to return data, pass a custom activity code here to
* avoid collisions. If you would like to force the use of legacy
* dialog-based authorization, pass FORCE_DIALOG_AUTH for this
* parameter. Otherwise just omit this parameter and Facebook
* will use a suitable default. See
* http://developer.android.com/reference/android/
* app/Activity.html for more information.
* @param listener
* Callback interface for notifying the calling application when
* the authentication dialog has completed, failed, or been
* canceled.
*/
@Deprecated
public void authorize(Activity activity, String[] permissions, int activityCode, final DialogListener listener) {
SessionLoginBehavior behavior = (activityCode >= 0) ? SessionLoginBehavior.SSO_WITH_FALLBACK
: SessionLoginBehavior.SUPPRESS_SSO;
authorize(activity, permissions, activityCode, behavior, listener);
}
/**
* Full authorize method.
*
* Starts either an Activity or a dialog which prompts the user to log in to
* Facebook and grant the requested permissions to the given application.
*
* This method will, when possible, use Facebook's single sign-on for
* Android to obtain an access token. This involves proxying a call through
* the Facebook for Android stand-alone application, which will handle the
* authentication flow, and return an OAuth access token for making API
* calls.
*
* Because this process will not be available for all users, if single
* sign-on is not possible, this method will automatically fall back to the
* OAuth 2.0 User-Agent flow. In this flow, the user credentials are handled
* by Facebook in an embedded WebView, not by the client application. As
* such, the dialog makes a network request and renders HTML content rather
* than a native UI. The access token is retrieved from a redirect to a
* special URL that the WebView handles.
*
* Note that User credentials could be handled natively using the OAuth 2.0
* Username and Password Flow, but this is not supported by this SDK.
*
* See http://developers.facebook.com/docs/authentication/ and
* http://wiki.oauth.net/OAuth-2 for more details.
*
* Note that this method is asynchronous and the callback will be invoked in
* the original calling thread (not in a background thread).
*
* Also note that requests may be made to the API without calling authorize
* first, in which case only public information is returned.
*
* IMPORTANT: Note that single sign-on authentication will not function
* correctly if you do not include a call to the authorizeCallback() method
* in your onActivityResult() function! Please see below for more
* information. single sign-on may be disabled by passing FORCE_DIALOG_AUTH
* as the activityCode parameter in your call to authorize().
*
* @param activity
* The Android activity in which we want to display the
* authorization dialog.
* @param permissions
* A list of permissions required for this application: e.g.
* "read_stream", "publish_stream", "offline_access", etc. see
* http://developers.facebook.com/docs/authentication/permissions
* This parameter should not be null -- if you do not require any
* permissions, then pass in an empty String array.
* @param activityCode
* Single sign-on requires an activity result to be called back
* to the client application -- if you are waiting on other
* activities to return data, pass a custom activity code here to
* avoid collisions. If you would like to force the use of legacy
* dialog-based authorization, pass FORCE_DIALOG_AUTH for this
* parameter. Otherwise just omit this parameter and Facebook
* will use a suitable default. See
* http://developer.android.com/reference/android/
* app/Activity.html for more information.
* @param behavior
* The {@link SessionLoginBehavior SessionLoginBehavior} that
* specifies what behaviors should be attempted during
* authorization.
* @param listener
* Callback interface for notifying the calling application when
* the authentication dialog has completed, failed, or been
* canceled.
*/
private void authorize(Activity activity, String[] permissions, int activityCode,
SessionLoginBehavior behavior, final DialogListener listener) {
checkUserSession("authorize");
pendingOpeningSession = new Session.Builder(activity).
setApplicationId(mAppId).
setTokenCachingStrategy(getTokenCache()).
build();
pendingAuthorizationActivity = activity;
pendingAuthorizationPermissions = (permissions != null) ? permissions : new String[0];
StatusCallback callback = new StatusCallback() {
@Override
public void call(Session callbackSession, SessionState state, Exception exception) {
// Invoke user-callback.
onSessionCallback(callbackSession, state, exception, listener);
}
};
Session.OpenRequest openRequest = new Session.OpenRequest(activity).
setCallback(callback).
setLoginBehavior(behavior).
setRequestCode(activityCode).
setPermissions(Arrays.asList(pendingAuthorizationPermissions));
openSession(pendingOpeningSession, openRequest, pendingAuthorizationPermissions.length > 0);
}
private void openSession(Session session, Session.OpenRequest openRequest, boolean isPublish) {
openRequest.setIsLegacy(true);
if (isPublish) {
session.openForPublish(openRequest);
} else {
session.openForRead(openRequest);
}
}
@SuppressWarnings("deprecation")
private void onSessionCallback(Session callbackSession, SessionState state, Exception exception,
DialogListener listener) {
Bundle extras = callbackSession.getAuthorizationBundle();
if (state == SessionState.OPENED) {
Session sessionToClose = null;
synchronized (Facebook.this.lock) {
if (callbackSession != Facebook.this.session) {
sessionToClose = Facebook.this.session;
Facebook.this.session = callbackSession;
Facebook.this.sessionInvalidated = false;
}
}
if (sessionToClose != null) {
sessionToClose.close();
}
listener.onComplete(extras);
} else if (exception != null) {
if (exception instanceof FacebookOperationCanceledException) {
listener.onCancel();
} else if ((exception instanceof FacebookAuthorizationException) && (extras != null)
&& extras.containsKey(Session.WEB_VIEW_ERROR_CODE_KEY)
&& extras.containsKey(Session.WEB_VIEW_FAILING_URL_KEY)) {
DialogError error = new DialogError(exception.getMessage(),
extras.getInt(Session.WEB_VIEW_ERROR_CODE_KEY),
extras.getString(Session.WEB_VIEW_FAILING_URL_KEY));
listener.onError(error);
} else {
FacebookError error = new FacebookError(exception.getMessage());
listener.onFacebookError(error);
}
}
}
/**
* Helper to validate a service intent by resolving and checking the
* provider's package signature.
*
* @param context
* @param intent
* @return true if the service intent resolution happens successfully and
* the signatures match.
*/
private boolean validateServiceIntent(Context context, Intent intent) {
ResolveInfo resolveInfo = context.getPackageManager().resolveService(intent, 0);
if (resolveInfo == null) {
return false;
}
return validateAppSignatureForPackage(context, resolveInfo.serviceInfo.packageName);
}
/**
* Query the signature for the application that would be invoked by the
* given intent and verify that it matches the FB application's signature.
*
* @param context
* @param packageName
* @return true if the app's signature matches the expected signature.
*/
private boolean validateAppSignatureForPackage(Context context, String packageName) {
PackageInfo packageInfo;
try {
packageInfo = context.getPackageManager().getPackageInfo(packageName, PackageManager.GET_SIGNATURES);
} catch (NameNotFoundException e) {
return false;
}
for (Signature signature : packageInfo.signatures) {
if (signature.toCharsString().equals(FB_APP_SIGNATURE)) {
return true;
}
}
return false;
}
/**
* IMPORTANT: If you are using the deprecated authorize() method,
* this method must be invoked at the top of the calling
* activity's onActivityResult() function or Facebook authentication will
* not function properly!
* <p/>
* If your calling activity does not currently implement onActivityResult(),
* you must implement it and include a call to this method if you intend to
* use the authorize() method in this SDK.
* <p/>
* For more information, see
* http://developer.android.com/reference/android/app/
* Activity.html#onActivityResult(int, int, android.content.Intent)
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*/
@Deprecated
public void authorizeCallback(int requestCode, int resultCode, Intent data) {
checkUserSession("authorizeCallback");
Session pending = this.pendingOpeningSession;
if (pending != null) {
if (pending.onActivityResult(this.pendingAuthorizationActivity, requestCode, resultCode, data)) {
this.pendingOpeningSession = null;
this.pendingAuthorizationActivity = null;
this.pendingAuthorizationPermissions = null;
}
}
}
/**
* Refresh OAuth access token method. Binds to Facebook for Android
* stand-alone application application to refresh the access token. This
* method tries to connect to the Facebook App which will handle the
* authentication flow, and return a new OAuth access token. This method
* will automatically replace the old token with a new one. Note that this
* method is asynchronous and the callback will be invoked in the original
* calling thread (not in a background thread).
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param context
* The Android Context that will be used to bind to the Facebook
* RefreshToken Service
* @param serviceListener
* Callback interface for notifying the calling application when
* the refresh request has completed or failed (can be null). In
* case of a success a new token can be found inside the result
* Bundle under Facebook.ACCESS_TOKEN key.
* @return true if the binding to the RefreshToken Service was created
*/
@Deprecated
public boolean extendAccessToken(Context context, ServiceListener serviceListener) {
checkUserSession("extendAccessToken");
Intent intent = new Intent();
intent.setClassName("com.facebook.katana", "com.facebook.katana.platform.TokenRefreshService");
// Verify that the application whose package name is
// com.facebook.katana
// has the expected FB app signature.
if (!validateServiceIntent(context, intent)) {
return false;
}
return context.bindService(intent, new TokenRefreshServiceConnection(context, serviceListener),
Context.BIND_AUTO_CREATE);
}
/**
* Calls extendAccessToken if shouldExtendAccessToken returns true.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @return the same value as extendAccessToken if the the token requires
* refreshing, true otherwise
*/
@Deprecated
public boolean extendAccessTokenIfNeeded(Context context, ServiceListener serviceListener) {
checkUserSession("extendAccessTokenIfNeeded");
if (shouldExtendAccessToken()) {
return extendAccessToken(context, serviceListener);
}
return true;
}
/**
* Check if the access token requires refreshing.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @return true if the last time a new token was obtained was over 24 hours
* ago.
*/
@Deprecated
public boolean shouldExtendAccessToken() {
checkUserSession("shouldExtendAccessToken");
return isSessionValid()
&& (System.currentTimeMillis() - lastAccessUpdateMillisecondsAfterEpoch >= REFRESH_TOKEN_BARRIER);
}
/**
* Handles connection to the token refresh service (this service is a part
* of Facebook App).
*/
private class TokenRefreshServiceConnection implements ServiceConnection {
final Messenger messageReceiver = new Messenger(
new TokenRefreshConnectionHandler(Facebook.this, this));
final ServiceListener serviceListener;
final Context applicationsContext;
Messenger messageSender = null;
public TokenRefreshServiceConnection(Context applicationsContext, ServiceListener serviceListener) {
this.applicationsContext = applicationsContext;
this.serviceListener = serviceListener;
}
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
messageSender = new Messenger(service);
refreshToken();
}
@Override
public void onServiceDisconnected(ComponentName arg) {
serviceListener.onError(new Error("Service disconnected"));
// We returned an error so there's no point in
// keeping the binding open.
applicationsContext.unbindService(TokenRefreshServiceConnection.this);
}
private void refreshToken() {
Bundle requestData = new Bundle();
requestData.putString(TOKEN, accessToken);
Message request = Message.obtain();
request.setData(requestData);
request.replyTo = messageReceiver;
try {
messageSender.send(request);
} catch (RemoteException e) {
serviceListener.onError(new Error("Service connection error"));
}
}
}
// Creating a static Handler class to reduce the possibility of a memory leak.
// Handler objects for the same thread all share a common Looper object, which they post messages
// to and read from. As messages contain target Handler, as long as there are messages with target
// handler in the message queue, the handler cannot be garbage collected. If handler is not static,
// the instance of the containing class also cannot be garbage collected even if it is destroyed.
private static class TokenRefreshConnectionHandler extends Handler {
WeakReference<Facebook> facebookWeakReference;
WeakReference<TokenRefreshServiceConnection> connectionWeakReference;
TokenRefreshConnectionHandler(Facebook facebook, TokenRefreshServiceConnection connection) {
super();
facebookWeakReference = new WeakReference<Facebook>(facebook);
connectionWeakReference = new WeakReference<TokenRefreshServiceConnection>(connection);
}
@Override
@SuppressWarnings("deprecation")
public void handleMessage(Message msg) {
Facebook facebook = facebookWeakReference.get();
TokenRefreshServiceConnection connection = connectionWeakReference.get();
if (facebook == null || connection == null) {
return;
}
String token = msg.getData().getString(TOKEN);
// Legacy functions in Facebook class (and ServiceListener implementors) expect expires_in in
// milliseconds from epoch
long expiresAtMsecFromEpoch = msg.getData().getLong(EXPIRES) * 1000L;
if (token != null) {
facebook.setAccessToken(token);
facebook.setAccessExpires(expiresAtMsecFromEpoch);
Session refreshSession = facebook.session;
if (refreshSession != null) {
// Session.internalRefreshToken expects the original bundle with expires_in in seconds from
// epoch.
LegacyHelper.extendTokenCompleted(refreshSession, msg.getData());
}
if (connection.serviceListener != null) {
// To avoid confusion we should return the expiration time in
// the same format as the getAccessExpires() function - that
// is in milliseconds.
Bundle resultBundle = (Bundle) msg.getData().clone();
resultBundle.putLong(EXPIRES, expiresAtMsecFromEpoch);
connection.serviceListener.onComplete(resultBundle);
}
} else if (connection.serviceListener != null) { // extract errors only if
// client wants them
String error = msg.getData().getString("error");
if (msg.getData().containsKey("error_code")) {
int errorCode = msg.getData().getInt("error_code");
connection.serviceListener.onFacebookError(new FacebookError(error, null, errorCode));
} else {
connection.serviceListener.onError(new Error(error != null ? error : "Unknown service error"));
}
}
// The refreshToken function should be called rarely,
// so there is no point in keeping the binding open.
connection.applicationsContext.unbindService(connection);
}
}
/**
* Invalidate the current user session by removing the access token in
* memory, clearing the browser cookie, and calling auth.expireSession
* through the API.
* <p/>
* Note that this method blocks waiting for a network response, so do not
* call it in a UI thread.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param context
* The Android context in which the logout should be called: it
* should be the same context in which the login occurred in
* order to clear any stored cookies
* @throws IOException
* @throws MalformedURLException
* @return JSON string representation of the auth.expireSession response
* ("true" if successful)
*/
@Deprecated
public String logout(Context context) throws MalformedURLException, IOException {
return logoutImpl(context);
}
String logoutImpl(Context context) throws MalformedURLException, IOException {
checkUserSession("logout");
Bundle b = new Bundle();
b.putString("method", "auth.expireSession");
String response = request(b);
long currentTimeMillis = System.currentTimeMillis();
Session sessionToClose = null;
synchronized (this.lock) {
sessionToClose = session;
session = null;
accessToken = null;
accessExpiresMillisecondsAfterEpoch = 0;
lastAccessUpdateMillisecondsAfterEpoch = currentTimeMillis;
sessionInvalidated = false;
}
if (sessionToClose != null) {
sessionToClose.closeAndClearTokenInformation();
}
return response;
}
/**
* Make a request to Facebook's old (pre-graph) API with the given
* parameters. One of the parameter keys must be "method" and its value
* should be a valid REST server API method.
* <p/>
* See http://developers.facebook.com/docs/reference/rest/
* <p/>
* Note that this method blocks waiting for a network response, so do not
* call it in a UI thread.
* <p/>
* Example: <code>
* Bundle parameters = new Bundle();
* parameters.putString("method", "auth.expireSession");
* String response = request(parameters);
* </code>
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
*
* @param parameters
* Key-value pairs of parameters to the request. Refer to the
* documentation: one of the parameters must be "method".
* @throws IOException
* if a network error occurs
* @throws MalformedURLException
* if accessing an invalid endpoint
* @throws IllegalArgumentException
* if one of the parameters is not "method"
* @return JSON string representation of the response
*/
@Deprecated
public String request(Bundle parameters) throws MalformedURLException, IOException {
if (!parameters.containsKey("method")) {
throw new IllegalArgumentException("API method must be specified. "
+ "(parameters must contain key \"method\" and value). See"
+ " http://developers.facebook.com/docs/reference/rest/");
}
return requestImpl(null, parameters, "GET");
}
/**
* Make a request to the Facebook Graph API without any parameters.
* <p/>
* See http://developers.facebook.com/docs/api
* <p/>
* Note that this method blocks waiting for a network response, so do not
* call it in a UI thread.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
*
* @param graphPath
* Path to resource in the Facebook graph, e.g., to fetch data
* about the currently logged authenticated user, provide "me",
* which will fetch http://graph.facebook.com/me
* @throws IOException
* @throws MalformedURLException
* @return JSON string representation of the response
*/
@Deprecated
public String request(String graphPath) throws MalformedURLException, IOException {
return requestImpl(graphPath, new Bundle(), "GET");
}
/**
* Make a request to the Facebook Graph API with the given string parameters
* using an HTTP GET (default method).
* <p/>
* See http://developers.facebook.com/docs/api
* <p/>
* Note that this method blocks waiting for a network response, so do not
* call it in a UI thread.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
*
* @param graphPath
* Path to resource in the Facebook graph, e.g., to fetch data
* about the currently logged authenticated user, provide "me",
* which will fetch http://graph.facebook.com/me
* @param parameters
* key-value string parameters, e.g. the path "search" with
* parameters "q" : "facebook" would produce a query for the
* following graph resource:
* https://graph.facebook.com/search?q=facebook
* @throws IOException
* @throws MalformedURLException
* @return JSON string representation of the response
*/
@Deprecated
public String request(String graphPath, Bundle parameters) throws MalformedURLException, IOException {
return requestImpl(graphPath, parameters, "GET");
}
/**
* Synchronously make a request to the Facebook Graph API with the given
* HTTP method and string parameters. Note that binary data parameters (e.g.
* pictures) are not yet supported by this helper function.
* <p/>
* See http://developers.facebook.com/docs/api
* <p/>
* Note that this method blocks waiting for a network response, so do not
* call it in a UI thread.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Request} for more info.
*
* @param graphPath
* Path to resource in the Facebook graph, e.g., to fetch data
* about the currently logged authenticated user, provide "me",
* which will fetch http://graph.facebook.com/me
* @param params
* Key-value string parameters, e.g. the path "search" with
* parameters {"q" : "facebook"} would produce a query for the
* following graph resource:
* https://graph.facebook.com/search?q=facebook
* @param httpMethod
* http verb, e.g. "GET", "POST", "DELETE"
* @throws IOException
* @throws MalformedURLException
* @return JSON string representation of the response
*/
@Deprecated
public String request(String graphPath, Bundle params, String httpMethod) throws FileNotFoundException,
MalformedURLException, IOException {
return requestImpl(graphPath, params, httpMethod);
}
// Internal call to avoid deprecated warnings.
@SuppressWarnings("deprecation")
String requestImpl(String graphPath, Bundle params, String httpMethod) throws FileNotFoundException,
MalformedURLException, IOException {
params.putString("format", "json");
if (isSessionValid()) {
params.putString(TOKEN, getAccessToken());
}
String url = (graphPath != null) ? GRAPH_BASE_URL + graphPath : RESTSERVER_URL;
return Util.openUrl(url, httpMethod, params);
}
/**
* Generate a UI dialog for the request action in the given Android context.
* <p/>
* Note that this method is asynchronous and the callback will be invoked in
* the original calling thread (not in a background thread).
*
* This method is deprecated. See {@link com.facebook.widget.WebDialog}.
*
* @param context
* The Android context in which we will generate this dialog.
* @param action
* String representation of the desired method: e.g. "login",
* "stream.publish", ...
* @param listener
* Callback interface to notify the application when the dialog
* has completed.
*/
@Deprecated
public void dialog(Context context, String action, DialogListener listener) {
dialog(context, action, new Bundle(), listener);
}
/**
* Generate a UI dialog for the request action in the given Android context
* with the provided parameters.
* <p/>
* Note that this method is asynchronous and the callback will be invoked in
* the original calling thread (not in a background thread).
*
* This method is deprecated. See {@link com.facebook.widget.WebDialog}.
*
* @param context
* The Android context in which we will generate this dialog.
* @param action
* String representation of the desired method: e.g. "feed" ...
* @param parameters
* String key-value pairs to be passed as URL parameters.
* @param listener
* Callback interface to notify the application when the dialog
* has completed.
*/
@Deprecated
public void dialog(Context context, String action, Bundle parameters, final DialogListener listener) {
parameters.putString("display", "touch");
parameters.putString("redirect_uri", REDIRECT_URI);
if (action.equals(LOGIN)) {
parameters.putString("type", "user_agent");
parameters.putString("client_id", mAppId);
} else {
parameters.putString("app_id", mAppId);
// We do not want to add an access token when displaying the auth dialog.
if (isSessionValid()) {
parameters.putString(TOKEN, getAccessToken());
}
}
if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET) != PackageManager.PERMISSION_GRANTED) {
Util.showAlert(context, "Error", "Application requires permission to access the Internet");
} else {
new FbDialog(context, action, parameters, listener).show();
}
}
/**
* Returns whether the current access token is valid
*
* @return boolean - whether this object has an non-expired session token
*/
@Deprecated
public boolean isSessionValid() {
return (getAccessToken() != null)
&& ((getAccessExpires() == 0) || (System.currentTimeMillis() < getAccessExpires()));
}
/**
* Allows the user to set a Session for the Facebook class to use.
* If a Session is set here, then one should not use the authorize, logout,
* or extendAccessToken methods which alter the Session object since that may
* result in undefined behavior. Using those methods after setting the
* session here will result in exceptions being thrown.
*
* @param session the Session object to use, cannot be null
*/
@Deprecated
public void setSession(Session session) {
if (session == null) {
throw new IllegalArgumentException("session cannot be null");
}
synchronized (this.lock) {
this.userSetSession = session;
}
}
private void checkUserSession(String methodName) {
if (userSetSession != null) {
throw new UnsupportedOperationException(
String.format("Cannot call %s after setSession has been called.", methodName));
}
}
/**
* Get the underlying Session object to use with 3.0 api.
*
* @return Session - underlying session
*/
@Deprecated
public final Session getSession() {
while (true) {
String cachedToken = null;
Session oldSession = null;
synchronized (this.lock) {
if (userSetSession != null) {
return userSetSession;
}
if ((session != null) || !sessionInvalidated) {
return session;
}
cachedToken = accessToken;
oldSession = session;
}
if (cachedToken == null) {
return null;
}
// At this point we do not have a valid session, but mAccessToken is
// non-null.
// So we can try building a session based on that.
List<String> permissions;
if (oldSession != null) {
permissions = oldSession.getPermissions();
} else if (pendingAuthorizationPermissions != null) {
permissions = Arrays.asList(pendingAuthorizationPermissions);
} else {
permissions = Collections.<String>emptyList();
}
Session newSession = new Session.Builder(pendingAuthorizationActivity).
setApplicationId(mAppId).
setTokenCachingStrategy(getTokenCache()).
build();
if (newSession.getState() != SessionState.CREATED_TOKEN_LOADED) {
return null;
}
Session.OpenRequest openRequest =
new Session.OpenRequest(pendingAuthorizationActivity).setPermissions(permissions);
openSession(newSession, openRequest, !permissions.isEmpty());
Session invalidatedSession = null;
Session returnSession = null;
synchronized (this.lock) {
if (sessionInvalidated || (session == null)) {
invalidatedSession = session;
returnSession = session = newSession;
sessionInvalidated = false;
}
}
if (invalidatedSession != null) {
invalidatedSession.close();
}
if (returnSession != null) {
return returnSession;
}
// Else token state changed between the synchronized blocks, so
// retry..
}
}
/**
* Retrieve the OAuth 2.0 access token for API access: treat with care.
* Returns null if no session exists.
*
* @return String - access token
*/
@Deprecated
public String getAccessToken() {
Session s = getSession();
if (s != null) {
return s.getAccessToken();
} else {
return null;
}
}
/**
* Retrieve the current session's expiration time (in milliseconds since
* Unix epoch), or 0 if the session doesn't expire or doesn't exist.
*
* @return long - session expiration time
*/
@Deprecated
public long getAccessExpires() {
Session s = getSession();
if (s != null) {
return s.getExpirationDate().getTime();
} else {
return accessExpiresMillisecondsAfterEpoch;
}
}
/**
* Retrieve the last time the token was updated (in milliseconds since
* the Unix epoch), or 0 if the token has not been set.
*
* @return long - timestamp of the last token update.
*/
@Deprecated
public long getLastAccessUpdate() {
return lastAccessUpdateMillisecondsAfterEpoch;
}
/**
* Restore the token, expiration time, and last update time from cached values.
* These should be values obtained from getAccessToken(), getAccessExpires, and
* getLastAccessUpdate() respectively.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param accessToken - access token
* @param accessExpires - access token expiration time
* @param lastAccessUpdate - timestamp of the last token update
*/
@Deprecated
public void setTokenFromCache(String accessToken, long accessExpires, long lastAccessUpdate) {
checkUserSession("setTokenFromCache");
synchronized (this.lock) {
this.accessToken = accessToken;
accessExpiresMillisecondsAfterEpoch = accessExpires;
lastAccessUpdateMillisecondsAfterEpoch = lastAccessUpdate;
}
}
/**
* Set the OAuth 2.0 access token for API access.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param token
* - access token
*/
@Deprecated
public void setAccessToken(String token) {
checkUserSession("setAccessToken");
synchronized (this.lock) {
accessToken = token;
lastAccessUpdateMillisecondsAfterEpoch = System.currentTimeMillis();
sessionInvalidated = true;
}
}
/**
* Set the current session's expiration time (in milliseconds since Unix
* epoch), or 0 if the session doesn't expire.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param timestampInMsec
* - timestamp in milliseconds
*/
@Deprecated
public void setAccessExpires(long timestampInMsec) {
checkUserSession("setAccessExpires");
synchronized (this.lock) {
accessExpiresMillisecondsAfterEpoch = timestampInMsec;
lastAccessUpdateMillisecondsAfterEpoch = System.currentTimeMillis();
sessionInvalidated = true;
}
}
/**
* Set the current session's duration (in seconds since Unix epoch), or "0"
* if session doesn't expire.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param expiresInSecsFromNow
* - duration in seconds (or 0 if the session doesn't expire)
*/
@Deprecated
public void setAccessExpiresIn(String expiresInSecsFromNow) {
checkUserSession("setAccessExpiresIn");
if (expiresInSecsFromNow != null) {
long expires = expiresInSecsFromNow.equals("0") ? 0 : System.currentTimeMillis()
+ Long.parseLong(expiresInSecsFromNow) * 1000L;
setAccessExpires(expires);
}
}
/**
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @return the String representing application ID
*/
@Deprecated
public String getAppId() {
return mAppId;
}
/**
* This method is deprecated. See {@link Facebook} and {@link Session} for more info.
*
* @param appId the String representing the application ID
*/
@Deprecated
public void setAppId(String appId) {
checkUserSession("setAppId");
synchronized (this.lock) {
mAppId = appId;
sessionInvalidated = true;
}
}
private TokenCachingStrategy getTokenCache() {
// Intentionally not volatile/synchronized--it is okay if we race to
// create more than one of these.
if (tokenCache == null) {
tokenCache = new SetterTokenCachingStrategy();
}
return tokenCache;
}
private static String[] stringArray(List<String> list) {
int size = (list != null) ? list.size() : 0;
String[] array = new String[size];
if (list != null) {
for (int i = 0; i < array.length; i++) {
array[i] = list.get(i);
}
}
return array;
}
private static List<String> stringList(String[] array) {
if (array != null) {
return Arrays.asList(array);
} else {
return Collections.emptyList();
}
}
private class SetterTokenCachingStrategy extends TokenCachingStrategy {
@Override
public Bundle load() {
Bundle bundle = new Bundle();
if (accessToken != null) {
TokenCachingStrategy.putToken(bundle, accessToken);
TokenCachingStrategy.putExpirationMilliseconds(bundle, accessExpiresMillisecondsAfterEpoch);
TokenCachingStrategy.putPermissions(bundle, stringList(pendingAuthorizationPermissions));
TokenCachingStrategy.putSource(bundle, AccessTokenSource.WEB_VIEW);
TokenCachingStrategy.putLastRefreshMilliseconds(bundle, lastAccessUpdateMillisecondsAfterEpoch);
}
return bundle;
}
@Override
public void save(Bundle bundle) {
accessToken = TokenCachingStrategy.getToken(bundle);
accessExpiresMillisecondsAfterEpoch = TokenCachingStrategy.getExpirationMilliseconds(bundle);
pendingAuthorizationPermissions = stringArray(TokenCachingStrategy.getPermissions(bundle));
lastAccessUpdateMillisecondsAfterEpoch = TokenCachingStrategy.getLastRefreshMilliseconds(bundle);
}
@Override
public void clear() {
accessToken = null;
}
}
/**
* Get Attribution ID for app install conversion tracking.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Settings} for more info.
*
* @param contentResolver
* @return Attribution ID that will be used for conversion tracking. It will be null only if
* the user has not installed or logged in to the Facebook app.
*/
@Deprecated
public static String getAttributionId(ContentResolver contentResolver) {
return Settings.getAttributionId(contentResolver);
}
/**
* Get the auto install publish setting. If true, an install event will be published during authorize(), unless
* it has occurred previously or the app does not have install attribution enabled on the application's developer
* config page.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Settings} for more info.
*
* @return a Boolean indicating whether installation of the app should be auto-published.
*/
@Deprecated
public boolean getShouldAutoPublishInstall() {
return Settings.getShouldAutoPublishInstall();
}
/**
* Sets whether auto publishing of installs will occur.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Settings} for more info.
*
* @param value a Boolean indicating whether installation of the app should be auto-published.
*/
@Deprecated
public void setShouldAutoPublishInstall(boolean value) {
Settings.setShouldAutoPublishInstall(value);
}
/**
* Manually publish install attribution to the Facebook graph. Internally handles tracking repeat calls to prevent
* multiple installs being published to the graph.
* <p/>
* This method is deprecated. See {@link Facebook} and {@link Settings} for more info.
*
* @param context the current Android context
* @return Always false. Earlier versions of the API returned true if it was no longer necessary to call.
* Apps should ignore this value, but for compatibility we will return false to ensure repeat calls (and the
* underlying code will prevent duplicate network traffic).
*/
@Deprecated
public boolean publishInstall(final Context context) {
Settings.publishInstallAsync(context, mAppId);
return false;
}
/**
* Callback interface for dialog requests.
* <p/>
* THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
* <p/>
* All public members of this class are intentionally deprecated.
* New code should instead use
* {@link com.facebook.widget.WebDialog}
* <p/>
* Adding @Deprecated to this class causes warnings in other deprecated classes
* that reference this one. That is the only reason this entire class is not
* deprecated.
*
* @devDocDeprecated
*/
public static interface DialogListener {
/**
* Called when a dialog completes.
*
* Executed by the thread that initiated the dialog.
*
* @param values
* Key-value string pairs extracted from the response.
*/
public void onComplete(Bundle values);
/**
* Called when a Facebook responds to a dialog with an error.
*
* Executed by the thread that initiated the dialog.
*
*/
public void onFacebookError(FacebookError e);
/**
* Called when a dialog has an error.
*
* Executed by the thread that initiated the dialog.
*
*/
public void onError(DialogError e);
/**
* Called when a dialog is canceled by the user.
*
* Executed by the thread that initiated the dialog.
*
*/
public void onCancel();
}
/**
* Callback interface for service requests.
* <p/>
* THIS CLASS SHOULD BE CONSIDERED DEPRECATED.
* <p/>
* All public members of this class are intentionally deprecated.
* New code should instead use
* {@link Session} to manage session state.
* <p/>
* Adding @Deprecated to this class causes warnings in other deprecated classes
* that reference this one. That is the only reason this entire class is not
* deprecated.
*
* @devDocDeprecated
*/
public static interface ServiceListener {
/**
* Called when a service request completes.
*
* @param values
* Key-value string pairs extracted from the response.
*/
public void onComplete(Bundle values);
/**
* Called when a Facebook server responds to the request with an error.
*/
public void onFacebookError(FacebookError e);
/**
* Called when a Facebook Service responds to the request with an error.
*/
public void onError(Error e);
}
@Deprecated
public static final String FB_APP_SIGNATURE =
"30820268308201d102044a9c4610300d06092a864886f70d0101040500307a310"
+ "b3009060355040613025553310b30090603550408130243413112301006035504"
+ "07130950616c6f20416c746f31183016060355040a130f46616365626f6f6b204"
+ "d6f62696c653111300f060355040b130846616365626f6f6b311d301b06035504"
+ "03131446616365626f6f6b20436f72706f726174696f6e3020170d30393038333"
+ "13231353231365a180f32303530303932353231353231365a307a310b30090603"
+ "55040613025553310b30090603550408130243413112301006035504071309506"
+ "16c6f20416c746f31183016060355040a130f46616365626f6f6b204d6f62696c"
+ "653111300f060355040b130846616365626f6f6b311d301b06035504031314466"
+ "16365626f6f6b20436f72706f726174696f6e30819f300d06092a864886f70d01"
+ "0101050003818d0030818902818100c207d51df8eb8c97d93ba0c8c1002c928fa"
+ "b00dc1b42fca5e66e99cc3023ed2d214d822bc59e8e35ddcf5f44c7ae8ade50d7"
+ "e0c434f500e6c131f4a2834f987fc46406115de2018ebbb0d5a3c261bd97581cc"
+ "fef76afc7135a6d59e8855ecd7eacc8f8737e794c60a761c536b72b11fac8e603"
+ "f5da1a2d54aa103b8a13c0dbc10203010001300d06092a864886f70d010104050"
+ "0038181005ee9be8bcbb250648d3b741290a82a1c9dc2e76a0af2f2228f1d9f9c"
+ "4007529c446a70175c5a900d5141812866db46be6559e2141616483998211f4a6"
+ "73149fb2232a10d247663b26a9031e15f84bc1c74d141ff98a02d76f85b2c8ab2"
+ "571b6469b232d8e768a7f7ca04f7abe4a775615916c07940656b58717457b42bd"
+ "928a2";
}