com.myandroidremote.AccountsActivity.java Source code

Java tutorial

Introduction

Here is the source code for com.myandroidremote.AccountsActivity.java

Source

/*
 * Copyright 2010 Google 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.myandroidremote;

import java.io.IOException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.List;

import org.apache.http.Header;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.params.HttpClientParams;
import org.apache.http.cookie.Cookie;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpParams;

import android.accounts.Account;
import android.accounts.AccountManager;
import android.accounts.AccountManagerCallback;
import android.accounts.AccountManagerFuture;
import android.accounts.AuthenticatorException;
import android.accounts.OperationCanceledException;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.SharedPreferences.Editor;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ListView;
import android.widget.TextView;

import com.google.android.c2dm.C2DMessaging;

/**
 * Account selections activity - handles device registration and unregistration.
 */
public class AccountsActivity extends Activity {
    /**
     * Tag for logging.
     */
    private static final String TAG = "AccountsActivity";

    /**
     * Cookie name for authorization.
     */
    private static final String AUTH_COOKIE_NAME = "SACSID";

    /**
     * The selected position in the ListView of accounts.
     */
    private int mAccountSelectedPosition = 0;

    /**
     * True if we are waiting for App Engine authorization.
     */
    private boolean mPendingAuth = false;

    /**
     * The current context.
     */
    private Context mContext = this;

    /**
     * Begins the activity.
     */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Register a receiver to provide register/unregister notifications
        registerReceiver(mUpdateUIReceiver, new IntentFilter(Util.UPDATE_UI_INTENT));

        SharedPreferences prefs = Util.getSharedPreferences(mContext);
        String deviceRegistrationID = prefs.getString(Util.DEVICE_REGISTRATION_ID, null);
        if (deviceRegistrationID == null) {
            // Show the 'connect' screen if we are not connected
            setScreenContent(R.layout.connect);
        } else {
            // Show the 'disconnect' screen if we are connected
            setScreenContent(R.layout.disconnect);
        }
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        SharedPreferences prefs = Util.getSharedPreferences(mContext);
        String deviceRegistrationID = prefs.getString(Util.DEVICE_REGISTRATION_ID, null);
        if (deviceRegistrationID == null) {
            setScreenContent(R.layout.connect);
        } else {
            setScreenContent(R.layout.disconnect);
        }
        return true;
    }

    /**
     * Resumes the activity.
     */
    @Override
    protected void onResume() {
        super.onResume();
        if (mPendingAuth) {
            mPendingAuth = false;
            String regId = C2DMessaging.getRegistrationId(mContext);
            if (regId != null && !"".equals(regId)) {
                DeviceRegistrar.registerOrUnregister(mContext, regId, true);
            } else {
                C2DMessaging.register(mContext, Setup.SENDER_ID);
            }
        }
    }

    // Manage UI Screens

    /**
     * Sets up the 'connect' screen content.
     */
    private void setConnectScreenContent() {
        List<String> accounts = getGoogleAccounts();
        if (accounts.size() == 0) {
            // Show a dialog and invoke the "Add Account" activity if requested
            AlertDialog.Builder builder = new AlertDialog.Builder(mContext);
            builder.setMessage(R.string.needs_account);
            builder.setPositiveButton(R.string.add_account, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    startActivity(new Intent(Settings.ACTION_ADD_ACCOUNT));
                }
            });
            builder.setNegativeButton(R.string.skip, new DialogInterface.OnClickListener() {
                public void onClick(DialogInterface dialog, int which) {
                    showLoadingScreen();
                }
            });
            builder.setIcon(android.R.drawable.stat_sys_warning);
            builder.setTitle(R.string.attention);
            builder.show();
        } else {
            final ListView listView = (ListView) findViewById(R.id.select_account);
            listView.setAdapter(new ArrayAdapter<String>(mContext, R.layout.account, accounts));
            listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
            listView.setItemChecked(mAccountSelectedPosition, true);

            final Button connectButton = (Button) findViewById(R.id.connect);
            connectButton.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    // Register in the background and terminate the activity
                    mAccountSelectedPosition = listView.getCheckedItemPosition();
                    TextView account = (TextView) listView.getChildAt(mAccountSelectedPosition);
                    register((String) account.getText());

                    // show connecting
                    showLoadingScreen();
                }
            });

            final Button exitButton = (Button) findViewById(R.id.exit);
            exitButton.setOnClickListener(new OnClickListener() {
                public void onClick(View v) {
                    finish();
                }
            });
        }
    }

    ProgressDialog progressDialog;

    private void showLoadingScreen() {
        progressDialog = ProgressDialog.show(AccountsActivity.this, "", "Loading. Please wait...", true);
    }

    private void stopLoadingScreen() {
        if (progressDialog != null)
            progressDialog.dismiss();
    }

    /**
     * Sets up the 'disconnected' screen.
     */
    private void setDisconnectScreenContent() {
        final SharedPreferences prefs = Util.getSharedPreferences(mContext);
        String accountName = prefs.getString(Util.ACCOUNT_NAME, "error");

        // Format the disconnect message with the currently connected account
        // name
        TextView disconnectText = (TextView) findViewById(R.id.disconnect_text);
        String message = getResources().getString(R.string.disconnect_text);
        String formatted = String.format(message, accountName);
        disconnectText.setText(formatted);

        Button disconnectButton = (Button) findViewById(R.id.disconnect);
        disconnectButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {

                // Unregister in the background and terminate the activity
                C2DMessaging.unregister(mContext);

                // Delete the current account from shared preferences
                Editor editor = prefs.edit();
                // editor.putString(Util.AUTH_COOKIE, null);
                editor.putString(Util.DEVICE_REGISTRATION_ID, null);
                editor.commit();

                showLoadingScreen();
            }
        });

        Button exitButton = (Button) findViewById(R.id.exit);
        exitButton.setOnClickListener(new OnClickListener() {
            public void onClick(View v) {
                finish();
            }
        });
    }

    /**
     * Sets the screen content based on the screen id.
     */
    private void setScreenContent(int screenId) {
        setContentView(screenId);
        switch (screenId) {
        case R.layout.disconnect:
            setDisconnectScreenContent();
            break;
        case R.layout.connect:
            setConnectScreenContent();
            break;
        }
    }

    // Register and Unregister

    /**
     * Registers for C2DM messaging with the given account name.
     * 
     * @param accountName
     *            a String containing a Google account name
     */
    private void register(final String accountName) {
        // Store the account name in shared preferences
        final SharedPreferences prefs = Util.getSharedPreferences(mContext);
        SharedPreferences.Editor editor = prefs.edit();
        editor.putString(Util.ACCOUNT_NAME, accountName);
        editor.putString(Util.AUTH_COOKIE, null);
        editor.commit();

        // Obtain an auth token and register
        final AccountManager mgr = AccountManager.get(mContext);
        Account[] accts = mgr.getAccountsByType("com.google");
        for (Account acct : accts) {
            if (acct.name.equals(accountName)) {
                if (Util.isDebug(mContext)) {
                    // Use a fake cookie for the dev mode app engine server
                    // The cookie has the form email:isAdmin:userId
                    // We set the userId to be the same as the account name
                    String authCookie = "dev_appserver_login=" + accountName + ":false:" + accountName;
                    boolean result = prefs.edit().putString(Util.AUTH_COOKIE, authCookie).commit();
                    C2DMessaging.register(mContext, Setup.SENDER_ID);
                } else {
                    // Get the auth token from the AccountManager and convert
                    // it into a cookie for the appengine server
                    mgr.getAuthToken(acct, "ah", null, this, new AccountManagerCallback<Bundle>() {
                        public void run(AccountManagerFuture<Bundle> future) {
                            try {
                                Bundle authTokenBundle = future.getResult();
                                String authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
                                String authCookie = getAuthCookie(authToken);
                                if (authCookie == null) {
                                    mgr.invalidateAuthToken("com.google", authToken);
                                }
                                prefs.edit().putString(Util.AUTH_COOKIE, authCookie).commit();

                                C2DMessaging.register(mContext, Setup.SENDER_ID);
                            } catch (AuthenticatorException e) {
                                Log.w(TAG, "Got AuthenticatorException " + e);
                                Log.w(TAG, Log.getStackTraceString(e));
                            } catch (IOException e) {
                                Log.w(TAG, "Got IOException " + Log.getStackTraceString(e));
                                Log.w(TAG, Log.getStackTraceString(e));
                            } catch (OperationCanceledException e) {
                                Log.w(TAG, "Got OperationCanceledException " + e);
                                Log.w(TAG, Log.getStackTraceString(e));
                            }
                        }
                    }, null);
                }
                break;
            }
        }
    }

    // Utility Methods

    /**
     * Retrieves the authorization cookie associated with the given token. This
     * method should only be used when running against a production appengine
     * backend (as opposed to a dev mode server).
     */
    private String getAuthCookie(String authToken) {
        try {
            // Get SACSID cookie
            DefaultHttpClient client = new DefaultHttpClient();
            String continueURL = Setup.PROD_URL;
            URI uri = new URI(Setup.PROD_URL + "/_ah/login?continue=" + URLEncoder.encode(continueURL, "UTF-8")
                    + "&auth=" + authToken);
            HttpGet method = new HttpGet(uri);
            final HttpParams getParams = new BasicHttpParams();
            HttpClientParams.setRedirecting(getParams, false);
            method.setParams(getParams);

            HttpResponse res = client.execute(method);
            Header[] headers = res.getHeaders("Set-Cookie");
            if (res.getStatusLine().getStatusCode() != 302 || headers.length == 0) {
                return null;
            }

            for (Cookie cookie : client.getCookieStore().getCookies()) {
                if (AUTH_COOKIE_NAME.equals(cookie.getName())) {
                    return AUTH_COOKIE_NAME + "=" + cookie.getValue();
                }
            }
        } catch (IOException e) {
            Log.w(TAG, "Got IOException " + e);
            Log.w(TAG, Log.getStackTraceString(e));
        } catch (URISyntaxException e) {
            Log.w(TAG, "Got URISyntaxException " + e);
            Log.w(TAG, Log.getStackTraceString(e));
        }

        return null;
    }

    /**
     * Returns a list of registered Google account names. If no Google accounts
     * are registered on the device, a zero-length list is returned.
     */
    private List<String> getGoogleAccounts() {
        ArrayList<String> result = new ArrayList<String>();
        Account[] accounts = AccountManager.get(mContext).getAccounts();
        for (Account account : accounts) {
            if (account.type.equals("com.google")) {
                result.add(account.name);
            }
        }

        return result;
    }

    private class MyAccountManagerCallback implements AccountManagerCallback<Bundle> {
        private AccountManager mgr;
        private Context context;

        public MyAccountManagerCallback(AccountManager mgr, Context context) {
            this.mgr = mgr;
            this.context = context;
        }

        public void run(AccountManagerFuture<Bundle> future) {
            final SharedPreferences prefs = Util.getSharedPreferences(context);
            try {
                Bundle authTokenBundle = future.getResult();
                String authToken = authTokenBundle.get(AccountManager.KEY_AUTHTOKEN).toString();
                String authCookie = getAuthCookie(authToken);
                if (authCookie == null) {
                    mgr.invalidateAuthToken("com.google", authToken);
                }
                prefs.edit().putString(Util.AUTH_COOKIE, authCookie).commit();

                C2DMessaging.register(mContext, Setup.SENDER_ID);
            } catch (AuthenticatorException e) {
                Log.w(TAG, "Got AuthenticatorException " + e);
                Log.w(TAG, Log.getStackTraceString(e));
            } catch (IOException e) {
                Log.w(TAG, "Got IOException " + Log.getStackTraceString(e));
                Log.w(TAG, Log.getStackTraceString(e));
            } catch (OperationCanceledException e) {
                Log.w(TAG, "Got OperationCanceledException " + e);
                Log.w(TAG, Log.getStackTraceString(e));
            }
        }
    }

    /**
     * A {@link BroadcastReceiver} to receive the response from a register or
     * unregister request, and to update the UI.
     */
    private final BroadcastReceiver mUpdateUIReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            int status = intent.getIntExtra(DeviceRegistrar.STATUS_EXTRA, DeviceRegistrar.ERROR_STATUS);
            String message = null;
            if (status == DeviceRegistrar.REGISTERED_STATUS) {
                message = getResources().getString(R.string.registration_succeeded);
                setScreenContent(R.layout.disconnect);
            } else if (status == DeviceRegistrar.UNREGISTERED_STATUS) {
                message = getResources().getString(R.string.unregistration_succeeded);
                setScreenContent(R.layout.connect);
            } else {
                message = getResources().getString(R.string.registration_error);
            }
            SharedPreferences prefs = Util.getSharedPreferences(mContext);
            stopLoadingScreen();

            String accountName = prefs.getString(Util.ACCOUNT_NAME, "Unknown");
            Util.generateNotification(mContext, String.format(message, accountName));
        }
    };

    /**
     * Shuts down the activity.
     */
    @Override
    public void onDestroy() {
        unregisterReceiver(mUpdateUIReceiver);
        super.onDestroy();
    }

}