/* * Copyright 2009 Codecarpet * * 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.codecarpet.fbconnect; import java.io.IOException; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Timer; import java.util.TimerTask; import temporary.CcDate; import android.content.Context; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.text.format.DateFormat; import android.util.Log; /** * An FBSession represents a single user's authenticated session for a Facebook application. * * To create a session, you must use the session key of your application (which can be found on the Facebook developer * website). You may then use the login dialog to ask the user to enter their email address and password. If successful, * you will get back a session key which can be used to make requests to the Facebook API. * * Session keys are cached and stored on the disk of the device so that you do not need to ask the user to login every * time they launch the app. To restore the last active session, call the resume method after instantiating your * session. */ public class FBSession { private static final String PREFS_NAME = "FBSessionPreferences"; // ///////////////////////////////////////////////////////////////////////////////////////////////// // global private static final String kAPIRestURL = "http://api.facebook.com/restserver.php"; private static final String kAPIRestSecureURL = "https://api.facebook.com/restserver.php"; private static final int kMaxBurstRequests = 3; private static final long kBurstDuration = 2; // NSTimeInterval - time interval in seconds private static FBSession sharedSession; // ///////////////////////////////////////////////////////////////////////////////////////////////// private List<FBSessionDelegate> _delegates; private String _apiKey; private String _apiSecret; private String _getSessionProxy; private Long _uid; private String _sessionKey; private String _sessionSecret; private Date _expirationDate; private List<FBRequest> _requestQueue; private Date _lastRequestTime; private int _requestBurstCount; private Timer _requestTimer; // ///////////////////////////////////////////////////////////////////////////////////////////////// // constructor private FBSession(String key, String secret, String getSessionProxy) { _delegates = new ArrayList<FBSessionDelegate>();//FBConnectGlobal.FBCreateNonRetainingArray(); _apiKey = key; _apiSecret = secret; _getSessionProxy = getSessionProxy; _uid = Long.valueOf(0); _sessionKey = null; _sessionSecret = null; _expirationDate = null; _requestQueue = new ArrayList<FBRequest>(); _lastRequestTime = new Date(); _requestBurstCount = 0; _requestTimer = null; } /** * Constructs a session for an application. * * @param secret * the application secret (optional) * @param getSessionProxy * a url to that proxies auth.getSession (optional) */ private static FBSession initWithKey(String key, String secret, String getSessionProxy) { FBSession instance = new FBSession(key, secret, getSessionProxy); if (sharedSession == null) { sharedSession = instance; } return instance; } // ///////////////////////////////////////////////////////////////////////////////////////////////// // class public /** * The globally shared session instance. */ public static FBSession getSession() { return sharedSession; } /** * Sets the globally shared session instance. * * This session is not retained, so you are still responsible for retaining it yourself. The first session that is * created is automatically stored here. */ public static void setSession(FBSession session) { sharedSession = session; } /** * Constructs a session and stores it as the globally shared session instance. * * @param secret * the application secret (optional) */ public static FBSession getSessionForApplication_secret(String key, String secret, FBSessionDelegate delegate) { FBSession session = initWithKey(key, /* secret */secret, /* getSessionProxy */null); session.getDelegates().add(delegate); return session; } /** * Constructs a session and stores it as the global singleton. * * @param getSessionProxy * a url to that proxies auth.getSession (optional) */ public static FBSession getSessionForApplication_getSessionProxy(String key, String getSessionProxy, FBSessionDelegate delegate) { FBSession session = initWithKey(key, /* secret */null, /* getSessionProxy */getSessionProxy); session.getDelegates().add(delegate); return session; } // ///////////////////////////////////////////////////////////////////////////////////////////////// // private public void save(Context context) { SharedPreferences defaults = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); Editor editor = defaults.edit(); if (_uid != null) { editor.putLong("FBUserId", _uid); } else { editor.remove("FBUserId"); } if (_sessionKey != null) { editor.putString("FBSessionKey", _sessionKey); } else { editor.remove("FBSessionKey"); } if (_sessionSecret != null) { editor.putString("FBSessionSecret", _sessionSecret); } else { editor.remove("FBSessionSecret"); } if (_expirationDate != null) { editor.putLong("FBSessionExpires", _expirationDate.getTime()); } else { editor.remove("FBSessionExpires"); } editor.commit(); } public void unsave(Context context) { Editor defaults = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE).edit(); defaults.remove("FBUserId"); defaults.remove("FBSessionKey"); defaults.remove("FBSessionSecret"); defaults.remove("FBSessionExpires"); defaults.commit(); } private void startFlushTimer() { if (_requestTimer == null) { long timeIntervalSinceNow = CcDate.timeIntervalSinceNow(_lastRequestTime); long t = kBurstDuration + timeIntervalSinceNow; _requestTimer = new Timer(); _requestTimer.schedule(requestTimerReady , t * 1000); } } private void enqueueRequest(FBRequest request) { _requestQueue.add(request); startFlushTimer(); } private boolean performRequest(FBRequest request, boolean enqueue) { // Stagger requests that happen in short bursts to prevent the server from rejecting // them for making too many requests in a short time long t = 0; boolean burst = false; if (_lastRequestTime != null) { t = new Date().getTime() - _lastRequestTime.getTime(); burst = t < kBurstDuration; } if (_lastRequestTime != null && burst && ++_requestBurstCount > kMaxBurstRequests) { if (enqueue) { enqueueRequest(request); } return false; } else { try { request.connect(); } catch (IOException e) { e.printStackTrace(); } if (!burst) { _requestBurstCount = 0; _lastRequestTime = request.getTimestamp(); } } return true; } private void flushRequestQueue() { while (_requestQueue.size() > 0) { FBRequest request = _requestQueue.get(0); if (performRequest(request, false)) { _requestQueue.remove(0); } else { startFlushTimer(); break; } } } private TimerTask requestTimerReady = new TimerTask() { public void run() { _requestTimer = null; flushRequestQueue(); } }; // ///////////////////////////////////////////////////////////////////////////////////////////////// // public /** * The URL used for API HTTP requests. */ public String getApiURL() { return kAPIRestURL; } /** * The URL used for secure API HTTP requests. */ public String getApiSecureURL() { return kAPIRestSecureURL; } /** * Determines if the session is active and connected to a user. */ public boolean isConnected() { return _sessionKey != null; } /** * Begins a session for a user with a given key and secret. */ public void begin(Context context, Long uid, String sessionKey, String sessionSecret, Date expires) { _uid = uid; _sessionKey = sessionKey; _sessionSecret = sessionSecret; _expirationDate = (Date) expires.clone(); save(context); } /** * Resumes a previous session whose uid, session key, and secret are cached on disk. */ public boolean resume(Context context) { SharedPreferences defaults = context.getSharedPreferences(PREFS_NAME, Context.MODE_PRIVATE); Long uid = defaults.getLong("FBUserId", 0); Log.d("FBSession", "FBUserId = " + uid); if (uid != 0) { boolean loadSession = false; long expires = defaults.getLong("FBSessionExpires", 0); if (expires > 0) { Date expirationDate = new Date(expires); Log.d("FBSession", "expirationDate = " + expirationDate != null ? expirationDate.toLocaleString() : "null"); long timeIntervalSinceNow = CcDate.timeIntervalSinceNow(expirationDate); Log.d("FBSession", "Time interval since now = " + timeIntervalSinceNow); if (expirationDate == null || timeIntervalSinceNow <= 0) { loadSession = true; } } else { Log.d("FBSession", "FBSessionExpires does not exist. Loading session..."); loadSession = true; } if (loadSession) { Log.d("FBSession", "Session can be loaded. Loading..."); _uid = uid; _sessionKey = defaults.getString("FBSessionKey", null); _sessionSecret = defaults.getString("FBSessionSecret", null); for (FBSessionDelegate delegate : _delegates) { delegate.session_didLogin(this, uid); } return true; } } return false; } /** * Ends the current session and deletes the uid, session key, and secret from disk. */ public void logout(Context context) { if (_sessionKey != null) { for (FBSessionDelegate delegate : _delegates) { delegate.session_willLogout(this, _uid); } // TODO Remove cookies that UIWebView may have stored // Remove cookies that UIWebView may have stored // NSHTTPCookieStorage* cookies = [NSHTTPCookieStorage sharedHTTPCookieStorage]; // NSArray* facebookCookies = [cookies cookiesForURL: // [NSURL URLWithString:@"http://login.facebook.com"]]; // for (NSHTTPCookie* cookie in facebookCookies) { // [cookies deleteCookie:cookie]; // } _uid = Long.valueOf(0); _sessionKey = null; _sessionSecret = null; _expirationDate = null; unsave(context); for (FBSessionDelegate delegate : _delegates) { delegate.sessionDidLogout(this); } } else { unsave(context); } } /** * Sends a fully configured request to the server for execution. */ public void send(FBRequest request) { performRequest(request, true); } // ///////////////////////////////////////////////////////////////////////////////////////////////// // instance variables getters and setters /** * Delegates which implement FBSessionDelegate. */ public List<FBSessionDelegate> getDelegates() { return _delegates; } /** * Your application's API key, as passed to the constructor. */ public String getApiKey() { return _apiKey; } /** * Your application's API secret, as passed to the constructor. */ public String getApiSecret() { return _apiSecret; } /** * The URL to call to create a session key after login. * * This is an alternative to calling auth.getSession directly using the secret key. */ public String getGetSessionProxy() { return _getSessionProxy; } /** * The current user's Facebook id. */ public Long getUid() { return _uid; } /** * The current user's session key. */ public String getSessionKey() { return _sessionKey; } /** * The current user's session secret. */ public String getSessionSecret() { return _sessionSecret; } /** * The expiration date of the session key. */ public Date getExpirationDate() { return _expirationDate; } // ///////////////////////////////////////////////////////////////////////////////////////////////// public static abstract class FBSessionDelegate { /** * Called when a user has successfully logged in and begun a session. */ protected void session_didLogin(FBSession session, Long uid) {} /** * Called when a session is about to log out. */ protected void session_willLogout(FBSession session, Long uid) {} /** * Called when a session has logged out. */ protected void sessionDidLogout(FBSession session) {} } }