diff --git a/TravelPost/.classpath b/TravelPost/.classpath
index db42afc..b450f30 100644
--- a/TravelPost/.classpath
+++ b/TravelPost/.classpath
@@ -4,5 +4,6 @@
+
diff --git a/TravelPost/.project b/TravelPost/.project
index ee1f480..09a8d0b 100644
--- a/TravelPost/.project
+++ b/TravelPost/.project
@@ -30,4 +30,11 @@
com.android.ide.eclipse.adt.AndroidNatureorg.eclipse.jdt.core.javanature
+
+
+ FacebookLib_src
+ 2
+ _android_FacebookLib_9c84f9bf/src
+
+
diff --git a/TravelPost/AndroidManifest.xml b/TravelPost/AndroidManifest.xml
index 38facca..1a51242 100644
--- a/TravelPost/AndroidManifest.xml
+++ b/TravelPost/AndroidManifest.xml
@@ -1,7 +1,6 @@
+ package="com.TwentyCodes.android.TravelPost" android:versionName="0.0.0" android:versionCode="5">
diff --git a/TravelPost/ChangeLog.txt b/TravelPost/ChangeLog.txt
index 139597f..f936d2d 100644
--- a/TravelPost/ChangeLog.txt
+++ b/TravelPost/ChangeLog.txt
@@ -1,2 +1,21 @@
+created FacebokAuth.java
+created package com.TwentyCodes.android.Facebook in TravelPost.
+moved FacebookAuth.java to package.
+created SessionStore.java and SessionEvents.java in Facebook package
+created BaseRequestListener.java and BaseDialogListener.java in Facebook package.
+
+modified FacebookAuth.java again. this file will be the object that interfaces with the facebook sdk.
+modified FacebookAuth.postToWall(). testing now
+
+added facebook sign in preference to R.xml.settings. currently Travel Post is acting like Your Mom. this should be changed before final testing.
+
+currently app crashes. i don't have enough time to check trace but i sent an email. for now im saving work and will continue tomorrow.
+the FC was related to onActivityForResult(). there was a string authcode that needed to be surrended with a null check as its either null or containing data.
+it is null when twitter is not used.
+
+all classes are integrated into Travel Post. as of right now I can post to my wall from Travel Post but it requires user interaction.
+committing work so far and will continue to remove user interaction.
+
+increased build but not version as facebook is not complete yet.
\ No newline at end of file
diff --git a/TravelPost/default.properties b/TravelPost/default.properties
index 77f7ad7..3d3665e 100644
--- a/TravelPost/default.properties
+++ b/TravelPost/default.properties
@@ -9,3 +9,6 @@
# Project target.
target=Google Inc.:Google APIs:9
+proguard.config=proguard.cfg
+android.library.reference.1=../FacebookLib/
+
diff --git a/TravelPost/gen/com/TwentyCodes/android/TravelPost/R.java b/TravelPost/gen/com/TwentyCodes/android/TravelPost/R.java
index ac24355..a81f12e 100644
--- a/TravelPost/gen/com/TwentyCodes/android/TravelPost/R.java
+++ b/TravelPost/gen/com/TwentyCodes/android/TravelPost/R.java
@@ -11,9 +11,10 @@ public final class R {
public static final class attr {
}
public static final class drawable {
- public static final int icon=0x7f020000;
- public static final int sign_post_icon=0x7f020001;
- public static final int sign_post_icon_mod=0x7f020002;
+ public static final int facebook_icon=0x7f020000;
+ public static final int icon=0x7f020001;
+ public static final int sign_post_icon=0x7f020002;
+ public static final int sign_post_icon_mod=0x7f020003;
}
public static final class id {
public static final int authCode=0x7f060002;
@@ -22,8 +23,9 @@ public final class R {
public static final int widgetbutton=0x7f060000;
}
public static final class layout {
- public static final int travelpostwidget=0x7f030000;
- public static final int webauth=0x7f030001;
+ public static final int main=0x7f030000;
+ public static final int travelpostwidget=0x7f030001;
+ public static final int webauth=0x7f030002;
}
public static final class string {
public static final int app_name=0x7f050001;
diff --git a/TravelPost/res/xml/settings.xml b/TravelPost/res/xml/settings.xml
index 3990646..4e1de67 100644
--- a/TravelPost/res/xml/settings.xml
+++ b/TravelPost/res/xml/settings.xml
@@ -1,21 +1,17 @@
-
-
-
-
-
-
-
-
+
+
+
+
-
-
-
-
-
-
-
-
+
+
diff --git a/TravelPost/src/com/TwentyCodes/android/Facebook/BaseDialogListener.java b/TravelPost/src/com/TwentyCodes/android/Facebook/BaseDialogListener.java
new file mode 100644
index 0000000..d23a8c3
--- /dev/null
+++ b/TravelPost/src/com/TwentyCodes/android/Facebook/BaseDialogListener.java
@@ -0,0 +1,25 @@
+package com.TwentyCodes.android.Facebook;
+
+import com.facebook.android.DialogError;
+import com.facebook.android.Facebook.DialogListener;
+import com.facebook.android.FacebookError;
+
+/**
+ * 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() {
+ }
+
+}
diff --git a/TravelPost/src/com/TwentyCodes/android/Facebook/BaseRequestListener.java b/TravelPost/src/com/TwentyCodes/android/Facebook/BaseRequestListener.java
new file mode 100644
index 0000000..2acd533
--- /dev/null
+++ b/TravelPost/src/com/TwentyCodes/android/Facebook/BaseRequestListener.java
@@ -0,0 +1,41 @@
+package com.TwentyCodes.android.Facebook;
+
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.net.MalformedURLException;
+
+import android.util.Log;
+
+import com.facebook.android.AsyncFacebookRunner.RequestListener;
+import com.facebook.android.FacebookError;
+
+/**
+ * Skeleton base class for RequestListeners, providing default error
+ * handling. Applications should handle these error conditions.
+ *
+ */
+public abstract class BaseRequestListener implements RequestListener {
+
+ public void onFacebookError(FacebookError e, final Object state) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+ public void onFileNotFoundException(FileNotFoundException e,
+ final Object state) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+ public void onIOException(IOException e, final Object state) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+ public void onMalformedURLException(MalformedURLException e,
+ final Object state) {
+ Log.e("Facebook", e.getMessage());
+ e.printStackTrace();
+ }
+
+}
diff --git a/TravelPost/src/com/TwentyCodes/android/Facebook/FacebookAuth.java b/TravelPost/src/com/TwentyCodes/android/Facebook/FacebookAuth.java
new file mode 100644
index 0000000..3172bc3
--- /dev/null
+++ b/TravelPost/src/com/TwentyCodes/android/Facebook/FacebookAuth.java
@@ -0,0 +1,206 @@
+package com.TwentyCodes.android.Facebook;
+
+import org.json.JSONException;
+import org.json.JSONObject;
+
+import android.app.Activity;
+import android.content.Context;
+import android.os.Bundle;
+import android.os.Handler;
+import android.util.Log;
+
+import com.TwentyCodes.android.Facebook.SessionEvents.AuthListener;
+import com.TwentyCodes.android.Facebook.SessionEvents.LogoutListener;
+import com.facebook.android.AsyncFacebookRunner;
+import com.facebook.android.DialogError;
+import com.facebook.android.Facebook;
+import com.facebook.android.FacebookError;
+import com.facebook.android.Util;
+import com.facebook.android.Facebook.DialogListener;
+
+/**
+ * This class handles SSO for facebook.
+ * @author warren
+ *
+ */
+
+public class FacebookAuth {
+
+ private static final String[] PERMISSIONS = new String[] {"publish_stream"};
+ /* Permissions that are prompted to the user for authorization */
+ private static final String APP_ID = "160048987380834";
+ /* This is the api key for the facebook application. See /FacebookLib/APIS.txt */
+ private static final String TAG = "FacebookAuth";
+ private Facebook mFb;
+ private AsyncFacebookRunner mAsyncRunner;
+ private Context mCtx;
+ private Activity mActivity;
+ private Handler mHandler;
+
+ /**
+ * Constructor
+ * @param Context - context to work in
+ * @param Activity - current displayed activity
+ */
+
+ public FacebookAuth(Context context, Activity activity) {
+ mCtx = context;
+ mActivity = activity;
+ mHandler = new Handler();
+ mFb = new Facebook(APP_ID);
+ mAsyncRunner = new AsyncFacebookRunner(mFb);
+ SessionStore.restore(mFb, mCtx);
+ SessionEvents.addAuthListener(new FacebookAuthListener());
+ SessionEvents.addLogoutListener(new FacebookLogoutListener());
+ }
+
+ /**
+ * This method initializes the authorization process.
+ */
+
+ public void init() {
+ Log.i(TAG, "init()");
+ if (mFb.isSessionValid()) {
+ SessionEvents.onLogoutBegin();
+ mAsyncRunner.logout(mCtx, new LogoutRequestListener());
+ } else {
+ mFb.authorize(mActivity, PERMISSIONS, new LoginDialogListener());
+ }
+ }
+
+ /**
+ * This method posts a message to the wall
+ * @param message
+ */
+
+ public void postToWall(String message) {
+ Log.i(TAG, "postToWall()");
+ Bundle parameters = new Bundle();
+ parameters.putString("message", message);
+ mFb.dialog(mCtx, "stream.publish", parameters, new FacebookDialogListener());
+ }
+
+ /**
+ * Handles facebook authorization callbacks.
+ * @author warren
+ *
+ */
+
+ private class FacebookAuthListener implements AuthListener {
+
+ @Override
+ public void onAuthSucceed() {
+ SessionStore.save(mFb, mCtx);
+ Log.i(TAG, "FacebookAuthListener.onAuthSucceed()");
+
+ }
+
+ @Override
+ public void onAuthFail(String error) {
+ // TODO Auto-generated method stub
+
+ }
+
+ }
+
+ /**
+ * Handles facebook logout callbacks
+ * @author warren
+ *
+ */
+
+ private class FacebookLogoutListener implements LogoutListener {
+
+ @Override
+ public void onLogoutBegin() {
+ // TODO Auto-generated method stub
+
+ }
+
+ @Override
+ public void onLogoutFinish() {
+ SessionStore.clear(mCtx);
+ Log.i(TAG, "FacebookLogoutListener.logout succeeded");
+
+ }
+
+ }
+
+ /**
+ * Handles facebook login callbacks
+ * @author warren
+ *
+ */
+
+ private final class LoginDialogListener implements DialogListener {
+ public void onComplete(Bundle values) {
+ SessionEvents.onLoginSuccess();
+ Log.i(TAG, "LoginDialogListener.onComplete.login success");
+ }
+
+ public void onFacebookError(FacebookError error) {
+ SessionEvents.onLoginError(error.getMessage());
+ Log.e(TAG, "LoginDialogListener.error: " + error.getMessage());
+ }
+
+ public void onError(DialogError error) {
+ SessionEvents.onLoginError(error.getMessage());
+ Log.e(TAG, "LoginDialogListener.error: " + error.getMessage());
+ }
+
+ public void onCancel() {
+ SessionEvents.onLoginError("Action Canceled");
+ Log.e(TAG, "LoginDialogListener.login cancelled");
+ }
+ }
+
+ /**
+ * Listens for requests to log out.
+ * @author warren
+ *
+ */
+
+ private class LogoutRequestListener extends BaseRequestListener {
+ public void onComplete(String response, final Object state) {
+ // callback should be run in the original thread,
+ // not the background thread
+ mHandler.post(new Runnable() {
+ public void run() {
+ SessionEvents.onLogoutFinish();
+ }
+ });
+ }
+ }
+
+ /**
+ * Listens for dialogs to post text to wall
+ * @author warren
+ *
+ */
+
+ public class FacebookDialogListener extends BaseDialogListener {
+
+ public void onComplete(Bundle values) {
+ final String postId = values.getString("post_id");
+ if (postId != null) {
+ Log.d("Facebook-Example", "Dialog Success! post_id=" + postId);
+ mAsyncRunner.request(postId, new WallPostRequestListener());
+ } else {
+ Log.d("Facebook-Example", "No wall post made");
+ }
+ }
+ }
+
+ /**
+ * Listens for posts to the wall
+ * @author warren
+ *
+ */
+
+ public class WallPostRequestListener extends BaseRequestListener {
+
+ public void onComplete(final String response, final Object state) {
+ Log.i(TAG, "WallPostRequestListener.post successful");
+ }
+ }
+}
diff --git a/TravelPost/src/com/TwentyCodes/android/Facebook/SessionEvents.java b/TravelPost/src/com/TwentyCodes/android/Facebook/SessionEvents.java
new file mode 100644
index 0000000..57cbe73
--- /dev/null
+++ b/TravelPost/src/com/TwentyCodes/android/Facebook/SessionEvents.java
@@ -0,0 +1,146 @@
+/*
+ * 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.TwentyCodes.android.Facebook;
+
+import java.util.LinkedList;
+
+public class SessionEvents {
+
+ private static LinkedList mAuthListeners =
+ new LinkedList();
+ private static LinkedList mLogoutListeners =
+ new LinkedList();
+
+ /**
+ * 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();
+ }
+
+}
diff --git a/TravelPost/src/com/TwentyCodes/android/Facebook/SessionStore.java b/TravelPost/src/com/TwentyCodes/android/Facebook/SessionStore.java
new file mode 100644
index 0000000..f3d2cf2
--- /dev/null
+++ b/TravelPost/src/com/TwentyCodes/android/Facebook/SessionStore.java
@@ -0,0 +1,53 @@
+/*
+ * 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.TwentyCodes.android.Facebook;
+
+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 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 void clear(Context context) {
+ Editor editor =
+ context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
+ editor.clear();
+ editor.commit();
+ }
+
+}
diff --git a/TravelPost/src/com/TwentyCodes/android/TravelPost/TravelPost.java b/TravelPost/src/com/TwentyCodes/android/TravelPost/TravelPost.java
index 6e14279..7a2e55c 100644
--- a/TravelPost/src/com/TwentyCodes/android/TravelPost/TravelPost.java
+++ b/TravelPost/src/com/TwentyCodes/android/TravelPost/TravelPost.java
@@ -6,6 +6,8 @@
*/
package com.TwentyCodes.android.TravelPost;
+import com.TwentyCodes.android.Facebook.FacebookAuth;
+
import twitter4j.TwitterException;
import android.app.Activity;
import android.content.Intent;
@@ -45,16 +47,25 @@ public class TravelPost extends PreferenceActivity implements OnPreferenceClickL
this.addPreferencesFromResource(R.xml.settings);
this.findPreference("twitter_sign_in").setOnPreferenceClickListener(this);
+ this.findPreference("facebook_sign_in").setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceClick(Preference preference) {
- mTwitterServices = new TwitterServices(this);
- try {
- this.startActivityForResult(new Intent(this, WebAuth.class).putExtra(WebAuth.AUTH_URL, mTwitterServices.getAuthorizationURL()), TWITTER_AUTH_REQUEST_CODE);
- } catch (TwitterException e) {
- e.printStackTrace();
+ if (preference.getKey().equals("twitter_sign_in")) {
+ mTwitterServices = new TwitterServices(this);
+ try {
+ this.startActivityForResult(new Intent(this, WebAuth.class).putExtra(WebAuth.AUTH_URL, mTwitterServices.getAuthorizationURL()), TWITTER_AUTH_REQUEST_CODE);
+ } catch (TwitterException e) {
+ e.printStackTrace();
+ }
+ } else if (preference.getKey().equals("facebook_sign_in")) {
+ Log.i(TAG, "onPreferenceClick.Facebook sign in clicked");
+ FacebookAuth auth = new FacebookAuth(this, this);
+ auth.init();
+ auth.postToWall("Woot woot!");
}
+
return false;
}
@@ -71,7 +82,11 @@ public class TravelPost extends PreferenceActivity implements OnPreferenceClickL
String authCode = null;
if(data != null){
authCode = data.getStringExtra(WebAuth.AUTH_CODE);
- Log.d(TAG, authCode);
+ if (authCode != null) {
+ Log.d(TAG, authCode);
+ } else {
+ Log.d(TAG, "onActivityForResult.authCode is null");
+ }
} else
Log.e(TAG, "WebAuth result was null!!!");
if(resultCode == Activity.RESULT_OK)