Home | History | Annotate | Download | only in setup
      1 package com.android.email.activity.setup;
      2 
      3 import android.app.Activity;
      4 import android.app.LoaderManager.LoaderCallbacks;
      5 import android.content.Context;
      6 import android.content.Intent;
      7 import android.content.Loader;
      8 import android.net.Uri;
      9 import android.os.Bundle;
     10 import android.text.TextUtils;
     11 import android.webkit.CookieManager;
     12 import android.webkit.CookieSyncManager;
     13 import android.webkit.WebView;
     14 import android.webkit.WebViewClient;
     15 import android.widget.Toast;
     16 
     17 import com.android.email.mail.internet.OAuthAuthenticator;
     18 import com.android.email.mail.internet.OAuthAuthenticator.AuthenticationResult;
     19 import com.android.email.R;
     20 import com.android.emailcommon.Logging;
     21 import com.android.emailcommon.VendorPolicyLoader.OAuthProvider;
     22 import com.android.emailcommon.mail.AuthenticationFailedException;
     23 import com.android.emailcommon.mail.MessagingException;
     24 import com.android.mail.ui.MailAsyncTaskLoader;
     25 import com.android.mail.utils.LogUtils;
     26 
     27 import java.io.IOException;
     28 
     29 
     30 /**
     31  * Activity to display a webview to perform oauth authentication. This activity
     32  * should obtain an authorization code, which can be used to obtain access and
     33  * refresh tokens.
     34  */
     35 public class OAuthAuthenticationActivity extends Activity implements
     36         LoaderCallbacks<AuthenticationResult> {
     37     public static final String EXTRA_EMAIL_ADDRESS = "email_address";
     38     public static final String EXTRA_PROVIDER = "provider";
     39     public static final String EXTRA_PROVIDER_ID = "provider_id";
     40     public static final String EXTRA_AUTHENTICATION_CODE = "authentication_code";
     41 
     42     public static final int LOADER_ID_OAUTH_TOKEN = 1;
     43 
     44     public static final String EXTRA_OAUTH_ACCESS_TOKEN = "accessToken";
     45     public static final String EXTRA_OAUTH_REFRESH_TOKEN = "refreshToken";
     46     public static final String EXTRA_OAUTH_EXPIRES_IN = "expiresIn";
     47 
     48     public static final int REQUEST_OAUTH = 1;
     49 
     50     public static final int RESULT_OAUTH_SUCCESS = Activity.RESULT_FIRST_USER + 0;
     51     public static final int RESULT_OAUTH_USER_CANCELED = Activity.RESULT_FIRST_USER + 1;
     52     public static final int RESULT_OAUTH_FAILURE = Activity.RESULT_FIRST_USER + 2;
     53 
     54     private WebView mWv;
     55     private OAuthProvider mProvider;
     56     private String mAuthenticationCode;
     57 
     58     private class MyWebViewClient extends WebViewClient {
     59 
     60         @Override
     61         public boolean shouldOverrideUrlLoading(WebView wv, String url) {
     62             // TODO: This method works for Google's redirect url to https://localhost.
     63             // Does it work for the general case? I don't know what redirect url other
     64             // providers use, or how the authentication code is returned.
     65             final String deparameterizedUrl;
     66             int i = url.lastIndexOf('?');
     67             if (i == -1) {
     68                 deparameterizedUrl = url;
     69             } else {
     70                 deparameterizedUrl = url.substring(0,i);
     71             }
     72 
     73             if (TextUtils.equals(deparameterizedUrl, mProvider.redirectUri)) {
     74                 final Uri uri = Uri.parse(url);
     75                 // Check the params of this uri, they contain success/failure info,
     76                 // along with the authentication token.
     77                 final String error = uri.getQueryParameter("error");
     78 
     79                 if (error != null) {
     80                     final Intent intent = new Intent();
     81                     setResult(RESULT_OAUTH_USER_CANCELED, intent);
     82                     finish();
     83                 } else {
     84                     mAuthenticationCode = uri.getQueryParameter("code");
     85                     Bundle params = new Bundle();
     86                     params.putString(EXTRA_PROVIDER_ID, mProvider.id);
     87                     params.putString(EXTRA_AUTHENTICATION_CODE, mAuthenticationCode);
     88                     getLoaderManager().initLoader(LOADER_ID_OAUTH_TOKEN, params,
     89                             OAuthAuthenticationActivity.this);
     90                 }
     91                 return true;
     92             } else {
     93                 return false;
     94             }
     95         }
     96     }
     97 
     98     @Override
     99     public void onCreate(Bundle bundle) {
    100         super.onCreate(bundle);
    101         CookieSyncManager.createInstance(this);
    102         CookieManager cm = CookieManager.getInstance();
    103         cm.removeAllCookie();
    104 
    105         mWv = new WebView(this);
    106         mWv.setWebViewClient(new MyWebViewClient());
    107         mWv.getSettings().setJavaScriptEnabled(true);
    108         setContentView(mWv);
    109 
    110         final Intent i = getIntent();
    111         final String email = i.getStringExtra(EXTRA_EMAIL_ADDRESS);
    112         final String providerName = i.getStringExtra(EXTRA_PROVIDER);
    113         mProvider = AccountSettingsUtils.findOAuthProvider(this, providerName);
    114         final Uri uri = AccountSettingsUtils.createOAuthRegistrationRequest(this, mProvider, email);
    115         mWv.loadUrl(uri.toString());
    116 
    117         if (bundle != null) {
    118             mAuthenticationCode = bundle.getString(EXTRA_AUTHENTICATION_CODE);
    119         } else {
    120             mAuthenticationCode = null;
    121         }
    122         if (mAuthenticationCode != null) {
    123             Bundle params = new Bundle();
    124             params.putString(EXTRA_PROVIDER_ID, mProvider.id);
    125             params.putString(EXTRA_AUTHENTICATION_CODE, mAuthenticationCode);
    126             getLoaderManager().initLoader(LOADER_ID_OAUTH_TOKEN, params,
    127                     OAuthAuthenticationActivity.this);
    128         }
    129         // Set the result to cancelled until we have success.
    130         setResult(RESULT_OAUTH_USER_CANCELED, null);
    131     }
    132 
    133     @Override
    134     protected void onSaveInstanceState(Bundle outState) {
    135         super.onSaveInstanceState(outState);
    136         outState.putString(EXTRA_AUTHENTICATION_CODE, mAuthenticationCode);
    137     }
    138 
    139     private static class OAuthTokenLoader extends MailAsyncTaskLoader<AuthenticationResult> {
    140         private final String mProviderId;
    141         private final String mCode;
    142 
    143         public OAuthTokenLoader(Context context, String providerId, String code) {
    144             super(context);
    145             mProviderId = providerId;
    146             mCode = code;
    147         }
    148 
    149         @Override
    150         protected void onDiscardResult(AuthenticationResult result) {
    151 
    152         }
    153 
    154         @Override
    155         public AuthenticationResult loadInBackground() {
    156             try {
    157                 final OAuthAuthenticator authenticator = new OAuthAuthenticator();
    158                 final AuthenticationResult result = authenticator.requestAccess(
    159                         getContext(), mProviderId, mCode);
    160                 LogUtils.d(Logging.LOG_TAG, "authentication %s", result);
    161                 return result;
    162                 // TODO: do I need a better UI for displaying exceptions?
    163             } catch (AuthenticationFailedException e) {
    164             } catch (MessagingException e) {
    165             } catch (IOException e) {
    166             }
    167             return null;
    168         }
    169     }
    170 
    171     @Override
    172     public Loader<AuthenticationResult> onCreateLoader(int id, Bundle data) {
    173         if (id == LOADER_ID_OAUTH_TOKEN) {
    174             final String providerId = data.getString(EXTRA_PROVIDER_ID);
    175             final String code = data.getString(EXTRA_AUTHENTICATION_CODE);
    176             return new OAuthTokenLoader(this, providerId, code);
    177         }
    178         return null;
    179     }
    180 
    181     @Override
    182     public void onLoadFinished(Loader<AuthenticationResult> loader,
    183         AuthenticationResult data) {
    184         if (data == null) {
    185             // TODO: need a better way to display errors. We might get IO or
    186             // MessagingExceptions.
    187             setResult(RESULT_OAUTH_FAILURE, null);
    188             Toast.makeText(this, R.string.oauth_error_description, Toast.LENGTH_SHORT).show();
    189             LogUtils.w(Logging.LOG_TAG, "null oauth result");
    190         } else {
    191             final Intent intent = new Intent();
    192             intent.putExtra(EXTRA_OAUTH_ACCESS_TOKEN, data.mAccessToken);
    193             intent.putExtra(EXTRA_OAUTH_REFRESH_TOKEN, data.mRefreshToken);
    194             intent.putExtra(EXTRA_OAUTH_EXPIRES_IN, data.mExpiresInSeconds);
    195             setResult(RESULT_OAUTH_SUCCESS, intent);
    196         }
    197         finish();
    198     }
    199 
    200     @Override
    201     public void onLoaderReset(Loader<AuthenticationResult> loader) {
    202 
    203     }
    204 }
    205