/**
* Copyright 2010 Mark Wyszomierski
*
* 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 com.joelapenna.foursquared.R;
import android.app.Activity;
import android.app.ProgressDialog;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.Window;
import android.view.ViewGroup.LayoutParams;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.LinearLayout;
import android.widget.TextView;
/**
* This activity can be used to run a facebook url request through a webview.
* The user must supply these intent extras:
* <ul>
* <li>INTENT_EXTRA_ACTION - string, which facebook action to perform, like
* "login", or "stream.publish".</li>
* <li>INTENT_EXTRA_KEY_APP_ID - string, facebook developer key.</li>
* <li>INTENT_EXTRA_KEY_PERMISSIONS - string array, set of facebook permissions
* you want to use.</li>
* </ul>
* or you can supply only INTENT_EXTRA_KEY_CLEAR_COOKIES to just have the
* activity clear its stored cookies (you can also supply it in combination with
* the above flags to clear cookies before trying to run a request too). If
* you've already authenticated the user, you can optionally pass in the token
* and expiration time as intent extras using:
* <ul>
* <li>INTENT_EXTRA_AUTHENTICATED_TOKEN</li>
* <li>INTENT_EXTRA_AUTHENTICATED_EXPIRES</li>
* </ul>
* they will then be used in web requests. You should use
* <code>startActivityForResult</code> to start the activity. When the activity
* finishes, it will return status code RESULT_OK. You can then check the
* returned intent data object for:
* <ul>
* <li>INTENT_RESULT_KEY_RESULT_STATUS - boolean, whether the request succeeded
* or not.</li>
* <li>INTENT_RESULT_KEY_SUPPLIED_ACTION - string, the action you supplied as an
* intent extra echoed back as a convenience.</li>
* <li>INTENT_RESULT_KEY_RESULT_BUNDLE - bundle, present if request succeeded,
* will have all the returned parameters as supplied by the WebView operation.</li>
* <li>INTENT_RESULT_KEY_ERROR - string, present if request failed.</li>
* </ul>
* If the user canceled this activity, the activity result code will be
* RESULT_CANCELED and there will be no intent data returned. You need the
* <code>android.permission.INTERNET</code> permission added to your manifest.
* You need to add this activity definition to your manifest. You can prevent
* this activity from restarting on rotation so the network operations are
* preserved within the WebView like so:
*
* <activity
* android:name="com.facebook.android.FacebookWebViewActivity"
* android:configChanges="orientation|keyboardHidden" />
*
* This class and the rest of the facebook classes within this package are from
* the facebook android library project:
*
* http://github.com/facebook/facebook-android-sdk
*
* The project implementation had several problems with it which made it unusable
* in a production application. It has been rewritten here for use with the
* Foursquare project.
*
* @date June 14, 2010
* @author Mark Wyszomierski (markww@gmail.com)
*/
public class FacebookWebViewActivity extends Activity {
private static final String TAG = "FacebookWebViewActivity";
public static final String INTENT_EXTRA_ACTION = "com.facebook.android.FacebookWebViewActivity.action";
public static final String INTENT_EXTRA_KEY_APP_ID = "com.facebook.android.FacebookWebViewActivity.appid";
public static final String INTENT_EXTRA_KEY_PERMISSIONS = "com.facebook.android.FacebookWebViewActivity.permissions";
public static final String INTENT_EXTRA_AUTHENTICATED_TOKEN = "com.facebook.android.FacebookWebViewActivity.authenticated_token";
public static final String INTENT_EXTRA_AUTHENTICATED_EXPIRES = "com.facebook.android.FacebookWebViewActivity.authenticated_expires";
public static final String INTENT_EXTRA_KEY_CLEAR_COOKIES = "com.facebook.android.FacebookWebViewActivity.clear_cookies";
public static final String INTENT_EXTRA_KEY_DEBUG = "com.facebook.android.FacebookWebViewActivity.debug";
public static final String INTENT_RESULT_KEY_RESULT_STATUS = "result_status";
public static final String INTENT_RESULT_KEY_SUPPLIED_ACTION = "supplied_action";
public static final String INTENT_RESULT_KEY_RESULT_BUNDLE = "bundle";
public static final String INTENT_RESULT_KEY_ERROR = "error";
private static final String DISPLAY_STRING = "touch";
private static final int FB_BLUE = 0xFF6D84B4;
private static final int MARGIN = 4;
private static final int PADDING = 2;
private TextView mTitle;
private WebView mWebView;
private ProgressDialog mSpinner;
private String mAction;
private String mAppId;
private String[] mPermissions;
private boolean mDebug;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
CookieSyncManager.createInstance(this);
LinearLayout ll = new LinearLayout(this);
ll.setOrientation(LinearLayout.VERTICAL);
ll.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT));
mTitle = new TextView(this);
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(this.getResources().getDrawable(
R.drawable.facebook_icon), null, null, null);
ll.addView(mTitle);
mWebView = new WebView(this);
mWebView.setLayoutParams(new LayoutParams(
LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
mWebView.setWebViewClient(new WebViewClientFacebook());
mWebView.setVerticalScrollBarEnabled(false);
mWebView.setHorizontalScrollBarEnabled(false);
mWebView.getSettings().setJavaScriptEnabled(true);
ll.addView(mWebView);
mSpinner = new ProgressDialog(this);
mSpinner.requestWindowFeature(Window.FEATURE_NO_TITLE);
mSpinner.setMessage("Loading...");
setContentView(ll, new LinearLayout.LayoutParams(LayoutParams.FILL_PARENT,
LayoutParams.FILL_PARENT));
Bundle extras = getIntent().getExtras();
if (extras != null) {
if (extras.containsKey(INTENT_EXTRA_KEY_DEBUG)) {
mDebug = extras.getBoolean(INTENT_EXTRA_KEY_DEBUG);
}
if (extras.containsKey(INTENT_EXTRA_ACTION)) {
if (extras.getBoolean(INTENT_EXTRA_KEY_CLEAR_COOKIES, false)) {
clearCookies();
}
if (extras.containsKey(INTENT_EXTRA_KEY_APP_ID)) {
if (extras.containsKey(INTENT_EXTRA_KEY_PERMISSIONS)) {
mAction = extras.getString(INTENT_EXTRA_ACTION);
mAppId = extras.getString(INTENT_EXTRA_KEY_APP_ID);
mPermissions = extras.getStringArray(INTENT_EXTRA_KEY_PERMISSIONS);
// If the user supplied a pre-authenticated info, use it
// here.
Facebook facebook = new Facebook();
if (extras.containsKey(INTENT_EXTRA_AUTHENTICATED_TOKEN) &&
extras.containsKey(INTENT_EXTRA_AUTHENTICATED_EXPIRES)) {
facebook.setAccessToken(extras
.getString(INTENT_EXTRA_AUTHENTICATED_TOKEN));
facebook.setAccessExpires(extras
.getLong(INTENT_EXTRA_AUTHENTICATED_EXPIRES));
if (mDebug) {
Log.d(TAG, "onCreate(): authenticated token being used.");
}
}
// Generate the url based on the action.
String url = facebook.generateUrl(mAction, mAppId, mPermissions);
if (mDebug) {
String permissionsDump = "(null)";
if (mPermissions != null) {
if (mPermissions.length > 0) {
for (int i = 0; i < mPermissions.length; i++) {
permissionsDump += mPermissions[i] + ", ";
}
} else {
permissionsDump = "[empty]";
}
}
Log.d(TAG, "onCreate(): action: " + mAction + ", appid: " + mAppId
+ ", permissions: " + permissionsDump);
Log.d(TAG, "onCreate(): Loading url: " + url);
}
// Start the request finally.
mWebView.loadUrl(url);
} else {
Log.e(TAG, "Missing intent extra: INTENT_EXTRA_KEY_PERMISSIONS, finishing immediately.");
finish();
}
} else {
Log.e(TAG, "Missing intent extra: INTENT_EXTRA_KEY_APP_ID, finishing immediately.");
finish();
}
} else if (extras.getBoolean(INTENT_EXTRA_KEY_CLEAR_COOKIES)) {
clearCookies();
} else {
Log.e(TAG, "Missing intent extra: INTENT_EXTRA_ACTION or INTENT_EXTRA_KEY_CLEAR_COOKIES, finishing immediately.");
finish();
}
} else {
Log.e(TAG, "No intent extras supplied, finishing immediately.");
finish();
}
}
private void clearCookies() {
CookieManager cookieManager = CookieManager.getInstance();
cookieManager.removeAllCookie();
}
@Override
protected void onResume() {
super.onResume();
CookieSyncManager.getInstance().startSync();
}
@Override
protected void onPause() {
super.onPause();
CookieSyncManager.getInstance().stopSync();
}
private class WebViewClientFacebook extends WebViewClient {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (mDebug) {
Log.d(TAG, "WebViewClientFacebook:shouldOverrideUrlLoading(): " + url);
}
if (url.startsWith(Facebook.REDIRECT_URI)) {
Bundle values = FacebookUtil.parseUrl(url);
String error = values.getString("error_reason");
Intent result = new Intent();
result.putExtra(INTENT_RESULT_KEY_SUPPLIED_ACTION, mAction);
if (error == null) {
CookieSyncManager.getInstance().sync();
result.putExtra(INTENT_RESULT_KEY_RESULT_STATUS, true);
result.putExtra(INTENT_RESULT_KEY_RESULT_BUNDLE, values);
FacebookWebViewActivity.this.setResult(Activity.RESULT_OK, result);
} else {
result.putExtra(INTENT_RESULT_KEY_RESULT_STATUS, false);
result.putExtra(INTENT_RESULT_KEY_SUPPLIED_ACTION, mAction);
result.putExtra(INTENT_RESULT_KEY_ERROR, error);
FacebookWebViewActivity.this.setResult(Activity.RESULT_OK, result);
}
FacebookWebViewActivity.this.finish();
return true;
} else if (url.startsWith(Facebook.CANCEL_URI)) {
FacebookWebViewActivity.this.setResult(Activity.RESULT_CANCELED);
FacebookWebViewActivity.this.finish();
return true;
} else if (url.contains(DISPLAY_STRING)) {
return false;
}
// Launch non-dialog URLs in a full browser.
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
}
@Override
public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
super.onReceivedError(view, errorCode, description, failingUrl);
if (mDebug) {
Log.e(TAG, "WebViewClientFacebook:onReceivedError(): " + errorCode + ", "
+ description + ", " + failingUrl);
}
Intent result = new Intent();
result.putExtra(INTENT_RESULT_KEY_RESULT_STATUS, false);
result.putExtra(INTENT_RESULT_KEY_SUPPLIED_ACTION, mAction);
result.putExtra(INTENT_RESULT_KEY_ERROR, description + ", " + errorCode + ", "
+ failingUrl);
FacebookWebViewActivity.this.setResult(Activity.RESULT_OK, result);
FacebookWebViewActivity.this.finish();
}
@Override
public void onPageStarted(WebView view, String url, Bitmap favicon) {
super.onPageStarted(view, url, favicon);
if (mDebug) {
Log.d(TAG, "WebViewClientFacebook:onPageStarted(): " + url);
}
mSpinner.show();
}
@Override
public void onPageFinished(WebView view, String url) {
super.onPageFinished(view, url);
if (mDebug) {
Log.d(TAG, "WebViewClientFacebook:onPageFinished(): " + url);
}
String title = mWebView.getTitle();
if (title != null && title.length() > 0) {
mTitle.setText(title);
}
mSpinner.dismiss();
}
}
}