2  *   ownCloud Android client application 
   4  *   @author Bartek Przybylski 
   5  *   @author David A. Velasco 
   7  *   Copyright (C) 2012  Bartek Przybylski 
   8  *   Copyright (C) 2015 ownCloud Inc. 
  10  *   This program is free software: you can redistribute it and/or modify 
  11  *   it under the terms of the GNU General Public License version 2, 
  12  *   as published by the Free Software Foundation. 
  14  *   This program is distributed in the hope that it will be useful, 
  15  *   but WITHOUT ANY WARRANTY; without even the implied warranty of 
  16  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  17  *   GNU General Public License for more details. 
  19  *   You should have received a copy of the GNU General Public License 
  20  *   along with this program.  If not, see <http://www.gnu.org/licenses/>. 
  24 package com
.owncloud
.android
.authentication
; 
  26 import java
.security
.cert
.X509Certificate
; 
  29 import android
.accounts
.Account
; 
  30 import android
.accounts
.AccountManager
; 
  31 import android
.app
.Dialog
; 
  32 import android
.content
.ComponentName
; 
  33 import android
.content
.Context
; 
  34 import android
.content
.Intent
; 
  35 import android
.content
.ServiceConnection
; 
  36 import android
.content
.SharedPreferences
; 
  37 import android
.graphics
.Rect
; 
  38 import android
.graphics
.drawable
.Drawable
; 
  39 import android
.net
.Uri
; 
  40 import android
.net
.http
.SslError
; 
  41 import android
.os
.Bundle
; 
  42 import android
.os
.Handler
; 
  43 import android
.os
.IBinder
; 
  44 import android
.preference
.PreferenceManager
; 
  45 import android
.support
.v4
.app
.DialogFragment
; 
  46 import android
.support
.v4
.app
.Fragment
; 
  47 import android
.support
.v4
.app
.FragmentManager
; 
  48 import android
.support
.v4
.app
.FragmentTransaction
; 
  49 import android
.text
.Editable
; 
  50 import android
.text
.InputType
; 
  51 import android
.text
.TextWatcher
; 
  52 import android
.view
.KeyEvent
; 
  53 import android
.view
.MotionEvent
; 
  54 import android
.view
.View
; 
  55 import android
.view
.View
.OnFocusChangeListener
; 
  56 import android
.view
.View
.OnTouchListener
; 
  57 import android
.view
.Window
; 
  58 import android
.view
.inputmethod
.EditorInfo
; 
  59 import android
.webkit
.HttpAuthHandler
; 
  60 import android
.webkit
.SslErrorHandler
; 
  61 import android
.webkit
.WebView
; 
  62 import android
.widget
.Button
; 
  63 import android
.widget
.CheckBox
; 
  64 import android
.widget
.EditText
; 
  65 import android
.widget
.TextView
; 
  66 import android
.widget
.TextView
.OnEditorActionListener
; 
  67 import android
.widget
.Toast
; 
  69 import com
.owncloud
.android
.MainApp
; 
  70 import com
.owncloud
.android
.R
; 
  71 import com
.owncloud
.android
.authentication
.SsoWebViewClient
.SsoWebViewClientListener
; 
  72 import com
.owncloud
.android
.lib
.common
.OwnCloudCredentials
; 
  73 import com
.owncloud
.android
.lib
.common
.OwnCloudCredentialsFactory
; 
  74 import com
.owncloud
.android
.lib
.common
.accounts
.AccountTypeUtils
; 
  75 import com
.owncloud
.android
.lib
.common
.accounts
.AccountUtils
.AccountNotFoundException
; 
  76 import com
.owncloud
.android
.lib
.common
.accounts
.AccountUtils
.Constants
; 
  77 import com
.owncloud
.android
.lib
.common
.network
.CertificateCombinedException
; 
  78 import com
.owncloud
.android
.lib
.common
.operations
.OnRemoteOperationListener
; 
  79 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperation
; 
  80 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
; 
  81 import com
.owncloud
.android
.lib
.common
.operations
.RemoteOperationResult
.ResultCode
; 
  82 import com
.owncloud
.android
.lib
.common
.utils
.Log_OC
; 
  83 import com
.owncloud
.android
.lib
.resources
.status
.OwnCloudVersion
; 
  84 import com
.owncloud
.android
.lib
.resources
.users
.GetRemoteUserNameOperation
; 
  85 import com
.owncloud
.android
.operations
.DetectAuthenticationMethodOperation
.AuthenticationMethod
; 
  86 import com
.owncloud
.android
.operations
.GetServerInfoOperation
; 
  87 import com
.owncloud
.android
.operations
.OAuth2GetAccessToken
; 
  88 import com
.owncloud
.android
.services
.OperationsService
; 
  89 import com
.owncloud
.android
.services
.OperationsService
.OperationsServiceBinder
; 
  90 import com
.owncloud
.android
.ui
.dialog
.CredentialsDialogFragment
; 
  91 import com
.owncloud
.android
.ui
.dialog
.IndeterminateProgressDialog
; 
  92 import com
.owncloud
.android
.ui
.dialog
.SamlWebViewDialog
; 
  93 import com
.owncloud
.android
.ui
.dialog
.SslUntrustedCertDialog
; 
  94 import com
.owncloud
.android
.ui
.dialog
.SslUntrustedCertDialog
.OnSslUntrustedCertListener
; 
  95 import com
.owncloud
.android
.utils
.DisplayUtils
; 
  98  * This Activity is used to add an ownCloud account to the App 
 100 public class AuthenticatorActivity 
extends AccountAuthenticatorActivity
 
 101         implements  OnRemoteOperationListener
, OnFocusChangeListener
, OnEditorActionListener
, 
 102         SsoWebViewClientListener
, OnSslUntrustedCertListener
, 
 103         AuthenticatorAsyncTask
.OnAuthenticatorTaskListener 
{ 
 105     private static final String TAG 
= AuthenticatorActivity
.class.getSimpleName(); 
 107     public static final String EXTRA_ACTION 
= "ACTION"; 
 108     public static final String EXTRA_ACCOUNT 
= "ACCOUNT"; 
 110     private static final String KEY_AUTH_TOKEN_TYPE 
= "AUTH_TOKEN_TYPE"; 
 112     private static final String KEY_HOST_URL_TEXT 
= "HOST_URL_TEXT"; 
 113     private static final String KEY_OC_VERSION 
= "OC_VERSION"; 
 114     private static final String KEY_SERVER_VALID 
= "SERVER_VALID"; 
 115     private static final String KEY_SERVER_CHECKED 
= "SERVER_CHECKED"; 
 116     private static final String KEY_SERVER_STATUS_TEXT 
= "SERVER_STATUS_TEXT"; 
 117     private static final String KEY_SERVER_STATUS_ICON 
= "SERVER_STATUS_ICON"; 
 118     private static final String KEY_IS_SSL_CONN 
= "IS_SSL_CONN"; 
 119     private static final String KEY_PASSWORD_EXPOSED 
= "PASSWORD_VISIBLE"; 
 120     private static final String KEY_AUTH_STATUS_TEXT 
= "AUTH_STATUS_TEXT"; 
 121     private static final String KEY_AUTH_STATUS_ICON 
= "AUTH_STATUS_ICON"; 
 122     private static final String KEY_SERVER_AUTH_METHOD 
= "SERVER_AUTH_METHOD"; 
 123     private static final String KEY_WAITING_FOR_OP_ID 
= "WAITING_FOR_OP_ID"; 
 124     private static final String KEY_AUTH_TOKEN 
= "AUTH_TOKEN"; 
 126     private static final String AUTH_ON 
= "on"; 
 127     private static final String AUTH_OPTIONAL 
= "optional"; 
 129     public static final byte ACTION_CREATE 
= 0; 
 130     public static final byte ACTION_UPDATE_TOKEN 
= 1;               // requested by the user 
 131     public static final byte ACTION_UPDATE_EXPIRED_TOKEN 
= 2;       // detected by the app 
 133     private static final String UNTRUSTED_CERT_DIALOG_TAG 
= "UNTRUSTED_CERT_DIALOG"; 
 134     private static final String SAML_DIALOG_TAG 
= "SAML_DIALOG"; 
 135     private static final String WAIT_DIALOG_TAG 
= "WAIT_DIALOG"; 
 136     private static final String CREDENTIALS_DIALOG_TAG 
= "CREDENTIALS_DIALOG"; 
 137     private static final String KEY_AUTH_IS_FIRST_ATTEMPT_TAG 
= "KEY_AUTH_IS_FIRST_ATTEMPT"; 
 139     private static final String KEY_USERNAME 
= "USERNAME"; 
 140     private static final String KEY_PASSWORD 
= "PASSWORD"; 
 141     private static final String KEY_ASYNC_TASK_IN_PROGRESS 
= "AUTH_IN_PROGRESS"; 
 143     /// parameters from EXTRAs in starter Intent 
 144     private byte mAction
; 
 145     private Account mAccount
; 
 146     private String mAuthTokenType
; 
 149     /// activity-level references / state 
 150     private final Handler mHandler 
= new Handler(); 
 151     private ServiceConnection mOperationsServiceConnection 
= null
; 
 152     private OperationsServiceBinder mOperationsServiceBinder 
= null
; 
 153     private AccountManager mAccountMgr
; 
 154     private Uri mNewCapturedUriFromOAuth2Redirection
; 
 157     /// Server PRE-Fragment elements  
 158     private EditText mHostUrlInput
; 
 159     private View mRefreshButton
; 
 160     private TextView mServerStatusView
; 
 162     private TextWatcher mHostUrlInputWatcher
; 
 163     private int mServerStatusText 
= 0, mServerStatusIcon 
= 0; 
 165     private boolean mServerIsChecked 
= false
; 
 166     private boolean mServerIsValid 
= false
; 
 167     private boolean mPendingAutoCheck 
= false
; 
 169     private GetServerInfoOperation
.ServerInfo mServerInfo 
=  
 170             new GetServerInfoOperation
.ServerInfo(); 
 173     /// Authentication PRE-Fragment elements  
 174     private CheckBox mOAuth2Check
; 
 175     private TextView mOAuthAuthEndpointText
; 
 176     private TextView mOAuthTokenEndpointText
; 
 177     private EditText mUsernameInput
; 
 178     private EditText mPasswordInput
; 
 179     private View mOkButton
; 
 180     private TextView mAuthStatusView
; 
 182     private int mAuthStatusText 
= 0, mAuthStatusIcon 
= 0; 
 184     private String mAuthToken 
= ""; 
 185     private AuthenticatorAsyncTask mAsyncTask
; 
 187     private boolean mIsFirstAuthAttempt
; 
 189     /// Identifier of operation in progress which result shouldn't be lost  
 190     private long mWaitingForOpId 
= Long
.MAX_VALUE
; 
 192     private final String BASIC_TOKEN_TYPE 
= AccountTypeUtils
.getAuthTokenTypePass( 
 193             MainApp
.getAccountType()); 
 194     private final String OAUTH_TOKEN_TYPE 
= AccountTypeUtils
.getAuthTokenTypeAccessToken( 
 195             MainApp
.getAccountType()); 
 196     private final String SAML_TOKEN_TYPE 
= 
 197             AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()); 
 203      * IMPORTANT ENTRY POINT 1: activity is shown to the user 
 206     protected void onCreate(Bundle savedInstanceState
) { 
 207         //Log_OC.wtf(TAG,  "onCreate init"); 
 208         super.onCreate(savedInstanceState
); 
 209         //getWindow().requestFeature(Window.FEATURE_NO_TITLE); 
 211         mIsFirstAuthAttempt 
= true
; 
 213         // bind to Operations Service 
 214         mOperationsServiceConnection 
= new OperationsServiceConnection(); 
 215         if (!bindService(new Intent(this, OperationsService
.class),  
 216                 mOperationsServiceConnection
,  
 217                 Context
.BIND_AUTO_CREATE
)) { 
 219                     R
.string
.error_cant_bind_to_operations_service
,  
 225         /// init activity state 
 226         mAccountMgr 
= AccountManager
.get(this); 
 227         mNewCapturedUriFromOAuth2Redirection 
= null
; 
 230         mAction 
= getIntent().getByteExtra(EXTRA_ACTION
, ACTION_CREATE
);  
 231         mAccount 
= getIntent().getExtras().getParcelable(EXTRA_ACCOUNT
); 
 232         if (savedInstanceState 
== null
) { 
 235             mAuthTokenType 
= savedInstanceState
.getString(KEY_AUTH_TOKEN_TYPE
); 
 236             mWaitingForOpId 
= savedInstanceState
.getLong(KEY_WAITING_FOR_OP_ID
); 
 237             mIsFirstAuthAttempt 
= savedInstanceState
.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG
); 
 240         /// load user interface 
 241         setContentView(R
.layout
.account_setup
); 
 243         /// initialize general UI elements 
 246         mOkButton 
= findViewById(R
.id
.buttonOK
); 
 248         /// initialize block to be moved to single Fragment to check server and get info about it  
 249         initServerPreFragment(savedInstanceState
); 
 251         /// initialize block to be moved to single Fragment to retrieve and validate credentials  
 252         initAuthorizationPreFragment(savedInstanceState
); 
 254         //Log_OC.wtf(TAG,  "onCreate end"); 
 257     private void initAuthTokenType() { 
 259                 getIntent().getExtras().getString(AccountAuthenticator
.KEY_AUTH_TOKEN_TYPE
); 
 260         if (mAuthTokenType 
== null
) { 
 261             if (mAccount 
!= null
) { 
 262                 boolean oAuthRequired 
=  
 263                     (mAccountMgr
.getUserData(mAccount
, Constants
.KEY_SUPPORTS_OAUTH2
) != null
); 
 264                 boolean samlWebSsoRequired 
= (  
 265                     mAccountMgr
.getUserData( 
 266                         mAccount
, Constants
.KEY_SUPPORTS_SAML_WEB_SSO
 
 269                 mAuthTokenType 
= chooseAuthTokenType(oAuthRequired
, samlWebSsoRequired
); 
 272                 boolean oAuthSupported 
= AUTH_ON
.equals(getString(R
.string
.auth_method_oauth2
)); 
 273                 boolean samlWebSsoSupported 
=  
 274                         AUTH_ON
.equals(getString(R
.string
.auth_method_saml_web_sso
)); 
 275                 mAuthTokenType 
= chooseAuthTokenType(oAuthSupported
, samlWebSsoSupported
); 
 280     private String 
chooseAuthTokenType(boolean oauth
, boolean saml
) { 
 282             return SAML_TOKEN_TYPE
; 
 284              return OAUTH_TOKEN_TYPE
; 
 286             return BASIC_TOKEN_TYPE
; 
 292      * Configures elements in the user interface under direct control of the Activity. 
 294     private void initOverallUi() { 
 296         /// step 1 - load and process relevant inputs (resources, intent, savedInstanceState) 
 297         boolean isWelcomeLinkVisible 
= getResources().getBoolean(R
.bool
.show_welcome_link
); 
 299         String instructionsMessageText 
= null
;  
 300         if (mAction 
== ACTION_UPDATE_EXPIRED_TOKEN
) { 
 301             if (AccountTypeUtils
.getAuthTokenTypeAccessToken(MainApp
.getAccountType()) 
 302                     .equals(mAuthTokenType
)) { 
 303                 instructionsMessageText 
= getString(R
.string
.auth_expired_oauth_token_toast
); 
 305             } else if (AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()) 
 306                     .equals(mAuthTokenType
)) { 
 307                 instructionsMessageText 
= getString(R
.string
.auth_expired_saml_sso_token_toast
); 
 310                 instructionsMessageText 
= getString(R
.string
.auth_expired_basic_auth_toast
); 
 314         /// step 2 - set properties of UI elements (text, visibility, enabled...) 
 315         Button welcomeLink 
= (Button
) findViewById(R
.id
.welcome_link
); 
 316         welcomeLink
.setVisibility(isWelcomeLinkVisible ? View
.VISIBLE 
: View
.GONE
); 
 318                 String
.format(getString(R
.string
.auth_register
), getString(R
.string
.app_name
))); 
 320         TextView instructionsView 
= (TextView
) findViewById(R
.id
.instructions_message
); 
 321         if (instructionsMessageText 
!= null
) { 
 322             instructionsView
.setVisibility(View
.VISIBLE
); 
 323             instructionsView
.setText(instructionsMessageText
); 
 325             instructionsView
.setVisibility(View
.GONE
); 
 332      * @param savedInstanceState        Saved activity state, as in {{@link #onCreate(Bundle)} 
 334     private void initServerPreFragment(Bundle savedInstanceState
) { 
 336         /// step 1 - load and process relevant inputs (resources, intent, savedInstanceState) 
 337         boolean isUrlInputAllowed 
= getResources().getBoolean(R
.bool
.show_server_url_input
);  
 338         if (savedInstanceState 
== null
) { 
 339             if (mAccount 
!= null
) { 
 340                 mServerInfo
.mBaseUrl 
= mAccountMgr
.getUserData(mAccount
, Constants
.KEY_OC_BASE_URL
); 
 341                 // TODO do next in a setter for mBaseUrl 
 342                 mServerInfo
.mIsSslConn 
= mServerInfo
.mBaseUrl
.startsWith("https://");    
 343                 String ocVersion 
= mAccountMgr
.getUserData(mAccount
, Constants
.KEY_OC_VERSION
); 
 344                 if (ocVersion 
!= null
) { 
 345                     mServerInfo
.mVersion 
= new OwnCloudVersion(ocVersion
); 
 348                 mServerInfo
.mBaseUrl 
= getString(R
.string
.server_url
).trim(); 
 349                 mServerInfo
.mIsSslConn 
= mServerInfo
.mBaseUrl
.startsWith("https://"); 
 352             mServerStatusText 
= savedInstanceState
.getInt(KEY_SERVER_STATUS_TEXT
); 
 353             mServerStatusIcon 
= savedInstanceState
.getInt(KEY_SERVER_STATUS_ICON
); 
 355             mServerIsValid 
= savedInstanceState
.getBoolean(KEY_SERVER_VALID
); 
 356             mServerIsChecked 
= savedInstanceState
.getBoolean(KEY_SERVER_CHECKED
); 
 359             mServerInfo
.mIsSslConn 
= savedInstanceState
.getBoolean(KEY_IS_SSL_CONN
); 
 360             mServerInfo
.mBaseUrl 
= savedInstanceState
.getString(KEY_HOST_URL_TEXT
); 
 361             String ocVersion 
= savedInstanceState
.getString(KEY_OC_VERSION
); 
 362             if (ocVersion 
!= null
) { 
 363                 mServerInfo
.mVersion 
= new OwnCloudVersion(ocVersion
); 
 365             mServerInfo
.mAuthMethod 
= AuthenticationMethod
.valueOf( 
 366                     savedInstanceState
.getString(KEY_SERVER_AUTH_METHOD
)); 
 370         /// step 2 - set properties of UI elements (text, visibility, enabled...) 
 371         mHostUrlInput 
= (EditText
) findViewById(R
.id
.hostUrlInput
); 
 372         // Convert IDN to Unicode 
 373         mHostUrlInput
.setText(DisplayUtils
.convertIdn(mServerInfo
.mBaseUrl
, false
)); 
 374         if (mAction 
!= ACTION_CREATE
) { 
 375             /// lock things that should not change 
 376             mHostUrlInput
.setEnabled(false
); 
 377             mHostUrlInput
.setFocusable(false
); 
 379         if (isUrlInputAllowed
) { 
 380             mRefreshButton 
= findViewById(R
.id
.embeddedRefreshButton
); 
 382             findViewById(R
.id
.hostUrlFrame
).setVisibility(View
.GONE
); 
 383             mRefreshButton 
= findViewById(R
.id
.centeredRefreshButton
); 
 385         showRefreshButton(mServerIsChecked 
&& !mServerIsValid 
&& 
 386                 mWaitingForOpId 
> Integer
.MAX_VALUE
); 
 387         mServerStatusView 
= (TextView
) findViewById(R
.id
.server_status_text
); 
 390         /// step 3 - bind some listeners and options 
 391         mHostUrlInput
.setImeOptions(EditorInfo
.IME_ACTION_NEXT
); 
 392         mHostUrlInput
.setOnEditorActionListener(this); 
 394         /// step 4 - create listeners that will be bound at onResume 
 395         mHostUrlInputWatcher 
= new TextWatcher() { 
 398             public void afterTextChanged(Editable s
) { 
 399                 if (mOkButton
.isEnabled() && 
 400                         !mServerInfo
.mBaseUrl
.equals( 
 401                                 normalizeUrl(s
.toString(), mServerInfo
.mIsSslConn
))) { 
 402                     mOkButton
.setEnabled(false
); 
 407             public void beforeTextChanged(CharSequence s
, int start
, int count
, int after
) { 
 411             public void onTextChanged(CharSequence s
, int start
, int before
, int count
) { 
 412                 if (mAuthStatusIcon 
!= 0) { 
 413                     Log_OC
.d(TAG
, "onTextChanged: hiding authentication status"); 
 422         // TODO find out if this is really necessary, or if it can done in a different way 
 423         findViewById(R
.id
.scroll
).setOnTouchListener(new OnTouchListener() { 
 425             public boolean onTouch(View view
, MotionEvent event
) { 
 426                 if (event
.getAction() == MotionEvent
.ACTION_DOWN
) { 
 428                             AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie( 
 429                                     MainApp
.getAccountType() 
 430                             ).equals(mAuthTokenType
) && 
 431                                     mHostUrlInput
.hasFocus() 
 441         /// step 4 - mark automatic check to be started when OperationsService is ready 
 442         mPendingAutoCheck 
= (savedInstanceState 
== null 
&&  
 443                 (mAction 
!= ACTION_CREATE 
|| !isUrlInputAllowed
)); 
 449      * @param savedInstanceState        Saved activity state, as in {{@link #onCreate(Bundle)} 
 451     private void initAuthorizationPreFragment(Bundle savedInstanceState
) { 
 453         /// step 0 - get UI elements in layout 
 454         mOAuth2Check 
= (CheckBox
) findViewById(R
.id
.oauth_onOff_check
); 
 455         mOAuthAuthEndpointText 
= (TextView
)findViewById(R
.id
.oAuthEntryPoint_1
); 
 456         mOAuthTokenEndpointText 
= (TextView
)findViewById(R
.id
.oAuthEntryPoint_2
); 
 457         mUsernameInput 
= (EditText
) findViewById(R
.id
.account_username
); 
 458         mPasswordInput 
= (EditText
) findViewById(R
.id
.account_password
); 
 459         mAuthStatusView 
= (TextView
) findViewById(R
.id
.auth_status_text
);  
 461         /// step 1 - load and process relevant inputs (resources, intent, savedInstanceState) 
 462         String presetUserName 
= null
; 
 463         boolean isPasswordExposed 
= false
; 
 464         if (savedInstanceState 
== null
) { 
 465             if (mAccount 
!= null
) { 
 466                 presetUserName 
= mAccount
.name
.substring(0, mAccount
.name
.lastIndexOf('@')); 
 470             isPasswordExposed 
= savedInstanceState
.getBoolean(KEY_PASSWORD_EXPOSED
, false
); 
 471             mAuthStatusText 
= savedInstanceState
.getInt(KEY_AUTH_STATUS_TEXT
); 
 472             mAuthStatusIcon 
= savedInstanceState
.getInt(KEY_AUTH_STATUS_ICON
); 
 473             mAuthToken 
= savedInstanceState
.getString(KEY_AUTH_TOKEN
); 
 476         /// step 2 - set properties of UI elements (text, visibility, enabled...) 
 477         mOAuth2Check
.setChecked( 
 478                 AccountTypeUtils
.getAuthTokenTypeAccessToken(MainApp
.getAccountType()) 
 479                     .equals(mAuthTokenType
)); 
 480         if (presetUserName 
!= null
) { 
 481             mUsernameInput
.setText(presetUserName
); 
 483         if (mAction 
!= ACTION_CREATE
) { 
 484             mUsernameInput
.setEnabled(false
); 
 485             mUsernameInput
.setFocusable(false
); 
 487         mPasswordInput
.setText(""); // clean password to avoid social hacking 
 488         if (isPasswordExposed
) { 
 491         updateAuthenticationPreFragmentVisibility(); 
 493         mOkButton
.setEnabled(mServerIsValid
); 
 496         /// step 3 - bind listeners 
 497         // bindings for password input field 
 498         mPasswordInput
.setOnFocusChangeListener(this); 
 499         mPasswordInput
.setImeOptions(EditorInfo
.IME_ACTION_DONE
); 
 500         mPasswordInput
.setOnEditorActionListener(this); 
 501         mPasswordInput
.setOnTouchListener(new RightDrawableOnTouchListener() { 
 503             public boolean onDrawableTouch(final MotionEvent event
) { 
 504                 if (event
.getAction() == MotionEvent
.ACTION_UP
) { 
 505                     AuthenticatorActivity
.this.onViewPasswordClick(); 
 515      * Changes the visibility of input elements depending on 
 516      * the current authorization method. 
 518     private void updateAuthenticationPreFragmentVisibility () { 
 519         if (AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()). 
 520                 equals(mAuthTokenType
)) { 
 521             // SAML-based web Single Sign On 
 522             mOAuth2Check
.setVisibility(View
.GONE
); 
 523             mOAuthAuthEndpointText
.setVisibility(View
.GONE
); 
 524             mOAuthTokenEndpointText
.setVisibility(View
.GONE
); 
 525             mUsernameInput
.setVisibility(View
.GONE
); 
 526             mPasswordInput
.setVisibility(View
.GONE
); 
 529             if (mAction 
== ACTION_CREATE 
&&  
 530                     AUTH_OPTIONAL
.equals(getString(R
.string
.auth_method_oauth2
))) { 
 531                 mOAuth2Check
.setVisibility(View
.VISIBLE
); 
 533                 mOAuth2Check
.setVisibility(View
.GONE
); 
 536             if (AccountTypeUtils
.getAuthTokenTypeAccessToken(MainApp
.getAccountType()). 
 537                     equals(mAuthTokenType
)) { 
 538                 // OAuth 2 authorization 
 540                 mOAuthAuthEndpointText
.setVisibility(View
.VISIBLE
); 
 541                 mOAuthTokenEndpointText
.setVisibility(View
.VISIBLE
); 
 542                 mUsernameInput
.setVisibility(View
.GONE
); 
 543                 mPasswordInput
.setVisibility(View
.GONE
); 
 546                 // basic HTTP authorization 
 547                 mOAuthAuthEndpointText
.setVisibility(View
.GONE
); 
 548                 mOAuthTokenEndpointText
.setVisibility(View
.GONE
); 
 549                 mUsernameInput
.setVisibility(View
.VISIBLE
); 
 550                 mPasswordInput
.setVisibility(View
.VISIBLE
); 
 558      * Saves relevant state before {@link #onPause()} 
 560      * Do NOT save {@link #mNewCapturedUriFromOAuth2Redirection}; it keeps a temporal flag,  
 561      * intended to defer the processing of the redirection caught in  
 562      * {@link #onNewIntent(Intent)} until {@link #onResume()}  
 564      * See {@link #onSaveInstanceState(Bundle)} 
 567     protected void onSaveInstanceState(Bundle outState
) { 
 568         //Log_OC.wtf(TAG, "onSaveInstanceState init" ); 
 569         super.onSaveInstanceState(outState
); 
 572         outState
.putString(KEY_AUTH_TOKEN_TYPE
, mAuthTokenType
); 
 573         outState
.putLong(KEY_WAITING_FOR_OP_ID
, mWaitingForOpId
); 
 575         /// Server PRE-fragment state 
 576         outState
.putInt(KEY_SERVER_STATUS_TEXT
, mServerStatusText
); 
 577         outState
.putInt(KEY_SERVER_STATUS_ICON
, mServerStatusIcon
); 
 578         outState
.putBoolean(KEY_SERVER_CHECKED
, mServerIsChecked
); 
 579         outState
.putBoolean(KEY_SERVER_VALID
, mServerIsValid
); 
 580         outState
.putBoolean(KEY_IS_SSL_CONN
, mServerInfo
.mIsSslConn
); 
 581         outState
.putString(KEY_HOST_URL_TEXT
, mServerInfo
.mBaseUrl
); 
 582         if (mServerInfo
.mVersion 
!= null
) { 
 583             outState
.putString(KEY_OC_VERSION
, mServerInfo
.mVersion
.getVersion()); 
 585         outState
.putString(KEY_SERVER_AUTH_METHOD
, mServerInfo
.mAuthMethod
.name()); 
 587         /// Authentication PRE-fragment state 
 588         outState
.putBoolean(KEY_PASSWORD_EXPOSED
, isPasswordVisible()); 
 589         outState
.putInt(KEY_AUTH_STATUS_ICON
, mAuthStatusIcon
); 
 590         outState
.putInt(KEY_AUTH_STATUS_TEXT
, mAuthStatusText
); 
 591         outState
.putString(KEY_AUTH_TOKEN
, mAuthToken
); 
 594         outState
.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG
, mIsFirstAuthAttempt
); 
 596         /// AsyncTask (User and password) 
 597         outState
.putString(KEY_USERNAME
, mUsernameInput
.getText().toString()); 
 598         outState
.putString(KEY_PASSWORD
, mPasswordInput
.getText().toString()); 
 600         if (mAsyncTask 
!= null
) { 
 601             mAsyncTask
.cancel(true
); 
 602             outState
.putBoolean(KEY_ASYNC_TASK_IN_PROGRESS
, true
); 
 604             outState
.putBoolean(KEY_ASYNC_TASK_IN_PROGRESS
, false
); 
 608         //Log_OC.wtf(TAG, "onSaveInstanceState end" ); 
 612     public void onRestoreInstanceState(Bundle savedInstanceState
) { 
 613         super.onRestoreInstanceState(savedInstanceState
); 
 616         boolean inProgress 
= savedInstanceState
.getBoolean(KEY_ASYNC_TASK_IN_PROGRESS
); 
 618             String username 
= savedInstanceState
.getString(KEY_USERNAME
); 
 619             String password 
= savedInstanceState
.getString(KEY_PASSWORD
); 
 621             OwnCloudCredentials credentials 
= null
; 
 622             if (BASIC_TOKEN_TYPE
.equals(mAuthTokenType
)) { 
 623                 credentials 
= OwnCloudCredentialsFactory
.newBasicCredentials(username
, password
); 
 625             } else if (OAUTH_TOKEN_TYPE
.equals(mAuthTokenType
)) { 
 626                 credentials 
= OwnCloudCredentialsFactory
.newBearerCredentials(mAuthToken
); 
 629             accessRootFolder(credentials
); 
 634      * The redirection triggered by the OAuth authentication server as response to the  
 635      * GET AUTHORIZATION request is caught here. 
 637      * To make this possible, this activity needs to be qualified with android:launchMode =  
 638      * "singleTask" in the AndroidManifest.xml file. 
 641     protected void onNewIntent (Intent intent
) { 
 642         Log_OC
.d(TAG
, "onNewIntent()"); 
 643         Uri data 
= intent
.getData(); 
 644         if (data 
!= null 
&& data
.toString().startsWith(getString(R
.string
.oauth2_redirect_uri
))) { 
 645             mNewCapturedUriFromOAuth2Redirection 
= data
; 
 651      * The redirection triggered by the OAuth authentication server as response to the  
 652      * GET AUTHORIZATION, and deferred in {@link #onNewIntent(Intent)}, is processed here. 
 655     protected void onResume() { 
 658         // bound here to avoid spurious changes triggered by Android on device rotations 
 659         mHostUrlInput
.setOnFocusChangeListener(this); 
 660         mHostUrlInput
.addTextChangedListener(mHostUrlInputWatcher
); 
 662         if (mNewCapturedUriFromOAuth2Redirection 
!= null
) { 
 663             getOAuth2AccessTokenFromCapturedRedirection();             
 666         if (mOperationsServiceBinder 
!= null
) { 
 667             doOnResumeAndBound(); 
 674     protected void onPause() { 
 675         if (mOperationsServiceBinder 
!= null
) { 
 676             mOperationsServiceBinder
.removeOperationListener(this); 
 679         mHostUrlInput
.removeTextChangedListener(mHostUrlInputWatcher
); 
 680         mHostUrlInput
.setOnFocusChangeListener(null
); 
 686     protected void onDestroy() { 
 688         mHostUrlInputWatcher 
= null
; 
 690         if (mOperationsServiceConnection 
!= null
) { 
 691             unbindService(mOperationsServiceConnection
); 
 692             mOperationsServiceBinder 
= null
; 
 699      * Parses the redirection with the response to the GET AUTHORIZATION request to the  
 700      * oAuth server and requests for the access token (GET ACCESS TOKEN) 
 702     private void getOAuth2AccessTokenFromCapturedRedirection() { 
 703         /// Parse data from OAuth redirection 
 704         String queryParameters 
= mNewCapturedUriFromOAuth2Redirection
.getQuery(); 
 705         mNewCapturedUriFromOAuth2Redirection 
= null
; 
 707         /// Showing the dialog with instructions for the user. 
 708         IndeterminateProgressDialog dialog 
=  
 709                 IndeterminateProgressDialog
.newInstance(R
.string
.auth_getting_authorization
, true
); 
 710         dialog
.show(getSupportFragmentManager(), WAIT_DIALOG_TAG
); 
 712         /// GET ACCESS TOKEN to the oAuth server 
 713         Intent getServerInfoIntent 
= new Intent(); 
 714         getServerInfoIntent
.setAction(OperationsService
.ACTION_OAUTH2_GET_ACCESS_TOKEN
); 
 716         getServerInfoIntent
.putExtra( 
 717                 OperationsService
.EXTRA_SERVER_URL
,  
 718                 mOAuthTokenEndpointText
.getText().toString().trim()); 
 720         getServerInfoIntent
.putExtra( 
 721                 OperationsService
.EXTRA_OAUTH2_QUERY_PARAMETERS
,  
 724         if (mOperationsServiceBinder 
!= null
) { 
 725             //Log_OC.wtf(TAG, "getting access token..." ); 
 726             mWaitingForOpId 
= mOperationsServiceBinder
.queueNewOperation(getServerInfoIntent
); 
 733      * Handles the change of focus on the text inputs for the server URL and the password 
 735     public void onFocusChange(View view
, boolean hasFocus
) { 
 736         if (view
.getId() == R
.id
.hostUrlInput
) {    
 738                 onUrlInputFocusLost(); 
 741                 showRefreshButton(false
); 
 744         } else if (view
.getId() == R
.id
.account_password
) { 
 745             onPasswordFocusChanged(hasFocus
); 
 751      * Handles changes in focus on the text input for the server URL. 
 753      * IMPORTANT ENTRY POINT 2: When (!hasFocus), user wrote the server URL and changed to  
 754      * other field. The operation to check the existence of the server in the entered URL is 
 757      * When hasFocus:    user 'comes back' to write again the server URL. 
 759     private void onUrlInputFocusLost() { 
 760         if (!mServerInfo
.mBaseUrl
.equals( 
 761                 normalizeUrl(mHostUrlInput
.getText().toString(), mServerInfo
.mIsSslConn
))) { 
 762             // check server again only if the user changed something in the field 
 765             mOkButton
.setEnabled(mServerIsValid
); 
 766             showRefreshButton(!mServerIsValid
); 
 771     private void checkOcServer() { 
 772         String uri 
= mHostUrlInput
.getText().toString().trim(); 
 773         mServerIsValid 
= false
; 
 774         mServerIsChecked 
= false
; 
 775         mOkButton
.setEnabled(false
); 
 776         mServerInfo 
= new GetServerInfoOperation
.ServerInfo(); 
 777         showRefreshButton(false
); 
 779         if (uri
.length() != 0) { 
 780             // Handle internationalized domain names 
 781             uri 
= DisplayUtils
.convertIdn(uri
, true
); 
 783             mServerStatusText 
= R
.string
.auth_testing_connection
; 
 784             mServerStatusIcon 
= R
.drawable
.progress_small
; 
 787             Intent getServerInfoIntent 
= new Intent(); 
 788             getServerInfoIntent
.setAction(OperationsService
.ACTION_GET_SERVER_INFO
); 
 789             getServerInfoIntent
.putExtra( 
 790                 OperationsService
.EXTRA_SERVER_URL
,  
 791                 normalizeUrlSuffix(uri
) 
 793             if (mOperationsServiceBinder 
!= null
) { 
 794                 mWaitingForOpId 
= mOperationsServiceBinder
.queueNewOperation(getServerInfoIntent
); 
 796               Log_OC
.wtf(TAG
, "Server check tried with OperationService unbound!" ); 
 800             mServerStatusText 
= 0; 
 801             mServerStatusIcon 
= 0; 
 808      * Handles changes in focus on the text input for the password (basic authorization). 
 810      * When (hasFocus), the button to toggle password visibility is shown. 
 812      * When (!hasFocus), the button is made invisible and the password is hidden. 
 814      * @param hasFocus          'True' if focus is received, 'false' if is lost 
 816     private void onPasswordFocusChanged(boolean hasFocus
) { 
 818             showViewPasswordButton(); 
 821             hidePasswordButton(); 
 826     private void showViewPasswordButton() { 
 827         int drawable 
= R
.drawable
.ic_view
; 
 828         if (isPasswordVisible()) { 
 829             drawable 
= R
.drawable
.ic_hide
; 
 831         mPasswordInput
.setCompoundDrawablesWithIntrinsicBounds(0, 0, drawable
, 0); 
 834     private boolean isPasswordVisible() { 
 835         return ((mPasswordInput
.getInputType() & InputType
.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
) ==  
 836                 InputType
.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
); 
 839     private void hidePasswordButton() { 
 840         mPasswordInput
.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0); 
 843     private void showPassword() { 
 844         mPasswordInput
.setInputType( 
 845                 InputType
.TYPE_CLASS_TEXT 
| InputType
.TYPE_TEXT_VARIATION_VISIBLE_PASSWORD
 
 847         showViewPasswordButton(); 
 850     private void hidePassword() { 
 851         mPasswordInput
.setInputType( 
 852                 InputType
.TYPE_CLASS_TEXT 
| InputType
.TYPE_TEXT_VARIATION_PASSWORD
 
 854         showViewPasswordButton(); 
 858      * Checks the credentials of the user in the root of the ownCloud server 
 859      * before creating a new local account. 
 861      * For basic authorization, a check of existence of the root folder is 
 864      * For OAuth, starts the flow to get an access token; the credentials test  
 865      * is postponed until it is available. 
 867      * IMPORTANT ENTRY POINT 4 
 869      * @param view      OK button 
 871     public void onOkClick(View view
) { 
 872         // this check should be unnecessary 
 873         if (mServerInfo
.mVersion 
== null 
||  
 874                 !mServerInfo
.mVersion
.isVersionValid()  ||  
 875                 mServerInfo
.mBaseUrl 
== null 
||  
 876                 mServerInfo
.mBaseUrl
.length() == 0) { 
 877             mServerStatusIcon 
= R
.drawable
.common_error
; 
 878             mServerStatusText 
= R
.string
.auth_wtf_reenter_URL
; 
 880             mOkButton
.setEnabled(false
); 
 884         if (AccountTypeUtils
.getAuthTokenTypeAccessToken(MainApp
.getAccountType()). 
 885                 equals(mAuthTokenType
)) { 
 887             startOauthorization(); 
 888         } else if (AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()). 
 889                 equals(mAuthTokenType
)) { 
 891             startSamlBasedFederatedSingleSignOnAuthorization(); 
 893             checkBasicAuthorization(); 
 899      * Tests the credentials entered by the user performing a check of existence on  
 900      * the root folder of the ownCloud server. 
 902     private void checkBasicAuthorization() { 
 903         /// get basic credentials entered by user 
 904         String username 
= mUsernameInput
.getText().toString(); 
 905         String password 
= mPasswordInput
.getText().toString(); 
 907         /// be gentle with the user 
 908         IndeterminateProgressDialog dialog 
=  
 909                 IndeterminateProgressDialog
.newInstance(R
.string
.auth_trying_to_login
, true
); 
 910         dialog
.show(getSupportFragmentManager(), WAIT_DIALOG_TAG
); 
 912         /// validate credentials accessing the root folder 
 913         OwnCloudCredentials credentials 
= OwnCloudCredentialsFactory
.newBasicCredentials(username
, 
 915         accessRootFolder(credentials
); 
 918     private void accessRootFolder(OwnCloudCredentials credentials
) { 
 919         mAsyncTask 
= new AuthenticatorAsyncTask(this); 
 920         Object
[] params 
= { mServerInfo
.mBaseUrl
, credentials 
}; 
 921         mAsyncTask
.execute(params
); 
 926      * Starts the OAuth 'grant type' flow to get an access token, with  
 927      * a GET AUTHORIZATION request to the BUILT-IN authorization server.  
 929     private void startOauthorization() { 
 930         // be gentle with the user 
 931         mAuthStatusIcon 
= R
.drawable
.progress_small
; 
 932         mAuthStatusText 
= R
.string
.oauth_login_connection
; 
 935         // GET AUTHORIZATION request 
 936         Uri uri 
= Uri
.parse(mOAuthAuthEndpointText
.getText().toString().trim()); 
 937         Uri
.Builder uriBuilder 
= uri
.buildUpon(); 
 938         uriBuilder
.appendQueryParameter( 
 939                 OAuth2Constants
.KEY_RESPONSE_TYPE
, getString(R
.string
.oauth2_response_type
) 
 941         uriBuilder
.appendQueryParameter( 
 942                 OAuth2Constants
.KEY_REDIRECT_URI
, getString(R
.string
.oauth2_redirect_uri
) 
 944         uriBuilder
.appendQueryParameter( 
 945                 OAuth2Constants
.KEY_CLIENT_ID
, getString(R
.string
.oauth2_client_id
) 
 947         uriBuilder
.appendQueryParameter( 
 948                 OAuth2Constants
.KEY_SCOPE
, getString(R
.string
.oauth2_scope
) 
 950         uri 
= uriBuilder
.build(); 
 951         Log_OC
.d(TAG
, "Starting browser to view " + uri
.toString()); 
 952         Intent i 
= new Intent(Intent
.ACTION_VIEW
, uri
); 
 958      * Starts the Web Single Sign On flow to get access to the root folder 
 961     private void startSamlBasedFederatedSingleSignOnAuthorization() { 
 962         /// be gentle with the user 
 963         mAuthStatusIcon 
= R
.drawable
.progress_small
; 
 964         mAuthStatusText 
= R
.string
.auth_connecting_auth_server
; 
 967         /// Show SAML-based SSO web dialog 
 968         String targetUrl 
= mServerInfo
.mBaseUrl
 
 969                 + AccountUtils
.getWebdavPath(mServerInfo
.mVersion
, mAuthTokenType
); 
 970         SamlWebViewDialog dialog 
= SamlWebViewDialog
.newInstance(targetUrl
, targetUrl
); 
 971         dialog
.show(getSupportFragmentManager(), SAML_DIALOG_TAG
); 
 975      * Callback method invoked when a RemoteOperation executed by this Activity finishes. 
 977      * Dispatches the operation flow to the right method. 
 980     public void onRemoteOperationFinish(RemoteOperation operation
, RemoteOperationResult result
) { 
 982         if (operation 
instanceof GetServerInfoOperation
) { 
 983             if (operation
.hashCode() == mWaitingForOpId
) { 
 984                 onGetServerInfoFinish(result
); 
 985             }   // else nothing ; only the last check operation is considered;  
 986                 // multiple can be started if the user amends a URL quickly 
 988         } else if (operation 
instanceof OAuth2GetAccessToken
) { 
 989             onGetOAuthAccessTokenFinish(result
); 
 991         } else if (operation 
instanceof GetRemoteUserNameOperation
) { 
 992             onGetUserNameFinish(result
); 
 997     private void onGetUserNameFinish(RemoteOperationResult result
) { 
 998         mWaitingForOpId 
= Long
.MAX_VALUE
; 
 999         if (result
.isSuccess()) { 
1000             boolean success 
= false
; 
1001             String username 
= (String
) result
.getData().get(0); 
1003             if ( mAction 
== ACTION_CREATE
) { 
1004                 mUsernameInput
.setText(username
); 
1005                 success 
= createAccount(); 
1008                 if (!mUsernameInput
.getText().toString().equals(username
)) { 
1009                     // fail - not a new account, but an existing one; disallow 
1010                     result 
= new RemoteOperationResult(ResultCode
.ACCOUNT_NOT_THE_SAME
); 
1012                     updateAuthStatusIconAndText(result
); 
1014                     Log_OC
.d(TAG
, result
.getLogMessage()); 
1017                         updateAccountAuthentication(); 
1020                     } catch (AccountNotFoundException e
) { 
1021                         Log_OC
.e(TAG
, "Account " + mAccount 
+ " was removed!", e
); 
1022                         Toast
.makeText(this, R
.string
.auth_account_does_not_exist
, 
1023                                 Toast
.LENGTH_SHORT
).show(); 
1032             updateStatusIconFailUserName(); 
1034             Log_OC
.e(TAG
, "Access to user name failed: " + result
.getLogMessage()); 
1040      * Processes the result of the server check performed when the user finishes the enter of the 
1043      * @param result        Result of the check. 
1045     private void onGetServerInfoFinish(RemoteOperationResult result
) { 
1046         /// update activity state 
1047         mServerIsChecked 
= true
; 
1048         mWaitingForOpId 
= Long
.MAX_VALUE
; 
1050         // update server status, but don't show it yet 
1051         updateServerStatusIconAndText(result
); 
1053         if (result
.isSuccess()) { 
1055             //      1. connection succeeded, and we know if it's SSL or not 
1056             //      2. server is installed 
1057             //      3. we got the server version 
1058             //      4. we got the authentication method required by the server  
1059             mServerInfo 
= (GetServerInfoOperation
.ServerInfo
) (result
.getData().get(0)); 
1061             if (!authSupported(mServerInfo
.mAuthMethod
)) { 
1063                 updateServerStatusIconNoRegularAuth();  // overrides updateServerStatusIconAndText()   
1064                 mServerIsValid 
= false
; 
1067                 mServerIsValid 
= true
; 
1071             mServerIsValid 
= false
; 
1075         showRefreshButton(!mServerIsValid
); 
1077         mOkButton
.setEnabled(mServerIsValid
); 
1079         /// very special case (TODO: move to a common place for all the remote operations) 
1080         if (result
.getCode() == ResultCode
.SSL_RECOVERABLE_PEER_UNVERIFIED
) { 
1081             showUntrustedCertDialog(result
); 
1086     private boolean authSupported(AuthenticationMethod authMethod
) { 
1087         return (( BASIC_TOKEN_TYPE
.equals(mAuthTokenType
) && 
1088                     AuthenticationMethod
.BASIC_HTTP_AUTH
.equals(authMethod
) ) || 
1089                 ( OAUTH_TOKEN_TYPE
.equals(mAuthTokenType
) && 
1090                     AuthenticationMethod
.BEARER_TOKEN
.equals(authMethod
)) || 
1091                 ( SAML_TOKEN_TYPE
.equals(mAuthTokenType
)  && 
1092                     AuthenticationMethod
.SAML_WEB_SSO
.equals(authMethod
)) 
1097     // TODO remove, if possible 
1098     private String 
normalizeUrl(String url
, boolean sslWhenUnprefixed
) { 
1099         if (url 
!= null 
&& url
.length() > 0) { 
1101             if (!url
.toLowerCase().startsWith("http://") && 
1102                     !url
.toLowerCase().startsWith("https://")) { 
1103                 if (sslWhenUnprefixed
) { 
1104                     url 
= "https://" + url
; 
1106                     url 
= "http://" + url
; 
1110             url 
= normalizeUrlSuffix(url
); 
1112         return (url 
!= null ? url 
: ""); 
1116     private String 
normalizeUrlSuffix(String url
) { 
1117         if (url
.endsWith("/")) { 
1118             url 
= url
.substring(0, url
.length() - 1); 
1120         url 
= trimUrlWebdav(url
); 
1125     // TODO remove, if possible 
1126     private String 
trimUrlWebdav(String url
){        
1127         if(url
.toLowerCase().endsWith(AccountUtils
.WEBDAV_PATH_4_0_AND_LATER
)){ 
1128             url 
= url
.substring(0, url
.length() - AccountUtils
.WEBDAV_PATH_4_0_AND_LATER
.length()); 
1130         return (url 
!= null ? url 
: ""); 
1135      * Chooses the right icon and text to show to the user for the received operation result. 
1137      * @param result    Result of a remote operation performed in this activity 
1139     private void updateServerStatusIconAndText(RemoteOperationResult result
) { 
1140         mServerStatusIcon 
= R
.drawable
.common_error
;    // the most common case in the switch below 
1142         switch (result
.getCode()) { 
1144             mServerStatusIcon 
= android
.R
.drawable
.ic_secure
; 
1145             mServerStatusText 
= R
.string
.auth_secure_connection
; 
1150             if (mHostUrlInput
.getText().toString().trim().toLowerCase().startsWith("http://") ) { 
1151                 mServerStatusText 
= R
.string
.auth_connection_established
; 
1152                 mServerStatusIcon 
= R
.drawable
.ic_ok
; 
1154                 mServerStatusText 
= R
.string
.auth_nossl_plain_ok_title
; 
1155                 mServerStatusIcon 
= android
.R
.drawable
.ic_partial_secure
; 
1159         case NO_NETWORK_CONNECTION
: 
1160             mServerStatusIcon 
= R
.drawable
.no_network
; 
1161             mServerStatusText 
= R
.string
.auth_no_net_conn_title
; 
1164         case SSL_RECOVERABLE_PEER_UNVERIFIED
: 
1165             mServerStatusText 
= R
.string
.auth_ssl_unverified_server_title
; 
1167         case BAD_OC_VERSION
: 
1168             mServerStatusText 
= R
.string
.auth_bad_oc_version_title
; 
1170         case WRONG_CONNECTION
: 
1171             mServerStatusText 
= R
.string
.auth_wrong_connection_title
; 
1174             mServerStatusText 
= R
.string
.auth_timeout_title
; 
1176         case INCORRECT_ADDRESS
: 
1177             mServerStatusText 
= R
.string
.auth_incorrect_address_title
; 
1180             mServerStatusText 
= R
.string
.auth_ssl_general_error_title
; 
1183             mServerStatusText 
= R
.string
.auth_unauthorized
; 
1185         case HOST_NOT_AVAILABLE
: 
1186             mServerStatusText 
= R
.string
.auth_unknown_host_title
; 
1188         case INSTANCE_NOT_CONFIGURED
: 
1189             mServerStatusText 
= R
.string
.auth_not_configured_title
; 
1191         case FILE_NOT_FOUND
: 
1192             mServerStatusText 
= R
.string
.auth_incorrect_path_title
; 
1195             mServerStatusText 
= R
.string
.auth_oauth_error
; 
1197         case OAUTH2_ERROR_ACCESS_DENIED
: 
1198             mServerStatusText 
= R
.string
.auth_oauth_error_access_denied
; 
1200         case UNHANDLED_HTTP_CODE
: 
1202             mServerStatusText 
= R
.string
.auth_unknown_error_title
; 
1204         case OK_REDIRECT_TO_NON_SECURE_CONNECTION
: 
1205             mServerStatusIcon 
= android
.R
.drawable
.ic_partial_secure
; 
1206             mServerStatusText 
= R
.string
.auth_redirect_non_secure_connection_title
; 
1209             mServerStatusText 
= 0; 
1210             mServerStatusIcon 
= 0; 
1216      * Chooses the right icon and text to show to the user for the received operation result. 
1218      * @param result    Result of a remote operation performed in this activity 
1220     private void updateAuthStatusIconAndText(RemoteOperationResult result
) { 
1221         mAuthStatusIcon 
= R
.drawable
.common_error
;    // the most common case in the switch below 
1223         switch (result
.getCode()) { 
1225             mAuthStatusIcon 
= android
.R
.drawable
.ic_secure
; 
1226             mAuthStatusText 
= R
.string
.auth_secure_connection
; 
1231             if (mHostUrlInput
.getText().toString().trim().toLowerCase().startsWith("http://") ) { 
1232                 mAuthStatusText 
= R
.string
.auth_connection_established
; 
1233                 mAuthStatusIcon 
= R
.drawable
.ic_ok
; 
1235                 mAuthStatusText 
= R
.string
.auth_nossl_plain_ok_title
; 
1236                 mAuthStatusIcon 
= android
.R
.drawable
.ic_partial_secure
; 
1240         case NO_NETWORK_CONNECTION
: 
1241             mAuthStatusIcon 
= R
.drawable
.no_network
; 
1242             mAuthStatusText 
= R
.string
.auth_no_net_conn_title
; 
1245         case SSL_RECOVERABLE_PEER_UNVERIFIED
: 
1246             mAuthStatusText 
= R
.string
.auth_ssl_unverified_server_title
; 
1248         case BAD_OC_VERSION
: 
1249             mAuthStatusText 
= R
.string
.auth_bad_oc_version_title
; 
1251         case WRONG_CONNECTION
: 
1252             mAuthStatusText 
= R
.string
.auth_wrong_connection_title
; 
1255             mAuthStatusText 
= R
.string
.auth_timeout_title
; 
1257         case INCORRECT_ADDRESS
: 
1258             mAuthStatusText 
= R
.string
.auth_incorrect_address_title
; 
1261             mAuthStatusText 
= R
.string
.auth_ssl_general_error_title
; 
1264             mAuthStatusText 
= R
.string
.auth_unauthorized
; 
1266         case HOST_NOT_AVAILABLE
: 
1267             mAuthStatusText 
= R
.string
.auth_unknown_host_title
; 
1269         case INSTANCE_NOT_CONFIGURED
: 
1270             mAuthStatusText 
= R
.string
.auth_not_configured_title
; 
1272         case FILE_NOT_FOUND
: 
1273             mAuthStatusText 
= R
.string
.auth_incorrect_path_title
; 
1276             mAuthStatusText 
= R
.string
.auth_oauth_error
; 
1278         case OAUTH2_ERROR_ACCESS_DENIED
: 
1279             mAuthStatusText 
= R
.string
.auth_oauth_error_access_denied
; 
1281         case ACCOUNT_NOT_NEW
: 
1282             mAuthStatusText 
= R
.string
.auth_account_not_new
; 
1284         case ACCOUNT_NOT_THE_SAME
: 
1285             mAuthStatusText 
= R
.string
.auth_account_not_the_same
; 
1287         case UNHANDLED_HTTP_CODE
: 
1289             mAuthStatusText 
= R
.string
.auth_unknown_error_title
; 
1292             mAuthStatusText 
= 0; 
1293             mAuthStatusIcon 
= 0; 
1298     private void updateStatusIconFailUserName(){ 
1299         mAuthStatusIcon 
= R
.drawable
.common_error
; 
1300         mAuthStatusText 
= R
.string
.auth_fail_get_user_name
; 
1303     private void updateServerStatusIconNoRegularAuth(){ 
1304         mServerStatusIcon 
= R
.drawable
.common_error
; 
1305         mServerStatusText 
= R
.string
.auth_can_not_auth_against_server
; 
1309      * Processes the result of the request for and access token send  
1310      * to an OAuth authorization server. 
1312      * @param result        Result of the operation. 
1314     private void onGetOAuthAccessTokenFinish(RemoteOperationResult result
) { 
1315         mWaitingForOpId 
= Long
.MAX_VALUE
; 
1316         dismissDialog(WAIT_DIALOG_TAG
); 
1318         if (result
.isSuccess()) { 
1319             /// be gentle with the user 
1320             IndeterminateProgressDialog dialog 
=  
1321                     IndeterminateProgressDialog
.newInstance(R
.string
.auth_trying_to_login
, true
); 
1322             dialog
.show(getSupportFragmentManager(), WAIT_DIALOG_TAG
); 
1324             /// time to test the retrieved access token on the ownCloud server 
1325             @SuppressWarnings("unchecked") 
1326             Map
<String
, String
> tokens 
= (Map
<String
, String
>)(result
.getData().get(0)); 
1327             mAuthToken 
= tokens
.get(OAuth2Constants
.KEY_ACCESS_TOKEN
); 
1328             Log_OC
.d(TAG
, "Got ACCESS TOKEN: " + mAuthToken
); 
1330             /// validate token accessing to root folder / getting session 
1331             OwnCloudCredentials credentials 
= OwnCloudCredentialsFactory
.newBearerCredentials( 
1333             accessRootFolder(credentials
); 
1336             updateAuthStatusIconAndText(result
); 
1338             Log_OC
.d(TAG
, "Access failed: " + result
.getLogMessage()); 
1344      * Processes the result of the access check performed to try the user credentials. 
1346      * Creates a new account through the AccountManager. 
1348      * @param result        Result of the operation. 
1351     public void onAuthenticatorTaskCallback(RemoteOperationResult result
) { 
1352         mWaitingForOpId 
= Long
.MAX_VALUE
; 
1353         dismissDialog(WAIT_DIALOG_TAG
); 
1355         if (result
.isSuccess()) { 
1356             Log_OC
.d(TAG
, "Successful access - time to save the account"); 
1358             boolean success 
= false
; 
1359             if (mAction 
== ACTION_CREATE
) { 
1360                 success 
= createAccount(); 
1364                     updateAccountAuthentication(); 
1367                 } catch (AccountNotFoundException e
) { 
1368                     Log_OC
.e(TAG
, "Account " + mAccount 
+ " was removed!", e
); 
1369                     Toast
.makeText(this, R
.string
.auth_account_does_not_exist
, 
1370                             Toast
.LENGTH_SHORT
).show(); 
1379         } else if (result
.isServerFail() || result
.isException()) { 
1380             /// server errors or exceptions in authorization take to requiring a new check of  
1382             mServerIsChecked 
= true
; 
1383             mServerIsValid 
= false
; 
1384             mServerInfo 
= new GetServerInfoOperation
.ServerInfo();   
1386             // update status icon and text 
1387             updateServerStatusIconAndText(result
); 
1389             mAuthStatusIcon 
= 0; 
1390             mAuthStatusText 
= 0; 
1393             // update input controls state 
1394             showRefreshButton(true
); 
1395             mOkButton
.setEnabled(false
); 
1397             // very special case (TODO: move to a common place for all the remote operations) 
1398             if (result
.getCode() == ResultCode
.SSL_RECOVERABLE_PEER_UNVERIFIED
) { 
1399                 showUntrustedCertDialog(result
); 
1402         } else {    // authorization fail due to client side - probably wrong credentials 
1403             updateAuthStatusIconAndText(result
); 
1405             Log_OC
.d(TAG
, "Access failed: " + result
.getLogMessage()); 
1413      * Updates the authentication token. 
1415      * Sets the proper response so that the AccountAuthenticator that started this activity 
1416      * saves a new authorization token for mAccount. 
1418      * Kills the session kept by OwnCloudClientManager so that a new one will created with 
1419      * the new credentials when needed. 
1421     private void updateAccountAuthentication() throws AccountNotFoundException 
{ 
1423         Bundle response 
= new Bundle(); 
1424         response
.putString(AccountManager
.KEY_ACCOUNT_NAME
, mAccount
.name
); 
1425         response
.putString(AccountManager
.KEY_ACCOUNT_TYPE
, mAccount
.type
); 
1427         if (AccountTypeUtils
.getAuthTokenTypeAccessToken(MainApp
.getAccountType()). 
1428                 equals(mAuthTokenType
)) {  
1429             response
.putString(AccountManager
.KEY_AUTHTOKEN
, mAuthToken
); 
1430             // the next line is necessary, notifications are calling directly to the  
1431             // AuthenticatorActivity to update, without AccountManager intervention 
1432             mAccountMgr
.setAuthToken(mAccount
, mAuthTokenType
, mAuthToken
); 
1434         } else if (AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()). 
1435                 equals(mAuthTokenType
)) { 
1437             response
.putString(AccountManager
.KEY_AUTHTOKEN
, mAuthToken
); 
1438             // the next line is necessary; by now, notifications are calling directly to the  
1439             // AuthenticatorActivity to update, without AccountManager intervention 
1440             mAccountMgr
.setAuthToken(mAccount
, mAuthTokenType
, mAuthToken
); 
1443             response
.putString(AccountManager
.KEY_AUTHTOKEN
, mPasswordInput
.getText().toString()); 
1444             mAccountMgr
.setPassword(mAccount
, mPasswordInput
.getText().toString()); 
1446         setAccountAuthenticatorResult(response
); 
1452      * Creates a new account through the Account Authenticator that started this activity.  
1454      * This makes the account permanent. 
1456      * TODO Decide how to name the OAuth accounts 
1458     private boolean createAccount() { 
1459         /// create and save new ownCloud account 
1460         boolean isOAuth 
= AccountTypeUtils
. 
1461                 getAuthTokenTypeAccessToken(MainApp
.getAccountType()).equals(mAuthTokenType
); 
1462         boolean isSaml 
=  AccountTypeUtils
. 
1463                 getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()).equals(mAuthTokenType
); 
1465         Uri uri 
= Uri
.parse(mServerInfo
.mBaseUrl
); 
1466         String username 
= mUsernameInput
.getText().toString().trim(); 
1468             username 
= "OAuth_user" + (new java
.util
.Random(System
.currentTimeMillis())).nextLong(); 
1470         String accountName 
= com
.owncloud
.android
.lib
.common
.accounts
.AccountUtils
. 
1471                 buildAccountName(uri
, username
); 
1472         Account newAccount 
= new Account(accountName
, MainApp
.getAccountType()); 
1473         if (AccountUtils
.exists(newAccount
, getApplicationContext())) { 
1474             // fail - not a new account, but an existing one; disallow 
1475             RemoteOperationResult result 
= new RemoteOperationResult(ResultCode
.ACCOUNT_NOT_NEW
);  
1476             updateAuthStatusIconAndText(result
); 
1478             Log_OC
.d(TAG
, result
.getLogMessage()); 
1482             mAccount 
= newAccount
; 
1484             if (isOAuth 
|| isSaml
) { 
1485                 // with external authorizations, the password is never input in the app 
1486                 mAccountMgr
.addAccountExplicitly(mAccount
, "", null
);   
1488                 mAccountMgr
.addAccountExplicitly( 
1489                         mAccount
, mPasswordInput
.getText().toString(), null
 
1493             // include account version with the new account 
1494             mAccountMgr
.setUserData( 
1496                 Constants
.KEY_OC_ACCOUNT_VERSION
, 
1497                 Integer
.toString(AccountUtils
.ACCOUNT_VERSION
) 
1500             /// add the new account as default in preferences, if there is none already 
1501             Account defaultAccount 
= AccountUtils
.getCurrentOwnCloudAccount(this); 
1502             if (defaultAccount 
== null
) { 
1503                 SharedPreferences
.Editor editor 
= PreferenceManager
 
1504                         .getDefaultSharedPreferences(this).edit(); 
1505                 editor
.putString("select_oc_account", accountName
); 
1509             /// prepare result to return to the Authenticator 
1510             //  TODO check again what the Authenticator makes with it; probably has the same  
1511             //  effect as addAccountExplicitly, but it's not well done 
1512             final Intent intent 
= new Intent();        
1513             intent
.putExtra(AccountManager
.KEY_ACCOUNT_TYPE
,    MainApp
.getAccountType()); 
1514             intent
.putExtra(AccountManager
.KEY_ACCOUNT_NAME
,    mAccount
.name
); 
1515             intent
.putExtra(AccountManager
.KEY_USERDATA
,        username
); 
1516             if (isOAuth 
|| isSaml
) { 
1517                 mAccountMgr
.setAuthToken(mAccount
, mAuthTokenType
, mAuthToken
); 
1519             /// add user data to the new account; TODO probably can be done in the last parameter  
1520             //      addAccountExplicitly, or in KEY_USERDATA 
1521             mAccountMgr
.setUserData( 
1522                     mAccount
, Constants
.KEY_OC_VERSION
,    mServerInfo
.mVersion
.getVersion() 
1524             mAccountMgr
.setUserData( 
1525                     mAccount
, Constants
.KEY_OC_BASE_URL
,   mServerInfo
.mBaseUrl
 
1529                 mAccountMgr
.setUserData(mAccount
, Constants
.KEY_SUPPORTS_SAML_WEB_SSO
, "TRUE");  
1530             } else if (isOAuth
) { 
1531                 mAccountMgr
.setUserData(mAccount
, Constants
.KEY_SUPPORTS_OAUTH2
, "TRUE");   
1534             setAccountAuthenticatorResult(intent
.getExtras()); 
1535             setResult(RESULT_OK
, intent
); 
1543      * Starts and activity to open the 'new account' page in the ownCloud web site 
1545      * @param view      'Account register' button 
1547     public void onRegisterClick(View view
) { 
1548         Intent register 
= new Intent( 
1549                 Intent
.ACTION_VIEW
, Uri
.parse(getString(R
.string
.welcome_link_url
)) 
1551         setResult(RESULT_CANCELED
); 
1552         startActivity(register
); 
1557      * Updates the content and visibility state of the icon and text associated 
1558      * to the last check on the ownCloud server. 
1561     private void showServerStatus() { 
1562         if (mServerStatusIcon 
== 0 && mServerStatusText 
== 0) { 
1563             mServerStatusView
.setVisibility(View
.INVISIBLE
); 
1566             mServerStatusView
.setText(mServerStatusText
); 
1567             mServerStatusView
.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon
, 0, 0, 0); 
1568             mServerStatusView
.setVisibility(View
.VISIBLE
); 
1575      * Updates the content and visibility state of the icon and text associated 
1576      * to the interactions with the OAuth authorization server. 
1578     private void showAuthStatus() { 
1579         if (mAuthStatusIcon 
== 0 && mAuthStatusText 
== 0) { 
1580             mAuthStatusView
.setVisibility(View
.INVISIBLE
); 
1583             mAuthStatusView
.setText(mAuthStatusText
); 
1584             mAuthStatusView
.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon
, 0, 0, 0); 
1585             mAuthStatusView
.setVisibility(View
.VISIBLE
); 
1590     private void showRefreshButton (boolean show
) { 
1592             mRefreshButton
.setVisibility(View
.VISIBLE
); 
1594             mRefreshButton
.setVisibility(View
.GONE
); 
1599      * Called when the refresh button in the input field for ownCloud host is clicked. 
1601      * Performs a new check on the URL in the input field. 
1603      * @param view      Refresh 'button' 
1605     public void onRefreshClick(View view
) { 
1611      * Called when the eye icon in the password field is clicked. 
1613      * Toggles the visibility of the password in the field.  
1615     public void onViewPasswordClick() { 
1616         int selectionStart 
= mPasswordInput
.getSelectionStart(); 
1617         int selectionEnd 
= mPasswordInput
.getSelectionEnd(); 
1618         if (isPasswordVisible()) { 
1623         mPasswordInput
.setSelection(selectionStart
, selectionEnd
); 
1628      * Called when the checkbox for OAuth authorization is clicked. 
1630      * Hides or shows the input fields for user & password.  
1632      * @param view      'View password' 'button' 
1634     public void onCheckClick(View view
) { 
1635         CheckBox oAuth2Check 
= (CheckBox
)view
; 
1636         if (oAuth2Check
.isChecked()) { 
1637             mAuthTokenType 
= OAUTH_TOKEN_TYPE
; 
1639             mAuthTokenType 
= BASIC_TOKEN_TYPE
; 
1641         updateAuthenticationPreFragmentVisibility(); 
1646      *  Called when the 'action' button in an IME is pressed ('enter' in software keyboard). 
1648      *  Used to trigger the authentication check when the user presses 'enter' after writing the  
1649      *  password, or to throw the server test when the only field on screen is the URL input field. 
1652     public boolean onEditorAction(TextView inputField
, int actionId
, KeyEvent event
) { 
1653         if (actionId 
== EditorInfo
.IME_ACTION_DONE 
&& inputField 
!= null 
&&  
1654                 inputField
.equals(mPasswordInput
)) { 
1655             if (mOkButton
.isEnabled()) { 
1656                 mOkButton
.performClick(); 
1659         } else if (actionId 
== EditorInfo
.IME_ACTION_NEXT 
&& inputField 
!= null 
&&  
1660                 inputField
.equals(mHostUrlInput
)) { 
1661             if (AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()). 
1662                     equals(mAuthTokenType
)) { 
1666         return false
;   // always return false to grant that the software keyboard is hidden anyway 
1670     private abstract static class RightDrawableOnTouchListener 
implements OnTouchListener  
{ 
1672         private int fuzz 
= 75; 
1678         public boolean onTouch(View view
, MotionEvent event
) { 
1679             Drawable rightDrawable 
= null
; 
1680             if (view 
instanceof TextView
) { 
1681                 Drawable
[] drawables 
= ((TextView
)view
).getCompoundDrawables(); 
1682                 if (drawables
.length 
> 2) { 
1683                     rightDrawable 
= drawables
[2]; 
1686             if (rightDrawable 
!= null
) { 
1687                 final int x 
= (int) event
.getX(); 
1688                 final int y 
= (int) event
.getY(); 
1689                 final Rect bounds 
= rightDrawable
.getBounds(); 
1690                 if (    x 
>= (view
.getRight() - bounds
.width() - fuzz
) &&  
1691                         x 
<= (view
.getRight() - view
.getPaddingRight() + fuzz
) &&  
1692                         y 
>= (view
.getPaddingTop() - fuzz
) && 
1693                         y 
<= (view
.getHeight() - view
.getPaddingBottom()) + fuzz
) { 
1695                     return onDrawableTouch(event
); 
1701         public abstract boolean onDrawableTouch(final MotionEvent event
); 
1705     private void getRemoteUserNameOperation(String sessionCookie
, boolean followRedirects
) { 
1707         Intent getUserNameIntent 
= new Intent(); 
1708         getUserNameIntent
.setAction(OperationsService
.ACTION_GET_USER_NAME
); 
1709         getUserNameIntent
.putExtra(OperationsService
.EXTRA_SERVER_URL
, mServerInfo
.mBaseUrl
); 
1710         getUserNameIntent
.putExtra(OperationsService
.EXTRA_COOKIE
, sessionCookie
); 
1712         if (mOperationsServiceBinder 
!= null
) { 
1713             mWaitingForOpId 
= mOperationsServiceBinder
.queueNewOperation(getUserNameIntent
); 
1719     public void onSsoFinished(String sessionCookie
) { 
1720         if (sessionCookie 
!= null 
&& sessionCookie
.length() > 0) { 
1721             Log_OC
.d(TAG
, "Successful SSO - time to save the account"); 
1722             mAuthToken 
= sessionCookie
; 
1723             getRemoteUserNameOperation(sessionCookie
, true
); 
1724             Fragment fd 
= getSupportFragmentManager().findFragmentByTag(SAML_DIALOG_TAG
); 
1725             if (fd 
!= null 
&& fd 
instanceof DialogFragment
) { 
1726                 Dialog d 
= ((DialogFragment
)fd
).getDialog(); 
1727                 if (d 
!= null 
&& d
.isShowing()) { 
1734             Log_OC
.d(TAG
, "SSO failed"); 
1740     public boolean onTouchEvent(MotionEvent event
) { 
1741         if (AccountTypeUtils
.getAuthTokenTypeSamlSessionCookie(MainApp
.getAccountType()). 
1742                 equals(mAuthTokenType
) && 
1743                 mHostUrlInput
.hasFocus() && event
.getAction() == MotionEvent
.ACTION_DOWN
) { 
1746         return super.onTouchEvent(event
); 
1751      * Show untrusted cert dialog  
1753     public void showUntrustedCertDialog( 
1754             X509Certificate x509Certificate
, SslError error
, SslErrorHandler handler
 
1756         // Show a dialog with the certificate info 
1757         SslUntrustedCertDialog dialog 
= null
; 
1758         if (x509Certificate 
== null
) { 
1759             dialog 
= SslUntrustedCertDialog
.newInstanceForEmptySslError(error
, handler
); 
1761             dialog 
= SslUntrustedCertDialog
. 
1762                     newInstanceForFullSslError(x509Certificate
, error
, handler
); 
1764         FragmentManager fm 
= getSupportFragmentManager(); 
1765         FragmentTransaction ft 
= fm
.beginTransaction(); 
1766         ft
.addToBackStack(null
); 
1767         dialog
.show(ft
, UNTRUSTED_CERT_DIALOG_TAG
); 
1772      * Show untrusted cert dialog  
1774     private void showUntrustedCertDialog(RemoteOperationResult result
) { 
1775         // Show a dialog with the certificate info 
1776         SslUntrustedCertDialog dialog 
= SslUntrustedCertDialog
. 
1777                 newInstanceForFullSslError((CertificateCombinedException
)result
.getException()); 
1778         FragmentManager fm 
= getSupportFragmentManager(); 
1779         FragmentTransaction ft 
= fm
.beginTransaction(); 
1780         ft
.addToBackStack(null
); 
1781         dialog
.show(ft
, UNTRUSTED_CERT_DIALOG_TAG
); 
1786      * Called from SslValidatorDialog when a new server certificate was correctly saved. 
1788     public void onSavedCertificate() { 
1789         Fragment fd 
= getSupportFragmentManager().findFragmentByTag(SAML_DIALOG_TAG
); 
1791             // if SAML dialog is not shown,  
1792             // the SslDialog was shown due to an SSL error in the server check 
1798      * Called from SslValidatorDialog when a new server certificate could not be saved  
1799      * when the user requested it. 
1802     public void onFailedSavingCertificate() { 
1803         dismissDialog(SAML_DIALOG_TAG
); 
1804         Toast
.makeText(this, R
.string
.ssl_validator_not_saved
, Toast
.LENGTH_LONG
).show(); 
1808     public void onCancelCertificate() { 
1809         dismissDialog(SAML_DIALOG_TAG
); 
1813     private void doOnResumeAndBound() { 
1814         //Log_OC.wtf(TAG, "registering to listen for operation callbacks" ); 
1815         mOperationsServiceBinder
.addOperationListener(AuthenticatorActivity
.this, mHandler
); 
1816         if (mWaitingForOpId 
<= Integer
.MAX_VALUE
) { 
1817             mOperationsServiceBinder
.dispatchResultIfFinished((int)mWaitingForOpId
, this); 
1820         if (mPendingAutoCheck
) { 
1826     private void dismissDialog(String dialogTag
){ 
1827         Fragment frag 
= getSupportFragmentManager().findFragmentByTag(dialogTag
); 
1828         if (frag 
!= null 
&& frag 
instanceof DialogFragment
) { 
1829             DialogFragment dialog 
= (DialogFragment
) frag
; 
1836      * Implements callback methods for service binding.  
1838     private class OperationsServiceConnection 
implements ServiceConnection 
{ 
1841         public void onServiceConnected(ComponentName component
, IBinder service
) { 
1842             if (component
.equals( 
1843                     new ComponentName(AuthenticatorActivity
.this, OperationsService
.class) 
1845                 mOperationsServiceBinder 
= (OperationsServiceBinder
) service
; 
1847                 doOnResumeAndBound(); 
1856         public void onServiceDisconnected(ComponentName component
) { 
1857             if (component
.equals( 
1858                     new ComponentName(AuthenticatorActivity
.this, OperationsService
.class) 
1860                 Log_OC
.e(TAG
, "Operations service crashed"); 
1861                 mOperationsServiceBinder 
= null
; 
1868      * Create and show dialog for request authentication to the user 
1872     public void createAuthenticationDialog(WebView webView
, HttpAuthHandler handler
) { 
1874         // Show a dialog with the certificate info 
1875         CredentialsDialogFragment dialog 
=  
1876                 CredentialsDialogFragment
.newInstanceForCredentials(webView
, handler
); 
1877         FragmentManager fm 
= getSupportFragmentManager(); 
1878         FragmentTransaction ft 
= fm
.beginTransaction(); 
1879         ft
.addToBackStack(null
); 
1880         dialog
.setCancelable(false
); 
1881         dialog
.show(ft
, CREDENTIALS_DIALOG_TAG
); 
1883         if (!mIsFirstAuthAttempt
) { 
1885                     getApplicationContext(),  
1886                     getText(R
.string
.saml_authentication_wrong_pass
),  
1890             mIsFirstAuthAttempt 
= false
; 
1895      * For retrieving the clicking on authentication cancel button 
1897     public void doNegativeAuthenticatioDialogClick(){ 
1898         mIsFirstAuthAttempt 
= true
;