mobile/android/base/sync/setup/activities/SetupSyncActivity.java

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4
michael@0 5 package org.mozilla.gecko.sync.setup.activities;
michael@0 6
michael@0 7 import java.io.UnsupportedEncodingException;
michael@0 8 import java.util.HashMap;
michael@0 9
michael@0 10 import org.json.simple.JSONObject;
michael@0 11 import org.mozilla.gecko.R;
michael@0 12 import org.mozilla.gecko.background.common.log.Logger;
michael@0 13 import org.mozilla.gecko.sync.SyncConstants;
michael@0 14 import org.mozilla.gecko.sync.ThreadPool;
michael@0 15 import org.mozilla.gecko.sync.Utils;
michael@0 16 import org.mozilla.gecko.sync.jpake.JPakeClient;
michael@0 17 import org.mozilla.gecko.sync.jpake.JPakeNoActivePairingException;
michael@0 18 import org.mozilla.gecko.sync.setup.Constants;
michael@0 19 import org.mozilla.gecko.sync.setup.SyncAccounts;
michael@0 20 import org.mozilla.gecko.sync.setup.SyncAccounts.SyncAccountParameters;
michael@0 21
michael@0 22 import android.accounts.Account;
michael@0 23 import android.accounts.AccountAuthenticatorActivity;
michael@0 24 import android.accounts.AccountManager;
michael@0 25 import android.app.Activity;
michael@0 26 import android.content.Context;
michael@0 27 import android.content.Intent;
michael@0 28 import android.net.ConnectivityManager;
michael@0 29 import android.net.NetworkInfo;
michael@0 30 import android.net.Uri;
michael@0 31 import android.os.Bundle;
michael@0 32 import android.text.Editable;
michael@0 33 import android.text.TextWatcher;
michael@0 34 import android.view.View;
michael@0 35 import android.view.Window;
michael@0 36 import android.view.WindowManager;
michael@0 37 import android.widget.Button;
michael@0 38 import android.widget.EditText;
michael@0 39 import android.widget.LinearLayout;
michael@0 40 import android.widget.TextView;
michael@0 41 import android.widget.Toast;
michael@0 42
michael@0 43 public class SetupSyncActivity extends AccountAuthenticatorActivity {
michael@0 44 private final static String LOG_TAG = "SetupSync";
michael@0 45
michael@0 46 private boolean pairWithPin = false;
michael@0 47
michael@0 48 // UI elements for pairing through PIN entry.
michael@0 49 private EditText row1;
michael@0 50 private EditText row2;
michael@0 51 private EditText row3;
michael@0 52 private Button connectButton;
michael@0 53 private LinearLayout pinError;
michael@0 54
michael@0 55 // UI elements for pairing through PIN generation.
michael@0 56 private TextView pinTextView1;
michael@0 57 private TextView pinTextView2;
michael@0 58 private TextView pinTextView3;
michael@0 59 private JPakeClient jClient;
michael@0 60
michael@0 61 // Android context.
michael@0 62 private AccountManager mAccountManager;
michael@0 63 private Context mContext;
michael@0 64
michael@0 65 public SetupSyncActivity() {
michael@0 66 super();
michael@0 67 }
michael@0 68
michael@0 69 /** Called when the activity is first created. */
michael@0 70 @Override
michael@0 71 public void onCreate(Bundle savedInstanceState) {
michael@0 72 ActivityUtils.prepareLogging();
michael@0 73 Logger.info(LOG_TAG, "Called SetupSyncActivity.onCreate.");
michael@0 74 super.onCreate(savedInstanceState);
michael@0 75
michael@0 76 // Set Activity variables.
michael@0 77 mContext = getApplicationContext();
michael@0 78 Logger.debug(LOG_TAG, "AccountManager.get(" + mContext + ")");
michael@0 79 mAccountManager = AccountManager.get(mContext);
michael@0 80
michael@0 81 // Set "screen on" flag for this activity. Screen will not automatically dim as long as this
michael@0 82 // activity is at the top of the stack.
michael@0 83 // Attempting to set this flag more than once causes hanging, so we set it here, not in onResume().
michael@0 84 Window w = getWindow();
michael@0 85 w.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
michael@0 86 Logger.debug(LOG_TAG, "Successfully set screen-on flag.");
michael@0 87 }
michael@0 88
michael@0 89 @Override
michael@0 90 public void onResume() {
michael@0 91 ActivityUtils.prepareLogging();
michael@0 92 Logger.info(LOG_TAG, "Called SetupSyncActivity.onResume.");
michael@0 93 super.onResume();
michael@0 94
michael@0 95 if (!hasInternet()) {
michael@0 96 runOnUiThread(new Runnable() {
michael@0 97 @Override
michael@0 98 public void run() {
michael@0 99 setContentView(R.layout.sync_setup_nointernet);
michael@0 100 }
michael@0 101 });
michael@0 102 return;
michael@0 103 }
michael@0 104
michael@0 105 // Check whether Sync accounts exist; if not, display J-PAKE PIN.
michael@0 106 // Run this on a separate thread to comply with Strict Mode thread policies.
michael@0 107 ThreadPool.run(new Runnable() {
michael@0 108 @Override
michael@0 109 public void run() {
michael@0 110 ActivityUtils.prepareLogging();
michael@0 111 Account[] accts = mAccountManager.getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 112 finishResume(accts);
michael@0 113 }
michael@0 114 });
michael@0 115 }
michael@0 116
michael@0 117 public void finishResume(Account[] accts) {
michael@0 118 Logger.debug(LOG_TAG, "Finishing Resume after fetching accounts.");
michael@0 119
michael@0 120 if (accts.length == 0) { // Start J-PAKE for pairing if no accounts present.
michael@0 121 Logger.debug(LOG_TAG, "No accounts; starting J-PAKE receiver.");
michael@0 122 displayReceiveNoPin();
michael@0 123 if (jClient != null) {
michael@0 124 // Mark previous J-PAKE as finished. Don't bother propagating back up to this Activity.
michael@0 125 jClient.finished = true;
michael@0 126 }
michael@0 127 jClient = new JPakeClient(this);
michael@0 128 jClient.receiveNoPin();
michael@0 129 return;
michael@0 130 }
michael@0 131
michael@0 132 // Set layout based on starting Intent.
michael@0 133 Bundle extras = this.getIntent().getExtras();
michael@0 134 if (extras != null) {
michael@0 135 Logger.debug(LOG_TAG, "SetupSync with extras.");
michael@0 136 boolean isSetup = extras.getBoolean(Constants.INTENT_EXTRA_IS_SETUP);
michael@0 137 if (!isSetup) {
michael@0 138 Logger.debug(LOG_TAG, "Account exists; Pair a Device started.");
michael@0 139 pairWithPin = true;
michael@0 140 displayPairWithPin();
michael@0 141 return;
michael@0 142 }
michael@0 143 }
michael@0 144
michael@0 145 runOnUiThread(new Runnable() {
michael@0 146 @Override
michael@0 147 public void run() {
michael@0 148 Logger.debug(LOG_TAG, "Only one account supported. Redirecting.");
michael@0 149 // Display toast for "Only one account supported."
michael@0 150 // Redirect to account management.
michael@0 151 Toast toast = Toast.makeText(mContext,
michael@0 152 R.string.sync_notification_oneaccount, Toast.LENGTH_LONG);
michael@0 153 toast.show();
michael@0 154
michael@0 155 // Setting up Sync when an existing account exists only happens from Settings,
michael@0 156 // so we can safely finish() the activity to return to Settings.
michael@0 157 finish();
michael@0 158 }
michael@0 159 });
michael@0 160 }
michael@0 161
michael@0 162
michael@0 163 @Override
michael@0 164 public void onPause() {
michael@0 165 super.onPause();
michael@0 166
michael@0 167 if (jClient != null) {
michael@0 168 jClient.abort(Constants.JPAKE_ERROR_USERABORT);
michael@0 169 }
michael@0 170 if (pairWithPin) {
michael@0 171 finish();
michael@0 172 }
michael@0 173 }
michael@0 174
michael@0 175 @Override
michael@0 176 public void onNewIntent(Intent intent) {
michael@0 177 Logger.debug(LOG_TAG, "Started SetupSyncActivity with new intent.");
michael@0 178 setIntent(intent);
michael@0 179 }
michael@0 180
michael@0 181 @Override
michael@0 182 public void onDestroy() {
michael@0 183 Logger.debug(LOG_TAG, "onDestroy() called.");
michael@0 184 super.onDestroy();
michael@0 185 }
michael@0 186
michael@0 187 /* Click Handlers */
michael@0 188 public void manualClickHandler(View target) {
michael@0 189 Intent accountIntent = new Intent(this, AccountActivity.class);
michael@0 190 accountIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
michael@0 191 startActivityForResult(accountIntent, 0);
michael@0 192 overridePendingTransition(0, 0);
michael@0 193 }
michael@0 194
michael@0 195 public void cancelClickHandler(View target) {
michael@0 196 finish();
michael@0 197 }
michael@0 198
michael@0 199 public void connectClickHandler(View target) {
michael@0 200 Logger.debug(LOG_TAG, "Connect clicked.");
michael@0 201 // Set UI feedback.
michael@0 202 pinError.setVisibility(View.INVISIBLE);
michael@0 203 enablePinEntry(false);
michael@0 204 connectButton.requestFocus();
michael@0 205 activateButton(connectButton, false);
michael@0 206
michael@0 207 // Extract PIN.
michael@0 208 String pin = row1.getText().toString();
michael@0 209 pin += row2.getText().toString() + row3.getText().toString();
michael@0 210
michael@0 211 // Start J-PAKE.
michael@0 212 if (jClient != null) {
michael@0 213 // Cancel previous J-PAKE exchange.
michael@0 214 jClient.finished = true;
michael@0 215 }
michael@0 216 jClient = new JPakeClient(this);
michael@0 217 jClient.pairWithPin(pin);
michael@0 218 }
michael@0 219
michael@0 220 /**
michael@0 221 * Handler when "Show me how" link is clicked.
michael@0 222 * @param target
michael@0 223 * View that received the click.
michael@0 224 */
michael@0 225 public void showClickHandler(View target) {
michael@0 226 Uri uri = null;
michael@0 227 // TODO: fetch these from fennec
michael@0 228 if (pairWithPin) {
michael@0 229 uri = Uri.parse(Constants.LINK_FIND_CODE);
michael@0 230 } else {
michael@0 231 uri = Uri.parse(Constants.LINK_FIND_ADD_DEVICE);
michael@0 232 }
michael@0 233 Intent intent = new Intent(this, WebViewActivity.class);
michael@0 234 intent.setData(uri);
michael@0 235 startActivity(intent);
michael@0 236 }
michael@0 237
michael@0 238 /* Controller methods */
michael@0 239
michael@0 240 /**
michael@0 241 * Display generated PIN to user.
michael@0 242 * @param pin
michael@0 243 * 12-character string generated for J-PAKE.
michael@0 244 */
michael@0 245 public void displayPin(String pin) {
michael@0 246 if (pin == null) {
michael@0 247 Logger.warn(LOG_TAG, "Asked to display null pin.");
michael@0 248 return;
michael@0 249 }
michael@0 250 // Format PIN for display.
michael@0 251 int charPerLine = pin.length() / 3;
michael@0 252 final String pin1 = pin.substring(0, charPerLine);
michael@0 253 final String pin2 = pin.substring(charPerLine, 2 * charPerLine);
michael@0 254 final String pin3 = pin.substring(2 * charPerLine, pin.length());
michael@0 255
michael@0 256 runOnUiThread(new Runnable() {
michael@0 257 @Override
michael@0 258 public void run() {
michael@0 259 TextView view1 = pinTextView1;
michael@0 260 TextView view2 = pinTextView2;
michael@0 261 TextView view3 = pinTextView3;
michael@0 262 if (view1 == null || view2 == null || view3 == null) {
michael@0 263 Logger.warn(LOG_TAG, "Couldn't find view to display PIN.");
michael@0 264 return;
michael@0 265 }
michael@0 266 view1.setText(pin1);
michael@0 267 view1.setContentDescription(pin1.replaceAll("\\B", ", "));
michael@0 268
michael@0 269 view2.setText(pin2);
michael@0 270 view2.setContentDescription(pin2.replaceAll("\\B", ", "));
michael@0 271
michael@0 272 view3.setText(pin3);
michael@0 273 view3.setContentDescription(pin3.replaceAll("\\B", ", "));
michael@0 274 }
michael@0 275 });
michael@0 276 }
michael@0 277
michael@0 278 /**
michael@0 279 * Abort current J-PAKE pairing. Clear forms/restart pairing.
michael@0 280 * @param error
michael@0 281 */
michael@0 282 public void displayAbort(String error) {
michael@0 283 if (!Constants.JPAKE_ERROR_USERABORT.equals(error) && !hasInternet()) {
michael@0 284 runOnUiThread(new Runnable() {
michael@0 285 @Override
michael@0 286 public void run() {
michael@0 287 setContentView(R.layout.sync_setup_nointernet);
michael@0 288 }
michael@0 289 });
michael@0 290 return;
michael@0 291 }
michael@0 292 if (pairWithPin) {
michael@0 293 // Clear PIN entries and display error.
michael@0 294 runOnUiThread(new Runnable() {
michael@0 295 @Override
michael@0 296 public void run() {
michael@0 297 enablePinEntry(true);
michael@0 298 row1.setText("");
michael@0 299 row2.setText("");
michael@0 300 row3.setText("");
michael@0 301 row1.requestFocus();
michael@0 302
michael@0 303 // Display error.
michael@0 304 pinError.setVisibility(View.VISIBLE);
michael@0 305 }
michael@0 306 });
michael@0 307 return;
michael@0 308 }
michael@0 309
michael@0 310 // Start new JPakeClient for restarting J-PAKE.
michael@0 311 Logger.debug(LOG_TAG, "abort reason: " + error);
michael@0 312 if (!Constants.JPAKE_ERROR_USERABORT.equals(error)) {
michael@0 313 jClient = new JPakeClient(this);
michael@0 314 runOnUiThread(new Runnable() {
michael@0 315 @Override
michael@0 316 public void run() {
michael@0 317 displayReceiveNoPin();
michael@0 318 jClient.receiveNoPin();
michael@0 319 }
michael@0 320 });
michael@0 321 }
michael@0 322 }
michael@0 323
michael@0 324 @SuppressWarnings({ "unchecked", "static-method" })
michael@0 325 protected JSONObject makeAccountJSON(String username, String password,
michael@0 326 String syncKey, String serverURL) {
michael@0 327
michael@0 328 JSONObject jAccount = new JSONObject();
michael@0 329
michael@0 330 // Hack to try to keep Java 1.7 from complaining about unchecked types,
michael@0 331 // despite the presence of SuppressWarnings.
michael@0 332 HashMap<String, String> fields = (HashMap<String, String>) jAccount;
michael@0 333
michael@0 334 fields.put(Constants.JSON_KEY_SYNCKEY, syncKey);
michael@0 335 fields.put(Constants.JSON_KEY_ACCOUNT, username);
michael@0 336 fields.put(Constants.JSON_KEY_PASSWORD, password);
michael@0 337 fields.put(Constants.JSON_KEY_SERVER, serverURL);
michael@0 338
michael@0 339 if (Logger.LOG_PERSONAL_INFORMATION) {
michael@0 340 Logger.pii(LOG_TAG, "Extracted account data: " + jAccount.toJSONString());
michael@0 341 }
michael@0 342 return jAccount;
michael@0 343 }
michael@0 344
michael@0 345 /**
michael@0 346 * Device has finished key exchange, waiting for remote device to set up or
michael@0 347 * link to a Sync account. Display "waiting for other device" dialog.
michael@0 348 */
michael@0 349 public void onPaired() {
michael@0 350 // Extract Sync account data.
michael@0 351 Account[] accts = mAccountManager.getAccountsByType(SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 352 if (accts.length == 0) {
michael@0 353 // Error, no account present.
michael@0 354 Logger.error(LOG_TAG, "No accounts present.");
michael@0 355 displayAbort(Constants.JPAKE_ERROR_INVALID);
michael@0 356 return;
michael@0 357 }
michael@0 358
michael@0 359 // TODO: Single account supported. Create account selection if spec changes.
michael@0 360 Account account = accts[0];
michael@0 361 String username = account.name;
michael@0 362 String password = mAccountManager.getPassword(account);
michael@0 363 String syncKey = mAccountManager.getUserData(account, Constants.OPTION_SYNCKEY);
michael@0 364 String serverURL = mAccountManager.getUserData(account, Constants.OPTION_SERVER);
michael@0 365
michael@0 366 JSONObject jAccount = makeAccountJSON(username, password, syncKey, serverURL);
michael@0 367 try {
michael@0 368 jClient.sendAndComplete(jAccount);
michael@0 369 } catch (JPakeNoActivePairingException e) {
michael@0 370 Logger.error(LOG_TAG, "No active J-PAKE pairing.", e);
michael@0 371 displayAbort(Constants.JPAKE_ERROR_INVALID);
michael@0 372 }
michael@0 373 }
michael@0 374
michael@0 375 /**
michael@0 376 * J-PAKE pairing has started, but when this device has generated the PIN for
michael@0 377 * pairing, does not require UI feedback to user.
michael@0 378 */
michael@0 379 public void onPairingStart() {
michael@0 380 if (!pairWithPin) {
michael@0 381 runOnUiThread(new Runnable() {
michael@0 382 @Override
michael@0 383 public void run() {
michael@0 384 setContentView(R.layout.sync_setup_jpake_waiting);
michael@0 385 }
michael@0 386 });
michael@0 387 return;
michael@0 388 }
michael@0 389 }
michael@0 390
michael@0 391 /**
michael@0 392 * On J-PAKE completion, store the Sync Account credentials sent by other
michael@0 393 * device. Display progress to user.
michael@0 394 *
michael@0 395 * @param jCreds
michael@0 396 */
michael@0 397 public void onComplete(JSONObject jCreds) {
michael@0 398 if (!pairWithPin) {
michael@0 399 // Create account from received credentials.
michael@0 400 String accountName = (String) jCreds.get(Constants.JSON_KEY_ACCOUNT);
michael@0 401 String password = (String) jCreds.get(Constants.JSON_KEY_PASSWORD);
michael@0 402 String syncKey = (String) jCreds.get(Constants.JSON_KEY_SYNCKEY);
michael@0 403 String serverURL = (String) jCreds.get(Constants.JSON_KEY_SERVER);
michael@0 404
michael@0 405 // The password we get is double-encoded.
michael@0 406 try {
michael@0 407 password = Utils.decodeUTF8(password);
michael@0 408 } catch (UnsupportedEncodingException e) {
michael@0 409 Logger.warn(LOG_TAG, "Unsupported encoding when decoding UTF-8 ASCII J-PAKE message. Ignoring.");
michael@0 410 }
michael@0 411
michael@0 412 final SyncAccountParameters syncAccount = new SyncAccountParameters(mContext, mAccountManager, accountName,
michael@0 413 syncKey, password, serverURL);
michael@0 414 createAccountOnThread(syncAccount);
michael@0 415 } else {
michael@0 416 // No need to create an account; just clean up.
michael@0 417 displayResultAndFinish(true);
michael@0 418 }
michael@0 419 }
michael@0 420
michael@0 421 private void displayResultAndFinish(final boolean isSuccess) {
michael@0 422 jClient = null;
michael@0 423 runOnUiThread(new Runnable() {
michael@0 424 @Override
michael@0 425 public void run() {
michael@0 426 int result = isSuccess ? RESULT_OK : RESULT_CANCELED;
michael@0 427 setResult(result);
michael@0 428 displayResult(isSuccess);
michael@0 429 }
michael@0 430 });
michael@0 431 }
michael@0 432
michael@0 433 private void createAccountOnThread(final SyncAccountParameters syncAccount) {
michael@0 434 ThreadPool.run(new Runnable() {
michael@0 435 @Override
michael@0 436 public void run() {
michael@0 437 Account account = SyncAccounts.createSyncAccount(syncAccount);
michael@0 438 boolean isSuccess = (account != null);
michael@0 439 if (isSuccess) {
michael@0 440 Bundle resultBundle = new Bundle();
michael@0 441 resultBundle.putString(AccountManager.KEY_ACCOUNT_NAME, syncAccount.username);
michael@0 442 resultBundle.putString(AccountManager.KEY_ACCOUNT_TYPE, SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 443 resultBundle.putString(AccountManager.KEY_AUTHTOKEN, SyncConstants.ACCOUNTTYPE_SYNC);
michael@0 444 setAccountAuthenticatorResult(resultBundle);
michael@0 445 }
michael@0 446 displayResultAndFinish(isSuccess);
michael@0 447 }
michael@0 448 });
michael@0 449 }
michael@0 450
michael@0 451 /*
michael@0 452 * Helper functions
michael@0 453 */
michael@0 454 private void activateButton(Button button, boolean toActivate) {
michael@0 455 button.setEnabled(toActivate);
michael@0 456 button.setClickable(toActivate);
michael@0 457 }
michael@0 458
michael@0 459 private void enablePinEntry(boolean toEnable) {
michael@0 460 row1.setEnabled(toEnable);
michael@0 461 row2.setEnabled(toEnable);
michael@0 462 row3.setEnabled(toEnable);
michael@0 463 }
michael@0 464
michael@0 465 /**
michael@0 466 * Displays Sync account setup result to user.
michael@0 467 *
michael@0 468 * @param isSetup
michael@0 469 * true if account was set up successfully, false otherwise.
michael@0 470 */
michael@0 471 private void displayResult(boolean isSuccess) {
michael@0 472 Intent intent = null;
michael@0 473 if (isSuccess) {
michael@0 474 intent = new Intent(mContext, SetupSuccessActivity.class);
michael@0 475 intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION);
michael@0 476 intent.putExtra(Constants.INTENT_EXTRA_IS_SETUP, !pairWithPin);
michael@0 477 startActivity(intent);
michael@0 478 finish();
michael@0 479 } else {
michael@0 480 intent = new Intent(mContext, SetupFailureActivity.class);
michael@0 481 intent.putExtra(Constants.INTENT_EXTRA_IS_ACCOUNTERROR, true);
michael@0 482 intent.setFlags(Constants.FLAG_ACTIVITY_REORDER_TO_FRONT_NO_ANIMATION);
michael@0 483 intent.putExtra(Constants.INTENT_EXTRA_IS_SETUP, !pairWithPin);
michael@0 484 startActivity(intent);
michael@0 485 // Do not finish, so user can retry setup by hitting "back."
michael@0 486 }
michael@0 487 }
michael@0 488
michael@0 489 /**
michael@0 490 * Validate PIN entry fields to check if the three PIN entry fields are all
michael@0 491 * filled in.
michael@0 492 *
michael@0 493 * @return true, if all PIN fields have 4 characters, false otherwise
michael@0 494 */
michael@0 495 private boolean pinEntryCompleted() {
michael@0 496 if (row1.length() == 4 &&
michael@0 497 row2.length() == 4 &&
michael@0 498 row3.length() == 4) {
michael@0 499 return true;
michael@0 500 }
michael@0 501 return false;
michael@0 502 }
michael@0 503
michael@0 504 private boolean hasInternet() {
michael@0 505 Logger.debug(LOG_TAG, "Checking internet connectivity.");
michael@0 506 ConnectivityManager connManager = (ConnectivityManager) mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
michael@0 507 NetworkInfo network = connManager.getActiveNetworkInfo();
michael@0 508
michael@0 509 if (network != null && network.isConnected()) {
michael@0 510 Logger.debug(LOG_TAG, network + " is connected.");
michael@0 511 return true;
michael@0 512 }
michael@0 513 Logger.debug(LOG_TAG, "No connected networks.");
michael@0 514 return false;
michael@0 515 }
michael@0 516
michael@0 517 /**
michael@0 518 * Displays layout for entering a PIN from another device.
michael@0 519 * A Sync Account has already been set up.
michael@0 520 */
michael@0 521 private void displayPairWithPin() {
michael@0 522 Logger.debug(LOG_TAG, "PairWithPin initiated.");
michael@0 523 runOnUiThread(new Runnable() {
michael@0 524
michael@0 525 @Override
michael@0 526 public void run() {
michael@0 527 setContentView(R.layout.sync_setup_pair);
michael@0 528 connectButton = (Button) findViewById(R.id.pair_button_connect);
michael@0 529 pinError = (LinearLayout) findViewById(R.id.pair_error);
michael@0 530
michael@0 531 row1 = (EditText) findViewById(R.id.pair_row1);
michael@0 532 row2 = (EditText) findViewById(R.id.pair_row2);
michael@0 533 row3 = (EditText) findViewById(R.id.pair_row3);
michael@0 534
michael@0 535 row1.addTextChangedListener(new TextWatcher() {
michael@0 536 @Override
michael@0 537 public void afterTextChanged(Editable s) {
michael@0 538 activateButton(connectButton, pinEntryCompleted());
michael@0 539 if (s.length() == 4) {
michael@0 540 row2.requestFocus();
michael@0 541 }
michael@0 542 }
michael@0 543
michael@0 544 @Override
michael@0 545 public void beforeTextChanged(CharSequence s, int start, int count,
michael@0 546 int after) {
michael@0 547 }
michael@0 548
michael@0 549 @Override
michael@0 550 public void onTextChanged(CharSequence s, int start, int before, int count) {
michael@0 551 }
michael@0 552
michael@0 553 });
michael@0 554 row2.addTextChangedListener(new TextWatcher() {
michael@0 555 @Override
michael@0 556 public void afterTextChanged(Editable s) {
michael@0 557 activateButton(connectButton, pinEntryCompleted());
michael@0 558 if (s.length() == 4) {
michael@0 559 row3.requestFocus();
michael@0 560 }
michael@0 561 }
michael@0 562
michael@0 563 @Override
michael@0 564 public void beforeTextChanged(CharSequence s, int start, int count,
michael@0 565 int after) {
michael@0 566 }
michael@0 567
michael@0 568 @Override
michael@0 569 public void onTextChanged(CharSequence s, int start, int before, int count) {
michael@0 570 }
michael@0 571
michael@0 572 });
michael@0 573
michael@0 574 row3.addTextChangedListener(new TextWatcher() {
michael@0 575 @Override
michael@0 576 public void afterTextChanged(Editable s) {
michael@0 577 activateButton(connectButton, pinEntryCompleted());
michael@0 578 }
michael@0 579
michael@0 580 @Override
michael@0 581 public void beforeTextChanged(CharSequence s, int start, int count,
michael@0 582 int after) {
michael@0 583 }
michael@0 584
michael@0 585 @Override
michael@0 586 public void onTextChanged(CharSequence s, int start, int before, int count) {
michael@0 587 }
michael@0 588 });
michael@0 589
michael@0 590 row1.requestFocus();
michael@0 591 }
michael@0 592 });
michael@0 593 }
michael@0 594
michael@0 595 /**
michael@0 596 * Displays layout with PIN for pairing with another device.
michael@0 597 * No Sync Account has been set up yet.
michael@0 598 */
michael@0 599 private void displayReceiveNoPin() {
michael@0 600 Logger.debug(LOG_TAG, "ReceiveNoPin initiated");
michael@0 601 runOnUiThread(new Runnable(){
michael@0 602
michael@0 603 @Override
michael@0 604 public void run() {
michael@0 605 setContentView(R.layout.sync_setup);
michael@0 606
michael@0 607 // Set up UI.
michael@0 608 pinTextView1 = ((TextView) findViewById(R.id.text_pin1));
michael@0 609 pinTextView2 = ((TextView) findViewById(R.id.text_pin2));
michael@0 610 pinTextView3 = ((TextView) findViewById(R.id.text_pin3));
michael@0 611 }
michael@0 612 });
michael@0 613 }
michael@0 614
michael@0 615 @Override
michael@0 616 public void onActivityResult(int requestCode, int resultCode, Intent data) {
michael@0 617 switch (resultCode) {
michael@0 618 case Activity.RESULT_OK:
michael@0 619 // Setup completed in manual setup.
michael@0 620 finish();
michael@0 621 }
michael@0 622 }
michael@0 623 }

mercurial