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

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

Issue 102273006: Refactor Chromoting.java to pull out some nested classes. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Move HostListAdapter to separate file Created 6 years, 11 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.Activity; 13 import android.app.Activity;
14 import android.content.Context;
15 import android.content.Intent; 14 import android.content.Intent;
16 import android.content.SharedPreferences; 15 import android.content.SharedPreferences;
17 import android.os.Bundle; 16 import android.os.Bundle;
18 import android.os.Handler; 17 import android.os.Handler;
19 import android.os.HandlerThread; 18 import android.os.HandlerThread;
20 import android.text.Html;
21 import android.util.Log; 19 import android.util.Log;
22 import android.view.Menu; 20 import android.view.Menu;
23 import android.view.MenuItem; 21 import android.view.MenuItem;
24 import android.view.View;
25 import android.view.ViewGroup;
26 import android.widget.ArrayAdapter; 22 import android.widget.ArrayAdapter;
27 import android.widget.ListView; 23 import android.widget.ListView;
28 import android.widget.TextView; 24 import android.widget.TextView;
29 import android.widget.Toast; 25 import android.widget.Toast;
30 26
31 import org.chromium.chromoting.jni.JniInterface; 27 import org.chromium.chromoting.jni.JniInterface;
32 import org.json.JSONArray; 28 import org.json.JSONArray;
33 import org.json.JSONException; 29 import org.json.JSONException;
34 import org.json.JSONObject; 30 import org.json.JSONObject;
35 31
(...skipping 11 matching lines...) Expand all
47 private static final String ACCOUNT_TYPE = "com.google"; 43 private static final String ACCOUNT_TYPE = "com.google";
48 44
49 /** Scopes at which the authentication token we request will be valid. */ 45 /** Scopes at which the authentication token we request will be valid. */
50 private static final String TOKEN_SCOPE = "oauth2:https://www.googleapis.com /auth/chromoting " + 46 private static final String TOKEN_SCOPE = "oauth2:https://www.googleapis.com /auth/chromoting " +
51 "https://www.googleapis.com/auth/googletalk"; 47 "https://www.googleapis.com/auth/googletalk";
52 48
53 /** Path from which to download a user's host list JSON object. */ 49 /** Path from which to download a user's host list JSON object. */
54 private static final String HOST_LIST_PATH = 50 private static final String HOST_LIST_PATH =
55 "https://www.googleapis.com/chromoting/v1/@me/hosts?key="; 51 "https://www.googleapis.com/chromoting/v1/@me/hosts?key=";
56 52
57 /** Color to use for hosts that are online. */ 53 /** Lock to protect |mAccount| and |mToken|. */
58 private static final String HOST_COLOR_ONLINE = "green"; 54 // TODO(lambroslambrou): |mHosts| needs to be protected as well.
59 55 private Object mLock = new Object();
Sergey Ulanov 2013/12/28 02:17:30 Do we really need this lock? It won't be necessary
Lambros 2013/12/30 21:46:35 We still need the network thread. I don't know if
60 /** Color to use for hosts that are offline. */
61 private static final String HOST_COLOR_OFFLINE = "red";
62 56
63 /** User's account details. */ 57 /** User's account details. */
64 private Account mAccount; 58 private Account mAccount;
65 59
66 /** Account auth token. */ 60 /** Account auth token. */
67 private String mToken; 61 private String mToken;
68 62
69 /** List of hosts. */ 63 /** List of hosts. */
70 private JSONArray mHosts; 64 private JSONArray mHosts;
71 65
(...skipping 32 matching lines...) Expand 10 before | Expand all | Expand 10 after
104 HandlerThread thread = new HandlerThread("auth_callback"); 98 HandlerThread thread = new HandlerThread("auth_callback");
105 thread.start(); 99 thread.start();
106 mNetwork = new Handler(thread.getLooper()); 100 mNetwork = new Handler(thread.getLooper());
107 101
108 SharedPreferences prefs = getPreferences(MODE_PRIVATE); 102 SharedPreferences prefs = getPreferences(MODE_PRIVATE);
109 if (prefs.contains("account_name") && prefs.contains("account_type")) { 103 if (prefs.contains("account_name") && prefs.contains("account_type")) {
110 // Perform authentication using saved account selection. 104 // Perform authentication using saved account selection.
111 mAccount = new Account(prefs.getString("account_name", null), 105 mAccount = new Account(prefs.getString("account_name", null),
112 prefs.getString("account_type", null)); 106 prefs.getString("account_type", null));
113 AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, t his, 107 AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, t his,
114 new HostListDirectoryGrabber(this), mNetwork); 108 new HostListDirectoryGrabber(this), mNetwork);
Sergey Ulanov 2013/12/28 02:17:30 The last parameter here specifies the thread on wh
Lambros 2013/12/30 21:46:35 We still need the lock, regardless of what we pass
115 if (mAccountSwitcher != null) { 109 if (mAccountSwitcher != null) {
116 mAccountSwitcher.setTitle(mAccount.name); 110 mAccountSwitcher.setTitle(mAccount.name);
117 } 111 }
118 } else { 112 } else {
119 // Request auth callback once user has chosen an account. 113 // Request auth callback once user has chosen an account.
120 Log.i("auth", "Requesting auth token from system"); 114 Log.i("auth", "Requesting auth token from system");
121 AccountManager.get(this).getAuthTokenByFeatures( 115 AccountManager.get(this).getAuthTokenByFeatures(
122 ACCOUNT_TYPE, 116 ACCOUNT_TYPE,
123 TOKEN_SCOPE, 117 TOKEN_SCOPE,
124 null, 118 null,
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
181 } 175 }
182 else { 176 else {
183 // The refresh button simply makes use of the currently-chosen accou nt. 177 // The refresh button simply makes use of the currently-chosen accou nt.
184 AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, t his, 178 AccountManager.get(this).getAuthToken(mAccount, TOKEN_SCOPE, null, t his,
185 new HostListDirectoryGrabber(this), mNetwork); 179 new HostListDirectoryGrabber(this), mNetwork);
186 } 180 }
187 181
188 return true; 182 return true;
189 } 183 }
190 184
185 /** Called when the user taps on a host entry. */
186 public void connectToHost(JSONObject host) {
187 try {
188 synchronized (mLock) {
189 JniInterface.connectToHost(mAccount.name, mToken, host.getString ("jabberId"),
190 host.getString("hostId"), host.getString("publicKey"),
191 new Runnable() {
192 @Override
193 public void run() {
194 startActivity(new Intent(Chromoting.this, De sktop.class));
195 }
196 });
197 }
198 } catch (JSONException ex) {
199 Log.w("host", ex);
200 Toast.makeText(this, getString(R.string.error_reading_host),
201 Toast.LENGTH_LONG).show();
202 // Close the application.
203 finish();
204 }
205 }
206
191 /** 207 /**
192 * Processes the authentication token once the system provides it. Once in p ossession of such a 208 * Processes the authentication token once the system provides it. Once in p ossession of such a
193 * token, attempts to request a host list from the directory server. In case of a bad response, 209 * token, attempts to request a host list from the directory server. In case of a bad response,
194 * this is retried once in case the system's cached auth token had expired. 210 * this is retried once in case the system's cached auth token had expired.
195 */ 211 */
196 private class HostListDirectoryGrabber implements AccountManagerCallback<Bun dle> { 212 private class HostListDirectoryGrabber implements AccountManagerCallback<Bun dle> {
Sergey Ulanov 2013/12/28 02:17:30 While you are removing nested classes, you can get
Lambros 2013/12/30 21:46:35 The problem is that the nested class has some inte
197 /** Whether authentication has already been attempted. */ 213 /** Whether authentication has already been attempted. */
198 private boolean mAlreadyTried; 214 private boolean mAlreadyTried;
199 215
200 /** Communication with the screen. */ 216 /** Communication with the screen. */
201 private Activity mUi; 217 private Activity mUi;
202 218
203 /** Constructor. */ 219 /** Constructor. */
204 public HostListDirectoryGrabber(Activity ui) { 220 public HostListDirectoryGrabber(Activity ui) {
205 mAlreadyTried = false; 221 mAlreadyTried = false;
206 mUi = ui; 222 mUi = ui;
207 } 223 }
208 224
209 /** 225 /**
210 * Retrieves the host list from the directory server. This method perfor ms 226 * Retrieves the host list from the directory server. This method perfor ms
211 * network operations and must be run an a non-UI thread. 227 * network operations and must be run an a non-UI thread.
212 */ 228 */
213 @Override 229 @Override
214 public void run(AccountManagerFuture<Bundle> future) { 230 public void run(AccountManagerFuture<Bundle> future) {
215 Log.i("auth", "User finished with auth dialogs"); 231 Log.i("auth", "User finished with auth dialogs");
216 try { 232 try {
217 // Here comes our auth token from the Android system. 233 // Here comes our auth token from the Android system.
218 Bundle result = future.getResult(); 234 Bundle result = future.getResult();
219 String accountName = result.getString(AccountManager.KEY_ACCOUNT _NAME); 235 String accountName = result.getString(AccountManager.KEY_ACCOUNT _NAME);
220 String accountType = result.getString(AccountManager.KEY_ACCOUNT _TYPE); 236 String accountType = result.getString(AccountManager.KEY_ACCOUNT _TYPE);
221 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN ); 237 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN );
222 Log.i("auth", "Received an auth token from system"); 238 Log.i("auth", "Received an auth token from system");
223 239
224 synchronized (mUi) { 240 synchronized (mLock) {
225 mAccount = new Account(accountName, accountType); 241 mAccount = new Account(accountName, accountType);
226 mToken = authToken; 242 mToken = authToken;
227 getPreferences(MODE_PRIVATE).edit().putString("account_name" , accountName). 243 getPreferences(MODE_PRIVATE).edit().putString("account_name" , accountName).
228 putString("account_type", accountType).apply(); 244 putString("account_type", accountType).apply();
229 } 245 }
230 246
231 // Send our HTTP request to the directory server. 247 // Send our HTTP request to the directory server.
232 URLConnection link = 248 URLConnection link =
233 new URL(HOST_LIST_PATH + JniInterface.nativeGetApiKey()) .openConnection(); 249 new URL(HOST_LIST_PATH + JniInterface.nativeGetApiKey()) .openConnection();
234 link.addRequestProperty("client_id", JniInterface.nativeGetClien tId()); 250 link.addRequestProperty("client_id", JniInterface.nativeGetClien tId());
(...skipping 20 matching lines...) Expand all
255 // Assemble error message to display to the user. 271 // Assemble error message to display to the user.
256 String explanation = getString(R.string.error_unknown); 272 String explanation = getString(R.string.error_unknown);
257 if (ex instanceof OperationCanceledException) { 273 if (ex instanceof OperationCanceledException) {
258 explanation = getString(R.string.error_auth_canceled); 274 explanation = getString(R.string.error_auth_canceled);
259 } else if (ex instanceof AuthenticatorException) { 275 } else if (ex instanceof AuthenticatorException) {
260 explanation = getString(R.string.error_no_accounts); 276 explanation = getString(R.string.error_no_accounts);
261 } else if (ex instanceof IOException) { 277 } else if (ex instanceof IOException) {
262 if (!mAlreadyTried) { 278 if (!mAlreadyTried) {
263 // This was our first connection attempt. 279 // This was our first connection attempt.
264 280
265 synchronized (mUi) { 281 synchronized (mLock) {
266 if (mAccount != null) { 282 if (mAccount != null) {
267 // We got an account, but couldn't log into it. We'll retry in case 283 // We got an account, but couldn't log into it. We'll retry in case
268 // the system's cached authentication token had already expired. 284 // the system's cached authentication token had already expired.
269 AccountManager authenticator = AccountManager.ge t(mUi); 285 AccountManager authenticator = AccountManager.ge t(mUi);
270 mAlreadyTried = true; 286 mAlreadyTried = true;
271 287
272 Log.w("auth", "Requesting renewal of rejected au th token"); 288 Log.w("auth", "Requesting renewal of rejected au th token");
273 authenticator.invalidateAuthToken(mAccount.type, mToken); 289 authenticator.invalidateAuthToken(mAccount.type, mToken);
274 mToken = null; 290 mToken = null;
275 authenticator.getAuthToken( 291 authenticator.getAuthToken(
276 mAccount, TOKEN_SCOPE, null, mUi, this, mNetwork); 292 mAccount, TOKEN_SCOPE, null, mUi, this, mNetwork);
277 293
278 // We're not in an error state *yet*. 294 // We're not in an error state *yet*.
279 return; 295 return;
280 } 296 }
281 } 297 }
282 298
283 // We didn't even get an account, so the auth server is likely unreachable. 299 // We didn't even get an account, so the auth server is likely unreachable.
284 explanation = getString(R.string.error_bad_connection); 300 explanation = getString(R.string.error_bad_connection);
285 } else { 301 } else {
286 // Authentication truly failed. 302 // Authentication truly failed.
287 Log.e("auth", "Fresh auth token was also rejected"); 303 Log.e("auth", "Fresh auth token was also rejected");
288 explanation = getString(R.string.error_auth_failed); 304 explanation = getString(R.string.error_auth_failed);
289 } 305 }
290 } else if (ex instanceof JSONException) { 306 } else if (ex instanceof JSONException) {
291 explanation = getString(R.string.error_unexpected_response); 307 explanation = getString(R.string.error_unexpected_response);
292 runOnUiThread(new HostListDisplayer(mUi));
293 } 308 }
294 309
295 mHosts = null; 310 mHosts = null;
296 Log.w("auth", ex); 311 Log.w("auth", ex);
297 Toast.makeText(mUi, explanation, Toast.LENGTH_LONG).show(); 312 Toast.makeText(mUi, explanation, Toast.LENGTH_LONG).show();
298 } 313 }
299 314
300 // Share our findings with the user. 315 // Share our findings with the user.
301 runOnUiThread(new HostListDisplayer(mUi)); 316 runOnUiThread(new Runnable() {
317 @Override
318 public void run() {
319 updateUi();
320 }
321 });
302 } 322 }
303 } 323 }
304 324
305 /** Formats the host list and offers it to the user. */ 325 /**
306 private class HostListDisplayer implements Runnable { 326 * Updates the infotext and host list display.
307 /** Communication with the screen. */ 327 * This method affects the UI and must be run on the main thread.
308 private Activity mUi; 328 */
309 329 private void updateUi() {
310 /** Constructor. */ 330 synchronized (mLock) {
311 public HostListDisplayer(Activity ui) { 331 mRefreshButton.setEnabled(mAccount != null);
312 mUi = ui; 332 if (mAccount != null) {
333 mAccountSwitcher.setTitle(mAccount.name);
334 }
313 } 335 }
314 336
315 /** 337 if (mHosts == null) {
316 * Updates the infotext and host list display. 338 mGreeting.setText(getString(R.string.inst_empty_list));
317 * This method affects the UI and must be run on its same thread. 339 mList.setAdapter(null);
318 */ 340 return;
319 @Override
320 public void run() {
321 synchronized (mUi) {
322 mRefreshButton.setEnabled(mAccount != null);
323 if (mAccount != null) {
324 mAccountSwitcher.setTitle(mAccount.name);
325 }
326 }
327
328 if (mHosts == null) {
329 mGreeting.setText(getString(R.string.inst_empty_list));
330 mList.setAdapter(null);
331 return;
332 }
333
334 mGreeting.setText(getString(R.string.inst_host_list));
335
336 ArrayAdapter<JSONObject> displayer = new HostListAdapter(mUi, R.layo ut.host);
337 Log.i("hostlist", "About to populate host list display");
338 try {
339 int index = 0;
340 while (!mHosts.isNull(index)) {
341 displayer.add(mHosts.getJSONObject(index));
342 ++index;
343 }
344 mList.setAdapter(displayer);
345 }
346 catch(JSONException ex) {
347 Log.w("hostlist", ex);
348 Toast.makeText(
349 mUi, getString(R.string.error_cataloging_hosts), Toast.L ENGTH_LONG).show();
350
351 // Close the application.
352 finish();
353 }
354 }
355 }
356
357 /** Describes the appearance and behavior of each host list entry. */
358 private class HostListAdapter extends ArrayAdapter<JSONObject> {
359 /** Constructor. */
360 public HostListAdapter(Context context, int textViewResourceId) {
361 super(context, textViewResourceId);
362 } 341 }
363 342
364 /** Generates a View corresponding to this particular host. */ 343 mGreeting.setText(getString(R.string.inst_host_list));
365 @Override
366 public View getView(int position, View convertView, ViewGroup parent) {
367 TextView target = (TextView)super.getView(position, convertView, par ent);
368 344
369 try { 345 ArrayAdapter<JSONObject> displayer = new HostListAdapter(this, R.layout. host);
370 final JSONObject host = getItem(position); 346 Log.i("hostlist", "About to populate host list display");
371 target.setText(Html.fromHtml(host.getString("hostName") + " (<fo nt color = \"" + 347 try {
372 (host.getString("status").equals("ONLINE") ? HOST_COLOR_ ONLINE : 348 int index = 0;
373 HOST_COLOR_OFFLINE) + "\">" + host.getString("status") + "</font>)")); 349 while (!mHosts.isNull(index)) {
350 displayer.add(mHosts.getJSONObject(index));
351 ++index;
352 }
353 mList.setAdapter(displayer);
354 } catch (JSONException ex) {
355 Log.w("hostlist", ex);
356 Toast.makeText(this, getString(R.string.error_cataloging_hosts),
357 Toast.LENGTH_LONG).show();
374 358
375 if (host.getString("status").equals("ONLINE")) { // Host is onl ine. 359 // Close the application.
376 target.setOnClickListener(new View.OnClickListener() { 360 finish();
377 @Override
378 public void onClick(View v) {
379 try {
380 synchronized (getContext()) {
381 JniInterface.connectToHost(mAccount.name , mToken,
382 host.getString("jabberId"),
383 host.getString("hostId"),
384 host.getString("publicKey"),
385 new Runnable() {
386 @Override
387 public void run() {
388 startActivity(
389 new Intent(getContext(), Desktop.class));
390 }
391 });
392 }
393 }
394 catch(JSONException ex) {
395 Log.w("host", ex);
396 Toast.makeText(getContext(),
397 getString(R.string.error_reading_hos t),
398 Toast.LENGTH_LONG).show();
399
400 // Close the application.
401 finish();
402 }
403 }
404 });
405 } else { // Host is offline.
406 // Disallow interaction with this entry.
407 target.setEnabled(false);
408 }
409 }
410 catch(JSONException ex) {
411 Log.w("hostlist", ex);
412 Toast.makeText(getContext(),
413 getString(R.string.error_displaying_host),
414 Toast.LENGTH_LONG).show();
415
416 // Close the application.
417 finish();
418 }
419
420 return target;
421 } 361 }
422 } 362 }
423 } 363 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698