Fixed bug when turning tablet with no file in the right fragment
[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 version 2,
7 * as published by the Free Software Foundation.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 *
17 */
18
19 package com.owncloud.android.ui.activity;
20
21 import java.net.MalformedURLException;
22 import java.net.URL;
23
24 import com.owncloud.android.AccountUtils;
25 import com.owncloud.android.Log_OC;
26 import com.owncloud.android.authenticator.AccountAuthenticator;
27 import com.owncloud.android.authenticator.AuthenticationRunnable;
28 import com.owncloud.android.authenticator.OnAuthenticationResultListener;
29 import com.owncloud.android.authenticator.OnConnectCheckListener;
30 import com.owncloud.android.ui.dialog.SslValidatorDialog;
31 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
32 import com.owncloud.android.network.OwnCloudClientUtils;
33 import com.owncloud.android.operations.ConnectionCheckOperation;
34 import com.owncloud.android.operations.OnRemoteOperationListener;
35 import com.owncloud.android.operations.RemoteOperation;
36 import com.owncloud.android.operations.RemoteOperationResult;
37
38 import android.accounts.Account;
39 import android.accounts.AccountAuthenticatorActivity;
40 import android.accounts.AccountManager;
41 import android.app.AlertDialog;
42 import android.app.Dialog;
43 import android.app.ProgressDialog;
44 import android.content.ContentResolver;
45 import android.content.DialogInterface;
46 import android.content.Intent;
47 import android.content.SharedPreferences;
48 import android.net.Uri;
49 import android.os.Bundle;
50 import android.os.Handler;
51 import android.preference.PreferenceManager;
52 import android.text.InputType;
53 import android.util.Log;
54 import android.view.View;
55 import android.view.View.OnClickListener;
56 import android.view.View.OnFocusChangeListener;
57 import android.view.Window;
58 import android.widget.Button;
59 import android.widget.EditText;
60 import android.widget.ImageView;
61 import android.widget.TextView;
62 import com.owncloud.android.R;
63
64 import eu.alefzero.webdav.WebdavClient;
65
66 /**
67 * This Activity is used to add an ownCloud account to the App
68 *
69 * @author Bartek Przybylski
70 *
71 */
72 public class AuthenticatorActivity extends AccountAuthenticatorActivity
73 implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener,
74 OnFocusChangeListener, OnClickListener {
75
76 private static final int DIALOG_LOGIN_PROGRESS = 0;
77 private static final int DIALOG_SSL_VALIDATOR = 1;
78 private static final int DIALOG_CERT_NOT_SAVED = 2;
79
80 private static final String TAG = "AuthActivity";
81
82 private Thread mAuthThread;
83 private AuthenticationRunnable mAuthRunnable;
84 //private ConnectionCheckerRunnable mConnChkRunnable = null;
85 private ConnectionCheckOperation mConnChkRunnable;
86 private final Handler mHandler = new Handler();
87 private String mBaseUrl;
88
89 private static final String STATUS_TEXT = "STATUS_TEXT";
90 private static final String STATUS_ICON = "STATUS_ICON";
91 private static final String STATUS_CORRECT = "STATUS_CORRECT";
92 private static final String IS_SSL_CONN = "IS_SSL_CONN";
93 private int mStatusText, mStatusIcon;
94 private boolean mStatusCorrect, mIsSslConn;
95 private RemoteOperationResult mLastSslUntrustedServerResult;
96
97 public static final String PARAM_USERNAME = "param_Username";
98 public static final String PARAM_HOSTNAME = "param_Hostname";
99
100 @Override
101 protected void onCreate(Bundle savedInstanceState) {
102 super.onCreate(savedInstanceState);
103 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
104 setContentView(R.layout.account_setup);
105 ImageView iv = (ImageView) findViewById(R.id.refreshButton);
106 ImageView iv2 = (ImageView) findViewById(R.id.viewPassword);
107 TextView tv = (TextView) findViewById(R.id.host_URL);
108 TextView tv2 = (TextView) findViewById(R.id.account_password);
109
110 if (savedInstanceState != null) {
111 mStatusIcon = savedInstanceState.getInt(STATUS_ICON);
112 mStatusText = savedInstanceState.getInt(STATUS_TEXT);
113 mStatusCorrect = savedInstanceState.getBoolean(STATUS_CORRECT);
114 mIsSslConn = savedInstanceState.getBoolean(IS_SSL_CONN);
115 setResultIconAndText(mStatusIcon, mStatusText);
116 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
117 if (!mStatusCorrect)
118 iv.setVisibility(View.VISIBLE);
119 else
120 iv.setVisibility(View.INVISIBLE);
121
122 } else {
123 mStatusText = mStatusIcon = 0;
124 mStatusCorrect = false;
125 mIsSslConn = false;
126 }
127 iv.setOnClickListener(this);
128 iv2.setOnClickListener(this);
129 tv.setOnFocusChangeListener(this);
130 tv2.setOnFocusChangeListener(this);
131
132 Button b = (Button) findViewById(R.id.account_register);
133 if (b != null) {
134 b.setText(String.format(getString(R.string.auth_register), getString(R.string.app_name)));
135 }
136 }
137
138 @Override
139 protected void onSaveInstanceState(Bundle outState) {
140 outState.putInt(STATUS_ICON, mStatusIcon);
141 outState.putInt(STATUS_TEXT, mStatusText);
142 outState.putBoolean(STATUS_CORRECT, mStatusCorrect);
143 super.onSaveInstanceState(outState);
144 }
145
146 @Override
147 protected Dialog onCreateDialog(int id) {
148 Dialog dialog = null;
149 switch (id) {
150 case DIALOG_LOGIN_PROGRESS: {
151 ProgressDialog working_dialog = new ProgressDialog(this);
152 working_dialog.setMessage(getResources().getString(
153 R.string.auth_trying_to_login));
154 working_dialog.setIndeterminate(true);
155 working_dialog.setCancelable(true);
156 working_dialog
157 .setOnCancelListener(new DialogInterface.OnCancelListener() {
158 @Override
159 public void onCancel(DialogInterface dialog) {
160 Log_OC.i(TAG, "Login canceled");
161 if (mAuthThread != null) {
162 mAuthThread.interrupt();
163 finish();
164 }
165 }
166 });
167 dialog = working_dialog;
168 break;
169 }
170 case DIALOG_SSL_VALIDATOR: {
171 dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
172 break;
173 }
174 case DIALOG_CERT_NOT_SAVED: {
175 AlertDialog.Builder builder = new AlertDialog.Builder(this);
176 builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
177 builder.setCancelable(false);
178 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
179 @Override
180 public void onClick(DialogInterface dialog, int which) {
181 dialog.dismiss();
182 };
183 });
184 dialog = builder.create();
185 break;
186 }
187 default:
188 Log_OC.e(TAG, "Incorrect dialog called with id = " + id);
189 }
190 return dialog;
191 }
192
193 @Override
194 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
195 switch (id) {
196 case DIALOG_LOGIN_PROGRESS:
197 case DIALOG_CERT_NOT_SAVED:
198 break;
199 case DIALOG_SSL_VALIDATOR: {
200 ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
201 break;
202 }
203 default:
204 Log_OC.e(TAG, "Incorrect dialog called with id = " + id);
205 }
206 }
207
208 public void onAuthenticationResult(boolean success, String message) {
209 if (success) {
210 TextView username_text = (TextView) findViewById(R.id.account_username), password_text = (TextView) findViewById(R.id.account_password);
211
212 URL url;
213 try {
214 url = new URL(message);
215 } catch (MalformedURLException e) {
216 // should never happen
217 Log_OC.e(getClass().getName(), "Malformed URL: " + message);
218 return;
219 }
220
221 String username = username_text.getText().toString().trim();
222 String accountName = username + "@" + url.getHost();
223 if (url.getPort() >= 0) {
224 accountName += ":" + url.getPort();
225 }
226 Account account = new Account(accountName,
227 AccountAuthenticator.ACCOUNT_TYPE);
228 AccountManager accManager = AccountManager.get(this);
229 accManager.addAccountExplicitly(account, password_text.getText()
230 .toString(), null);
231
232 // Add this account as default in the preferences, if there is none
233 // already
234 Account defaultAccount = AccountUtils
235 .getCurrentOwnCloudAccount(this);
236 if (defaultAccount == null) {
237 SharedPreferences.Editor editor = PreferenceManager
238 .getDefaultSharedPreferences(this).edit();
239 editor.putString("select_oc_account", accountName);
240 editor.commit();
241 }
242
243 final Intent intent = new Intent();
244 intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,
245 AccountAuthenticator.ACCOUNT_TYPE);
246 intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
247 intent.putExtra(AccountManager.KEY_AUTHTOKEN,
248 AccountAuthenticator.ACCOUNT_TYPE);
249 intent.putExtra(AccountManager.KEY_USERDATA, username);
250
251 accManager.setUserData(account,
252 AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable
253 .getDiscoveredVersion().toString());
254
255 accManager.setUserData(account,
256 AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);
257
258 setAccountAuthenticatorResult(intent.getExtras());
259 setResult(RESULT_OK, intent);
260 Bundle bundle = new Bundle();
261 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
262 //getContentResolver().startSync(ProviderTableMeta.CONTENT_URI,
263 // bundle);
264 ContentResolver.requestSync(account, "org.owncloud", bundle);
265
266 /*
267 * if
268 * (mConnChkRunnable.getDiscoveredVersion().compareTo(OwnCloudVersion
269 * .owncloud_v2) >= 0) { Intent i = new Intent(this,
270 * ExtensionsAvailableActivity.class); startActivity(i); }
271 */
272
273 finish();
274 } else {
275 try {
276 dismissDialog(DIALOG_LOGIN_PROGRESS);
277 } catch (IllegalArgumentException e) {
278 // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens
279 }
280 TextView tv = (TextView) findViewById(R.id.account_username);
281 tv.setError(message + " "); // the extra spaces are a workaround for an ugly bug:
282 // 1. insert wrong credentials and connect
283 // 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
284 // 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
285 // Seen, at least, in Android 2.x devices
286 }
287 }
288 public void onCancelClick(View view) {
289 setResult(RESULT_CANCELED);
290 finish();
291 }
292
293 public void onOkClick(View view) {
294 String prefix = "";
295 String url = ((TextView) findViewById(R.id.host_URL)).getText()
296 .toString().trim();
297 if (mIsSslConn) {
298 prefix = "https://";
299 } else {
300 prefix = "http://";
301 }
302 if (url.toLowerCase().startsWith("http://")
303 || url.toLowerCase().startsWith("https://")) {
304 prefix = "";
305 }
306 continueConnection(prefix);
307 }
308
309 public void onRegisterClick(View view) {
310 Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse(getString(R.string.url_account_register)));
311 setResult(RESULT_CANCELED);
312 startActivity(register);
313 }
314
315 private void continueConnection(String prefix) {
316 String url = ((TextView) findViewById(R.id.host_URL)).getText()
317 .toString().trim();
318 String username = ((TextView) findViewById(R.id.account_username))
319 .getText().toString();
320 String password = ((TextView) findViewById(R.id.account_password))
321 .getText().toString();
322 if (url.endsWith("/"))
323 url = url.substring(0, url.length() - 1);
324
325 URL uri = null;
326 String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable
327 .getDiscoveredVersion());
328
329 if (webdav_path == null) {
330 onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));
331 return;
332 }
333
334 try {
335 mBaseUrl = prefix + url;
336 String url_str = prefix + url + webdav_path;
337 uri = new URL(url_str);
338 } catch (MalformedURLException e) {
339 // should never happen
340 onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title));
341 return;
342 }
343
344 showDialog(DIALOG_LOGIN_PROGRESS);
345 mAuthRunnable = new AuthenticationRunnable(uri, username, password, this);
346 mAuthRunnable.setOnAuthenticationResultListener(this, mHandler);
347 mAuthThread = new Thread(mAuthRunnable);
348 mAuthThread.start();
349 }
350
351 @Override
352 public void onConnectionCheckResult(ResultType type) {
353 mStatusText = mStatusIcon = 0;
354 mStatusCorrect = false;
355 String t_url = ((TextView) findViewById(R.id.host_URL)).getText()
356 .toString().trim().toLowerCase();
357
358 switch (type) {
359 case OK_SSL:
360 mIsSslConn = true;
361 mStatusIcon = android.R.drawable.ic_secure;
362 mStatusText = R.string.auth_secure_connection;
363 mStatusCorrect = true;
364 break;
365 case OK_NO_SSL:
366 mIsSslConn = false;
367 mStatusCorrect = true;
368 if (t_url.startsWith("http://") ) {
369 mStatusText = R.string.auth_connection_established;
370 mStatusIcon = R.drawable.ic_ok;
371 } else {
372 mStatusText = R.string.auth_nossl_plain_ok_title;
373 mStatusIcon = android.R.drawable.ic_partial_secure;
374 }
375 break;
376 case BAD_OC_VERSION:
377 mStatusIcon = R.drawable.common_error;
378 mStatusText = R.string.auth_bad_oc_version_title;
379 break;
380 case WRONG_CONNECTION:
381 mStatusIcon = R.drawable.common_error;
382 mStatusText = R.string.auth_wrong_connection_title;
383 break;
384 case TIMEOUT:
385 mStatusIcon = R.drawable.common_error;
386 mStatusText = R.string.auth_timeout_title;
387 break;
388 case INCORRECT_ADDRESS:
389 mStatusIcon = R.drawable.common_error;
390 mStatusText = R.string.auth_incorrect_address_title;
391 break;
392 case SSL_UNVERIFIED_SERVER:
393 mStatusIcon = R.drawable.common_error;
394 mStatusText = R.string.auth_ssl_unverified_server_title;
395 break;
396 case SSL_INIT_ERROR:
397 mStatusIcon = R.drawable.common_error;
398 mStatusText = R.string.auth_ssl_general_error_title;
399 break;
400 case HOST_NOT_AVAILABLE:
401 mStatusIcon = R.drawable.common_error;
402 mStatusText = R.string.auth_unknown_host_title;
403 break;
404 case NO_NETWORK_CONNECTION:
405 mStatusIcon = R.drawable.no_network;
406 mStatusText = R.string.auth_no_net_conn_title;
407 break;
408 case INSTANCE_NOT_CONFIGURED:
409 mStatusIcon = R.drawable.common_error;
410 mStatusText = R.string.auth_not_configured_title;
411 break;
412 case UNKNOWN_ERROR:
413 mStatusIcon = R.drawable.common_error;
414 mStatusText = R.string.auth_unknown_error_title;
415 break;
416 case FILE_NOT_FOUND:
417 mStatusIcon = R.drawable.common_error;
418 mStatusText = R.string.auth_incorrect_path_title;
419 break;
420 default:
421 Log_OC.e(TAG, "Incorrect connection checker result type: " + type);
422 }
423 setResultIconAndText(mStatusIcon, mStatusText);
424 if (!mStatusCorrect)
425 findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);
426 else
427 findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);
428 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
429 }
430
431 @Override
432 public void onFocusChange(View view, boolean hasFocus) {
433 if (view.getId() == R.id.host_URL) {
434 if (!hasFocus) {
435 TextView tv = ((TextView) findViewById(R.id.host_URL));
436 String uri = tv.getText().toString().trim();
437 if (uri.length() != 0) {
438 setResultIconAndText(R.drawable.progress_small,
439 R.string.auth_testing_connection);
440 //mConnChkRunnable = new ConnectionCheckerRunnable(uri, this);
441 mConnChkRunnable = new ConnectionCheckOperation(uri, this);
442 //mConnChkRunnable.setListener(this, mHandler);
443 //mAuthThread = new Thread(mConnChkRunnable);
444 //mAuthThread.start();
445 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);
446 mAuthThread = mConnChkRunnable.execute(client, this, mHandler);
447 } else {
448 findViewById(R.id.refreshButton).setVisibility(
449 View.INVISIBLE);
450 setResultIconAndText(0, 0);
451 }
452 } else {
453 // avoids that the 'connect' button can be clicked if the test was previously passed
454 findViewById(R.id.buttonOK).setEnabled(false);
455 }
456 } else if (view.getId() == R.id.account_password) {
457 ImageView iv = (ImageView) findViewById(R.id.viewPassword);
458 if (hasFocus) {
459 iv.setVisibility(View.VISIBLE);
460 } else {
461 TextView v = (TextView) findViewById(R.id.account_password);
462 int input_type = InputType.TYPE_CLASS_TEXT
463 | InputType.TYPE_TEXT_VARIATION_PASSWORD;
464 v.setInputType(input_type);
465 iv.setVisibility(View.INVISIBLE);
466 }
467 }
468 }
469
470 private void setResultIconAndText(int drawable_id, int text_id) {
471 ImageView iv = (ImageView) findViewById(R.id.action_indicator);
472 TextView tv = (TextView) findViewById(R.id.status_text);
473
474 if (drawable_id == 0 && text_id == 0) {
475 iv.setVisibility(View.INVISIBLE);
476 tv.setVisibility(View.INVISIBLE);
477 } else {
478 iv.setImageResource(drawable_id);
479 tv.setText(text_id);
480 iv.setVisibility(View.VISIBLE);
481 tv.setVisibility(View.VISIBLE);
482 }
483 }
484
485 @Override
486 public void onClick(View v) {
487 if (v.getId() == R.id.refreshButton) {
488 onFocusChange(findViewById(R.id.host_URL), false);
489 } else if (v.getId() == R.id.viewPassword) {
490 EditText view = (EditText) findViewById(R.id.account_password);
491 int selectionStart = view.getSelectionStart();
492 int selectionEnd = view.getSelectionEnd();
493 int input_type = view.getInputType();
494 if ((input_type & InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) == InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD) {
495 input_type = InputType.TYPE_CLASS_TEXT
496 | InputType.TYPE_TEXT_VARIATION_PASSWORD;
497 } else {
498 input_type = InputType.TYPE_CLASS_TEXT
499 | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
500 }
501 view.setInputType(input_type);
502 view.setSelection(selectionStart, selectionEnd);
503 }
504 }
505
506 @Override
507 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
508 if (operation.equals(mConnChkRunnable)) {
509
510 mStatusText = mStatusIcon = 0;
511 mStatusCorrect = false;
512 String t_url = ((TextView) findViewById(R.id.host_URL)).getText()
513 .toString().trim().toLowerCase();
514
515 switch (result.getCode()) {
516 case OK_SSL:
517 mIsSslConn = true;
518 mStatusIcon = android.R.drawable.ic_secure;
519 mStatusText = R.string.auth_secure_connection;
520 mStatusCorrect = true;
521 break;
522
523 case OK_NO_SSL:
524 case OK:
525 mIsSslConn = false;
526 mStatusCorrect = true;
527 if (t_url.startsWith("http://") ) {
528 mStatusText = R.string.auth_connection_established;
529 mStatusIcon = R.drawable.ic_ok;
530 } else {
531 mStatusText = R.string.auth_nossl_plain_ok_title;
532 mStatusIcon = android.R.drawable.ic_partial_secure;
533 }
534 break;
535
536
537 case BAD_OC_VERSION:
538 mStatusIcon = R.drawable.common_error;
539 mStatusText = R.string.auth_bad_oc_version_title;
540 break;
541 case WRONG_CONNECTION:
542 mStatusIcon = R.drawable.common_error;
543 mStatusText = R.string.auth_wrong_connection_title;
544 break;
545 case TIMEOUT:
546 mStatusIcon = R.drawable.common_error;
547 mStatusText = R.string.auth_timeout_title;
548 break;
549 case INCORRECT_ADDRESS:
550 mStatusIcon = R.drawable.common_error;
551 mStatusText = R.string.auth_incorrect_address_title;
552 break;
553
554 case SSL_RECOVERABLE_PEER_UNVERIFIED:
555 mStatusIcon = R.drawable.common_error;
556 mStatusText = R.string.auth_ssl_unverified_server_title;
557 mLastSslUntrustedServerResult = result;
558 showDialog(DIALOG_SSL_VALIDATOR);
559 break;
560
561 case SSL_ERROR:
562 mStatusIcon = R.drawable.common_error;
563 mStatusText = R.string.auth_ssl_general_error_title;
564 break;
565
566 case HOST_NOT_AVAILABLE:
567 mStatusIcon = R.drawable.common_error;
568 mStatusText = R.string.auth_unknown_host_title;
569 break;
570 case NO_NETWORK_CONNECTION:
571 mStatusIcon = R.drawable.no_network;
572 mStatusText = R.string.auth_no_net_conn_title;
573 break;
574 case INSTANCE_NOT_CONFIGURED:
575 mStatusIcon = R.drawable.common_error;
576 mStatusText = R.string.auth_not_configured_title;
577 break;
578 case FILE_NOT_FOUND:
579 mStatusIcon = R.drawable.common_error;
580 mStatusText = R.string.auth_incorrect_path_title;
581 break;
582 case UNHANDLED_HTTP_CODE:
583 case UNKNOWN_ERROR:
584 mStatusIcon = R.drawable.common_error;
585 mStatusText = R.string.auth_unknown_error_title;
586 break;
587 default:
588 Log_OC.e(TAG, "Incorrect connection checker result type: " + result.getHttpCode());
589 }
590 setResultIconAndText(mStatusIcon, mStatusText);
591 if (!mStatusCorrect)
592 findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);
593 else
594 findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);
595 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
596 }
597 }
598
599
600 public void onSavedCertificate() {
601 mAuthThread = mConnChkRunnable.retry(this, mHandler);
602 }
603
604 @Override
605 public void onFailedSavingCertificate() {
606 showDialog(DIALOG_CERT_NOT_SAVED);
607 }
608
609 }