Эх сурвалжийг харах

Merge pull request #771 from nextcloud/web-login

Add support for web login
Andy Scherzinger 8 жил өмнө
parent
commit
89409ce049

+ 319 - 173
src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -4,8 +4,10 @@
  * @author Bartek Przybylski
  * @author Bartek Przybylski
  * @author David A. Velasco
  * @author David A. Velasco
  * @author masensio
  * @author masensio
+ * @author Mario Danic
  * Copyright (C) 2012  Bartek Przybylski
  * Copyright (C) 2012  Bartek Przybylski
  * Copyright (C) 2015 ownCloud Inc.
  * Copyright (C) 2015 ownCloud Inc.
+ * Copyright (C) 2017 Mario Danic
  *
  *
  * This program is free software: you can redistribute it and/or modify
  * This program is free software: you can redistribute it and/or modify
  * it under the terms of the GNU General Public License version 2,
  * it under the terms of the GNU General Public License version 2,
@@ -18,6 +20,22 @@
  *
  *
  * You should have received a copy of the GNU General Public License
  * You should have received a copy of the GNU General Public License
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * All changes by Mario Danic are distributed under the following terms:
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Affero General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU Affero General Public License for more details.
+ *
+ * You should have received a copy of the GNU Affero General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
  */
  */
 
 
 package com.owncloud.android.authentication;
 package com.owncloud.android.authentication;
@@ -30,6 +48,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.net.Uri;
@@ -38,12 +57,14 @@ import android.os.Bundle;
 import android.os.Handler;
 import android.os.Handler;
 import android.os.IBinder;
 import android.os.IBinder;
 import android.preference.PreferenceManager;
 import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
 import android.support.v4.app.FragmentTransaction;
 import android.text.Editable;
 import android.text.Editable;
 import android.text.InputType;
 import android.text.InputType;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
 import android.view.MotionEvent;
@@ -54,6 +75,7 @@ import android.view.inputmethod.EditorInfo;
 import android.webkit.HttpAuthHandler;
 import android.webkit.HttpAuthHandler;
 import android.webkit.SslErrorHandler;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebView;
 import android.webkit.WebView;
+import android.webkit.WebViewClient;
 import android.widget.Button;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.CheckBox;
 import android.widget.EditText;
 import android.widget.EditText;
@@ -191,6 +213,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private View mOkButton;
     private View mOkButton;
     private TextView mAuthStatusView;
     private TextView mAuthStatusView;
 
 
+    private WebView mLoginWebView;
+
     private int mAuthStatusText = 0, mAuthStatusIcon = 0;
     private int mAuthStatusText = 0, mAuthStatusIcon = 0;
 
 
     private String mAuthToken = "";
     private String mAuthToken = "";
@@ -205,6 +229,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private final String OAUTH_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());
     private final String OAUTH_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeAccessToken(MainApp.getAccountType());
     private final String SAML_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());
     private final String SAML_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());
 
 
+    private boolean webViewLoginMethod;
+    private String webViewUser;
+    private String webViewPassword;
+
     /**
     /**
      * {@inheritDoc}
      * {@inheritDoc}
      *
      *
@@ -242,42 +270,86 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG);
             mIsFirstAuthAttempt = savedInstanceState.getBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG);
         }
         }
 
 
+        webViewLoginMethod = !TextUtils.isEmpty(getResources().getString(R.string.webview_login_url));
+
+        if (webViewLoginMethod) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
+        }
+
         /// load user interface
         /// load user interface
-        setContentView(R.layout.account_setup);
+        if (!webViewLoginMethod) {
+            setContentView(R.layout.account_setup);
 
 
-        /// initialize general UI elements
-        initOverallUi();
+            /// initialize general UI elements
+            initOverallUi();
 
 
-        mOkButton = findViewById(R.id.buttonOK);
-        mOkButton.setOnClickListener(new View.OnClickListener() {
+            mOkButton = findViewById(R.id.buttonOK);
+            mOkButton.setOnClickListener(new View.OnClickListener() {
 
 
-            @Override
-            public void onClick(View v) {
-                onOkClick();
-            }
-        });
+                @Override
+                public void onClick(View v) {
+                    onOkClick();
+                }
+            });
 
 
-        findViewById(R.id.centeredRefreshButton).setOnClickListener(new View.OnClickListener() {
+            findViewById(R.id.centeredRefreshButton).setOnClickListener(new View.OnClickListener() {
 
 
-            @Override
-            public void onClick(View v) {
-                checkOcServer();
-            }
-        });
+                @Override
+                public void onClick(View v) {
+                    checkOcServer();
+                }
+            });
+
+            findViewById(R.id.embeddedRefreshButton).setOnClickListener(new View.OnClickListener() {
+
+                @Override
+                public void onClick(View v) {
+                    checkOcServer();
+                }
+            });
+
+            /// initialize block to be moved to single Fragment to check server and get info about it
+
+            /// initialize block to be moved to single Fragment to retrieve and validate credentials
+            initAuthorizationPreFragment(savedInstanceState);
 
 
-        findViewById(R.id.embeddedRefreshButton).setOnClickListener(new View.OnClickListener() {
+        } else {
+            setContentView(R.layout.account_setup_webview);
+            mLoginWebView = (WebView) findViewById(R.id.login_webview);
+            initWebViewLogin();
+        }
 
 
+        initServerPreFragment(savedInstanceState);
+    }
+
+    private void initWebViewLogin() {
+        mLoginWebView.getSettings().setAllowFileAccess(false);
+        mLoginWebView.getSettings().setJavaScriptEnabled(true);
+        mLoginWebView.getSettings().setUserAgentString(MainApp.getUserAgent());
+        mLoginWebView.loadUrl(getResources().getString(R.string.webview_login_url));
+        mLoginWebView.setWebViewClient(new WebViewClient() {
             @Override
             @Override
-            public void onClick(View v) {
-                checkOcServer();
+            public boolean shouldOverrideUrlLoading(WebView view, String url) {
+                if (url.startsWith(getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/")) {
+                    parseAndLoginFromWebView(url);
+                    return true;
+                }
+                return false;
             }
             }
         });
         });
+    }
 
 
-        /// initialize block to be moved to single Fragment to check server and get info about it 
-        initServerPreFragment(savedInstanceState);
+    private void parseAndLoginFromWebView(String dataString) {
+        String prefix = getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/";
+        LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString);
+
+        if (loginUrlInfo != null) {
+            mServerInfo.mBaseUrl = normalizeUrlSuffix(loginUrlInfo.serverAddress);
+            webViewUser = loginUrlInfo.username;
+            webViewPassword = loginUrlInfo.password;
+            checkOcServer();
+        }
 
 
-        /// initialize block to be moved to single Fragment to retrieve and validate credentials 
-        initAuthorizationPreFragment(savedInstanceState);
     }
     }
 
 
     private void populateLoginFields(String dataString) throws IllegalArgumentException {
     private void populateLoginFields(String dataString) throws IllegalArgumentException {
@@ -301,7 +373,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     /**
     /**
      * parses a URI string and returns a login data object with the information from the URI string.
      * parses a URI string and returns a login data object with the information from the URI string.
      *
      *
-     * @param prefix URI beginning, e.g. cloud://login/
+     * @param prefix     URI beginning, e.g. cloud://login/
      * @param dataString the complete URI
      * @param dataString the complete URI
      * @return login data
      * @return login data
      * @throws IllegalArgumentException when
      * @throws IllegalArgumentException when
@@ -420,7 +492,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 mServerInfo.mIsSslConn = mServerInfo.mBaseUrl.startsWith(HTTPS_PROTOCOL);
                 mServerInfo.mIsSslConn = mServerInfo.mBaseUrl.startsWith(HTTPS_PROTOCOL);
                 mServerInfo.mVersion = AccountUtils.getServerVersion(mAccount);
                 mServerInfo.mVersion = AccountUtils.getServerVersion(mAccount);
             } else {
             } else {
-                mServerInfo.mBaseUrl = getString(R.string.server_url).trim();
+                if (!webViewLoginMethod) {
+                    mServerInfo.mBaseUrl = getString(R.string.server_url).trim();
+                } else {
+                    mServerInfo.mBaseUrl = getString(R.string.webview_login_url).trim();
+                }
                 mServerInfo.mIsSslConn = mServerInfo.mBaseUrl.startsWith(HTTPS_PROTOCOL);
                 mServerInfo.mIsSslConn = mServerInfo.mBaseUrl.startsWith(HTTPS_PROTOCOL);
             }
             }
         } else {
         } else {
@@ -442,81 +518,74 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 
         }
         }
 
 
-        /// step 2 - set properties of UI elements (text, visibility, enabled...)
-        mHostUrlInput = (CustomEditText) findViewById(R.id.hostUrlInput);
-        // Convert IDN to Unicode
-        mHostUrlInput.setText(DisplayUtils.convertIdn(mServerInfo.mBaseUrl, false));
-        if (mAction != ACTION_CREATE) {
-            /// lock things that should not change
-            mHostUrlInput.setEnabled(false);
-            mHostUrlInput.setFocusable(false);
-        }
-
-        String serverInputType = getResources().getString(R.string.server_input_type);
-
-        if (isUrlInputAllowed) {
-            mRefreshButton = findViewById(R.id.embeddedRefreshButton);
-            if (mAction == ACTION_CREATE &&
-                    (serverInputType.equals(DIRECTORY_SERVER_INPUT_TYPE) ||
-                    serverInputType.equals(SUBDOMAIN_SERVER_INPUT_TYPE))) {
-                mHostUrlInput.setText("");
+        if (!webViewLoginMethod) {
+            /// step 2 - set properties of UI elements (text, visibility, enabled...)
+            mHostUrlInput = (CustomEditText) findViewById(R.id.hostUrlInput);
+            // Convert IDN to Unicode
+            mHostUrlInput.setText(DisplayUtils.convertIdn(mServerInfo.mBaseUrl, false));
+            if (mAction != ACTION_CREATE) {
+                /// lock things that should not change
+                mHostUrlInput.setEnabled(false);
+                mHostUrlInput.setFocusable(false);
             }
             }
-        } else {
-            findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);
-            mRefreshButton = findViewById(R.id.centeredRefreshButton);
-        }
-
-        showRefreshButton(mServerIsChecked && !mServerIsValid &&
-                mWaitingForOpId > Integer.MAX_VALUE);
-        mServerStatusView = (TextView) findViewById(R.id.server_status_text);
-        showServerStatus();
+            if (isUrlInputAllowed) {
+                mRefreshButton = findViewById(R.id.embeddedRefreshButton);
+            } else {
+                findViewById(R.id.hostUrlFrame).setVisibility(View.GONE);
+                mRefreshButton = findViewById(R.id.centeredRefreshButton);
+            }
+            showRefreshButton(mServerIsChecked && !mServerIsValid &&
+                    mWaitingForOpId > Integer.MAX_VALUE);
+            mServerStatusView = (TextView) findViewById(R.id.server_status_text);
+            showServerStatus();
 
 
-        /// step 3 - bind some listeners and options
-        mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
-        mHostUrlInput.setOnEditorActionListener(this);
+            /// step 3 - bind some listeners and options
+            mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
+            mHostUrlInput.setOnEditorActionListener(this);
 
 
-        /// step 4 - create listeners that will be bound at onResume
-        mHostUrlInputWatcher = new TextWatcher() {
+            /// step 4 - create listeners that will be bound at onResume
+            mHostUrlInputWatcher = new TextWatcher() {
 
 
-            @Override
-            public void afterTextChanged(Editable s) {
-                if (mOkButton.isEnabled() &&
-                        !mServerInfo.mBaseUrl.equals(
-                                normalizeUrl(mHostUrlInput.getFullServerUrl(), mServerInfo.mIsSslConn))) {
-                    mOkButton.setEnabled(false);
+                @Override
+                public void afterTextChanged(Editable s) {
+                    if (mOkButton.isEnabled() &&
+                            !mServerInfo.mBaseUrl.equals(
+                                    normalizeUrl(s.toString(), mServerInfo.mIsSslConn))) {
+                        mOkButton.setEnabled(false);
+                    }
                 }
                 }
-            }
 
 
-            @Override
-            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-            }
-
-            @Override
-            public void onTextChanged(CharSequence s, int start, int before, int count) {
-                if (mAuthStatusIcon != 0) {
-                    Log_OC.d(TAG, "onTextChanged: hiding authentication status");
-                    mAuthStatusIcon = 0;
-                    mAuthStatusText = 0;
-                    showAuthStatus();
+                @Override
+                public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                 }
                 }
-            }
-        };
-
 
 
-        // TODO find out if this is really necessary, or if it can done in a different way
-        findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {
-            @Override
-            public boolean onTouch(View view, MotionEvent event) {
-                if (event.getAction() == MotionEvent.ACTION_DOWN &&
-                        AccountTypeUtils
-                                .getAuthTokenTypeSamlSessionCookie(MainApp
-                                        .getAccountType()).equals(mAuthTokenType) &&
-                        mHostUrlInput.hasFocus()) {
-                    checkOcServer();
+                @Override
+                public void onTextChanged(CharSequence s, int start, int before, int count) {
+                    if (mAuthStatusIcon != 0) {
+                        Log_OC.d(TAG, "onTextChanged: hiding authentication status");
+                        mAuthStatusIcon = 0;
+                        mAuthStatusText = 0;
+                        showAuthStatus();
+                    }
                 }
                 }
-                return false;
-            }
-        });
+            };
+
+
+            // TODO find out if this is really necessary, or if it can done in a different way
+            findViewById(R.id.scroll).setOnTouchListener(new OnTouchListener() {
+                @Override
+                public boolean onTouch(View view, MotionEvent event) {
+                    if (event.getAction() == MotionEvent.ACTION_DOWN &&
+                            AccountTypeUtils
+                                    .getAuthTokenTypeSamlSessionCookie(MainApp
+                                            .getAccountType()).equals(mAuthTokenType) &&
+                            mHostUrlInput.hasFocus()) {
+                        checkOcServer();
+                    }
+                    return false;
+                }
+            });
+        }
     }
     }
 
 
 
 
@@ -645,11 +714,20 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         outState.putString(KEY_AUTH_TOKEN_TYPE, mAuthTokenType);
         outState.putString(KEY_AUTH_TOKEN_TYPE, mAuthTokenType);
         outState.putLong(KEY_WAITING_FOR_OP_ID, mWaitingForOpId);
         outState.putLong(KEY_WAITING_FOR_OP_ID, mWaitingForOpId);
 
 
-        /// Server PRE-fragment state
-        outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);
-        outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);
-        outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);
-        outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);
+        if (!webViewLoginMethod) {
+            /// Server PRE-fragment state
+            outState.putInt(KEY_SERVER_STATUS_TEXT, mServerStatusText);
+            outState.putInt(KEY_SERVER_STATUS_ICON, mServerStatusIcon);
+            outState.putBoolean(KEY_SERVER_CHECKED, mServerIsChecked);
+            outState.putBoolean(KEY_SERVER_VALID, mServerIsValid);
+
+            /// Authentication PRE-fragment state
+            outState.putBoolean(KEY_PASSWORD_EXPOSED, isPasswordVisible());
+            outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);
+            outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);
+            outState.putString(KEY_AUTH_TOKEN, mAuthToken);
+        }
+
         outState.putBoolean(KEY_IS_SSL_CONN, mServerInfo.mIsSslConn);
         outState.putBoolean(KEY_IS_SSL_CONN, mServerInfo.mIsSslConn);
         outState.putString(KEY_HOST_URL_TEXT, mServerInfo.mBaseUrl);
         outState.putString(KEY_HOST_URL_TEXT, mServerInfo.mBaseUrl);
         if (mServerInfo.mVersion != null) {
         if (mServerInfo.mVersion != null) {
@@ -657,18 +735,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
         }
         outState.putString(KEY_SERVER_AUTH_METHOD, mServerInfo.mAuthMethod.name());
         outState.putString(KEY_SERVER_AUTH_METHOD, mServerInfo.mAuthMethod.name());
 
 
-        /// Authentication PRE-fragment state
-        outState.putBoolean(KEY_PASSWORD_EXPOSED, isPasswordVisible());
-        outState.putInt(KEY_AUTH_STATUS_ICON, mAuthStatusIcon);
-        outState.putInt(KEY_AUTH_STATUS_TEXT, mAuthStatusText);
-        outState.putString(KEY_AUTH_TOKEN, mAuthToken);
-
         /// authentication
         /// authentication
         outState.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG, mIsFirstAuthAttempt);
         outState.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG, mIsFirstAuthAttempt);
 
 
         /// AsyncTask (User and password)
         /// AsyncTask (User and password)
-        outState.putString(KEY_USERNAME, mUsernameInput.getText().toString().trim());
-        outState.putString(KEY_PASSWORD, mPasswordInput.getText().toString());
+        if (!webViewLoginMethod) {
+            outState.putString(KEY_USERNAME, mUsernameInput.getText().toString().trim());
+            outState.putString(KEY_PASSWORD, mPasswordInput.getText().toString());
+        }
 
 
         if (mAsyncTask != null) {
         if (mAsyncTask != null) {
             mAsyncTask.cancel(true);
             mAsyncTask.cancel(true);
@@ -730,21 +804,23 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     protected void onResume() {
     protected void onResume() {
         super.onResume();
         super.onResume();
 
 
-        // bound here to avoid spurious changes triggered by Android on device rotations
-        mHostUrlInput.setOnFocusChangeListener(this);
-        mHostUrlInput.addTextChangedListener(mHostUrlInputWatcher);
+        if (!webViewLoginMethod) {
+            // bound here to avoid spurious changes triggered by Android on device rotations
+            mHostUrlInput.setOnFocusChangeListener(this);
+            mHostUrlInput.addTextChangedListener(mHostUrlInputWatcher);
 
 
-        if (mNewCapturedUriFromOAuth2Redirection != null) {
-            getOAuth2AccessTokenFromCapturedRedirection();
-        }
+            if (mNewCapturedUriFromOAuth2Redirection != null) {
+                getOAuth2AccessTokenFromCapturedRedirection();
+            }
 
 
-        String dataString = getIntent().getDataString();
-        if (dataString != null) {
-            try {
-                populateLoginFields(dataString);
-            } catch (IllegalArgumentException e) {
-                Toast.makeText(this, "Illegal login data URL used", Toast.LENGTH_SHORT).show();
-                Log_OC.e(TAG, "Illegal login data URL used, no Login pre-fill!", e);
+            String dataString = getIntent().getDataString();
+            if (dataString != null) {
+                try {
+                    populateLoginFields(dataString);
+                } catch (IllegalArgumentException e) {
+                    Toast.makeText(this, "Illegal login data URL used", Toast.LENGTH_SHORT).show();
+                    Log_OC.e(TAG, "Illegal login data URL used, no Login pre-fill!", e);
+                }
             }
             }
         }
         }
 
 
@@ -772,8 +848,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mOperationsServiceBinder.removeOperationListener(this);
             mOperationsServiceBinder.removeOperationListener(this);
         }
         }
 
 
-        mHostUrlInput.removeTextChangedListener(mHostUrlInputWatcher);
-        mHostUrlInput.setOnFocusChangeListener(null);
+        if (!webViewLoginMethod) {
+            mHostUrlInput.removeTextChangedListener(mHostUrlInputWatcher);
+            mHostUrlInput.setOnFocusChangeListener(null);
+        }
 
 
         super.onPause();
         super.onPause();
     }
     }
@@ -787,10 +865,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             unbindService(mOperationsServiceConnection);
             unbindService(mOperationsServiceConnection);
             mOperationsServiceBinder = null;
             mOperationsServiceBinder = null;
         }
         }
+
+        if (webViewLoginMethod) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
+        }
+
         super.onDestroy();
         super.onDestroy();
     }
     }
 
 
-
     /**
     /**
      * Parses the redirection with the response to the GET AUTHORIZATION request to the
      * Parses the redirection with the response to the GET AUTHORIZATION request to the
      * oAuth server and requests for the access token (GET ACCESS TOKEN)
      * oAuth server and requests for the access token (GET ACCESS TOKEN)
@@ -863,15 +945,23 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 
 
 
     private void checkOcServer() {
     private void checkOcServer() {
-        String uri = mHostUrlInput.getFullServerUrl().trim();
+        String uri;
+        if (mHostUrlInput != null) {
+            uri = mHostUrlInput.getText().toString().trim();
+            mOkButton.setEnabled(false);
+            showRefreshButton(false);
+        } else {
+            uri = mServerInfo.mBaseUrl;
+        }
+
         mServerIsValid = false;
         mServerIsValid = false;
         mServerIsChecked = false;
         mServerIsChecked = false;
-        mOkButton.setEnabled(false);
         mServerInfo = new GetServerInfoOperation.ServerInfo();
         mServerInfo = new GetServerInfoOperation.ServerInfo();
-        showRefreshButton(false);
 
 
         if (uri.length() != 0) {
         if (uri.length() != 0) {
-            uri = stripIndexPhpOrAppsFiles(uri, mHostUrlInput);
+            if (mHostUrlInput != null) {
+                uri = stripIndexPhpOrAppsFiles(uri, mHostUrlInput);
+            }
 
 
             // Handle internationalized domain names
             // Handle internationalized domain names
             try {
             try {
@@ -880,9 +970,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 // Let Owncloud library check the error of the malformed URI
                 // Let Owncloud library check the error of the malformed URI
             }
             }
 
 
-            mServerStatusText = R.string.auth_testing_connection;
-            mServerStatusIcon = R.drawable.progress_small;
-            showServerStatus();
+            if (mHostUrlInput != null) {
+                mServerStatusText = R.string.auth_testing_connection;
+                mServerStatusIcon = R.drawable.progress_small;
+                showServerStatus();
+            }
 
 
             Intent getServerInfoIntent = new Intent();
             Intent getServerInfoIntent = new Intent();
             getServerInfoIntent.setAction(OperationsService.ACTION_GET_SERVER_INFO);
             getServerInfoIntent.setAction(OperationsService.ACTION_GET_SERVER_INFO);
@@ -890,6 +982,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                     OperationsService.EXTRA_SERVER_URL,
                     OperationsService.EXTRA_SERVER_URL,
                     normalizeUrlSuffix(uri)
                     normalizeUrlSuffix(uri)
             );
             );
+
             if (mOperationsServiceBinder != null) {
             if (mOperationsServiceBinder != null) {
                 mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(getServerInfoIntent);
                 mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(getServerInfoIntent);
             } else {
             } else {
@@ -899,7 +992,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         } else {
         } else {
             mServerStatusText = 0;
             mServerStatusText = 0;
             mServerStatusIcon = 0;
             mServerStatusIcon = 0;
-            showServerStatus();
+            if (!webViewLoginMethod) {
+                showServerStatus();
+            }
         }
         }
     }
     }
 
 
@@ -913,6 +1008,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      *
      *
      * @param hasFocus 'True' if focus is received, 'false' if is lost
      * @param hasFocus 'True' if focus is received, 'false' if is lost
      */
      */
+
     private void onPasswordFocusChanged(boolean hasFocus) {
     private void onPasswordFocusChanged(boolean hasFocus) {
         if (hasFocus) {
         if (hasFocus) {
             showViewPasswordButton();
             showViewPasswordButton();
@@ -992,7 +1088,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 
             startSamlBasedFederatedSingleSignOnAuthorization();
             startSamlBasedFederatedSingleSignOnAuthorization();
         } else {
         } else {
-            checkBasicAuthorization();
+            checkBasicAuthorization(null, null);
         }
         }
 
 
     }
     }
@@ -1002,10 +1098,17 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * Tests the credentials entered by the user performing a check of existence on
      * Tests the credentials entered by the user performing a check of existence on
      * the root folder of the ownCloud server.
      * the root folder of the ownCloud server.
      */
      */
-    private void checkBasicAuthorization() {
+    private void checkBasicAuthorization(@Nullable String webViewUsername, @Nullable String webViewPassword) {
         /// get basic credentials entered by user
         /// get basic credentials entered by user
-        String username = mUsernameInput.getText().toString().trim();
-        String password = mPasswordInput.getText().toString();
+        String username;
+        String password;
+        if (!webViewLoginMethod) {
+            username = mUsernameInput.getText().toString().trim();
+            password = mPasswordInput.getText().toString();
+        } else {
+            username = webViewUsername;
+            password = webViewPassword;
+        }
 
 
         /// be gentle with the user
         /// be gentle with the user
         IndeterminateProgressDialog dialog =
         IndeterminateProgressDialog dialog =
@@ -1109,11 +1212,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             }
             }
 
 
             if (mAction == ACTION_CREATE) {
             if (mAction == ACTION_CREATE) {
-                mUsernameInput.setText(username);
+                if (!webViewLoginMethod) {
+                    mUsernameInput.setText(username);
+                }
                 success = createAccount(result);
                 success = createAccount(result);
             } else {
             } else {
 
 
-                if (!mUsernameInput.getText().toString().trim().equals(username)) {
+                if (!webViewLoginMethod && !mUsernameInput.getText().toString().trim().equals(username)) {
                     // fail - not a new account, but an existing one; disallow
                     // fail - not a new account, but an existing one; disallow
                     result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME);
                     result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME);
                     mAuthToken = "";
                     mAuthToken = "";
@@ -1138,8 +1243,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 finish();
                 finish();
             }
             }
         } else {
         } else {
-            updateStatusIconFailUserName();
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateStatusIconFailUserName();
+                showAuthStatus();
+            }
             Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage());
             Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage());
         }
         }
 
 
@@ -1157,7 +1264,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         mWaitingForOpId = Long.MAX_VALUE;
         mWaitingForOpId = Long.MAX_VALUE;
 
 
         // update server status, but don't show it yet
         // update server status, but don't show it yet
-        updateServerStatusIconAndText(result);
+        if (!webViewLoginMethod) {
+            updateServerStatusIconAndText(result);
+        }
 
 
         if (result.isSuccess()) {
         if (result.isSuccess()) {
             /// SUCCESS means:
             /// SUCCESS means:
@@ -1167,9 +1276,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             //      4. we got the authentication method required by the server 
             //      4. we got the authentication method required by the server 
             mServerInfo = (GetServerInfoOperation.ServerInfo) (result.getData().get(0));
             mServerInfo = (GetServerInfoOperation.ServerInfo) (result.getData().get(0));
 
 
+            if (webViewLoginMethod) {
+                checkBasicAuthorization(webViewUser, webViewPassword);
+            }
+
             if (!authSupported(mServerInfo.mAuthMethod)) {
             if (!authSupported(mServerInfo.mAuthMethod)) {
 
 
-                updateServerStatusIconNoRegularAuth();  // overrides updateServerStatusIconAndText()  
+                if (!webViewLoginMethod) {
+                    // overrides updateServerStatusIconAndText()
+                    updateServerStatusIconNoRegularAuth();
+                }
                 mServerIsValid = false;
                 mServerIsValid = false;
 
 
             } else {
             } else {
@@ -1181,9 +1297,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
         }
 
 
         // refresh UI
         // refresh UI
-        showRefreshButton(!mServerIsValid);
-        showServerStatus();
-        mOkButton.setEnabled(mServerIsValid);
+        if (!webViewLoginMethod) {
+            showRefreshButton(!mServerIsValid);
+            showServerStatus();
+            mOkButton.setEnabled(mServerIsValid);
+        }
 
 
         /// very special case (TODO: move to a common place for all the remote operations)
         /// very special case (TODO: move to a common place for all the remote operations)
         if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
         if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
@@ -1511,11 +1629,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             showServerStatus();
             showServerStatus();
             mAuthStatusIcon = 0;
             mAuthStatusIcon = 0;
             mAuthStatusText = 0;
             mAuthStatusText = 0;
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                showAuthStatus();
 
 
-            // update input controls state
-            showRefreshButton(true);
-            mOkButton.setEnabled(false);
+                // update input controls state
+                showRefreshButton(true);
+                mOkButton.setEnabled(false);
+            }
 
 
             // very special case (TODO: move to a common place for all the remote operations)
             // very special case (TODO: move to a common place for all the remote operations)
             if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
             if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
@@ -1523,8 +1643,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             }
             }
 
 
         } else {    // authorization fail due to client side - probably wrong credentials
         } else {    // authorization fail due to client side - probably wrong credentials
-            updateAuthStatusIconAndText(result);
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateAuthStatusIconAndText(result);
+                showAuthStatus();
+            }
             Log_OC.d(TAG, "Access failed: " + result.getLogMessage());
             Log_OC.d(TAG, "Access failed: " + result.getLogMessage());
         }
         }
     }
     }
@@ -1562,8 +1684,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
             mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
 
 
         } else {
         } else {
-            response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
-            mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
+            if (!webViewLoginMethod) {
+                response.putString(AccountManager.KEY_AUTHTOKEN, mPasswordInput.getText().toString());
+                mAccountMgr.setPassword(mAccount, mPasswordInput.getText().toString());
+            } else {
+                response.putString(AccountManager.KEY_AUTHTOKEN, webViewPassword);
+                mAccountMgr.setPassword(mAccount, webViewPassword);
+            }
         }
         }
 
 
         // remove managed clients for this account to enforce creation with fresh credentials
         // remove managed clients for this account to enforce creation with fresh credentials
@@ -1599,7 +1726,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
         }
 
 
         Uri uri = Uri.parse(mServerInfo.mBaseUrl);
         Uri uri = Uri.parse(mServerInfo.mBaseUrl);
-        String username = mUsernameInput.getText().toString().trim();
+        String username;
+        if (!webViewLoginMethod) {
+            username = mUsernameInput.getText().toString().trim();
+        } else {
+            username = webViewUser;
+        }
         if (isOAuth) {
         if (isOAuth) {
             username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();
             username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();
         }
         }
@@ -1609,8 +1741,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         if (AccountUtils.exists(newAccount, getApplicationContext())) {
         if (AccountUtils.exists(newAccount, getApplicationContext())) {
             // fail - not a new account, but an existing one; disallow
             // fail - not a new account, but an existing one; disallow
             RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW);
             RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW);
-            updateAuthStatusIconAndText(result);
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateAuthStatusIconAndText(result);
+                showAuthStatus();
+            }
             Log_OC.d(TAG, result.getLogMessage());
             Log_OC.d(TAG, result.getLogMessage());
             return false;
             return false;
 
 
@@ -1621,9 +1755,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 // with external authorizations, the password is never input in the app
                 // with external authorizations, the password is never input in the app
                 mAccountMgr.addAccountExplicitly(mAccount, "", null);
                 mAccountMgr.addAccountExplicitly(mAccount, "", null);
             } else {
             } else {
-                mAccountMgr.addAccountExplicitly(
-                        mAccount, mPasswordInput.getText().toString(), null
-                );
+                if (!webViewLoginMethod) {
+                    mAccountMgr.addAccountExplicitly(
+                            mAccount, mPasswordInput.getText().toString(), null
+                    );
+                } else {
+                    mAccountMgr.addAccountExplicitly(
+                            mAccount, webViewPassword, null
+                    );
+                }
             }
             }
 
 
             // include account version with the new account
             // include account version with the new account
@@ -1706,15 +1846,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * to the last check on the ownCloud server.
      * to the last check on the ownCloud server.
      */
      */
     private void showServerStatus() {
     private void showServerStatus() {
-        if (mServerStatusIcon == 0 && mServerStatusText == 0) {
-            mServerStatusView.setVisibility(View.INVISIBLE);
+        if (!webViewLoginMethod) {
+            if (mServerStatusIcon == 0 && mServerStatusText == 0) {
+                mServerStatusView.setVisibility(View.INVISIBLE);
 
 
-        } else {
-            mServerStatusView.setText(mServerStatusText);
-            mServerStatusView.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);
-            mServerStatusView.setVisibility(View.VISIBLE);
+            } else {
+                mServerStatusView.setText(mServerStatusText);
+                mServerStatusView.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);
+                mServerStatusView.setVisibility(View.VISIBLE);
+            }
         }
         }
-
     }
     }
 
 
 
 
@@ -1723,22 +1864,26 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * to the interactions with the OAuth authorization server.
      * to the interactions with the OAuth authorization server.
      */
      */
     private void showAuthStatus() {
     private void showAuthStatus() {
-        if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {
-            mAuthStatusView.setVisibility(View.INVISIBLE);
+        if (!webViewLoginMethod) {
+            if (mAuthStatusIcon == 0 && mAuthStatusText == 0) {
+                mAuthStatusView.setVisibility(View.INVISIBLE);
 
 
-        } else {
-            mAuthStatusView.setText(mAuthStatusText);
-            mAuthStatusView.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);
-            mAuthStatusView.setVisibility(View.VISIBLE);
+            } else {
+                mAuthStatusView.setText(mAuthStatusText);
+                mAuthStatusView.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);
+                mAuthStatusView.setVisibility(View.VISIBLE);
+            }
         }
         }
     }
     }
 
 
 
 
     private void showRefreshButton(boolean show) {
     private void showRefreshButton(boolean show) {
-        if (show) {
-            mRefreshButton.setVisibility(View.VISIBLE);
-        } else {
-            mRefreshButton.setVisibility(View.GONE);
+        if (webViewLoginMethod) {
+            if (show) {
+                mRefreshButton.setVisibility(View.VISIBLE);
+            } else {
+                mRefreshButton.setVisibility(View.GONE);
+            }
         }
         }
     }
     }
 
 
@@ -1951,7 +2096,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mOperationsServiceBinder.dispatchResultIfFinished((int) mWaitingForOpId, this);
             mOperationsServiceBinder.dispatchResultIfFinished((int) mWaitingForOpId, this);
         }
         }
 
 
-        if (mHostUrlInput.getText() != null && mHostUrlInput.getText().length() > 0 && !mServerIsChecked) {
+        if (!webViewLoginMethod && mHostUrlInput.getText() != null && mHostUrlInput.getText().length() > 0
+                && !mServerIsChecked) {
             checkOcServer();
             checkOcServer();
         }
         }
     }
     }

+ 12 - 0
src/main/res/layout/account_setup_webview.xml

@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+              android:orientation="vertical"
+              android:layout_width="match_parent"
+              android:layout_height="match_parent">
+
+    <WebView
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:id="@+id/login_webview">
+    </WebView>
+</LinearLayout>

+ 3 - 0
src/main/res/values/setup.xml

@@ -102,6 +102,9 @@
 
 
     <!-- login data links -->
     <!-- login data links -->
     <string name="login_data_own_scheme" translatable="false">cloud</string>
     <string name="login_data_own_scheme" translatable="false">cloud</string>
+    <!-- url for webview login, with the protocol prefix
+    If set, will replace all other login methods available -->
+    <string name="webview_login_url" translatable="false"></string>
 
 
     <!-- analytics enabled -->
     <!-- analytics enabled -->
     <bool name="analytics_enabled">false</bool>
     <bool name="analytics_enabled">false</bool>