Login page updated to get OAuth2 access token
[pub/Android/ownCloud.git] / src / com / owncloud / android / ui / activity / AuthenticatorActivity.java
1 /* ownCloud Android client application
2 * Copyright (C) 2012 Bartek Przybylski
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
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 import java.util.HashMap;
24
25 import org.json.JSONException;
26 import org.json.JSONObject;
27
28 import com.owncloud.android.AccountUtils;
29 import com.owncloud.android.authenticator.AccountAuthenticator;
30 import com.owncloud.android.authenticator.AuthenticationRunnable;
31 import com.owncloud.android.authenticator.OnAuthenticationResultListener;
32 import com.owncloud.android.authenticator.OnConnectCheckListener;
33 import com.owncloud.android.authenticator.oauth2.OAuth2GetCodeRunnable;
34 import com.owncloud.android.authenticator.oauth2.OnOAuth2GetCodeResultListener;
35 import com.owncloud.android.authenticator.oauth2.connection.ConnectorOAuth2;
36 import com.owncloud.android.authenticator.oauth2.services.OAuth2GetTokenService;
37 import com.owncloud.android.ui.dialog.SslValidatorDialog;
38 import com.owncloud.android.ui.dialog.SslValidatorDialog.OnSslValidatorListener;
39 import com.owncloud.android.network.OwnCloudClientUtils;
40 import com.owncloud.android.operations.ConnectionCheckOperation;
41 import com.owncloud.android.operations.OnRemoteOperationListener;
42 import com.owncloud.android.operations.RemoteOperation;
43 import com.owncloud.android.operations.RemoteOperationResult;
44
45 import android.accounts.Account;
46 import android.accounts.AccountAuthenticatorActivity;
47 import android.accounts.AccountManager;
48 import android.app.AlertDialog;
49 import android.app.Dialog;
50 import android.app.ProgressDialog;
51 import android.content.BroadcastReceiver;
52 import android.content.ContentResolver;
53 import android.content.Context;
54 import android.content.DialogInterface;
55 import android.content.Intent;
56 import android.content.IntentFilter;
57 import android.content.SharedPreferences;
58 import android.net.Uri;
59 import android.os.Bundle;
60 import android.os.Handler;
61 import android.preference.PreferenceManager;
62 import android.text.InputType;
63 import android.util.Log;
64 import android.view.View;
65 import android.view.View.OnClickListener;
66 import android.view.View.OnFocusChangeListener;
67 import android.view.Window;
68 import android.widget.CheckBox;
69 import android.widget.EditText;
70 import android.widget.ImageView;
71 import android.widget.TextView;
72 import com.owncloud.android.R;
73
74 import eu.alefzero.webdav.WebdavClient;
75
76 /**
77 * This Activity is used to add an ownCloud account to the App
78 *
79 * @author Bartek Przybylski
80 *
81 */
82 public class AuthenticatorActivity extends AccountAuthenticatorActivity
83 implements OnAuthenticationResultListener, OnConnectCheckListener, OnRemoteOperationListener, OnSslValidatorListener,
84 OnFocusChangeListener, OnClickListener, OnOAuth2GetCodeResultListener {
85
86 private static final int DIALOG_LOGIN_PROGRESS = 0;
87 private static final int DIALOG_SSL_VALIDATOR = 1;
88 private static final int DIALOG_CERT_NOT_SAVED = 2;
89
90 private static final String TAG = "AuthActivity";
91
92 private Thread mAuthThread;
93 private AuthenticationRunnable mAuthRunnable;
94 //private ConnectionCheckerRunnable mConnChkRunnable = null;
95 private ConnectionCheckOperation mConnChkRunnable;
96 private final Handler mHandler = new Handler();
97 private String mBaseUrl;
98
99 private static final String STATUS_TEXT = "STATUS_TEXT";
100 private static final String STATUS_ICON = "STATUS_ICON";
101 private static final String STATUS_CORRECT = "STATUS_CORRECT";
102 private static final String IS_SSL_CONN = "IS_SSL_CONN";
103 private int mStatusText, mStatusIcon;
104 private boolean mStatusCorrect, mIsSslConn;
105 private RemoteOperationResult mLastSslUntrustedServerResult;
106
107 public static final String PARAM_USERNAME = "param_Username";
108 public static final String PARAM_HOSTNAME = "param_Hostname";
109
110 // oAuth2 variables.
111 private static final int OAUTH2_LOGIN_PROGRESS = 3;
112 private static final String OAUTH2_STATUS_TEXT = "OAUTH2_STATUS_TEXT";
113 private static final String OAUTH2_STATUS_ICON = "OAUTH2_STATUS_ICON";
114 private static final String OAUTH2_CODE_RESULT = "CODE_RESULT";
115 private static final String OAUTH2_BASE_URL = "BASE_URL";
116 private static final String OAUTH2_IS_CHECKED = "OAUTH2_IS_CHECKED";
117 private Thread mOAuth2GetCodeThread;
118 private OAuth2GetCodeRunnable mOAuth2GetCodeRunnable;
119 private String oAuth2BaseUrl;
120 private TokenReceiver tokenReceiver;
121 private JSONObject codeResponseJson;
122 private int mOAuth2StatusText, mOAuth2StatusIcon;
123
124 public ConnectorOAuth2 connectorOAuth2;
125
126 // Variables used to save the on the state the contents of all fields.
127 private static final String HOST_URL_TEXT = "HOST_URL_TEXT";
128 private static final String OAUTH2_URL_TEXT = "OAUTH2_URL_TEXT";
129 private static final String ACCOUNT_USERNAME = "ACCOUNT_USERNAME";
130 private static final String ACCOUNT_PASSWORD = "ACCOUNT_PASSWORD";
131
132 // END of oAuth2 variables.
133
134 @Override
135 protected void onCreate(Bundle savedInstanceState) {
136 super.onCreate(savedInstanceState);
137 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
138 setContentView(R.layout.account_setup);
139 ImageView iv = (ImageView) findViewById(R.id.refreshButton);
140 ImageView iv2 = (ImageView) findViewById(R.id.viewPassword);
141 TextView tv = (TextView) findViewById(R.id.host_URL);
142 TextView tv2 = (TextView) findViewById(R.id.account_password);
143 // New textview to oAuth2 URL.
144 TextView tv3 = (TextView) findViewById(R.id.oAuth_URL);
145
146 if (savedInstanceState != null) {
147 mStatusIcon = savedInstanceState.getInt(STATUS_ICON);
148 mStatusText = savedInstanceState.getInt(STATUS_TEXT);
149 mStatusCorrect = savedInstanceState.getBoolean(STATUS_CORRECT);
150 mIsSslConn = savedInstanceState.getBoolean(IS_SSL_CONN);
151 setResultIconAndText(mStatusIcon, mStatusText);
152 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
153 if (!mStatusCorrect)
154 iv.setVisibility(View.VISIBLE);
155 else
156 iv.setVisibility(View.INVISIBLE);
157
158 // Getting the state of oAuth2 components.
159 mOAuth2StatusIcon = savedInstanceState.getInt(OAUTH2_STATUS_ICON);
160 mOAuth2StatusText = savedInstanceState.getInt(OAUTH2_STATUS_TEXT);
161 // We set this to true if the rotation happens when the user is validating oAuth2 user_code.
162 changeViewByOAuth2Check(savedInstanceState.getBoolean(OAUTH2_IS_CHECKED));
163 oAuth2BaseUrl = savedInstanceState.getString(OAUTH2_BASE_URL);
164 // We store a JSon object with all the data returned from oAuth2 server when we get user_code.
165 // Is better than store variable by variable. We use String object to serialize from/to it.
166 try {
167 if (savedInstanceState.containsKey(OAUTH2_CODE_RESULT)) {
168 codeResponseJson = new JSONObject(savedInstanceState.getString(OAUTH2_CODE_RESULT));
169 }
170 } catch (JSONException e) {
171 Log.e(TAG, "onCreate->JSONException: " + e.toString());
172 }
173 // END of getting the state of oAuth2 components.
174
175 // Getting contents of each field.
176 EditText hostUrl = (EditText)findViewById(R.id.host_URL);
177 hostUrl.setText(savedInstanceState.getString(HOST_URL_TEXT), TextView.BufferType.EDITABLE);
178 EditText oauth2Url = (EditText)findViewById(R.id.oAuth_URL);
179 oauth2Url.setText(savedInstanceState.getString(OAUTH2_URL_TEXT), TextView.BufferType.EDITABLE);
180 EditText accountUsername = (EditText)findViewById(R.id.account_username);
181 accountUsername.setText(savedInstanceState.getString(ACCOUNT_USERNAME), TextView.BufferType.EDITABLE);
182 EditText accountPassword = (EditText)findViewById(R.id.account_password);
183 accountPassword.setText(savedInstanceState.getString(ACCOUNT_PASSWORD), TextView.BufferType.EDITABLE);
184 // END of getting contents of each field
185
186 } else {
187 mStatusText = mStatusIcon = 0;
188 mStatusCorrect = false;
189 mIsSslConn = false;
190 }
191 iv.setOnClickListener(this);
192 iv2.setOnClickListener(this);
193 tv.setOnFocusChangeListener(this);
194 tv2.setOnFocusChangeListener(this);
195 // Setting the listener for oAuth2 URL TextView.
196 tv3.setOnFocusChangeListener(this);
197
198 super.onCreate(savedInstanceState);
199 }
200
201 @Override
202 protected void onSaveInstanceState(Bundle outState) {
203 outState.putInt(STATUS_ICON, mStatusIcon);
204 outState.putInt(STATUS_TEXT, mStatusText);
205 outState.putBoolean(STATUS_CORRECT, mStatusCorrect);
206
207 // Saving the state of oAuth2 components.
208 outState.putInt(OAUTH2_STATUS_ICON, mOAuth2StatusIcon);
209 outState.putInt(OAUTH2_STATUS_TEXT, mOAuth2StatusText);
210 CheckBox oAuth2Check = (CheckBox) findViewById(R.id.oauth_onOff_check);
211 outState.putBoolean(OAUTH2_IS_CHECKED, oAuth2Check.isChecked());
212 if (codeResponseJson != null){
213 outState.putString(OAUTH2_CODE_RESULT, codeResponseJson.toString());
214 }
215 outState.putString(OAUTH2_BASE_URL, oAuth2BaseUrl);
216 // END of saving the state of oAuth2 components.
217
218 // Saving contents of each field.
219 outState.putString(HOST_URL_TEXT,((TextView) findViewById(R.id.host_URL)).getText().toString().trim());
220 outState.putString(OAUTH2_URL_TEXT,((TextView) findViewById(R.id.oAuth_URL)).getText().toString().trim());
221 outState.putString(ACCOUNT_USERNAME,((TextView) findViewById(R.id.account_username)).getText().toString().trim());
222 outState.putString(ACCOUNT_PASSWORD,((TextView) findViewById(R.id.account_password)).getText().toString().trim());
223
224 super.onSaveInstanceState(outState);
225 }
226
227 @Override
228 protected Dialog onCreateDialog(int id) {
229 Dialog dialog = null;
230 switch (id) {
231 case DIALOG_LOGIN_PROGRESS: {
232 ProgressDialog working_dialog = new ProgressDialog(this);
233 working_dialog.setMessage(getResources().getString(
234 R.string.auth_trying_to_login));
235 working_dialog.setIndeterminate(true);
236 working_dialog.setCancelable(true);
237 working_dialog
238 .setOnCancelListener(new DialogInterface.OnCancelListener() {
239 @Override
240 public void onCancel(DialogInterface dialog) {
241 Log.i(TAG, "Login canceled");
242 if (mAuthThread != null) {
243 mAuthThread.interrupt();
244 finish();
245 }
246 }
247 });
248 dialog = working_dialog;
249 break;
250 }
251 // oAuth2 dialog. We show here to the user the URL and user_code that the user must validate in a web browser.
252 case OAUTH2_LOGIN_PROGRESS: {
253 ProgressDialog working_dialog = new ProgressDialog(this);
254 try {
255 working_dialog.setMessage(String.format(getString(R.string.oauth_code_validation_message),
256 codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL),
257 codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE)));
258 } catch (JSONException e) {
259 Log.e(TAG, "onCreateDialog->JSONException: " + e.toString());
260 }
261 working_dialog.setIndeterminate(true);
262 working_dialog.setCancelable(true);
263 working_dialog
264 .setOnCancelListener(new DialogInterface.OnCancelListener() {
265 @Override
266 public void onCancel(DialogInterface dialog) {
267 Log.i(TAG, "Login canceled");
268 if (mOAuth2GetCodeThread != null) {
269 mOAuth2GetCodeThread.interrupt();
270 finish();
271 }
272 if (tokenReceiver != null) {
273 unregisterReceiver(tokenReceiver);
274 tokenReceiver = null;
275 finish();
276 }
277 }
278 });
279 dialog = working_dialog;
280 break;
281 }
282 case DIALOG_SSL_VALIDATOR: {
283 dialog = SslValidatorDialog.newInstance(this, mLastSslUntrustedServerResult, this);
284 break;
285 }
286 case DIALOG_CERT_NOT_SAVED: {
287 AlertDialog.Builder builder = new AlertDialog.Builder(this);
288 builder.setMessage(getResources().getString(R.string.ssl_validator_not_saved));
289 builder.setCancelable(false);
290 builder.setPositiveButton(R.string.common_ok, new DialogInterface.OnClickListener() {
291 @Override
292 public void onClick(DialogInterface dialog, int which) {
293 dialog.dismiss();
294 };
295 });
296 dialog = builder.create();
297 break;
298 }
299 default:
300 Log.e(TAG, "Incorrect dialog called with id = " + id);
301 }
302 return dialog;
303 }
304
305 @Override
306 protected void onPrepareDialog(int id, Dialog dialog, Bundle args) {
307 switch (id) {
308 case DIALOG_LOGIN_PROGRESS:
309 case DIALOG_CERT_NOT_SAVED:
310 break;
311 case DIALOG_SSL_VALIDATOR: {
312 ((SslValidatorDialog)dialog).updateResult(mLastSslUntrustedServerResult);
313 break;
314 }
315 default:
316 Log.e(TAG, "Incorrect dialog called with id = " + id);
317 }
318 }
319
320 @Override
321 protected void onResume() {
322 Log.d(TAG, "onResume() start");
323 // Registering token receiver. We must listening to the service that is pooling to the oAuth server for a token.
324 if (tokenReceiver == null) {
325 IntentFilter tokenFilter = new IntentFilter(OAuth2GetTokenService.TOKEN_RECEIVED_MESSAGE);
326 tokenReceiver = new TokenReceiver();
327 this.registerReceiver(tokenReceiver,tokenFilter);
328 }
329 super.onResume();
330 }
331
332 @Override
333 protected void onPause() {
334 Log.d(TAG, "onPause() start");
335 super.onPause();
336 }
337
338
339 public void onAuthenticationResult(boolean success, String message) {
340 if (success) {
341 TextView username_text = (TextView) findViewById(R.id.account_username), password_text = (TextView) findViewById(R.id.account_password);
342
343 URL url;
344 try {
345 url = new URL(message);
346 } catch (MalformedURLException e) {
347 // should never happen
348 Log.e(getClass().getName(), "Malformed URL: " + message);
349 return;
350 }
351
352 String username = username_text.getText().toString().trim();
353 String accountName = username + "@" + url.getHost();
354 if (url.getPort() >= 0) {
355 accountName += ":" + url.getPort();
356 }
357 Account account = new Account(accountName,
358 AccountAuthenticator.ACCOUNT_TYPE);
359 AccountManager accManager = AccountManager.get(this);
360 accManager.addAccountExplicitly(account, password_text.getText()
361 .toString(), null);
362
363 // Add this account as default in the preferences, if there is none
364 // already
365 Account defaultAccount = AccountUtils
366 .getCurrentOwnCloudAccount(this);
367 if (defaultAccount == null) {
368 SharedPreferences.Editor editor = PreferenceManager
369 .getDefaultSharedPreferences(this).edit();
370 editor.putString("select_oc_account", accountName);
371 editor.commit();
372 }
373
374 final Intent intent = new Intent();
375 intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,
376 AccountAuthenticator.ACCOUNT_TYPE);
377 intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
378 intent.putExtra(AccountManager.KEY_AUTHTOKEN,
379 AccountAuthenticator.ACCOUNT_TYPE);
380 intent.putExtra(AccountManager.KEY_USERDATA, username);
381
382 accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL,
383 url.toString());
384 accManager.setUserData(account,
385 AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable
386 .getDiscoveredVersion().toString());
387
388 accManager.setUserData(account,
389 AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);
390
391 setAccountAuthenticatorResult(intent.getExtras());
392 setResult(RESULT_OK, intent);
393 Bundle bundle = new Bundle();
394 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
395 //getContentResolver().startSync(ProviderTableMeta.CONTENT_URI,
396 // bundle);
397 ContentResolver.requestSync(account, "org.owncloud", bundle);
398
399 /*
400 * if
401 * (mConnChkRunnable.getDiscoveredVersion().compareTo(OwnCloudVersion
402 * .owncloud_v2) >= 0) { Intent i = new Intent(this,
403 * ExtensionsAvailableActivity.class); startActivity(i); }
404 */
405
406 finish();
407 } else {
408 try {
409 dismissDialog(DIALOG_LOGIN_PROGRESS);
410 } catch (IllegalArgumentException e) {
411 // NOTHING TO DO ; can't find out what situation that leads to the exception in this code, but user logs signal that it happens
412 }
413 TextView tv = (TextView) findViewById(R.id.account_username);
414 tv.setError(message);
415 }
416 }
417 public void onCancelClick(View view) {
418 setResult(RESULT_CANCELED);
419 finish();
420 }
421
422 public void onOkClick(View view) {
423 String prefix = "";
424 String url = ((TextView) findViewById(R.id.host_URL)).getText()
425 .toString().trim();
426 if (mIsSslConn) {
427 prefix = "https://";
428 } else {
429 prefix = "http://";
430 }
431 if (url.toLowerCase().startsWith("http://")
432 || url.toLowerCase().startsWith("https://")) {
433 prefix = "";
434 }
435 continueConnection(prefix);
436 }
437
438 public void onRegisterClick(View view) {
439 Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse("https://owncloud.com/mobile/new"));
440 setResult(RESULT_CANCELED);
441 startActivity(register);
442 }
443
444 private void continueConnection(String prefix) {
445 String url = ((TextView) findViewById(R.id.host_URL)).getText()
446 .toString().trim();
447 String username = ((TextView) findViewById(R.id.account_username))
448 .getText().toString();
449 String password = ((TextView) findViewById(R.id.account_password))
450 .getText().toString();
451 if (url.endsWith("/"))
452 url = url.substring(0, url.length() - 1);
453
454 URL uri = null;
455 String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable
456 .getDiscoveredVersion());
457
458 if (webdav_path == null) {
459 onAuthenticationResult(false, getString(R.string.auth_bad_oc_version_title));
460 return;
461 }
462
463 try {
464 mBaseUrl = prefix + url;
465 String url_str = prefix + url + webdav_path;
466 uri = new URL(url_str);
467 } catch (MalformedURLException e) {
468 // should never happen
469 onAuthenticationResult(false, getString(R.string.auth_incorrect_address_title));
470 return;
471 }
472
473 showDialog(DIALOG_LOGIN_PROGRESS);
474 mAuthRunnable = new AuthenticationRunnable(uri, username, password, this);
475 mAuthRunnable.setOnAuthenticationResultListener(this, mHandler);
476 mAuthThread = new Thread(mAuthRunnable);
477 mAuthThread.start();
478 }
479
480 @Override
481 public void onConnectionCheckResult(ResultType type) {
482 mStatusText = mStatusIcon = 0;
483 mStatusCorrect = false;
484 String t_url = ((TextView) findViewById(R.id.host_URL)).getText()
485 .toString().trim().toLowerCase();
486
487 switch (type) {
488 case OK_SSL:
489 mIsSslConn = true;
490 mStatusIcon = android.R.drawable.ic_secure;
491 mStatusText = R.string.auth_secure_connection;
492 mStatusCorrect = true;
493 break;
494 case OK_NO_SSL:
495 mIsSslConn = false;
496 mStatusCorrect = true;
497 if (t_url.startsWith("http://") ) {
498 mStatusText = R.string.auth_connection_established;
499 mStatusIcon = R.drawable.ic_ok;
500 } else {
501 mStatusText = R.string.auth_nossl_plain_ok_title;
502 mStatusIcon = android.R.drawable.ic_partial_secure;
503 }
504 break;
505 case BAD_OC_VERSION:
506 mStatusIcon = R.drawable.common_error;
507 mStatusText = R.string.auth_bad_oc_version_title;
508 break;
509 case WRONG_CONNECTION:
510 mStatusIcon = R.drawable.common_error;
511 mStatusText = R.string.auth_wrong_connection_title;
512 break;
513 case TIMEOUT:
514 mStatusIcon = R.drawable.common_error;
515 mStatusText = R.string.auth_timeout_title;
516 break;
517 case INCORRECT_ADDRESS:
518 mStatusIcon = R.drawable.common_error;
519 mStatusText = R.string.auth_incorrect_address_title;
520 break;
521 case SSL_UNVERIFIED_SERVER:
522 mStatusIcon = R.drawable.common_error;
523 mStatusText = R.string.auth_ssl_unverified_server_title;
524 break;
525 case SSL_INIT_ERROR:
526 mStatusIcon = R.drawable.common_error;
527 mStatusText = R.string.auth_ssl_general_error_title;
528 break;
529 case HOST_NOT_AVAILABLE:
530 mStatusIcon = R.drawable.common_error;
531 mStatusText = R.string.auth_unknown_host_title;
532 break;
533 case NO_NETWORK_CONNECTION:
534 mStatusIcon = R.drawable.no_network;
535 mStatusText = R.string.auth_no_net_conn_title;
536 break;
537 case INSTANCE_NOT_CONFIGURED:
538 mStatusIcon = R.drawable.common_error;
539 mStatusText = R.string.auth_not_configured_title;
540 break;
541 case UNKNOWN_ERROR:
542 mStatusIcon = R.drawable.common_error;
543 mStatusText = R.string.auth_unknown_error_title;
544 break;
545 case FILE_NOT_FOUND:
546 mStatusIcon = R.drawable.common_error;
547 mStatusText = R.string.auth_incorrect_path_title;
548 break;
549 default:
550 Log.e(TAG, "Incorrect connection checker result type: " + type);
551 }
552 setResultIconAndText(mStatusIcon, mStatusText);
553 if (!mStatusCorrect)
554 findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);
555 else
556 findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);
557 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
558 }
559
560 @Override
561 public void onFocusChange(View view, boolean hasFocus) {
562 if (view.getId() == R.id.host_URL) {
563 if (!hasFocus) {
564 TextView tv = ((TextView) findViewById(R.id.host_URL));
565 String uri = tv.getText().toString().trim();
566 if (uri.length() != 0) {
567 setResultIconAndText(R.drawable.progress_small,
568 R.string.auth_testing_connection);
569 //mConnChkRunnable = new ConnectionCheckerRunnable(uri, this);
570 mConnChkRunnable = new ConnectionCheckOperation(uri, this);
571 //mConnChkRunnable.setListener(this, mHandler);
572 //mAuthThread = new Thread(mConnChkRunnable);
573 //mAuthThread.start();
574 WebdavClient client = OwnCloudClientUtils.createOwnCloudClient(Uri.parse(uri), this);
575 mAuthThread = mConnChkRunnable.execute(client, this, mHandler);
576 } else {
577 findViewById(R.id.refreshButton).setVisibility(
578 View.INVISIBLE);
579 setResultIconAndText(0, 0);
580 }
581 } else {
582 // avoids that the 'connect' button can be clicked if the test was previously passed
583 findViewById(R.id.buttonOK).setEnabled(false);
584 }
585 } else if (view.getId() == R.id.account_password) {
586 ImageView iv = (ImageView) findViewById(R.id.viewPassword);
587 if (hasFocus) {
588 iv.setVisibility(View.VISIBLE);
589 } else {
590 TextView v = (TextView) findViewById(R.id.account_password);
591 int input_type = InputType.TYPE_CLASS_TEXT
592 | InputType.TYPE_TEXT_VARIATION_PASSWORD;
593 v.setInputType(input_type);
594 iv.setVisibility(View.INVISIBLE);
595 }
596 // If the focusChange occurs on the oAuth2 URL field, we do this.
597 } else if (view.getId() == R.id.oAuth_URL) {
598 if (!hasFocus) {
599 TextView tv3 = ((TextView) findViewById(R.id.oAuth_URL));
600 // We get the URL of oAuth2 server.
601 oAuth2BaseUrl = tv3.getText().toString().trim();
602 if (oAuth2BaseUrl.length() != 0) {
603 // We start a thread to get user_code from the oAuth2 server.
604 setOAuth2ResultIconAndText(R.drawable.progress_small, R.string.oauth_login_connection);
605 mOAuth2GetCodeRunnable = new OAuth2GetCodeRunnable(oAuth2BaseUrl, this);
606 mOAuth2GetCodeRunnable.setListener(this, mHandler);
607 mOAuth2GetCodeThread = new Thread(mOAuth2GetCodeRunnable);
608 mOAuth2GetCodeThread.start();
609 } else {
610 findViewById(R.id.refreshButton).setVisibility(
611 View.INVISIBLE);
612 setOAuth2ResultIconAndText(0, 0);
613 }
614 } else {
615 // avoids that the 'connect' button can be clicked if the test was previously passed
616 findViewById(R.id.buttonOK).setEnabled(false);
617 }
618 }
619 }
620
621 private void setResultIconAndText(int drawable_id, int text_id) {
622 ImageView iv = (ImageView) findViewById(R.id.action_indicator);
623 TextView tv = (TextView) findViewById(R.id.status_text);
624
625 if (drawable_id == 0 && text_id == 0) {
626 iv.setVisibility(View.INVISIBLE);
627 tv.setVisibility(View.INVISIBLE);
628 } else {
629 iv.setImageResource(drawable_id);
630 tv.setText(text_id);
631 iv.setVisibility(View.VISIBLE);
632 tv.setVisibility(View.VISIBLE);
633 }
634 }
635
636 @Override
637 public void onClick(View v) {
638 if (v.getId() == R.id.refreshButton) {
639 onFocusChange(findViewById(R.id.host_URL), false);
640 } else if (v.getId() == R.id.viewPassword) {
641 TextView view = (TextView) findViewById(R.id.account_password);
642 int input_type = InputType.TYPE_CLASS_TEXT
643 | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
644 view.setInputType(input_type);
645 }
646 }
647
648 @Override protected void onDestroy() {
649 // We must stop the service thats it's pooling to oAuth2 server for a token.
650 Intent tokenService = new Intent(this, OAuth2GetTokenService.class);
651 stopService(tokenService);
652
653 // We stop listening the result of the pooling service.
654 if (tokenReceiver != null) {
655 unregisterReceiver(tokenReceiver);
656 tokenReceiver = null;
657 finish();
658 }
659
660 super.onDestroy();
661 }
662
663 // Controlling the oAuth2 checkbox on the activity: hide and show widgets.
664 public void onOff_check_Click(View view) {
665 CheckBox oAuth2Check = (CheckBox)view;
666 changeViewByOAuth2Check(oAuth2Check.isChecked());
667
668 }
669
670 public void changeViewByOAuth2Check(Boolean checked) {
671
672 EditText oAuth2Url = (EditText) findViewById(R.id.oAuth_URL);
673 EditText accountUsername = (EditText) findViewById(R.id.account_username);
674 EditText accountPassword = (EditText) findViewById(R.id.account_password);
675 ImageView viewPassword = (ImageView) findViewById(R.id.viewPassword);
676 ImageView auth2ActionIndicator = (ImageView) findViewById(R.id.auth2_action_indicator);
677 TextView oauth2StatusText = (TextView) findViewById(R.id.oauth2_status_text);
678
679 if (checked) {
680 oAuth2Url.setVisibility(View.VISIBLE);
681 accountUsername.setVisibility(View.GONE);
682 accountPassword.setVisibility(View.GONE);
683 viewPassword.setVisibility(View.GONE);
684 auth2ActionIndicator.setVisibility(View.INVISIBLE);
685 oauth2StatusText.setVisibility(View.INVISIBLE);
686 } else {
687 oAuth2Url.setVisibility(View.GONE);
688 accountUsername.setVisibility(View.VISIBLE);
689 accountPassword.setVisibility(View.VISIBLE);
690 viewPassword.setVisibility(View.INVISIBLE);
691 auth2ActionIndicator.setVisibility(View.GONE);
692 oauth2StatusText.setVisibility(View.GONE);
693 }
694
695 }
696
697 // Controlling the oAuth2 result of server connection.
698 private void setOAuth2ResultIconAndText(int drawable_id, int text_id) {
699 ImageView iv = (ImageView) findViewById(R.id.auth2_action_indicator);
700 TextView tv = (TextView) findViewById(R.id.oauth2_status_text);
701
702 if (drawable_id == 0 && text_id == 0) {
703 iv.setVisibility(View.INVISIBLE);
704 tv.setVisibility(View.INVISIBLE);
705 } else {
706 iv.setImageResource(drawable_id);
707 tv.setText(text_id);
708 iv.setVisibility(View.VISIBLE);
709 tv.setVisibility(View.VISIBLE);
710 }
711 }
712
713 // Results from the first call to oAuth2 server : getting the user_code and verification_url.
714 @Override
715 public void onOAuth2GetCodeResult(ResultOAuthType type, JSONObject responseJson) {
716 if ((type == ResultOAuthType.OK_SSL)||(type == ResultOAuthType.OK_NO_SSL)) {
717 codeResponseJson = responseJson;
718 startOAuth2Authentication();
719 } else if (type == ResultOAuthType.HOST_NOT_AVAILABLE) {
720 setOAuth2ResultIconAndText(R.drawable.common_error, R.string.oauth_connection_url_unavailable);
721 }
722 }
723
724 // If the results of getting the user_code and verification_url are OK, we get the received data and we start
725 // the pooling service to oAuth2 server to get a valid token.
726 private void startOAuth2Authentication () {
727 String deviceCode = null;
728 String verificationUrl = null;
729 String userCode = null;
730 int expiresIn = -1;
731 int interval = -1;
732
733 Log.d(TAG, "ResponseOAuth2->" + codeResponseJson.toString());
734
735 try {
736 // We get data that we must show to the user or we will use internally.
737 verificationUrl = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_VERIFICATION_URL);
738 userCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_USER_CODE);
739 expiresIn = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_EXPIRES_IN);
740
741 // And we get data that we must use to get a token.
742 deviceCode = codeResponseJson.getString(OAuth2GetCodeRunnable.CODE_DEVICE_CODE);
743 interval = codeResponseJson.getInt(OAuth2GetCodeRunnable.CODE_INTERVAL);
744
745 } catch (JSONException e) {
746 Log.e(TAG, "Exception accesing data in Json object" + e.toString());
747 }
748
749 // Updating status widget to OK.
750 setOAuth2ResultIconAndText(R.drawable.ic_ok, R.string.auth_connection_established);
751
752 // Showing the dialog with instructions for the user.
753 showDialog(OAUTH2_LOGIN_PROGRESS);
754
755 // Loggin all the data.
756 Log.d(TAG, "verificationUrl->" + verificationUrl);
757 Log.d(TAG, "userCode->" + userCode);
758 Log.d(TAG, "deviceCode->" + deviceCode);
759 Log.d(TAG, "expiresIn->" + expiresIn);
760 Log.d(TAG, "interval->" + interval);
761
762 // Starting the pooling service.
763 try {
764 Intent tokenService = new Intent(this, OAuth2GetTokenService.class);
765 tokenService.putExtra(OAuth2GetTokenService.TOKEN_BASE_URI, oAuth2BaseUrl);
766 tokenService.putExtra(OAuth2GetTokenService.TOKEN_DEVICE_CODE, deviceCode);
767 tokenService.putExtra(OAuth2GetTokenService.TOKEN_INTERVAL, interval);
768
769 startService(tokenService);
770 }
771 catch (Exception e) {
772 Log.e(TAG, "tokenService creation problem :", e);
773 }
774 }
775
776 // We get data from the oAuth2 token service with this broadcast receiver.
777 private class TokenReceiver extends BroadcastReceiver {
778 /**
779 * The token is received.
780 * @author
781 * {@link BroadcastReceiver} to enable oAuth2 token receiving.
782 */
783 @Override
784 public void onReceive(Context context, Intent intent) {
785 @SuppressWarnings("unchecked")
786 HashMap<String, String> tokenResponse = (HashMap<String, String>)intent.getExtras().get(OAuth2GetTokenService.TOKEN_RECEIVED_DATA);
787 Log.d(TAG, "TokenReceiver->" + tokenResponse.get(OAuth2GetTokenService.TOKEN_ACCESS_TOKEN));
788 dismissDialog(OAUTH2_LOGIN_PROGRESS);
789
790 }
791 }
792
793 @Override
794 public void onRemoteOperationFinish(RemoteOperation operation, RemoteOperationResult result) {
795 if (operation.equals(mConnChkRunnable)) {
796
797 mStatusText = mStatusIcon = 0;
798 mStatusCorrect = false;
799 String t_url = ((TextView) findViewById(R.id.host_URL)).getText()
800 .toString().trim().toLowerCase();
801
802 switch (result.getCode()) {
803 case OK_SSL:
804 mIsSslConn = true;
805 mStatusIcon = android.R.drawable.ic_secure;
806 mStatusText = R.string.auth_secure_connection;
807 mStatusCorrect = true;
808 break;
809
810 case OK_NO_SSL:
811 case OK:
812 mIsSslConn = false;
813 mStatusCorrect = true;
814 if (t_url.startsWith("http://") ) {
815 mStatusText = R.string.auth_connection_established;
816 mStatusIcon = R.drawable.ic_ok;
817 } else {
818 mStatusText = R.string.auth_nossl_plain_ok_title;
819 mStatusIcon = android.R.drawable.ic_partial_secure;
820 }
821 break;
822
823
824 case BAD_OC_VERSION:
825 mStatusIcon = R.drawable.common_error;
826 mStatusText = R.string.auth_bad_oc_version_title;
827 break;
828 case WRONG_CONNECTION:
829 mStatusIcon = R.drawable.common_error;
830 mStatusText = R.string.auth_wrong_connection_title;
831 break;
832 case TIMEOUT:
833 mStatusIcon = R.drawable.common_error;
834 mStatusText = R.string.auth_timeout_title;
835 break;
836 case INCORRECT_ADDRESS:
837 mStatusIcon = R.drawable.common_error;
838 mStatusText = R.string.auth_incorrect_address_title;
839 break;
840
841 case SSL_RECOVERABLE_PEER_UNVERIFIED:
842 mStatusIcon = R.drawable.common_error;
843 mStatusText = R.string.auth_ssl_unverified_server_title;
844 mLastSslUntrustedServerResult = result;
845 showDialog(DIALOG_SSL_VALIDATOR);
846 break;
847
848 case SSL_ERROR:
849 mStatusIcon = R.drawable.common_error;
850 mStatusText = R.string.auth_ssl_general_error_title;
851 break;
852
853 case HOST_NOT_AVAILABLE:
854 mStatusIcon = R.drawable.common_error;
855 mStatusText = R.string.auth_unknown_host_title;
856 break;
857 case NO_NETWORK_CONNECTION:
858 mStatusIcon = R.drawable.no_network;
859 mStatusText = R.string.auth_no_net_conn_title;
860 break;
861 case INSTANCE_NOT_CONFIGURED:
862 mStatusIcon = R.drawable.common_error;
863 mStatusText = R.string.auth_not_configured_title;
864 break;
865 case FILE_NOT_FOUND:
866 mStatusIcon = R.drawable.common_error;
867 mStatusText = R.string.auth_incorrect_path_title;
868 break;
869 case UNHANDLED_HTTP_CODE:
870 case UNKNOWN_ERROR:
871 mStatusIcon = R.drawable.common_error;
872 mStatusText = R.string.auth_unknown_error_title;
873 break;
874 default:
875 Log.e(TAG, "Incorrect connection checker result type: " + result.getHttpCode());
876 }
877 setResultIconAndText(mStatusIcon, mStatusText);
878 if (!mStatusCorrect)
879 findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);
880 else
881 findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);
882 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
883 }
884 }
885
886
887 public void onSavedCertificate() {
888 mAuthThread = mConnChkRunnable.retry(this, mHandler);
889 }
890
891 @Override
892 public void onFailedSavingCertificate() {
893 showDialog(DIALOG_CERT_NOT_SAVED);
894 }
895
896 }