Update facebook project to version 1.2.1, commit 5a72863

pull/14/head
Sam Bosley 12 years ago
parent 06279d50ac
commit 20cd0cd113

@ -3,68 +3,22 @@ This open source Java library allows you to integrate Facebook into your Android
Getting Started
===============
See our [Android SDK Getting Started Guide](http://developers.facebook.com/docs/guides/mobile#android)
See our [Android SDK Getting Started Guide](https://developers.facebook.com/docs/mobile/android/build/)
Sample Applications
===============
This library includes two sample applications to guide you in development.
This library includes three sample applications to guide you in development.
* __simple__: A bare-bones app that demonstrates authorization, making API calls, and invoking a dialog.
* __stream__: This slightly beefier application lets you view your news feed.
To install a sample application into Eclipse (3.5):
* __Hackbook__: This includes Single Sign On implementation (SSO), sample API calls, and advanced features like Get new Permissions, Run sample FQL Query and Graph API Explorer. Check out [Hackbook for Android](https://developers.facebook.com/docs/mobile/android/hackbook/)
* Create the sample application in your workspace:
2. Select __File__ -> __New__ -> __Project__, choose __Android Project__, and then click __Next__.
3. Select "Create project from existing source".
4. Choose either __examples/simple__ or __examples/stream__. You should see the project properties populated.
5. Click Finish to continue.
* Build the project: from the Project menu, select "Build Project".
* Run the application: from the Run menu, select "Run Configurations...". Under Android Application, you can create a new run configuration: give it a name and select the simple Example project; use the default activity Launch Action. See http://developer.android.com/guide/developing/eclipse-adt.html#RunConfig for more details.
Testing
===============
Here are some tips to help test your application:
* You will need to have the Facebook application in your test environment. The SDK includes a developer release of the Facebook application that can be side-loaded for testing purposes. On an actual device, you can just download the latest version of the app from the Android Market, but on the emulator you will have to install it yourself:
adb install FBAndroid.apk
* Use a signed build. You can sign with a debug key, but make sure that the key you used to sign matches the __Key Hash__ field in the Facebook developer settings.
* Make sure to test both with and without the Facebook application. The SDK will fall back to a Webview if the Facebook app is not installed.
* You can use this [guide to developing on a device](http://developer.android.com/guide/developing/device.html).
Debugging
==========
Here's a few common errors and their solutions.
* __Build error: "missing gen files".__
This should go away when you rebuild the project. If you have trouble, try running __Clean...__ from the __Project__ menu.
* __Error: "invalid_key"__
This error means that the Facebook server doesn't recognize your Android key hash. Make sure that you correctly generated and copy/pasted your key hash into the Facebook developer settings console (http://www.facebook.com/developers/apps.php), and make sure that your application has been signed with the same key you registered with Facebook.
* __Dialog won't load or shows a blank screen.__
This can be tricky to debug. If the logs don't give an indication of what's wrong, I suggest installing tcpdump on your device and getting a trace. Tutorial: http://www.vbsteven.be/blog/android-debugging-inspectin-network-traffic-with-tcpdump/
If you still can't tell what's going on, then file an issue and please include the HTTP trace.
* __I can't upload photos with photos.upload.__
Make sure the Bundle value for the photo parameter is a byte array.
Report Issues/Bugs
===============
[http://bugs.developers.facebook.net/enter_bug.cgi?product=SDKs](http://bugs.developers.facebook.net/enter_bug.cgi?product=SDKs)
[Bugs](https://developers.facebook.com/bugs)
[Questions](http://facebook.stackoverflow.com/questions/tagged/android)

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.facebook.android"
android:versionCode="1"
android:versionName="1.0">
<supports-screens
android:smallScreens="true"
android:normalScreens="true"
android:largeScreens="true"
android:anyDensity="true" />
<application android:icon="@drawable/icon"
android:label="@string/app_name"
android:debuggable="true">
<activity android:name=".SplashActivity"
android:label="@string/app_name"
android:screenOrientation="portrait">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".Hackbook"
android:label="@string/app_name"
android:configChanges="keyboardHidden"
android:screenOrientation="portrait">
</activity>
<activity
android:name=".GraphExplorer"
android:windowSoftInputMode="stateHidden"
android:screenOrientation="portrait" />
<activity android:name=".IntentUriHandler">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<data android:scheme="fbgraphex" />
</intent-filter>
</activity>
<activity
android:name=".FriendsList"
android:screenOrientation="portrait" />
<activity
android:name=".Places"
android:screenOrientation="portrait" />
</application>
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.MANAGE_ACCOUNTS" />
<uses-sdk android:minSdkVersion="3" />
</manifest>

@ -0,0 +1,130 @@
This 'Hackbook for Android' app includes Single Sign On implementation (SSO), sample API calls and Graph API Explorer and is targeted towards android developers who want to make their apps social using Facebook Social Graph. The Code provided here is to showcase how to implement the SSO and make the API calls. If you have any questiosn or comments related to this sample app, please post them here - http://facebook.stackoverflow.com/questions/tagged/hackbook-for-android
Getting Started
===============
See Android tutorial - https://developers.facebook.com/docs/mobile/android/build/
Configuring the app
===============
1. Launch Eclipse
2. Ensure you have installed the Android plugin.
3. Create Facebook SDK Project - follow the Step-1 instructions in the tutorial
4. Create the Hackbook Project :
4.1 Select __File__ -> __New__ -> __Project__, choose __Android Project__, and then click __Next__.
4.2 Select "Create project from existing source".
4.3. Choose examples/Hackbook. You should see the project properties populated.
4.4. Click Finish to continue.
5. Add reference to the Facebook SDK - follow the Step-3 instructions in the tutorial
6. Create a Facebook app if you don't have one already and add the app id in the Hackbook.java->APP_ID
Build the app
===============
7. And you are done and ready to compile the project:
7.1 From the Project menu, select "Build Project".
Run the app
===============
8. Hopefully project would compile fine. Next run the app on the emulator or on the phone (See http://developer.android.com/guide/developing/eclipse-adt.html#RunConfig for more details.)
8.1 If you plan to run on emulator, ensure you have created an Android Virtual Device (AVD):
8.1.1 Go to Window -> Android SDK and AVD Manager -> Click New
8.1.2 Provide a Name (AVD 2.3 e.g.) and choose the Target (Android 2.3 if available).
8.1.3 Click 'Create AVD' at the bottom and that should create an AVD which you can run the app on described next.
8.2 Go to Run->Run Configurations->Android Application->create a new run configuration by clicking the icon with + button on it.
8.3 Name it 'Hackbook'
8.4 Under the Project, Browse and choose Hackbook
8.5 Go to Target tab -> Choose manual if you wish to run on the phone, else choose Automatic and select an AVD created in step 8.1
8.6 Click Run and your 'Hackbook for Android' app should be up and running.
Installing the Facebook app
===============
You will need to have the Facebook application on the handset or the emulator to test the Single Sign On. The SDK includes a developer release of the Facebook application that can be side-loaded for testing purposes. On an actual device, you can just download the latest version of the app from the Android Market, but on the emulator you will have to install it yourself:
adb install FBAndroid.apk
What's in there
===============
Note: The source tags are provided through out the code base to facilitate easy search for the relevant code. Do a project-wide search for the source tags to get straight to the relevant code. Refer below for source tags for each feature.
1. Login button - This uses SSO to authorize the app. Clicking on Login should activate SSO (if the app is installed) or show OAuth dialog. When authorizing, no permissions are requested and the app will get basic permission by default.
Source Tag - login_tag
- Hackbook.java - this layout the login button and initialize it. Since this is also the calling acitivty, this overrides the onActivityResult() method.
- LoginButton.java - this calls the mFb.authorize(mActivity, mPermissions, mActivityCode, new LoginDialogListener()) which authorizes the app via SSO or OAuth.
- SessionStore.java - stores the access token and access expiry time for future app launch. This is important that you save the access token, else user will need to authorize your app each time they launch it which is annoying and user is likely to churn out.
- SessionEvents.java - Authorization state tracker, calls the listener on login/logout success or error.
------------------------
2. Update Status - this allows user to update his status by calling the 'feed' dialog. More info on feed dialog - https://developers.facebook.com/docs/reference/dialogs/feed/
Source Tag - update_status_tag, view_post_tag, delete_post_tag
- Hackbook.java - Case 0: update status by calling the 'feed' dialog.
- UpdateStatusResultDialog.java - shows the object-id returned in the dialog response. You can view or delete the post here.
------------------------
3. App Requests - this allows to send app requests to user's friends by calling the 'apprequests' dialog. More info - https://developers.facebook.com/docs/reference/dialogs/requests/
Source Tag - app_requests_tag
- Hackbook.java - Case 1: send the app requests by calling the the 'apprequests' dialog.
------------------------
4. Get Friends - Get user's friends via Graph API or FQL Query. User must be logged-in to use this. Also post on a friend's wall by clicking on his name in the list.
Source Tag - get_friends_tag, friend_wall_tag
- Hackbook.java - Case 2: Use Graph API 'me/friends' which returns friends sorted by UID, currently it's not possible to sort any other way in the Graph API. Use the FQL Query to sort by name - select name, current_location, uid, pic_square from user where uid in (select uid2 from friend where uid1=me()) order by name
- FriendsList.java - displays the friends profile pic and names as returned by the api. Also post on friend's wall by clicking on the friend.
- FriendsModel.java - run async tasks to fetch the profile picture limited to 15 tasks at any given time.
5. Upload Photo - Upload a photo either from the media gallery or from a remote server. You require 'photo_upload' to upload photos on user profile.
Source Tag - upload_photo, view_photo_tag, tag_photo_tag
- Hackbook.java - Case 3: Photo is uploaded by posting byte-array or url to me/photos endpoint. Media Gallery is launched by invoking the MediaStore.Images.Media.EXTERNAL_CONTENT_URI intent and overriding the OnActivityResult() to get the picture from the media gallery. Photo from remote server is uploaded by simply providing the image url in the 'url' param in the graph.facebook.com/me/photos endpoint.
- UploadPhotoResultDialog.java - shows the object-id returned after uploaded the photo. You can view or tag the photo here.
------------------------
6. Place Check-in - Fetch user's current location or use Times Square as the current location and get nearby places which user can check-in at.
Source Tag - fetch_places_tag, check_in_tag
- Hackbook.java - Case 4: Ask to fetch current location or use Times Square as the current location.
- Places.java - Get user's current location and fetch the nearby places by calling the graph.facebook.com/search?type=places&distance=1000&center=lat,lon. Check-in user at a place by calling the graph.facebook.com/me/checkins&place=<place_id_>&message=<message>&coordinates={"latitude": <lat>, "longitude:": <lon>}
------------------------
7. Run FQL Query - Type and run any FQL Query and see the results.
Source Tag - fql_query_tag
- FQLQuery.java - Layout the FQL Query Dialog and run the query and show the results.
------------------------
8. Graph API Explorer - Explore user's social graph, see his and friends' connections and get new permissions. This is similar to the Graph Explorer dev tool - http://developers.facebook.com/tools/explorer/. The ObjectIDs in the API response are linkified and can be clicked to fetch object specific data.
- Click the 'x' button to clear the textfield
- Click the green up arrow button in the textfield to get the 'me' object data.
- Click on 'Get Permissions' to get new permissions including user's, his friends or extended permissions.
- Click on 'Fields/Connections' to see current object's fields and connections.
- On the Fields & Connections dialog, in the Fields tab, select the fields to view or in the Connections tab, click the connection to view it's content.
Source Tag - graph_explorer
- Hackbook.java - Case 5: Launch the GraphExplorer intent
- GraphExplorer.java - Layout and execute the graph explorer
- IntentUriHandler.java - Handle the fbgraphex: schema generated while linkifying the Object IDs in the graph explorer output
Report Issues/Bugs
===============
Please report issues here - http://facebook.stackoverflow.com/questions/tagged/hackbook-for-android

@ -0,0 +1,14 @@
# 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 use,
# "build.properties", and override values to adapt the script to your
# project structure.
# Indicates whether an apk should be generated for each density.
split.density=false
# Project target.
target=android-8
android.library.reference.1=../../../newdialog/facebook-android-sdk/facebook

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/login_down" />
<item android:drawable="@drawable/login" /> <!-- default -->
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true" android:drawable="@drawable/logout_down" />
<item android:drawable="@drawable/logout" /> <!-- default -->
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 B

@ -0,0 +1,11 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight">
<TextView
android:id="@+id/connection_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="26sp"
android:textColor="@color/lite_blue"
android:paddingLeft="5dp" />
</RelativeLayout>

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<TabHost
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:id="@+id/tabHost">
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<TabWidget
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@android:id/tabs" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@android:id/tabcontent"
android:layout_below="@android:id/tabs">
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:id="@+id/fields_layout">
<Button android:id="@+id/get_fields_button"
android:text="@string/get_fields"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
<ListView
android:id="@+id/fields_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>
<ListView
android:id="@+id/connections_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</FrameLayout>
</RelativeLayout>
</TabHost>
</LinearLayout>

@ -0,0 +1,14 @@
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<CheckBox
android:id="@+id/fields_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
<TextView
android:id="@+id/fields_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

@ -0,0 +1,41 @@
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<EditText
android:layout_height="wrap_content"
android:layout_width="240dp"
android:layout_weight="1"
android:textSize="25px"
android:gravity="left|center"
android:id="@+id/fqlquery" />
<Button android:id="@+id/submit_button"
android:text="@string/submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="3dip"
android:background="@color/grey" />
<ScrollView
android:id="@+id/ScrollView01"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<TextView
android:id="@+id/fqlOutput"
android:textColor="@color/white"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp"
android:visibility="invisible"
android:background="@color/grey" />
</ScrollView>
</LinearLayout>

@ -0,0 +1,33 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:padding="6dip"
android:layout_height="?android:attr/listPreferredItemHeight"
android:id="@+id/friend_item">
<ImageView
android:id="@+id/profile_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="6dip" />
<TextView
android:id="@+id/name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/profile_pic"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:gravity="center_vertical" />
<TextView
android:id="@+id/info"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/profile_pic"
android:layout_below="@id/name"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignWithParentIfMissing="true"
android:singleLine="true"
android:ellipsize="marquee" />
</RelativeLayout>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<ListView
android:id="@+id/friends_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/accessTokenButton"
android:text="@string/get_permissions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<TextView
android:id="@+id/graphDomain"
android:text="@string/graph_domain"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="5dp"
android:paddingLeft="5dp" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<EditText android:id="@+id/inputId"
android:layout_height="wrap_content"
android:layout_width="240dp"
android:layout_weight="1"
android:textSize="25px"
android:gravity="left"
android:hint="Enter Object ID" />
<Button
android:id="@+id/meButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="30dip"
android:layout_gravity="right|center_vertical"
android:background="@drawable/me"
android:visibility="invisible" />
<Button
android:id="@+id/textDeleteButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginRight="5dip"
android:layout_gravity="right|center_vertical"
android:background="@drawable/x" />
</FrameLayout>
<Button
android:id="@+id/submitButton"
android:text="@string/submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<LinearLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<Button android:id="@+id/fieldsAndConnectionsButton"
android:text="@string/fields_and_connections"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:visibility="invisible" />
<Button android:id="@+id/viewURLButton"
android:text="@string/view_url"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:visibility="invisible" />
<Button android:id="@+id/backParentButton"
android:text="@string/back_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp"
android:visibility="invisible" />
</LinearLayout>
<ScrollView
android:id="@+id/ScrollView01"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<TextView android:id="@+id/output"
android:textColor="@color/white"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp"
android:textSize="16sp"
android:visibility="invisible"
android:background="@color/grey" />
</ScrollView>
</LinearLayout>

@ -0,0 +1,43 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal">
<RelativeLayout
android:layout_width="fill_parent"
android:padding="6dip"
android:layout_height="?android:attr/listPreferredItemHeight">
<ImageView
android:id="@+id/user_pic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_marginRight="6dip" />
<TextView
android:id="@+id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/user_pic" />
<com.facebook.android.LoginButton
android:id="@+id/login"
android:src="@drawable/login_button"
android:layout_toRightOf="@id/user_pic"
android:layout_below="@id/txt"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@color/grey" />
<ListView
android:id="@+id/main_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<View
android:layout_width="fill_parent"
android:layout_height="1dip"
android:background="@color/grey" />
</LinearLayout>

@ -0,0 +1,9 @@
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/main_api_item"
android:padding="10dip"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:gravity="center_vertical"
android:textSize="20sp" />

@ -0,0 +1,8 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="?android:attr/listPreferredItemHeight">
<CheckBox
android:id="@+id/permission_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</RelativeLayout>

@ -0,0 +1,59 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<TextView
android:id="@+id/permission_detail"
android:text="@string/permission_detail"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:textSize="16sp" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<Button
android:id="@+id/get_permissions_button"
android:text="@string/get_permissions"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<TabHost
android:layout_width="wrap_content"
android:layout_height="fill_parent"
android:id="@+id/tabHost">
<RelativeLayout
android:layout_height="wrap_content"
android:layout_width="fill_parent">
<TabWidget
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@android:id/tabs" />
<FrameLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="@android:id/tabcontent"
android:layout_below="@android:id/tabs">
<ListView
android:id="@+id/user_permissions_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ListView
android:id="@+id/friend_permissions_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ListView
android:id="@+id/extended_permissions_list"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
</FrameLayout>
</RelativeLayout>
</TabHost>
</LinearLayout>

@ -0,0 +1,24 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:padding="6dip"
android:layout_height="?android:attr/listPreferredItemHeight"
android:id="@+id/friend_item">
<TextView
android:id="@+id/place_name"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_alignWithParentIfMissing="true"
android:gravity="center_vertical" />
<TextView
android:id="@+id/place_location"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/place_name"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignWithParentIfMissing="true"
android:singleLine="true"
android:ellipsize="marquee" />
</RelativeLayout>

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<ListView
android:id="@+id/places_list"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />
</LinearLayout>

@ -0,0 +1,5 @@
<ImageView android:id="@+id/imageView1" xmlns:android="http://schemas.android.com/apk/res/android"
android:src="@drawable/splash"
android:scaleType="fitXY"
android:layout_width="fill_parent"
android:layout_height="fill_parent" />

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<TextView
android:id="@+id/tokenLabel"
android:text="@string/access_token_label"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp" />
<EditText
android:id="@+id/tokenEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false" />
<TextView
android:id="@+id/tokenExpiresLabel"
android:text="@string/access_token_expires_label"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp" />
<EditText
android:id="@+id/tokenExpiresEdit"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:editable="false" />
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/refresh_button"
android:text="@string/refresh_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<TextView
android:id="@+id/tip_label"
android:text="@string/tip_label"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp" />
<TextView
android:id="@+id/usefulTip"
android:text="@string/refresh_token_tip"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingLeft="3dp" />
</LinearLayout>

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<TextView
android:id="@+id/apiOutputLabel"
android:text="@string/api_response"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp" />
<ScrollView
android:id="@+id/ScrollView01"
android:layout_height="140dp"
android:layout_width="fill_parent">
<TextView android:id="@+id/apiOutput"
android:textColor="@color/white"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp"
android:background="@color/grey" />
</ScrollView>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/view_post_button"
android:text="@string/view_post"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
<Button
android:id="@+id/delete_post_button"
android:text="@string/delete_post"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
</LinearLayout>
<View
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<TextView
android:id="@+id/tip_label"
android:text="@string/tip_label"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp" />
<TextView
android:id="@+id/usefulTip"
android:text="@string/post_tip"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingLeft="3dp" />
</LinearLayout>

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@color/black">
<TextView
android:id="@+id/apiOutputLabel"
android:text="@string/api_response"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp" />
<ScrollView
android:id="@+id/ScrollView01"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
android:paddingBottom="3dp">
<TextView android:id="@+id/apiOutput"
android:textColor="@color/white"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp"
android:background="@color/grey" />
</ScrollView>
<View
android:paddingTop="3dp"
android:layout_width="fill_parent"
android:layout_height="2dip"
android:background="@color/grey" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<Button
android:id="@+id/view_photo_button"
android:text="@string/view_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
<Button
android:id="@+id/tag_photo_button"
android:text="@string/tag_photo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="10dp" />
</LinearLayout>
<ImageView
android:id="@+id/uploadedPhoto"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingBottom="3dp" />
<TextView
android:id="@+id/tip_label"
android:text="@string/tip_label"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="3dp"
android:paddingLeft="3dp" />
<TextView
android:id="@+id/usefulTip"
android:textColor="@color/white"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="5dp"
android:paddingLeft="3dp" />
</LinearLayout>

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="white">#ffffff</color>
<color name="black">#000000</color>
<color name="green">#23cf34</color>
<color name="orange">#E47833</color>
<color name="lite_blue">#4E78A0</color>
<color name="blue">#0000FF</color>
<color name="grey">#FF909090</color>
</resources>

@ -0,0 +1,88 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Hackbook for Android</string>
<string name="app_desc">Checkout out Hackbook for Android to learn how you can make your android apps social using Facebook Platform.</string>
<string name="app_action">I am using the Hackbook for Android</string>
<string name="request_message">Learn how to make your android apps social.</string>
<string name="request">Request!</string>
<string name="upload">Upload Photo</string><string name="post">Wall Post!</string>
<string name="delete">Delete Post</string>
<string name="no_login">Graph API Explorer</string>
<string name="get_permissions">Get Permissions</string>
<string name="access_token">Access Token:</string>
<string name="graph_domain">https://graph.facebook.com/</string>
<string name="submit">Submit</string>
<string name="label_output">Output:</string>
<string name="show_metadata">Show metadata</string>
<string name="show_picture">Show picture</string>
<string name="view_url">URL</string>
<string name="target_url">Target URL:</string>
<string name="exception">Exception: </string>
<string name="tip_label">Tip:</string>
<string name="error">Error</string>
<string name="facebook_error">Facebook Error: </string>
<string name="api_response">Api Response:</string>
<string name="view_source">View Source</string>
<string name="view_post">View Post</string>
<string name="delete_post">Delete Post</string>
<string name="source_code">Source Code:</string>
<string name="please_wait">Please wait...</string>
<string name="view_photo">View Photo</string>
<string name="hide_photo">Hide Photo</string>
<string name="tag_photo">Tag Photo</string>
<string name="graph_button">Graph API</string>
<string name="fql_button">FQL Query</string>
<string name="source_tag">Source tag:</string>
<string name="permission_source_tag">Source tag: \"perms_tag\"</string>
<string name="Graph_FQL_title">Graph API or FQL Query?</string>
<string name="Graph_FQL_msg">Get friends using Graph or FQL Query?\n\nWith FQL query you can sort the list by names, whereas with Graph list will be ordered by UID and can\'t be changed.</string>
<string name="can_post_on_wall_title">Post on friend\'s wall</string>
<string name="can_post_on_wall">Friends list fetched. You can post on friend\'s wall by clicking on the item.</string>
<string name="ok">OK</string>
<string name="post_on_wall_title">Post on Wall?</string>
<string name="post_on_wall">Would you like to post on %1$s\'s wall</string>
<string name="yes">Yes</string>
<string name="no">No</string>
<string name="save">Save</string>
<string name="reset">Reset</string>
<string name="get_fields">Get Fields</string>
<string name="fields">Fields</string>
<string name="connections">Connections</string>
<string name="checkin">Check-in</string>
<string name="cancel">Cancel</string>
<string name="user">User Data</string>
<string name="friend">Friends Data</string>
<string name="extended">Extended</string>
<string name="gallery_remote_title">Upload from gallery or remote?</string>
<string name="gallery_remote_msg">Upload picture from phone\' gallery or remote server?</string>
<string name="gallery_button">Gallery</string>
<string name="remote_button">Remote</string>
<string name="get_location">Get Location?</string>
<string name="get_default_or_new_location">Get current location or use Times Square as the location?</string>
<string name="current_location_button">Current Location</string>
<string name="fetching_current_permissions">Fetching current permissions...</string>
<string name="explorer_message">Also, checkout the web-version of the <a href="http://developers.facebook.com/tools/explorer">Graph API Explorer tool</a></string>
<string name="nearby_places">Fetching nearby places...</string>
<string name="times_square_button">Times Square</string>
<string name="check_in_title">Check-in?</string>
<string name="permissions">Permissions</string>
<string name="back_parent">Back to parent</string>
<string name="fields_and_connections">Fields \u0026 Connections</string>
<string name="permissions_request">Select Permissions:</string>
<string name="permission_detail">Checkout <a href="https://developers.facebook.com/docs/authentication/permissions/">permissions reference doc</a></string>
<string name="fetching_location">Fetching Location...</string>
<string name="check_in_at">Would you like to check-in at %1$s?</string>
<string name="post_tip">\'read_stream\' permission is required to view and delete the post.\n\nUse SDK dialogs to publish stories or post on friends wall. <a href="https://developers.facebook.com/docs/reference/dialogs">More info</a>\n\n<a href="https://developers.facebook.com/docs/reference/api/Comment">Like and Comment on a post</a></string>
<string name="photo_tip">Photos are by default uploaded to the app\'s album. Specify album_id to upload to specific album. E.g. https://graph.facebook.com/{album_id}/photos.\n\nMore Info: https://developers.facebook.com/docs/reference/api/photo</string>
<string name="fqlquery">Enter FQL Query:</string>
<string name="enable_gps_title">Enable GPS</string>
<string name="enable_gps">Please enable GPS to get nearby places and check-in</string>
<string name="gps_settings">GPS Settings</string>
<string name="refresh_token_title">Refresh Token</string>
<string name="refresh_button">Refresh</string>
<string name="refresh_button_pending">Refreshing…</string>
<string name="refresh_token_tip">In most cases the access token should be refreshed silently when the application is running (for example see Hackbook onResume method).</string>
<string name="refresh_token_binding_error">Binding to the Facebook Android Application failed (is it installed?).</string>
<string name="access_token_label">Current access token:</string>
<string name="access_token_expires_label">Token expires at:</string>
</resources>

@ -0,0 +1,25 @@
package com.facebook.android;
import com.facebook.android.Facebook.DialogListener;
/**
* Skeleton base class for RequestListeners, providing default error handling.
* Applications should handle these error conditions.
*/
public abstract class BaseDialogListener implements DialogListener {
@Override
public void onFacebookError(FacebookError e) {
e.printStackTrace();
}
@Override
public void onError(DialogError e) {
e.printStackTrace();
}
@Override
public void onCancel() {
}
}

@ -0,0 +1,41 @@
package com.facebook.android;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.MalformedURLException;
import android.util.Log;
import com.facebook.android.AsyncFacebookRunner.RequestListener;
/**
* Skeleton base class for RequestListeners, providing default error handling.
* Applications should handle these error conditions.
*/
public abstract class BaseRequestListener implements RequestListener {
@Override
public void onFacebookError(FacebookError e, final Object state) {
Log.e("Facebook", e.getMessage());
e.printStackTrace();
}
@Override
public void onFileNotFoundException(FileNotFoundException e, final Object state) {
Log.e("Facebook", e.getMessage());
e.printStackTrace();
}
@Override
public void onIOException(IOException e, final Object state) {
Log.e("Facebook", e.getMessage());
e.printStackTrace();
}
@Override
public void onMalformedURLException(MalformedURLException e, final Object state) {
Log.e("Facebook", e.getMessage());
e.printStackTrace();
}
}

@ -0,0 +1,112 @@
package com.facebook.android;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class FQLQuery extends Dialog {
private EditText mFQLQuery;
private TextView mFQLOutput;
private Button mSubmitButton;
private Activity activity;
private Handler mHandler;
private ProgressDialog dialog;
public FQLQuery(Activity activity) {
super(activity);
this.activity = activity;
setTitle(R.string.fqlquery);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
setContentView(R.layout.fql_query);
LayoutParams params = getWindow().getAttributes();
params.width = LayoutParams.FILL_PARENT;
params.height = LayoutParams.FILL_PARENT;
getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
mFQLQuery = (EditText) findViewById(R.id.fqlquery);
mFQLOutput = (TextView) findViewById(R.id.fqlOutput);
mSubmitButton = (Button) findViewById(R.id.submit_button);
mSubmitButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
((InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(mFQLQuery.getWindowToken(), 0);
dialog = ProgressDialog.show(FQLQuery.this.activity, "",
FQLQuery.this.activity.getString(R.string.please_wait), true, true);
/*
* Source tag: fql_query_tag
*/
String query = mFQLQuery.getText().toString();
Bundle params = new Bundle();
params.putString("method", "fql.query");
params.putString("query", query);
Utility.mAsyncRunner.request(null, params, new FQLRequestListener());
}
});
}
public class FQLRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
dialog.dismiss();
/*
* Output can be a JSONArray or a JSONObject.
* Try JSONArray and if there's a JSONException, parse to JSONObject
*/
try {
JSONArray json = new JSONArray(response);
setText(json.toString(2));
} catch (JSONException e) {
try {
/*
* JSONObject probably indicates there was some error
* Display that error, but for end user you should parse the
* error and show appropriate message
*/
JSONObject json = new JSONObject(response);
setText(json.toString(2));
} catch (JSONException e1) {
setText(activity.getString(R.string.exception) + e1.getMessage());
}
}
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
setText(activity.getString(R.string.facebook_error) + error.getMessage());
}
}
public void setText(final String txt) {
mHandler.post(new Runnable() {
@Override
public void run() {
mFQLOutput.setText(txt);
mFQLOutput.setVisibility(View.VISIBLE);
}
});
}
}

@ -0,0 +1,305 @@
package com.facebook.android;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.Vector;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Dialog;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.style.UnderlineSpan;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import android.widget.Toast;
public class FieldsConnectionsDialog extends Dialog {
private final static int TAB_HEIGHT = 50;
private Button mGetFieldsButton;
private ListView fieldsList, connectionsList;
private BaseAdapter fieldsAdapter, connectionsAdapter;
private GraphExplorer explorerActivity;
protected Vector<String> fieldsVector;
private ArrayList<JSONObject> fieldsArray;
private ArrayList<String> connectionsArray;
public FieldsConnectionsDialog(GraphExplorer explorerActivity, JSONObject metadata) {
super(explorerActivity);
this.explorerActivity = explorerActivity;
/*
* Sort the fields and connections
*/
try {
sortFields(metadata.getJSONArray("fields"));
sortConnections(metadata.getJSONObject("connections").names());
} catch (JSONException e) {
Toast.makeText(explorerActivity.getBaseContext(),
"Fields/Connections could not be fetched.", Toast.LENGTH_SHORT).show();
}
setTitle(explorerActivity.getString(R.string.fields_and_connections));
fieldsVector = new Vector<String>();
}
/*
* Sort fields which are returned as JSONObject in the JSONArray
*/
public void sortFields(JSONArray jsonFieldsArray) {
this.fieldsArray = new ArrayList<JSONObject>(jsonFieldsArray.length());
for (int i = 0; i < jsonFieldsArray.length(); i++) {
try {
this.fieldsArray.add(jsonFieldsArray.getJSONObject(i));
} catch (JSONException e) {
e.printStackTrace();
}
}
Collections.sort(this.fieldsArray, new Comparator<JSONObject>() {
@Override
public int compare(JSONObject object1, JSONObject object2) {
try {
return object1.getString("name").compareToIgnoreCase(object2.getString("name"));
} catch (JSONException e) {
e.printStackTrace();
}
return 0;
}
});
}
/*
* Sort the Connections returned in the JSONArray
*/
public void sortConnections(JSONArray jsonConnectionsArray) {
this.connectionsArray = new ArrayList<String>(jsonConnectionsArray.length());
for (int i = 0; i < jsonConnectionsArray.length(); i++) {
try {
this.connectionsArray.add(jsonConnectionsArray.get(i).toString());
} catch (JSONException e) {
e.printStackTrace();
}
}
Collections.sort(this.connectionsArray);
}
/*
* Layout the dialog
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fields_connections_list);
LayoutParams params = getWindow().getAttributes();
params.width = LayoutParams.FILL_PARENT;
params.height = LayoutParams.FILL_PARENT;
getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
fieldsList = (ListView) findViewById(R.id.fields_list);
connectionsList = (ListView) findViewById(R.id.connections_list);
fieldsAdapter = new FieldsListAdapter();
if (this.fieldsArray == null) {
fieldsList.setAdapter(new ArrayAdapter<String>(explorerActivity,
android.R.layout.simple_list_item_1, new String[] { "No fields available" }));
} else {
fieldsList.setAdapter(fieldsAdapter);
}
connectionsAdapter = new ConnectionsListAdapter();
if (this.connectionsArray == null) {
connectionsList.setAdapter(new ArrayAdapter<String>(explorerActivity,
android.R.layout.simple_list_item_1,
new String[] { "No connections available" }));
} else {
connectionsList.setAdapter(connectionsAdapter);
}
TabHost tabHost = (TabHost) findViewById(R.id.tabHost);
tabHost.setup();
TabSpec spec1 = tabHost.newTabSpec("Tab 1");
spec1.setIndicator(explorerActivity.getString(R.string.fields));
spec1.setContent(R.id.fields_layout);
TabSpec spec2 = tabHost.newTabSpec("Tab 2");
spec2.setIndicator(explorerActivity.getString(R.string.connections));
spec2.setContent(R.id.connections_list);
tabHost.addTab(spec1);
tabHost.addTab(spec2);
tabHost.setCurrentTab(0);
tabHost.getTabWidget().getChildAt(0).getLayoutParams().height = TAB_HEIGHT;
tabHost.getTabWidget().getChildAt(1).getLayoutParams().height = TAB_HEIGHT;
mGetFieldsButton = (Button) findViewById(R.id.get_fields_button);
mGetFieldsButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* Source Tag:
*/
FieldsConnectionsDialog.this.dismiss();
if (!fieldsVector.isEmpty()) {
explorerActivity.getFields(fieldsVector);
} else {
Toast.makeText(explorerActivity.getBaseContext(), "No Fields selected.",
Toast.LENGTH_SHORT).show();
}
}
});
connectionsList.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) {
FieldsConnectionsDialog.this.dismiss();
explorerActivity.getConnection(connectionsArray.get(position));
}
});
}
/**
* Definition of the list adapter
*/
public class FieldsListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
boolean[] isChecked;
public FieldsListAdapter() {
mInflater = LayoutInflater.from(explorerActivity.getBaseContext());
isChecked = new boolean[fieldsArray.size()];
}
@Override
public int getCount() {
return fieldsArray.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View hView = convertView;
ViewHolder holder;
JSONObject fieldObject = null;
fieldObject = fieldsArray.get(position);
if (hView == null) {
hView = mInflater.inflate(R.layout.fields_item, null);
holder = new ViewHolder();
holder.checkbox = (CheckBox) hView.findViewById(R.id.fields_checkbox);
holder.fieldsInfo = (TextView) hView.findViewById(R.id.fields_info);
hView.setTag(holder);
} else {
holder = (ViewHolder) hView.getTag();
}
try {
holder.checkbox.setText(fieldObject.getString("name"));
} catch (JSONException e) {
holder.checkbox.setText("");
}
try {
holder.fieldsInfo.setText(fieldObject.getString("description"));
} catch (JSONException e) {
holder.fieldsInfo.setText("");
}
holder.checkbox.setId(position);
holder.checkbox.setChecked(isChecked[position]);
holder.checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton button, boolean checked) {
isChecked[button.getId()] = checked;
String field = button.getText().toString();
if (checked) {
fieldsVector.add(field);
} else if (fieldsVector.contains(field)) {
fieldsVector.remove(field);
}
}
});
return hView;
}
}
class ViewHolder {
CheckBox checkbox;
TextView fieldsInfo;
}
/**
* Definition of the list adapter
*/
public class ConnectionsListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public ConnectionsListAdapter() {
mInflater = LayoutInflater.from(explorerActivity.getBaseContext());
}
@Override
public int getCount() {
return connectionsArray.size();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View hView = convertView;
TextView connection;
if (hView == null) {
hView = mInflater.inflate(R.layout.connection_item, null);
connection = (TextView) hView.findViewById(R.id.connection_name);
hView.setTag(connection);
} else {
connection = (TextView) hView.getTag();
}
SpannableString name;
name = new SpannableString(connectionsArray.get(position));
name.setSpan(new UnderlineSpan(), 0, name.length(), 0);
connection.setText(name);
return hView;
}
}
}

@ -0,0 +1,110 @@
package com.facebook.android;
import java.util.Hashtable;
import java.util.Stack;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.widget.BaseAdapter;
/*
* Fetch friends profile pictures request via AsyncTask
*/
public class FriendsGetProfilePics {
Hashtable<String, Bitmap> friendsImages;
Hashtable<String, String> positionRequested;
BaseAdapter listener;
int runningCount = 0;
Stack<ItemPair> queue;
/*
* 15 max async tasks at any given time.
*/
final static int MAX_ALLOWED_TASKS = 15;
public FriendsGetProfilePics() {
friendsImages = new Hashtable<String, Bitmap>();
positionRequested = new Hashtable<String, String>();
queue = new Stack<ItemPair>();
}
/*
* Inform the listener when the image has been downloaded. listener is
* FriendsList here.
*/
public void setListener(BaseAdapter listener) {
this.listener = listener;
reset();
}
public void reset() {
positionRequested.clear();
runningCount = 0;
queue.clear();
}
/*
* If the profile picture has already been downloaded and cached, return it
* else execute a new async task to fetch it - if total async tasks >15,
* queue the request.
*/
public Bitmap getImage(String uid, String url) {
Bitmap image = friendsImages.get(uid);
if (image != null) {
return image;
}
if (!positionRequested.containsKey(uid)) {
positionRequested.put(uid, "");
if (runningCount >= MAX_ALLOWED_TASKS) {
queue.push(new ItemPair(uid, url));
} else {
runningCount++;
new GetProfilePicAsyncTask().execute(uid, url);
}
}
return null;
}
public void getNextImage() {
if (!queue.isEmpty()) {
ItemPair item = queue.pop();
new GetProfilePicAsyncTask().execute(item.uid, item.url);
}
}
/*
* Start a AsyncTask to fetch the request
*/
private class GetProfilePicAsyncTask extends AsyncTask<Object, Void, Bitmap> {
String uid;
@Override
protected Bitmap doInBackground(Object... params) {
this.uid = (String) params[0];
String url = (String) params[1];
return Utility.getBitmap(url);
}
@Override
protected void onPostExecute(Bitmap result) {
runningCount--;
if (result != null) {
friendsImages.put(uid, result);
listener.notifyDataSetChanged();
getNextImage();
}
}
}
class ItemPair {
String uid;
String url;
public ItemPair(String uid, String url) {
this.uid = uid;
this.url = url;
}
}
}

@ -0,0 +1,216 @@
package com.facebook.android;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class FriendsList extends Activity implements OnItemClickListener {
private Handler mHandler;
protected ListView friendsList;
protected static JSONArray jsonArray;
protected String graph_or_fql;
/*
* Layout the friends' list
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
setContentView(R.layout.friends_list);
Bundle extras = getIntent().getExtras();
String apiResponse = extras.getString("API_RESPONSE");
graph_or_fql = extras.getString("METHOD");
try {
if (graph_or_fql.equals("graph")) {
jsonArray = new JSONObject(apiResponse).getJSONArray("data");
} else {
jsonArray = new JSONArray(apiResponse);
}
} catch (JSONException e) {
showToast("Error: " + e.getMessage());
return;
}
friendsList = (ListView) findViewById(R.id.friends_list);
friendsList.setOnItemClickListener(this);
friendsList.setAdapter(new FriendListAdapter(this));
showToast(getString(R.string.can_post_on_wall));
}
/*
* Clicking on a friend should popup a dialog for user to post on friend's
* wall.
*/
@Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) {
try {
final long friendId;
if (graph_or_fql.equals("graph")) {
friendId = jsonArray.getJSONObject(position).getLong("id");
} else {
friendId = jsonArray.getJSONObject(position).getLong("uid");
}
String name = jsonArray.getJSONObject(position).getString("name");
new AlertDialog.Builder(this).setTitle(R.string.post_on_wall_title)
.setMessage(String.format(getString(R.string.post_on_wall), name))
.setPositiveButton(R.string.yes, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Bundle params = new Bundle();
/*
* Source Tag: friend_wall_tag To write on a friend's wall,
* provide friend's UID in the 'to' parameter.
* More info on feed dialog:
* https://developers.facebook.com/docs/reference/dialogs/feed/
*/
params.putString("to", String.valueOf(friendId));
params.putString("caption", getString(R.string.app_name));
params.putString("description", getString(R.string.app_desc));
params.putString("picture", Utility.HACK_ICON_URL);
params.putString("name", getString(R.string.app_action));
Utility.mFacebook.dialog(FriendsList.this, "feed", params,
new PostDialogListener());
}
}).setNegativeButton(R.string.no, null).show();
} catch (JSONException e) {
showToast("Error: " + e.getMessage());
}
}
/*
* Callback after the message has been posted on friend's wall.
*/
public class PostDialogListener extends BaseDialogListener {
@Override
public void onComplete(Bundle values) {
final String postId = values.getString("post_id");
if (postId != null) {
showToast("Message posted on the wall.");
} else {
showToast("No message posted on the wall.");
}
}
}
public void showToast(final String msg) {
mHandler.post(new Runnable() {
@Override
public void run() {
Toast toast = Toast.makeText(FriendsList.this, msg, Toast.LENGTH_LONG);
toast.show();
}
});
}
/**
* Definition of the list adapter
*/
public class FriendListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
FriendsList friendsList;
public FriendListAdapter(FriendsList friendsList) {
this.friendsList = friendsList;
if (Utility.model == null) {
Utility.model = new FriendsGetProfilePics();
}
Utility.model.setListener(this);
mInflater = LayoutInflater.from(friendsList.getBaseContext());
}
@Override
public int getCount() {
return jsonArray.length();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
JSONObject jsonObject = null;
try {
jsonObject = jsonArray.getJSONObject(position);
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
View hView = convertView;
if (convertView == null) {
hView = mInflater.inflate(R.layout.friend_item, null);
ViewHolder holder = new ViewHolder();
holder.profile_pic = (ImageView) hView.findViewById(R.id.profile_pic);
holder.name = (TextView) hView.findViewById(R.id.name);
holder.info = (TextView) hView.findViewById(R.id.info);
hView.setTag(holder);
}
ViewHolder holder = (ViewHolder) hView.getTag();
try {
if (graph_or_fql.equals("graph")) {
holder.profile_pic.setImageBitmap(Utility.model.getImage(
jsonObject.getString("id"), jsonObject.getString("picture")));
} else {
holder.profile_pic.setImageBitmap(Utility.model.getImage(
jsonObject.getString("uid"), jsonObject.getString("pic_square")));
}
} catch (JSONException e) {
holder.name.setText("");
}
try {
holder.name.setText(jsonObject.getString("name"));
} catch (JSONException e) {
holder.name.setText("");
}
try {
if (graph_or_fql.equals("graph")) {
holder.info.setText(jsonObject.getJSONObject("location").getString("name"));
} else {
JSONObject location = jsonObject.getJSONObject("current_location");
holder.info.setText(location.getString("city") + ", "
+ location.getString("state"));
}
} catch (JSONException e) {
holder.info.setText("");
}
return hView;
}
}
class ViewHolder {
ImageView profile_pic;
TextView name;
TextView info;
}
}

@ -0,0 +1,359 @@
package com.facebook.android;
/*
* The me, delete and back_parent buttons are downloaded from http://icongal.com/
*/
import java.util.Iterator;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.text.util.Linkify.TransformFilter;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ScrollView;
import android.widget.TextView;
import android.widget.Toast;
public class GraphExplorer extends Activity {
private Button mSubmitButton, mViewURLButton;
private Button mGetPermissionsButton;
private Button mTextDeleteButton, mMeButton;
private Button mFieldsConnectionsButton, mBackParentButton;
private TextView mOutput;
private EditText mInputId;
private Bundle params;
private String url, mParentObjectId;
private ProgressDialog dialog;
private String rootString;
private ScrollView mScrollView;
private Handler mHandler;
private final static String BASE_GRAPH_URL = "https://graph.facebook.com";
private JSONObject metadataObject;
/*
* Layout the Graph Explorer
*/
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
setContentView(R.layout.graph_explorer);
url = BASE_GRAPH_URL; // Base URL
mInputId = (EditText) findViewById(R.id.inputId);
mOutput = (TextView) findViewById(R.id.output);
mSubmitButton = (Button) findViewById(R.id.submitButton);
mViewURLButton = (Button) findViewById(R.id.viewURLButton);
mGetPermissionsButton = (Button) findViewById(R.id.accessTokenButton);
mFieldsConnectionsButton = (Button) findViewById(R.id.fieldsAndConnectionsButton);
mBackParentButton = (Button) findViewById(R.id.backParentButton);
mScrollView = (ScrollView) findViewById(R.id.ScrollView01);
mTextDeleteButton = (Button) findViewById(R.id.textDeleteButton);
mMeButton = (Button) findViewById(R.id.meButton);
if (Utility.mFacebook.isSessionValid()) {
mMeButton.setVisibility(View.VISIBLE);
}
params = new Bundle();
mSubmitButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
((InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(mInputId.getWindowToken(), 0);
// Prepare the URL to be shown on 'View URL' click action. This
// is not used by the SDK
url = BASE_GRAPH_URL; // Base URL
/*
* Source Tag: graph_explorer
*/
rootString = mInputId.getText().toString();
if (!TextUtils.isEmpty(rootString)) {
dialog = ProgressDialog.show(GraphExplorer.this, "",
getString(R.string.please_wait), true, true);
params.putString("metadata", "1");
Utility.mAsyncRunner.request(rootString, params, new graphApiRequestListener());
url += "/" + rootString; // Relative Path provided by you
}
}
});
mViewURLButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
setText(url);
Linkify.addLinks(mOutput, Linkify.WEB_URLS);
}
});
mGetPermissionsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (Utility.mFacebook.isSessionValid()) {
dialog = ProgressDialog.show(GraphExplorer.this, "",
getString(R.string.fetching_current_permissions), true, true);
Bundle params = new Bundle();
params.putString("access_token", Utility.mFacebook.getAccessToken());
Utility.mAsyncRunner.request("me/permissions", params,
new permissionsRequestListener());
} else {
new PermissionsDialog(GraphExplorer.this).show();
}
}
});
mFieldsConnectionsButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (metadataObject == null) {
makeToast("No fields, connections availalbe for this object.");
} else {
new FieldsConnectionsDialog(GraphExplorer.this, metadataObject).show();
}
}
});
mTextDeleteButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
url = BASE_GRAPH_URL; // Base URL
mParentObjectId = "";
mInputId.setText("");
params.clear();
metadataObject = null;
setText("");
mBackParentButton.setVisibility(View.INVISIBLE);
}
});
mMeButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mInputId.setText("me");
mSubmitButton.performClick();
}
});
mBackParentButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mInputId.setText(mParentObjectId);
mParentObjectId = "";
mSubmitButton.performClick();
}
});
}
@Override
public void onResume() {
super.onResume();
if (Utility.mFacebook.isSessionValid()) {
mMeButton.setVisibility(View.VISIBLE);
}
if (Utility.objectID != null) {
mInputId.setText(Utility.objectID);
Utility.objectID = null;
mSubmitButton.performClick();
}
}
protected void processIntent(Intent incomingIntent) {
Uri intentUri = incomingIntent.getData();
if (intentUri == null) {
return;
}
String objectID = intentUri.getHost();
mInputId.setText(objectID);
mSubmitButton.performClick();
}
public void getConnection(String connection) {
mInputId.setText(rootString + "/" + connection);
mParentObjectId = rootString;
mSubmitButton.performClick();
}
public void getFields(Vector<String> fieldsVector) {
String fields = "";
int count = 0;
for (String field : fieldsVector) {
fields += field;
if (++count < fieldsVector.size()) {
fields += ",";
}
}
params.putString("fields", fields);
mSubmitButton.performClick();
}
/*
* Callback for the permission OAuth Dialog
*/
public class permissionsRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
dialog.dismiss();
/*
* Clear the current permission list and repopulate with new
* permissions. This is used to mark assigned permission green and
* unclickable.
*/
Utility.currentPermissions.clear();
try {
JSONObject jsonObject = new JSONObject(response).getJSONArray("data")
.getJSONObject(0);
Iterator<?> iterator = jsonObject.keys();
String permission;
while (iterator.hasNext()) {
permission = (String) iterator.next();
Utility.currentPermissions.put(permission,
String.valueOf(jsonObject.getInt(permission)));
}
} catch (JSONException e) {
makeToast("Permissions could not be fetched, none will be selected by default.");
}
mHandler.post(new Runnable() {
@Override
public void run() {
new PermissionsDialog(GraphExplorer.this).show();
}
});
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
makeToast("Permissions could not be fetched, none will be selected by default.");
mHandler.post(new Runnable() {
@Override
public void run() {
new PermissionsDialog(GraphExplorer.this).show();
}
});
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
Utility.mFacebook.authorizeCallback(requestCode, resultCode, data);
}
/*
* Callback after a given Graph API request is executed Get the response and
* show it.
*/
public class graphApiRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
dialog.dismiss();
// access token is appended by Facebook object, hence params are
// added here after request is complete
if (!params.isEmpty()) {
url += "?" + Util.encodeUrl(params); // Params
}
metadataObject = null;
params.clear();
try {
JSONObject json = Util.parseJson(response);
if (json.has("metadata")) {
metadataObject = json.getJSONObject("metadata");
json.remove("metadata");
} else {
metadataObject = null;
}
setText(json.toString(2));
} catch (JSONException e) {
setText(e.getMessage());
e.printStackTrace();
} catch (FacebookError e) {
setText(e.getMessage());
e.printStackTrace();
}
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
setText(error.getMessage());
params.clear();
metadataObject = null;
}
}
public void setText(final String txt) {
mHandler.post(new Runnable() {
/*
* A transform filter that simply returns just the text captured by
* the first regular expression group.
*/
TransformFilter idFilter = new TransformFilter() {
@Override
public final String transformUrl(final Matcher match, String url) {
return match.group(1);
}
};
@Override
public void run() {
mViewURLButton.setVisibility(
TextUtils.isEmpty(txt) ? View.INVISIBLE : View.VISIBLE);
mFieldsConnectionsButton.setVisibility(TextUtils.isEmpty(txt) ? View.INVISIBLE
: View.VISIBLE);
mOutput.setVisibility(TextUtils.isEmpty(txt) ? View.INVISIBLE : View.VISIBLE);
mBackParentButton.setVisibility(
TextUtils.isEmpty(mParentObjectId) ? View.INVISIBLE : View.VISIBLE);
String convertedTxt = txt.replace("\\/", "/");
mOutput.setText(convertedTxt);
mScrollView.scrollTo(0, 0);
Linkify.addLinks(mOutput, Linkify.WEB_URLS);
/*
* Linkify the object ids so they can be clicked. match pattern:
* "id" : "objectid" (objectid can be int or int_int)
*/
Pattern pattern = Pattern.compile("\"id\": \"(\\d*_?\\d*)\"");
String scheme = "fbGraphEx://";
Linkify.addLinks(mOutput, pattern, scheme, null, idFilter);
}
});
}
private void makeToast(final String msg) {
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(GraphExplorer.this, msg, Toast.LENGTH_SHORT).show();
}
});
}
}

@ -0,0 +1,627 @@
/*
* Copyright 2004 - Present 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.IOException;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ArrayAdapter;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
import com.facebook.android.SessionEvents.AuthListener;
import com.facebook.android.SessionEvents.LogoutListener;
public class Hackbook extends Activity implements OnItemClickListener {
/*
* Your Facebook Application ID must be set before running this example See
* http://www.facebook.com/developers/createapp.php
*/
public static final String APP_ID = "157111564357680";
private LoginButton mLoginButton;
private TextView mText;
private ImageView mUserPic;
private Handler mHandler;
ProgressDialog dialog;
final static int AUTHORIZE_ACTIVITY_RESULT_CODE = 0;
final static int PICK_EXISTING_PHOTO_RESULT_CODE = 1;
private String graph_or_fql;
private ListView list;
String[] main_items = { "Update Status", "App Requests", "Get Friends", "Upload Photo",
"Place Check-in", "Run FQL Query", "Graph API Explorer", "Token Refresh" };
String[] permissions = { "offline_access", "publish_stream", "user_photos", "publish_checkins",
"photo_upload" };
/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (APP_ID == null) {
Util.showAlert(this, "Warning", "Facebook Applicaton ID must be "
+ "specified before running this example: see FbAPIs.java");
return;
}
setContentView(R.layout.main);
mHandler = new Handler();
mText = (TextView) Hackbook.this.findViewById(R.id.txt);
mUserPic = (ImageView) Hackbook.this.findViewById(R.id.user_pic);
// Create the Facebook Object using the app id.
Utility.mFacebook = new Facebook(APP_ID);
// Instantiate the asynrunner object for asynchronous api calls.
Utility.mAsyncRunner = new AsyncFacebookRunner(Utility.mFacebook);
mLoginButton = (LoginButton) findViewById(R.id.login);
// restore session if one exists
SessionStore.restore(Utility.mFacebook, this);
SessionEvents.addAuthListener(new FbAPIsAuthListener());
SessionEvents.addLogoutListener(new FbAPIsLogoutListener());
/*
* Source Tag: login_tag
*/
mLoginButton.init(this, AUTHORIZE_ACTIVITY_RESULT_CODE, Utility.mFacebook, permissions);
if (Utility.mFacebook.isSessionValid()) {
requestUserData();
}
list = (ListView) findViewById(R.id.main_list);
list.setOnItemClickListener(this);
list.setAdapter(new ArrayAdapter<String>(this, R.layout.main_list_item, main_items));
}
@Override
public void onResume() {
super.onResume();
if(Utility.mFacebook != null) {
if (!Utility.mFacebook.isSessionValid()) {
mText.setText("You are logged out! ");
mUserPic.setImageBitmap(null);
} else {
Utility.mFacebook.extendAccessTokenIfNeeded(this, null);
}
}
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
/*
* if this is the activity result from authorization flow, do a call
* back to authorizeCallback Source Tag: login_tag
*/
case AUTHORIZE_ACTIVITY_RESULT_CODE: {
Utility.mFacebook.authorizeCallback(requestCode, resultCode, data);
break;
}
/*
* if this is the result for a photo picker from the gallery, upload
* the image after scaling it. You can use the Utility.scaleImage()
* function for scaling
*/
case PICK_EXISTING_PHOTO_RESULT_CODE: {
if (resultCode == Activity.RESULT_OK) {
Uri photoUri = data.getData();
if (photoUri != null) {
Bundle params = new Bundle();
try {
params.putByteArray("photo",
Utility.scaleImage(getApplicationContext(), photoUri));
} catch (IOException e) {
e.printStackTrace();
}
params.putString("caption", "FbAPIs Sample App photo upload");
Utility.mAsyncRunner.request("me/photos", params, "POST",
new PhotoUploadListener(), null);
} else {
Toast.makeText(getApplicationContext(),
"Error selecting image from the gallery.", Toast.LENGTH_SHORT)
.show();
}
} else {
Toast.makeText(getApplicationContext(), "No image selected for upload.",
Toast.LENGTH_SHORT).show();
}
break;
}
}
}
@Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) {
switch (position) {
/*
* Source Tag: update_status_tag Update user's status by invoking the
* feed dialog To post to a friend's wall, provide his uid in the 'to'
* parameter Refer to
* https://developers.facebook.com/docs/reference/dialogs/feed/ for more
* info.
*/
case 0: {
Bundle params = new Bundle();
params.putString("caption", getString(R.string.app_name));
params.putString("description", getString(R.string.app_desc));
params.putString("picture", Utility.HACK_ICON_URL);
params.putString("name", getString(R.string.app_action));
Utility.mFacebook.dialog(Hackbook.this, "feed", params, new UpdateStatusListener());
String access_token = Utility.mFacebook.getAccessToken();
System.out.println(access_token);
break;
}
/*
* Source Tag: app_requests Send an app request to friends. If no
* friend is specified, the user will see a friend selector and will
* be able to select a maximum of 50 recipients. To send request to
* specific friend, provide the uid in the 'to' parameter Refer to
* https://developers.facebook.com/docs/reference/dialogs/requests/
* for more info.
*/
case 1: {
Bundle params = new Bundle();
params.putString("message", getString(R.string.request_message));
Utility.mFacebook.dialog(Hackbook.this, "apprequests", params,
new AppRequestsListener());
break;
}
/*
* Source Tag: friends_tag You can get friends using
* graph.facebook.com/me/friends, this returns the list sorted by
* UID OR using the friend table. With this you can sort the way you
* want it.
* Friend table - https://developers.facebook.com/docs/reference/fql/friend/
* User table - https://developers.facebook.com/docs/reference/fql/user/
*/
case 2: {
if (!Utility.mFacebook.isSessionValid()) {
Util.showAlert(this, "Warning", "You must first log in.");
} else {
dialog = ProgressDialog.show(Hackbook.this, "",
getString(R.string.please_wait), true, true);
new AlertDialog.Builder(this)
.setTitle(R.string.Graph_FQL_title)
.setMessage(R.string.Graph_FQL_msg)
.setPositiveButton(R.string.graph_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
graph_or_fql = "graph";
Bundle params = new Bundle();
params.putString("fields", "name, picture, location");
Utility.mAsyncRunner.request("me/friends", params,
new FriendsRequestListener());
}
})
.setNegativeButton(R.string.fql_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
graph_or_fql = "fql";
String query = "select name, current_location, uid, pic_square from user where uid in (select uid2 from friend where uid1=me()) order by name";
Bundle params = new Bundle();
params.putString("method", "fql.query");
params.putString("query", query);
Utility.mAsyncRunner.request(null, params,
new FriendsRequestListener());
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface d) {
dialog.dismiss();
}
}).show();
}
break;
}
/*
* Source Tag: upload_photo You can upload a photo from the media
* gallery or from a remote server How to upload photo:
* https://developers.facebook.com/blog/post/498/
*/
case 3: {
if (!Utility.mFacebook.isSessionValid()) {
Util.showAlert(this, "Warning", "You must first log in.");
} else {
dialog = ProgressDialog.show(Hackbook.this, "",
getString(R.string.please_wait), true, true);
new AlertDialog.Builder(this)
.setTitle(R.string.gallery_remote_title)
.setMessage(R.string.gallery_remote_msg)
.setPositiveButton(R.string.gallery_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(Intent.ACTION_PICK,
(MediaStore.Images.Media.EXTERNAL_CONTENT_URI));
startActivityForResult(intent,
PICK_EXISTING_PHOTO_RESULT_CODE);
}
})
.setNegativeButton(R.string.remote_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
/*
* Source tag: upload_photo_tag
*/
Bundle params = new Bundle();
params.putString("url",
"http://www.facebook.com/images/devsite/iphone_connect_btn.jpg");
params.putString("caption",
"FbAPIs Sample App photo upload");
Utility.mAsyncRunner.request("me/photos", params,
"POST", new PhotoUploadListener(), null);
}
}).setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface d) {
dialog.dismiss();
}
}).show();
}
break;
}
/*
* User can check-in to a place, you require publish_checkins
* permission for that. You can use the default Times Square
* location or fetch user's current location. Get user's checkins:
* https://developers.facebook.com/docs/reference/api/checkin/
*/
case 4: {
final Intent myIntent = new Intent(getApplicationContext(), Places.class);
new AlertDialog.Builder(this)
.setTitle(R.string.get_location)
.setMessage(R.string.get_default_or_new_location)
.setPositiveButton(R.string.current_location_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
myIntent.putExtra("LOCATION", "current");
startActivity(myIntent);
}
})
.setNegativeButton(R.string.times_square_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
myIntent.putExtra("LOCATION", "times_square");
startActivity(myIntent);
}
}).show();
break;
}
case 5: {
if (!Utility.mFacebook.isSessionValid()) {
Util.showAlert(this, "Warning", "You must first log in.");
} else {
new FQLQuery(Hackbook.this).show();
}
break;
}
/*
* This is advanced feature where you can request new permissions
* Browser user's graph, his fields and connections. This is similar
* to the www version:
* http://developers.facebook.com/tools/explorer/
*/
case 6: {
Intent myIntent = new Intent(getApplicationContext(), GraphExplorer.class);
if (Utility.mFacebook.isSessionValid()) {
Utility.objectID = "me";
}
startActivity(myIntent);
break;
}
case 7: {
if(!Utility.mFacebook.isSessionValid()) {
Util.showAlert(this, "Warning", "You must first log in.");
} else {
new TokenRefreshDialog(Hackbook.this).show();
}
}
}
}
/*
* callback for the feed dialog which updates the profile status
*/
public class UpdateStatusListener extends BaseDialogListener {
@Override
public void onComplete(Bundle values) {
final String postId = values.getString("post_id");
if (postId != null) {
new UpdateStatusResultDialog(Hackbook.this, "Update Status executed", values)
.show();
} else {
Toast toast = Toast.makeText(getApplicationContext(), "No wall post made",
Toast.LENGTH_SHORT);
toast.show();
}
}
@Override
public void onFacebookError(FacebookError error) {
Toast.makeText(getApplicationContext(), "Facebook Error: " + error.getMessage(),
Toast.LENGTH_SHORT).show();
}
@Override
public void onCancel() {
Toast toast = Toast.makeText(getApplicationContext(), "Update status cancelled",
Toast.LENGTH_SHORT);
toast.show();
}
}
/*
* callback for the apprequests dialog which sends an app request to user's
* friends.
*/
public class AppRequestsListener extends BaseDialogListener {
@Override
public void onComplete(Bundle values) {
Toast toast = Toast.makeText(getApplicationContext(), "App request sent",
Toast.LENGTH_SHORT);
toast.show();
}
@Override
public void onFacebookError(FacebookError error) {
Toast.makeText(getApplicationContext(), "Facebook Error: " + error.getMessage(),
Toast.LENGTH_SHORT).show();
}
@Override
public void onCancel() {
Toast toast = Toast.makeText(getApplicationContext(), "App request cancelled",
Toast.LENGTH_SHORT);
toast.show();
}
}
/*
* callback after friends are fetched via me/friends or fql query.
*/
public class FriendsRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
dialog.dismiss();
Intent myIntent = new Intent(getApplicationContext(), FriendsList.class);
myIntent.putExtra("API_RESPONSE", response);
myIntent.putExtra("METHOD", graph_or_fql);
startActivity(myIntent);
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
Toast.makeText(getApplicationContext(), "Facebook Error: " + error.getMessage(),
Toast.LENGTH_SHORT).show();
}
}
/*
* callback for the photo upload
*/
public class PhotoUploadListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
dialog.dismiss();
mHandler.post(new Runnable() {
@Override
public void run() {
new UploadPhotoResultDialog(Hackbook.this, "Upload Photo executed", response)
.show();
}
});
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
Toast.makeText(getApplicationContext(), "Facebook Error: " + error.getMessage(),
Toast.LENGTH_LONG).show();
}
}
public class FQLRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
mHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(getApplicationContext(), "Response: " + response,
Toast.LENGTH_LONG).show();
}
});
}
public void onFacebookError(FacebookError error) {
Toast.makeText(getApplicationContext(), "Facebook Error: " + error.getMessage(),
Toast.LENGTH_LONG).show();
}
}
/*
* Callback for fetching current user's name, picture, uid.
*/
public class UserRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
JSONObject jsonObject;
try {
jsonObject = new JSONObject(response);
final String picURL = jsonObject.getString("picture");
final String name = jsonObject.getString("name");
Utility.userUID = jsonObject.getString("id");
mHandler.post(new Runnable() {
@Override
public void run() {
mText.setText("Welcome " + name + "!");
mUserPic.setImageBitmap(Utility.getBitmap(picURL));
}
});
} catch (JSONException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
/*
* The Callback for notifying the application when authorization succeeds or
* fails.
*/
public class FbAPIsAuthListener implements AuthListener {
@Override
public void onAuthSucceed() {
requestUserData();
}
@Override
public void onAuthFail(String error) {
mText.setText("Login Failed: " + error);
}
}
/*
* The Callback for notifying the application when log out starts and
* finishes.
*/
public class FbAPIsLogoutListener implements LogoutListener {
@Override
public void onLogoutBegin() {
mText.setText("Logging out...");
}
@Override
public void onLogoutFinish() {
mText.setText("You have logged out! ");
mUserPic.setImageBitmap(null);
}
}
/*
* Request user name, and picture to show on the main screen.
*/
public void requestUserData() {
mText.setText("Fetching user name, profile pic...");
Bundle params = new Bundle();
params.putString("fields", "name, picture");
Utility.mAsyncRunner.request("me", params, new UserRequestListener());
}
/**
* Definition of the list adapter
*/
public class MainListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
public MainListAdapter() {
mInflater = LayoutInflater.from(Hackbook.this.getBaseContext());
}
@Override
public int getCount() {
return main_items.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
View hView = convertView;
if (convertView == null) {
hView = mInflater.inflate(R.layout.main_list_item, null);
ViewHolder holder = new ViewHolder();
holder.main_list_item = (TextView) hView.findViewById(R.id.main_api_item);
hView.setTag(holder);
}
ViewHolder holder = (ViewHolder) hView.getTag();
holder.main_list_item.setText(main_items[position]);
return hView;
}
}
class ViewHolder {
TextView main_list_item;
}
}

@ -0,0 +1,30 @@
package com.facebook.android;
import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
/**
* A transient activity which handles fbgraphex: URIs and passes those to the
* GraphExplorer class This is used to linkify the Object IDs in the graph api
* response
*/
public class IntentUriHandler extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Intent incomingIntent = getIntent();
if (incomingIntent != null) {
Uri intentUri = incomingIntent.getData();
if (intentUri != null) {
Utility.objectID = intentUri.getHost();
Intent graphIntent = new Intent(getApplicationContext(), GraphExplorer.class);
graphIntent.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
startActivity(graphIntent);
}
finish();
}
}
}

@ -0,0 +1,151 @@
/*
* 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 android.app.Activity;
import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ImageButton;
import com.facebook.android.Facebook.DialogListener;
import com.facebook.android.SessionEvents.AuthListener;
import com.facebook.android.SessionEvents.LogoutListener;
public class LoginButton extends ImageButton {
private Facebook mFb;
private Handler mHandler;
private SessionListener mSessionListener = new SessionListener();
private String[] mPermissions;
private Activity mActivity;
private int mActivityCode;
public LoginButton(Context context) {
super(context);
}
public LoginButton(Context context, AttributeSet attrs) {
super(context, attrs);
}
public LoginButton(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init(final Activity activity, final int activityCode, final Facebook fb) {
init(activity, activityCode, fb, new String[] {});
}
public void init(final Activity activity, final int activityCode, final Facebook fb,
final String[] permissions) {
mActivity = activity;
mActivityCode = activityCode;
mFb = fb;
mPermissions = permissions;
mHandler = new Handler();
setBackgroundColor(Color.TRANSPARENT);
setImageResource(fb.isSessionValid() ? R.drawable.logout_button : R.drawable.login_button);
drawableStateChanged();
SessionEvents.addAuthListener(mSessionListener);
SessionEvents.addLogoutListener(mSessionListener);
setOnClickListener(new ButtonOnClickListener());
}
private final class ButtonOnClickListener implements OnClickListener {
/*
* Source Tag: login_tag
*/
@Override
public void onClick(View arg0) {
if (mFb.isSessionValid()) {
SessionEvents.onLogoutBegin();
AsyncFacebookRunner asyncRunner = new AsyncFacebookRunner(mFb);
asyncRunner.logout(getContext(), new LogoutRequestListener());
} else {
mFb.authorize(mActivity, mPermissions, mActivityCode, new LoginDialogListener());
}
}
}
private final class LoginDialogListener implements DialogListener {
@Override
public void onComplete(Bundle values) {
SessionEvents.onLoginSuccess();
}
@Override
public void onFacebookError(FacebookError error) {
SessionEvents.onLoginError(error.getMessage());
}
@Override
public void onError(DialogError error) {
SessionEvents.onLoginError(error.getMessage());
}
@Override
public void onCancel() {
SessionEvents.onLoginError("Action Canceled");
}
}
private class LogoutRequestListener extends BaseRequestListener {
@Override
public void onComplete(String response, final Object state) {
/*
* callback should be run in the original thread, not the background
* thread
*/
mHandler.post(new Runnable() {
@Override
public void run() {
SessionEvents.onLogoutFinish();
}
});
}
}
private class SessionListener implements AuthListener, LogoutListener {
@Override
public void onAuthSucceed() {
setImageResource(R.drawable.logout_button);
SessionStore.save(mFb, getContext());
}
@Override
public void onAuthFail(String error) {
}
@Override
public void onLogoutBegin() {
}
@Override
public void onLogoutFinish() {
SessionStore.clear(getContext());
setImageResource(R.drawable.login_button);
}
}
}

@ -0,0 +1,251 @@
package com.facebook.android;
import java.util.Vector;
import android.app.Activity;
import android.app.Dialog;
import android.graphics.Color;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.BaseAdapter;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ListView;
import android.widget.TabHost;
import android.widget.TabHost.TabSpec;
import android.widget.TextView;
import android.widget.Toast;
import com.facebook.android.Facebook.DialogListener;
public class PermissionsDialog extends Dialog {
private final static int TAB_HEIGHT = 50;
private Button mGetPermissions;
private TextView mPermissionDetails;
private Activity activity;
private ListView userPermissionsList, friendPermissionsList, extendedPermissionsList;
private BaseAdapter userPermissionsAdapter, friendPermissionsAdapter,
extendedPermissionAdapter;
protected Vector<String> reqPermVector;
String[] user_permissions = { "user_about_me", "user_activities", "user_birthday",
"user_checkins", "user_education_history", "user_events", "user_groups",
"user_hometown", "user_interests", "user_likes", "user_location", "user_notes",
"user_online_presence", "user_photos", "user_photo_video_tags", "user_relationships",
"user_relationship_details", "user_religion_politics", "user_status", "user_videos",
"user_website", "user_work_history" };
String[] friend_permissions = { "friends_about_me", "friends_activities", "friends_birthday",
"friends_checkins", "friends_education_history", "friends_events", "friends_groups",
"friends_hometown", "friends_interests", "friends_likes", "friends_location",
"friends_notes", "friends_online_presence", "friends_photos",
"friends_photo_video_tags", "friends_relationships", "friends_relationship_details",
"friends_religion_politics", "friends_status", "friends_videos", "friends_website",
"friends_work_history" };
String[] extended_permissions = { "ads_management", "create_event", "create_note", "email",
"export_stream", "manage_friendlists", "manage_groups", "manage_pages",
"offline_access", "publish_actions", "photo_upload", "publish_checkins",
"publish_stream", "read_friendlists", "read_insights", "read_mailbox", "read_requests",
"read_stream", "rsvp_event", "share_item", "status_update", "sms", "video_upload",
"xmpp_login" };
public PermissionsDialog(Activity activity) {
super(activity);
this.activity = activity;
setTitle(activity.getString(R.string.permissions_request));
reqPermVector = new Vector<String>();
}
/*
* Layout the permission dialog
*/
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.permissions_list);
LayoutParams params = getWindow().getAttributes();
params.width = LayoutParams.FILL_PARENT;
params.height = LayoutParams.FILL_PARENT;
getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
mPermissionDetails = (TextView) findViewById(R.id.permission_detail);
mPermissionDetails.setMovementMethod(LinkMovementMethod.getInstance());
userPermissionsList = (ListView) findViewById(R.id.user_permissions_list);
friendPermissionsList = (ListView) findViewById(R.id.friend_permissions_list);
extendedPermissionsList = (ListView) findViewById(R.id.extended_permissions_list);
userPermissionsAdapter = new PermissionsListAdapter(user_permissions);
userPermissionsList.setAdapter(userPermissionsAdapter);
friendPermissionsAdapter = new PermissionsListAdapter(friend_permissions);
friendPermissionsList.setAdapter(friendPermissionsAdapter);
extendedPermissionAdapter = new PermissionsListAdapter(extended_permissions);
extendedPermissionsList.setAdapter(extendedPermissionAdapter);
TabHost tabHost = (TabHost) findViewById(R.id.tabHost);
tabHost.setup();
TabSpec spec1 = tabHost.newTabSpec("Tab 1");
spec1.setIndicator(activity.getString(R.string.user));
spec1.setContent(R.id.user_permissions_list);
TabSpec spec2 = tabHost.newTabSpec("Tab 2");
spec2.setIndicator(activity.getString(R.string.friend));
spec2.setContent(R.id.friend_permissions_list);
TabSpec spec3 = tabHost.newTabSpec("Tab 3");
spec3.setIndicator(activity.getString(R.string.extended));
spec3.setContent(R.id.extended_permissions_list);
tabHost.addTab(spec1);
tabHost.addTab(spec2);
tabHost.addTab(spec3);
tabHost.setCurrentTab(0);
tabHost.getTabWidget().getChildAt(0).getLayoutParams().height = TAB_HEIGHT;
tabHost.getTabWidget().getChildAt(1).getLayoutParams().height = TAB_HEIGHT;
tabHost.getTabWidget().getChildAt(2).getLayoutParams().height = TAB_HEIGHT;
mGetPermissions = (Button) findViewById(R.id.get_permissions_button);
mGetPermissions.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* Source Tag: perms_tag Call authorize to get the new
* permissions
*/
if (reqPermVector.isEmpty() && Utility.mFacebook.isSessionValid()) {
Toast.makeText(activity.getBaseContext(), "No Permissions selected.",
Toast.LENGTH_SHORT).show();
PermissionsDialog.this.dismiss();
} else {
String[] permissions = reqPermVector.toArray(new String[0]);
Utility.mFacebook.authorize(activity, permissions, new LoginDialogListener());
}
}
});
}
/*
* Callback when user has authorized the app with the new permissions
*/
private final class LoginDialogListener implements DialogListener {
@Override
public void onComplete(Bundle values) {
// Inform the parent loginlistener so it can update the user's
// profile pic and name on the home screen.
SessionEvents.onLoginSuccess();
Toast.makeText(activity.getBaseContext(), "New Permissions granted.",
Toast.LENGTH_SHORT).show();
PermissionsDialog.this.dismiss();
}
@Override
public void onFacebookError(FacebookError error) {
Toast.makeText(activity.getBaseContext(),
"Facebook Error! No new permissions granted.", Toast.LENGTH_SHORT).show();
PermissionsDialog.this.dismiss();
}
@Override
public void onError(DialogError error) {
Toast.makeText(activity.getBaseContext(), "Error! No new permissions granted.",
Toast.LENGTH_SHORT).show();
PermissionsDialog.this.dismiss();
}
@Override
public void onCancel() {
Toast.makeText(activity.getBaseContext(),
"Action cancelled, No new permissions granted.", Toast.LENGTH_SHORT).show();
PermissionsDialog.this.dismiss();
}
}
/**
* Definition of the list adapter
*/
public class PermissionsListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
String[] permissions;
boolean[] isChecked;
public PermissionsListAdapter(String[] permissions) {
this.permissions = permissions;
this.isChecked = new boolean[permissions.length];
mInflater = LayoutInflater.from(activity.getBaseContext());
}
@Override
public int getCount() {
return permissions.length;
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
View hView = convertView;
CheckBox checkbox;
if (hView == null) {
hView = mInflater.inflate(R.layout.permission_item, null);
checkbox = (CheckBox) hView.findViewById(R.id.permission_checkbox);
hView.setTag(checkbox);
} else {
checkbox = (CheckBox) hView.getTag();
}
checkbox.setText(this.permissions[position]);
checkbox.setId(position);
if (Utility.currentPermissions.containsKey(this.permissions[position])
&& Utility.currentPermissions.get(this.permissions[position]).equals("1")) {
checkbox.setTextColor(Color.GREEN);
checkbox.setChecked(true);
checkbox.setEnabled(false);
checkbox.setOnCheckedChangeListener(null);
} else {
checkbox.setTextColor(Color.WHITE);
checkbox.setChecked(this.isChecked[position]);
checkbox.setEnabled(true);
checkbox.setOnCheckedChangeListener(new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton button, boolean checked) {
isChecked[button.getId()] = checked;
if (checked) {
reqPermVector.add(button.getText().toString());
} else if (reqPermVector.contains(button.getText())) {
reqPermVector.remove(button.getText());
}
}
});
}
return hView;
}
}
}

@ -0,0 +1,363 @@
package com.facebook.android;
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.TextView;
import android.widget.Toast;
public class Places extends Activity implements OnItemClickListener {
private Handler mHandler;
private JSONObject location;
protected ListView placesList;
protected LocationManager lm;
protected MyLocationListener locationListener;
protected static JSONArray jsonArray;
final static double TIMES_SQUARE_LAT = 40.756;
final static double TIMES_SQUARE_LON = -73.987;
protected ProgressDialog dialog;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
location = new JSONObject();
setContentView(R.layout.places_list);
Bundle extras = getIntent().getExtras();
String default_or_new = extras.getString("LOCATION");
if (default_or_new.equals("times_square")) {
try {
location.put("latitude", new Double(TIMES_SQUARE_LAT));
location.put("longitude", new Double(TIMES_SQUARE_LON));
} catch (JSONException e) {
}
fetchPlaces();
} else {
getLocation();
}
}
public void getLocation() {
/*
* launch a new Thread to get new location
*/
new Thread() {
@Override
public void run() {
Looper.prepare();
dialog = ProgressDialog.show(Places.this, "",
getString(R.string.fetching_location), false, true,
new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
showToast("No location fetched.");
}
});
if (lm == null) {
lm = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
}
if (locationListener == null) {
locationListener = new MyLocationListener();
}
Criteria criteria = new Criteria();
criteria.setAccuracy(Criteria.ACCURACY_COARSE);
String provider = lm.getBestProvider(criteria, true);
if (provider != null && lm.isProviderEnabled(provider)) {
lm.requestLocationUpdates(provider, 1, 0, locationListener,
Looper.getMainLooper());
} else {
/*
* GPS not enabled, prompt user to enable GPS in the
* Location menu
*/
new AlertDialog.Builder(Places.this)
.setTitle(R.string.enable_gps_title)
.setMessage(getString(R.string.enable_gps))
.setPositiveButton(R.string.gps_settings,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
startActivityForResult(
new Intent(
android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS),
0);
}
})
.setNegativeButton(R.string.cancel,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
Places.this.finish();
}
}).show();
}
Looper.loop();
}
}.start();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
/*
* User returning from the Location settings menu. try to fetch location
* again.
*/
dialog.dismiss();
getLocation();
}
/*
* Fetch nearby places by providing the search type as 'place' within 1000
* mtrs of the provided lat & lon
*/
private void fetchPlaces() {
if (!isFinishing()) {
dialog = ProgressDialog.show(Places.this, "", getString(R.string.nearby_places), true,
true, new DialogInterface.OnCancelListener() {
@Override
public void onCancel(DialogInterface dialog) {
showToast("No places fetched.");
}
});
}
/*
* Source tag: fetch_places_tag
*/
Bundle params = new Bundle();
params.putString("type", "place");
try {
params.putString("center",
location.getString("latitude") + "," + location.getString("longitude"));
} catch (JSONException e) {
showToast("No places fetched.");
return;
}
params.putString("distance", "1000");
Utility.mAsyncRunner.request("search", params, new placesRequestListener());
}
/*
* Callback after places are fetched.
*/
public class placesRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
Log.d("Facebook-FbAPIs", "Got response: " + response);
dialog.dismiss();
try {
jsonArray = new JSONObject(response).getJSONArray("data");
if (jsonArray == null) {
showToast("Error: nearby places could not be fetched");
return;
}
} catch (JSONException e) {
showToast("Error: " + e.getMessage());
return;
}
mHandler.post(new Runnable() {
@Override
public void run() {
placesList = (ListView) findViewById(R.id.places_list);
placesList.setOnItemClickListener(Places.this);
placesList.setAdapter(new PlacesListAdapter(Places.this));
}
});
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
showToast("Fetch Places Error: " + error.getMessage());
}
}
@Override
public void onItemClick(AdapterView<?> arg0, View v, int position, long arg3) {
if (!Utility.mFacebook.isSessionValid()) {
Util.showAlert(this, "Warning", "You must first log in.");
} else {
try {
final String message = "Check-in from the " + getString(R.string.app_name);
final String name = jsonArray.getJSONObject(position).getString("name");
final String placeID = jsonArray.getJSONObject(position).getString("id");
new AlertDialog.Builder(this).setTitle(R.string.check_in_title)
.setMessage(String.format(getString(R.string.check_in_at), name))
.setPositiveButton(R.string.checkin, new DialogInterface.OnClickListener() {
/*
* Source tag: check_in_tag Check-in user at the
* selected location posting to the me/checkins
* endpoint. More info here:
* https://developers.facebook
* .com/docs/reference/api/user/ - checkins
*/
@Override
public void onClick(DialogInterface dialog, int which) {
Bundle params = new Bundle();
params.putString("place", placeID);
params.putString("message", message);
params.putString("coordinates", location.toString());
Utility.mAsyncRunner.request("me/checkins", params, "POST",
new placesCheckInListener(), null);
}
}).setNegativeButton(R.string.cancel, null).show();
} catch (JSONException e) {
showToast("Error: " + e.getMessage());
}
}
}
public class placesCheckInListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
showToast("API Response: " + response);
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
showToast("Check-in Error: " + error.getMessage());
}
}
public void showToast(final String msg) {
mHandler.post(new Runnable() {
@Override
public void run() {
Toast toast = Toast.makeText(Places.this, msg, Toast.LENGTH_LONG);
toast.show();
}
});
}
/**
* Definition of the list adapter
*/
public class PlacesListAdapter extends BaseAdapter {
private LayoutInflater mInflater;
Places placesList;
public PlacesListAdapter(Context context) {
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return jsonArray.length();
}
@Override
public Object getItem(int position) {
return null;
}
@Override
public long getItemId(int position) {
return 0;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
JSONObject jsonObject = null;
try {
jsonObject = jsonArray.getJSONObject(position);
} catch (JSONException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
View hView = convertView;
if (convertView == null) {
hView = mInflater.inflate(R.layout.place_item, null);
ViewHolder holder = new ViewHolder();
holder.name = (TextView) hView.findViewById(R.id.place_name);
holder.location = (TextView) hView.findViewById(R.id.place_location);
hView.setTag(holder);
}
ViewHolder holder = (ViewHolder) hView.getTag();
try {
holder.name.setText(jsonObject.getString("name"));
} catch (JSONException e) {
holder.name.setText("");
}
try {
String location = jsonObject.getJSONObject("location").getString("street") + ", "
+ jsonObject.getJSONObject("location").getString("city") + ", "
+ jsonObject.getJSONObject("location").getString("state");
holder.location.setText(location);
} catch (JSONException e) {
holder.location.setText("");
}
return hView;
}
}
class ViewHolder {
TextView name;
TextView location;
}
class MyLocationListener implements LocationListener {
@Override
public void onLocationChanged(Location loc) {
dialog.dismiss();
if (loc != null) {
try {
location.put("latitude", new Double(loc.getLatitude()));
location.put("longitude", new Double(loc.getLongitude()));
} catch (JSONException e) {
}
showToast("Location acquired: " + String.valueOf(loc.getLatitude()) + " "
+ String.valueOf(loc.getLongitude()));
lm.removeUpdates(this);
fetchPlaces();
}
}
@Override
public void onProviderDisabled(String provider) {
}
@Override
public void onProviderEnabled(String provider) {
}
@Override
public void onStatusChanged(String provider, int status, Bundle extras) {
}
}
}

@ -0,0 +1,138 @@
/*
* 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.util.LinkedList;
public class SessionEvents {
private static LinkedList<AuthListener> mAuthListeners = new LinkedList<AuthListener>();
private static LinkedList<LogoutListener> mLogoutListeners = new LinkedList<LogoutListener>();
/**
* Associate the given listener with this Facebook object. The listener's
* callback interface will be invoked when authentication events occur.
*
* @param listener
* The callback object for notifying the application when auth
* events happen.
*/
public static void addAuthListener(AuthListener listener) {
mAuthListeners.add(listener);
}
/**
* Remove the given listener from the list of those that will be notified
* when authentication events occur.
*
* @param listener
* The callback object for notifying the application when auth
* events happen.
*/
public static void removeAuthListener(AuthListener listener) {
mAuthListeners.remove(listener);
}
/**
* Associate the given listener with this Facebook object. The listener's
* callback interface will be invoked when logout occurs.
*
* @param listener
* The callback object for notifying the application when log out
* starts and finishes.
*/
public static void addLogoutListener(LogoutListener listener) {
mLogoutListeners.add(listener);
}
/**
* Remove the given listener from the list of those that will be notified
* when logout occurs.
*
* @param listener
* The callback object for notifying the application when log out
* starts and finishes.
*/
public static void removeLogoutListener(LogoutListener listener) {
mLogoutListeners.remove(listener);
}
public static void onLoginSuccess() {
for (AuthListener listener : mAuthListeners) {
listener.onAuthSucceed();
}
}
public static void onLoginError(String error) {
for (AuthListener listener : mAuthListeners) {
listener.onAuthFail(error);
}
}
public static void onLogoutBegin() {
for (LogoutListener l : mLogoutListeners) {
l.onLogoutBegin();
}
}
public static void onLogoutFinish() {
for (LogoutListener l : mLogoutListeners) {
l.onLogoutFinish();
}
}
/**
* Callback interface for authorization events.
*/
public static interface AuthListener {
/**
* Called when a auth flow completes successfully and a valid OAuth
* Token was received. Executed by the thread that initiated the
* authentication. API requests can now be made.
*/
public void onAuthSucceed();
/**
* Called when a login completes unsuccessfully with an error.
*
* Executed by the thread that initiated the authentication.
*/
public void onAuthFail(String error);
}
/**
* Callback interface for logout events.
*/
public static interface LogoutListener {
/**
* Called when logout begins, before session is invalidated. Last chance
* to make an API call. Executed by the thread that initiated the
* logout.
*/
public void onLogoutBegin();
/**
* Called when the session information has been cleared. UI should be
* updated to reflect logged-out state.
*
* Executed by the thread that initiated the logout.
*/
public void onLogoutFinish();
}
}

@ -0,0 +1,57 @@
/*
* 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 android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
public class SessionStore {
private static final String TOKEN = "access_token";
private static final String EXPIRES = "expires_in";
private static final String KEY = "facebook-session";
/*
* Save the access token and expiry date so you don't have to fetch it each
* time
*/
public static boolean save(Facebook session, Context context) {
Editor editor = context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
editor.putString(TOKEN, session.getAccessToken());
editor.putLong(EXPIRES, session.getAccessExpires());
return editor.commit();
}
/*
* Restore the access token and the expiry date from the shared preferences.
*/
public static boolean restore(Facebook session, Context context) {
SharedPreferences savedSession = context.getSharedPreferences(KEY, Context.MODE_PRIVATE);
session.setAccessToken(savedSession.getString(TOKEN, null));
session.setAccessExpires(savedSession.getLong(EXPIRES, 0));
return session.isSessionValid();
}
public static void clear(Context context) {
Editor editor = context.getSharedPreferences(KEY, Context.MODE_PRIVATE).edit();
editor.clear();
editor.commit();
}
}

@ -0,0 +1,29 @@
package com.facebook.android;
import java.util.Timer;
import java.util.TimerTask;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
public class SplashActivity extends Activity {
private long splashDelay = 1500;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splash);
TimerTask task = new TimerTask() {
@Override
public void run() {
finish();
Intent hackbookIntent = new Intent().setClass(SplashActivity.this, Hackbook.class);
startActivity(hackbookIntent);
}
};
Timer timer = new Timer();
timer.schedule(task, splashDelay);
}
}

@ -0,0 +1,96 @@
package com.facebook.android;
import java.text.DateFormat;
import java.util.Date;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
public class TokenRefreshDialog extends Dialog {
private EditText tokenEdit, tokenExpiresEdit;
private TextView mUsefulTip;
private Button mRefreshButton;
private Activity activity;
public TokenRefreshDialog(Activity activity) {
super(activity);
this.activity = activity;
setTitle(R.string.refresh_token_title);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.token_refresh);
tokenEdit = (EditText) findViewById(R.id.tokenEdit);
tokenEdit.setText(Utility.mFacebook.getAccessToken());
tokenExpiresEdit = (EditText) findViewById(R.id.tokenExpiresEdit);
setExpiresAt(Utility.mFacebook.getAccessExpires());
mUsefulTip = (TextView) findViewById(R.id.usefulTip);
mUsefulTip.setMovementMethod(LinkMovementMethod.getInstance());
mRefreshButton = (Button) findViewById(R.id.refresh_button);
mRefreshButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
changeButtonState(false);
RefreshTokenListener listener = new RefreshTokenListener();
if (!Utility.mFacebook.extendAccessToken(activity, listener)) {
listener.onError(new Error(
activity.getString(R.string.refresh_token_binding_error)));
}
}
});
}
private class RefreshTokenListener implements Facebook.ServiceListener {
@Override
public void onFacebookError(FacebookError e) {
changeButtonState(true);
String title = String.format(activity.getString(R.string.facebook_error) + "%d",
e.getErrorCode());
Util.showAlert(activity, title, e.getMessage());
}
@Override
public void onError(Error e) {
changeButtonState(true);
Util.showAlert(activity, activity.getString(R.string.error), e.getMessage());
}
@Override
public void onComplete(Bundle values) {
changeButtonState(true);
// The access_token and expires_at values are automatically updated,
// so they can be obtained by using:
// - Facebook.getAccessToken()
// - Facebook.getAccessExpires()
// methods, but we can also get them from the 'values' bundle.
tokenEdit.setText(values.getString(Facebook.TOKEN));
setExpiresAt(values.getLong(Facebook.EXPIRES));
}
}
private void changeButtonState(boolean enabled) {
mRefreshButton.setEnabled(enabled);
mRefreshButton.setText(enabled ? R.string.refresh_button : R.string.refresh_button_pending);
}
private void setExpiresAt(long time) {
DateFormat dateFormat = DateFormat.getDateTimeInstance(DateFormat.SHORT, DateFormat.SHORT);
tokenExpiresEdit.setText(dateFormat.format(new Date(time)));
}
}

@ -0,0 +1,116 @@
package com.facebook.android;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.Dialog;
import android.os.Bundle;
import android.os.Handler;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.TextView;
public class UpdateStatusResultDialog extends Dialog {
private Bundle values;
private TextView mOutput, mUsefulTip;
private Button mViewPostButton, mDeletePostButton;
private Activity activity;
private Handler mHandler;
public UpdateStatusResultDialog(Activity activity, String title, Bundle values) {
super(activity);
this.activity = activity;
this.values = values;
setTitle(title);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
setContentView(R.layout.update_post_response);
LayoutParams params = getWindow().getAttributes();
params.width = LayoutParams.FILL_PARENT;
params.height = LayoutParams.FILL_PARENT;
getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
mOutput = (TextView) findViewById(R.id.apiOutput);
mOutput.setText(values.toString());
mUsefulTip = (TextView) findViewById(R.id.usefulTip);
mUsefulTip.setMovementMethod(LinkMovementMethod.getInstance());
mViewPostButton = (Button) findViewById(R.id.view_post_button);
mDeletePostButton = (Button) findViewById(R.id.delete_post_button);
final String postId = values.getString("post_id");
mViewPostButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* Source tag: view_post_tag
*/
Utility.mAsyncRunner.request(postId, new WallPostRequestListener());
}
});
mDeletePostButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* Source tag: delete_post_tag
*/
Utility.mAsyncRunner.request(postId, new Bundle(), "DELETE",
new WallPostDeleteListener(), null);
}
});
}
public class WallPostRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
try {
JSONObject json = new JSONObject(response);
setText(json.toString(2));
} catch (JSONException e) {
setText(activity.getString(R.string.exception) + e.getMessage());
}
}
public void onFacebookError(FacebookError error) {
setText(activity.getString(R.string.facebook_error) + error.getMessage());
}
}
public class WallPostDeleteListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
if (response.equals("true")) {
String message = "Wall Post deleted" + "\n";
message += "Api Response: " + response;
setText(message);
} else {
setText("wall post could not be deleted");
}
}
public void onFacebookError(FacebookError error) {
setText(activity.getString(R.string.facebook_error) + error.getMessage());
}
}
public void setText(final String txt) {
mHandler.post(new Runnable() {
@Override
public void run() {
mOutput.setText(txt);
}
});
}
}

@ -0,0 +1,185 @@
package com.facebook.android;
import org.json.JSONException;
import org.json.JSONObject;
import android.app.Activity;
import android.app.Dialog;
import android.app.ProgressDialog;
import android.graphics.Bitmap;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
public class UploadPhotoResultDialog extends Dialog {
private String response, photo_id;
private TextView mOutput, mUsefulTip;
private Button mViewPhotoButton, mTagPhotoButton;
private ImageView mUploadedPhoto;
private Activity activity;
private ProgressDialog dialog;
private boolean hidePhoto = false;
private Handler mHandler;
public UploadPhotoResultDialog(Activity activity, String title, String response) {
super(activity);
this.activity = activity;
this.response = response;
setTitle(title);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mHandler = new Handler();
setContentView(R.layout.upload_photo_response);
LayoutParams params = getWindow().getAttributes();
params.width = LayoutParams.FILL_PARENT;
params.height = LayoutParams.FILL_PARENT;
getWindow().setAttributes((android.view.WindowManager.LayoutParams) params);
mOutput = (TextView) findViewById(R.id.apiOutput);
mUsefulTip = (TextView) findViewById(R.id.usefulTip);
mViewPhotoButton = (Button) findViewById(R.id.view_photo_button);
mTagPhotoButton = (Button) findViewById(R.id.tag_photo_button);
mUploadedPhoto = (ImageView) findViewById(R.id.uploadedPhoto);
JSONObject json;
try {
json = Util.parseJson(response);
final String photo_id = json.getString("id");
this.photo_id = photo_id;
mOutput.setText(json.toString(2));
mUsefulTip.setText(activity.getString(R.string.photo_tip));
Linkify.addLinks(mUsefulTip, Linkify.WEB_URLS);
mViewPhotoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (hidePhoto) {
mViewPhotoButton.setText(R.string.view_photo);
hidePhoto = false;
mUploadedPhoto.setImageBitmap(null);
} else {
hidePhoto = true;
mViewPhotoButton.setText(R.string.hide_photo);
/*
* Source tag: view_photo_tag
*/
Bundle params = new Bundle();
params.putString("fields", "picture");
dialog = ProgressDialog.show(activity, "",
activity.getString(R.string.please_wait), true, true);
dialog.show();
Utility.mAsyncRunner.request(photo_id, params,
new ViewPhotoRequestListener());
}
}
});
mTagPhotoButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
/*
* Source tag: tag_photo_tag
*/
setTag();
}
});
} catch (JSONException e) {
setText(activity.getString(R.string.exception) + e.getMessage());
} catch (FacebookError e) {
setText(activity.getString(R.string.facebook_error) + e.getMessage());
}
}
public void setTag() {
String relativePath = photo_id + "/tags/" + Utility.userUID;
Bundle params = new Bundle();
params.putString("x", "5");
params.putString("y", "5");
Utility.mAsyncRunner.request(relativePath, params, "POST", new TagPhotoRequestListener(),
null);
}
public class ViewPhotoRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
try {
JSONObject json = Util.parseJson(response);
final String pictureURL = json.getString("picture");
if (TextUtils.isEmpty(pictureURL)) {
setText("Error getting \'picture\' field of the photo");
} else {
mHandler.post(new Runnable() {
@Override
public void run() {
new FetchImage().execute(pictureURL);
}
});
}
} catch (JSONException e) {
dialog.dismiss();
setText(activity.getString(R.string.exception) + e.getMessage());
} catch (FacebookError e) {
dialog.dismiss();
setText(activity.getString(R.string.facebook_error) + e.getMessage());
}
}
public void onFacebookError(FacebookError error) {
dialog.dismiss();
setText(activity.getString(R.string.facebook_error) + error.getMessage());
}
}
public class TagPhotoRequestListener extends BaseRequestListener {
@Override
public void onComplete(final String response, final Object state) {
if (response.equals("true")) {
String message = "User tagged in photo at (5, 5)" + "\n";
message += "Api Response: " + response;
setText(message);
} else {
setText("User could not be tagged.");
}
}
public void onFacebookError(FacebookError error) {
setText(activity.getString(R.string.facebook_error) + error.getMessage());
}
}
public void setText(final String txt) {
mHandler.post(new Runnable() {
@Override
public void run() {
mOutput.setText(txt);
}
});
}
private class FetchImage extends AsyncTask<String, Void, Bitmap> {
@Override
protected Bitmap doInBackground(String... urls) {
return Utility.getBitmap(urls[0]);
}
@Override
protected void onPostExecute(Bitmap result) {
dialog.dismiss();
mUploadedPhoto.setImageBitmap(result);
}
}
}

@ -0,0 +1,153 @@
package com.facebook.android;
import java.io.BufferedInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FilterInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.Hashtable;
import org.json.JSONObject;
import android.app.Application;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.net.Uri;
import android.net.http.AndroidHttpClient;
import android.provider.MediaStore;
public class Utility extends Application {
public static Facebook mFacebook;
public static AsyncFacebookRunner mAsyncRunner;
public static JSONObject mFriendsList;
public static String userUID = null;
public static String objectID = null;
public static FriendsGetProfilePics model;
public static AndroidHttpClient httpclient = null;
public static Hashtable<String, String> currentPermissions = new Hashtable<String, String>();
private static int MAX_IMAGE_DIMENSION = 720;
public static final String HACK_ICON_URL = "http://www.facebookmobileweb.com/hackbook/img/facebook_icon_large.png";
public static Bitmap getBitmap(String url) {
Bitmap bm = null;
try {
URL aURL = new URL(url);
URLConnection conn = aURL.openConnection();
conn.connect();
InputStream is = conn.getInputStream();
BufferedInputStream bis = new BufferedInputStream(is);
bm = BitmapFactory.decodeStream(new FlushedInputStream(is));
bis.close();
is.close();
} catch (Exception e) {
e.printStackTrace();
} finally {
if (httpclient != null) {
httpclient.close();
}
}
return bm;
}
static class FlushedInputStream extends FilterInputStream {
public FlushedInputStream(InputStream inputStream) {
super(inputStream);
}
@Override
public long skip(long n) throws IOException {
long totalBytesSkipped = 0L;
while (totalBytesSkipped < n) {
long bytesSkipped = in.skip(n - totalBytesSkipped);
if (bytesSkipped == 0L) {
int b = read();
if (b < 0) {
break; // we reached EOF
} else {
bytesSkipped = 1; // we read one byte
}
}
totalBytesSkipped += bytesSkipped;
}
return totalBytesSkipped;
}
}
public static byte[] scaleImage(Context context, Uri photoUri) throws IOException {
InputStream is = context.getContentResolver().openInputStream(photoUri);
BitmapFactory.Options dbo = new BitmapFactory.Options();
dbo.inJustDecodeBounds = true;
BitmapFactory.decodeStream(is, null, dbo);
is.close();
int rotatedWidth, rotatedHeight;
int orientation = getOrientation(context, photoUri);
if (orientation == 90 || orientation == 270) {
rotatedWidth = dbo.outHeight;
rotatedHeight = dbo.outWidth;
} else {
rotatedWidth = dbo.outWidth;
rotatedHeight = dbo.outHeight;
}
Bitmap srcBitmap;
is = context.getContentResolver().openInputStream(photoUri);
if (rotatedWidth > MAX_IMAGE_DIMENSION || rotatedHeight > MAX_IMAGE_DIMENSION) {
float widthRatio = ((float) rotatedWidth) / ((float) MAX_IMAGE_DIMENSION);
float heightRatio = ((float) rotatedHeight) / ((float) MAX_IMAGE_DIMENSION);
float maxRatio = Math.max(widthRatio, heightRatio);
// Create the bitmap from file
BitmapFactory.Options options = new BitmapFactory.Options();
options.inSampleSize = (int) maxRatio;
srcBitmap = BitmapFactory.decodeStream(is, null, options);
} else {
srcBitmap = BitmapFactory.decodeStream(is);
}
is.close();
/*
* if the orientation is not 0 (or -1, which means we don't know), we
* have to do a rotation.
*/
if (orientation > 0) {
Matrix matrix = new Matrix();
matrix.postRotate(orientation);
srcBitmap = Bitmap.createBitmap(srcBitmap, 0, 0, srcBitmap.getWidth(),
srcBitmap.getHeight(), matrix, true);
}
String type = context.getContentResolver().getType(photoUri);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
if (type.equals("image/png")) {
srcBitmap.compress(Bitmap.CompressFormat.PNG, 100, baos);
} else if (type.equals("image/jpg") || type.equals("image/jpeg")) {
srcBitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
}
byte[] bMapArray = baos.toByteArray();
baos.close();
return bMapArray;
}
public static int getOrientation(Context context, Uri photoUri) {
/* it's on the external media. */
Cursor cursor = context.getContentResolver().query(photoUri,
new String[] { MediaStore.Images.ImageColumns.ORIENTATION }, null, null, null);
if (cursor.getCount() != 1) {
return -1;
}
cursor.moveToFirst();
return cursor.getInt(0);
}
}

@ -0,0 +1,12 @@
# 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 use,
# "build.properties", and override values to adapt the script to your
# project structure.
android.library=true
# Project target.
target=android-3

@ -0,0 +1,20 @@
# To enable ProGuard in your project, edit project.properties
# to define the proguard.config property as described in that file.
#
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in ${sdk.dir}/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the ProGuard
# include property in project.properties.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

@ -23,16 +23,22 @@ import java.net.MalformedURLException;
import android.Manifest;
import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.pm.Signature;
import android.os.Bundle;
import android.os.Handler;
import android.os.IBinder;
import android.os.Message;
import android.os.Messenger;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.Log;
import android.webkit.CookieSyncManager;
/**
@ -70,6 +76,7 @@ public class Facebook {
"https://api.facebook.com/restserver.php";
private String mAccessToken = null;
private long mLastAccessUpdate = 0;
private long mAccessExpires = 0;
private String mAppId;
@ -77,6 +84,10 @@ public class Facebook {
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;
/**
* Constructor for Facebook object.
@ -225,7 +236,7 @@ public class Facebook {
// Verify that the application whose package name is
// com.facebook.katana.ProxyAuth
// has the expected FB app signature.
if (!validateAppSignatureForIntent(activity, intent)) {
if (!validateActivityIntent(activity, intent)) {
return false;
}
@ -242,27 +253,62 @@ public class Facebook {
}
/**
* Query the signature for the application that would be invoked by the
* given intent and verify that it matches the FB application's signature.
* Helper to validate an activity intent by resolving and checking the
* provider's package signature.
*
* @param activity
* @param context
* @param intent
* @param validSignature
* @return true if the app's signature matches the expected signature.
* @return true if the service intent resolution happens successfully and the
* signatures match.
*/
private boolean validateAppSignatureForIntent(Activity activity,
Intent intent) {
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 =
activity.getPackageManager().resolveActivity(intent, 0);
context.getPackageManager().resolveService(intent, 0);
if (resolveInfo == null) {
return false;
}
String packageName = resolveInfo.activityInfo.packageName;
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 = activity.getPackageManager().getPackageInfo(
packageInfo = context.getPackageManager().getPackageInfo(
packageName, PackageManager.GET_SIGNATURES);
} catch (NameNotFoundException e) {
return false;
@ -302,7 +348,7 @@ public class Facebook {
setAccessToken(values.getString(TOKEN));
setAccessExpiresIn(values.getString(EXPIRES));
if (isSessionValid()) {
Log.d("Facebook-authorize", "Login Success! access_token="
Util.logd("Facebook-authorize", "Login Success! access_token="
+ getAccessToken() + " expires="
+ getAccessExpires());
mAuthDialogListener.onComplete(values);
@ -313,17 +359,17 @@ public class Facebook {
}
public void onError(DialogError error) {
Log.d("Facebook-authorize", "Login failed: " + error);
Util.logd("Facebook-authorize", "Login failed: " + error);
mAuthDialogListener.onError(error);
}
public void onFacebookError(FacebookError error) {
Log.d("Facebook-authorize", "Login failed: " + error);
Util.logd("Facebook-authorize", "Login failed: " + error);
mAuthDialogListener.onFacebookError(error);
}
public void onCancel() {
Log.d("Facebook-authorize", "Login canceled");
Util.logd("Facebook-authorize", "Login canceled");
mAuthDialogListener.onCancel();
}
});
@ -358,17 +404,21 @@ public class Facebook {
if (error != null) {
if (error.equals(SINGLE_SIGN_ON_DISABLED)
|| error.equals("AndroidAuthKillSwitchException")) {
Log.d("Facebook-authorize", "Hosted auth currently "
Util.logd("Facebook-authorize", "Hosted auth currently "
+ "disabled. Retrying dialog auth...");
startDialogAuth(mAuthActivity, mAuthPermissions);
} else if (error.equals("access_denied")
|| error.equals("OAuthAccessDeniedException")) {
Log.d("Facebook-authorize", "Login canceled by user.");
Util.logd("Facebook-authorize", "Login canceled by user.");
mAuthDialogListener.onCancel();
} else {
Log.d("Facebook-authorize", "Login failed: " + error);
String description = data.getStringExtra("error_description");
if (description != null) {
error = error + ":" + description;
}
Util.logd("Facebook-authorize", "Login failed: " + error);
mAuthDialogListener.onFacebookError(
new FacebookError(error));
new FacebookError(error));
}
// No errors.
@ -376,7 +426,7 @@ public class Facebook {
setAccessToken(data.getStringExtra(TOKEN));
setAccessExpiresIn(data.getStringExtra(EXPIRES));
if (isSessionValid()) {
Log.d("Facebook-authorize",
Util.logd("Facebook-authorize",
"Login Success! access_token="
+ getAccessToken() + " expires="
+ getAccessExpires());
@ -392,7 +442,7 @@ public class Facebook {
// An Android error occured.
if (data != null) {
Log.d("Facebook-authorize",
Util.logd("Facebook-authorize",
"Login failed: " + data.getStringExtra("error"));
mAuthDialogListener.onError(
new DialogError(
@ -402,13 +452,155 @@ public class Facebook {
// User pressed the 'back' button.
} else {
Log.d("Facebook-authorize", "Login canceled by user.");
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
@ -655,34 +847,36 @@ public class Facebook {
/**
* Set the OAuth 2.0 access token for API access.
*
* @param token
* - access token
* @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
* @param time - timestamp in milliseconds
*/
public void setAccessExpires(long time) {
mAccessExpires = time;
}
/**
* Set the current session's duration (in seconds since Unix epoch).
* Set the current session's duration (in seconds since Unix epoch), or "0"
* if session doesn't expire.
*
* @param expiresIn
* - duration in seconds
* - duration in seconds (or 0 if the session doesn't expire)
*/
public void setAccessExpiresIn(String expiresIn) {
if (expiresIn != null && !expiresIn.equals("0")) {
setAccessExpires(System.currentTimeMillis()
+ Integer.parseInt(expiresIn) * 1000);
if (expiresIn != null) {
long expires = expiresIn.equals("0")
? 0
: System.currentTimeMillis() + Long.parseLong(expiresIn) * 1000L;
setAccessExpires(expires);
}
}
@ -735,6 +929,31 @@ public class Facebook {
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"

@ -22,27 +22,27 @@ import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Display;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.Window;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.facebook.android.Facebook.DialogListener;
public class FbDialog extends Dialog {
static final int FB_BLUE = 0xFF6D84B4;
static final float[] DIMENSIONS_LANDSCAPE = {460, 260};
static final float[] DIMENSIONS_PORTRAIT = {280, 420};
static final float[] DIMENSIONS_DIFF_LANDSCAPE = {20, 60};
static final float[] DIMENSIONS_DIFF_PORTRAIT = {40, 60};
static final FrameLayout.LayoutParams FILL =
new FrameLayout.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,
ViewGroup.LayoutParams.FILL_PARENT);
@ -54,12 +54,12 @@ public class FbDialog extends Dialog {
private String mUrl;
private DialogListener mListener;
private ProgressDialog mSpinner;
private ImageView mCrossImage;
private WebView mWebView;
private LinearLayout mContent;
private TextView mTitle;
private FrameLayout mContent;
public FbDialog(Context context, String url, DialogListener listener) {
super(context);
super(context, android.R.style.Theme_Translucent_NoTitleBar);
mUrl = url;
mListener = listener;
}
@ -70,38 +70,49 @@ public class FbDialog extends Dialog {
mSpinner = new ProgressDialog(getContext());
mSpinner.requestWindowFeature(Window.FEATURE_NO_TITLE);
mSpinner.setMessage("Loading...");
mContent = new LinearLayout(getContext());
mContent.setOrientation(LinearLayout.VERTICAL);
setUpTitle();
setUpWebView();
Display display = getWindow().getWindowManager().getDefaultDisplay();
final float scale = getContext().getResources().getDisplayMetrics().density;
float[] dimensions =
(display.getWidth() < display.getHeight())
? DIMENSIONS_PORTRAIT : DIMENSIONS_LANDSCAPE;
addContentView(mContent, new FrameLayout.LayoutParams(
(int) (dimensions[0] * scale + 0.5f),
(int) (dimensions[1] * scale + 0.5f)));
}
private void setUpTitle() {
requestWindowFeature(Window.FEATURE_NO_TITLE);
Drawable icon = getContext().getResources().getDrawable(
R.drawable.facebook_icon);
mTitle = new TextView(getContext());
mTitle.setText("Facebook");
mTitle.setTextColor(Color.WHITE);
mTitle.setTypeface(Typeface.DEFAULT_BOLD);
mTitle.setBackgroundColor(FB_BLUE);
mTitle.setPadding(MARGIN + PADDING, MARGIN, MARGIN, MARGIN);
mTitle.setCompoundDrawablePadding(MARGIN + PADDING);
mTitle.setCompoundDrawablesWithIntrinsicBounds(
icon, null, null, null);
mContent.addView(mTitle);
mContent = new FrameLayout(getContext());
/* Create the 'x' image, but don't add to the mContent layout yet
* at this point, we only need to know its drawable width and height
* to place the webview
*/
createCrossImage();
/* Now we know 'x' drawable width and height,
* layout the webivew and add it the mContent layout
*/
int crossWidth = mCrossImage.getDrawable().getIntrinsicWidth();
setUpWebView(crossWidth / 2);
/* Finally add the 'x' image to the mContent layout and
* add mContent to the Dialog view
*/
mContent.addView(mCrossImage, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
addContentView(mContent, new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
}
private void createCrossImage() {
mCrossImage = new ImageView(getContext());
// Dismiss the dialog when user click on the 'x'
mCrossImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mListener.onCancel();
FbDialog.this.dismiss();
}
});
Drawable crossDrawable = getContext().getResources().getDrawable(R.drawable.close);
mCrossImage.setImageDrawable(crossDrawable);
/* 'x' should not be visible while webview is loading
* make it visible only after webview has fully loaded
*/
mCrossImage.setVisibility(View.INVISIBLE);
}
private void setUpWebView() {
private void setUpWebView(int margin) {
LinearLayout webViewContainer = new LinearLayout(getContext());
mWebView = new WebView(getContext());
mWebView.setVerticalScrollBarEnabled(false);
mWebView.setHorizontalScrollBarEnabled(false);
@ -109,14 +120,18 @@ public class FbDialog extends Dialog {
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.loadUrl(mUrl);
mWebView.setLayoutParams(FILL);
mContent.addView(mWebView);
mWebView.setVisibility(View.INVISIBLE);
webViewContainer.setPadding(margin, margin, margin, margin);
webViewContainer.addView(mWebView);
mContent.addView(webViewContainer);
}
private class FbWebViewClient extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
Log.d("Facebook-WebView", "Redirect URL: " + url);
Util.logd("Facebook-WebView", "Redirect URL: " + url);
if (url.startsWith(Facebook.REDIRECT_URI)) {
Bundle values = Util.parseUrl(url);
@ -160,7 +175,7 @@ public class FbDialog extends Dialog {
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
Log.d("Facebook-WebView", "Webview loading URL: " + url);
Util.logd("Facebook-WebView", "Webview loading URL: " + url);
super.onPageStarted(view, url, favicon);
mSpinner.show();
}
@ -168,12 +183,14 @@ public class FbDialog extends Dialog {
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
String title = mWebView.getTitle();
if (title != null && title.length() > 0) {
mTitle.setText(title);
}
mSpinner.dismiss();
/*
* Once webview is fully loaded, set the mContent background to be transparent
* and make visible the 'x' image.
*/
mContent.setBackgroundColor(Color.TRANSPARENT);
mWebView.setVisibility(View.VISIBLE);
mCrossImage.setVisibility(View.VISIBLE);
}
}
}

@ -47,6 +47,12 @@ import android.webkit.CookieSyncManager;
*/
public final class Util {
/**
* Set this to true to enable log output. Remember to turn this back off
* before releasing. Sending sensitive data to log is a security risk.
*/
private static boolean ENABLE_LOG = false;
/**
* Generate the multi-part post body providing the parameters and boundary
* string
@ -144,7 +150,7 @@ public final class Util {
if (method.equals("GET")) {
url = url + "?" + encodeUrl(params);
}
Log.d("Facebook-Util", method + " URL: " + url);
Util.logd("Facebook-Util", method + " URL: " + url);
HttpURLConnection conn =
(HttpURLConnection) new URL(url).openConnection();
conn.setRequestProperty("User-Agent", System.getProperties().
@ -298,4 +304,17 @@ public final class Util {
alertBuilder.create().show();
}
/**
* A proxy for Log.d api that kills log messages in release build. It
* not recommended to send sensitive information to log output in
* shipping apps.
*
* @param tag
* @param msg
*/
public static void logd(String tag, String msg) {
if (ENABLE_LOG) {
Log.d(tag, msg);
}
}
}

Loading…
Cancel
Save