move due to task refs #327
This commit is contained in:
7
FacebookLib/.classpath
Normal file
7
FacebookLib/.classpath
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<classpath>
|
||||||
|
<classpathentry kind="src" path="src"/>
|
||||||
|
<classpathentry kind="src" path="gen"/>
|
||||||
|
<classpathentry kind="con" path="com.android.ide.eclipse.adt.ANDROID_FRAMEWORK"/>
|
||||||
|
<classpathentry kind="output" path="bin"/>
|
||||||
|
</classpath>
|
||||||
33
FacebookLib/.project
Normal file
33
FacebookLib/.project
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<projectDescription>
|
||||||
|
<name>FacebookLib</name>
|
||||||
|
<comment></comment>
|
||||||
|
<projects>
|
||||||
|
</projects>
|
||||||
|
<buildSpec>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ResourceManagerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.PreCompilerBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>org.eclipse.jdt.core.javabuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
<buildCommand>
|
||||||
|
<name>com.android.ide.eclipse.adt.ApkBuilder</name>
|
||||||
|
<arguments>
|
||||||
|
</arguments>
|
||||||
|
</buildCommand>
|
||||||
|
</buildSpec>
|
||||||
|
<natures>
|
||||||
|
<nature>com.android.ide.eclipse.adt.AndroidNature</nature>
|
||||||
|
<nature>org.eclipse.jdt.core.javanature</nature>
|
||||||
|
</natures>
|
||||||
|
</projectDescription>
|
||||||
21
FacebookLib/AndroidManifest.xml
Normal file
21
FacebookLib/AndroidManifest.xml
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
* Copyright (C) 2010 Facebook Inc.
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="com.facebook.android">
|
||||||
|
<application/>
|
||||||
|
</manifest>
|
||||||
12
FacebookLib/default.properties
Normal file
12
FacebookLib/default.properties
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
# This file is automatically generated by Android Tools.
|
||||||
|
# Do not modify this file -- YOUR CHANGES WILL BE ERASED!
|
||||||
|
#
|
||||||
|
# This file must be checked in Version Control Systems.
|
||||||
|
#
|
||||||
|
# To customize properties used by the Ant build system use,
|
||||||
|
# "build.properties", and override values to adapt the script to your
|
||||||
|
# project structure.
|
||||||
|
|
||||||
|
android.library=true
|
||||||
|
# Project target.
|
||||||
|
target=Google Inc.:Google APIs:9
|
||||||
16
FacebookLib/gen/com/facebook/android/R.java
Normal file
16
FacebookLib/gen/com/facebook/android/R.java
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
/* AUTO-GENERATED FILE. DO NOT MODIFY.
|
||||||
|
*
|
||||||
|
* This class was automatically generated by the
|
||||||
|
* aapt tool from the resource data it found. It
|
||||||
|
* should not be modified by hand.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.facebook.android;
|
||||||
|
|
||||||
|
public final class R {
|
||||||
|
public static final class attr {
|
||||||
|
}
|
||||||
|
public static final class drawable {
|
||||||
|
public static final int facebook_icon=0x7f020000;
|
||||||
|
}
|
||||||
|
}
|
||||||
BIN
FacebookLib/res/drawable-hdpi/facebook_icon.png
Normal file
BIN
FacebookLib/res/drawable-hdpi/facebook_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 255 B |
BIN
FacebookLib/res/drawable-ldpi/facebook_icon.png
Normal file
BIN
FacebookLib/res/drawable-ldpi/facebook_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 253 B |
BIN
FacebookLib/res/drawable/facebook_icon.png
Normal file
BIN
FacebookLib/res/drawable/facebook_icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 226 B |
316
FacebookLib/src/com/facebook/android/AsyncFacebookRunner.java
Normal file
316
FacebookLib/src/com/facebook/android/AsyncFacebookRunner.java
Normal file
@@ -0,0 +1,316 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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 java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A sample implementation of asynchronous API requests. This class provides
|
||||||
|
* the ability to execute API methods and have the call return immediately,
|
||||||
|
* without blocking the calling thread. This is necessary when accessing the
|
||||||
|
* API in the UI thread, for instance. The request response is returned to
|
||||||
|
* the caller via a callback interface, which the developer must implement.
|
||||||
|
*
|
||||||
|
* This sample implementation simply spawns a new thread for each request,
|
||||||
|
* and makes the API call immediately. This may work in many applications,
|
||||||
|
* but more sophisticated users may re-implement this behavior using a thread
|
||||||
|
* pool, a network thread, a request queue, or other mechanism. Advanced
|
||||||
|
* functionality could be built, such as rate-limiting of requests, as per
|
||||||
|
* a specific application's needs.
|
||||||
|
*
|
||||||
|
* @see RequestListener
|
||||||
|
* The callback interface.
|
||||||
|
*
|
||||||
|
* @author Jim Brusstar (jimbru@fb.com),
|
||||||
|
* Yariv Sadan (yariv@fb.com),
|
||||||
|
* Luke Shepard (lshepard@fb.com)
|
||||||
|
*/
|
||||||
|
public class AsyncFacebookRunner {
|
||||||
|
|
||||||
|
Facebook fb;
|
||||||
|
|
||||||
|
public AsyncFacebookRunner(Facebook fb) {
|
||||||
|
this.fb = fb;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate the current user session by removing the access token in
|
||||||
|
* memory, clearing the browser cookies, and calling auth.expireSession
|
||||||
|
* through the API. The application will be notified when logout is
|
||||||
|
* complete via the callback interface.
|
||||||
|
*
|
||||||
|
* Note that this method is asynchronous and the callback will be invoked
|
||||||
|
* in a background thread; operations that affect the UI will need to be
|
||||||
|
* posted to the UI thread or an appropriate handler.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* @param listener
|
||||||
|
* Callback interface to notify the application when the request
|
||||||
|
* has completed.
|
||||||
|
* @param state
|
||||||
|
* An arbitrary object used to identify the request when it
|
||||||
|
* returns to the callback. This has no effect on the request
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
public void logout(final Context context,
|
||||||
|
final RequestListener listener,
|
||||||
|
final Object state) {
|
||||||
|
new Thread() {
|
||||||
|
@Override public void run() {
|
||||||
|
try {
|
||||||
|
String response = fb.logout(context);
|
||||||
|
if (response.length() == 0 || response.equals("false")){
|
||||||
|
listener.onFacebookError(new FacebookError(
|
||||||
|
"auth.expireSession failed"), state);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
listener.onComplete(response, state);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
listener.onFileNotFoundException(e, state);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
listener.onMalformedURLException(e, state);
|
||||||
|
} catch (IOException e) {
|
||||||
|
listener.onIOException(e, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void logout(final Context context, final RequestListener listener) {
|
||||||
|
logout(context, listener, /* state */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/reference/rest/
|
||||||
|
*
|
||||||
|
* Note that this method is asynchronous and the callback will be invoked
|
||||||
|
* in a background thread; operations that affect the UI will need to be
|
||||||
|
* posted to the UI thread or an appropriate handler.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code>
|
||||||
|
* Bundle parameters = new Bundle();
|
||||||
|
* parameters.putString("method", "auth.expireSession", new Listener());
|
||||||
|
* String response = request(parameters);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @param parameters
|
||||||
|
* Key-value pairs of parameters to the request. Refer to the
|
||||||
|
* documentation: one of the parameters must be "method".
|
||||||
|
* @param listener
|
||||||
|
* Callback interface to notify the application when the request
|
||||||
|
* has completed.
|
||||||
|
* @param state
|
||||||
|
* An arbitrary object used to identify the request when it
|
||||||
|
* returns to the callback. This has no effect on the request
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
public void request(Bundle parameters,
|
||||||
|
RequestListener listener,
|
||||||
|
final Object state) {
|
||||||
|
request(null, parameters, "GET", listener, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void request(Bundle parameters, RequestListener listener) {
|
||||||
|
request(null, parameters, "GET", listener, /* state */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request to the Facebook Graph API without any parameters.
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/api
|
||||||
|
*
|
||||||
|
* Note that this method is asynchronous and the callback will be invoked
|
||||||
|
* in a background thread; operations that affect the UI will need to be
|
||||||
|
* posted to the UI thread or an appropriate handler.
|
||||||
|
*
|
||||||
|
* @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 listener
|
||||||
|
* Callback interface to notify the application when the request
|
||||||
|
* has completed.
|
||||||
|
* @param state
|
||||||
|
* An arbitrary object used to identify the request when it
|
||||||
|
* returns to the callback. This has no effect on the request
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
public void request(String graphPath,
|
||||||
|
RequestListener listener,
|
||||||
|
final Object state) {
|
||||||
|
request(graphPath, new Bundle(), "GET", listener, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void request(String graphPath, RequestListener listener) {
|
||||||
|
request(graphPath, new Bundle(), "GET", listener, /* state */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request to the Facebook Graph API with the given string parameters
|
||||||
|
* using an HTTP GET (default method).
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/api
|
||||||
|
*
|
||||||
|
* Note that this method is asynchronous and the callback will be invoked
|
||||||
|
* in a background thread; operations that affect the UI will need to be
|
||||||
|
* posted to the UI thread or an appropriate handler.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* @param listener
|
||||||
|
* Callback interface to notify the application when the request
|
||||||
|
* has completed.
|
||||||
|
* @param state
|
||||||
|
* An arbitrary object used to identify the request when it
|
||||||
|
* returns to the callback. This has no effect on the request
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
public void request(String graphPath,
|
||||||
|
Bundle parameters,
|
||||||
|
RequestListener listener,
|
||||||
|
final Object state) {
|
||||||
|
request(graphPath, parameters, "GET", listener, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void request(String graphPath,
|
||||||
|
Bundle parameters,
|
||||||
|
RequestListener listener) {
|
||||||
|
request(graphPath, parameters, "GET", listener, /* state */ null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/api
|
||||||
|
*
|
||||||
|
* Note that this method is asynchronous and the callback will be invoked
|
||||||
|
* in a background thread; operations that affect the UI will need to be
|
||||||
|
* posted to the UI thread or an appropriate handler.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
* @param httpMethod
|
||||||
|
* http verb, e.g. "POST", "DELETE"
|
||||||
|
* @param listener
|
||||||
|
* Callback interface to notify the application when the request
|
||||||
|
* has completed.
|
||||||
|
* @param state
|
||||||
|
* An arbitrary object used to identify the request when it
|
||||||
|
* returns to the callback. This has no effect on the request
|
||||||
|
* itself.
|
||||||
|
*/
|
||||||
|
public void request(final String graphPath,
|
||||||
|
final Bundle parameters,
|
||||||
|
final String httpMethod,
|
||||||
|
final RequestListener listener,
|
||||||
|
final Object state) {
|
||||||
|
new Thread() {
|
||||||
|
@Override public void run() {
|
||||||
|
try {
|
||||||
|
String resp = fb.request(graphPath, parameters, httpMethod);
|
||||||
|
listener.onComplete(resp, state);
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
listener.onFileNotFoundException(e, state);
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
listener.onMalformedURLException(e, state);
|
||||||
|
} catch (IOException e) {
|
||||||
|
listener.onIOException(e, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface for API requests.
|
||||||
|
*
|
||||||
|
* Each method includes a 'state' parameter that identifies the calling
|
||||||
|
* request. It will be set to the value passed when originally calling the
|
||||||
|
* request method, or null if none was passed.
|
||||||
|
*/
|
||||||
|
public static interface RequestListener {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a request completes with the given response.
|
||||||
|
*
|
||||||
|
* Executed by a background thread: do not update the UI in this method.
|
||||||
|
*/
|
||||||
|
public void onComplete(String response, Object state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a request has a network or request error.
|
||||||
|
*
|
||||||
|
* Executed by a background thread: do not update the UI in this method.
|
||||||
|
*/
|
||||||
|
public void onIOException(IOException e, Object state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when a request fails because the requested resource is
|
||||||
|
* invalid or does not exist.
|
||||||
|
*
|
||||||
|
* Executed by a background thread: do not update the UI in this method.
|
||||||
|
*/
|
||||||
|
public void onFileNotFoundException(FileNotFoundException e,
|
||||||
|
Object state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called if an invalid graph path is provided (which may result in a
|
||||||
|
* malformed URL).
|
||||||
|
*
|
||||||
|
* Executed by a background thread: do not update the UI in this method.
|
||||||
|
*/
|
||||||
|
public void onMalformedURLException(MalformedURLException e,
|
||||||
|
Object state);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Called when the server-side Facebook method fails.
|
||||||
|
*
|
||||||
|
* Executed by a background thread: do not update the UI in this method.
|
||||||
|
*/
|
||||||
|
public void onFacebookError(FacebookError e, Object state);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
51
FacebookLib/src/com/facebook/android/DialogError.java
Normal file
51
FacebookLib/src/com/facebook/android/DialogError.java
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulation of Dialog Error.
|
||||||
|
*
|
||||||
|
* @author ssoneff@facebook.com
|
||||||
|
*/
|
||||||
|
public class DialogError extends Throwable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The ErrorCode received by the WebView: see
|
||||||
|
* http://developer.android.com/reference/android/webkit/WebViewClient.html
|
||||||
|
*/
|
||||||
|
private int mErrorCode;
|
||||||
|
|
||||||
|
/** The URL that the dialog was trying to load */
|
||||||
|
private String mFailingUrl;
|
||||||
|
|
||||||
|
public DialogError(String message, int errorCode, String failingUrl) {
|
||||||
|
super(message);
|
||||||
|
mErrorCode = errorCode;
|
||||||
|
mFailingUrl = failingUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
int getErrorCode() {
|
||||||
|
return mErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
String getFailingUrl() {
|
||||||
|
return mFailingUrl;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
757
FacebookLib/src/com/facebook/android/Facebook.java
Normal file
757
FacebookLib/src/com/facebook/android/Facebook.java
Normal file
@@ -0,0 +1,757 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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 java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
|
||||||
|
import android.Manifest;
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.content.pm.PackageManager;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.CookieSyncManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Main Facebook object for interacting with the Facebook developer API.
|
||||||
|
* Provides methods to log in and log out a user, make requests using the REST
|
||||||
|
* and Graph APIs, and start user interface interactions with the API (such as
|
||||||
|
* pop-ups promoting for credentials, permissions, stream posts, etc.)
|
||||||
|
*
|
||||||
|
* @author Jim Brusstar (jimbru@facebook.com),
|
||||||
|
* Yariv Sadan (yariv@facebook.com),
|
||||||
|
* Luke Shepard (lshepard@facebook.com)
|
||||||
|
*/
|
||||||
|
public class Facebook {
|
||||||
|
|
||||||
|
// Strings used in the authorization flow
|
||||||
|
public static final String REDIRECT_URI = "fbconnect://success";
|
||||||
|
public static final String CANCEL_URI = "fbconnect://cancel";
|
||||||
|
public static final String TOKEN = "access_token";
|
||||||
|
public static final String EXPIRES = "expires_in";
|
||||||
|
public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled";
|
||||||
|
|
||||||
|
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
|
||||||
|
protected static String DIALOG_BASE_URL = "https://m.facebook.com/dialog/";
|
||||||
|
protected static String GRAPH_BASE_URL = "https://graph.facebook.com/";
|
||||||
|
protected static String RESTSERVER_URL = "https://api.facebook.com/restserver.php";
|
||||||
|
|
||||||
|
private String mAccessToken = null;
|
||||||
|
private long mAccessExpires = 0;
|
||||||
|
private String mAppId;
|
||||||
|
|
||||||
|
private Activity mAuthActivity;
|
||||||
|
private String[] mAuthPermissions;
|
||||||
|
private int mAuthActivityCode;
|
||||||
|
private DialogListener mAuthDialogListener;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Constructor for Facebook object.
|
||||||
|
*
|
||||||
|
* @param appId
|
||||||
|
* Your Facebook application ID. Found at
|
||||||
|
* www.facebook.com/developers/apps.php.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* See authorize() below for @params.
|
||||||
|
*/
|
||||||
|
public void authorize(Context context, final DialogListener listener) {
|
||||||
|
authorize(context, new String[] {}, DEFAULT_AUTH_ACTIVITY_CODE,
|
||||||
|
listener);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Authorize method that grants custom permissions.
|
||||||
|
*
|
||||||
|
* See authorize() below for @params.
|
||||||
|
*/
|
||||||
|
public void authorize(Context context, String[] permissions,
|
||||||
|
final DialogListener listener) {
|
||||||
|
authorize(context, permissions, DEFAULT_AUTH_ACTIVITY_CODE, 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 applicationId
|
||||||
|
* The Facebook application identifier e.g. "350685531728"
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
public void authorize(Context context, String[] permissions,
|
||||||
|
int activityCode, final DialogListener listener) {
|
||||||
|
|
||||||
|
// boolean singleSignOnStarted = false;
|
||||||
|
|
||||||
|
mAuthDialogListener = listener;
|
||||||
|
|
||||||
|
// // Prefer single sign-on, where available.
|
||||||
|
// if (activityCode >= 0) {
|
||||||
|
// singleSignOnStarted = startSingleSignOn(activity, mAppId,
|
||||||
|
// permissions, activityCode);
|
||||||
|
// }
|
||||||
|
// Otherwise fall back to traditional dialog.
|
||||||
|
// if (!singleSignOnStarted) {
|
||||||
|
startDialogAuth(context, permissions);
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * Internal method to handle single sign-on backend for authorize().
|
||||||
|
// *
|
||||||
|
// * @param activity
|
||||||
|
// * The Android Activity that will parent the ProxyAuth Activity.
|
||||||
|
// * @param applicationId
|
||||||
|
// * The Facebook application identifier.
|
||||||
|
// * @param permissions
|
||||||
|
// * A list of permissions required for this application. If you do
|
||||||
|
// * not require any permissions, pass an empty String array.
|
||||||
|
// * @param activityCode
|
||||||
|
// * Activity code to uniquely identify the result Intent in the
|
||||||
|
// * callback.
|
||||||
|
// */
|
||||||
|
// private boolean startSingleSignOn(Activity activity, String applicationId,
|
||||||
|
// String[] permissions, int activityCode) {
|
||||||
|
// boolean didSucceed = true;
|
||||||
|
// Intent intent = new Intent();
|
||||||
|
//
|
||||||
|
// intent.setClassName("com.facebook.katana",
|
||||||
|
// "com.facebook.katana.ProxyAuth");
|
||||||
|
// intent.putExtra("client_id", applicationId);
|
||||||
|
// if (permissions.length > 0) {
|
||||||
|
// intent.putExtra("scope", TextUtils.join(",", permissions));
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // Verify that the application whose package name is
|
||||||
|
// // com.facebook.katana.ProxyAuth
|
||||||
|
// // has the expected FB app signature.
|
||||||
|
// if (!validateAppSignatureForIntent(activity, intent)) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// mAuthActivity = activity;
|
||||||
|
// mAuthPermissions = permissions;
|
||||||
|
// mAuthActivityCode = activityCode;
|
||||||
|
// try {
|
||||||
|
// activity.startActivityForResult(intent, activityCode);
|
||||||
|
// } catch (ActivityNotFoundException e) {
|
||||||
|
// didSucceed = false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return didSucceed;
|
||||||
|
// }
|
||||||
|
|
||||||
|
// /**
|
||||||
|
// * 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 activity
|
||||||
|
// * @param intent
|
||||||
|
// * @param validSignature
|
||||||
|
// * @return true if the app's signature matches the expected signature.
|
||||||
|
// */
|
||||||
|
// private boolean validateAppSignatureForIntent(Activity activity,
|
||||||
|
// Intent intent) {
|
||||||
|
//
|
||||||
|
// ResolveInfo resolveInfo =
|
||||||
|
// activity.getPackageManager().resolveActivity(intent, 0);
|
||||||
|
// if (resolveInfo == null) {
|
||||||
|
// return false;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// String packageName = resolveInfo.activityInfo.packageName;
|
||||||
|
// PackageInfo packageInfo;
|
||||||
|
// try {
|
||||||
|
// packageInfo = activity.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;
|
||||||
|
// }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Internal method to handle dialog-based authentication backend for
|
||||||
|
* authorize().
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* The Android Activity that will parent the auth dialog.
|
||||||
|
* @param applicationId
|
||||||
|
* The Facebook application identifier.
|
||||||
|
* @param permissions
|
||||||
|
* A list of permissions required for this application. If you do
|
||||||
|
* not require any permissions, pass an empty String array.
|
||||||
|
*/
|
||||||
|
private void startDialogAuth(Context context, String[] permissions) {
|
||||||
|
Bundle params = new Bundle();
|
||||||
|
if (permissions.length > 0) {
|
||||||
|
params.putString("scope", TextUtils.join(",", permissions));
|
||||||
|
}
|
||||||
|
CookieSyncManager.createInstance(context);
|
||||||
|
dialog(context, LOGIN, params, new DialogListener() {
|
||||||
|
|
||||||
|
public void onComplete(Bundle values, Dialog dialog) {
|
||||||
|
// ensure any cookies set by the dialog are saved
|
||||||
|
CookieSyncManager.getInstance().sync();
|
||||||
|
setAccessToken(values.getString(TOKEN));
|
||||||
|
setAccessExpiresIn(values.getString(EXPIRES));
|
||||||
|
if (isSessionValid()) {
|
||||||
|
Log.d("Facebook-authorize", "Login Success! access_token="
|
||||||
|
+ getAccessToken() + " expires="
|
||||||
|
+ getAccessExpires());
|
||||||
|
mAuthDialogListener.onComplete(values, dialog);
|
||||||
|
} else {
|
||||||
|
mAuthDialogListener.onFacebookError(new FacebookError(
|
||||||
|
"Failed to receive access token."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onError(DialogError error) {
|
||||||
|
Log.d("Facebook-authorize", "Login failed: " + error);
|
||||||
|
mAuthDialogListener.onError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onFacebookError(FacebookError error) {
|
||||||
|
Log.d("Facebook-authorize", "Login failed: " + error);
|
||||||
|
mAuthDialogListener.onFacebookError(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void onCancel() {
|
||||||
|
Log.d("Facebook-authorize", "Login canceled");
|
||||||
|
mAuthDialogListener.onCancel();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* IMPORTANT: This method must be invoked at the top of the calling
|
||||||
|
* activity's onActivityResult() function or Facebook authentication will
|
||||||
|
* not function properly!
|
||||||
|
*
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* For more information, see
|
||||||
|
* http://developer.android.com/reference/android/app/
|
||||||
|
* Activity.html#onActivityResult(int, int, android.content.Intent)
|
||||||
|
*/
|
||||||
|
public void authorizeCallback(int requestCode, int resultCode, Intent data) {
|
||||||
|
if (requestCode == mAuthActivityCode) {
|
||||||
|
|
||||||
|
// Successfully redirected.
|
||||||
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
|
|
||||||
|
// Check OAuth 2.0/2.10 error code.
|
||||||
|
String error = data.getStringExtra("error");
|
||||||
|
if (error == null) {
|
||||||
|
error = data.getStringExtra("error_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
// A Facebook error occurred.
|
||||||
|
if (error != null) {
|
||||||
|
if (error.equals(SINGLE_SIGN_ON_DISABLED)
|
||||||
|
|| error.equals("AndroidAuthKillSwitchException")) {
|
||||||
|
Log.d("Facebook-authorize", "Hosted auth currently "
|
||||||
|
+ "disabled. Retrying dialog auth...");
|
||||||
|
startDialogAuth(mAuthActivity, mAuthPermissions);
|
||||||
|
} else if (error.equals("access_denied")
|
||||||
|
|| error.equals("OAuthAccessDeniedException")) {
|
||||||
|
Log.d("Facebook-authorize", "Login canceled by user.");
|
||||||
|
mAuthDialogListener.onCancel();
|
||||||
|
} else {
|
||||||
|
Log.d("Facebook-authorize", "Login failed: " + error);
|
||||||
|
mAuthDialogListener.onFacebookError(
|
||||||
|
new FacebookError(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
// No errors.
|
||||||
|
} else {
|
||||||
|
setAccessToken(data.getStringExtra(TOKEN));
|
||||||
|
setAccessExpiresIn(data.getStringExtra(EXPIRES));
|
||||||
|
if (isSessionValid()) {
|
||||||
|
Log.d("Facebook-authorize",
|
||||||
|
"Login Success! access_token="
|
||||||
|
+ getAccessToken() + " expires="
|
||||||
|
+ getAccessExpires());
|
||||||
|
mAuthDialogListener.onComplete(data.getExtras(), null);
|
||||||
|
} else {
|
||||||
|
mAuthDialogListener.onFacebookError(new FacebookError(
|
||||||
|
"Failed to receive access token."));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// An error occurred before we could be redirected.
|
||||||
|
} else if (resultCode == Activity.RESULT_CANCELED) {
|
||||||
|
|
||||||
|
// An Android error occured.
|
||||||
|
if (data != null) {
|
||||||
|
Log.d("Facebook-authorize",
|
||||||
|
"Login failed: " + data.getStringExtra("error"));
|
||||||
|
mAuthDialogListener.onError(
|
||||||
|
new DialogError(
|
||||||
|
data.getStringExtra("error"),
|
||||||
|
data.getIntExtra("error_code", -1),
|
||||||
|
data.getStringExtra("failing_url")));
|
||||||
|
|
||||||
|
// User pressed the 'back' button.
|
||||||
|
} else {
|
||||||
|
Log.d("Facebook-authorize", "Login canceled by user.");
|
||||||
|
mAuthDialogListener.onCancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invalidate the current user session by removing the access token in
|
||||||
|
* memory, clearing the browser cookie, and calling auth.expireSession
|
||||||
|
* through the API.
|
||||||
|
*
|
||||||
|
* Note that this method blocks waiting for a network response, so do not
|
||||||
|
* call it in a UI thread.
|
||||||
|
*
|
||||||
|
* @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)
|
||||||
|
*/
|
||||||
|
public String logout(Context context)
|
||||||
|
throws MalformedURLException, IOException {
|
||||||
|
Util.clearCookies(context);
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putString("method", "auth.expireSession");
|
||||||
|
String response = request(b);
|
||||||
|
setAccessToken(null);
|
||||||
|
setAccessExpires(0);
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/reference/rest/
|
||||||
|
*
|
||||||
|
* Note that this method blocks waiting for a network response, so do not
|
||||||
|
* call it in a UI thread.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* <code>
|
||||||
|
* Bundle parameters = new Bundle();
|
||||||
|
* parameters.putString("method", "auth.expireSession");
|
||||||
|
* String response = request(parameters);
|
||||||
|
* </code>
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
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 request(null, parameters, "GET");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request to the Facebook Graph API without any parameters.
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/api
|
||||||
|
*
|
||||||
|
* Note that this method blocks waiting for a network response, so do not
|
||||||
|
* call it in a UI thread.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
public String request(String graphPath)
|
||||||
|
throws MalformedURLException, IOException {
|
||||||
|
return request(graphPath, new Bundle(), "GET");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make a request to the Facebook Graph API with the given string parameters
|
||||||
|
* using an HTTP GET (default method).
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/api
|
||||||
|
*
|
||||||
|
* Note that this method blocks waiting for a network response, so do not
|
||||||
|
* call it in a UI thread.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
public String request(String graphPath, Bundle parameters)
|
||||||
|
throws MalformedURLException, IOException {
|
||||||
|
return request(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.
|
||||||
|
*
|
||||||
|
* See http://developers.facebook.com/docs/api
|
||||||
|
*
|
||||||
|
* Note that this method blocks waiting for a network response, so do not
|
||||||
|
* call it in a UI thread.
|
||||||
|
*
|
||||||
|
* @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
|
||||||
|
*/
|
||||||
|
public String request(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.
|
||||||
|
*
|
||||||
|
* Note that this method is asynchronous and the callback will be invoked in
|
||||||
|
* the original calling thread (not in a background thread).
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
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.
|
||||||
|
*
|
||||||
|
* Note that this method is asynchronous and the callback will be invoked in
|
||||||
|
* the original calling thread (not in a background thread).
|
||||||
|
*
|
||||||
|
* @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.
|
||||||
|
*/
|
||||||
|
public FbDialog dialog(Context context, String action, Bundle parameters, final DialogListener listener) {
|
||||||
|
|
||||||
|
String endpoint = DIALOG_BASE_URL + action;
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isSessionValid()) {
|
||||||
|
parameters.putString(TOKEN, getAccessToken());
|
||||||
|
}
|
||||||
|
String url = endpoint + "?" + Util.encodeUrl(parameters);
|
||||||
|
if (context.checkCallingOrSelfPermission(Manifest.permission.INTERNET)
|
||||||
|
!= PackageManager.PERMISSION_GRANTED) {
|
||||||
|
Util.showAlert(context, "Error",
|
||||||
|
"Application requires permission to access the Internet");
|
||||||
|
} else {
|
||||||
|
FbDialog fbDialog = new FbDialog(context, url, listener);
|
||||||
|
fbDialog.show();
|
||||||
|
return fbDialog;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return boolean - whether this object has an non-expired session token
|
||||||
|
*/
|
||||||
|
public boolean isSessionValid() {
|
||||||
|
return (getAccessToken() != null) &&
|
||||||
|
((getAccessExpires() == 0) ||
|
||||||
|
(System.currentTimeMillis() < getAccessExpires()));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Retrieve the OAuth 2.0 access token for API access: treat with care.
|
||||||
|
* Returns null if no session exists.
|
||||||
|
*
|
||||||
|
* @return String - access token
|
||||||
|
*/
|
||||||
|
public String getAccessToken() {
|
||||||
|
return mAccessToken;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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
|
||||||
|
*/
|
||||||
|
public long getAccessExpires() {
|
||||||
|
return mAccessExpires;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the OAuth 2.0 access token for API access.
|
||||||
|
*
|
||||||
|
* @param token
|
||||||
|
* - access token
|
||||||
|
*/
|
||||||
|
public void setAccessToken(String token) {
|
||||||
|
mAccessToken = token;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current session's expiration time (in milliseconds since Unix
|
||||||
|
* epoch), or 0 if the session doesn't expire.
|
||||||
|
*
|
||||||
|
* @param time
|
||||||
|
* - timestamp in milliseconds
|
||||||
|
*/
|
||||||
|
public void setAccessExpires(long time) {
|
||||||
|
mAccessExpires = time;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Set the current session's duration (in seconds since Unix epoch).
|
||||||
|
*
|
||||||
|
* @param expiresIn
|
||||||
|
* - duration in seconds
|
||||||
|
*/
|
||||||
|
public void setAccessExpiresIn(String expiresIn) {
|
||||||
|
if (expiresIn != null && !expiresIn.equals("0")) {
|
||||||
|
setAccessExpires(System.currentTimeMillis()
|
||||||
|
+ Integer.parseInt(expiresIn) * 1000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAppId() {
|
||||||
|
return mAppId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setAppId(String appId) {
|
||||||
|
mAppId = appId;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback interface for dialog requests.
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
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, Dialog dialog);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// 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";
|
||||||
|
//
|
||||||
|
}
|
||||||
50
FacebookLib/src/com/facebook/android/FacebookError.java
Normal file
50
FacebookLib/src/com/facebook/android/FacebookError.java
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Encapsulation of a Facebook Error: a Facebook request that could not be
|
||||||
|
* fulfilled.
|
||||||
|
*
|
||||||
|
* @author ssoneff@facebook.com
|
||||||
|
*/
|
||||||
|
public class FacebookError extends Throwable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
private int mErrorCode = 0;
|
||||||
|
private String mErrorType;
|
||||||
|
|
||||||
|
public FacebookError(String message) {
|
||||||
|
super(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public FacebookError(String message, String type, int code) {
|
||||||
|
super(message);
|
||||||
|
mErrorType = type;
|
||||||
|
mErrorCode = code;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getErrorCode() {
|
||||||
|
return mErrorCode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getErrorType() {
|
||||||
|
return mErrorType;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
179
FacebookLib/src/com/facebook/android/FbDialog.java
Normal file
179
FacebookLib/src/com/facebook/android/FbDialog.java
Normal file
@@ -0,0 +1,179 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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.app.Dialog;
|
||||||
|
import android.app.ProgressDialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.Color;
|
||||||
|
import android.graphics.Typeface;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.view.Display;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.webkit.WebView;
|
||||||
|
import android.webkit.WebViewClient;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.facebook.android.Facebook.DialogListener;
|
||||||
|
|
||||||
|
public class FbDialog extends Dialog {
|
||||||
|
|
||||||
|
static final int FB_BLUE = 0xFF6D84B4;
|
||||||
|
static final float[] DIMENSIONS_LANDSCAPE = {460, 260};
|
||||||
|
static final float[] DIMENSIONS_PORTRAIT = {280, 420};
|
||||||
|
static final FrameLayout.LayoutParams FILL =
|
||||||
|
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
|
||||||
|
ViewGroup.LayoutParams.FILL_PARENT);
|
||||||
|
static final int MARGIN = 4;
|
||||||
|
static final int PADDING = 2;
|
||||||
|
static final String DISPLAY_STRING = "touch";
|
||||||
|
static final String FB_ICON = "icon.png";
|
||||||
|
|
||||||
|
private String mUrl;
|
||||||
|
private DialogListener mListener;
|
||||||
|
private ProgressDialog mSpinner;
|
||||||
|
private WebView mWebView;
|
||||||
|
private LinearLayout mContent;
|
||||||
|
private TextView mTitle;
|
||||||
|
|
||||||
|
public FbDialog(Context context, String url, DialogListener listener) {
|
||||||
|
super(context);
|
||||||
|
mUrl = url;
|
||||||
|
mListener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onCreate(Bundle savedInstanceState) {
|
||||||
|
super.onCreate(savedInstanceState);
|
||||||
|
mSpinner = new ProgressDialog(getContext());
|
||||||
|
mSpinner.requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
mSpinner.setMessage("Loading...");
|
||||||
|
|
||||||
|
mContent = new LinearLayout(getContext());
|
||||||
|
mContent.setOrientation(LinearLayout.VERTICAL);
|
||||||
|
setUpTitle();
|
||||||
|
setUpWebView();
|
||||||
|
Display display = getWindow().getWindowManager().getDefaultDisplay();
|
||||||
|
final float scale = getContext().getResources().getDisplayMetrics().density;
|
||||||
|
float[] dimensions =
|
||||||
|
(display.getWidth() < display.getHeight())
|
||||||
|
? DIMENSIONS_PORTRAIT : DIMENSIONS_LANDSCAPE;
|
||||||
|
addContentView(mContent, new FrameLayout.LayoutParams(
|
||||||
|
(int) (dimensions[0] * scale + 0.5f),
|
||||||
|
(int) (dimensions[1] * scale + 0.5f)));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpTitle() {
|
||||||
|
requestWindowFeature(Window.FEATURE_NO_TITLE);
|
||||||
|
Drawable icon = getContext().getResources().getDrawable(
|
||||||
|
R.drawable.facebook_icon);
|
||||||
|
mTitle = new TextView(getContext());
|
||||||
|
mTitle.setText("Facebook");
|
||||||
|
mTitle.setTextColor(Color.WHITE);
|
||||||
|
mTitle.setTypeface(Typeface.DEFAULT_BOLD);
|
||||||
|
mTitle.setBackgroundColor(FB_BLUE);
|
||||||
|
mTitle.setPadding(MARGIN + PADDING, MARGIN, MARGIN, MARGIN);
|
||||||
|
mTitle.setCompoundDrawablePadding(MARGIN + PADDING);
|
||||||
|
mTitle.setCompoundDrawablesWithIntrinsicBounds(
|
||||||
|
icon, null, null, null);
|
||||||
|
mContent.addView(mTitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpWebView() {
|
||||||
|
mWebView = new WebView(getContext());
|
||||||
|
mWebView.setVerticalScrollBarEnabled(false);
|
||||||
|
mWebView.setHorizontalScrollBarEnabled(false);
|
||||||
|
mWebView.setWebViewClient(new FbDialog.FbWebViewClient());
|
||||||
|
mWebView.getSettings().setJavaScriptEnabled(true);
|
||||||
|
mWebView.loadUrl(mUrl);
|
||||||
|
mWebView.setLayoutParams(FILL);
|
||||||
|
mContent.addView(mWebView);
|
||||||
|
}
|
||||||
|
|
||||||
|
private class FbWebViewClient extends WebViewClient {
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean shouldOverrideUrlLoading(WebView view, String url) {
|
||||||
|
Log.d("Facebook-WebView", "Redirect URL: " + url);
|
||||||
|
if (url.startsWith(Facebook.REDIRECT_URI)) {
|
||||||
|
Bundle values = Util.parseUrl(url);
|
||||||
|
|
||||||
|
String error = values.getString("error");
|
||||||
|
if (error == null) {
|
||||||
|
error = values.getString("error_type");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error == null) {
|
||||||
|
mListener.onComplete(values, FbDialog.this);
|
||||||
|
} else if (error.equals("access_denied") ||
|
||||||
|
error.equals("OAuthAccessDeniedException")) {
|
||||||
|
mListener.onCancel();
|
||||||
|
} else {
|
||||||
|
mListener.onFacebookError(new FacebookError(error));
|
||||||
|
}
|
||||||
|
|
||||||
|
FbDialog.this.dismiss();
|
||||||
|
return true;
|
||||||
|
} else if (url.startsWith(Facebook.CANCEL_URI)) {
|
||||||
|
mListener.onCancel();
|
||||||
|
FbDialog.this.dismiss();
|
||||||
|
return true;
|
||||||
|
} else if (url.contains(DISPLAY_STRING)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// launch non-dialog URLs in a full browser
|
||||||
|
getContext().startActivity(
|
||||||
|
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onReceivedError(WebView view, int errorCode,
|
||||||
|
String description, String failingUrl) {
|
||||||
|
super.onReceivedError(view, errorCode, description, failingUrl);
|
||||||
|
mListener.onError(
|
||||||
|
new DialogError(description, errorCode, failingUrl));
|
||||||
|
FbDialog.this.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageStarted(WebView view, String url, Bitmap favicon) {
|
||||||
|
Log.d("Facebook-WebView", "Webview loading URL: " + url);
|
||||||
|
super.onPageStarted(view, url, favicon);
|
||||||
|
mSpinner.show();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPageFinished(WebView view, String url) {
|
||||||
|
super.onPageFinished(view, url);
|
||||||
|
String title = mWebView.getTitle();
|
||||||
|
if (title != null && title.length() > 0) {
|
||||||
|
mTitle.setText(title);
|
||||||
|
}
|
||||||
|
mSpinner.dismiss();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
301
FacebookLib/src/com/facebook/android/Util.java
Normal file
301
FacebookLib/src/com/facebook/android/Util.java
Normal file
@@ -0,0 +1,301 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2010 Facebook, Inc.
|
||||||
|
*
|
||||||
|
* 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 java.io.BufferedOutputStream;
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.OutputStream;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.net.URLDecoder;
|
||||||
|
import java.net.URLEncoder;
|
||||||
|
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import android.app.AlertDialog.Builder;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.util.Log;
|
||||||
|
import android.webkit.CookieManager;
|
||||||
|
import android.webkit.CookieSyncManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Utility class supporting the Facebook Object.
|
||||||
|
*
|
||||||
|
* @author ssoneff@facebook.com
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public final class Util {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate the multi-part post body providing the parameters and boundary
|
||||||
|
* string
|
||||||
|
*
|
||||||
|
* @param parameters the parameters need to be posted
|
||||||
|
* @param boundary the random string as boundary
|
||||||
|
* @return a string of the post body
|
||||||
|
*/
|
||||||
|
public static String encodePostBody(Bundle parameters, String boundary) {
|
||||||
|
if (parameters == null) return "";
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
|
||||||
|
for (String key : parameters.keySet()) {
|
||||||
|
if (parameters.getByteArray(key) != null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
sb.append("Content-Disposition: form-data; name=\"" + key +
|
||||||
|
"\"\r\n\r\n" + parameters.getString(key));
|
||||||
|
sb.append("\r\n" + "--" + boundary + "\r\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String encodeUrl(Bundle parameters) {
|
||||||
|
if (parameters == null) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
boolean first = true;
|
||||||
|
for (String key : parameters.keySet()) {
|
||||||
|
if (first) first = false; else sb.append("&");
|
||||||
|
sb.append(URLEncoder.encode(key) + "=" +
|
||||||
|
URLEncoder.encode(parameters.getString(key)));
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Bundle decodeUrl(String s) {
|
||||||
|
Bundle params = new Bundle();
|
||||||
|
if (s != null) {
|
||||||
|
String array[] = s.split("&");
|
||||||
|
for (String parameter : array) {
|
||||||
|
String v[] = parameter.split("=");
|
||||||
|
params.putString(URLDecoder.decode(v[0]),
|
||||||
|
URLDecoder.decode(v[1]));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return params;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a URL query and fragment parameters into a key-value bundle.
|
||||||
|
*
|
||||||
|
* @param url the URL to parse
|
||||||
|
* @return a dictionary bundle of keys and values
|
||||||
|
*/
|
||||||
|
public static Bundle parseUrl(String url) {
|
||||||
|
// hack to prevent MalformedURLException
|
||||||
|
url = url.replace("fbconnect", "http");
|
||||||
|
try {
|
||||||
|
URL u = new URL(url);
|
||||||
|
Bundle b = decodeUrl(u.getQuery());
|
||||||
|
b.putAll(decodeUrl(u.getRef()));
|
||||||
|
return b;
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return new Bundle();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Connect to an HTTP URL and return the response as a string.
|
||||||
|
*
|
||||||
|
* Note that the HTTP method override is used on non-GET requests. (i.e.
|
||||||
|
* requests are made as "POST" with method specified in the body).
|
||||||
|
*
|
||||||
|
* @param url - the resource to open: must be a welformed URL
|
||||||
|
* @param method - the HTTP method to use ("GET", "POST", etc.)
|
||||||
|
* @param params - the query parameter for the URL (e.g. access_token=foo)
|
||||||
|
* @return the URL contents as a String
|
||||||
|
* @throws MalformedURLException - if the URL format is invalid
|
||||||
|
* @throws IOException - if a network problem occurs
|
||||||
|
*/
|
||||||
|
public static String openUrl(String url, String method, Bundle params)
|
||||||
|
throws MalformedURLException, IOException {
|
||||||
|
// random string as boundary for multi-part http post
|
||||||
|
String strBoundary = "3i2ndDfv2rTHiSisAbouNdArYfORhtTPEefj3q2f";
|
||||||
|
String endLine = "\r\n";
|
||||||
|
|
||||||
|
OutputStream os;
|
||||||
|
|
||||||
|
if (method.equals("GET")) {
|
||||||
|
url = url + "?" + encodeUrl(params);
|
||||||
|
}
|
||||||
|
Log.d("Facebook-Util", method + " URL: " + url);
|
||||||
|
HttpURLConnection conn =
|
||||||
|
(HttpURLConnection) new URL(url).openConnection();
|
||||||
|
conn.setRequestProperty("User-Agent", System.getProperties().
|
||||||
|
getProperty("http.agent") + " FacebookAndroidSDK");
|
||||||
|
if (!method.equals("GET")) {
|
||||||
|
Bundle dataparams = new Bundle();
|
||||||
|
for (String key : params.keySet()) {
|
||||||
|
if (params.getByteArray(key) != null) {
|
||||||
|
dataparams.putByteArray(key, params.getByteArray(key));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// use method override
|
||||||
|
if (!params.containsKey("method")) {
|
||||||
|
params.putString("method", method);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.containsKey("access_token")) {
|
||||||
|
String decoded_token =
|
||||||
|
URLDecoder.decode(params.getString("access_token"));
|
||||||
|
params.putString("access_token", decoded_token);
|
||||||
|
}
|
||||||
|
|
||||||
|
conn.setRequestMethod("POST");
|
||||||
|
conn.setRequestProperty(
|
||||||
|
"Content-Type",
|
||||||
|
"multipart/form-data;boundary="+strBoundary);
|
||||||
|
conn.setDoOutput(true);
|
||||||
|
conn.setDoInput(true);
|
||||||
|
conn.setRequestProperty("Connection", "Keep-Alive");
|
||||||
|
conn.connect();
|
||||||
|
os = new BufferedOutputStream(conn.getOutputStream());
|
||||||
|
|
||||||
|
os.write(("--" + strBoundary +endLine).getBytes());
|
||||||
|
os.write((encodePostBody(params, strBoundary)).getBytes());
|
||||||
|
os.write((endLine + "--" + strBoundary + endLine).getBytes());
|
||||||
|
|
||||||
|
if (!dataparams.isEmpty()) {
|
||||||
|
|
||||||
|
for (String key: dataparams.keySet()){
|
||||||
|
os.write(("Content-Disposition: form-data; filename=\"" + key + "\"" + endLine).getBytes());
|
||||||
|
os.write(("Content-Type: content/unknown" + endLine + endLine).getBytes());
|
||||||
|
os.write(dataparams.getByteArray(key));
|
||||||
|
os.write((endLine + "--" + strBoundary + endLine).getBytes());
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
os.flush();
|
||||||
|
}
|
||||||
|
|
||||||
|
String response = "";
|
||||||
|
try {
|
||||||
|
response = read(conn.getInputStream());
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
// Error Stream contains JSON that we can parse to a FB error
|
||||||
|
response = read(conn.getErrorStream());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String read(InputStream in) throws IOException {
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
BufferedReader r = new BufferedReader(new InputStreamReader(in), 1000);
|
||||||
|
for (String line = r.readLine(); line != null; line = r.readLine()) {
|
||||||
|
sb.append(line);
|
||||||
|
}
|
||||||
|
in.close();
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void clearCookies(Context context) {
|
||||||
|
// Edge case: an illegal state exception is thrown if an instance of
|
||||||
|
// CookieSyncManager has not be created. CookieSyncManager is normally
|
||||||
|
// created by a WebKit view, but this might happen if you start the
|
||||||
|
// app, restore saved state, and click logout before running a UI
|
||||||
|
// dialog in a WebView -- in which case the app crashes
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
CookieSyncManager cookieSyncMngr =
|
||||||
|
CookieSyncManager.createInstance(context);
|
||||||
|
CookieManager cookieManager = CookieManager.getInstance();
|
||||||
|
cookieManager.removeAllCookie();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse a server response into a JSON Object. This is a basic
|
||||||
|
* implementation using org.json.JSONObject representation. More
|
||||||
|
* sophisticated applications may wish to do their own parsing.
|
||||||
|
*
|
||||||
|
* The parsed JSON is checked for a variety of error fields and
|
||||||
|
* a FacebookException is thrown if an error condition is set,
|
||||||
|
* populated with the error message and error type or code if
|
||||||
|
* available.
|
||||||
|
*
|
||||||
|
* @param response - string representation of the response
|
||||||
|
* @return the response as a JSON Object
|
||||||
|
* @throws JSONException - if the response is not valid JSON
|
||||||
|
* @throws FacebookError - if an error condition is set
|
||||||
|
*/
|
||||||
|
public static JSONObject parseJson(String response)
|
||||||
|
throws JSONException, FacebookError {
|
||||||
|
// Edge case: when sending a POST request to /[post_id]/likes
|
||||||
|
// the return value is 'true' or 'false'. Unfortunately
|
||||||
|
// these values cause the JSONObject constructor to throw
|
||||||
|
// an exception.
|
||||||
|
if (response.equals("false")) {
|
||||||
|
throw new FacebookError("request failed");
|
||||||
|
}
|
||||||
|
if (response.equals("true")) {
|
||||||
|
response = "{value : true}";
|
||||||
|
}
|
||||||
|
JSONObject json = new JSONObject(response);
|
||||||
|
|
||||||
|
// errors set by the server are not consistent
|
||||||
|
// they depend on the method and endpoint
|
||||||
|
if (json.has("error")) {
|
||||||
|
JSONObject error = json.getJSONObject("error");
|
||||||
|
throw new FacebookError(
|
||||||
|
error.getString("message"), error.getString("type"), 0);
|
||||||
|
}
|
||||||
|
if (json.has("error_code") && json.has("error_msg")) {
|
||||||
|
throw new FacebookError(json.getString("error_msg"), "",
|
||||||
|
Integer.parseInt(json.getString("error_code")));
|
||||||
|
}
|
||||||
|
if (json.has("error_code")) {
|
||||||
|
throw new FacebookError("request failed", "",
|
||||||
|
Integer.parseInt(json.getString("error_code")));
|
||||||
|
}
|
||||||
|
if (json.has("error_msg")) {
|
||||||
|
throw new FacebookError(json.getString("error_msg"));
|
||||||
|
}
|
||||||
|
if (json.has("error_reason")) {
|
||||||
|
throw new FacebookError(json.getString("error_reason"));
|
||||||
|
}
|
||||||
|
return json;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Display a simple alert dialog with the given text and title.
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* Android context in which the dialog should be displayed
|
||||||
|
* @param title
|
||||||
|
* Alert dialog title
|
||||||
|
* @param text
|
||||||
|
* Alert dialog message
|
||||||
|
*/
|
||||||
|
public static void showAlert(Context context, String title, String text) {
|
||||||
|
Builder alertBuilder = new Builder(context);
|
||||||
|
alertBuilder.setTitle(title);
|
||||||
|
alertBuilder.setMessage(text);
|
||||||
|
alertBuilder.create().show();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user