/* * Copyright 2012 Evernote Corporation. * All rights reserved. * * Redistribution and use in source and binary forms, with or without modification, * are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, this * list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. * IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ package com.evernote.client.android; import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.os.Build; import android.os.Parcel; import android.os.Parcelable; import android.text.TextUtils; import android.util.Log; import android.webkit.CookieManager; import android.webkit.CookieSyncManager; import com.evernote.client.oauth.EvernoteAuthToken; import java.util.Locale; /** * Represents a session with the Evernote web service API. Used to authenticate * to the service via OAuth and obtain NoteStore.Client objects, which are used * to make authenticated API calls. * * To use EvernoteSession, first initialize the EvernoteSession singleton and * initiate authentication at an appropriate time: * <pre> * EvernoteSession session = EvernoteSession.init(...); * if (!session.isLoggedIn()) { * session.authenticate(...); * } * </pre> * * When authentication completes, you will want to trap the result in onActivityResult * to see if it was successful: * <pre> * public void onActivityResult(int requestCode, int resultCode, Intent data) { * super.onActivityResult(requestCode, resultCode, data); * switch(requestCode) { * case EvernoteSession.REQUEST_CODE_OAUTH: * if (resultCode == Activity.RESULT_OK) { * // OAuth login was successful, do the appropriate thing for your app * } * break; * } * } * </pre> * * Later, you can make any Evernote API calls that you need by obtaining a * NoteStore.Client from the session and using the session's auth token: * <pre> * NoteStore.client noteStore = session.createNoteStoreClient(); * Notebook notebook = noteStore.getDefaultNotebook(session.getAuthToken()); * </pre> * * class created by @tylersmithnet */ public class EvernoteSession { private static final String LOGTAG = "EvernoteSession"; // Standard hostnames for bootstrap detection public static final String HOST_SANDBOX = "https://sandbox.evernote.com"; public static final String HOST_PRODUCTION = "https://www.evernote.com"; public static final String HOST_CHINA = "https://app.yinxiang.com"; /** * Evernote Service to use with the bootstrap profile detection. * Sandbox will return profiles referencing sandbox.evernote.com * Production will return evernote.com and app.yinxiang.com */ public enum EvernoteService implements Parcelable { SANDBOX, PRODUCTION; @Override public int describeContents() { return 0; } @Override public void writeToParcel(final Parcel dest, final int flags) { dest.writeInt(ordinal()); } public static final Creator<EvernoteService> CREATOR = new Creator<EvernoteService>() { @Override public EvernoteService createFromParcel(final Parcel source) { return EvernoteService.values()[source.readInt()]; } @Override public EvernoteService[] newArray(final int size) { return new EvernoteService[size]; } }; } public static final int REQUEST_CODE_OAUTH = 14390; private static EvernoteSession sInstance = null; private String mConsumerKey; private String mConsumerSecret; private EvernoteService mEvernoteService; private BootstrapManager mBootstrapManager; private ClientFactory mClientFactory; private AuthenticationResult mAuthenticationResult; /** * Use to acquire a singleton instance of the EvernoteSession for authentication. * If the singleton has already been initialized, the existing instance will * be returned (and the parameters passed to this method will be ignored). * * @param ctx * @param consumerKey The consumer key portion of your application's API key. * @param consumerSecret The consumer secret portion of your application's API key. * @param evernoteService The enum of the Evernote service instance that you wish * to use. Development and testing is typically performed against {@link EvernoteService#SANDBOX}. * The production Evernote service is {@link EvernoteService#HOST_PRODUCTION}. * * @return The EvernoteSession singleton instance. * @throws IllegalArgumentException */ public static EvernoteSession getInstance(Context ctx, String consumerKey, String consumerSecret, EvernoteService evernoteService) throws IllegalArgumentException{ if (sInstance == null) { sInstance = new EvernoteSession(ctx, consumerKey, consumerSecret, evernoteService); } return sInstance; } /** * Used to access the initialized EvernoteSession singleton instance. * * @return The previously initialized EvernoteSession instance, * or null if {@link #getInstance(android.content.Context, String, String, com.evernote.client.android.EvernoteSession.EvernoteService)} * has not been called yet. */ static EvernoteSession getOpenSession() { return sInstance; } /** * Private constructor. */ private EvernoteSession(Context ctx, String consumerKey, String consumerSecret, EvernoteService evernoteService) throws IllegalArgumentException { if( ctx == null || TextUtils.isEmpty(consumerKey) || TextUtils.isEmpty(consumerSecret) || evernoteService == null) { throw new IllegalArgumentException("Parameters canot be null or empty"); } mConsumerKey = consumerKey; mConsumerSecret = consumerSecret; mEvernoteService = evernoteService; synchronized (this) { mAuthenticationResult = getAuthenticationResultFromPref(SessionPreferences.getPreferences(ctx)); } mClientFactory = new ClientFactory(generateUserAgentString(ctx), ctx.getFilesDir()); mBootstrapManager = new BootstrapManager(mEvernoteService, mClientFactory); } /** * * @return the Bootstrap object to check for server host urls */ protected BootstrapManager getBootstrapSession() { return mBootstrapManager; } /** * Use this to create {@link AsyncNoteStoreClient} and {@link AsyncUserStoreClient} */ public ClientFactory getClientFactory() { return mClientFactory; } /** * Restore an AuthenticationResult from shared preferences. * @return The restored AuthenticationResult, or null if the preferences * did not contain the required information. */ private AuthenticationResult getAuthenticationResultFromPref(SharedPreferences prefs) { AuthenticationResult authResult = new AuthenticationResult(prefs); if (TextUtils.isEmpty(authResult.getEvernoteHost()) || TextUtils.isEmpty(authResult.getAuthToken()) || TextUtils.isEmpty(authResult.getNoteStoreUrl()) || TextUtils.isEmpty(authResult.getWebApiUrlPrefix()) || TextUtils.isEmpty(authResult.getEvernoteHost())) { return null; } return authResult; } /** * Get the authentication token that is used to make API calls * though a NoteStore.Client. * * @return the authentication token, or null if {@link #isLoggedIn()} * is false. */ public String getAuthToken() { if (mAuthenticationResult != null) { return mAuthenticationResult.getAuthToken(); } else { return null; } } /** * Get the authentication information returned by a successful * OAuth authentication to the Evernote web service. */ public AuthenticationResult getAuthenticationResult() { return mAuthenticationResult; } /** * Construct a user-agent string based on the running application and * the device and operating system information. This information is * included in HTTP requests made to the Evernote service and assists * in measuring traffic and diagnosing problems. */ private String generateUserAgentString(Context ctx) { // com.evernote.sample Android/216817 (en); Android/4.0.3; Xoom/15;" String packageName = null; int packageVersion = 0; try { packageName= ctx.getPackageName(); packageVersion = ctx.getPackageManager().getPackageInfo(packageName, 0).versionCode; } catch (PackageManager.NameNotFoundException e) { Log.e(LOGTAG, e.getMessage()); } String userAgent = packageName+ " Android/" +packageVersion; Locale locale = java.util.Locale.getDefault(); if (locale == null) { userAgent += " ("+Locale.US+");"; } else { userAgent += " (" + locale.toString()+ "); "; } userAgent += "Android/"+Build.VERSION.RELEASE+"; "; userAgent += Build.MODEL + "/" + Build.VERSION.SDK_INT + ";"; return userAgent; } /** * Start the OAuth authentication process. * * TODO do we need to do anything special here if you're already logged in? */ public void authenticate(Context ctx) { // Create an activity that will be used for authentication Intent intent = new Intent(ctx, EvernoteOAuthActivity.class); intent.putExtra(EvernoteOAuthActivity.EXTRA_EVERNOTE_SERVICE, (Parcelable) mEvernoteService); intent.putExtra(EvernoteOAuthActivity.EXTRA_CONSUMER_KEY, mConsumerKey); intent.putExtra(EvernoteOAuthActivity.EXTRA_CONSUMER_SECRET, mConsumerSecret); if (ctx instanceof Activity) { //If this is being called from an activity, an activity can register for the result code ((Activity)ctx).startActivityForResult(intent, REQUEST_CODE_OAUTH); } else { //If this is being called from a service, the refresh will be handled manually intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); ctx.startActivity(intent); } } /** * Called upon completion of the OAuth process to save resulting authentication * information into the application's SharedPreferences, allowing it to be reused * later. * * @param ctx Application Context or activity * @param authToken The authentication information returned at the end of a * successful OAuth authentication. * @param evernoteHost the URL of the Evernote Web API to connect to, provided by the bootstrap results */ protected boolean persistAuthenticationToken(Context ctx, EvernoteAuthToken authToken, String evernoteHost) { if (ctx == null || authToken == null) { return false; } synchronized (this) { mAuthenticationResult = new AuthenticationResult( authToken.getToken(), authToken.getNoteStoreUrl(), authToken.getWebApiUrlPrefix(), evernoteHost, authToken.getUserId()); mAuthenticationResult.persist(SessionPreferences.getPreferences(ctx)); } return true; } /** * Check whether the session has valid authentication information * that will allow successful API calls to be made. */ public boolean isLoggedIn() { synchronized (this) { return mAuthenticationResult != null; } } /** * Clear all stored authentication information. */ public void logOut(Context ctx) throws InvalidAuthenticationException { if(!isLoggedIn()) { throw new InvalidAuthenticationException("Must not call when already logged out"); } synchronized (this) { mAuthenticationResult.clear(SessionPreferences.getPreferences(ctx)); mAuthenticationResult = null; } // TODO The cookie jar is application scope, so we should only be removing // evernote.com cookies. CookieSyncManager.createInstance(ctx); CookieManager cookieManager = CookieManager.getInstance(); cookieManager.removeAllCookie(); } }