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

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: Add TODO 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
« no previous file with comments | « no previous file | remoting/android/java/src/org/chromium/chromoting/HostListAdapter.java » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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();
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 109 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> {
213 // TODO(lambroslambrou): Refactor this class to provide async interface usable on the UI
214 // thread.
215
197 /** Whether authentication has already been attempted. */ 216 /** Whether authentication has already been attempted. */
198 private boolean mAlreadyTried; 217 private boolean mAlreadyTried;
199 218
200 /** Communication with the screen. */ 219 /** Communication with the screen. */
201 private Activity mUi; 220 private Activity mUi;
202 221
203 /** Constructor. */ 222 /** Constructor. */
204 public HostListDirectoryGrabber(Activity ui) { 223 public HostListDirectoryGrabber(Activity ui) {
205 mAlreadyTried = false; 224 mAlreadyTried = false;
206 mUi = ui; 225 mUi = ui;
207 } 226 }
208 227
209 /** 228 /**
210 * Retrieves the host list from the directory server. This method perfor ms 229 * Retrieves the host list from the directory server. This method perfor ms
211 * network operations and must be run an a non-UI thread. 230 * network operations and must be run an a non-UI thread.
212 */ 231 */
213 @Override 232 @Override
214 public void run(AccountManagerFuture<Bundle> future) { 233 public void run(AccountManagerFuture<Bundle> future) {
215 Log.i("auth", "User finished with auth dialogs"); 234 Log.i("auth", "User finished with auth dialogs");
216 try { 235 try {
217 // Here comes our auth token from the Android system. 236 // Here comes our auth token from the Android system.
218 Bundle result = future.getResult(); 237 Bundle result = future.getResult();
219 String accountName = result.getString(AccountManager.KEY_ACCOUNT _NAME); 238 String accountName = result.getString(AccountManager.KEY_ACCOUNT _NAME);
220 String accountType = result.getString(AccountManager.KEY_ACCOUNT _TYPE); 239 String accountType = result.getString(AccountManager.KEY_ACCOUNT _TYPE);
221 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN ); 240 String authToken = result.getString(AccountManager.KEY_AUTHTOKEN );
222 Log.i("auth", "Received an auth token from system"); 241 Log.i("auth", "Received an auth token from system");
223 242
224 synchronized (mUi) { 243 synchronized (mLock) {
225 mAccount = new Account(accountName, accountType); 244 mAccount = new Account(accountName, accountType);
226 mToken = authToken; 245 mToken = authToken;
227 getPreferences(MODE_PRIVATE).edit().putString("account_name" , accountName). 246 getPreferences(MODE_PRIVATE).edit().putString("account_name" , accountName).
228 putString("account_type", accountType).apply(); 247 putString("account_type", accountType).apply();
229 } 248 }
230 249
231 // Send our HTTP request to the directory server. 250 // Send our HTTP request to the directory server.
232 URLConnection link = 251 URLConnection link =
233 new URL(HOST_LIST_PATH + JniInterface.nativeGetApiKey()) .openConnection(); 252 new URL(HOST_LIST_PATH + JniInterface.nativeGetApiKey()) .openConnection();
234 link.addRequestProperty("client_id", JniInterface.nativeGetClien tId()); 253 link.addRequestProperty("client_id", JniInterface.nativeGetClien tId());
(...skipping 20 matching lines...) Expand all
255 // Assemble error message to display to the user. 274 // Assemble error message to display to the user.
256 String explanation = getString(R.string.error_unknown); 275 String explanation = getString(R.string.error_unknown);
257 if (ex instanceof OperationCanceledException) { 276 if (ex instanceof OperationCanceledException) {
258 explanation = getString(R.string.error_auth_canceled); 277 explanation = getString(R.string.error_auth_canceled);
259 } else if (ex instanceof AuthenticatorException) { 278 } else if (ex instanceof AuthenticatorException) {
260 explanation = getString(R.string.error_no_accounts); 279 explanation = getString(R.string.error_no_accounts);
261 } else if (ex instanceof IOException) { 280 } else if (ex instanceof IOException) {
262 if (!mAlreadyTried) { 281 if (!mAlreadyTried) {
263 // This was our first connection attempt. 282 // This was our first connection attempt.
264 283
265 synchronized (mUi) { 284 synchronized (mLock) {
266 if (mAccount != null) { 285 if (mAccount != null) {
267 // We got an account, but couldn't log into it. We'll retry in case 286 // 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. 287 // the system's cached authentication token had already expired.
269 AccountManager authenticator = AccountManager.ge t(mUi); 288 AccountManager authenticator = AccountManager.ge t(mUi);
270 mAlreadyTried = true; 289 mAlreadyTried = true;
271 290
272 Log.w("auth", "Requesting renewal of rejected au th token"); 291 Log.w("auth", "Requesting renewal of rejected au th token");
273 authenticator.invalidateAuthToken(mAccount.type, mToken); 292 authenticator.invalidateAuthToken(mAccount.type, mToken);
274 mToken = null; 293 mToken = null;
275 authenticator.getAuthToken( 294 authenticator.getAuthToken(
276 mAccount, TOKEN_SCOPE, null, mUi, this, mNetwork); 295 mAccount, TOKEN_SCOPE, null, mUi, this, mNetwork);
277 296
278 // We're not in an error state *yet*. 297 // We're not in an error state *yet*.
279 return; 298 return;
280 } 299 }
281 } 300 }
282 301
283 // We didn't even get an account, so the auth server is likely unreachable. 302 // We didn't even get an account, so the auth server is likely unreachable.
284 explanation = getString(R.string.error_bad_connection); 303 explanation = getString(R.string.error_bad_connection);
285 } else { 304 } else {
286 // Authentication truly failed. 305 // Authentication truly failed.
287 Log.e("auth", "Fresh auth token was also rejected"); 306 Log.e("auth", "Fresh auth token was also rejected");
288 explanation = getString(R.string.error_auth_failed); 307 explanation = getString(R.string.error_auth_failed);
289 } 308 }
290 } else if (ex instanceof JSONException) { 309 } else if (ex instanceof JSONException) {
291 explanation = getString(R.string.error_unexpected_response); 310 explanation = getString(R.string.error_unexpected_response);
292 runOnUiThread(new HostListDisplayer(mUi));
293 } 311 }
294 312
295 mHosts = null; 313 mHosts = null;
296 Log.w("auth", ex); 314 Log.w("auth", ex);
297 Toast.makeText(mUi, explanation, Toast.LENGTH_LONG).show(); 315 Toast.makeText(mUi, explanation, Toast.LENGTH_LONG).show();
298 } 316 }
299 317
300 // Share our findings with the user. 318 // Share our findings with the user.
301 runOnUiThread(new HostListDisplayer(mUi)); 319 runOnUiThread(new Runnable() {
320 @Override
321 public void run() {
322 updateUi();
323 }
324 });
302 } 325 }
303 } 326 }
304 327
305 /** Formats the host list and offers it to the user. */ 328 /**
306 private class HostListDisplayer implements Runnable { 329 * Updates the infotext and host list display.
307 /** Communication with the screen. */ 330 * This method affects the UI and must be run on the main thread.
308 private Activity mUi; 331 */
309 332 private void updateUi() {
310 /** Constructor. */ 333 synchronized (mLock) {
311 public HostListDisplayer(Activity ui) { 334 mRefreshButton.setEnabled(mAccount != null);
312 mUi = ui; 335 if (mAccount != null) {
336 mAccountSwitcher.setTitle(mAccount.name);
337 }
313 } 338 }
314 339
315 /** 340 if (mHosts == null) {
316 * Updates the infotext and host list display. 341 mGreeting.setText(getString(R.string.inst_empty_list));
317 * This method affects the UI and must be run on its same thread. 342 mList.setAdapter(null);
318 */ 343 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 } 344 }
363 345
364 /** Generates a View corresponding to this particular host. */ 346 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 347
369 try { 348 ArrayAdapter<JSONObject> displayer = new HostListAdapter(this, R.layout. host);
370 final JSONObject host = getItem(position); 349 Log.i("hostlist", "About to populate host list display");
371 target.setText(Html.fromHtml(host.getString("hostName") + " (<fo nt color = \"" + 350 try {
372 (host.getString("status").equals("ONLINE") ? HOST_COLOR_ ONLINE : 351 int index = 0;
373 HOST_COLOR_OFFLINE) + "\">" + host.getString("status") + "</font>)")); 352 while (!mHosts.isNull(index)) {
353 displayer.add(mHosts.getJSONObject(index));
354 ++index;
355 }
356 mList.setAdapter(displayer);
357 } catch (JSONException ex) {
358 Log.w("hostlist", ex);
359 Toast.makeText(this, getString(R.string.error_cataloging_hosts),
360 Toast.LENGTH_LONG).show();
374 361
375 if (host.getString("status").equals("ONLINE")) { // Host is onl ine. 362 // Close the application.
376 target.setOnClickListener(new View.OnClickListener() { 363 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 } 364 }
422 } 365 }
423 } 366 }
OLDNEW
« no previous file with comments | « no previous file | remoting/android/java/src/org/chromium/chromoting/HostListAdapter.java » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698