Fixed visibility of authorization details when the device is turned aside; removed...
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / AuthenticatorActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 * Copyright (C) 2012-2013 ownCloud Inc.
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17 *
18 */
19
20 package com.owncloud.android.ui.activity;
21
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import com.owncloud.android.AccountUtils;
26 import com.owncloud.android.authenticator.AccountAuthenticator;
27 import com.owncloud.android.authenticator.oauth2.OAuth2Context;
28 import com.owncloud.android.ui.dialog.SslValidatorDialog;
29 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
30 import com.owncloud.android.utils.OwnCloudVersion;
31 import com.owncloud.android.network.OwnCloudClientUtils;
32 import com.owncloud.android.operations.OwnCloudServerCheckOperation;
33 import com.owncloud.android.operations.ExistenceCheckOperation;
34 import com.owncloud.android.operations.OAuth2GetAccessToken;
35 import com.owncloud.android.operations.OnRemoteOperationListener;
36 import com.owncloud.android.operations.RemoteOperation;
37 import com.owncloud.android.operations.RemoteOperationResult;
38
39 import android.accounts.Account;
40 import android.accounts.AccountAuthenticatorActivity;
41 import android.accounts.AccountManager;
42 import android.app.AlertDialog;
43 import android.app.Dialog;
44 import android.app.ProgressDialog;
45 import android.content.ContentResolver;
46 import android.content.DialogInterface;
47 import android.content.Intent;
48 import android.content.SharedPreferences;
49 import android.net.Uri;
50 import android.os.Bundle;
51 import android.os.Handler;
52 import android.preference.PreferenceManager;
53 import android.text.InputType;
54 import android.util.Log;
55 import android.view.View;
56 import android.view.View.OnFocusChangeListener;
57 import android.view.Window;
58 import android.widget.CheckBox;
59 import android.widget.EditText;
60 import android.widget.Button;
61 import android.widget.ImageView;
62 import android.widget.TextView;
63 import com.owncloud.android.R;
64
65 import eu.alefzero.webdav.WebdavClient;
66
67 /**
68 * This Activity is used to add an ownCloud account to the App
69 *
70 * @author Bartek Przybylski
71 * @author David A. Velasco
72 */
73 public class AuthenticatorActivity extends AccountAuthenticatorActivity
74 implements OnRemoteOperationListener, OnSslValidatorListener, OnFocusChangeListener {
75
76 private static final String TAG = AuthenticatorActivity.class.getSimpleName();
77
78 public static final String EXTRA_ACCOUNT = "ACCOUNT";
79 public static final String EXTRA_USER_NAME = "USER_NAME";
80 public static final String EXTRA_HOST_NAME = "HOST_NAME";
81
82 private static final String KEY_HOST_URL_TEXT = "HOST_URL_TEXT";
83 private static final String KEY_OC_VERSION = "OC_VERSION";
84 private static final String KEY_STATUS_TEXT = "STATUS_TEXT";
85 private static final String KEY_STATUS_ICON = "STATUS_ICON";
86 private static final String KEY_STATUS_CORRECT = "STATUS_CORRECT";
87 private static final String KEY_IS_SSL_CONN = "IS_SSL_CONN";
88 private static final String KEY_OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT";
89 private static final String KEY_OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON";
90
91 private static final int DIALOG_LOGIN_PROGRESS = 0;
92 private static final int DIALOG_SSL_VALIDATOR = 1;
93 private static final int DIALOG_CERT_NOT_SAVED = 2;
94 private static final int DIALOG_OAUTH2_LOGIN_PROGRESS = 3;
95
96
97 private String mHostBaseUrl;
98 private OwnCloudVersion mDiscoveredVersion;
99
100 private int mStatusText, mStatusIcon;
101 private boolean mStatusCorrect, mIsSslConn;
102 private int mOAuth2StatusText, mOAuth2StatusIcon;
103
104 private final Handler mHandler = new Handler();
105 private Thread mOperationThread;
106 private OwnCloudServerCheckOperation mOcServerChkOperation;
107 private ExistenceCheckOperation mAuthCheckOperation;
108 private RemoteOperationResult mLastSslUntrustedServerResult;
109
110 //private Thread mOAuth2GetCodeThread;
111 //private OAuth2GetAuthorizationToken mOAuth2GetCodeRunnable;
112 //private TokenReceiver tokenReceiver;
113 //private JSONObject mCodeResponseJson;
114 private Uri mNewCapturedUriFromOAuth2Redirection;
115
116 private AccountManager mAccountMgr;
117
118 private ImageView mRefreshButton;
119 private ImageView mViewPasswordButton;
120 private EditText mHostUrlInput;
121 private EditText mUsernameInput;
122 private EditText mPasswordInput;
123 private CheckBox mOAuth2Check;
124 private String mOAuthAccessToken;
125 private View mOkButton;
126
127 private TextView mOAuthAuthEndpointText;
128 private TextView mOAuthTokenEndpointText;
129
130
131 /**
132 * {@inheritDoc}
133 *
134 * IMPORTANT ENTRY POINT 1: activity is shown to the user
135 */
136 @Override
137 protected void onCreate(Bundle savedInstanceState) {
138 super.onCreate(savedInstanceState);
139 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
140
141 /// set view and get references to view elements
142 setContentView(R.layout.account_setup);
143 mRefreshButton = (ImageView) findViewById(R.id.refreshButton);
144 mViewPasswordButton = (ImageView) findViewById(R.id.viewPasswordButton);
145 mHostUrlInput = (EditText) findViewById(R.id.hostUrlInput);
146 mUsernameInput = (EditText) findViewById(R.id.account_username);
147 mPasswordInput = (EditText) findViewById(R.id.account_password);
148 mOAuthAuthEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_1);
149 mOAuthTokenEndpointText = (TextView)findViewById(R.id.oAuthEntryPoint_2);
150 mOAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);
151 mOkButton = findViewById(R.id.buttonOK);
152
153 /// complete label for 'register account' button
154 Button b = (Button) findViewById(R.id.account_register);
155 if (b != null) {
156 b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));
157 }
158
159 /// bind view elements to listeners
160 mHostUrlInput.setOnFocusChangeListener(this);
161 mPasswordInput.setOnFocusChangeListener(this);
162
163 /// initialization
164 mAccountMgr = AccountManager.get(this);
165 mNewCapturedUriFromOAuth2Redirection = null; // TODO save?
166
167 if (savedInstanceState == null) {
168 /// connection state and info
169 mStatusText = mStatusIcon = 0;
170 mStatusCorrect = false;
171 mIsSslConn = false;
172
173 /// retrieve extras from intent
174 String tokenType = getIntent().getExtras().getString(AccountAuthenticator.KEY_AUTH_TOKEN_TYPE);
175 boolean oAuthRequired = AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN.equals(tokenType);
176 mOAuth2Check.setChecked(oAuthRequired);
177 changeViewByOAuth2Check(oAuthRequired);
178
179 Account account = getIntent().getExtras().getParcelable(EXTRA_ACCOUNT);
180 if (account != null) {
181 String ocVersion = mAccountMgr.getUserData(account, AccountAuthenticator.KEY_OC_VERSION);
182 if (ocVersion != null) {
183 mDiscoveredVersion = new OwnCloudVersion(ocVersion);
184 }
185 mHostBaseUrl = mAccountMgr.getUserData(account, AccountAuthenticator.KEY_OC_BASE_URL);
186 mHostUrlInput.setText(mHostBaseUrl);
187 String userName = account.name.substring(0, account.name.lastIndexOf('@'));
188 mUsernameInput.setText(userName);
189 }
190
191 } else {
192 loadSavedInstanceState(savedInstanceState);
193 }
194
195 mPasswordInput.setText(""); // clean password to avoid social hacking (disadvantage: password in removed if the device is turned aside)
196 }
197
198
199 /**
200 * Saves relevant state before {@link #onPause()}
201 *
202 * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag, intended to defer the
203 * processing of the redirection caught in {@link #onNewIntent(Intent)} until {@link #onResume()}
204 *
205 * See {@link #loadSavedInstanceState(Bundle)}
206 */
207 @Override
208 protected void onSaveInstanceState(Bundle outState) {
209 super.onSaveInstanceState(outState);
210
211 /// connection state and info
212 outState.putInt(KEY_STATUS_TEXT, mStatusText);
213 outState.putInt(KEY_STATUS_ICON, mStatusIcon);
214 outState.putBoolean(KEY_STATUS_CORRECT, mStatusCorrect);
215 outState.putBoolean(KEY_IS_SSL_CONN, mIsSslConn);
216
217 /// server data
218 if (mDiscoveredVersion != null)
219 outState.putString(KEY_OC_VERSION, mDiscoveredVersion.toString());
220 outState.putString(KEY_HOST_URL_TEXT, mHostBaseUrl);
221
222 // Saving the state of oAuth2 components.
223 outState.putInt(KEY_OAUTH2_STATUS_ICON, mOAuth2StatusIcon);
224 outState.putInt(KEY_OAUTH2_STATUS_TEXT, mOAuth2StatusText);
225
226 /* Leave old OAuth flow
227 if (codeResponseJson != null){
228 outState.putString(KEY_OAUTH2_CODE_RESULT, codeResponseJson.toString());
229 }
230 */
231 }
232
233
234 /**
235 * Loads saved state
236 *
237 * See {@link #onSaveInstanceState(Bundle)}.
238 *
239 * @param savedInstanceState Saved state, as received in {@link #onCreate(Bundle)}.
240 */
241 private void loadSavedInstanceState(Bundle savedInstanceState) {
242 /// connection state and info
243 mStatusCorrect = savedInstanceState.getBoolean(KEY_STATUS_CORRECT);
244 mIsSslConn = savedInstanceState.getBoolean(KEY_IS_SSL_CONN);
245 mStatusText = savedInstanceState.getInt(KEY_STATUS_TEXT);
246 mStatusIcon = savedInstanceState.getInt(KEY_STATUS_ICON);
247 updateOcServerCheckIconAndText();
248
249 /// UI settings depending upon connection
250 mOkButton.setEnabled(mStatusCorrect); // TODO really necessary?
251 if (!mStatusCorrect)
252 mRefreshButton.setVisibility(View.VISIBLE); // seems that setting visibility is necessary
253 else
254 mRefreshButton.setVisibility(View.INVISIBLE);
255
256 /// server data
257 String ocVersion = savedInstanceState.getString(KEY_OC_VERSION);
258 if (ocVersion != null)
259 mDiscoveredVersion = new OwnCloudVersion(ocVersion);
260 mHostBaseUrl = savedInstanceState.getString(KEY_HOST_URL_TEXT);
261
262 // state of oAuth2 components
263 mOAuth2StatusIcon = savedInstanceState.getInt(KEY_OAUTH2_STATUS_ICON);
264 mOAuth2StatusText = savedInstanceState.getInt(KEY_OAUTH2_STATUS_TEXT);
265
266 /* Leave old OAuth flow
267 // We store a JSon object with all the data returned from oAuth2 server when we get user_code.
268 // Is better than store variable by variable. We use String object to serialize from/to it.
269 try {
270 if (savedInstanceState.containsKey(KEY_OAUTH2_CODE_RESULT)) {
271 codeResponseJson = new JSONObject(savedInstanceState.getString(KEY_OAUTH2_CODE_RESULT));
272 }
273 } catch (JSONException e) {
274 Log.e(TAG, "onCreate->JSONException: " + e.toString());
275 }*/
276 // END of getting the state of oAuth2 components.
277
278 }
279
280
281 /**
282 * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION request
283 * is caught here.
284 *
285 * To make this possible, this activity needs to be qualified with android:launchMode = "singleTask" in the
286 * AndroidManifest.xml file.
287 */
288 @Override
289 protected void onNewIntent (Intent intent) {
290 Log.d(TAG, "onNewIntent()");
291 Uri data = intent.getData();
292 if (data != null && data.toString().startsWith(OAuth2Context.MY_REDIRECT_URI)) {
293 mNewCapturedUriFromOAuth2Redirection = data;
294 }
295 }
296
297
298 /**
299 * The redirection triggered by the OAuth authentication server as response to the GET AUTHORIZATION, and
300 * deferred in {@link #onNewIntent(Intent)}, is processed here.
301 */
302 @Override
303 protected void onResume() {
304 super.onResume();
305 changeViewByOAuth2Check(mOAuth2Check.isChecked());
306 // the state of mOAuth2Check is automatically recovered between configuration changes, but not before onCreate() finishes
307
308 /* LEAVE OLD OAUTH FLOW ;
309 // (old oauth code) Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token.
310 if (tokenReceiver == null) {
311 IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE);
312 tokenReceiver = new TokenReceiver();
313 this.registerReceiver(tokenReceiver,tokenFilter);
314 } */
315 // (new oauth code)
316 if (mNewCapturedUriFromOAuth2Redirection != null) {
317 getOAuth2AccessTokenFromCapturedRedirection();
318 }
319 }
320
321
322 @Override protected void onDestroy() {
323 super.onDestroy();
324
325 /* LEAVE OLD OAUTH FLOW
326 // We must stop the service thats it's pooling to oAuth2 server for a token.
327 Intent tokenService = new Intent(this, OAuth2GetTokenService.class);
328 stopService(tokenService);
329
330 // We stop listening the result of the pooling service.
331 if (tokenReceiver != null) {
332 unregisterReceiver(tokenReceiver);
333 tokenReceiver = null;
334 }*/
335
336 }
337
338
339 /**
340 * Parses the redirection with the response to the GET AUTHORIZATION request to the
341 * oAuth server and requests for the access token (GET ACCESS TOKEN)
342 */
343 private void getOAuth2AccessTokenFromCapturedRedirection() {
344 /// Parse data from OAuth redirection
345 Map<String, String> responseValues = new HashMap<String, String>();
346 String queryParameters = mNewCapturedUriFromOAuth2Redirection.getQuery();
347 mNewCapturedUriFromOAuth2Redirection = null;
348 String[] pairs = queryParameters.split("&");
349 int i = 0;
350 String key = "";
351 String value = "";
352 StringBuilder sb = new StringBuilder();
353 while (pairs.length > i) {
354 int j = 0;
355 String[] part = pairs[i].split("=");
356 while (part.length > j) {
357 String p = part[j];
358 if (j == 0) {
359 key = p;
360 sb.append(key + " = ");
361 } else if (j == 1) {
362 value = p;
363 responseValues.put(key, value);
364 sb.append(value + "\n");
365 }
366
367 Log.v(TAG, "[" + i + "," + j + "] = " + p);
368 j++;
369 }
370 i++;
371 }
372
373 /// Updating status widget to OK.
374 updateOAuth2IconAndText(R.drawable.ic_ok, R.string.auth_connection_established);
375
376 /// Showing the dialog with instructions for the user.
377 showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);
378
379 /// GET ACCESS TOKEN to the oAuth server
380 RemoteOperation operation = new OAuth2GetAccessToken(responseValues);
381 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(getString(R.string.oauth_url_endpoint_access)), getApplicationContext());
382 operation.execute(client, this, mHandler);
383 }
384
385
386
387 /**
388 * Handles the change of focus on the text inputs for the server URL and the password
389 */
390 public void onFocusChange(View view, boolean hasFocus) {
391 if (view.getId() == R.id.hostUrlInput) {
392 onUrlInputFocusChanged((TextView) view, hasFocus);
393
394 } else if (view.getId() == R.id.account_password) {
395 onPasswordFocusChanged((TextView) view, hasFocus);
396 }
397 }
398
399
400 /**
401 * Handles changes in focus on the text input for the server URL.
402 *
403 * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to
404 * other field. The operation to check the existence of the server in the entered URL is
405 * started.
406 *
407 * When hasFocus: user 'comes back' to write again the server URL.
408 *
409 * @param hostInput TextView with the URL input field receiving the change of focus.
410 * @param hasFocus 'True' if focus is received, 'false' if is lost
411 */
412 private void onUrlInputFocusChanged(TextView hostInput, boolean hasFocus) {
413 if (!hasFocus) {
414 String uri = hostInput.getText().toString().trim();
415 if (uri.length() != 0) {
416 mStatusText = R.string.auth_testing_connection;
417 mStatusIcon = R.drawable.progress_small;
418 updateOcServerCheckIconAndText();
419 /** TODO cancel previous connection check if the user tries to ammend a wrong URL
420 if(mConnChkOperation != null) {
421 mConnChkOperation.cancel();
422 } */
423 mOcServerChkOperation = new OwnCloudServerCheckOperation(uri, this);
424 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);
425 mHostBaseUrl = "";
426 mDiscoveredVersion = null;
427 mOperationThread = mOcServerChkOperation.execute(client, this, mHandler);
428 } else {
429 mRefreshButton.setVisibility(View.INVISIBLE);
430 mStatusText = 0;
431 mStatusIcon = 0;
432 updateOcServerCheckIconAndText();
433 }
434 } else {
435 // avoids that the 'connect' button can be clicked if the test was previously passed
436 mOkButton.setEnabled(false);
437 }
438 }
439
440
441 /**
442 * Handles changes in focus on the text input for the password (basic authorization).
443 *
444 * When (hasFocus), the button to toggle password visibility is shown.
445 *
446 * When (!hasFocus), the button is made invisible and the password is hidden.
447 *
448 * @param passwordInput TextView with the password input field receiving the change of focus.
449 * @param hasFocus 'True' if focus is received, 'false' if is lost
450 */
451 private void onPasswordFocusChanged(TextView passwordInput, boolean hasFocus) {
452 if (hasFocus) {
453 mViewPasswordButton.setVisibility(View.VISIBLE);
454 } else {
455 int input_type = InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD;
456 passwordInput.setInputType(input_type);
457 mViewPasswordButton.setVisibility(View.INVISIBLE);
458 }
459 }
460
461
462
463 /**
464 * Cancels the authenticator activity
465 *
466 * IMPORTANT ENTRY POINT 3: Never underestimate the importance of cancellation
467 *
468 * This method is bound in the layout/acceoun_setup.xml resource file.
469 *
470 * @param view Cancel button
471 */
472 public void onCancelClick(View view) {
473 setResult(RESULT_CANCELED); // TODO review how is this related to AccountAuthenticator
474 finish();
475 }
476
477
478
479 /**
480 * Checks the credentials of the user in the root of the ownCloud server
481 * before creating a new local account.
482 *
483 * For basic authorization, a check of existence of the root folder is
484 * performed.
485 *
486 * For OAuth, starts the flow to get an access token; the credentials test
487 * is postponed until it is available.
488 *
489 * IMPORTANT ENTRY POINT 4
490 *
491 * @param view OK button
492 */
493 public void onOkClick(View view) {
494 // this check should be unnecessary
495 if (mDiscoveredVersion == null || !mDiscoveredVersion.isVersionValid() || mHostBaseUrl == null || mHostBaseUrl.length() == 0) {
496 mStatusIcon = R.drawable.common_error;
497 mStatusText = R.string.auth_wtf_reenter_URL;
498 updateOcServerCheckIconAndText();
499 mOkButton.setEnabled(false);
500 Log.wtf(TAG, "The user was allowed to click 'connect' to an unchecked server!!");
501 return;
502 }
503
504 if (mOAuth2Check.isChecked()) {
505 startOauthorization();
506
507 } else {
508 checkBasicAuthorization();
509 }
510 }
511
512
513 /**
514 * Tests the credentials entered by the user performing a check of existence on
515 * the root folder of the ownCloud server.
516 */
517 private void checkBasicAuthorization() {
518 /// get the path to the root folder through WebDAV from the version server
519 String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false);
520
521 /// get basic credentials entered by user
522 String username = mUsernameInput.getText().toString();
523 String password = mPasswordInput.getText().toString();
524
525 /// be gentle with the user
526 showDialog(DIALOG_LOGIN_PROGRESS);
527
528 /// test credentials accessing the root folder
529 mAuthCheckOperation = new ExistenceCheckOperation("", this, false);
530 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this);
531 client.setBasicCredentials(username, password);
532 mOperationThread = mAuthCheckOperation.execute(client, this, mHandler);
533 }
534
535
536 /**
537 * Starts the OAuth 'grant type' flow to get an access token, with
538 * a GET AUTHORIZATION request to the BUILT-IN authorization server.
539 */
540 private void startOauthorization() {
541 // be gentle with the user
542 updateOAuth2IconAndText(R.drawable.progress_small, R.string.oauth_login_connection);
543
544 // GET AUTHORIZATION request
545 /*
546 mOAuth2GetCodeRunnable = new OAuth2GetAuthorizationToken(, this);
547 mOAuth2GetCodeRunnable.setListener(this, mHandler);
548 mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable);
549 mOAuth2GetCodeThread.start();
550 */
551
552 //if (mGrantType.equals(OAuth2Context.OAUTH2_AUTH_CODE_GRANT_TYPE)) {
553 Uri uri = Uri.parse(getString(R.string.oauth_url_endpoint_auth));
554 Uri.Builder uriBuilder = uri.buildUpon();
555 uriBuilder.appendQueryParameter(OAuth2Context.CODE_RESPONSE_TYPE, OAuth2Context.OAUTH2_CODE_RESPONSE_TYPE);
556 uriBuilder.appendQueryParameter(OAuth2Context.CODE_REDIRECT_URI, OAuth2Context.MY_REDIRECT_URI);
557 uriBuilder.appendQueryParameter(OAuth2Context.CODE_CLIENT_ID, OAuth2Context.OAUTH2_F_CLIENT_ID);
558 uriBuilder.appendQueryParameter(OAuth2Context.CODE_SCOPE, OAuth2Context.OAUTH2_F_SCOPE);
559 //uriBuilder.appendQueryParameter(OAuth2Context.CODE_STATE, whateverwewant);
560 uri = uriBuilder.build();
561 Log.d(TAG, "Starting browser to view " + uri.toString());
562 Intent i = new Intent(Intent.ACTION_VIEW, uri);
563 startActivity(i);
564 //}
565 }
566
567
568 /**
569 * Callback method invoked when a RemoteOperation executed by this Activity finishes.
570 *
571 * Dispatches the operation flow to the right method.
572 */
573 @Override
574 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
575
576 if (operation instanceof OwnCloudServerCheckOperation) {
577 onOcServerCheckFinish((OwnCloudServerCheckOperation) operation, result);
578
579 } else if (operation instanceof OAuth2GetAccessToken) {
580 onGetOAuthAccessTokenFinish((OAuth2GetAccessToken)operation, result);
581
582 } else if (operation instanceof ExistenceCheckOperation) {
583 onAuthorizationCheckFinish((ExistenceCheckOperation)operation, result);
584
585 }
586 }
587
588
589 /**
590 * Processes the result of the server check performed when the user finishes the enter of the
591 * server URL.
592 *
593 * @param operation Server check performed.
594 * @param result Result of the check.
595 */
596 private void onOcServerCheckFinish(OwnCloudServerCheckOperation operation, RemoteOperationResult result) {
597 /// update status connection icon and text
598 mStatusText = mStatusIcon = 0;
599 mStatusCorrect = false;
600
601 switch (result.getCode()) {
602 case OK_SSL:
603 mIsSslConn = true;
604 mStatusIcon = android.R.drawable.ic_secure;
605 mStatusText = R.string.auth_secure_connection;
606 mStatusCorrect = true;
607 break;
608
609 case OK_NO_SSL:
610 case OK:
611 mIsSslConn = false;
612 mStatusCorrect = true;
613 if (mHostUrlInput.getText().toString().trim().toLowerCase().startsWith("http://") ) {
614 mStatusText = R.string.auth_connection_established;
615 mStatusIcon = R.drawable.ic_ok;
616 } else {
617 mStatusText = R.string.auth_nossl_plain_ok_title;
618 mStatusIcon = android.R.drawable.ic_partial_secure;
619 }
620 break;
621
622 /// very special case (TODO: move to a common place for all the remote operations)
623 case SSL_RECOVERABLE_PEER_UNVERIFIED:
624 mStatusIcon = R.drawable.common_error;
625 mStatusText = R.string.auth_ssl_unverified_server_title;
626 mLastSslUntrustedServerResult = result;
627 showDialog(DIALOG_SSL_VALIDATOR);
628 break;
629
630 case BAD_OC_VERSION:
631 mStatusIcon = R.drawable.common_error;
632 mStatusText = R.string.auth_bad_oc_version_title;
633 break;
634 case WRONG_CONNECTION:
635 mStatusIcon = R.drawable.common_error;
636 mStatusText = R.string.auth_wrong_connection_title;
637 break;
638 case TIMEOUT:
639 mStatusIcon = R.drawable.common_error;
640 mStatusText = R.string.auth_timeout_title;
641 break;
642 case INCORRECT_ADDRESS:
643 mStatusIcon = R.drawable.common_error;
644 mStatusText = R.string.auth_incorrect_address_title;
645 break;
646
647 case SSL_ERROR:
648 mStatusIcon = R.drawable.common_error;
649 mStatusText = R.string.auth_ssl_general_error_title;
650 break;
651
652 case HOST_NOT_AVAILABLE:
653 mStatusIcon = R.drawable.common_error;
654 mStatusText = R.string.auth_unknown_host_title;
655 break;
656 case NO_NETWORK_CONNECTION:
657 mStatusIcon = R.drawable.no_network;
658 mStatusText = R.string.auth_no_net_conn_title;
659 break;
660 case INSTANCE_NOT_CONFIGURED:
661 mStatusIcon = R.drawable.common_error;
662 mStatusText = R.string.auth_not_configured_title;
663 break;
664 case FILE_NOT_FOUND:
665 mStatusIcon = R.drawable.common_error;
666 mStatusText = R.string.auth_incorrect_path_title;
667 break;
668 case UNHANDLED_HTTP_CODE:
669 case UNKNOWN_ERROR:
670 mStatusIcon = R.drawable.common_error;
671 mStatusText = R.string.auth_unknown_error_title;
672 break;
673 default:
674 Log.e(TAG, "Incorrect connection checker result type: " + result.getHttpCode());
675 }
676 updateOcServerCheckIconAndText();
677
678 /// update the visibility of the 'retry connection' button
679 if (!mStatusCorrect)
680 mRefreshButton.setVisibility(View.VISIBLE);
681 else
682 mRefreshButton.setVisibility(View.INVISIBLE);
683
684 /// retrieve discovered version and normalize server URL
685 mDiscoveredVersion = operation.getDiscoveredVersion();
686 mHostBaseUrl = mHostUrlInput.getText().toString().trim();
687 if (!mHostBaseUrl.toLowerCase().startsWith("http://") &&
688 !mHostBaseUrl.toLowerCase().startsWith("https://")) {
689
690 if (mIsSslConn) {
691 mHostBaseUrl = "https://" + mHostBaseUrl;
692 } else {
693 mHostBaseUrl = "http://" + mHostBaseUrl;
694 }
695
696 }
697 if (mHostBaseUrl.endsWith("/"))
698 mHostBaseUrl = mHostBaseUrl.substring(0, mHostBaseUrl.length() - 1);
699
700 /// allow or not the user try to access the server
701 mOkButton.setEnabled(mStatusCorrect);
702 }
703
704
705 /**
706 * Processes the result of the request for and access token send
707 * to an OAuth authorization server.
708 *
709 * @param operation Operation performed requesting the access token.
710 * @param result Result of the operation.
711 */
712 private void onGetOAuthAccessTokenFinish(OAuth2GetAccessToken operation, RemoteOperationResult result) {
713 try {
714 dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);
715 } catch (IllegalArgumentException e) {
716 // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens
717 }
718
719 String webdav_path = AccountUtils.getWebdavPath(mDiscoveredVersion, false);
720 if (result.isSuccess() && webdav_path != null) {
721 /// be gentle with the user
722 showDialog(DIALOG_LOGIN_PROGRESS);
723
724 /// time to test the retrieved access token on the ownCloud server
725 mOAuthAccessToken = ((OAuth2GetAccessToken)operation).getResultTokenMap().get(OAuth2Context.KEY_ACCESS_TOKEN);
726 Log.d(TAG, "Got ACCESS TOKEN: " + mOAuthAccessToken);
727 mAuthCheckOperation = new ExistenceCheckOperation("", this, false);
728 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(mHostBaseUrl + webdav_path), this);
729 client.setBearerCredentials(mOAuthAccessToken);
730 mAuthCheckOperation.execute(client, this, mHandler);
731
732 } else {
733 if (webdav_path != null) {
734 mOAuthAuthEndpointText.setError("A valid authorization could not be obtained");
735 } else {
736 mOAuthAuthEndpointText.setError(getString(R.string.auth_bad_oc_version_title)); // should never happen
737 }
738 }
739 }
740
741
742 /**
743 * Processes the result of the access check performed to try the user credentials.
744 *
745 * Creates a new account through the AccountManager.
746 *
747 * @param operation Access check performed.
748 * @param result Result of the operation.
749 */
750 private void onAuthorizationCheckFinish(ExistenceCheckOperation operation, RemoteOperationResult result) {
751 try {
752 dismissDialog(DIALOG_LOGIN_PROGRESS);
753 } catch (IllegalArgumentException e) {
754 // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens
755 }
756
757 boolean isOAuth = mOAuth2Check.isChecked();
758
759 if (result.isSuccess()) {
760 Log.d(TAG, "Successful access - time to save the account");
761
762 /// create and save new ownCloud account
763 Uri uri = Uri.parse(mHostBaseUrl);
764 String username = isOAuth ?
765 "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong() :
766 mUsernameInput.getText().toString().trim();
767 // TODO a better way to set an account name
768 String accountName = username + "@" + uri.getHost();
769 if (uri.getPort() >= 0) {
770 accountName += ":" + uri.getPort();
771 }
772 Account account = new Account(accountName, AccountAuthenticator.ACCOUNT_TYPE);
773 AccountManager accManager = AccountManager.get(this);
774 if (isOAuth) {
775 accManager.addAccountExplicitly(account, "", null); // with our implementation, the password is never input in the app
776 } else {
777 accManager.addAccountExplicitly(account, mPasswordInput.getText().toString(), null);
778 }
779
780 /// add the new account as default in preferences, if there is none already
781 Account defaultAccount = AccountUtils.getCurrentOwnCloudAccount(this);
782 if (defaultAccount == null) {
783 SharedPreferences.Editor editor = PreferenceManager
784 .getDefaultSharedPreferences(this).edit();
785 editor.putString("select_oc_account", accountName);
786 editor.commit();
787 }
788
789
790 /// prepare result to return to the Authenticator
791 // TODO check again what the Authenticator makes with it; probably has the same effect as addAccountExplicitly, but it's not well done
792 final Intent intent = new Intent(); // TODO check if the intent can be retrieved from getIntent(), passed from AccountAuthenticator
793 intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE, AccountAuthenticator.ACCOUNT_TYPE);
794 intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
795 if (!isOAuth)
796 intent.putExtra(AccountManager.KEY_AUTHTOKEN, AccountAuthenticator.ACCOUNT_TYPE); // TODO check this; not sure it's right; maybe
797 intent.putExtra(AccountManager.KEY_USERDATA, username);
798 if (isOAuth) {
799 accManager.setAuthToken(account, AccountAuthenticator.AUTH_TOKEN_TYPE_ACCESS_TOKEN, mOAuthAccessToken);
800 }
801 /// add user data to the new account; TODO probably can be done in the last parameter addAccountExplicitly, or in KEY_USERDATA
802 accManager.setUserData(account, AccountAuthenticator.KEY_OC_VERSION, mDiscoveredVersion.toString());
803 accManager.setUserData(account, AccountAuthenticator.KEY_OC_BASE_URL, mHostBaseUrl);
804 if (isOAuth)
805 accManager.setUserData(account, AccountAuthenticator.KEY_SUPPORTS_OAUTH2, "TRUE"); // TODO this flag should be unnecessary
806
807 setAccountAuthenticatorResult(intent.getExtras());
808 setResult(RESULT_OK, intent);
809
810 /// immediately request for the synchronization of the new account
811 Bundle bundle = new Bundle();
812 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
813 ContentResolver.requestSync(account, AccountAuthenticator.AUTHORITY, bundle);
814
815 finish();
816
817 } else {
818 if (!isOAuth) {
819 mUsernameInput.setError(result.getLogMessage() + " ");
820 // the extra spaces are a workaround for an ugly bug:
821 // 1. insert wrong credentials and connect
822 // 2. put the focus on the user name field with using hardware controls (don't touch the screen); the error is shown UNDER the field
823 // 3. touch the user name field; the software keyboard appears; the error popup is moved OVER the field and SHRINKED in width, losing the last word
824 // Seen, at least, in Android 2.x devices
825
826 } else {
827 mOAuthAuthEndpointText.setError(result.getLogMessage() + " ");
828 }
829 Log.d(TAG, "Access failed: " + result.getLogMessage());
830 }
831 }
832
833
834
835 /**
836 * {@inheritDoc}
837 *
838 * Necessary to update the contents of the SSL Dialog
839 *
840 * TODO move to some common place for all possible untrusted SSL failures
841 */
842 @Override
843 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
844 switch (id) {
845 case DIALOG_LOGIN_PROGRESS:
846 case DIALOG_CERT_NOT_SAVED:
847 case DIALOG_OAUTH2_LOGIN_PROGRESS:
848 break;
849 case DIALOG_SSL_VALIDATOR: {
850 ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
851 break;
852 }
853 default:
854 Log.e(TAG, "Incorrect dialog called with id = " + id);
855 }
856 }
857
858
859 /**
860 * {@inheritDoc}
861 */
862 @Override
863 protected Dialog onCreateDialog(int id) {
864 Dialog dialog = null;
865 switch (id) {
866 case DIALOG_LOGIN_PROGRESS: {
867 /// simple progress dialog
868 ProgressDialog working_dialog = new ProgressDialog(this);
869 working_dialog.setMessage(getResources().getString(R.string.auth_trying_to_login));
870 working_dialog.setIndeterminate(true);
871 working_dialog.setCancelable(true);
872 working_dialog
873 .setOnCancelListener(new DialogInterface.OnCancelListener() {
874 @Override
875 public void onCancel(DialogInterface dialog) {
876 /// TODO study if this is enough
877 Log.i(TAG, "Login canceled");
878 if (mOperationThread != null) {
879 mOperationThread.interrupt();
880 finish();
881 }
882 }
883 });
884 dialog = working_dialog;
885 break;
886 }
887 case DIALOG_OAUTH2_LOGIN_PROGRESS: {
888 /// oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser. - OLD!
889 // TODO optimize this dialog
890 ProgressDialog working_dialog = new ProgressDialog(this);
891 /* Leave the old OAuth flow
892 try {
893 if (mCodeResponseJson != null && mCodeResponseJson.has(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL)) {
894 working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message),
895 mCodeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL),
896 mCodeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE)));
897 } else {*/
898 working_dialog.setMessage(String.format("Getting authorization"));
899 /*}
900 } catch (JSONException e) {
901 Log.e(TAG, "onCreateDialog->JSONException: " + e.toString());
902 }*/
903 working_dialog.setIndeterminate(true);
904 working_dialog.setCancelable(true);
905 working_dialog
906 .setOnCancelListener(new DialogInterface.OnCancelListener() {
907 @Override
908 public void onCancel(DialogInterface dialog) {
909 Log.i(TAG, "Login canceled");
910 /*if (mOAuth2GetCodeThread != null) {
911 mOAuth2GetCodeThread.interrupt();
912 finish();
913 } */
914 /*if (tokenReceiver != null) {
915 unregisterReceiver(tokenReceiver);
916 tokenReceiver = null;
917 finish();
918 }*/
919 finish();
920 }
921 });
922 dialog = working_dialog;
923 break;
924 }
925 case DIALOG_SSL_VALIDATOR: {
926 /// TODO start to use new dialog interface, at least for this (it is a FragmentDialog already)
927 dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
928 break;
929 }
930 case DIALOG_CERT_NOT_SAVED: {
931 AlertDialog.Builder builder = new AlertDialog.Builder(this);
932 builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
933 builder.setCancelable(false);
934 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
935 @Override
936 public void onClick(DialogInterface dialog, int which) {
937 dialog.dismiss();
938 };
939 });
940 dialog = builder.create();
941 break;
942 }
943 default:
944 Log.e(TAG, "Incorrect dialog called with id = " + id);
945 }
946 return dialog;
947 }
948
949
950 /**
951 * Starts and activity to open the 'new account' page in the ownCloud web site
952 *
953 * @param view 'Account register' button
954 */
955 public void onRegisterClick(View view) {
956 Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));
957 setResult(RESULT_CANCELED);
958 startActivity(register);
959 }
960
961
962 /**
963 * Updates the content and visibility state of the icon and text associated
964 * to the last check on the ownCloud server.
965 */
966 private void updateOcServerCheckIconAndText() {
967 ImageView iv = (ImageView) findViewById(R.id.action_indicator);
968 TextView tv = (TextView) findViewById(R.id.status_text);
969
970 if (mStatusIcon == 0 && mStatusText == 0) {
971 iv.setVisibility(View.INVISIBLE);
972 tv.setVisibility(View.INVISIBLE);
973 } else {
974 iv.setImageResource(mStatusIcon);
975 tv.setText(mStatusText);
976 iv.setVisibility(View.VISIBLE);
977 tv.setVisibility(View.VISIBLE);
978 }
979 }
980
981
982 /**
983 * Called when the refresh button in the input field for ownCloud host is clicked.
984 *
985 * Performs a new check on the URL in the input field.
986 *
987 * @param view Refresh 'button'
988 */
989 public void onRefreshClick(View view) {
990 onFocusChange(mRefreshButton, false);
991 }
992
993
994 /**
995 * Called when the eye icon in the password field is clicked.
996 *
997 * Toggles the visibility of the password in the field.
998 *
999 * @param view 'View password' 'button'
1000 */
1001 public void onViewPasswordClick(View view) {
1002 int selectionStart = mPasswordInput.getSelectionStart();
1003 int selectionEnd = mPasswordInput.getSelectionEnd();
1004 int input_type = mPasswordInput.getInputType();
1005 if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
1006 input_type = InputType.TYPE_CLASS_TEXT
1007 | InputType.TYPE_TEXT_VARIATION_PASSWORD;
1008 } else {
1009 input_type = InputType.TYPE_CLASS_TEXT
1010 | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
1011 }
1012 mPasswordInput.setInputType(input_type);
1013 mPasswordInput.setSelection(selectionStart, selectionEnd);
1014 }
1015
1016
1017 /**
1018 * Called when the checkbox for OAuth authorization is clicked.
1019 *
1020 * Hides or shows the input fields for user & password.
1021 *
1022 * @param view 'View password' 'button'
1023 */
1024 public void onCheckClick(View view) {
1025 CheckBox oAuth2Check = (CheckBox)view;
1026 changeViewByOAuth2Check(oAuth2Check.isChecked());
1027
1028 }
1029
1030 /**
1031 * Changes the visibility of input elements depending upon the kind of authorization
1032 * chosen by the user: basic or OAuth
1033 *
1034 * @param checked 'True' when OAuth is selected.
1035 */
1036 public void changeViewByOAuth2Check(Boolean checked) {
1037
1038 ImageView auth2ActionIndicator = (ImageView) findViewById(R.id.auth2_action_indicator);
1039 TextView oauth2StatusText = (TextView) findViewById(R.id.oauth2_status_text);
1040
1041 if (checked) {
1042 mOAuthAuthEndpointText.setVisibility(View.VISIBLE);
1043 mOAuthTokenEndpointText.setVisibility(View.VISIBLE);
1044 mUsernameInput.setVisibility(View.GONE);
1045 mPasswordInput.setVisibility(View.GONE);
1046 mViewPasswordButton.setVisibility(View.GONE);
1047 auth2ActionIndicator.setVisibility(View.INVISIBLE);
1048 oauth2StatusText.setVisibility(View.INVISIBLE);
1049 } else {
1050 mOAuthAuthEndpointText.setVisibility(View.GONE);
1051 mOAuthTokenEndpointText.setVisibility(View.GONE);
1052 mUsernameInput.setVisibility(View.VISIBLE);
1053 mPasswordInput.setVisibility(View.VISIBLE);
1054 mViewPasswordButton.setVisibility(View.INVISIBLE);
1055 auth2ActionIndicator.setVisibility(View.GONE);
1056 oauth2StatusText.setVisibility(View.GONE);
1057 }
1058
1059 }
1060
1061 /**
1062 * Updates the content and visibility state of the icon and text associated
1063 * to the interactions with the OAuth authorization server.
1064 *
1065 * @param drawable_id Resource id for the icon.
1066 * @param text_id Resource id for the text.
1067 */
1068 private void updateOAuth2IconAndText(int drawable_id, int text_id) {
1069 ImageView iv = (ImageView) findViewById(R.id.auth2_action_indicator);
1070 TextView tv = (TextView) findViewById(R.id.oauth2_status_text);
1071
1072 if (drawable_id == 0 && text_id == 0) {
1073 iv.setVisibility(View.INVISIBLE);
1074 tv.setVisibility(View.INVISIBLE);
1075 } else {
1076 iv.setImageResource(drawable_id);
1077 tv.setText(text_id);
1078 iv.setVisibility(View.VISIBLE);
1079 tv.setVisibility(View.VISIBLE);
1080 }
1081 }
1082
1083 /* Leave the old OAuth flow
1084 // Results from the first call to oAuth2 server : getting the user_code and verification_url.
1085 @Override
1086 public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) {
1087 if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) {
1088 mCodeResponseJson = responseJson;
1089 if (mCodeResponseJson != null) {
1090 getOAuth2AccessTokenFromJsonResponse();
1091 } // else - nothing to do here - wait for callback !!!
1092
1093 } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) {
1094 updateOAuth2IconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable);
1095 }
1096 }
1097
1098 // If the results of getting the user_code and verification_url are OK, we get the received data and we start
1099 // the polling service to oAuth2 server to get a valid token.
1100 private void getOAuth2AccessTokenFromJsonResponse() {
1101 String deviceCode = null;
1102 String verificationUrl = null;
1103 String userCode = null;
1104 int expiresIn = -1;
1105 int interval = -1;
1106
1107 Log.d(TAG, "ResponseOAuth2->" + mCodeResponseJson.toString());
1108
1109 try {
1110 // We get data that we must show to the user or we will use internally.
1111 verificationUrl = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_VERIFICATION_URL);
1112 userCode = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_USER_CODE);
1113 expiresIn = mCodeResponseJson.getInt(OAuth2GetAuthorizationToken.CODE_EXPIRES_IN);
1114
1115 // And we get data that we must use to get a token.
1116 deviceCode = mCodeResponseJson.getString(OAuth2GetAuthorizationToken.CODE_DEVICE_CODE);
1117 interval = mCodeResponseJson.getInt(OAuth2GetAuthorizationToken.CODE_INTERVAL);
1118
1119 } catch (JSONException e) {
1120 Log.e(TAG, "Exception accesing data in Json object" + e.toString());
1121 }
1122
1123 // Updating status widget to OK.
1124 updateOAuth2IconAndText(R.drawable.ic_ok, R.string.auth_connection_established);
1125
1126 // Showing the dialog with instructions for the user.
1127 showDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);
1128
1129 // Loggin all the data.
1130 Log.d(TAG, "verificationUrl->" + verificationUrl);
1131 Log.d(TAG, "userCode->" + userCode);
1132 Log.d(TAG, "deviceCode->" + deviceCode);
1133 Log.d(TAG, "expiresIn->" + expiresIn);
1134 Log.d(TAG, "interval->" + interval);
1135
1136 // Starting the pooling service.
1137 try {
1138 Intent tokenService = new Intent(this, OAuth2GetTokenService.class);
1139 tokenService.putExtra(OAuth2GetTokenService.TOKEN_URI, OAuth2Context.OAUTH2_G_DEVICE_GETTOKEN_URL);
1140 tokenService.putExtra(OAuth2GetTokenService.TOKEN_DEVICE_CODE, deviceCode);
1141 tokenService.putExtra(OAuth2GetTokenService.TOKEN_INTERVAL, interval);
1142
1143 startService(tokenService);
1144 }
1145 catch (Exception e) {
1146 Log.e(TAG, "tokenService creation problem :", e);
1147 }
1148
1149 }
1150 */
1151
1152 /* Leave the old OAuth flow
1153 // We get data from the oAuth2 token service with this broadcast receiver.
1154 private class TokenReceiver extends BroadcastReceiver {
1155 /**
1156 * The token is received.
1157 * @author
1158 * {@link BroadcastReceiver} to enable oAuth2 token receiving.
1159 *-/
1160 @Override
1161 public void onReceive(Context context, Intent intent) {
1162 @SuppressWarnings("unchecked")
1163 HashMap<String, String> tokenResponse = (HashMap<String, String>)intent.getExtras().get(OAuth2GetTokenService.TOKEN_RECEIVED_DATA);
1164 Log.d(TAG, "TokenReceiver->" + tokenResponse.get(OAuth2GetTokenService.TOKEN_ACCESS_TOKEN));
1165 dismissDialog(DIALOG_OAUTH2_LOGIN_PROGRESS);
1166
1167 }
1168 }
1169 */
1170
1171
1172 /**
1173 * Called from SslValidatorDialog when a new server certificate was correctly saved.
1174 */
1175 public void onSavedCertificate() {
1176 mOperationThread = mOcServerChkOperation.retry(this, mHandler);
1177 }
1178
1179 /**
1180 * Called from SslValidatorDialog when a new server certificate could not be saved
1181 * when the user requested it.
1182 */
1183 @Override
1184 public void onFailedSavingCertificate() {
1185 showDialog(DIALOG_CERT_NOT_SAVED);
1186 }
1187
1188 }