1 /* ownCloud Android client application 
   2  *   Copyright (C) 2012  Bartek Przybylski 
   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. 
   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. 
  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/>. 
  19 package eu
.alefzero
.owncloud
.ui
.activity
; 
  21 import java
.net
.MalformedURLException
; 
  24 import android
.accounts
.Account
; 
  25 import android
.accounts
.AccountAuthenticatorActivity
; 
  26 import android
.accounts
.AccountManager
; 
  27 import android
.app
.Dialog
; 
  28 import android
.app
.ProgressDialog
; 
  29 import android
.content
.ContentResolver
; 
  30 import android
.content
.DialogInterface
; 
  31 import android
.content
.Intent
; 
  32 import android
.content
.SharedPreferences
; 
  33 import android
.os
.Bundle
; 
  34 import android
.os
.Handler
; 
  35 import android
.preference
.PreferenceManager
; 
  36 import android
.text
.InputType
; 
  37 import android
.util
.Log
; 
  38 import android
.view
.View
; 
  39 import android
.view
.View
.OnClickListener
; 
  40 import android
.view
.View
.OnFocusChangeListener
; 
  41 import android
.view
.Window
; 
  42 import android
.widget
.ImageView
; 
  43 import android
.widget
.TextView
; 
  44 import eu
.alefzero
.owncloud
.AccountUtils
; 
  45 import eu
.alefzero
.owncloud
.R
; 
  46 import eu
.alefzero
.owncloud
.authenticator
.AccountAuthenticator
; 
  47 import eu
.alefzero
.owncloud
.authenticator
.AuthenticationRunnable
; 
  48 import eu
.alefzero
.owncloud
.authenticator
.ConnectionCheckerRunnable
; 
  49 import eu
.alefzero
.owncloud
.authenticator
.OnAuthenticationResultListener
; 
  50 import eu
.alefzero
.owncloud
.authenticator
.OnConnectCheckListener
; 
  51 import eu
.alefzero
.owncloud
.db
.ProviderMeta
.ProviderTableMeta
; 
  52 import eu
.alefzero
.owncloud
.extensions
.ExtensionsAvailableActivity
; 
  53 import eu
.alefzero
.owncloud
.utils
.OwnCloudVersion
; 
  56  * This Activity is used to add an ownCloud account to the App 
  58  * @author Bartek Przybylski 
  61 public class AuthenticatorActivity 
extends AccountAuthenticatorActivity
 
  62         implements OnAuthenticationResultListener
, OnConnectCheckListener
, 
  63         OnFocusChangeListener
, OnClickListener 
{ 
  64     private static final int DIALOG_LOGIN_PROGRESS 
= 0; 
  66     private static final String TAG 
= "AuthActivity"; 
  68     private Thread mAuthThread
; 
  69     private AuthenticationRunnable mAuthRunnable
; 
  70     private ConnectionCheckerRunnable mConnChkRunnable
; 
  71     private final Handler mHandler 
= new Handler(); 
  72     private String mBaseUrl
; 
  74     private static final String STATUS_TEXT 
= "STATUS_TEXT"; 
  75     private static final String STATUS_ICON 
= "STATUS_ICON"; 
  76     private static final String STATUS_CORRECT 
= "STATUS_CORRECT"; 
  77     private static final String IS_SSL_CONN 
= "IS_SSL_CONN"; 
  78     private int mStatusText
, mStatusIcon
; 
  79     private boolean mStatusCorrect
, mIsSslConn
; 
  81     public static final String PARAM_USERNAME 
= "param_Username"; 
  82     public static final String PARAM_HOSTNAME 
= "param_Hostname"; 
  85     protected void onCreate(Bundle savedInstanceState
) { 
  86         super.onCreate(savedInstanceState
); 
  87         getWindow().requestFeature(Window
.FEATURE_NO_TITLE
); 
  88         setContentView(R
.layout
.account_setup
); 
  89         ImageView iv 
= (ImageView
) findViewById(R
.id
.refreshButton
); 
  90         ImageView iv2 
= (ImageView
) findViewById(R
.id
.viewPassword
); 
  91         TextView tv 
= (TextView
) findViewById(R
.id
.host_URL
); 
  92         TextView tv2 
= (TextView
) findViewById(R
.id
.account_password
); 
  94         if (savedInstanceState 
!= null
) { 
  95             mStatusIcon 
= savedInstanceState
.getInt(STATUS_ICON
); 
  96             mStatusText 
= savedInstanceState
.getInt(STATUS_TEXT
); 
  97             mStatusCorrect 
= savedInstanceState
.getBoolean(STATUS_CORRECT
); 
  98             mIsSslConn 
= savedInstanceState
.getBoolean(IS_SSL_CONN
); 
  99             setResultIconAndText(mStatusIcon
, mStatusText
); 
 100             findViewById(R
.id
.buttonOK
).setEnabled(mStatusCorrect
); 
 102                 iv
.setVisibility(View
.VISIBLE
); 
 104                 iv
.setVisibility(View
.INVISIBLE
); 
 107             mStatusText 
= mStatusIcon 
= 0; 
 108             mStatusCorrect 
= false
; 
 111         iv
.setOnClickListener(this); 
 112         iv2
.setOnClickListener(this); 
 113         tv
.setOnFocusChangeListener(this); 
 114         tv2
.setOnFocusChangeListener(this); 
 118     protected void onSaveInstanceState(Bundle outState
) { 
 119         outState
.putInt(STATUS_ICON
, mStatusIcon
); 
 120         outState
.putInt(STATUS_TEXT
, mStatusText
); 
 121         outState
.putBoolean(STATUS_CORRECT
, mStatusCorrect
); 
 122         super.onSaveInstanceState(outState
); 
 126     protected Dialog 
onCreateDialog(int id
) { 
 127         Dialog dialog 
= null
; 
 129         case DIALOG_LOGIN_PROGRESS
: { 
 130             ProgressDialog working_dialog 
= new ProgressDialog(this); 
 131             working_dialog
.setMessage(getResources().getString( 
 132                     R
.string
.auth_trying_to_login
)); 
 133             working_dialog
.setIndeterminate(true
); 
 134             working_dialog
.setCancelable(true
); 
 136                     .setOnCancelListener(new DialogInterface
.OnCancelListener() { 
 138                         public void onCancel(DialogInterface dialog
) { 
 139                             Log
.i(TAG
, "Login canceled"); 
 140                             if (mAuthThread 
!= null
) { 
 141                                 mAuthThread
.interrupt(); 
 146             dialog 
= working_dialog
; 
 150             Log
.e(TAG
, "Incorrect dialog called with id = " + id
); 
 155     public void onAuthenticationResult(boolean success
, String message
) { 
 157             TextView username_text 
= (TextView
) findViewById(R
.id
.account_username
), password_text 
= (TextView
) findViewById(R
.id
.account_password
); 
 161                 url 
= new URL(message
); 
 162             } catch (MalformedURLException e
) { 
 163                 // should never happen 
 164                 Log
.e(getClass().getName(), "Malformed URL: " + message
); 
 168             String username 
= username_text
.getText().toString().trim(); 
 169             String accountName 
= username 
+ "@" + url
.getHost(); 
 170             Account account 
= new Account(accountName
, 
 171                     AccountAuthenticator
.ACCOUNT_TYPE
); 
 172             AccountManager accManager 
= AccountManager
.get(this); 
 173             accManager
.addAccountExplicitly(account
, password_text
.getText() 
 176             // Add this account as default in the preferences, if there is none 
 178             Account defaultAccount 
= AccountUtils
 
 179                     .getCurrentOwnCloudAccount(this); 
 180             if (defaultAccount 
== null
) { 
 181                 SharedPreferences
.Editor editor 
= PreferenceManager
 
 182                         .getDefaultSharedPreferences(this).edit(); 
 183                 editor
.putString("select_oc_account", accountName
); 
 187             final Intent intent 
= new Intent(); 
 188             intent
.putExtra(AccountManager
.KEY_ACCOUNT_TYPE
, 
 189                     AccountAuthenticator
.ACCOUNT_TYPE
); 
 190             intent
.putExtra(AccountManager
.KEY_ACCOUNT_NAME
, account
.name
); 
 191             intent
.putExtra(AccountManager
.KEY_AUTHTOKEN
, 
 192                     AccountAuthenticator
.ACCOUNT_TYPE
); 
 193             intent
.putExtra(AccountManager
.KEY_USERDATA
, username
); 
 195             accManager
.setUserData(account
, AccountAuthenticator
.KEY_OC_URL
, 
 197             accManager
.setUserData(account
, 
 198                     AccountAuthenticator
.KEY_OC_VERSION
, mConnChkRunnable
 
 199                             .getDiscoveredVersion().toString()); 
 200             accManager
.setUserData(account
, 
 201                     AccountAuthenticator
.KEY_OC_BASE_URL
, mBaseUrl
); 
 203             setAccountAuthenticatorResult(intent
.getExtras()); 
 204             setResult(RESULT_OK
, intent
); 
 205             Bundle bundle 
= new Bundle(); 
 206             bundle
.putBoolean(ContentResolver
.SYNC_EXTRAS_MANUAL
, true
); 
 207             //getContentResolver().startSync(ProviderTableMeta.CONTENT_URI, 
 209             ContentResolver
.requestSync(account
, "org.owncloud", bundle
); 
 213              * (mConnChkRunnable.getDiscoveredVersion().compareTo(OwnCloudVersion 
 214              * .owncloud_v2) >= 0) { Intent i = new Intent(this, 
 215              * ExtensionsAvailableActivity.class); startActivity(i); } 
 220             dismissDialog(DIALOG_LOGIN_PROGRESS
); 
 221             TextView tv 
= (TextView
) findViewById(R
.id
.account_username
); 
 222             tv
.setError(message
); 
 225     public void onCancelClick(View view
) { 
 229     public void onOkClick(View view
) { 
 231         String url 
= ((TextView
) findViewById(R
.id
.host_URL
)).getText() 
 238         if (url
.toLowerCase().startsWith("http://") 
 239                 || url
.toLowerCase().startsWith("https://")) { 
 242         continueConnection(prefix
); 
 245     private void continueConnection(String prefix
) { 
 246         String url 
= ((TextView
) findViewById(R
.id
.host_URL
)).getText() 
 248         String username 
= ((TextView
) findViewById(R
.id
.account_username
)) 
 249                 .getText().toString(); 
 250         String password 
= ((TextView
) findViewById(R
.id
.account_password
)) 
 251                 .getText().toString(); 
 252         if (url
.endsWith("/")) 
 253             url 
= url
.substring(0, url
.length() - 1); 
 256         String webdav_path 
= AccountUtils
.getWebdavPath(mConnChkRunnable
 
 257                 .getDiscoveredVersion()); 
 260             mBaseUrl 
= prefix 
+ url
; 
 261             String url_str 
= prefix 
+ url 
+ webdav_path
; 
 262             uri 
= new URL(url_str
); 
 263         } catch (MalformedURLException e
) { 
 264             // should not happend 
 268         showDialog(DIALOG_LOGIN_PROGRESS
); 
 269         mAuthRunnable 
= new AuthenticationRunnable(uri
, username
, password
); 
 270         mAuthRunnable
.setOnAuthenticationResultListener(this, mHandler
); 
 271         mAuthThread 
= new Thread(mAuthRunnable
); 
 276     public void onConnectionCheckResult(ResultType type
) { 
 277         mStatusText 
= mStatusIcon 
= 0; 
 278         mStatusCorrect 
= false
; 
 279         String t_url 
= ((TextView
) findViewById(R
.id
.host_URL
)).getText() 
 280                 .toString().toLowerCase(); 
 285             if (t_url
.startsWith("http://") || t_url
.startsWith("https://")) { 
 286                 mIsSslConn 
= t_url
.startsWith("http://") ? false 
: true
; 
 287                 mStatusIcon 
= R
.drawable
.ic_ok
; 
 288                 mStatusText 
= R
.string
.auth_connection_established
; 
 289                 mStatusCorrect 
= true
; 
 292                 mStatusIcon 
= android
.R
.drawable
.ic_secure
; 
 293                 mStatusText 
= R
.string
.auth_secure_connection
; 
 294                 mStatusCorrect 
= true
; 
 298             mStatusIcon 
= android
.R
.drawable
.ic_secure
; 
 299             mStatusText 
= R
.string
.auth_nossl_plain_ok_title
; 
 300             mStatusCorrect 
= true
; 
 304         case INORRECT_ADDRESS
: 
 306         case HOST_NOT_AVAILABLE
: 
 307             mStatusIcon 
= R
.drawable
.common_error
; 
 308             mStatusText 
= R
.string
.auth_unknow_host_title
; 
 310         case NO_NETWORK_CONNECTION
: 
 311             mStatusIcon 
= R
.drawable
.no_network
; 
 312             mStatusText 
= R
.string
.auth_no_net_conn_title
; 
 314         case INSTANCE_NOT_CONFIGURED
: 
 315             mStatusIcon 
= R
.drawable
.common_error
; 
 316             mStatusText 
= R
.string
.auth_not_configured_title
; 
 319             mStatusIcon 
= R
.drawable
.common_error
; 
 320             mStatusText 
= R
.string
.auth_unknow_error
; 
 323             mStatusIcon 
= R
.drawable
.common_error
; 
 324             mStatusText 
= R
.string
.auth_incorrect_path_title
; 
 327             Log
.e(TAG
, "Incorrect connection checker result type: " + type
); 
 329         setResultIconAndText(mStatusIcon
, mStatusText
); 
 331             findViewById(R
.id
.refreshButton
).setVisibility(View
.VISIBLE
); 
 333             findViewById(R
.id
.refreshButton
).setVisibility(View
.INVISIBLE
); 
 334         findViewById(R
.id
.buttonOK
).setEnabled(mStatusCorrect
); 
 338     public void onFocusChange(View view
, boolean hasFocus
) { 
 339         if (view
.getId() == R
.id
.host_URL
) { 
 341                 TextView tv 
= ((TextView
) findViewById(R
.id
.host_URL
)); 
 342                 String uri 
= tv
.getText().toString(); 
 343                 if (uri
.length() != 0) { 
 344                     setResultIconAndText(R
.drawable
.progress_small
, 
 345                             R
.string
.auth_testing_connection
); 
 346                     findViewById(R
.id
.buttonOK
).setEnabled(false
);  // avoid connect can be clicked if the test was previously passed 
 347                     mConnChkRunnable 
= new ConnectionCheckerRunnable(uri
, this); 
 348                     mConnChkRunnable
.setListener(this, mHandler
); 
 349                     mAuthThread 
= new Thread(mConnChkRunnable
); 
 352                     findViewById(R
.id
.refreshButton
).setVisibility( 
 354                     setResultIconAndText(0, 0); 
 357         } else if (view
.getId() == R
.id
.account_password
) { 
 358             ImageView iv 
= (ImageView
) findViewById(R
.id
.viewPassword
); 
 360                 iv
.setVisibility(View
.VISIBLE
); 
 362                 TextView v 
= (TextView
) findViewById(R
.id
.account_password
); 
 363                 int input_type 
= InputType
.TYPE_CLASS_TEXT
 
 364                         | InputType
.TYPE_TEXT_VARIATION_PASSWORD
; 
 365                 v
.setInputType(input_type
); 
 366                 iv
.setVisibility(View
.INVISIBLE
); 
 371     private void setResultIconAndText(int drawable_id
, int text_id
) { 
 372         ImageView iv 
= (ImageView
) findViewById(R
.id
.action_indicator
); 
 373         TextView tv 
= (TextView
) findViewById(R
.id
.status_text
); 
 375         if (drawable_id 
== 0 && text_id 
== 0) { 
 376             iv
.setVisibility(View
.INVISIBLE
); 
 377             tv
.setVisibility(View
.INVISIBLE
); 
 379             iv
.setImageResource(drawable_id
); 
 381             iv
.setVisibility(View
.VISIBLE
); 
 382             tv
.setVisibility(View
.VISIBLE
); 
 387     public void onClick(View v
) { 
 388         if (v
.getId() == R
.id
.refreshButton
) { 
 389             onFocusChange(findViewById(R
.id
.host_URL
), false
); 
 390         } else if (v
.getId() == R
.id
.viewPassword
) { 
 391             TextView view 
= (TextView
) findViewById(R
.id
.account_password
); 
 392             int input_type 
= InputType
.TYPE_CLASS_TEXT
 
 393                     | InputType
.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
; 
 394             view
.setInputType(input_type
);