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