com.microsoft.aad.test.todoapi.ToDoActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.microsoft.aad.test.todoapi.ToDoActivity.java

Source

/*
Copyright (c) Microsoft
All Rights Reserved
Apache 2.0 License
     
   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.
     
See the Apache Version 2.0 License for specific language governing permissions and limitations under the License.
 */

package com.microsoft.aad.test.todoapi;

import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.Iterator;
import java.util.List;
import java.util.TimeZone;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.DatePickerDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.Window;
import android.webkit.CookieManager;
import android.webkit.CookieSyncManager;
import android.widget.DatePicker;
import android.widget.EditText;
import android.widget.ListView;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.Toast;

import com.azure.webapi.DateSerializer;
import com.azure.webapi.LongSerializer;
import com.azure.webapi.MobileServiceClient;
import com.azure.webapi.MobileServiceQueryOperations;
import com.azure.webapi.MobileServiceTable;
import com.azure.webapi.MobileServiceUser;
import com.azure.webapi.NextServiceFilterCallback;
import com.azure.webapi.ServiceFilter;
import com.azure.webapi.ServiceFilterRequest;
import com.azure.webapi.ServiceFilterResponse;
import com.azure.webapi.ServiceFilterResponseCallback;
import com.azure.webapi.TableOperationCallback;
import com.azure.webapi.TableQueryCallback;
import com.google.gson.GsonBuilder;
import com.microsoft.aad.adal.AuthenticationCallback;
import com.microsoft.aad.adal.AuthenticationContext;
import com.microsoft.aad.adal.AuthenticationResult;
import com.microsoft.aad.adal.CacheKey;
import com.microsoft.aad.adal.DefaultTokenCacheStore;
import com.microsoft.aad.adal.ITokenCacheStore;
import com.microsoft.aad.adal.TokenCacheItem;

public class ToDoActivity extends Activity {

    private final static String TAG = "ToDoActivity";

    public static final int MENU_LOGOUT = Menu.FIRST;

    public static final int MENU_CLEAR_TOKEN = Menu.FIRST + 1;

    public static final int MENU_GET_TOKEN = Menu.FIRST + 2;

    public static final int MENU_GET_NEWSFEED = Menu.FIRST + 3;

    public static final int MENU_GET_SETTINGS = Menu.FIRST + 4;

    public static final int MENU_GET_LAYOUT_DEMO = Menu.FIRST + 5;

    public static final int MENU_REFRESH_TOKEN_NORMAL = Menu.FIRST + 6;

    public static final int MENU_REFRESH_TOKEN_DELAY = Menu.FIRST + 7;

    /**
     * ask to refresh and then fail to go to prompt to check the scenario where
     * prompt screen shows up after user navigates to something else
     */
    public static final int MENU_REFRESH_TOKEN_DELAY_PROMPT = Menu.FIRST + 8;

    public static final int MENU_CANCEL_REQUEST = Menu.FIRST + 9;

    public static final int MENU_SHOW_TOKEN = Menu.FIRST + 10;

    public static final int MENU_EXPIRE_TOKEN = Menu.FIRST + 11;

    private AuthenticationContext mAuthContext;

    private int mLastRequestId = 0;

    private boolean mSendCancel = false;

    private boolean refreshInProgress = false;

    /**
     * Mobile Service Client reference
     */
    private MobileServiceClient mClient;

    /**
     * Mobile Service Table used to access data
     */
    private MobileServiceTable<WorkItem> mToDoTable = null;

    /**
     * Adapter to sync the items list with the view
     */
    private WorkItemAdapter mAdapter = null;

    /**
     * EditText containing the "New ToDo" text
     */
    private EditText mTextNewToDo;

    /**
     * Progress spinner to use for table operations
     */
    private ProgressBar mProgressBar;

    /**
     * Show this dialog when activity first launches to check if user has login
     * or not.
     */
    private ProgressDialog mLoginProgressDialog;

    private AuthenticationResult mToken;

    private static int pickerYear;

    private static int pickerMonth;

    private static int pickerDayOfMonth;

    TextView txtSummary;

    /**
     * Initializes the activity
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_to_do);
        setDefaultDate();
        Toast.makeText(getApplicationContext(), TAG + "LifeCycle: OnCreate", Toast.LENGTH_SHORT).show();

        txtSummary = (TextView) findViewById(R.id.textViewTitle);
        txtSummary.setText("TODO Services");
        mProgressBar = (ProgressBar) findViewById(R.id.loadingProgressBar);

        // Initialize the progress bar
        mProgressBar.setVisibility(ProgressBar.GONE);

        mLoginProgressDialog = new ProgressDialog(this);
        mLoginProgressDialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
        mLoginProgressDialog.setMessage("Login in progress...");
        mLoginProgressDialog.show();
        refreshInProgress = false;

        // Ask for token and provide callback
        try {
            Utils.setupKeyForSample();
            mAuthContext = new AuthenticationContext(ToDoActivity.this, Constants.AUTHORITY_URL, false);
            mAuthContext.acquireToken(ToDoActivity.this, Constants.RESOURCE_ID, Constants.CLIENT_ID,
                    Constants.REDIRECT_URL, Constants.USER_HINT,
                    new AuthenticationCallback<AuthenticationResult>() {

                        @Override
                        public void onError(Exception exc) {
                            if (mLoginProgressDialog.isShowing()) {
                                mLoginProgressDialog.dismiss();
                            }
                            Toast.makeText(getApplicationContext(), TAG + "getToken Error:" + exc.getMessage(),
                                    Toast.LENGTH_SHORT).show();
                            navigateToLogOut();
                        }

                        @Override
                        public void onSuccess(AuthenticationResult result) {
                            if (mLoginProgressDialog.isShowing()) {
                                mLoginProgressDialog.dismiss();
                            }

                            if (result != null && !result.getAccessToken().isEmpty()) {
                                setLocalToken(result);
                                sendRequest();
                            } else {
                                navigateToLogOut();
                            }
                        }
                    });
        } catch (Exception e) {
            Toast.makeText(getApplicationContext(), "Encryption is failed", Toast.LENGTH_SHORT).show();
        }

        Toast.makeText(getApplicationContext(), TAG + "done", Toast.LENGTH_SHORT).show();
    }

    private void setDefaultDate() {
        final Calendar c = Calendar.getInstance();
        // If user does not change the date, add button will use this date
        pickerYear = c.get(Calendar.YEAR);
        pickerMonth = c.get(Calendar.MONTH);
        pickerDayOfMonth = c.get(Calendar.DAY_OF_MONTH);
    }

    private void sendRequest() {
        if (refreshInProgress || mToken == null || mToken.getAccessToken().isEmpty())
            return;

        if (mClient == null) {
            initAppTables();
        }
        refreshInProgress = true;
        refreshItemsFromTable();
    }

    private URL getEndpointUrl() {
        URL endpoint = null;
        try {
            endpoint = new URL(Constants.SERVICE_URL + "/api/" + Constants.TABLE_WORKITEM);
        } catch (MalformedURLException e) {
            e.printStackTrace();
        }
        return endpoint;
    }

    /**
     * Register gson serializer for long and date type
     * 
     * @return
     */
    public static GsonBuilder createServiceGsonBuilder() {
        GsonBuilder gsonBuilder = new GsonBuilder();

        // Register custom date serializer/deserializer
        gsonBuilder.registerTypeAdapter(Date.class, new DateSerializer());
        LongSerializer longSerializer = new LongSerializer();
        gsonBuilder.registerTypeAdapter(Long.class, longSerializer);
        gsonBuilder.registerTypeAdapter(long.class, longSerializer);
        gsonBuilder.serializeNulls();
        return gsonBuilder;
    }

    private void initAppTables() {
        try {
            // Create the Mobile Service Client instance, using the provided
            // Mobile Service URL
            // TODO this is targeting single webapi controller
            // each table should target different webapi controller

            mClient = new MobileServiceClient(Constants.SERVICE_URL, ToDoActivity.this)
                    .withFilter(new ProgressFilter());

            // When app is initializing, It needs token to continue. If not
            // possible to get token without UI flow, it should direct to login
            // screen so that
            // user can see some instructions.

            if (getLocalToken() != null) {
                MobileServiceUser user = new MobileServiceUser();
                user.setAuthenticationToken(getLocalToken().getAccessToken());
                mClient.setCurrentUser(user);
            } else {

                navigateToLogOut();
                return;
            }

            // Get the Mobile Service Table instance to use
            mToDoTable = mClient.getTable(WorkItem.class);
            mToDoTable.TABLES_URL = "/api/";
            mTextNewToDo = (EditText) findViewById(R.id.textNewToDo);

            // Create an adapter to bind the items with the view
            mAdapter = new WorkItemAdapter(ToDoActivity.this, R.layout.row_list_to_do);
            ListView listViewToDo = (ListView) findViewById(R.id.listViewToDo);
            listViewToDo.setAdapter(mAdapter);

        } catch (MalformedURLException e) {
            createAndShowDialog(new Exception("There was an error creating the Mobile Service. Verify the URL"),
                    "Error");
        }
    }

    private void navigateToLogOut() {
        // Show logout page
        // Go to logout page
        Intent intent = new Intent(ToDoActivity.this, LogOutActivity.class);
        startActivity(intent);

        // Close this activity
        finish();
    }

    private void getToken(final AuthenticationCallback callback) {

        // one of the acquireToken overloads
        mAuthContext.acquireToken(ToDoActivity.this, Constants.RESOURCE_ID, Constants.CLIENT_ID,
                Constants.REDIRECT_URL, Constants.USER_HINT, callback);
        mLastRequestId = callback.hashCode();
    }

    private AuthenticationResult getLocalToken() {
        return mToken;
    }

    private void setLocalToken(AuthenticationResult newToken) {
        mToken = newToken;
    }

    /**
     * Mark an item as completed
     * 
     * @param item The item to mark
     */
    public void checkItem(WorkItem item) {
        if (mClient == null) {
            return;
        }

        // Set the item as completed and update it in the table
        item.setComplete(true);
        final WorkItem itemRemove = item;
        mToDoTable.update(item, new TableOperationCallback<WorkItem>() {

            public void onCompleted(WorkItem entity, Exception exception, ServiceFilterResponse response) {
                if (exception == null) {
                    // We dont need to wait for entity in update operation
                    mAdapter.remove(itemRemove);
                } else {
                    createAndShowDialog(exception, "Error");
                }
            }

        });
    }

    public void updateDate(WorkItem item) {
        if (mClient == null) {
            return;
        }

        // Set the item as completed and update it in the table
        mToDoTable.update(item, new TableOperationCallback<WorkItem>() {
            public void onCompleted(WorkItem entity, Exception exception, ServiceFilterResponse response) {
                if (exception != null) {
                    createAndShowDialog(exception, "Error");
                }
            }

        });
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        super.onCreateOptionsMenu(menu);

        menu.add(Menu.NONE, MENU_LOGOUT, Menu.NONE, "LogOut for User");
        menu.add(Menu.NONE, MENU_CLEAR_TOKEN, Menu.NONE, "Clear All Tokens");
        menu.add(Menu.NONE, MENU_GET_TOKEN, Menu.NONE, "Get Token");
        menu.add(Menu.NONE, MENU_GET_NEWSFEED, Menu.NONE, "Other Activity");
        menu.add(Menu.NONE, MENU_GET_SETTINGS, Menu.NONE, "Test Settings");
        menu.add(Menu.NONE, MENU_REFRESH_TOKEN_NORMAL, Menu.NONE, "RefreshNormal");
        menu.add(Menu.NONE, MENU_REFRESH_TOKEN_DELAY, Menu.NONE, "RefreshDelay");
        menu.add(Menu.NONE, MENU_REFRESH_TOKEN_DELAY_PROMPT, Menu.NONE, "RefreshDelayToPrompt");
        menu.add(Menu.NONE, MENU_CANCEL_REQUEST, Menu.NONE, "CancelAuthentication");
        menu.add(Menu.NONE, MENU_SHOW_TOKEN, Menu.NONE, "ShowTokens");
        menu.add(Menu.NONE, MENU_EXPIRE_TOKEN, Menu.NONE, "Expire");

        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case MENU_LOGOUT: {
            resetTokens();
            clearCookies();

            // Go to logout page
            Intent intent = new Intent(ToDoActivity.this, LogOutActivity.class);
            startActivity(intent);
            // dont finish since when it comes back this page can be used
            // again
            // if not discarded
            return true;
        }
        case MENU_CLEAR_TOKEN: {
            resetTokens();
            clearCookies();
            return true;
        }
        case MENU_GET_TOKEN:
            getToken(new AuthenticationCallback<AuthenticationResult>() {

                @Override
                public void onError(Exception exc) {
                    Toast.makeText(getApplicationContext(), exc.getMessage(), Toast.LENGTH_LONG).show();
                }

                @Override
                public void onSuccess(AuthenticationResult result) {
                    Toast.makeText(getApplicationContext(), "OnCompleted", Toast.LENGTH_LONG).show();
                    setLocalToken(result);
                }
            });
            return true;
        case MENU_GET_NEWSFEED: {
            Intent intent = new Intent(ToDoActivity.this, FeedActivity.class);
            startActivity(intent);
            return true;
        }
        case MENU_GET_SETTINGS: {
            Intent intent = new Intent(ToDoActivity.this, SettingsActivity.class);
            startActivity(intent);
            return true;
        }
        case MENU_REFRESH_TOKEN_NORMAL: {
            refreshTokenNormal();
            return true;
        }
        case MENU_REFRESH_TOKEN_DELAY: {
            refreshTokenWithDelay(false);
            return true;
        }
        case MENU_REFRESH_TOKEN_DELAY_PROMPT: {
            refreshTokenWithDelay(true);
            return true;
        }
        case MENU_CANCEL_REQUEST: {
            // Clear tokens and then choose MENU_REFRESH_TOKEN_DELAY_PROMPT
            // option.
            // It will launch prompt screen with delay
            //
            sendCancelRequest();
            return true;
        }
        case MENU_SHOW_TOKEN: {
            showTokens();
            return true;
        }
        default:
            return super.onOptionsItemSelected(item);
        }
    }

    private void clearCookies() {
        CookieSyncManager.createInstance(ToDoActivity.this);
        CookieManager cookieManager = CookieManager.getInstance();
        cookieManager.removeAllCookie();
        CookieSyncManager.getInstance().sync();
    }

    private void showTokens() {
        ITokenCacheStore currentCache = mAuthContext.getCache();
        if (currentCache != null && currentCache instanceof DefaultTokenCacheStore) {
            DefaultTokenCacheStore cache = (DefaultTokenCacheStore) currentCache;
            StringBuilder tokeninfo = new StringBuilder();
            Iterator<TokenCacheItem> iterator = cache.getAll();

            while (iterator.hasNext()) {
                TokenCacheItem item = iterator.next();

                tokeninfo.append("Key:" + CacheKey.createCacheKey(item).toString() + " Expires:"
                        + item.getExpiresOn() + " token:" + item.getAccessToken().substring(0, 10) + "\n");
                tokeninfo.append("--------------\n");

            }
            createAndShowDialog(tokeninfo.toString(), "Tokens");
        }

    }

    private void refreshTokenNormal() {
        setTokenExpire();

        // reset cache to normal without delay
        try {
            mAuthContext = new AuthenticationContext(ToDoActivity.this, Constants.AUTHORITY_URL, false);
            txtSummary.setText("TODO Services sending services...");
            // Normal token request will be send but it will have delay
            getToken(new AuthenticationCallback<AuthenticationResult>() {

                @Override
                public void onError(Exception exc) {
                    Log.e(TAG, "refreshTokenNormal error" + exc.getMessage(), exc);
                    Toast.makeText(getApplicationContext(), exc.getMessage(), Toast.LENGTH_LONG).show();
                }

                @Override
                public void onSuccess(AuthenticationResult result) {
                    if (isNotEmpty(result.getAccessToken()) && isNotEmpty(result.getRefreshToken())) {
                        Log.d(TAG, "refreshTokenNormal onSuccess");
                    } else {
                        Log.d(TAG, "refreshTokenNormal Problem....");
                    }
                    Toast.makeText(getApplicationContext(), "OnCompleted", Toast.LENGTH_LONG).show();
                    txtSummary.setText("TODO Services refreshed");
                    setLocalToken(result);
                }
            });
        } catch (Exception e) {
            Log.e(TAG, "Refresh token normal error", e);
            Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    private boolean isNotEmpty(String value) {
        return value != null && !value.isEmpty();
    }

    private void sendCancelRequest() {
        mSendCancel = true;
        // when activity is at the front cancel can be tested
    }

    private void refreshTokenWithDelay(boolean invalidateRefresh) {
        ITokenCacheStore currentCache = mAuthContext.getCache();

        // make token expired to force refresh token
        TokenCacheItem item = currentCache.getItem(CacheKey.createCacheKey(Constants.AUTHORITY_URL,
                Constants.RESOURCE_ID, Constants.CLIENT_ID, false, Constants.USER_HINT));
        if (item != null) {
            Calendar timeExpired = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
            timeExpired.add(Calendar.MINUTE, -50);
            item.setExpiresOn(timeExpired.getTime());
            if (invalidateRefresh) {
                item.setRefreshToken("invalidRefreshToken");
            }
            currentCache.setItem(CacheKey.createCacheKey(item), item);
        }

        try {
            mAuthContext = new AuthenticationContext(ToDoActivity.this, Constants.AUTHORITY_URL, false);

            setNetworkDelayForDebugging(5000);

            // Normal token request will be send, but it will have delay
            getToken(new AuthenticationCallback<AuthenticationResult>() {

                @Override
                public void onError(Exception exc) {
                    Log.e(TAG, "refreshTokenWithDelay error" + exc.getMessage(), exc);
                    Toast.makeText(getApplicationContext(), exc.getMessage(), Toast.LENGTH_LONG).show();
                    // reset cache to normal without delay
                    try {
                        mAuthContext = new AuthenticationContext(ToDoActivity.this, Constants.AUTHORITY_URL, false);
                    } catch (Exception e) {
                        Log.e(TAG, "refreshTokenWithDelay", e);
                        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
                    }
                    setNetworkDelayForDebugging(0);
                }

                @Override
                public void onSuccess(AuthenticationResult result) {
                    Log.d(TAG, "refreshTokenWithDelay onSuccess");
                    Toast.makeText(getApplicationContext(), "OnCompleted", Toast.LENGTH_LONG).show();
                    setLocalToken(result);
                    // reset cache to normal without delay
                    try {
                        mAuthContext = new AuthenticationContext(ToDoActivity.this, Constants.AUTHORITY_URL, false);
                    } catch (Exception e) {
                        Log.e(TAG, "refreshTokenWithDelay", e);
                        Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
                    }
                    setNetworkDelayForDebugging(0);
                }
            });

        } catch (Exception e) {
            Log.e(TAG, "Refresh token with delay", e);
            Toast.makeText(getApplicationContext(), e.getMessage(), Toast.LENGTH_LONG).show();
        }
    }

    private void setNetworkDelayForDebugging(int miliSeconds) {
        // HttpWebRequest class has a static field to use to introduce delays in
        // debugger mode
        Log.d(TAG, "setNetworkDelayForDebugging:" + miliSeconds);
        Class<?> c = null;
        Field field;
        try {
            c = Class.forName("com.microsoft.adal.HttpWebRequest");
            field = c.getDeclaredField("sDebugSimulateDelay");
            field.setAccessible(true);
            field.set(null, miliSeconds);
        } catch (Exception e) {
            // TODO Auto-generated catch block
            Log.d(TAG, "Reflection issue");
            e.printStackTrace();
        }
    }

    private void setTokenExpire() {
        // get items and set them to expired from cache
        // default cache is in use by default
        ITokenCacheStore currentCache = mAuthContext.getCache();

        String key = CacheKey.createCacheKey(Constants.AUTHORITY_URL, Constants.RESOURCE_ID, Constants.CLIENT_ID,
                false, Constants.USER_HINT);
        TokenCacheItem item = currentCache.getItem(key);
        if (item != null) {
            Calendar timeExpired = new GregorianCalendar(TimeZone.getTimeZone("UTC"));
            timeExpired.add(Calendar.MINUTE, -50);
            item.setExpiresOn(timeExpired.getTime());
            currentCache.setItem(key, item);
        }
    }

    @Override
    public void onResume() {
        super.onResume(); // Always call the superclass method first

        // User can click logout, it will come back here
        // It should refresh list again
        refreshInProgress = false;
        sendRequest();
    }

    @Override
    protected void onRestart() {
        super.onRestart();

    }

    @Override
    protected void onPause() {
        super.onPause();
        Log.d(TAG, "TODOActivity onPause");
        if (mSendCancel) {
            // refresh token will have delay and then this will send cancel
            // request
            // to the authenticationActivity
            final Runnable r = new Runnable() {

                @Override
                public void run() {
                    Log.d(TAG, "Sending cancel request for requestId:" + mLastRequestId);
                    boolean result = mAuthContext.cancelAuthenticationActivity(mLastRequestId);
                    Log.d(TAG, "Result from cancel request:" + result);
                }
            };
            // Manual tests: adjust timing to catch when activity launches
            new Handler().postDelayed(r, 5000);
            mSendCancel = false;
        }
    }

    @Override
    protected void onStop() {
        super.onStop();

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        Toast.makeText(getApplicationContext(), TAG + "LifeCycle: OnDestroy", Toast.LENGTH_SHORT).show();

    }

    private void resetTokens() {
        // Clear auth context tokens and local field as well
        mAuthContext.getCache().removeAll();
        mToken = null;

        // clear token from current user obj
        if (mClient != null) {
            MobileServiceUser user = mClient.getCurrentUser();
            if (user != null) {
                user.setAuthenticationToken(null);
            }
        }
    }

    /*
     * Target SDK above 11 for fragments
     */
    public void showDatePicker(View v) {
        DialogFragment newFragment = new DatePickerFragment();
        newFragment.show(getFragmentManager(), "datePicker");
    }

    /*
     * Datepicker to set duedate
     */
    public static class DatePickerFragment extends DialogFragment implements DatePickerDialog.OnDateSetListener {

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            // Create a new instance of DatePickerDialog and return it
            return new DatePickerDialog(getActivity(), this, pickerYear, pickerMonth, pickerDayOfMonth);
        }

        public void onDateSet(DatePicker view, int year, int month, int day) {

            // Update current selected date to be used in add post
            // there is only one item posting
            pickerYear = year;
            pickerMonth = month;
            pickerDayOfMonth = day;
        }
    }

    /**
     * Add a new item
     * 
     * @param view The view that originated the call
     */
    public void addItem(View view) {
        if (mClient == null) {
            initAppTables();
        }

        if (getLocalToken() != null) {
            MobileServiceUser user = new MobileServiceUser();
            user.setAuthenticationToken(getLocalToken().getAccessToken());
            mClient.setCurrentUser(user);
        }

        if (pickerYear == 0 && pickerMonth == 0 && pickerDayOfMonth == 0) {
            setDefaultDate();
        }

        // Create a new item
        WorkItem item = new WorkItem();

        // Get title from edit text field
        item.setTitle(mTextNewToDo.getText().toString());
        // Checkbox set to false
        item.setComplete(false);

        Calendar calendar = Calendar.getInstance();
        calendar.set(pickerYear, pickerMonth, pickerDayOfMonth);
        item.setDueDate(calendar.getTime());

        // Insert the new item
        mToDoTable.insert(item, new TableOperationCallback<WorkItem>() {

            public void onCompleted(WorkItem entity, Exception exception, ServiceFilterResponse response) {

                if (exception == null) {
                    if (!entity.isComplete()) {
                        mAdapter.add(entity);

                    }
                } else {
                    createAndShowDialog(exception, "Error");
                }

            }
        });

        mTextNewToDo.setText("");
    }

    public void refreshItem(View view) {
        if (mClient == null) {
            return;
        }

        // Load the items from the Mobile Service
        refreshItemsFromTable();
    }

    /**
     * Refresh the list with the items in the Mobile Service Table
     */
    private void refreshItemsFromTable() {

        // Get the items that weren't marked as completed and add them in the
        // adapter
        if (getLocalToken() != null) {
            MobileServiceUser user = new MobileServiceUser();
            user.setAuthenticationToken(getLocalToken().getAccessToken());
            mClient.setCurrentUser(user);
        }

        if (mToDoTable != null) {
            mToDoTable.where().field("Complete").eq(MobileServiceQueryOperations.val(false))
                    .execute(new TableQueryCallback<WorkItem>() {

                        public void onCompleted(List<WorkItem> result, int count, Exception exception,
                                ServiceFilterResponse response) {
                            if (exception == null) {
                                mAdapter.clear();

                                for (WorkItem item : result) {
                                    mAdapter.add(item);
                                }

                            } else {
                                createAndShowDialog(exception, "Error");
                            }
                        }
                    });
        }
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        mAuthContext.onActivityResult(requestCode, resultCode, data);
    }

    /**
     * Creates a dialog and shows it
     * 
     * @param exception The exception to show in the dialog
     * @param title The dialog title
     */
    private void createAndShowDialog(Exception exception, String title) {
        createAndShowDialog(exception.toString(), title);
    }

    /**
     * Creates a dialog and shows it
     * 
     * @param message The dialog message
     * @param title The dialog title
     */
    private void createAndShowDialog(String message, String title) {
        AlertDialog.Builder builder = new AlertDialog.Builder(this);

        builder.setMessage(message);
        builder.setTitle(title);
        builder.create().show();
    }

    private class ProgressFilter implements ServiceFilter {

        @Override
        public void handleRequest(ServiceFilterRequest request, NextServiceFilterCallback nextServiceFilterCallback,
                final ServiceFilterResponseCallback responseCallback) {
            runOnUiThread(new Runnable() {

                @Override
                public void run() {
                    if (mProgressBar != null)
                        mProgressBar.setVisibility(ProgressBar.VISIBLE);
                }
            });

            nextServiceFilterCallback.onNext(request, new ServiceFilterResponseCallback() {

                @Override
                public void onResponse(ServiceFilterResponse response, Exception exception) {
                    runOnUiThread(new Runnable() {

                        @Override
                        public void run() {
                            if (mProgressBar != null)
                                mProgressBar.setVisibility(ProgressBar.GONE);
                        }
                    });

                    if (responseCallback != null)
                        responseCallback.onResponse(response, exception);
                }
            });
        }
    }

    @Override
    @Deprecated
    public Object onRetainNonConfigurationInstance() {

        return mAuthContext;
    }

}