Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Merge pull request #19 from akshay2047/master

Merge my changes
  • Loading branch information...
commit e3fc7c56936cce4da8497917826db4a51c51f78f 2 parents 60d801e + 303c232
@rscottm rscottm authored
Showing with 2,483 additions and 3 deletions.
  1. +2 −0  AndroidManifest.xml
  2. BIN  res/drawable-hdpi/directory_icon.png
  3. BIN  res/drawable-hdpi/directory_up.png
  4. BIN  res/drawable-hdpi/facebook_icon.png
  5. BIN  res/drawable-hdpi/file_icon.png
  6. BIN  res/drawable-hdpi/ic_facebook_off.png
  7. BIN  res/drawable-hdpi/ic_facebook_on.png
  8. BIN  res/drawable-hdpi/ic_launcher.png
  9. +24 −0 res/drawable/btn_check_facebook.xml
  10. +37 −0 res/layout/connect.xml
  11. +43 −0 res/layout/post.xml
  12. +2 −0  res/values/strings.xml
  13. +265 −0 src/com/facebook/android/AsyncFacebookRunner.java
  14. +23 −0 src/com/facebook/android/BaseDialogListener.java
  15. +33 −0 src/com/facebook/android/BaseRequestListener.java
  16. +30 −0 src/com/facebook/android/DialogError.java
  17. +736 −0 src/com/facebook/android/Facebook.java
  18. +28 −0 src/com/facebook/android/FacebookError.java
  19. +156 −0 src/com/facebook/android/FbDialog.java
  20. +130 −0 src/com/facebook/android/SessionEvents.java
  21. +53 −0 src/com/facebook/android/SessionStore.java
  22. +279 −0 src/com/facebook/android/Util.java
  23. +237 −0 src/net/android/facebook/TestConnect.java
  24. +104 −0 src/net/android/facebook/TestPost.java
  25. +301 −3 src/org/ruboto/irb/IRB.java
View
2  AndroidManifest.xml
@@ -30,6 +30,8 @@
<activity android:name='org.ruboto.RubotoActivity' android:alwaysRetainTaskState='true' android:configChanges='orientation|screenSize'/>
<activity android:theme='@android:style/Theme.Dialog' android:name='org.ruboto.RubotoDialog' android:configChanges='orientation|screenSize'/>
<activity android:name='org.ruboto.RubotoPreferenceActivity'/>
+ <activity android:name='net.android.facebook.TestConnect' android:configChanges='orientation|screenSize'/>
+ <activity android:name='net.android.facebook.TestPost' android:configChanges='orientation|screenSize'/>
<service android:name='org.ruboto.RubotoService' android:exported='false'/>
</application>
<uses-sdk android:minSdkVersion='7' android:targetSdkVersion='13'/>
View
BIN  res/drawable-hdpi/directory_icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  res/drawable-hdpi/directory_up.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  res/drawable-hdpi/facebook_icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  res/drawable-hdpi/file_icon.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  res/drawable-hdpi/ic_facebook_off.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  res/drawable-hdpi/ic_facebook_on.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN  res/drawable-hdpi/ic_launcher.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
24 res/drawable/btn_check_facebook.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0" encoding="UTF-8"?>
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item
+ android:state_checked="true"
+ android:state_window_focused="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/ic_facebook_on" />
+
+ <item
+ android:state_checked="true"
+ android:state_window_focused="false"
+ android:state_enabled="true"
+ android:drawable="@drawable/ic_facebook_on" />
+
+ <item
+ android:state_checked="false"
+ android:state_window_focused="true"
+ android:state_enabled="true"
+ android:drawable="@drawable/ic_facebook_off" />
+
+ <item
+ android:drawable="@drawable/ic_facebook_off" />
+</selector>
View
37 res/layout/connect.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:textSize="13sp"
+ android:textColor="#fff"
+ android:text="Connect to facebook to share your scripts."/>
+
+ <CheckBox
+ android:id="@+id/cb_facebook"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginLeft="5dp"
+ android:text=" Facebook (Not connected) "
+ android:textSize="14sp"
+ android:textStyle="bold"
+ android:textColor="#ccc"
+ android:clickable="true"
+ android:focusable="true"
+ android:button="@drawable/btn_check_facebook"/>
+
+ <Button
+ android:id="@+id/button1"
+ android:layout_marginTop="10dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=" Continue" />
+
+</LinearLayout>
View
43 res/layout/post.xml
@@ -0,0 +1,43 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<LinearLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="fill_parent"
+ android:layout_height="fill_parent"
+ android:padding="10dp">
+
+ <TextView
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:gravity="left"
+ android:textSize="13sp"
+ android:textColor="#fff"
+ android:text="Post Script on your facebook Wall"/>
+
+ <EditText
+ android:id="@+id/revieew"
+ android:layout_marginTop="15dp"
+ android:layout_width="250dp"
+ android:layout_height="100dp"
+ android:hint="Comments about your Script"/>
+
+ <CheckBox
+ android:id="@+id/cb_facebook"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="10dp"
+ android:layout_marginLeft="5dp"
+ android:text=" Facebook"
+ android:textSize="14sp"
+ android:clickable="true"
+ android:focusable="true"
+ android:button="@drawable/btn_check_facebook"/>
+
+
+ <Button
+ android:id="@+id/button1"
+ android:layout_marginTop="15dp"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text=" Submit "/>
+</LinearLayout>
View
2  res/values/strings.xml
@@ -18,6 +18,7 @@
<string name="Menu_about">About</string>
<string name="Menu_edit">Edit</string>
<string name="Menu_delete">Delete</string>
+ <string name="Menu_share">Share on Facebook</string>
<string name="Menu_rescan">Reload scripts list</string>
<string name="Menu_reload">Reload demos from assets</string>
<string name="Menu_clear_irb">Clear IRB output</string>
@@ -25,6 +26,7 @@
<string name="Menu_max_screen">Toggle usable screen</string>
<string name="Menu_line_numbers">Toggle line numbers</string>
<string name="Menu_goto">Go to line number</string>
+ <string name="Menu_import">Import Script</string>
<string name="Dialog_ok">Ok</string>
<string name="Dialog_cancel">Cancel</string>
View
265 src/com/facebook/android/AsyncFacebookRunner.java
@@ -0,0 +1,265 @@
+/*
+ * 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 ssoneff@facebook.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.
+ */
+ public void logout(final Context context, final RequestListener listener) {
+ 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"));
+ return;
+ }
+ listener.onComplete(response);
+ } catch (FileNotFoundException e) {
+ listener.onFileNotFoundException(e);
+ } catch (MalformedURLException e) {
+ listener.onMalformedURLException(e);
+ } catch (IOException e) {
+ listener.onIOException(e);
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * 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.
+ */
+ public void request(Bundle parameters,
+ RequestListener listener) {
+ request(null, parameters, "GET", listener);
+ }
+
+ /**
+ * 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.
+ */
+ public void request(String graphPath,
+ RequestListener listener) {
+ request(graphPath, new Bundle(), "GET", listener);
+ }
+
+ /**
+ * 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.
+ */
+ public void request(String graphPath,
+ Bundle parameters,
+ RequestListener listener) {
+ request(graphPath, parameters, "GET", listener);
+ }
+
+ /**
+ * 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.
+ */
+ public void request(final String graphPath,
+ final Bundle parameters,
+ final String httpMethod,
+ final RequestListener listener) {
+ new Thread() {
+ @Override public void run() {
+ try {
+ String resp = fb.request(graphPath, parameters, httpMethod);
+ listener.onComplete(resp);
+ } catch (FileNotFoundException e) {
+ listener.onFileNotFoundException(e);
+ } catch (MalformedURLException e) {
+ listener.onMalformedURLException(e);
+ } catch (IOException e) {
+ listener.onIOException(e);
+ }
+ }
+ }.start();
+ }
+
+ /**
+ * Callback interface for API requests.
+ */
+ 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ /**
+ * 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);
+
+ }
+
+}
View
23 src/com/facebook/android/BaseDialogListener.java
@@ -0,0 +1,23 @@
+package com.facebook.android;
+
+import com.facebook.android.Facebook.DialogListener;
+
+/**
+ * Skeleton base class for RequestListeners, providing default error
+ * handling. Applications should handle these error conditions.
+ *
+ */
+public abstract class BaseDialogListener implements DialogListener {
+
+ public void onFacebookError(FacebookError e) {
+ e.printStackTrace();
+ }
+
+ public void onError(DialogError e) {
+ e.printStackTrace();
+ }
+
+ public void onCancel() {
+ }
+
+}
View
33 src/com/facebook/android/BaseRequestListener.java
@@ -0,0 +1,33 @@
+package com.facebook.android;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import android.util.Log;
+
+import com.facebook.android.AsyncFacebookRunner.RequestListener;
+
+public abstract class BaseRequestListener implements RequestListener {
+
+ public void onFacebookError(FacebookError e) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+ public void onFileNotFoundException(FileNotFoundException e) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+ public void onIOException(IOException e) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+ public void onMalformedURLException(MalformedURLException e) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+}
View
30 src/com/facebook/android/DialogError.java
@@ -0,0 +1,30 @@
+package com.facebook.android;
+
+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;
+ }
+
+}
View
736 src/com/facebook/android/Facebook.java
@@ -0,0 +1,736 @@
+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.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+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.os.Bundle;
+import android.text.TextUtils;
+import android.util.Log;
+import android.webkit.CookieSyncManager;
+
+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(Activity activity, final DialogListener listener) {
+ authorize(activity, new String[] {}, DEFAULT_AUTH_ACTIVITY_CODE,
+ listener);
+ }
+
+ /**
+ * Authorize method that grants custom permissions.
+ *
+ * See authorize() below for @params.
+ */
+ public void authorize(Activity activity, String[] permissions,
+ final DialogListener listener) {
+ authorize(activity, 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(Activity activity, 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(activity, 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 activity
+ * 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(Activity activity, String[] permissions) {
+ Bundle params = new Bundle();
+ if (permissions.length > 0) {
+ params.putString("scope", TextUtils.join(",", permissions));
+ }
+ CookieSyncManager.createInstance(activity);
+ dialog(activity, LOGIN, params, new DialogListener() {
+
+ public void onComplete(Bundle values) {
+ // 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);
+ } 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());
+ } 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 void 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 {
+ new FbDialog(context, url, listener).show();
+ }
+ }
+
+ /**
+ * @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);
+
+ /**
+ * 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";
+
+}
View
28 src/com/facebook/android/FacebookError.java
@@ -0,0 +1,28 @@
+package com.facebook.android;
+
+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;
+ }
+
+}
View
156 src/com/facebook/android/FbDialog.java
@@ -0,0 +1,156 @@
+package com.facebook.android;
+
+//import net.android.facebook.R;
+import org.ruboto.irb.R;
+
+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_reason");
+ if (error == null) {
+ mListener.onComplete(values);
+ } 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();
+ }
+
+ }
+}
View
130 src/com/facebook/android/SessionEvents.java
@@ -0,0 +1,130 @@
+package com.facebook.android;
+
+import java.util.LinkedList;
+
+public class SessionEvents {
+
+ private static LinkedList<AuthListener> mAuthListeners =
+ new LinkedList<AuthListener>();
+ private static LinkedList<LogoutListener> mLogoutListeners =
+ new LinkedList<LogoutListener>();
+
+ /**
+ * Associate the given listener with this Facebook object. The listener's
+ * callback interface will be invoked when authentication events occur.
+ *
+ * @param listener
+ * The callback object for notifying the application when auth
+ * events happen.
+ */
+ public static void addAuthListener(AuthListener listener) {
+ mAuthListeners.add(listener);
+ }
+
+ /**
+ * Remove the given listener from the list of those that will be notified
+ * when authentication events occur.
+ *
+ * @param listener
+ * The callback object for notifying the application when auth
+ * events happen.
+ */
+ public static void removeAuthListener(AuthListener listener) {
+ mAuthListeners.remove(listener);
+ }
+
+ /**
+ * Associate the given listener with this Facebook object. The listener's
+ * callback interface will be invoked when logout occurs.
+ *
+ * @param listener
+ * The callback object for notifying the application when log out
+ * starts and finishes.
+ */
+ public static void addLogoutListener(LogoutListener listener) {
+ mLogoutListeners.add(listener);
+ }
+
+ /**
+ * Remove the given listener from the list of those that will be notified
+ * when logout occurs.
+ *
+ * @param listener
+ * The callback object for notifying the application when log out
+ * starts and finishes.
+ */
+ public static void removeLogoutListener(LogoutListener listener) {
+ mLogoutListeners.remove(listener);
+ }
+
+ public static void onLoginSuccess() {
+ for (AuthListener listener : mAuthListeners) {
+ listener.onAuthSucceed();
+ }
+ }
+
+ public static void onLoginError(String error) {
+ for (AuthListener listener : mAuthListeners) {
+ listener.onAuthFail(error);
+ }
+ }
+
+ public static void onLogoutBegin() {
+ for (LogoutListener l : mLogoutListeners) {
+ l.onLogoutBegin();
+ }
+ }
+
+ public static void onLogoutFinish() {
+ for (LogoutListener l : mLogoutListeners) {
+ l.onLogoutFinish();
+ }
+ }
+
+ /**
+ * Callback interface for authorization events.
+ *
+ */
+ public static interface AuthListener {
+
+ /**
+ * Called when a auth flow completes successfully and a valid OAuth
+ * Token was received.
+ *
+ * Executed by the thread that initiated the authentication.
+ *
+ * API requests can now be made.
+ */
+ public void onAuthSucceed();
+
+ /**
+ * Called when a login completes unsuccessfully with an error.
+ *
+ * Executed by the thread that initiated the authentication.
+ */
+ public void onAuthFail(String error);
+ }
+
+ /**
+ * Callback interface for logout events.
+ *
+ */
+ public static interface LogoutListener {
+ /**
+ * Called when logout begins, before session is invalidated.
+ * Last chance to make an API call.
+ *
+ * Executed by the thread that initiated the logout.
+ */
+ public void onLogoutBegin();
+
+ /**
+ * Called when the session information has been cleared.
+ * UI should be updated to reflect logged-out state.
+ *
+ * Executed by the thread that initiated the logout.
+ */
+ public void onLogoutFinish();
+ }
+
+}
View
53 src/com/facebook/android/SessionStore.java
@@ -0,0 +1,53 @@
+package com.facebook.android;
+
+import com.facebook.android.Facebook;
+import android.content.Context;
+import android.content.SharedPreferences;
+import android.content.SharedPreferences.Editor;
+
+public class SessionStore {
+
+ private static final String TOKEN = "access_token";
+ private static final String EXPIRES = "expires_in";
+ private static final String NAME = "fb_name";
+ private static final String KEY = "facebook-session";
+
+ public static boolean save(Facebook session, Context context) {
+ Editor editor =
+ context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
+ editor.putString(TOKEN, session.getAccessToken());
+ editor.putLong(EXPIRES, session.getAccessExpires());
+ return editor.commit();
+ }
+
+ public static boolean restore(Facebook session, Context context) {
+ SharedPreferences savedSession =
+ context.getSharedPreferences(KEY, Context.MODE_PRIVATE);
+ session.setAccessToken(savedSession.getString(TOKEN, null));
+ session.setAccessExpires(savedSession.getLong(EXPIRES, 0));
+ return session.isSessionValid();
+ }
+
+ public static boolean saveName(String name, Context context) {
+ Editor editor =
+ context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
+ editor.putString(NAME, name);
+
+ return editor.commit();
+ }
+
+ public static String getName(Context context) {
+ SharedPreferences savedSession =
+ context.getSharedPreferences(KEY, Context.MODE_PRIVATE);
+
+ return savedSession.getString(NAME, "Unknown");
+ }
+
+ public static void clear(Context context) {
+ Editor editor =
+ context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
+ editor.clear();
+ editor.commit();
+ }
+
+}
View
279 src/com/facebook/android/Util.java
@@ -0,0 +1,279 @@
+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;
+
+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();
+ }
+
+}
View
237 src/net/android/facebook/TestConnect.java
@@ -0,0 +1,237 @@
+package net.android.facebook;
+
+import org.ruboto.irb.R;
+import org.json.JSONObject;
+import org.json.JSONTokener;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.ProgressDialog;
+
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.graphics.Color;
+
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.Toast;
+
+import com.facebook.android.DialogError;
+import com.facebook.android.Facebook;
+import com.facebook.android.FacebookError;
+import com.facebook.android.SessionStore;
+import com.facebook.android.Facebook.DialogListener;
+
+
+public class TestConnect extends Activity {
+ private Facebook mFacebook;
+ private CheckBox mFacebookBtn;
+ private ProgressDialog mProgress;
+ public Button btn1;
+ private static final String[] PERMISSIONS = new String[] {"publish_stream", "read_stream", "offline_access"};
+
+ private static final String APP_ID = "340864435993007";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.connect);
+
+ Intent i = getIntent();
+ final String x= i.getStringExtra("code");
+ final String Sname= i.getStringExtra("Sname");
+
+ mFacebookBtn = (CheckBox) findViewById(R.id.cb_facebook);
+
+ mProgress = new ProgressDialog(this);
+ mFacebook = new Facebook(APP_ID);
+
+ SessionStore.restore(mFacebook, this);
+
+ if (mFacebook.isSessionValid()) {
+ mFacebookBtn.setChecked(true);
+
+ String name = SessionStore.getName(this);
+ name = (name.equals("")) ? "Unknown" : name;
+
+ mFacebookBtn.setText(" Facebook (" + name + ")");
+ mFacebookBtn.setTextColor(Color.WHITE);
+ }
+
+ mFacebookBtn.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ onFacebookClick();
+ }
+ });
+
+ btn1 = (Button) findViewById(R.id.button1);
+ if (!mFacebook.isSessionValid())
+ btn1.setVisibility(View.INVISIBLE);
+
+ btn1.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent connect = new Intent(TestConnect.this, TestPost.class);
+ connect.putExtra("code", x);
+ connect.putExtra("Sname", Sname);
+ startActivity(connect);
+
+ finish();
+ }
+ });
+ }
+
+ private void onFacebookClick() {
+ if (mFacebook.isSessionValid()) {
+ final AlertDialog.Builder builder = new AlertDialog.Builder(this);
+
+ builder.setMessage("Delete current Facebook connection?")
+ .setCancelable(false)
+ .setPositiveButton("Yes", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ fbLogout();
+ btn1.setVisibility(View.INVISIBLE);
+ }
+ })
+ .setNegativeButton("No", new DialogInterface.OnClickListener() {
+ public void onClick(DialogInterface dialog, int id) {
+ dialog.cancel();
+
+ mFacebookBtn.setChecked(true);
+ }
+ });
+
+ final AlertDialog alert = builder.create();
+
+ alert.show();
+ } else {
+ mFacebookBtn.setChecked(false);
+
+ mFacebook.authorize(this, PERMISSIONS, -1, new FbLoginDialogListener());
+
+ }
+ }
+
+ private final class FbLoginDialogListener implements DialogListener {
+ public void onComplete(Bundle values) {
+ SessionStore.save(mFacebook, TestConnect.this);
+
+ mFacebookBtn.setText(" Facebook (No Name)");
+ mFacebookBtn.setChecked(true);
+ mFacebookBtn.setTextColor(Color.WHITE);
+
+ getFbName();
+ }
+
+ public void onFacebookError(FacebookError error) {
+ Toast.makeText(TestConnect.this, "Facebook connection failed", Toast.LENGTH_SHORT).show();
+
+ mFacebookBtn.setChecked(false);
+ }
+
+ public void onError(DialogError error) {
+ Toast.makeText(TestConnect.this, "Facebook connection failed", Toast.LENGTH_SHORT).show();
+
+ mFacebookBtn.setChecked(false);
+ }
+
+ public void onCancel() {
+ mFacebookBtn.setChecked(false);
+ }
+ }
+
+ private void getFbName() {
+ mProgress.setMessage("Finalizing ...");
+ mProgress.show();
+ btn1.setVisibility(View.VISIBLE);
+ new Thread() {
+ @Override
+ public void run() {
+ String name = "";
+ int what = 1;
+
+ try {
+ String me = mFacebook.request("me");
+
+ JSONObject jsonObj = (JSONObject) new JSONTokener(me).nextValue();
+ name = jsonObj.getString("name");
+ what = 0;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ mFbHandler.sendMessage(mFbHandler.obtainMessage(what, name));
+ }
+ }.start();
+ }
+
+ private void fbLogout() {
+ mProgress.setMessage("Disconnecting from Facebook");
+ mProgress.show();
+
+ new Thread() {
+ @Override
+ public void run() {
+ SessionStore.clear(TestConnect.this);
+
+ int what = 1;
+
+ try {
+ mFacebook.logout(TestConnect.this);
+
+ what = 0;
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+
+ mHandler.sendMessage(mHandler.obtainMessage(what));
+ }
+ }.start();
+ }
+
+ private Handler mFbHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ mProgress.dismiss();
+
+ if (msg.what == 0) {
+ String username = (String) msg.obj;
+ username = (username.equals("")) ? "No Name" : username;
+
+ SessionStore.saveName(username, TestConnect.this);
+
+ mFacebookBtn.setText(" Facebook (" + username + ")");
+
+ Toast.makeText(TestConnect.this, "Connected to Facebook as " + username, Toast.LENGTH_SHORT).show();
+ } else {
+ Toast.makeText(TestConnect.this, "Connected to Facebook", Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+
+ private Handler mHandler = new Handler() {
+ @Override
+ public void handleMessage(Message msg) {
+ mProgress.dismiss();
+
+ if (msg.what == 1) {
+ Toast.makeText(TestConnect.this, "Facebook logout failed", Toast.LENGTH_SHORT).show();
+ } else {
+ mFacebookBtn.setChecked(false);
+ mFacebookBtn.setText(" Facebook (Not connected)");
+ mFacebookBtn.setTextColor(Color.GRAY);
+
+ Toast.makeText(TestConnect.this, "Disconnected from Facebook", Toast.LENGTH_SHORT).show();
+ }
+ }
+ };
+}
View
104 src/net/android/facebook/TestPost.java
@@ -0,0 +1,104 @@
+package net.android.facebook;
+
+import org.ruboto.irb.R;
+import android.os.Bundle;
+import android.os.Handler;
+
+import android.app.Activity;
+import android.app.ProgressDialog;
+import android.content.Intent;
+
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.EditText;
+import android.widget.Toast;
+
+import com.facebook.android.AsyncFacebookRunner;
+import com.facebook.android.BaseRequestListener;
+import com.facebook.android.Facebook;
+import com.facebook.android.SessionStore;
+
+public class TestPost extends Activity{
+ private Facebook mFacebook;
+ private CheckBox mFacebookCb;
+ private ProgressDialog mProgress;
+
+ private Handler mRunOnUi = new Handler();
+
+ private static final String APP_ID = "340864435993007";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.post);
+
+ Intent get = getIntent();
+ final String x = get.getStringExtra("code");
+ final String Sname = get.getStringExtra("Sname");
+
+
+ final EditText reviewEdit = (EditText) findViewById(R.id.revieew);
+ mFacebookCb = (CheckBox) findViewById(R.id.cb_facebook);
+
+ mProgress = new ProgressDialog(this);
+
+ mFacebook = new Facebook(APP_ID);
+
+ SessionStore.restore(mFacebook, this);
+
+ if (mFacebook.isSessionValid()) {
+ mFacebookCb.setChecked(true);
+
+ String name = SessionStore.getName(this);
+ name = (name.equals("")) ? "Unknown" : name;
+
+ mFacebookCb.setText(" Facebook (" + name + ")");
+ }
+
+ ((Button) findViewById(R.id.button1)).setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ String review = reviewEdit.getText().toString();
+
+ review = review + "\nScript name:" +Sname +"\n=====================\n" + x;
+ if (mFacebookCb.isChecked()) postToFacebook(review);
+ }
+ });
+ }
+
+ private void postToFacebook(String review) {
+ mProgress.setMessage("Posting ...");
+ mProgress.show();
+
+ AsyncFacebookRunner mAsyncFbRunner = new AsyncFacebookRunner(mFacebook);
+
+ Bundle params = new Bundle();
+
+ params.putString("message", review );
+ params.putString("name", "Ruboto");
+ params.putString("caption", "ruboto.org");
+ params.putString("link", "http://ruboto.org/");
+ params.putString("description", "Ruboto, sharing your jruby scripts is fun");
+ params.putString("picture", "http://ruboto.org/ruboto.png");
+
+ mAsyncFbRunner.request("me/feed", params, "POST", new WallPostListener());
+ }
+
+ private final class WallPostListener extends BaseRequestListener {
+ public void onComplete(final String response) {
+ mRunOnUi.post(new Runnable() {
+ @Override
+ public void run() {
+ mProgress.cancel();
+
+ Toast.makeText(TestPost.this, "Posted to Facebook", Toast.LENGTH_SHORT).show();
+ finish();
+ }
+ });
+ }
+ }
+}
View
304 src/org/ruboto/irb/IRB.java
@@ -1,19 +1,26 @@
package org.ruboto.irb;
import java.io.BufferedOutputStream;
+import java.io.BufferedReader;
import java.io.File;
+import java.io.FileInputStream;
import java.io.FileOutputStream;
+import java.io.FilenameFilter;
import java.io.IOException;
import java.io.InputStream;
+import java.io.InputStreamReader;
import java.io.OutputStream;
import java.io.PrintStream;
import java.io.Writer;
+import java.util.ArrayList;
import java.util.List;
import org.jruby.embed.io.WriterOutputStream;
import android.app.Activity;
import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.AlertDialog.Builder;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
@@ -29,6 +36,7 @@
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
+import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
@@ -37,6 +45,7 @@
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.EditText;
+import android.widget.ListAdapter;
import android.widget.ListView;
import android.widget.ScrollView;
import android.widget.TabHost;
@@ -45,17 +54,51 @@
import android.widget.TextView;
import android.widget.Toast;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import java.io.File;
+import java.io.FilenameFilter;
+import java.util.ArrayList;
+import android.app.AlertDialog;
+import android.app.AlertDialog.Builder;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.os.Environment;
+import android.util.Log;
+import android.view.ViewGroup;
+import android.widget.ArrayAdapter;
+import android.widget.ListAdapter;
+import android.widget.TextView;
+import android.widget.Toast;
+
+
import org.ruboto.JRubyAdapter;
public class IRB extends org.ruboto.EntryPointActivity implements OnItemClickListener,
OnTabChangeListener {
+ //////////////////////////////////////////////////////////////
+ // Stores names of traversed directories
+ ArrayList<String> str = new ArrayList<String>();
+
+ // Check if the first level of the directory structure is the one showing
+ private Boolean firstLvl = true;
+
+
+ private Item[] fileList;
+ private File path = new File(Environment.getExternalStorageDirectory() + "");
+ private String chosenFile;
+ private static final int DIALOG_LOAD_FILE = 1000;
+
+ ListAdapter adapter1;
+ //////////////////////////////////////////////////////////////
public static final String TAG = "Ruboto-IRB";
public static final String SDCARD_SCRIPTS_DIR = "/sdcard/jruby";
+
private TabHost tabs;
private TabWidget tabWidget;
private final Handler handler = new Handler();
- private PrintStream printStream;
+ private PrintStream printStream;
/* IRB_Tab Elements */
public static TextView currentIrbOutput;
@@ -89,12 +132,13 @@
private static final int MAX_SCREEN_MENU = 10;
private static final int LINE_NUMBERS_MENU = 11;
private static final int GOTO_MENU = 12;
-
+ private static final int IMPORT_MENU = 13;
+
/* Context menu option identifiers for script list */
private static final int EDIT_MENU = 20;
private static final int EXECUTE_MENU = 21;
private static final int DELETE_MENU = 22;
-
+ private static final int SHARE_MENU = 23;
private static final String DEMO_SCRIPTS = "demo-scripts";
/*********************************************************************************************
@@ -301,6 +345,7 @@ public boolean onCreateOptionsMenu(Menu menu) {
menu.add(0, MAX_SCREEN_MENU, 0, R.string.Menu_max_screen);
menu.add(0, LINE_NUMBERS_MENU, 0, R.string.Menu_line_numbers);
menu.add(0, GOTO_MENU, 0, R.string.Menu_goto);
+ menu.add(0, IMPORT_MENU, 0, R.string.Menu_import);
return true;
}
@@ -383,11 +428,225 @@ public boolean onMenuItemSelected(int featureId, MenuItem item) {
tabs.setCurrentTab(EDITOR_TAB);
gotoDialog();
return true;
+ case IMPORT_MENU:
+ loadFileList();
+ showDialog(DIALOG_LOAD_FILE);
+ Log.d(TAG, path.getAbsolutePath());
+ return true;
+
}
return super.onMenuItemSelected(featureId, item);
}
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////
+ public String readfiles(String path)
+ {
+ String aBuffer = "";
+ try {
+ File myFile = new File(path);
+ FileInputStream fIn = new FileInputStream(myFile);
+ BufferedReader myReader = new BufferedReader(
+ new InputStreamReader(fIn));
+ String aDataRow = "";
+ // String aBuffer = "";
+ while ((aDataRow = myReader.readLine()) != null) {
+ aBuffer += aDataRow + "\n";
+ }
+ // txtData.setText(aBuffer);
+ myReader.close();
+ Toast.makeText(getBaseContext(),
+ "Done reading SD 'mysdfile.txt'",
+ Toast.LENGTH_SHORT).show();
+ } catch (Exception e) {
+ Toast.makeText(getBaseContext(), e.getMessage(),
+ Toast.LENGTH_SHORT).show();
+ }
+
+
+ return aBuffer;
+ }
+
+ //////////////////////////////////////////////////////////////////////////////////////////////////////////
+ public void loadFileList() {
+ try {
+ path.mkdirs();
+ } catch (SecurityException e) {
+ Log.e(TAG, "unable to write on the sd card ");
+ }
+
+ // Checks whether path exists
+ if (path.exists()) {
+ FilenameFilter filter = new FilenameFilter() {
+ @Override
+ public boolean accept(File dir, String filename) {
+ File sel = new File(dir, filename);
+ // Filters based on whether the file is hidden or not
+ return (sel.isFile() || sel.isDirectory())
+ && !sel.isHidden();
+
+ }
+ };
+
+ String[] fList = null;
+ try{
+ fList = path.list(filter);
+ fileList = new Item[fList.length];
+ }
+ catch (Exception e) {
+ Toast.makeText(IRB.this,"No SD card",1).show();
+ return;
+ }
+ for (int i = 0; i < fList.length; i++) {
+ fileList[i] = new Item(fList[i], R.drawable.file_icon);
+
+ // Convert into file path
+ File sel = new File(path, fList[i]);
+
+ // Set drawables
+ if (sel.isDirectory()) {
+ fileList[i].icon = R.drawable.directory_icon;
+ Log.d("DIRECTORY", fileList[i].file);
+ } else {
+ Log.d("FILE", fileList[i].file);
+ }
+ }
+
+ if (!firstLvl) {
+ Item temp[] = new Item[fileList.length + 1];
+ for (int i = 0; i < fileList.length; i++) {