a1098094dbbac36d77499406e25826cb0fae1cd9
[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.net.URLEncoder;
24
25 import com.owncloud.android.AccountUtils;
26 import com.owncloud.android.authenticator.AccountAuthenticator;
27 import com.owncloud.android.authenticator.AuthenticationRunnable;
28 import com.owncloud.android.authenticator.ConnectionCheckerRunnable;
29 import com.owncloud.android.authenticator.OnAuthenticationResultListener;
30 import com.owncloud.android.authenticator.OnConnectCheckListener;
31 import com.owncloud.android.db.ProviderMeta.ProviderTableMeta;
32 import com.owncloud.android.extensions.ExtensionsAvailableActivity;
33 import com.owncloud.android.utils.OwnCloudVersion;
34
35 import android.accounts.Account;
36 import android.accounts.AccountAuthenticatorActivity;
37 import android.accounts.AccountManager;
38 import android.app.Dialog;
39 import android.app.ProgressDialog;
40 import android.content.ContentResolver;
41 import android.content.DialogInterface;
42 import android.content.Intent;
43 import android.content.SharedPreferences;
44 import android.net.Uri;
45 import android.os.Bundle;
46 import android.os.Handler;
47 import android.preference.PreferenceManager;
48 import android.text.InputType;
49 import android.util.Log;
50 import android.view.View;
51 import android.view.View.OnClickListener;
52 import android.view.View.OnFocusChangeListener;
53 import android.view.Window;
54 import android.widget.ImageView;
55 import android.widget.TextView;
56 import com.owncloud.android.R;
57
58 /**
59 * This Activity is used to add an ownCloud account to the App
60 *
61 * @author Bartek Przybylski
62 *
63 */
64 public class AuthenticatorActivity extends AccountAuthenticatorActivity
65 implements OnAuthenticationResultListener, OnConnectCheckListener,
66 OnFocusChangeListener, OnClickListener {
67 private static final int DIALOG_LOGIN_PROGRESS = 0;
68
69 private static final String TAG = "AuthActivity";
70
71 private Thread mAuthThread;
72 private AuthenticationRunnable mAuthRunnable;
73 private ConnectionCheckerRunnable mConnChkRunnable;
74 private final Handler mHandler = new Handler();
75 private String mBaseUrl;
76
77 private static final String STATUS_TEXT = "STATUS_TEXT";
78 private static final String STATUS_ICON = "STATUS_ICON";
79 private static final String STATUS_CORRECT = "STATUS_CORRECT";
80 private static final String IS_SSL_CONN = "IS_SSL_CONN";
81 private int mStatusText, mStatusIcon;
82 private boolean mStatusCorrect, mIsSslConn;
83
84 public static final String PARAM_USERNAME = "param_Username";
85 public static final String PARAM_HOSTNAME = "param_Hostname";
86
87 @Override
88 protected void onCreate(Bundle savedInstanceState) {
89 super.onCreate(savedInstanceState);
90 getWindow().requestFeature(Window.FEATURE_NO_TITLE);
91 setContentView(R.layout.account_setup);
92 ImageView iv = (ImageView) findViewById(R.id.refreshButton);
93 ImageView iv2 = (ImageView) findViewById(R.id.viewPassword);
94 TextView tv = (TextView) findViewById(R.id.host_URL);
95 TextView tv2 = (TextView) findViewById(R.id.account_password);
96
97 if (savedInstanceState != null) {
98 mStatusIcon = savedInstanceState.getInt(STATUS_ICON);
99 mStatusText = savedInstanceState.getInt(STATUS_TEXT);
100 mStatusCorrect = savedInstanceState.getBoolean(STATUS_CORRECT);
101 mIsSslConn = savedInstanceState.getBoolean(IS_SSL_CONN);
102 setResultIconAndText(mStatusIcon, mStatusText);
103 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
104 if (!mStatusCorrect)
105 iv.setVisibility(View.VISIBLE);
106 else
107 iv.setVisibility(View.INVISIBLE);
108
109 } else {
110 mStatusText = mStatusIcon = 0;
111 mStatusCorrect = false;
112 mIsSslConn = false;
113 }
114 iv.setOnClickListener(this);
115 iv2.setOnClickListener(this);
116 tv.setOnFocusChangeListener(this);
117 tv2.setOnFocusChangeListener(this);
118 }
119
120 @Override
121 protected void onSaveInstanceState(Bundle outState) {
122 outState.putInt(STATUS_ICON, mStatusIcon);
123 outState.putInt(STATUS_TEXT, mStatusText);
124 outState.putBoolean(STATUS_CORRECT, mStatusCorrect);
125 super.onSaveInstanceState(outState);
126 }
127
128 @Override
129 protected Dialog onCreateDialog(int id) {
130 Dialog dialog = null;
131 switch (id) {
132 case DIALOG_LOGIN_PROGRESS: {
133 ProgressDialog working_dialog = new ProgressDialog(this);
134 working_dialog.setMessage(getResources().getString(
135 R.string.auth_trying_to_login));
136 working_dialog.setIndeterminate(true);
137 working_dialog.setCancelable(true);
138 working_dialog
139 .setOnCancelListener(new DialogInterface.OnCancelListener() {
140 @Override
141 public void onCancel(DialogInterface dialog) {
142 Log.i(TAG, "Login canceled");
143 if (mAuthThread != null) {
144 mAuthThread.interrupt();
145 finish();
146 }
147 }
148 });
149 dialog = working_dialog;
150 break;
151 }
152 default:
153 Log.e(TAG, "Incorrect dialog called with id = " + id);
154 }
155 return dialog;
156 }
157
158 public void onAuthenticationResult(boolean success, String message) {
159 if (success) {
160 TextView username_text = (TextView) findViewById(R.id.account_username), password_text = (TextView) findViewById(R.id.account_password);
161
162 URL url;
163 try {
164 url = new URL(message);
165 } catch (MalformedURLException e) {
166 // should never happen
167 Log.e(getClass().getName(), "Malformed URL: " + message);
168 return;
169 }
170
171 String username = username_text.getText().toString().trim();
172 String accountName = username + "@" + url.getHost();
173 if (url.getPort() >= 0) {
174 accountName += ":" + url.getPort();
175 }
176 Account account = new Account(accountName,
177 AccountAuthenticator.ACCOUNT_TYPE);
178 AccountManager accManager = AccountManager.get(this);
179 accManager.addAccountExplicitly(account, password_text.getText()
180 .toString(), null);
181
182 // Add this account as default in the preferences, if there is none
183 // already
184 Account defaultAccount = AccountUtils
185 .getCurrentOwnCloudAccount(this);
186 if (defaultAccount == null) {
187 SharedPreferences.Editor editor = PreferenceManager
188 .getDefaultSharedPreferences(this).edit();
189 editor.putString("select_oc_account", accountName);
190 editor.commit();
191 }
192
193 final Intent intent = new Intent();
194 intent.putExtra(AccountManager.KEY_ACCOUNT_TYPE,
195 AccountAuthenticator.ACCOUNT_TYPE);
196 intent.putExtra(AccountManager.KEY_ACCOUNT_NAME, account.name);
197 intent.putExtra(AccountManager.KEY_AUTHTOKEN,
198 AccountAuthenticator.ACCOUNT_TYPE);
199 intent.putExtra(AccountManager.KEY_USERDATA, username);
200
201 accManager.setUserData(account, AccountAuthenticator.KEY_OC_URL,
202 url.toString());
203 accManager.setUserData(account,
204 AccountAuthenticator.KEY_OC_VERSION, mConnChkRunnable
205 .getDiscoveredVersion().toString());
206 accManager.setUserData(account,
207 AccountAuthenticator.KEY_OC_BASE_URL, mBaseUrl);
208
209 setAccountAuthenticatorResult(intent.getExtras());
210 setResult(RESULT_OK, intent);
211 Bundle bundle = new Bundle();
212 bundle.putBoolean(ContentResolver.SYNC_EXTRAS_MANUAL, true);
213 //getContentResolver().startSync(ProviderTableMeta.CONTENT_URI,
214 // bundle);
215 ContentResolver.requestSync(account, "org.owncloud", bundle);
216
217 /*
218 * if
219 * (mConnChkRunnable.getDiscoveredVersion().compareTo(OwnCloudVersion
220 * .owncloud_v2) >= 0) { Intent i = new Intent(this,
221 * ExtensionsAvailableActivity.class); startActivity(i); }
222 */
223
224 finish();
225 } else {
226 dismissDialog(DIALOG_LOGIN_PROGRESS);
227 TextView tv = (TextView) findViewById(R.id.account_username);
228 tv.setError(message);
229 }
230 }
231 public void onCancelClick(View view) {
232 setResult(RESULT_CANCELED);
233 finish();
234 }
235
236 public void onOkClick(View view) {
237 String prefix = "";
238 String url = ((TextView) findViewById(R.id.host_URL)).getText()
239 .toString().trim();
240 if (mIsSslConn) {
241 prefix = "https://";
242 } else {
243 prefix = "http://";
244 }
245 if (url.toLowerCase().startsWith("http://")
246 || url.toLowerCase().startsWith("https://")) {
247 prefix = "";
248 }
249 continueConnection(prefix);
250 }
251
252 public void onRegisterClick(View view) {
253 Intent register = new Intent(Intent.ACTION_VIEW, Uri.parse("https://owncloud.com/mobile/new"));
254 setResult(RESULT_CANCELED);
255 startActivity(register);
256 }
257
258 private void continueConnection(String prefix) {
259 String url = ((TextView) findViewById(R.id.host_URL)).getText()
260 .toString().trim();
261 String username = ((TextView) findViewById(R.id.account_username))
262 .getText().toString();
263 String password = ((TextView) findViewById(R.id.account_password))
264 .getText().toString();
265 if (url.endsWith("/"))
266 url = url.substring(0, url.length() - 1);
267
268 URL uri = null;
269 String webdav_path = AccountUtils.getWebdavPath(mConnChkRunnable
270 .getDiscoveredVersion());
271
272 try {
273 mBaseUrl = prefix + url;
274 String url_str = prefix + url + webdav_path;
275 uri = new URL(url_str);
276 } catch (MalformedURLException e) {
277 // should not happen
278 e.printStackTrace();
279 }
280
281 showDialog(DIALOG_LOGIN_PROGRESS);
282 mAuthRunnable = new AuthenticationRunnable(uri, username, password);
283 mAuthRunnable.setOnAuthenticationResultListener(this, mHandler);
284 mAuthThread = new Thread(mAuthRunnable);
285 mAuthThread.start();
286 }
287
288 @Override
289 public void onConnectionCheckResult(ResultType type) {
290 mStatusText = mStatusIcon = 0;
291 mStatusCorrect = false;
292 String t_url = ((TextView) findViewById(R.id.host_URL)).getText()
293 .toString().trim().toLowerCase();
294
295 switch (type) {
296 case OK_SSL:
297 mIsSslConn = true;
298 mStatusIcon = android.R.drawable.ic_secure;
299 mStatusText = R.string.auth_secure_connection;
300 mStatusCorrect = true;
301 break;
302 case OK_NO_SSL:
303 mIsSslConn = false;
304 mStatusCorrect = true;
305 if (t_url.startsWith("http://") ) {
306 mStatusText = R.string.auth_connection_established;
307 mStatusIcon = R.drawable.ic_ok;
308 } else {
309 mStatusText = R.string.auth_nossl_plain_ok_title;
310 mStatusIcon = android.R.drawable.ic_partial_secure;
311 }
312 break;
313 case BAD_OC_VERSION:
314 mStatusIcon = R.drawable.common_error;
315 mStatusText = R.string.auth_bad_oc_version_title;
316 break;
317 case WRONG_CONNECTION:
318 mStatusIcon = R.drawable.common_error;
319 mStatusText = R.string.auth_wrong_connection_title;
320 break;
321 case TIMEOUT:
322 mStatusIcon = R.drawable.common_error;
323 mStatusText = R.string.auth_timeout_title;
324 break;
325 case INCORRECT_ADDRESS:
326 mStatusIcon = R.drawable.common_error;
327 mStatusText = R.string.auth_incorrect_address_title;
328 break;
329 case SSL_UNVERIFIED_SERVER:
330 mStatusIcon = R.drawable.common_error;
331 mStatusText = R.string.auth_ssl_unverified_server_title;
332 break;
333 case SSL_INIT_ERROR:
334 mStatusIcon = R.drawable.common_error;
335 mStatusText = R.string.auth_ssl_general_error_title;
336 break;
337 case HOST_NOT_AVAILABLE:
338 mStatusIcon = R.drawable.common_error;
339 mStatusText = R.string.auth_unknown_host_title;
340 break;
341 case NO_NETWORK_CONNECTION:
342 mStatusIcon = R.drawable.no_network;
343 mStatusText = R.string.auth_no_net_conn_title;
344 break;
345 case INSTANCE_NOT_CONFIGURED:
346 mStatusIcon = R.drawable.common_error;
347 mStatusText = R.string.auth_not_configured_title;
348 break;
349 case UNKNOWN_ERROR:
350 mStatusIcon = R.drawable.common_error;
351 mStatusText = R.string.auth_unknown_error_title;
352 break;
353 case FILE_NOT_FOUND:
354 mStatusIcon = R.drawable.common_error;
355 mStatusText = R.string.auth_incorrect_path_title;
356 break;
357 default:
358 Log.e(TAG, "Incorrect connection checker result type: " + type);
359 }
360 setResultIconAndText(mStatusIcon, mStatusText);
361 if (!mStatusCorrect)
362 findViewById(R.id.refreshButton).setVisibility(View.VISIBLE);
363 else
364 findViewById(R.id.refreshButton).setVisibility(View.INVISIBLE);
365 findViewById(R.id.buttonOK).setEnabled(mStatusCorrect);
366 }
367
368 @Override
369 public void onFocusChange(View view, boolean hasFocus) {
370 if (view.getId() == R.id.host_URL) {
371 if (!hasFocus) {
372 TextView tv = ((TextView) findViewById(R.id.host_URL));
373 String uri = tv.getText().toString().trim();
374 if (uri.length() != 0) {
375 setResultIconAndText(R.drawable.progress_small,
376 R.string.auth_testing_connection);
377 findViewById(R.id.buttonOK).setEnabled(false); // avoid connect can be clicked if the test was previously passed
378 mConnChkRunnable = new ConnectionCheckerRunnable(uri, this);
379 mConnChkRunnable.setListener(this, mHandler);
380 mAuthThread = new Thread(mConnChkRunnable);
381 mAuthThread.start();
382 } else {
383 findViewById(R.id.refreshButton).setVisibility(
384 View.INVISIBLE);
385 setResultIconAndText(0, 0);
386 }
387 }
388 } else if (view.getId() == R.id.account_password) {
389 ImageView iv = (ImageView) findViewById(R.id.viewPassword);
390 if (hasFocus) {
391 iv.setVisibility(View.VISIBLE);
392 } else {
393 TextView v = (TextView) findViewById(R.id.account_password);
394 int input_type = InputType.TYPE_CLASS_TEXT
395 | InputType.TYPE_TEXT_VARIATION_PASSWORD;
396 v.setInputType(input_type);
397 iv.setVisibility(View.INVISIBLE);
398 }
399 }
400 }
401
402 private void setResultIconAndText(int drawable_id, int text_id) {
403 ImageView iv = (ImageView) findViewById(R.id.action_indicator);
404 TextView tv = (TextView) findViewById(R.id.status_text);
405
406 if (drawable_id == 0 && text_id == 0) {
407 iv.setVisibility(View.INVISIBLE);
408 tv.setVisibility(View.INVISIBLE);
409 } else {
410 iv.setImageResource(drawable_id);
411 tv.setText(text_id);
412 iv.setVisibility(View.VISIBLE);
413 tv.setVisibility(View.VISIBLE);
414 }
415 }
416
417 @Override
418 public void onClick(View v) {
419 if (v.getId() == R.id.refreshButton) {
420 onFocusChange(findViewById(R.id.host_URL), false);
421 } else if (v.getId() == R.id.viewPassword) {
422 TextView view = (TextView) findViewById(R.id.account_password);
423 int input_type = InputType.TYPE_CLASS_TEXT
424 | InputType.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD;
425 view.setInputType(input_type);
426 }
427 }
428 }