1ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// Copyright 2013 The Chromium Authors. All rights reserved.
2ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// Use of this source code is governed by a BSD-style license that can be
3ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch// found in the LICENSE file.
4ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
5ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochpackage org.chromium.chromoting;
6ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
7ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.accounts.Account;
8ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.accounts.AccountManager;
9ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.accounts.AccountManagerCallback;
10ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.accounts.AccountManagerFuture;
11ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.accounts.AuthenticatorException;
12ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.accounts.OperationCanceledException;
13cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)import android.annotation.SuppressLint;
14a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.app.ActionBar;
15ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.app.Activity;
16a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.app.AlertDialog;
175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.app.ProgressDialog;
185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import android.content.DialogInterface;
19ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.content.Intent;
20a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)import android.content.SharedPreferences;
21a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.content.res.Configuration;
22ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.os.Bundle;
23a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.provider.Settings;
24ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.util.Log;
25a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)import android.view.Menu;
26a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)import android.view.MenuItem;
27a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)import android.view.View;
28ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.widget.ArrayAdapter;
29ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.widget.ListView;
30ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport android.widget.Toast;
31ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
32ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport org.chromium.chromoting.jni.JniInterface;
33ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
34ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdochimport java.io.IOException;
355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)import java.util.Arrays;
36ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
37ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch/**
38ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * The user interface for querying and displaying a user's host list from the directory server. It
39ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch * also requests and renews authentication tokens using the system account manager.
40ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch */
415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)public class Chromoting extends Activity implements JniInterface.ConnectionListener,
42effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        AccountManagerCallback<Bundle>, ActionBar.OnNavigationListener, HostListLoader.Callback,
43effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        View.OnClickListener {
44ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    /** Only accounts of this type will be selectable for authentication. */
45ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    private static final String ACCOUNT_TYPE = "com.google";
46ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
47ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    /** Scopes at which the authentication token we request will be valid. */
48ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    private static final String TOKEN_SCOPE = "oauth2:https://www.googleapis.com/auth/chromoting " +
49ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch            "https://www.googleapis.com/auth/googletalk";
50ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
51a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** Web page to be displayed in the Help screen when launched from this activity. */
52a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private static final String HELP_URL =
53a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            "http://support.google.com/chrome/?p=mobile_crd_hostslist";
54a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
55effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /** Web page to be displayed when user triggers the hyperlink for setting up hosts. */
56effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private static final String HOST_SETUP_URL =
57effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            "https://support.google.com/chrome/answer/1649523";
58effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
59ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    /** User's account details. */
60a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    private Account mAccount;
61ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
62a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** List of accounts on the system. */
63a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private Account[] mAccounts;
64a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
65effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /** SpinnerAdapter used in the action bar for selecting accounts. */
66effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private AccountsAdapter mAccountsAdapter;
67effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
68ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    /** Account auth token. */
69a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    private String mToken;
70ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /** Helper for fetching the host list. */
725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private HostListLoader mHostListLoader;
735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
74ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    /** List of hosts. */
75a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private HostInfo[] mHosts = new HostInfo[0];
76a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
77ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    /** Refresh button. */
78ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch    private MenuItem mRefreshButton;
79ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
80effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /** Host list as it appears to the user. */
81effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    private ListView mHostListView;
82a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
83effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /** Progress view shown instead of the host list when the host list is loading. */
84a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private View mProgressView;
85ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /** Dialog for reporting connection progress. */
875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private ProgressDialog mProgressIndicator;
885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
89f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    /** Object for fetching OAuth2 access tokens from third party authorization servers. */
90f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    private ThirdPartyTokenFetcher mTokenFetcher;
91f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * This is set when receiving an authentication error from the HostListLoader. If that occurs,
945d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * this flag is set and a fresh authentication token is fetched from the AccountsService, and
955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * used to request the host list a second time.
965d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
97a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    boolean mTriedNewAuthToken;
98a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
9934680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)    /**
10034680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)     * Flag to track whether a call to AccountManager.getAuthToken() is currently pending.
10134680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)     * This avoids infinitely-nested calls in case onStart() gets triggered a second time
10234680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)     * while a token is being fetched.
10334680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)     */
10434680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)    private boolean mWaitingForAuthToken = false;
10534680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)
106a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** Shows a warning explaining that a Google account is required, then closes the activity. */
107a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private void showNoAccountsDialog() {
108a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        AlertDialog.Builder builder = new AlertDialog.Builder(this);
109a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        builder.setMessage(R.string.noaccounts_message);
110a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        builder.setPositiveButton(R.string.noaccounts_add_account,
111a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                new DialogInterface.OnClickListener() {
112cedac228d2dd51db4b79ea1e72c7f249408ee061Torne (Richard Coles)                    @SuppressLint("InlinedApi")
113a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    @Override
114a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    public void onClick(DialogInterface dialog, int id) {
115a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
116a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        intent.putExtra(Settings.EXTRA_ACCOUNT_TYPES,
117a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                                new String[] { ACCOUNT_TYPE });
118a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        if (intent.resolveActivity(getPackageManager()) != null) {
119a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                            startActivity(intent);
120a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        }
121a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                        finish();
122a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    }
123a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                });
124a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        builder.setNegativeButton(R.string.close, new DialogInterface.OnClickListener() {
125a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                @Override
126a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                public void onClick(DialogInterface dialog, int id) {
127a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    finish();
128a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                }
129a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            });
130a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        builder.setOnCancelListener(new DialogInterface.OnCancelListener() {
131a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                @Override
132a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                public void onCancel(DialogInterface dialog) {
133a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    finish();
134a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                }
135a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            });
136a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
137a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        AlertDialog dialog = builder.create();
138a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        dialog.show();
139a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
140a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
141a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** Shows or hides the progress indicator for loading the host list. */
142a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private void setHostListProgressVisible(boolean visible) {
143effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mHostListView.setVisibility(visible ? View.GONE : View.VISIBLE);
144a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mProgressView.setVisibility(visible ? View.VISIBLE : View.GONE);
145effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
146effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        // Hiding the host-list does not automatically hide the empty view, so do that here.
147effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        if (visible) {
148effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            mHostListView.getEmptyView().setVisibility(View.GONE);
149effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        }
150a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
151ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
152ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    /**
153ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch     * Called when the activity is first created. Loads the native library and requests an
154ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch     * authentication token from the system.
155ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch     */
156ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    @Override
157ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    public void onCreate(Bundle savedInstanceState) {
158ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        super.onCreate(savedInstanceState);
159ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        setContentView(R.layout.main);
160ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
161a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mTriedNewAuthToken = false;
1625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mHostListLoader = new HostListLoader();
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
164ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        // Get ahold of our view widgets.
1651320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mHostListView = (ListView) findViewById(R.id.hostList_chooser);
166effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mHostListView.setEmptyView(findViewById(R.id.hostList_empty));
167a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mProgressView = findViewById(R.id.hostList_progress);
168effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
169effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        findViewById(R.id.host_setup_link_android).setOnClickListener(this);
170ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
171ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        // Bring native components online.
172ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        JniInterface.loadLibrary(this);
173effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
174effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
175f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    @Override
176f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    protected void onNewIntent(Intent intent) {
177f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        super.onNewIntent(intent);
178f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        if (mTokenFetcher != null) {
179f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            if (mTokenFetcher.handleTokenFetched(intent)) {
180f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                mTokenFetcher = null;
181f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            }
182f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        }
183f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
1841320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
185effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /**
186effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * Called when the activity becomes visible. This happens on initial launch and whenever the
187effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * user switches to the activity, for example, by using the window-switcher or when coming from
188effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     * the device's lock screen.
189effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch     */
190effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    @Override
191effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    public void onStart() {
192effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        super.onStart();
193ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
194a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mAccounts = AccountManager.get(this).getAccountsByType(ACCOUNT_TYPE);
195a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (mAccounts.length == 0) {
196a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            showNoAccountsDialog();
197a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            return;
198a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
199a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
200a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        SharedPreferences prefs = getPreferences(MODE_PRIVATE);
201a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        int index = -1;
202a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        if (prefs.contains("account_name") && prefs.contains("account_type")) {
203a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)            mAccount = new Account(prefs.getString("account_name", null),
204a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)                    prefs.getString("account_type", null));
205a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            index = Arrays.asList(mAccounts).indexOf(mAccount);
206a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
207a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (index == -1) {
208a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            // Preference not loaded, or does not correspond to a valid account, so just pick the
209a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            // first account arbitrarily.
210a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            index = 0;
211a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            mAccount = mAccounts[0];
212a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
213a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
214a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (mAccounts.length == 1) {
215a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            getActionBar().setDisplayShowTitleEnabled(true);
216effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
217effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            getActionBar().setTitle(R.string.mode_me2me);
218a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            getActionBar().setSubtitle(mAccount.name);
219a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        } else {
220effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            mAccountsAdapter = new AccountsAdapter(this, mAccounts);
221effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            getActionBar().setDisplayShowTitleEnabled(false);
222a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
223effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            getActionBar().setListNavigationCallbacks(mAccountsAdapter, this);
224a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            getActionBar().setSelectedNavigationItem(index);
225a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        }
226a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
227a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        refreshHostList();
228a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
229a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
230a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    /** Called when the activity is finally finished. */
231a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    @Override
232a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    public void onDestroy() {
233a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        super.onDestroy();
234a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        JniInterface.disconnectFromHost();
235a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
236a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
237a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    /** Called when the display is rotated (as registered in the manifest). */
238a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    @Override
239a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public void onConfigurationChanged(Configuration newConfig) {
240a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        super.onConfigurationChanged(newConfig);
241a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
242a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // Reload the spinner resources, since the font sizes are dependent on the screen
243a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // orientation.
244a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (mAccounts.length != 1) {
245effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch            mAccountsAdapter.notifyDataSetChanged();
246a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
247a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
248a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
249a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    /** Called to initialize the action bar. */
250a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    @Override
251a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    public boolean onCreateOptionsMenu(Menu menu) {
252a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        getMenuInflater().inflate(R.menu.chromoting_actionbar, menu);
253ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        mRefreshButton = menu.findItem(R.id.actionbar_directoryrefresh);
254ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch
255ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch        if (mAccount == null) {
256a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            // If there is no account, don't allow the user to refresh the listing.
257ba5b9a6411cb1792fd21f0a078d7a25cd1ceec16Ben Murdoch            mRefreshButton.setEnabled(false);
258a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        }
259a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
260a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)        return super.onCreateOptionsMenu(menu);
261a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    }
262a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
263a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    /** Called whenever an action bar button is pressed. */
264a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    @Override
265a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)    public boolean onOptionsItemSelected(MenuItem item) {
2660529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        int id = item.getItemId();
2670529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        if (id == R.id.actionbar_directoryrefresh) {
2680529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            refreshHostList();
2690529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            return true;
2700529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        }
2710529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        if (id == R.id.actionbar_help) {
2720529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            HelpActivity.launch(this, HELP_URL);
2730529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch            return true;
274a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        }
2750529e5d033099cbfc42635f6f6183833b09dff6eBen Murdoch        return super.onOptionsItemSelected(item);
276ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
277ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
278effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    /** Called when the user touches hyperlinked text. */
279effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    @Override
280effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    public void onClick(View view) {
281effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        HelpActivity.launch(this, HOST_SETUP_URL);
282effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch    }
283effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch
2845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /** Called when the user taps on a host entry. */
2855d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    public void connectToHost(HostInfo host) {
286a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mProgressIndicator = ProgressDialog.show(this,
287a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              host.name, getString(R.string.footer_connecting), true, true,
288a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              new DialogInterface.OnCancelListener() {
289a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                  @Override
290a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                  public void onCancel(DialogInterface dialog) {
291a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                      JniInterface.disconnectFromHost();
2926d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                      mTokenFetcher = null;
293a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                  }
294a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)              });
295a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        SessionConnector connector = new SessionConnector(this, this, mHostListLoader);
2966d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        assert mTokenFetcher == null;
2976d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        mTokenFetcher = createTokenFetcher(host);
298a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        connector.connectToHost(mAccount.name, mToken, host);
299a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
300ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
301a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    private void refreshHostList() {
30234680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)        if (mWaitingForAuthToken) {
30334680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)            return;
30434680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)        }
30534680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)
306a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mTriedNewAuthToken = false;
307a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        setHostListProgressVisible(true);
308a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
309a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // The refresh button simply makes use of the currently-chosen account.
31034680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)        requestAuthToken();
31134680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)    }
31234680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)
31334680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)    private void requestAuthToken() {
314a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, this, this, null);
31534680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)        mWaitingForAuthToken = true;
3165d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
317ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3185d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Override
3195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    public void run(AccountManagerFuture<Bundle> future) {
3205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        Log.i("auth", "User finished with auth dialogs");
32134680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)        mWaitingForAuthToken = false;
32234680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)
3235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        Bundle result = null;
3245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        String explanation = null;
3255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        try {
3265d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // Here comes our auth token from the Android system.
3275d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            result = future.getResult();
3285d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } catch (OperationCanceledException ex) {
329a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            // User canceled authentication. No need to report an error.
3305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } catch (AuthenticatorException ex) {
331a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            explanation = getString(R.string.error_unexpected);
3325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } catch (IOException ex) {
333a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            explanation = getString(R.string.error_network_error);
334ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        }
335ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (result == null) {
33734680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)            setHostListProgressVisible(false);
338a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            if (explanation != null) {
339a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                Toast.makeText(this, explanation, Toast.LENGTH_LONG).show();
340a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            }
3415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return;
3425d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
343ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mToken = result.getString(AccountManager.KEY_AUTHTOKEN);
3455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        Log.i("auth", "Received an auth token from system");
346ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3471320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        mHostListLoader.retrieveHostList(mToken, this);
3485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
3495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
3505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Override
351a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    public boolean onNavigationItemSelected(int itemPosition, long itemId) {
352a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mAccount = mAccounts[itemPosition];
353a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
354a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        getPreferences(MODE_PRIVATE).edit().putString("account_name", mAccount.name).
355a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                    putString("account_type", mAccount.type).apply();
356a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
357a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        // The current host list is no longer valid for the new account, so clear the list.
358a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        mHosts = new HostInfo[0];
359a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        updateUi();
360a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        refreshHostList();
361a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        return true;
362a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    }
363a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
364a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    @Override
3655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    public void onHostListReceived(HostInfo[] hosts) {
3665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        // Store a copy of the array, so that it can't be mutated by the HostListLoader. HostInfo
3675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        // is an immutable type, so a shallow copy of the array is sufficient here.
3685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        mHosts = Arrays.copyOf(hosts, hosts.length);
369a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        setHostListProgressVisible(false);
3705d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        updateUi();
371ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
372ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Override
3745d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    public void onError(HostListLoader.Error error) {
3755d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        String explanation = null;
3765d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        switch (error) {
3775d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case AUTH_FAILED:
3785d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break;
3795d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case NETWORK_ERROR:
380a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                explanation = getString(R.string.error_network_error);
3815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break;
3825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case UNEXPECTED_RESPONSE:
383a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            case SERVICE_UNAVAILABLE:
3845d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case UNKNOWN:
385a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                explanation = getString(R.string.error_unexpected);
3865d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break;
3875d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            default:
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                // Unreachable.
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                return;
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
391ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3925d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (explanation != null) {
3935d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            Toast.makeText(this, explanation, Toast.LENGTH_LONG).show();
394a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            setHostListProgressVisible(false);
3955d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return;
396ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        }
397ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
3985d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        // This is the AUTH_FAILED case.
399a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
400a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        if (!mTriedNewAuthToken) {
4015d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // This was our first connection attempt.
402a36e5920737c6adbddd3e43b760e5de8431db6e0Torne (Richard Coles)
4035d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            AccountManager authenticator = AccountManager.get(this);
404a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            mTriedNewAuthToken = true;
405ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
4065d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            Log.w("auth", "Requesting renewal of rejected auth token");
4075d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            authenticator.invalidateAuthToken(mAccount.type, mToken);
4085d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mToken = null;
40934680572440d7894ef8dafce81d8039ed80726a2Torne (Richard Coles)            requestAuthToken();
410ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
4115d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // We're not in an error state *yet*.
4125d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            return;
4135d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        } else {
4145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            // Authentication truly failed.
4155d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            Log.e("auth", "Fresh auth token was also rejected");
416a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            explanation = getString(R.string.error_authentication_failed);
4175d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            Toast.makeText(this, explanation, Toast.LENGTH_LONG).show();
418a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)            setHostListProgressVisible(false);
419ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        }
420ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
421ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
4225d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    /**
4235d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     * Updates the infotext and host list display.
4245d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)     */
4255d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    private void updateUi() {
42646d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        if (mRefreshButton != null) {
42746d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)            mRefreshButton.setEnabled(mAccount != null);
42846d4c2bc3267f3f028f39e7e311b0f89aba2e4fdTorne (Richard Coles)        }
4295d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        ArrayAdapter<HostInfo> displayer = new HostListAdapter(this, R.layout.host, mHosts);
4305d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        Log.i("hostlist", "About to populate host list display");
431effb81e5f8246d0db0270817048dc992db66e9fbBen Murdoch        mHostListView.setAdapter(displayer);
4325d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    }
4335d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4345d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    @Override
4355d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    public void onConnectionState(JniInterface.ConnectionListener.State state,
4365d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            JniInterface.ConnectionListener.Error error) {
4375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        boolean dismissProgress = false;
4385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        switch (state) {
4395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case INITIALIZING:
4405d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case CONNECTING:
4415d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case AUTHENTICATED:
442a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                // The connection is still being established.
4435d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break;
4445d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4455d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case CONNECTED:
4465d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                dismissProgress = true;
4475d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                // Display the remote desktop.
4485d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                startActivityForResult(new Intent(this, Desktop.class), 0);
4495d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break;
4505d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4515d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case FAILED:
4525d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                dismissProgress = true;
453a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)                Toast.makeText(this, getString(error.message()), Toast.LENGTH_LONG).show();
4545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                // Close the Desktop view, if it is currently running.
4555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                finishActivity(0);
4565d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break;
4575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            case CLOSED:
4595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                // No need to show toast in this case. Either the connection will have failed
4605d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                // because of an error, which will trigger toast already. Or the disconnection will
4615d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                // have been initiated by the user.
4625d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                dismissProgress = true;
4635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                finishActivity(0);
4645d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                break;
4655d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
4665d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            default:
4675d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                // Unreachable, but required by Google Java style and findbugs.
4685d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                assert false : "Unreached";
4695d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        }
470ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch
4715d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)        if (dismissProgress && mProgressIndicator != null) {
4725d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mProgressIndicator.dismiss();
4735d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)            mProgressIndicator = null;
474ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch        }
475ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch    }
476f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
4776d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    private ThirdPartyTokenFetcher createTokenFetcher(HostInfo host) {
478f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        ThirdPartyTokenFetcher.Callback callback = new ThirdPartyTokenFetcher.Callback() {
4791320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci            @Override
480f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            public void onTokenFetched(String code, String accessToken) {
481f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                // The native client sends the OAuth authorization code to the host as the token so
482f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                // that the host can obtain the shared secret from the third party authorization
483f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                // server.
484f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                String token = code;
485f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
486f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                // The native client uses the OAuth access token as the shared secret to
487f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                // authenticate itself with the host using spake.
488f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                String sharedSecret = accessToken;
489f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
4906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                JniInterface.onThirdPartyTokenFetched(token, sharedSecret);
491f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            }
492f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        };
4936d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        return new ThirdPartyTokenFetcher(this, host.getTokenUrlPatterns(), callback);
494f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
495f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
4966d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    public void fetchThirdPartyToken(String tokenUrl, String clientId, String scope) {
4976d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        assert mTokenFetcher != null;
4986d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        mTokenFetcher.fetchToken(tokenUrl, clientId, scope);
4996d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    }
500ca12bfac764ba476d6cd062bf1dde12cc64c3f40Ben Murdoch}
501