Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(56)

Side by Side Diff: remoting/android/java/src/org/chromium/chromoting/Chromoting.java

Issue 165743002: Implement account switcher as action-bar navigation spinner. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 6 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
1 // Copyright 2013 The Chromium Authors. All rights reserved. 1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 package org.chromium.chromoting; 5 package org.chromium.chromoting;
6 6
7 import android.accounts.Account; 7 import android.accounts.Account;
8 import android.accounts.AccountManager; 8 import android.accounts.AccountManager;
9 import android.accounts.AccountManagerCallback; 9 import android.accounts.AccountManagerCallback;
10 import android.accounts.AccountManagerFuture; 10 import android.accounts.AccountManagerFuture;
11 import android.accounts.AuthenticatorException; 11 import android.accounts.AuthenticatorException;
12 import android.accounts.OperationCanceledException; 12 import android.accounts.OperationCanceledException;
13 import android.app.ActionBar;
13 import android.app.Activity; 14 import android.app.Activity;
14 import android.app.ProgressDialog; 15 import android.app.ProgressDialog;
15 import android.content.DialogInterface; 16 import android.content.DialogInterface;
16 import android.content.Intent; 17 import android.content.Intent;
17 import android.content.SharedPreferences; 18 import android.content.SharedPreferences;
19 import android.content.res.Configuration;
18 import android.os.Bundle; 20 import android.os.Bundle;
19 import android.util.Log; 21 import android.util.Log;
20 import android.view.Menu; 22 import android.view.Menu;
21 import android.view.MenuItem; 23 import android.view.MenuItem;
22 import android.widget.ArrayAdapter; 24 import android.widget.ArrayAdapter;
23 import android.widget.ListView; 25 import android.widget.ListView;
24 import android.widget.TextView; 26 import android.widget.TextView;
25 import android.widget.Toast; 27 import android.widget.Toast;
26 28
27 import org.chromium.chromoting.jni.JniInterface; 29 import org.chromium.chromoting.jni.JniInterface;
28 30
29 import java.io.IOException; 31 import java.io.IOException;
32 import java.util.Arrays;
30 33
31 /** 34 /**
32 * The user interface for querying and displaying a user's host list from the di rectory server. It 35 * The user interface for querying and displaying a user's host list from the di rectory server. It
33 * also requests and renews authentication tokens using the system account manag er. 36 * also requests and renews authentication tokens using the system account manag er.
34 */ 37 */
35 public class Chromoting extends Activity implements JniInterface.ConnectionListe ner, 38 public class Chromoting extends Activity implements JniInterface.ConnectionListe ner,
36 AccountManagerCallback<Bundle>, HostListLoader.Callback { 39 AccountManagerCallback<Bundle>, ActionBar.OnNavigationListener,
40 HostListLoader.Callback {
37 /** Only accounts of this type will be selectable for authentication. */ 41 /** Only accounts of this type will be selectable for authentication. */
38 private static final String ACCOUNT_TYPE = "com.google"; 42 private static final String ACCOUNT_TYPE = "com.google";
39 43
40 /** Scopes at which the authentication token we request will be valid. */ 44 /** Scopes at which the authentication token we request will be valid. */
41 private static final String TOKEN_SCOPE = "oauth2:https://www.googleapis.com /auth/chromoting " + 45 private static final String TOKEN_SCOPE = "oauth2:https://www.googleapis.com /auth/chromoting " +
42 "https://www.googleapis.com/auth/googletalk"; 46 "https://www.googleapis.com/auth/googletalk";
43 47
44 /** User's account details. */ 48 /** User's account details. */
45 private Account mAccount; 49 private Account mAccount;
46 50
51 /** List of accounts on the system. */
52 private Account[] mAccounts;
53
47 /** Account auth token. */ 54 /** Account auth token. */
48 private String mToken; 55 private String mToken;
49 56
50 /** Helper for fetching the host list. */ 57 /** Helper for fetching the host list. */
51 private HostListLoader mHostListLoader; 58 private HostListLoader mHostListLoader;
52 59
53 /** List of hosts. */ 60 /** List of hosts. */
54 private Host[] mHosts; 61 private Host[] mHosts;
55 62
56 /** Refresh button. */ 63 /** Refresh button. */
57 private MenuItem mRefreshButton; 64 private MenuItem mRefreshButton;
58 65
59 /** Account switcher. */
60 private MenuItem mAccountSwitcher;
61
62 /** Greeting at the top of the displayed list. */ 66 /** Greeting at the top of the displayed list. */
63 private TextView mGreeting; 67 private TextView mGreeting;
64 68
65 /** Host list as it appears to the user. */ 69 /** Host list as it appears to the user. */
66 private ListView mList; 70 private ListView mList;
67 71
68 /** Dialog for reporting connection progress. */ 72 /** Dialog for reporting connection progress. */
69 private ProgressDialog mProgressIndicator; 73 private ProgressDialog mProgressIndicator;
70 74
71 /** 75 /**
72 * This is set when receiving an authentication error from the HostListLoade r. If that occurs, 76 * This is set when receiving an authentication error from the HostListLoade r. If that occurs,
73 * this flag is set and a fresh authentication token is fetched from the Acc ountsService, and 77 * this flag is set and a fresh authentication token is fetched from the Acc ountsService, and
74 * used to request the host list a second time. 78 * used to request the host list a second time.
75 */ 79 */
76 boolean mAlreadyTried; 80 boolean mAlreadyTried;
Sergey Ulanov 2014/02/18 21:58:43 maybe rename this to make it clear that this has t
Lambros 2014/02/19 23:51:39 Done.
77 81
78 /** 82 /**
79 * Called when the activity is first created. Loads the native library and r equests an 83 * Called when the activity is first created. Loads the native library and r equests an
80 * authentication token from the system. 84 * authentication token from the system.
81 */ 85 */
82 @Override 86 @Override
83 public void onCreate(Bundle savedInstanceState) { 87 public void onCreate(Bundle savedInstanceState) {
84 super.onCreate(savedInstanceState); 88 super.onCreate(savedInstanceState);
85 setContentView(R.layout.main); 89 setContentView(R.layout.main);
86 90
87 mAlreadyTried = false; 91 mAlreadyTried = false;
88 mHostListLoader = new HostListLoader(); 92 mHostListLoader = new HostListLoader();
89 93
90 // Get ahold of our view widgets. 94 // Get ahold of our view widgets.
91 mGreeting = (TextView)findViewById(R.id.hostList_greeting); 95 mGreeting = (TextView)findViewById(R.id.hostList_greeting);
92 mList = (ListView)findViewById(R.id.hostList_chooser); 96 mList = (ListView)findViewById(R.id.hostList_chooser);
93 97
94 // Bring native components online. 98 // Bring native components online.
95 JniInterface.loadLibrary(this); 99 JniInterface.loadLibrary(this);
96 100
101 mAccounts = AccountManager.get(this).getAccountsByType(ACCOUNT_TYPE);
102 if (mAccounts.length == 0) {
103 // TODO(lambroslambrou): Show a dialog with message: "To use <App Na me>, you'll need
104 // to add a Google Account to your device." and two buttons: "Close" , "Add account".
105 // The "Add account" button should navigate to the system "Add a Goo gle Account"
106 // screen.
107 return;
108 }
109
97 SharedPreferences prefs = getPreferences(MODE_PRIVATE); 110 SharedPreferences prefs = getPreferences(MODE_PRIVATE);
111 int index = -1;
98 if (prefs.contains("account_name") && prefs.contains("account_type")) { 112 if (prefs.contains("account_name") && prefs.contains("account_type")) {
99 // Perform authentication using saved account selection.
100 mAccount = new Account(prefs.getString("account_name", null), 113 mAccount = new Account(prefs.getString("account_name", null),
101 prefs.getString("account_type", null)); 114 prefs.getString("account_type", null));
102 AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, t his, this, null); 115 index = Arrays.asList(mAccounts).indexOf(mAccount);
103 if (mAccountSwitcher != null) { 116 }
104 mAccountSwitcher.setTitle(mAccount.name); 117 if (index == -1) {
105 } 118 // Preference not loaded, or does not correspond to a valid account, so just pick the
119 // first account arbitrarily.
120 index = 0;
121 mAccount = mAccounts[0];
122 }
123
124 if (mAccounts.length == 1) {
125 getActionBar().setDisplayShowTitleEnabled(true);
126 getActionBar().setSubtitle(mAccount.name);
106 } else { 127 } else {
107 // Request auth callback once user has chosen an account. 128 AccountsAdapter adapter = new AccountsAdapter(this, mAccounts);
108 Log.i("auth", "Requesting auth token from system"); 129 getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
109 AccountManager.get(this).getAuthTokenByFeatures(ACCOUNT_TYPE, TOKEN_ SCOPE, null, this, 130 getActionBar().setListNavigationCallbacks(adapter, this);
110 null, null, this, null); 131 getActionBar().setSelectedNavigationItem(index);
111 } 132 }
133
134 refreshHostList();
112 } 135 }
113 136
114 /** Called when the activity is finally finished. */ 137 /** Called when the activity is finally finished. */
115 @Override 138 @Override
116 public void onDestroy() { 139 public void onDestroy() {
117 super.onDestroy(); 140 super.onDestroy();
118 JniInterface.disconnectFromHost(); 141 JniInterface.disconnectFromHost();
119 } 142 }
120 143
144 /** Called when the display is rotated (as registered in the manifest). */
145 @Override
146 public void onConfigurationChanged(Configuration newConfig) {
147 super.onConfigurationChanged(newConfig);
148
149 // Reload the spinner resources, since the font sizes are dependent on t he screen
150 // orientation.
151 if (mAccounts.length != 1) {
152 AccountsAdapter adapter = new AccountsAdapter(this, mAccounts);
153 getActionBar().setListNavigationCallbacks(adapter, this);
154 }
155 }
156
121 /** Called to initialize the action bar. */ 157 /** Called to initialize the action bar. */
122 @Override 158 @Override
123 public boolean onCreateOptionsMenu(Menu menu) { 159 public boolean onCreateOptionsMenu(Menu menu) {
124 getMenuInflater().inflate(R.menu.chromoting_actionbar, menu); 160 getMenuInflater().inflate(R.menu.chromoting_actionbar, menu);
125 mRefreshButton = menu.findItem(R.id.actionbar_directoryrefresh); 161 mRefreshButton = menu.findItem(R.id.actionbar_directoryrefresh);
126 mAccountSwitcher = menu.findItem(R.id.actionbar_accountswitcher);
127
128 Account[] usableAccounts = AccountManager.get(this).getAccountsByType(AC COUNT_TYPE);
129 if (usableAccounts.length == 1 && usableAccounts[0].equals(mAccount)) {
130 // If we're using the only available account, don't offer account sw itching.
131 // (If there are *no* accounts available, clicking this allows you t o add a new one.)
132 mAccountSwitcher.setEnabled(false);
133 }
134 162
135 if (mAccount == null) { 163 if (mAccount == null) {
136 // If no account has been chosen, don't allow the user to refresh th e listing. 164 // If there is no account, don't allow the user to refresh the listi ng.
137 mRefreshButton.setEnabled(false); 165 mRefreshButton.setEnabled(false);
138 } else {
139 // If the user has picked an account, show its name directly on the account switcher.
140 mAccountSwitcher.setTitle(mAccount.name);
141 } 166 }
142 167
143 return super.onCreateOptionsMenu(menu); 168 return super.onCreateOptionsMenu(menu);
144 } 169 }
145 170
146 /** Called whenever an action bar button is pressed. */ 171 /** Called whenever an action bar button is pressed. */
147 @Override 172 @Override
148 public boolean onOptionsItemSelected(MenuItem item) { 173 public boolean onOptionsItemSelected(MenuItem item) {
149 mAlreadyTried = false; 174 refreshHostList();
150 if (item == mAccountSwitcher) {
151 // The account switcher triggers a listing of all available accounts .
152 AccountManager.get(this).getAuthTokenByFeatures(ACCOUNT_TYPE, TOKEN_ SCOPE, null, this,
153 null, null, this, null);
154 } else {
155 // The refresh button simply makes use of the currently-chosen accou nt.
156 AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, t his, this, null);
157 }
158
159 return true; 175 return true;
160 } 176 }
161 177
162 /** Called when the user taps on a host entry. */ 178 /** Called when the user taps on a host entry. */
163 public void connectToHost(Host host) { 179 public void connectToHost(Host host) {
164 JniInterface.connectToHost(mAccount.name, mToken, host.jabberId, host.id , host.publicKey, 180 JniInterface.connectToHost(mAccount.name, mToken, host.jabberId, host.id , host.publicKey,
165 this); 181 this);
166 } 182 }
167 183
184 private void refreshHostList() {
185 mAlreadyTried = false;
186
187 // The refresh button simply makes use of the currently-chosen account.
188 AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, this, this, null);
189 }
190
168 @Override 191 @Override
169 public void run(AccountManagerFuture<Bundle> future) { 192 public void run(AccountManagerFuture<Bundle> future) {
170 Log.i("auth", "User finished with auth dialogs"); 193 Log.i("auth", "User finished with auth dialogs");
171 try { 194 try {
172 // Here comes our auth token from the Android system. 195 // Here comes our auth token from the Android system.
173 Bundle result = future.getResult(); 196 Bundle result = future.getResult();
174 String accountName = result.getString(AccountManager.KEY_ACCOUNT_NAM E);
175 String accountType = result.getString(AccountManager.KEY_ACCOUNT_TYP E);
176 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN); 197 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN);
177 Log.i("auth", "Received an auth token from system"); 198 Log.i("auth", "Received an auth token from system");
178 199
179 mAccount = new Account(accountName, accountType);
180 mToken = authToken; 200 mToken = authToken;
181 getPreferences(MODE_PRIVATE).edit().putString("account_name", accoun tName).
182 putString("account_type", accountType).apply();
183 201
184 mHostListLoader.retrieveHostList(authToken, this); 202 mHostListLoader.retrieveHostList(authToken, this);
185 } catch (OperationCanceledException ex) { 203 } catch (OperationCanceledException ex) {
186 String explanation = getString(R.string.error_auth_canceled); 204 String explanation = getString(R.string.error_auth_canceled);
187 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show(); 205 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show();
188 } catch (AuthenticatorException ex) { 206 } catch (AuthenticatorException ex) {
189 String explanation = getString(R.string.error_no_accounts); 207 String explanation = getString(R.string.error_no_accounts);
190 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show(); 208 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show();
191 } catch (IOException ex) { 209 } catch (IOException ex) {
192 String explanation = getString(R.string.error_bad_connection); 210 String explanation = getString(R.string.error_bad_connection);
193 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show(); 211 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show();
194 } 212 }
195 } 213 }
196 214
197 @Override 215 @Override
216 public boolean onNavigationItemSelected(int itemPosition, long itemId) {
217 mAccount = mAccounts[itemPosition];
218
219 getPreferences(MODE_PRIVATE).edit().putString("account_name", mAccount.n ame).
220 putString("account_type", mAccount.type).apply();
221
222 refreshHostList();
223 return true;
224 }
225
226 @Override
198 public void onHostListReceived(Host[] hosts) { 227 public void onHostListReceived(Host[] hosts) {
199 mHosts = hosts; 228 mHosts = hosts;
200 updateUi(); 229 updateUi();
201 } 230 }
202 231
203 @Override 232 @Override
204 public void onError(HostListLoader.Error error) { 233 public void onError(HostListLoader.Error error) {
205 String explanation = null; 234 String explanation = null;
206 switch (error) { 235 switch (error) {
207 case AUTH_FAILED: 236 case AUTH_FAILED:
(...skipping 39 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 explanation = getString(R.string.error_auth_failed); 276 explanation = getString(R.string.error_auth_failed);
248 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show(); 277 Toast.makeText(this, explanation, Toast.LENGTH_LONG).show();
249 } 278 }
250 } 279 }
251 280
252 /** 281 /**
253 * Updates the infotext and host list display. 282 * Updates the infotext and host list display.
254 */ 283 */
255 private void updateUi() { 284 private void updateUi() {
256 mRefreshButton.setEnabled(mAccount != null); 285 mRefreshButton.setEnabled(mAccount != null);
257 if (mAccount != null) {
258 mAccountSwitcher.setTitle(mAccount.name);
259 }
260 286
261 if (mHosts == null) { 287 if (mHosts == null) {
262 mGreeting.setText(getString(R.string.inst_empty_list)); 288 mGreeting.setText(getString(R.string.inst_empty_list));
263 mList.setAdapter(null); 289 mList.setAdapter(null);
264 return; 290 return;
265 } 291 }
266 292
267 mGreeting.setText(getString(R.string.inst_host_list)); 293 mGreeting.setText(getString(R.string.inst_host_list));
268 294
269 ArrayAdapter<Host> displayer = new HostListAdapter(this, R.layout.host, mHosts); 295 ArrayAdapter<Host> displayer = new HostListAdapter(this, R.layout.host, mHosts);
(...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after
325 // Unreachable, but required by Google Java style and findbugs. 351 // Unreachable, but required by Google Java style and findbugs.
326 assert false : "Unreached"; 352 assert false : "Unreached";
327 } 353 }
328 354
329 if (dismissProgress && mProgressIndicator != null) { 355 if (dismissProgress && mProgressIndicator != null) {
330 mProgressIndicator.dismiss(); 356 mProgressIndicator.dismiss();
331 mProgressIndicator = null; 357 mProgressIndicator = null;
332 } 358 }
333 } 359 }
334 } 360 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698