浏览代码

Merge branch 'master' of https://github.com/nextcloud/android into favourites

AndyScherzinger 8 年之前
父节点
当前提交
e828cc701b

+ 99 - 0
ICONS.txt

@@ -0,0 +1,99 @@
+Standard Google Material Design icons
+Copyright (c) 2014, Google (http://www.google.com/design/)
+uses the license at https://github.com/google/material-design-icons/blob/master/LICENSE
+
+Twitter icon graphic
+Copyright (c) 2014, Austin Andrews (http://materialdesignicons.com/),
+with Reserved Font Name Material Design Icons.
+
+This Font Software is licensed under the SIL Open Font License, Version 1.1.
+This license is copied below, and is also available with a FAQ at:
+http://scripts.sil.org/OFL
+
+
+-----------------------------------------------------------
+SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
+-----------------------------------------------------------
+
+PREAMBLE
+The goals of the Open Font License (OFL) are to stimulate worldwide
+development of collaborative font projects, to support the font creation
+efforts of academic and linguistic communities, and to provide a free and
+open framework in which fonts may be shared and improved in partnership
+with others.
+
+The OFL allows the licensed fonts to be used, studied, modified and
+redistributed freely as long as they are not sold by themselves. The
+fonts, including any derivative works, can be bundled, embedded, 
+redistributed and/or sold with any software provided that any reserved
+names are not used by derivative works. The fonts and derivatives,
+however, cannot be released under any other type of license. The
+requirement for fonts to remain under this license does not apply
+to any document created using the fonts or their derivatives.
+
+DEFINITIONS
+"Font Software" refers to the set of files released by the Copyright
+Holder(s) under this license and clearly marked as such. This may
+include source files, build scripts and documentation.
+
+"Reserved Font Name" refers to any names specified as such after the
+copyright statement(s).
+
+"Original Version" refers to the collection of Font Software components as
+distributed by the Copyright Holder(s).
+
+"Modified Version" refers to any derivative made by adding to, deleting,
+or substituting -- in part or in whole -- any of the components of the
+Original Version, by changing formats or by porting the Font Software to a
+new environment.
+
+"Author" refers to any designer, engineer, programmer, technical
+writer or other person who contributed to the Font Software.
+
+PERMISSION & CONDITIONS
+Permission is hereby granted, free of charge, to any person obtaining
+a copy of the Font Software, to use, study, copy, merge, embed, modify,
+redistribute, and sell modified and unmodified copies of the Font
+Software, subject to the following conditions:
+
+1) Neither the Font Software nor any of its individual components,
+in Original or Modified Versions, may be sold by itself.
+
+2) Original or Modified Versions of the Font Software may be bundled,
+redistributed and/or sold with any software, provided that each copy
+contains the above copyright notice and this license. These can be
+included either as stand-alone text files, human-readable headers or
+in the appropriate machine-readable metadata fields within text or
+binary files as long as those fields can be easily viewed by the user.
+
+3) No Modified Version of the Font Software may use the Reserved Font
+Name(s) unless explicit written permission is granted by the corresponding
+Copyright Holder. This restriction only applies to the primary font name as
+presented to the users.
+
+4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
+Software shall not be used to promote, endorse or advertise any
+Modified Version, except to acknowledge the contribution(s) of the
+Copyright Holder(s) and the Author(s) or with their explicit written
+permission.
+
+5) The Font Software, modified or unmodified, in part or in whole,
+must be distributed entirely under this license, and must not be
+distributed under any other license. The requirement for fonts to
+remain under this license does not apply to any document created
+using the Font Software.
+
+TERMINATION
+This license becomes null and void if any of the above conditions are
+not met.
+
+DISCLAIMER
+THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
+OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
+COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
+INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
+DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
+FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
+OTHER DEALINGS IN THE FONT SOFTWARE.

+ 1 - 0
drawable_resources/ic_twitter.svg

@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" /></svg>

+ 1 - 0
drawable_resources/ic_web.svg

@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="24" height="24" viewBox="0 0 24 24"><path d="M16.36,14C16.44,13.34 16.5,12.68 16.5,12C16.5,11.32 16.44,10.66 16.36,10H19.74C19.9,10.64 20,11.31 20,12C20,12.69 19.9,13.36 19.74,14M14.59,19.56C15.19,18.45 15.65,17.25 15.97,16H18.92C17.96,17.65 16.43,18.93 14.59,19.56M14.34,14H9.66C9.56,13.34 9.5,12.68 9.5,12C9.5,11.32 9.56,10.65 9.66,10H14.34C14.43,10.65 14.5,11.32 14.5,12C14.5,12.68 14.43,13.34 14.34,14M12,19.96C11.17,18.76 10.5,17.43 10.09,16H13.91C13.5,17.43 12.83,18.76 12,19.96M8,8H5.08C6.03,6.34 7.57,5.06 9.4,4.44C8.8,5.55 8.35,6.75 8,8M5.08,16H8C8.35,17.25 8.8,18.45 9.4,19.56C7.57,18.93 6.03,17.65 5.08,16M4.26,14C4.1,13.36 4,12.69 4,12C4,11.31 4.1,10.64 4.26,10H7.64C7.56,10.66 7.5,11.32 7.5,12C7.5,12.68 7.56,13.34 7.64,14M12,4.03C12.83,5.23 13.5,6.57 13.91,8H10.09C10.5,6.57 11.17,5.23 12,4.03M18.92,8H15.97C15.65,6.75 15.19,5.55 14.59,4.44C16.43,5.07 17.96,6.34 18.92,8M12,2C6.47,2 2,6.5 2,12A10,10 0 0,0 12,22A10,10 0 0,0 22,12A10,10 0 0,0 12,2Z" /></svg>

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

@@ -4,8 +4,10 @@
  * @author Bartek Przybylski
  * @author David A. Velasco
  * @author masensio
+ * @author Mario Danic
  * Copyright (C) 2012  Bartek Przybylski
  * Copyright (C) 2015 ownCloud Inc.
+ * Copyright (C) 2017 Mario Danic
  *
  * This program is free software: you can redistribute it and/or modify
  * 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
  * 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;
@@ -30,6 +48,7 @@ import android.content.Context;
 import android.content.Intent;
 import android.content.ServiceConnection;
 import android.content.SharedPreferences;
+import android.content.pm.ActivityInfo;
 import android.graphics.Rect;
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
@@ -38,12 +57,14 @@ import android.os.Bundle;
 import android.os.Handler;
 import android.os.IBinder;
 import android.preference.PreferenceManager;
+import android.support.annotation.Nullable;
 import android.support.v4.app.DialogFragment;
 import android.support.v4.app.Fragment;
 import android.support.v4.app.FragmentManager;
 import android.support.v4.app.FragmentTransaction;
 import android.text.Editable;
 import android.text.InputType;
+import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.MotionEvent;
@@ -54,6 +75,7 @@ import android.view.inputmethod.EditorInfo;
 import android.webkit.HttpAuthHandler;
 import android.webkit.SslErrorHandler;
 import android.webkit.WebView;
+import android.webkit.WebViewClient;
 import android.widget.Button;
 import android.widget.CheckBox;
 import android.widget.EditText;
@@ -191,6 +213,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private View mOkButton;
     private TextView mAuthStatusView;
 
+    private WebView mLoginWebView;
+
     private int mAuthStatusText = 0, mAuthStatusIcon = 0;
 
     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 SAML_TOKEN_TYPE = AccountTypeUtils.getAuthTokenTypeSamlSessionCookie(MainApp.getAccountType());
 
+    private boolean webViewLoginMethod;
+    private String webViewUser;
+    private String webViewPassword;
+
     /**
      * {@inheritDoc}
      *
@@ -242,42 +270,86 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             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
-        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
-            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 {
@@ -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.
      *
-     * @param prefix URI beginning, e.g. cloud://login/
+     * @param prefix     URI beginning, e.g. cloud://login/
      * @param dataString the complete URI
      * @return login data
      * @throws IllegalArgumentException when
@@ -420,7 +492,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 mServerInfo.mIsSslConn = mServerInfo.mBaseUrl.startsWith(HTTPS_PROTOCOL);
                 mServerInfo.mVersion = AccountUtils.getServerVersion(mAccount);
             } 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);
             }
         } 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.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.putString(KEY_HOST_URL_TEXT, mServerInfo.mBaseUrl);
         if (mServerInfo.mVersion != null) {
@@ -657,18 +735,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
         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
         outState.putBoolean(KEY_AUTH_IS_FIRST_ATTEMPT_TAG, mIsFirstAuthAttempt);
 
         /// 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) {
             mAsyncTask.cancel(true);
@@ -730,21 +804,23 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     protected void 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);
         }
 
-        mHostUrlInput.removeTextChangedListener(mHostUrlInputWatcher);
-        mHostUrlInput.setOnFocusChangeListener(null);
+        if (!webViewLoginMethod) {
+            mHostUrlInput.removeTextChangedListener(mHostUrlInputWatcher);
+            mHostUrlInput.setOnFocusChangeListener(null);
+        }
 
         super.onPause();
     }
@@ -787,10 +865,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             unbindService(mOperationsServiceConnection);
             mOperationsServiceBinder = null;
         }
+
+        if (webViewLoginMethod) {
+            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
+        }
+
         super.onDestroy();
     }
 
-
     /**
      * Parses the redirection with the response to the GET AUTHORIZATION request to the
      * oAuth server and requests for the access token (GET ACCESS TOKEN)
@@ -863,15 +945,23 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
 
     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;
         mServerIsChecked = false;
-        mOkButton.setEnabled(false);
         mServerInfo = new GetServerInfoOperation.ServerInfo();
-        showRefreshButton(false);
 
         if (uri.length() != 0) {
-            uri = stripIndexPhpOrAppsFiles(uri, mHostUrlInput);
+            if (mHostUrlInput != null) {
+                uri = stripIndexPhpOrAppsFiles(uri, mHostUrlInput);
+            }
 
             // Handle internationalized domain names
             try {
@@ -880,9 +970,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 // 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();
             getServerInfoIntent.setAction(OperationsService.ACTION_GET_SERVER_INFO);
@@ -890,6 +982,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                     OperationsService.EXTRA_SERVER_URL,
                     normalizeUrlSuffix(uri)
             );
+
             if (mOperationsServiceBinder != null) {
                 mWaitingForOpId = mOperationsServiceBinder.queueNewOperation(getServerInfoIntent);
             } else {
@@ -899,7 +992,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         } else {
             mServerStatusText = 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
      */
+
     private void onPasswordFocusChanged(boolean hasFocus) {
         if (hasFocus) {
             showViewPasswordButton();
@@ -992,7 +1088,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
             startSamlBasedFederatedSingleSignOnAuthorization();
         } 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
      * 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
-        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
         IndeterminateProgressDialog dialog =
@@ -1109,11 +1212,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             }
 
             if (mAction == ACTION_CREATE) {
-                mUsernameInput.setText(username);
+                if (!webViewLoginMethod) {
+                    mUsernameInput.setText(username);
+                }
                 success = createAccount(result);
             } 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
                     result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_THE_SAME);
                     mAuthToken = "";
@@ -1138,8 +1243,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 finish();
             }
         } else {
-            updateStatusIconFailUserName();
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateStatusIconFailUserName();
+                showAuthStatus();
+            }
             Log_OC.e(TAG, "Access to user name failed: " + result.getLogMessage());
         }
 
@@ -1157,7 +1264,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         mWaitingForOpId = Long.MAX_VALUE;
 
         // update server status, but don't show it yet
-        updateServerStatusIconAndText(result);
+        if (!webViewLoginMethod) {
+            updateServerStatusIconAndText(result);
+        }
 
         if (result.isSuccess()) {
             /// SUCCESS means:
@@ -1167,9 +1276,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             //      4. we got the authentication method required by the server 
             mServerInfo = (GetServerInfoOperation.ServerInfo) (result.getData().get(0));
 
+            if (webViewLoginMethod) {
+                checkBasicAuthorization(webViewUser, webViewPassword);
+            }
+
             if (!authSupported(mServerInfo.mAuthMethod)) {
 
-                updateServerStatusIconNoRegularAuth();  // overrides updateServerStatusIconAndText()  
+                if (!webViewLoginMethod) {
+                    // overrides updateServerStatusIconAndText()
+                    updateServerStatusIconNoRegularAuth();
+                }
                 mServerIsValid = false;
 
             } else {
@@ -1181,9 +1297,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
 
         // 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)
         if (result.getCode() == ResultCode.SSL_RECOVERABLE_PEER_UNVERIFIED) {
@@ -1511,11 +1629,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             showServerStatus();
             mAuthStatusIcon = 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)
             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
-            updateAuthStatusIconAndText(result);
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateAuthStatusIconAndText(result);
+                showAuthStatus();
+            }
             Log_OC.d(TAG, "Access failed: " + result.getLogMessage());
         }
     }
@@ -1562,8 +1684,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             mAccountMgr.setAuthToken(mAccount, mAuthTokenType, mAuthToken);
 
         } 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
@@ -1599,7 +1726,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
 
         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) {
             username = "OAuth_user" + (new java.util.Random(System.currentTimeMillis())).nextLong();
         }
@@ -1609,8 +1741,10 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         if (AccountUtils.exists(newAccount, getApplicationContext())) {
             // fail - not a new account, but an existing one; disallow
             RemoteOperationResult result = new RemoteOperationResult(ResultCode.ACCOUNT_NOT_NEW);
-            updateAuthStatusIconAndText(result);
-            showAuthStatus();
+            if (!webViewLoginMethod) {
+                updateAuthStatusIconAndText(result);
+                showAuthStatus();
+            }
             Log_OC.d(TAG, result.getLogMessage());
             return false;
 
@@ -1621,9 +1755,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 // with external authorizations, the password is never input in the app
                 mAccountMgr.addAccountExplicitly(mAccount, "", null);
             } 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
@@ -1706,15 +1846,16 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * to the last check on the ownCloud server.
      */
     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.
      */
     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) {
-        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);
         }
 
-        if (mHostUrlInput.getText() != null && mHostUrlInput.getText().length() > 0 && !mServerIsChecked) {
+        if (!webViewLoginMethod && mHostUrlInput.getText() != null && mHostUrlInput.getText().length() > 0
+                && !mServerIsChecked) {
             checkOcServer();
         }
     }

+ 18 - 11
src/main/java/com/owncloud/android/ui/activity/UserInfoActivity.java

@@ -130,12 +130,18 @@ public class UserInfoActivity extends FileActivity {
     @BindView(R.id.website_address)
     public TextView mWebsiteTextView;
 
+    @BindView(R.id.website_icon)
+    public ImageView mWebsiteIcon;
+
     @BindView(R.id.twitter_container)
     public View mTwitterContainer;
 
     @BindView(R.id.twitter_handle)
     public TextView mTwitterHandleTextView;
 
+    @BindView(R.id.twitter_icon)
+    public ImageView mTwitterIcon;
+
     @BindView(R.id.empty_list_progress)
     public ProgressBar multiListProgressBar;
 
@@ -252,16 +258,18 @@ public class UserInfoActivity extends FileActivity {
             populateUserInfoElement(mEmailContainer, mEmailAddressTextView, userInfo.getEmail(), mEmailIcon, tint);
             populateUserInfoElement(mAddressContainer, mAddressTextView, userInfo.getAddress(), mAddressIcon, tint);
 
-            populateUserInfoElement(mWebsiteContainer, mWebsiteTextView, userInfo.getWebpage());
-            populateUserInfoElement(mTwitterContainer, mTwitterHandleTextView, userInfo.getTwitter());
-        }
-    }
-
-    private void populateUserInfoElement(View container, TextView textView, String text) {
-        if (!TextUtils.isEmpty(text)) {
-            textView.setText(text);
-        } else {
-            container.setVisibility(View.GONE);
+            populateUserInfoElement(
+                    mWebsiteContainer,
+                    mWebsiteTextView,
+                    DisplayUtils.beautifyURL(userInfo.getWebpage()),
+                    mWebsiteIcon,
+                    tint);
+            populateUserInfoElement(
+                    mTwitterContainer,
+                    mTwitterHandleTextView,
+                    DisplayUtils.beautifyTwitterHandle(userInfo.getTwitter()),
+                    mTwitterIcon,
+                    tint);
         }
     }
 
@@ -275,7 +283,6 @@ public class UserInfoActivity extends FileActivity {
         }
     }
 
-
     private void changeAccountPassword(Account account) {
         Intent updateAccountCredentials = new Intent(UserInfoActivity.this, AuthenticatorActivity.class);
         updateAccountCredentials.putExtra(AuthenticatorActivity.EXTRA_ACCOUNT, account);

+ 44 - 0
src/main/java/com/owncloud/android/utils/DisplayUtils.java

@@ -78,6 +78,10 @@ public class DisplayUtils {
     private static final int RELATIVE_THRESHOLD_CRITICAL = 95;
     private static final String MIME_TYPE_UNKNOWN = "Unknown type";
 
+    private static final String HTTP_PROTOCOLL = "http://";
+    private static final String HTTPS_PROTOCOLL = "https://";
+    private static final String TWITTER_HANDLE_PREFIX = "@";
+
     private static Map<String, String> mimeType2HumanReadable;
 
     static {
@@ -150,6 +154,46 @@ public class DisplayUtils {
         DateFormat df = DateFormat.getDateTimeInstance();
         return df.format(date);
     }
+
+    /**
+     * beautifies a given URL by removing any http/https protocol prefix.
+     *
+     * @param url to be beautified url
+     * @return beautified url
+     */
+    public static String beautifyURL(String url) {
+        if (url == null) {
+            return "";
+        }
+
+        if (url.length() >= 7 && url.substring(0, 7).equalsIgnoreCase(HTTP_PROTOCOLL)) {
+            return url.substring(HTTP_PROTOCOLL.length());
+        }
+
+        if (url.length() >= 8 && url.substring(0, 8).equalsIgnoreCase(HTTPS_PROTOCOLL)) {
+            return url.substring(HTTPS_PROTOCOLL.length());
+        }
+
+        return url;
+    }
+
+    /**
+     * beautifies a given twitter handle by prefixing it with an @ in case it is missing.
+     *
+     * @param handle to be beautified twitter handle
+     * @return beautified twitter handle
+     */
+    public static String beautifyTwitterHandle(String handle) {
+        if (handle == null) {
+            return "";
+        }
+
+        if (handle.startsWith(TWITTER_HANDLE_PREFIX)) {
+            return handle;
+        } else {
+            return TWITTER_HANDLE_PREFIX + handle;
+        }
+    }
     
     /**
      * Converts an internationalized domain name (IDN) in an URL to and from ASCII/Unicode.

二进制
src/main/res/drawable-hdpi/ic_twitter.png


二进制
src/main/res/drawable-hdpi/ic_web.png


二进制
src/main/res/drawable-mdpi/ic_twitter.png


二进制
src/main/res/drawable-mdpi/ic_web.png


二进制
src/main/res/drawable-xhdpi/ic_twitter.png


二进制
src/main/res/drawable-xhdpi/ic_web.png


二进制
src/main/res/drawable-xxhdpi/ic_twitter.png


二进制
src/main/res/drawable-xxhdpi/ic_web.png


二进制
src/main/res/drawable-xxxhdpi/ic_twitter.png


二进制
src/main/res/drawable-xxxhdpi/ic_web.png


+ 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>

+ 40 - 15
src/main/res/layout/user_info_layout.xml

@@ -174,17 +174,27 @@
                         android:layout_height="wrap_content"
                         android:orientation="vertical">
 
-                        <LinearLayout
+                        <RelativeLayout
                             android:id="@+id/website_container"
                             android:layout_width="match_parent"
-                            android:layout_height="wrap_content"
-                            android:layout_margin="@dimen/standard_margin"
-                            android:orientation="vertical">
+                            android:layout_height="wrap_content">
+
+                            <ImageView
+                                android:id="@+id/website_icon"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:src="@drawable/ic_web"
+                                android:layout_margin="@dimen/standard_margin"/>
 
                             <TextView
                                 android:id="@+id/website_headline"
                                 android:layout_width="match_parent"
                                 android:layout_height="wrap_content"
+                                android:layout_marginRight="@dimen/standard_margin"
+                                android:layout_marginTop="@dimen/standard_margin"
+                                android:layout_toEndOf="@id/website_icon"
+                                android:layout_toRightOf="@id/website_icon"
+                                android:maxLines="1"
                                 android:text="@string/user_info_website"
                                 android:textAppearance="?android:attr/textAppearanceListItem"/>
 
@@ -192,31 +202,43 @@
                                 android:id="@+id/website_address"
                                 android:layout_width="match_parent"
                                 android:layout_height="wrap_content"
+                                android:layout_marginRight="@dimen/standard_margin"
+                                android:layout_marginBottom="@dimen/standard_margin"
+                                android:layout_below="@+id/website_headline"
+                                android:layout_toEndOf="@id/website_icon"
+                                android:layout_toRightOf="@id/website_icon"
                                 android:maxLines="3"
-                                android:text="@string/user_info_twitter"
+                                android:text="@string/user_info_website"
                                 android:textColor="?android:attr/textColorSecondary"/>
 
-                        </LinearLayout>
+                        </RelativeLayout>
 
-                        <LinearLayout
+                        <RelativeLayout
                             android:id="@+id/twitter_container"
                             android:layout_width="match_parent"
-                            android:layout_height="wrap_content"
-                            android:layout_marginBottom="@dimen/standard_margin"
-                            android:orientation="vertical">
+                            android:layout_height="wrap_content">
 
                             <View
                                 android:layout_width="match_parent"
                                 android:layout_height="1dp"
-                                android:layout_marginBottom="@dimen/standard_margin"
                                 android:background="@color/list_divider_background"/>
 
+                            <ImageView
+                                android:id="@+id/twitter_icon"
+                                android:layout_width="wrap_content"
+                                android:layout_height="wrap_content"
+                                android:layout_margin="@dimen/standard_margin"
+                                android:src="@drawable/ic_twitter"/>
+
                             <TextView
                                 android:id="@+id/twitter_headline"
                                 android:layout_width="match_parent"
                                 android:layout_height="wrap_content"
                                 android:layout_marginRight="@dimen/standard_margin"
-                                android:layout_marginLeft="@dimen/standard_margin"
+                                android:layout_marginTop="@dimen/standard_margin"
+                                android:layout_toEndOf="@id/twitter_icon"
+                                android:layout_toRightOf="@id/twitter_icon"
+                                android:maxLines="1"
                                 android:text="Twitter"
                                 android:textAppearance="?android:attr/textAppearanceListItem"/>
 
@@ -225,12 +247,15 @@
                                 android:layout_width="match_parent"
                                 android:layout_height="wrap_content"
                                 android:layout_marginRight="@dimen/standard_margin"
-                                android:layout_marginLeft="@dimen/standard_margin"
-                                android:maxLines="1"
+                                android:layout_marginBottom="@dimen/standard_margin"
+                                android:layout_below="@+id/twitter_headline"
+                                android:layout_toEndOf="@id/twitter_icon"
+                                android:layout_toRightOf="@id/twitter_icon"
+                                android:maxLines="3"
                                 android:text="@string/placeholder_filename"
                                 android:textColor="?android:attr/textColorSecondary"/>
 
-                        </LinearLayout>
+                        </RelativeLayout>
 
                     </LinearLayout>
 

+ 0 - 31
src/main/res/layout/user_info_list_item.xml

@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-                android:id="@+id/user_info_item_layout"
-                android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:orientation="vertical">
-
-    <TextView
-        android:id="@+id/attribute_headline_tv"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_marginTop="8dp"
-        android:layout_marginRight="8dp"
-        android:layout_marginLeft="8dp"
-        android:layout_marginBottom="4dp"
-        android:textSize="20sp"
-        android:textStyle="bold"
-        android:text="@string/placeholder_filename"/>
-
-    <TextView
-        android:id="@+id/attribute_value_tv"
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_below="@id/attribute_headline_tv"
-        android:layout_marginLeft="8dp"
-        android:layout_marginRight="8dp"
-        android:layout_marginBottom="8dp"
-        android:textSize="16sp"
-        android:text="@string/placeholder_filename"/>
-
-</RelativeLayout>

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

@@ -104,6 +104,9 @@
 
     <!-- login data links -->
     <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 -->
     <bool name="analytics_enabled">false</bool>