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.AndroidNature org.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)