diff --git a/.gitignore b/.gitignore index 03caa301a..cfada7883 100644 --- a/.gitignore +++ b/.gitignore @@ -11,3 +11,19 @@ test/www/*.js *.xcworkspace !default.xcworkspace xcuserdata + +# Android Projects + +*.apk +*.ap_ +*.dex +*.class + +bin/ +gen/ + +.gradle/ +build/ + +local.properties +proguard/ \ No newline at end of file diff --git a/README.md b/README.md index 8369d0082..36df38cb4 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,10 @@ This is the official plugin for Facebook in Apache Cordova/PhoneGap! The Facebook plugin for [Apache Cordova](http://incubator.apache.org/cordova/) allows you to use the same JavaScript code in your Cordova application as you use in your web application. However, unlike in the browser, the Cordova application will use the native Facebook app to perform Single Sign On for the user. If this is not possible then the sign on will degrade gracefully using the standard dialog based authentication. -* Supported on PhoneGap (Cordova) v3.0.0 and above. +* Supported on PhoneGap (Cordova) v3.3.0 and above. +* This plugin is build for + * iOS FacebookSDK 3.11.1 + * Android FacebookSDK 3.6.0 ## Facebook Requirements and Set-Up @@ -178,14 +181,104 @@ Make sure you add the scheme to your [PROJECTNAME]-Info.plist (located as one of ## Automatic Installation -This plugin is based on [plugman](https://git-wip-us.apache.org/repos/asf?p=cordova-plugman.git;a=summary). To install it to your app, simply execute plugman as follows; It does not currently work with plugman at all. WORK IN PROGRESS - plugman install --platform [PLATFORM] --project [TARGET-PATH] --plugin [PLUGIN-PATH] --variable APP_ID="[APP_ID]" --variable APP_NAME="[APP_NAME]" +This plugin is based on [plugman](https://git-wip-us.apache.org/repos/asf?p=cordova-plugman.git;a=summary). To install it to your app, execute the following (and replace variables where necessary)... + +### iOS + + + cordova create myApp + + cd myApp/ + + cordova platform add ios + + cordova -d plugin add /Users/your/path/here/phonegap-facebook-plugin --variable APP_ID="123456789" --variable APP_NAME="myApplication" + +### Android + + cordova create myApp + + cd myApp/ + + cordova platform add android + + cordova -d plugin add /Users/your/path/here/phonegap-facebook-plugin --variable APP_ID="123456789" --variable APP_NAME="myApplication" + +**Android requires an additional step which is to reference the FacebookSDK project as a library to your project.** + +Open your project in Eclipse (New > Project... Existing Android project from source), import everything (***see Img. 1***). + +![image](./android_setup_1.png) ***Img. 1*** + +In Eclipse, right click your project folder in the left-had column. Select "Properties", select Android in the left column and in the right side of the window add FacebookSDK as a library (***see Img. 2***). + +![image](./android_setup_2.png) ***Img. 2*** + +## JS API + +###facebookConnectPlugin.login(Function success, Function failure) + +Success function returns an Object like; + + { + id: "634565435", + lastName: "bob" + ... + } - where - [PLATFORM] = ios or android - [TARGET-PATH] = path to folder containing your phonegap project - [PLUGIN-PATH] = path to folder containing this plugin - [APP_ID] = Your APP_ID as registered on Facebook +Failure function returns an error String. +###facebookConnectPlugin.logout(Function success, Function failure) + +###facebookConnectPlugin.getLoginStatus(Function success, Function failure) + +Success function returns a status String. + +###facebookConnectPlugin.showDialog(JSONObject options, Function success, Function failure) + +Example options: + + { + method: "feed" | "apprequests" + } + +Success function returns an Object with `postId` as String. +Failure function returns an error String. + +## Sample JavaScript Code + +### Login + +In your `onDeviceReady` event add the following + + var fbLoginSuccess = function (userData) { + alert("UserInfo: " + JSON.stringify(userData)); + } + + facebookConnectPlugin.login(["basic_info"], + fbLoginSuccess, + function (error) { alert("" + error) } + ); + +### Get Status & Post-to-wall + +For a more instructive example change the above `fbLoginSuccess` to; + + var fbLoginSuccess = function (userData) { + alert("UserInfo: " + JSON.stringify(userData)); + facebookConnectPlugin.getLoginStatus( + function (status) { + alert("current status: " + JSON.stringify(status)); + + var options = { method:"feed" }; + facebookConnectPlugin.showDialog(options, + function (result) { + alert("Posted. " + JSON.stringify(result)); }, + function (e) { + alert("Failed: " + e); + }); + } + ); + }; diff --git a/android_setup_1.png b/android_setup_1.png new file mode 100644 index 000000000..2efb81311 Binary files /dev/null and b/android_setup_1.png differ diff --git a/android_setup_2.png b/android_setup_2.png new file mode 100644 index 000000000..02d0fc364 Binary files /dev/null and b/android_setup_2.png differ diff --git a/plugin.xml b/plugin.xml index 8c7a7200a..074804eb9 100644 --- a/plugin.xml +++ b/plugin.xml @@ -48,30 +48,27 @@ - + + + + + $APP_ID + $APP_NAME + + + + + + - - - - - - - - + + - - - - - - - - diff --git a/src/android/ConnectPlugin.java b/src/android/ConnectPlugin.java index a8b35a5a3..ac2436941 100644 --- a/src/android/ConnectPlugin.java +++ b/src/android/ConnectPlugin.java @@ -1,317 +1,365 @@ package org.apache.cordova.facebook; -import java.io.IOException; -import java.net.MalformedURLException; +import java.util.Arrays; +import java.util.HashSet; import java.util.Iterator; +import java.util.List; +import java.util.Set; +import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.PluginResult; -import org.apache.cordova.CallbackContext; +import org.apache.cordova.CordovaWebView; +import org.apache.cordova.CordovaInterface; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.content.Intent; -import android.content.SharedPreferences; import android.os.Bundle; -import android.preference.PreferenceManager; import android.util.Log; -import com.facebook.android.DialogError; -import com.facebook.android.Facebook; -import com.facebook.android.Facebook.DialogListener; -import com.facebook.android.FacebookError; +import com.facebook.FacebookDialogException; +import com.facebook.FacebookException; +import com.facebook.FacebookOperationCanceledException; +import com.facebook.Request; +import com.facebook.Response; +import com.facebook.Session; +import com.facebook.SessionState; +import com.facebook.model.GraphObject; +import com.facebook.model.GraphUser; +import com.facebook.widget.WebDialog; +import com.facebook.widget.WebDialog.OnCompleteListener; public class ConnectPlugin extends CordovaPlugin { - public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled"; + private static final String PUBLISH_PERMISSION_PREFIX = "publish"; + private static final String MANAGE_PERMISSION_PREFIX = "manage"; + @SuppressWarnings("serial") + private static final Set OTHER_PUBLISH_PERMISSIONS = new HashSet() {{ + add("ads_management"); + add("create_event"); + add("rsvp_event"); + }}; private final String TAG = "ConnectPlugin"; - private Facebook facebook; - private String userId; - //used for dialog auth - private String[] permissions = new String[] {}; - private CallbackContext cb; + private String applicationId = null; + private CallbackContext loginContext = null; + private CallbackContext showDialogContext = null; private Bundle paramBundle; private String method; + @Override + public void initialize(CordovaInterface cordova, CordovaWebView webView) { + + int appResId = cordova.getActivity().getResources().getIdentifier("fb_app_id", "string", cordova.getActivity().getPackageName()); + applicationId = cordova.getActivity().getString(appResId); + + // Set up the activity result callback to this class + cordova.setActivityResultCallback(this); + + // Open a session if we have one cached + Session session = new Session.Builder(cordova.getActivity()) + .setApplicationId(applicationId) + .build(); + if (session.getState() == SessionState.CREATED_TOKEN_LOADED) { + Session.setActiveSession(session); + // - Create the request + Session.OpenRequest openRequest = new Session.OpenRequest(cordova.getActivity()); + // - Set the status change call back + openRequest.setCallback(new Session.StatusCallback() { + @Override + public void call(Session session, SessionState state, Exception exception) { + onSessionStateChange(state, exception); + } + }); + session.openForRead(openRequest); + } + + // If we have a valid open session, get user's info + if (session != null && session.isOpened()) { + // Call this method to initialize the session state info + onSessionStateChange(session.getState(), null); + } + super.initialize(cordova, webView); + } + + @Override + public void onActivityResult(int requestCode, int resultCode, Intent intent) { + super.onActivityResult(requestCode, resultCode, intent); + Log.d(TAG, "activity result in plugin"); + Session.getActiveSession().onActivityResult(cordova.getActivity(), requestCode, resultCode, intent); + } + @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { - PluginResult pr = new PluginResult(PluginResult.Status.NO_RESULT); - pr.setKeepCallback(true); - cb = callbackContext; - if (action.equals("init")) { + if (action.equals("login")) { + Log.d(TAG, "login FB"); + // Get the permissions + String[] arrayPermissions = new String[args.length()]; + for (int i=0; i permissions = null; + if (arrayPermissions.length > 0) { + permissions = Arrays.asList(arrayPermissions); + } + + // Get the currently active session + Session session = Session.getActiveSession(); + + // Set a pending callback to cordova + loginContext = callbackContext; + PluginResult pr = new PluginResult(PluginResult.Status.NO_RESULT); + pr.setKeepCallback(true); + loginContext.sendPluginResult(pr); + + // Check if the active session is open + if (session != null && session.isOpened()) { + // Reauthorize flow + boolean publishPermissions = false; + boolean readPermissions = false; + // Figure out if this will be a read or publish reauthorize + if (permissions == null) { + // No permissions, read + readPermissions = true; + } + // Loop through the permissions to see what + // is being requested + for (String permission : arrayPermissions) { + if (isPublishPermission(permission)) { + publishPermissions = true; + } else { + readPermissions = true; + } + // Break if we have a mixed bag, as this is an error + if (publishPermissions && readPermissions) { + break; + } + } + + if (publishPermissions && readPermissions) { + callbackContext.error("Cannot ask for both read and publish permissions."); + } else { + // Set up the new permissions request + Session.NewPermissionsRequest newPermissionsRequest = new Session.NewPermissionsRequest(cordova.getActivity(), + permissions); + // Set up the activity result callback to this class + cordova.setActivityResultCallback(this); + // Check for write permissions, the default is read (empty) + if (publishPermissions) { + // Request new publish permissions + session.requestNewPublishPermissions(newPermissionsRequest); + } else { + // Request new read permissions + session.requestNewReadPermissions(newPermissionsRequest); + } + } + } else { + // Initial login, build a new session open request. + + // - Create a new session and set the application ID + session = new Session.Builder(cordova.getActivity()) + .setApplicationId(applicationId) + .build(); + Session.setActiveSession(session); + // - Create the request + Session.OpenRequest openRequest = new Session.OpenRequest(cordova.getActivity()); + // - Set the permissions + openRequest.setPermissions(permissions); + // - Set the status change call back + openRequest.setCallback(new Session.StatusCallback() { + @Override + public void call(Session session, + SessionState state, + Exception exception) { + onSessionStateChange(state, exception); + } + }); + + // Can only ask for read permissions initially + session.openForRead(openRequest); + } + return true; + } else if (action.equals("logout")) { + + Session session = Session.getActiveSession(); + if (session != null) { + if (session.isOpened()) { + session.closeAndClearTokenInformation(); + callbackContext.success(); + } else { + // Session not open + callbackContext.error("Session not open."); + } + } else { + callbackContext + .error("No valid session found, must call init and login before logout."); + } + return true; + } else if (action.equals("getLoginStatus")) { + callbackContext.success(Session.getActiveSession().getState().toString()); + return true; + } else if (action.equals("showDialog")) { + Bundle collect = new Bundle(); + JSONObject params = null; try { - String appId = args.getString(0); - - facebook = new Facebook(appId); - - Log.d(TAG, "init: Initializing plugin."); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(cordova.getActivity()); - String access_token = prefs.getString("access_token", null); - Long expires = prefs.getLong("access_expires", -1); - - if (access_token != null && expires != -1) { - this.facebook.setAccessToken(access_token); - this.facebook.setAccessExpires(expires); - try { - JSONObject o = new JSONObject(this.facebook.request("/me")); - this.userId = o.getString("id"); - } catch (MalformedURLException e) { - - e.printStackTrace(); - } catch (IOException e) { - - e.printStackTrace(); - } catch (JSONException e) { - - e.printStackTrace(); - } - } - - if(facebook.isSessionValid() && this.userId != null) { - cb.success(this.getResponse()); - return true; - } - else { - cb.sendPluginResult(new PluginResult(PluginResult.Status.NO_RESULT)); - return true; - } + params = args.getJSONObject(0); } catch (JSONException e) { - - e.printStackTrace(); - cb.error("Invalid JSON args used. expected a string as the first arg."); - return true; + params = new JSONObject(); } - } - - else if (action.equals("login")) { - if (facebook != null) { - final ConnectPlugin me = this; - String[] permissions = new String[args.length()]; - try { - for (int i=0; i iter = params.keys(); + while (iter.hasNext()) { + String key = (String) iter.next(); + if (key.equals("method")) { + try { + this.method = params.getString(key); + } catch (JSONException e) { + Log.w(TAG, "Nonstring method parameter provided to dialog"); } - } catch (JSONException e1) { - - e1.printStackTrace(); - cb.error("Invalid JSON args used. Expected a string array of permissions."); - return true; - } - cordova.setActivityResultCallback(this); - this.permissions = permissions; - Runnable runnable = new Runnable() { - public void run() { - me.facebook.authorize(cordova.getActivity(), me.permissions, new AuthorizeListener(me)); - }; - }; - cordova.getActivity().runOnUiThread(runnable); - } else { - pr = new PluginResult(PluginResult.Status.ERROR, "Must call init before login."); - } - } - - else if (action.equals("logout")) { - if (facebook != null) { - try { - facebook.logout(cordova.getActivity()); - - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.cordova.getActivity()); - prefs.edit().putLong("access_expires", -1).commit(); - prefs.edit().putString("access_token", null).commit(); - } catch (MalformedURLException e) { - - e.printStackTrace(); - pr = new PluginResult(PluginResult.Status.MALFORMED_URL_EXCEPTION, "Error logging out."); - } catch (IOException e) { - - e.printStackTrace(); - pr = new PluginResult(PluginResult.Status.IO_EXCEPTION, "Error logging out."); - } - pr = new PluginResult(PluginResult.Status.OK, getResponse()); - } else { - pr = new PluginResult(PluginResult.Status.ERROR, "Must call init before logout."); - } - } - - else if (action.equals("getLoginStatus")) { - if (facebook != null) { - pr = new PluginResult(PluginResult.Status.OK, getResponse()); - } else { - pr = new PluginResult(PluginResult.Status.ERROR, "Must call init before getLoginStatus."); - } - } - - else if (action.equals("showDialog")) { - if (facebook != null) { - Bundle collect = new Bundle(); - JSONObject params = null; - try { - params = args.getJSONObject(0); - } catch (JSONException e) { - params = new JSONObject(); - } - - final ConnectPlugin me = this; - Iterator iter = params.keys(); - while (iter.hasNext()) { - String key = (String) iter.next(); - if (key.equals("method")) { - try { - this.method = params.getString(key); - } catch (JSONException e) { - Log.w(TAG, "Nonstring method parameter provided to dialog"); - } - } else { - try { - collect.putString(key, params.getString(key)); - } catch (JSONException e) { - // Need to handle JSON parameters - Log.w(TAG, "Nonstring parameter provided to dialog discarded"); - } + } else { + try { + collect.putString(key, params.getString(key)); + } catch (JSONException e) { + // Need to handle JSON parameters + Log.w(TAG, "Nonstring parameter provided to dialog discarded"); } } - this.paramBundle = new Bundle(collect); - Runnable runnable = new Runnable() { - public void run() { - me.facebook.dialog (me.cordova.getActivity(), me.method , me.paramBundle , new UIDialogListener(me)); - }; - }; - cordova.getActivity().runOnUiThread(runnable); -// this.ctx.runOnUiThread(runnable); - } else { - pr = new PluginResult(PluginResult.Status.ERROR, "Must call init before showDialog."); } - - } - - cb.sendPluginResult(pr); - return true; + this.paramBundle = new Bundle(collect); + + // Begin by sending a callback pending notice to Cordova + showDialogContext = callbackContext; + PluginResult pr = new PluginResult(PluginResult.Status.NO_RESULT); + pr.setKeepCallback(true); + showDialogContext.sendPluginResult(pr); + + // Setup callback context + final OnCompleteListener dialogCallback = new OnCompleteListener() { + + @Override + public void onComplete(Bundle values, FacebookException exception) { + String errMsg; + if (exception != null) { + // User clicked "x" + if (exception instanceof FacebookOperationCanceledException) { + errMsg = "User cancelled dialog"; + Log.e(TAG, errMsg); + showDialogContext.error(errMsg); + } else if (exception instanceof FacebookDialogException) { + // Dialog error + errMsg = "Dialog error: " + exception.getMessage(); + Log.e(TAG, errMsg); + showDialogContext.error(errMsg); + } else { + // Facebook error + errMsg = "Facebook error: " + exception.getMessage(); + Log.e(TAG, errMsg); + showDialogContext.error(errMsg); + } + } else { + // Handle a successful dialog: + // Send the URL parameters back, for a requests dialog, the "request" parameter + // will include the resulting request id. For a feed dialog, the "post_id" + // parameter will include the resulting post id. + // Note: If the user clicks on the Cancel button, the parameter will be empty + if (values.size() > 0) { + JSONObject response = new JSONObject(); + try { + Set keys = values.keySet(); + for (String key : keys) { + response.put(key, values.get(key)); + } + } catch (JSONException e) { + e.printStackTrace(); + } + showDialogContext.success(response); + } else { + errMsg = "User cancelled dialog"; + Log.e(TAG, errMsg); + showDialogContext.error(errMsg); + } + } + } + }; + + if (this.method.equalsIgnoreCase("feed")) { + Runnable runnable = new Runnable() { + public void run() { + WebDialog feedDialog = (new WebDialog.FeedDialogBuilder( + me.cordova.getActivity(), + Session.getActiveSession(), + paramBundle)) + .setOnCompleteListener(dialogCallback) + .build(); + feedDialog.show(); + }; + + }; + cordova.getActivity().runOnUiThread(runnable); + } else if (this.method.equalsIgnoreCase("apprequests")) { + Runnable runnable = new Runnable() { + public void run() { + WebDialog requestsDialog = (new WebDialog.RequestsDialogBuilder( + me.cordova.getActivity(), + Session.getActiveSession(), + paramBundle)) + .setOnCompleteListener(dialogCallback) + .build(); + requestsDialog.show(); + }; + }; + cordova.getActivity().runOnUiThread(runnable); + } else { + callbackContext.error("Unsupported dialog method."); + } + return true; + } + return false; } - - @Override - public void onActivityResult(int requestCode, int resultCode, Intent data) { - super.onActivityResult(requestCode, resultCode, data); - - facebook.authorizeCallback(requestCode, resultCode, data); - } - - public JSONObject getResponse() { - String response; - if (facebook.isSessionValid()) { - long expiresTimeInterval = facebook.getAccessExpires() - System.currentTimeMillis(); - long expiresIn = (expiresTimeInterval > 0) ? expiresTimeInterval : 0; - response = "{"+ - "\"status\": \"connected\","+ - "\"authResponse\": {"+ - "\"accessToken\": \""+facebook.getAccessToken()+"\","+ - "\"expiresIn\": \""+expiresIn+"\","+ - "\"session_key\": true,"+ - "\"sig\": \"...\","+ - "\"userId\": \""+this.userId+"\""+ - "}"+ - "}"; - } else { - response = "{"+ - "\"status\": \"unknown\""+ - "}"; - } - - try { - return new JSONObject(response); - } catch (JSONException e) { - - e.printStackTrace(); - } - return new JSONObject(); + + private void getUserInfo(final Session session) { + if (cordova != null) { + Request.newMeRequest(session, new Request.GraphUserCallback() { + + @Override + public void onCompleted(GraphUser user, Response response) { + // Create a new result with response data + if (loginContext != null) { + GraphObject graphObject = response.getGraphObject(); + Log.d(TAG, "returning login object " + graphObject.getInnerJSONObject().toString()); + loginContext.success(graphObject.getInnerJSONObject()); + loginContext = null; + } + } + }).executeAsync(); + } } - class UIDialogListener implements DialogListener { - final ConnectPlugin fba; - - public UIDialogListener(ConnectPlugin fba){ - super(); - this.fba = fba; - } - - public void onComplete(Bundle values) { - // Handle a successful dialog - Log.d(TAG,values.toString()); - this.fba.cb.success(); - } - - public void onFacebookError(FacebookError e) { - Log.d(TAG, "facebook error"); - this.fba.cb.error("Facebook error: " + e.getMessage()); - } - - public void onError(DialogError e) { - Log.d(TAG, "other error"); - this.fba.cb.error("Dialog error: " + e.getMessage()); - } - - public void onCancel() { - Log.d(TAG, "cancel"); - this.fba.cb.error("Cancelled"); - } + /* + * Handles session state changes + */ + private void onSessionStateChange(SessionState state, Exception exception) { + final Session session = Session.getActiveSession(); + // Check if the session is open + if (state.isOpened()) { + // Get user info + getUserInfo(session); + } } + + /* + * Checks for publish permissions + */ + private boolean isPublishPermission(String permission) { + return permission != null && + (permission.startsWith(PUBLISH_PERMISSION_PREFIX) || + permission.startsWith(MANAGE_PERMISSION_PREFIX) || + OTHER_PUBLISH_PERMISSIONS.contains(permission)); - class AuthorizeListener implements DialogListener { - final ConnectPlugin fba; - - public AuthorizeListener(ConnectPlugin fba){ - super(); - this.fba = fba; - } - - public void onComplete(Bundle values) { - // Handle a successful login - - String token = this.fba.facebook.getAccessToken(); - long token_expires = this.fba.facebook.getAccessExpires(); - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.fba.cordova.getActivity()); - prefs.edit().putLong("access_expires", token_expires).commit(); - prefs.edit().putString("access_token", token).commit(); - - Log.d(TAG, "authorized"); - - Thread t = new Thread(new Runnable() { - public void run() { - try { - JSONObject o = new JSONObject(fba.facebook.request("/me")); - fba.userId = o.getString("id"); - fba.cb.success(getResponse()); - } catch (MalformedURLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } catch (JSONException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - } - }); - t.start(); - } - - public void onFacebookError(FacebookError e) { - Log.d(TAG, "facebook error"); - this.fba.cb.error("Facebook error: " + e.getMessage()); - } - - public void onError(DialogError e) { - Log.d(TAG, "other error"); - this.fba.cb.error("Dialog error: " + e.getMessage()); - } - - public void onCancel() { - Log.d(TAG, "cancel"); - this.fba.cb.error("Cancelled"); - } } } diff --git a/src/android/facebook/Facebook.java b/src/android/facebook/Facebook.java deleted file mode 100644 index 173f34205..000000000 --- a/src/android/facebook/Facebook.java +++ /dev/null @@ -1,1179 +0,0 @@ -/* - * Copyright 2010 Facebook, Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.facebook.android; - -import java.io.FileNotFoundException; -import java.io.IOException; -import java.net.MalformedURLException; - -import android.Manifest; -import android.app.Activity; -import android.content.ActivityNotFoundException; -import android.content.ComponentName; -import android.content.ContentResolver; -import android.content.Context; -import android.content.Intent; -import android.content.ServiceConnection; -import android.content.SharedPreferences; -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.database.Cursor; -import android.net.Uri; -import android.os.AsyncTask; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Message; -import android.os.Messenger; -import android.os.RemoteException; -import android.text.TextUtils; -import android.webkit.CookieSyncManager; -import org.json.JSONException; -import org.json.JSONObject; - -/** - * Main Facebook object for interacting with the Facebook developer API. - * Provides methods to log in and log out a user, make requests using the REST - * and Graph APIs, and start user interface interactions with the API (such as - * pop-ups promoting for credentials, permissions, stream posts, etc.) - * - * @author Jim Brusstar (jimbru@facebook.com), - * Yariv Sadan (yariv@facebook.com), - * Luke Shepard (lshepard@facebook.com) - */ -public class Facebook { - - // Strings used in the authorization flow - public static final String REDIRECT_URI = "fbconnect://success"; - public static final String CANCEL_URI = "fbconnect://cancel"; - public static final String TOKEN = "access_token"; - public static final String EXPIRES = "expires_in"; - public static final String SINGLE_SIGN_ON_DISABLED = "service_disabled"; - - public static final Uri ATTRIBUTION_ID_CONTENT_URI = - Uri.parse("content://com.facebook.katana.provider.AttributionIdProvider"); - public static final String ATTRIBUTION_ID_COLUMN_NAME = "aid"; - - private static final String ATTRIBUTION_PREFERENCES = "com.facebook.sdk.attributionTracking"; - private static final String PUBLISH_ACTIVITY_PATH = "%s/activities"; - private static final String MOBILE_INSTALL_EVENT = "MOBILE_APP_INSTALL"; - private static final String SUPPORTS_ATTRIBUTION = "supports_attribution"; - private static final String APPLICATION_FIELDS = "fields"; - private static final String ANALYTICS_EVENT = "event"; - private static final String ATTRIBUTION_KEY = "attribution"; - - 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 mLastAccessUpdate = 0; - private long mAccessExpires = 0; - private String mAppId; - - private Activity mAuthActivity; - private String[] mAuthPermissions; - private int mAuthActivityCode; - private DialogListener mAuthDialogListener; - - // If the last time we extended the access token was more than 24 hours ago - // we try to refresh the access token again. - final private long REFRESH_TOKEN_BARRIER = 24L * 60L * 60L * 1000L; - - private boolean shouldAutoPublishInstall = true; - private AutoPublishAsyncTask mAutoPublishAsyncTask = null; - - /** - * 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; - - // fire off an auto-attribution publish if appropriate. - autoPublishAsync(activity.getApplicationContext()); - - // 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 (!validateActivityIntent(activity, intent)) { - return false; - } - - mAuthActivity = activity; - mAuthPermissions = permissions; - mAuthActivityCode = activityCode; - try { - activity.startActivityForResult(intent, activityCode); - } catch (ActivityNotFoundException e) { - didSucceed = false; - } - - return didSucceed; - } - - /** - * Helper to validate an activity intent by resolving and checking the - * provider's package signature. - * - * @param context - * @param intent - * @return true if the service intent resolution happens successfully and the - * signatures match. - */ - private boolean validateActivityIntent(Context context, Intent intent) { - ResolveInfo resolveInfo = - context.getPackageManager().resolveActivity(intent, 0); - if (resolveInfo == null) { - return false; - } - - return validateAppSignatureForPackage( - context, - resolveInfo.activityInfo.packageName); - } - - - /** - * Helper to validate a service intent by resolving and checking the - * provider's package signature. - * - * @param context - * @param intent - * @return true if the service intent resolution happens successfully and the - * signatures match. - */ - private boolean validateServiceIntent(Context context, Intent intent) { - ResolveInfo resolveInfo = - context.getPackageManager().resolveService(intent, 0); - if (resolveInfo == null) { - return false; - } - - return validateAppSignatureForPackage( - context, - resolveInfo.serviceInfo.packageName); - } - - /** - * 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 context - * @param packageName - * @return true if the app's signature matches the expected signature. - */ - private boolean validateAppSignatureForPackage(Context context, - String packageName) { - - PackageInfo packageInfo; - try { - packageInfo = context.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()) { - Util.logd("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) { - Util.logd("Facebook-authorize", "Login failed: " + error); - mAuthDialogListener.onError(error); - } - - public void onFacebookError(FacebookError error) { - Util.logd("Facebook-authorize", "Login failed: " + error); - mAuthDialogListener.onFacebookError(error); - } - - public void onCancel() { - Util.logd("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")) { - Util.logd("Facebook-authorize", "Hosted auth currently " - + "disabled. Retrying dialog auth..."); - startDialogAuth(mAuthActivity, mAuthPermissions); - } else if (error.equals("access_denied") - || error.equals("OAuthAccessDeniedException")) { - Util.logd("Facebook-authorize", "Login canceled by user."); - mAuthDialogListener.onCancel(); - } else { - String description = data.getStringExtra("error_description"); - if (description != null) { - error = error + ":" + description; - } - Util.logd("Facebook-authorize", "Login failed: " + error); - mAuthDialogListener.onFacebookError( - new FacebookError(error)); - } - - // No errors. - } else { - setAccessToken(data.getStringExtra(TOKEN)); - setAccessExpiresIn(data.getStringExtra(EXPIRES)); - if (isSessionValid()) { - Util.logd("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) { - Util.logd("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 { - Util.logd("Facebook-authorize", "Login canceled by user."); - mAuthDialogListener.onCancel(); - } - } - } - } - - /** - * Refresh OAuth access token method. Binds to Facebook for Android - * stand-alone application application to refresh the access token. This - * method tries to connect to the Facebook App which will handle the - * authentication flow, and return a new OAuth access token. This method - * will automatically replace the old token with a new one. 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 that will be used to bind to the Facebook - * RefreshToken Service - * @param serviceListener - * Callback interface for notifying the calling application when - * the refresh request has completed or failed (can be null). In - * case of a success a new token can be found inside the result - * Bundle under Facebook.ACCESS_TOKEN key. - * @return true if the binding to the RefreshToken Service was created - */ - public boolean extendAccessToken(Context context, ServiceListener serviceListener) { - Intent intent = new Intent(); - - intent.setClassName("com.facebook.katana", - "com.facebook.katana.platform.TokenRefreshService"); - - // Verify that the application whose package name is - // com.facebook.katana - // has the expected FB app signature. - if (!validateServiceIntent(context, intent)) { - return false; - } - - return context.bindService(intent, - new TokenRefreshServiceConnection(context, serviceListener), - Context.BIND_AUTO_CREATE); - } - - /** - * Calls extendAccessToken if shouldExtendAccessToken returns true. - * - * @return the same value as extendAccessToken if the the token requires - * refreshing, true otherwise - */ - public boolean extendAccessTokenIfNeeded(Context context, ServiceListener serviceListener) { - if (shouldExtendAccessToken()) { - return extendAccessToken(context, serviceListener); - } - return true; - } - - /** - * Check if the access token requires refreshing. - * - * @return true if the last time a new token was obtained was over 24 hours ago. - */ - public boolean shouldExtendAccessToken() { - return isSessionValid() && - (System.currentTimeMillis() - mLastAccessUpdate >= REFRESH_TOKEN_BARRIER); - } - - /** - * Handles connection to the token refresh service (this service is a part - * of Facebook App). - */ - private class TokenRefreshServiceConnection implements ServiceConnection { - - final Messenger messageReceiver = new Messenger(new Handler() { - @Override - public void handleMessage(Message msg) { - String token = msg.getData().getString(TOKEN); - long expiresAt = msg.getData().getLong(EXPIRES) * 1000L; - - // To avoid confusion we should return the expiration time in - // the same format as the getAccessExpires() function - that - // is in milliseconds. - Bundle resultBundle = (Bundle) msg.getData().clone(); - resultBundle.putLong(EXPIRES, expiresAt); - - if (token != null) { - setAccessToken(token); - setAccessExpires(expiresAt); - if (serviceListener != null) { - serviceListener.onComplete(resultBundle); - } - } else if (serviceListener != null) { // extract errors only if client wants them - String error = msg.getData().getString("error"); - if (msg.getData().containsKey("error_code")) { - int errorCode = msg.getData().getInt("error_code"); - serviceListener.onFacebookError(new FacebookError(error, null, errorCode)); - } else { - serviceListener.onError(new Error(error != null ? error - : "Unknown service error")); - } - } - - // The refreshToken function should be called rarely, - // so there is no point in keeping the binding open. - applicationsContext.unbindService(TokenRefreshServiceConnection.this); - } - }); - - final ServiceListener serviceListener; - final Context applicationsContext; - - Messenger messageSender = null; - - public TokenRefreshServiceConnection(Context applicationsContext, - ServiceListener serviceListener) { - this.applicationsContext = applicationsContext; - this.serviceListener = serviceListener; - } - - @Override - public void onServiceConnected(ComponentName className, IBinder service) { - messageSender = new Messenger(service); - refreshToken(); - } - - @Override - public void onServiceDisconnected(ComponentName arg) { - serviceListener.onError(new Error("Service disconnected")); - // We returned an error so there's no point in - // keeping the binding open. - applicationsContext.unbindService(TokenRefreshServiceConnection.this); - } - - private void refreshToken() { - Bundle requestData = new Bundle(); - requestData.putString(TOKEN, mAccessToken); - - Message request = Message.obtain(); - request.setData(requestData); - request.replyTo = messageReceiver; - - try { - messageSender.send(request); - } catch (RemoteException e) { - serviceListener.onError(new Error("Service connection error")); - } - } - }; - - /** - * 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: - * - * Bundle parameters = new Bundle(); - * parameters.putString("method", "auth.expireSession"); - * String response = request(parameters); - * - * - * @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; - } - - /** - * Retrieve the last time the token was updated (in milliseconds since - * the Unix epoch), or 0 if the token has not been set. - * - * @return long - timestamp of the last token update. - */ - public long getLastAccessUpdate() { - return mLastAccessUpdate; - } - - /** - * Restore the token, expiration time, and last update time from cached values. - * These should be values obtained from getAccessToken(), getAccessExpires, and - * getLastAccessUpdate() respectively. - * - * @param accessToken - access token - * @param accessExpires - access token expiration time - * @param lastAccessUpdate - timestamp of the last token update - */ - public void setTokenFromCache(String accessToken, long accessExpires, long lastAccessUpdate) { - mAccessToken = accessToken; - mAccessExpires = accessExpires; - mLastAccessUpdate = lastAccessUpdate; - } - - /** - * Set the OAuth 2.0 access token for API access. - * - * @param token - access token - */ - public void setAccessToken(String token) { - mAccessToken = token; - mLastAccessUpdate = System.currentTimeMillis(); - } - - /** - * 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), or "0" - * if session doesn't expire. - * - * @param expiresIn - * - duration in seconds (or 0 if the session doesn't expire) - */ - public void setAccessExpiresIn(String expiresIn) { - if (expiresIn != null) { - long expires = expiresIn.equals("0") - ? 0 - : System.currentTimeMillis() + Long.parseLong(expiresIn) * 1000L; - setAccessExpires(expires); - } - } - - public String getAppId() { - return mAppId; - } - - public void setAppId(String appId) { - mAppId = appId; - } - - /** - * Get Attribution ID for app install conversion tracking. - * @param contentResolver - * @return Attribution ID that will be used for conversion tracking. It will be null only if - * the user has not installed or logged in to the Facebook app. - */ - public static String getAttributionId(ContentResolver contentResolver) { - String [] projection = {ATTRIBUTION_ID_COLUMN_NAME}; - Cursor c = contentResolver.query(ATTRIBUTION_ID_CONTENT_URI, projection, null, null, null); - if (c == null || !c.moveToFirst()) { - return null; - } - String attributionId = c.getString(c.getColumnIndex(ATTRIBUTION_ID_COLUMN_NAME)); - - return attributionId; - } - - /** - * Get the auto install publish setting. If true, an install event will be published during authorize(), unless - * it has occurred previously or the app does not have install attribution enabled on the application's developer - * config page. - * @return - */ - public boolean getShouldAutoPublishInstall() { - return shouldAutoPublishInstall; - } - - /** - * Sets whether auto publishing of installs will occur. - * @param value - */ - public void setShouldAutoPublishInstall(boolean value) { - shouldAutoPublishInstall = value; - } - - /** - * Manually publish install attribution to the facebook graph. Internally handles tracking repeat calls to prevent - * multiple installs being published to the graph. - * @param context - * @return returns false on error. Applications should retry until true is returned. Safe to call again after - * true is returned. - */ - public boolean publishInstall(final Context context) { - try { - // copy the application id to guarantee thread safety.. - String applicationId = mAppId; - if (applicationId != null) { - publishInstall(this, applicationId, context); - return true; - } - } catch (Exception e) { - // if there was an error, fall through to the failure case. - Util.logd("Facebook-publish", e.getMessage()); - } - return false; - } - - /** - * This function does the heavy lifting of publishing an install. - * @param fb - * @param applicationId - * @param context - * @throws Exception - */ - private static void publishInstall(final Facebook fb, final String applicationId, final Context context) - throws JSONException, FacebookError, MalformedURLException, IOException { - - String attributionId = Facebook.getAttributionId(context.getContentResolver()); - SharedPreferences preferences = context.getSharedPreferences(ATTRIBUTION_PREFERENCES, Context.MODE_PRIVATE); - String pingKey = applicationId+"ping"; - long lastPing = preferences.getLong(pingKey, 0); - if (lastPing == 0 && attributionId != null) { - Bundle supportsAttributionParams = new Bundle(); - supportsAttributionParams.putString(APPLICATION_FIELDS, SUPPORTS_ATTRIBUTION); - JSONObject supportResponse = Util.parseJson(fb.request(applicationId, supportsAttributionParams)); - Object doesSupportAttribution = (Boolean)supportResponse.get(SUPPORTS_ATTRIBUTION); - - if (!(doesSupportAttribution instanceof Boolean)) { - throw new JSONException(String.format( - "%s contains %s instead of a Boolean", SUPPORTS_ATTRIBUTION, doesSupportAttribution)); - } - - if ((Boolean)doesSupportAttribution) { - Bundle publishParams = new Bundle(); - publishParams.putString(ANALYTICS_EVENT, MOBILE_INSTALL_EVENT); - publishParams.putString(ATTRIBUTION_KEY, attributionId); - - String publishUrl = String.format(PUBLISH_ACTIVITY_PATH, applicationId); - - fb.request(publishUrl, publishParams, "POST"); - - // denote success since no error threw from the post. - SharedPreferences.Editor editor = preferences.edit(); - editor.putLong(pingKey, System.currentTimeMillis()); - editor.commit(); - } - } - } - - void autoPublishAsync(final Context context) { - AutoPublishAsyncTask asyncTask = null; - synchronized (this) { - if (mAutoPublishAsyncTask == null && getShouldAutoPublishInstall()) { - // copy the application id to guarantee thread safety against our container. - String applicationId = Facebook.this.mAppId; - - // skip publish if we don't have an application id. - if (applicationId != null) { - asyncTask = mAutoPublishAsyncTask = new AutoPublishAsyncTask(applicationId, context); - } - } - } - - if (asyncTask != null) { - asyncTask.execute(); - } - } - - /** - * Async implementation to allow auto publishing to not block the ui thread. - */ - private class AutoPublishAsyncTask extends AsyncTask { - private final String mApplicationId; - private final Context mApplicationContext; - - public AutoPublishAsyncTask(String applicationId, Context context) { - mApplicationId = applicationId; - mApplicationContext = context.getApplicationContext(); - } - - @Override - protected Void doInBackground(Void... voids) { - try { - Facebook.publishInstall(Facebook.this, mApplicationId, mApplicationContext); - } catch (Exception e) { - Util.logd("Facebook-publish", e.getMessage()); - } - return null; - } - - @Override - protected void onPostExecute(Void result) { - // always clear out the publisher to allow other invocations. - synchronized (Facebook.this) { - mAutoPublishAsyncTask = null; - } - } - } - - /** - * 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(); - - } - - /** - * Callback interface for service requests. - */ - public static interface ServiceListener { - - /** - * Called when a service request completes. - * - * @param values - * Key-value string pairs extracted from the response. - */ - public void onComplete(Bundle values); - - /** - * Called when a Facebook server responds to the request with an error. - */ - public void onFacebookError(FacebookError e); - - /** - * Called when a Facebook Service responds to the request with an error. - */ - public void onError(Error e); - - } - - 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"; - -} diff --git a/src/android/facebook/FacebookLib/.classpath b/src/android/facebook/FacebookLib/.classpath new file mode 100644 index 000000000..51769745b --- /dev/null +++ b/src/android/facebook/FacebookLib/.classpath @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/android/facebook/FacebookLib/.project b/src/android/facebook/FacebookLib/.project new file mode 100644 index 000000000..d0551a112 --- /dev/null +++ b/src/android/facebook/FacebookLib/.project @@ -0,0 +1,33 @@ + + + FacebookSDK + + + + + + com.android.ide.eclipse.adt.ResourceManagerBuilder + + + + + com.android.ide.eclipse.adt.PreCompilerBuilder + + + + + org.eclipse.jdt.core.javabuilder + + + + + com.android.ide.eclipse.adt.ApkBuilder + + + + + + com.android.ide.eclipse.adt.AndroidNature + org.eclipse.jdt.core.javanature + + diff --git a/src/android/facebook/FacebookLib/AndroidManifest.xml b/src/android/facebook/FacebookLib/AndroidManifest.xml new file mode 100644 index 000000000..2f9284fb0 --- /dev/null +++ b/src/android/facebook/FacebookLib/AndroidManifest.xml @@ -0,0 +1,22 @@ + + + + + + + diff --git a/src/android/facebook/FacebookLib/build.gradle b/src/android/facebook/FacebookLib/build.gradle new file mode 100644 index 000000000..ea2914c49 --- /dev/null +++ b/src/android/facebook/FacebookLib/build.gradle @@ -0,0 +1,23 @@ +apply plugin: 'android-library' + +dependencies { + compile 'com.android.support:support-v4:13.0.+' +} + +android { + compileSdkVersion 19 + buildToolsVersion "19" + + defaultConfig { + minSdkVersion 8 + targetSdkVersion 19 + } + + sourceSets { + main { + manifest.srcFile 'AndroidManifest.xml' + java.srcDirs = ['src'] + res.srcDirs = ['res'] + } + } +} diff --git a/src/android/facebook/FacebookLib/build.xml b/src/android/facebook/FacebookLib/build.xml new file mode 100644 index 000000000..d85ad3e64 --- /dev/null +++ b/src/android/facebook/FacebookLib/build.xml @@ -0,0 +1,83 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/android/facebook/FacebookLib/libs/android-support-v4.jar b/src/android/facebook/FacebookLib/libs/android-support-v4.jar new file mode 100644 index 000000000..feaf44f80 Binary files /dev/null and b/src/android/facebook/FacebookLib/libs/android-support-v4.jar differ diff --git a/src/android/facebook/FacebookLib/project.properties b/src/android/facebook/FacebookLib/project.properties new file mode 100644 index 000000000..cd0ca122a --- /dev/null +++ b/src/android/facebook/FacebookLib/project.properties @@ -0,0 +1,15 @@ +# This file is automatically generated by Android Tools. +# Do not modify this file -- YOUR CHANGES WILL BE ERASED! +# +# This file must be checked in Version Control Systems. +# +# To customize properties used by the Ant build system edit +# "ant.properties", and override values to adapt the script to your +# project structure. +# +# To enable ProGuard to shrink and obfuscate your code, uncomment this (available properties: sdk.dir, user.home): +#proguard.config=${sdk.dir}/tools/proguard/proguard-android.txt:proguard-project.txt + +android.library=true +# Project target. +target=android-8 diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_focused.9.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_focused.9.png new file mode 100644 index 000000000..cf60eb0ac Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_normal.9.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_normal.9.png new file mode 100644 index 000000000..ece0c4c94 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_normal.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_pressed.9.png new file mode 100644 index 000000000..a123c2555 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_blue_pressed.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_focused.9.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_focused.9.png new file mode 100644 index 000000000..2e6f66dbc Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_normal.9.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_normal.9.png new file mode 100644 index 000000000..6098f0b0e Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_normal.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_pressed.9.png new file mode 100644 index 000000000..23b9757d1 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_button_grey_pressed.9.png differ diff --git a/src/android/facebook/res/drawable-hdpi/close.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_close.png similarity index 51% rename from src/android/facebook/res/drawable-hdpi/close.png rename to src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_close.png index d44c33575..d925cb7b2 100644 Binary files a/src/android/facebook/res/drawable-hdpi/close.png and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_close.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_inverse_icon.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_inverse_icon.png new file mode 100644 index 000000000..a7289c112 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_inverse_icon.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_logo.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_logo.png new file mode 100644 index 000000000..26ab7945f Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_logo.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_picker_magnifier.png b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_picker_magnifier.png new file mode 100644 index 000000000..d6ec37d04 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-hdpi/com_facebook_picker_magnifier.png differ diff --git a/src/android/facebook/res/drawable-ldpi/close.png b/src/android/facebook/FacebookLib/res/drawable-ldpi/com_facebook_close.png similarity index 100% rename from src/android/facebook/res/drawable-ldpi/close.png rename to src/android/facebook/FacebookLib/res/drawable-ldpi/com_facebook_close.png diff --git a/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_focused.9.png b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_focused.9.png new file mode 100644 index 000000000..cfb7a015c Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_normal.9.png b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_normal.9.png new file mode 100644 index 000000000..1e9390133 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_normal.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_pressed.9.png new file mode 100644 index 000000000..d8427fdef Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_button_blue_pressed.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_inverse_icon.png b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_inverse_icon.png new file mode 100644 index 000000000..a806a2f5d Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_inverse_icon.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_picker_magnifier.png b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_picker_magnifier.png new file mode 100644 index 000000000..c5170c18b Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-mdpi/com_facebook_picker_magnifier.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_focused.9.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_focused.9.png new file mode 100644 index 000000000..5a47068d7 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_normal.9.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_normal.9.png new file mode 100644 index 000000000..1449c8a13 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_normal.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_pressed.9.png new file mode 100644 index 000000000..28bd184ed Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_blue_pressed.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_focused.9.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_focused.9.png new file mode 100644 index 000000000..bfd883f92 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_normal.9.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_normal.9.png new file mode 100644 index 000000000..aa9895432 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_normal.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_pressed.9.png new file mode 100644 index 000000000..92f2ad12a Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_button_grey_pressed.9.png differ diff --git a/src/android/facebook/res/drawable-xhdpi/close.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_close.png old mode 100644 new mode 100755 similarity index 100% rename from src/android/facebook/res/drawable-xhdpi/close.png rename to src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_close.png diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_inverse_icon.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_inverse_icon.png new file mode 100644 index 000000000..4192c4887 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_inverse_icon.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_logo.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_logo.png new file mode 100644 index 000000000..24d7fc56b Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_logo.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_picker_magnifier.png b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_picker_magnifier.png new file mode 100644 index 000000000..a5b372468 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable-xhdpi/com_facebook_picker_magnifier.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue.xml new file mode 100644 index 000000000..91aebe685 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue.xml @@ -0,0 +1,24 @@ + + + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_focused.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_focused.9.png new file mode 100644 index 000000000..cfb7a015c Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_normal.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_normal.9.png new file mode 100644 index 000000000..1e9390133 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_normal.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_pressed.9.png new file mode 100644 index 000000000..d8427fdef Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_blue_pressed.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check.xml new file mode 100644 index 000000000..dfa510607 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check.xml @@ -0,0 +1,20 @@ + + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check_off.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check_off.png new file mode 100644 index 000000000..e9737df5c Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check_off.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check_on.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check_on.png new file mode 100644 index 000000000..d793151e6 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_check_on.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_focused.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_focused.9.png new file mode 100644 index 000000000..56f603571 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_normal.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_normal.9.png new file mode 100644 index 000000000..dff3b7d73 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_normal.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_pressed.9.png new file mode 100644 index 000000000..46648efcd Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_button_grey_pressed.9.png differ diff --git a/src/android/facebook/res/drawable/close.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_close.png old mode 100644 new mode 100755 similarity index 60% rename from src/android/facebook/res/drawable/close.png rename to src/android/facebook/FacebookLib/res/drawable/com_facebook_close.png index ed8b374b1..ad0147460 Binary files a/src/android/facebook/res/drawable/close.png and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_close.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_inverse_icon.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_inverse_icon.png new file mode 100644 index 000000000..a806a2f5d Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_inverse_icon.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_list_divider.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_list_divider.9.png new file mode 100644 index 000000000..7d4c46c47 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_list_divider.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_list_section_header_background.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_list_section_header_background.9.png new file mode 100644 index 000000000..1763c4a64 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_list_section_header_background.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_loginbutton_silver.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_loginbutton_silver.xml new file mode 100644 index 000000000..adf9885d7 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_loginbutton_silver.xml @@ -0,0 +1,23 @@ + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_logo.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_logo.png new file mode 100644 index 000000000..785acf9ea Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_logo.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_item_background.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_item_background.xml new file mode 100644 index 000000000..e43eb6cf5 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_item_background.xml @@ -0,0 +1,26 @@ + + + + + + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_focused.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_focused.9.png new file mode 100644 index 000000000..7c0599e3a Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_focused.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_longpressed.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_longpressed.9.png new file mode 100644 index 000000000..3bf8e0362 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_longpressed.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_pressed.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_pressed.9.png new file mode 100644 index 000000000..6e77525d2 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_pressed.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector.xml new file mode 100644 index 000000000..b35deba5e --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector.xml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector_background_transition.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector_background_transition.xml new file mode 100644 index 000000000..0e4aa8eb8 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector_background_transition.xml @@ -0,0 +1,20 @@ + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector_disabled.9.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector_disabled.9.png new file mode 100644 index 000000000..42cb6463e Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_list_selector_disabled.9.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_top_button.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_top_button.xml new file mode 100644 index 000000000..d282105b0 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_picker_top_button.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_place_default_icon.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_place_default_icon.png new file mode 100644 index 000000000..97ec3e608 Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_place_default_icon.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_default_icon.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_default_icon.png new file mode 100644 index 000000000..15864ccdb Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_default_icon.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_picture_blank_portrait.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_picture_blank_portrait.png new file mode 100644 index 000000000..107d7f89e Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_picture_blank_portrait.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_picture_blank_square.png b/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_picture_blank_square.png new file mode 100644 index 000000000..8b4a76a0e Binary files /dev/null and b/src/android/facebook/FacebookLib/res/drawable/com_facebook_profile_picture_blank_square.png differ diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_top_background.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_top_background.xml new file mode 100644 index 000000000..9b2989709 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_top_background.xml @@ -0,0 +1,22 @@ + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_top_button.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_top_button.xml new file mode 100644 index 000000000..7a60e4844 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_top_button.xml @@ -0,0 +1,27 @@ + + + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/drawable/com_facebook_usersettingsfragment_background_gradient.xml b/src/android/facebook/FacebookLib/res/drawable/com_facebook_usersettingsfragment_background_gradient.xml new file mode 100644 index 000000000..b28dab56c --- /dev/null +++ b/src/android/facebook/FacebookLib/res/drawable/com_facebook_usersettingsfragment_background_gradient.xml @@ -0,0 +1,23 @@ + + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_friendpickerfragment.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_friendpickerfragment.xml new file mode 100644 index 000000000..b42f15e7d --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_friendpickerfragment.xml @@ -0,0 +1,44 @@ + + + + + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_login_activity_layout.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_login_activity_layout.xml new file mode 100644 index 000000000..09b28999f --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_login_activity_layout.xml @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_activity_circle_row.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_activity_circle_row.xml new file mode 100644 index 000000000..de3e7e323 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_activity_circle_row.xml @@ -0,0 +1,32 @@ + + + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_checkbox.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_checkbox.xml new file mode 100644 index 000000000..ecc4aa88e --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_checkbox.xml @@ -0,0 +1,25 @@ + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_image.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_image.xml new file mode 100644 index 000000000..dd02cfe9c --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_image.xml @@ -0,0 +1,24 @@ + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_list_row.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_list_row.xml new file mode 100644 index 000000000..8f6725b11 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_list_row.xml @@ -0,0 +1,48 @@ + + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_list_section_header.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_list_section_header.xml new file mode 100644 index 000000000..ef04e3bc9 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_list_section_header.xml @@ -0,0 +1,34 @@ + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_search_box.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_search_box.xml new file mode 100644 index 000000000..616bba4e0 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_search_box.xml @@ -0,0 +1,29 @@ + + + + + + + diff --git a/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_title_bar.xml b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_title_bar.xml new file mode 100644 index 000000000..ecadecac9 --- /dev/null +++ b/src/android/facebook/FacebookLib/res/layout/com_facebook_picker_title_bar.xml @@ -0,0 +1,63 @@ + + + + +