浏览代码

Merge pull request #7971 from nextcloud/textfieldDesign

Textfield design
Andy Scherzinger 4 年之前
父节点
当前提交
b760abde36

+ 1 - 1
scripts/analysis/lint-results.txt

@@ -1,2 +1,2 @@
 DO NOT TOUCH; GENERATED BY DRONE
-      <span class="mdl-layout-title">Lint Report: 243 warnings</span>
+      <span class="mdl-layout-title">Lint Report: 240 warnings</span>

+ 2 - 1
src/androidTest/java/com/owncloud/android/ui/LoginIT.java

@@ -47,6 +47,7 @@ import androidx.test.rule.GrantPermissionRule;
 import static androidx.test.espresso.Espresso.onView;
 import static androidx.test.espresso.action.ViewActions.click;
 import static androidx.test.espresso.action.ViewActions.typeText;
+import static androidx.test.espresso.action.ViewActions.typeTextIntoFocusedView;
 import static androidx.test.espresso.matcher.ViewMatchers.withId;
 import static androidx.test.espresso.web.sugar.Web.onWebView;
 import static androidx.test.espresso.web.webdriver.DriverAtoms.findElement;
@@ -86,7 +87,7 @@ public class LoginIT extends AbstractIT {
 
         onView(withId(R.id.login)).perform(click());
         onView(withId(R.id.host_url_input)).perform(typeText(baseUrl));
-        onView(withId(R.id.test_server_button)).perform(click());
+        onView(withId(R.id.host_url_input)).perform(typeTextIntoFocusedView("\n"));
 
         Thread.sleep(3000);
 

+ 12 - 0
src/androidTest/java/com/owncloud/android/ui/activity/PassCodeActivityIT.kt

@@ -38,6 +38,10 @@ class PassCodeActivityIT : AbstractIT() {
         val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_CHECK))
 
         waitForIdleSync()
+
+        sut.runOnUiThread { sut.binding.txt0.clearFocus() }
+
+        shortSleep()
         screenshot(sut)
     }
 
@@ -47,6 +51,10 @@ class PassCodeActivityIT : AbstractIT() {
         val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_REQUEST_WITH_RESULT))
 
         waitForIdleSync()
+
+        sut.runOnUiThread { sut.binding.txt0.clearFocus() }
+
+        shortSleep()
         screenshot(sut)
     }
 
@@ -56,6 +64,10 @@ class PassCodeActivityIT : AbstractIT() {
         val sut = activityRule.launchActivity(Intent(PassCodeActivity.ACTION_CHECK_WITH_RESULT))
 
         waitForIdleSync()
+
+        sut.runOnUiThread { sut.binding.txt0.clearFocus() }
+
+        shortSleep()
         screenshot(sut)
     }
 }

+ 90 - 96
src/main/java/com/owncloud/android/authentication/AuthenticatorActivity.java

@@ -73,7 +73,6 @@ import android.webkit.WebResourceRequest;
 import android.webkit.WebResourceResponse;
 import android.webkit.WebView;
 import android.webkit.WebViewClient;
-import android.widget.EditText;
 import android.widget.ImageView;
 import android.widget.ProgressBar;
 import android.widget.TextView;
@@ -91,6 +90,8 @@ import com.nextcloud.client.onboarding.OnboardingService;
 import com.nextcloud.client.preferences.AppPreferences;
 import com.owncloud.android.MainApp;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.AccountSetupBinding;
+import com.owncloud.android.databinding.AccountSetupWebviewBinding;
 import com.owncloud.android.lib.common.OwnCloudAccount;
 import com.owncloud.android.lib.common.OwnCloudClient;
 import com.owncloud.android.lib.common.OwnCloudClientFactory;
@@ -198,7 +199,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private static final int REQUEST_CODE_QR_SCAN = 101;
     public static final int REQUEST_CODE_FIRST_RUN = 102;
 
-
     /// parameters from EXTRAs in starter Intent
     private byte mAction;
     private Account mAccount;
@@ -210,8 +210,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private AccountManager mAccountMgr;
 
     /// Server PRE-Fragment elements
-    private EditText mHostUrlInput;
-    private TextView mServerStatusView;
+    private AccountSetupBinding accountSetupBinding;
+    private AccountSetupWebviewBinding accountSetupWebviewBinding;
 
     private String mServerStatusText = EMPTY_STRING;
     private int mServerStatusIcon;
@@ -219,9 +219,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     private GetServerInfoOperation.ServerInfo mServerInfo = new GetServerInfoOperation.ServerInfo();
 
     /// Authentication PRE-Fragment elements
-    private TextView mAuthStatusView;
-    private WebView mLoginWebView;
-
     private WebViewFidoBridge webViewFidoU2fBridge;
     private WebViewWebauthnBridge webViewWebauthnBridge;
 
@@ -308,11 +305,12 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         /// load user interface
         if (webViewLoginMethod) {
             setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
-            setContentView(R.layout.account_setup_webview);
-            mLoginWebView = findViewById(R.id.login_webview);
+            accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater());
+            setContentView(accountSetupWebviewBinding.getRoot());
             initWebViewLogin(webloginUrl, false);
         } else {
-            setContentView(R.layout.account_setup);
+            accountSetupBinding = AccountSetupBinding.inflate(getLayoutInflater());
+            setContentView(accountSetupBinding.getRoot());
 
             /// initialize general UI elements
             initOverallUi();
@@ -321,7 +319,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
             /// initialize block to be moved to single Fragment to retrieve and validate credentials
             initAuthorizationPreFragment(savedInstanceState);
-
         }
 
         initServerPreFragment(savedInstanceState);
@@ -344,32 +341,32 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     @SuppressFBWarnings("ANDROID_WEB_VIEW_JAVASCRIPT")
     @SuppressLint("SetJavaScriptEnabled")
     private void initWebViewLogin(String baseURL, boolean useGenericUserAgent) {
-        mLoginWebView.setVisibility(View.GONE);
-
-        final ProgressBar progressBar = findViewById(R.id.login_webview_progress_bar);
+        accountSetupWebviewBinding.loginWebview.setVisibility(View.GONE);
 
-        mLoginWebView.getSettings().setAllowFileAccess(false);
-        mLoginWebView.getSettings().setJavaScriptEnabled(true);
-        mLoginWebView.getSettings().setDomStorageEnabled(true);
+        accountSetupWebviewBinding.loginWebview.getSettings().setAllowFileAccess(false);
+        accountSetupWebviewBinding.loginWebview.getSettings().setJavaScriptEnabled(true);
+        accountSetupWebviewBinding.loginWebview.getSettings().setDomStorageEnabled(true);
 
         if (useGenericUserAgent) {
-            mLoginWebView.getSettings().setUserAgentString(MainApp.getUserAgent());
+            accountSetupWebviewBinding.loginWebview.getSettings().setUserAgentString(MainApp.getUserAgent());
         } else {
-            mLoginWebView.getSettings().setUserAgentString(getWebLoginUserAgent());
+            accountSetupWebviewBinding.loginWebview.getSettings().setUserAgentString(getWebLoginUserAgent());
         }
-        mLoginWebView.getSettings().setSaveFormData(false);
-        mLoginWebView.getSettings().setSavePassword(false);
+        accountSetupWebviewBinding.loginWebview.getSettings().setSaveFormData(false);
+        accountSetupWebviewBinding.loginWebview.getSettings().setSavePassword(false);
 
         FidoDialogOptions.Builder dialogOptionsBuilder = FidoDialogOptions.builder();
         dialogOptionsBuilder.setShowSdkLogo(true);
         dialogOptionsBuilder.setTheme(R.style.FidoDialog);
-        webViewFidoU2fBridge = WebViewFidoBridge.createInstanceForWebView(this, mLoginWebView, dialogOptionsBuilder);
+        webViewFidoU2fBridge = WebViewFidoBridge.createInstanceForWebView(
+            this, accountSetupWebviewBinding.loginWebview, dialogOptionsBuilder);
 
         WebauthnDialogOptions.Builder webauthnOptionsBuilder = WebauthnDialogOptions.builder();
         webauthnOptionsBuilder.setShowSdkLogo(true);
         webauthnOptionsBuilder.setAllowSkipPin(true);
         webauthnOptionsBuilder.setTheme(R.style.FidoDialog);
-        webViewWebauthnBridge = WebViewWebauthnBridge.createInstanceForWebView(this, mLoginWebView, webauthnOptionsBuilder);
+        webViewWebauthnBridge = WebViewWebauthnBridge.createInstanceForWebView(
+            this, accountSetupWebviewBinding.loginWebview, webauthnOptionsBuilder);
 
         Map<String, String> headers = new HashMap<>();
         headers.put(RemoteOperation.OCS_API_HEADER, RemoteOperation.OCS_API_HEADER_VALUE);
@@ -381,17 +378,17 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             url = getResources().getString(R.string.webview_login_url);
         }
 
-        mLoginWebView.loadUrl(url, headers);
+        accountSetupWebviewBinding.loginWebview.loadUrl(url, headers);
 
-        setClient(progressBar);
+        setClient();
     }
 
     @Override
     public boolean onKeyDown(int keyCode, KeyEvent event) {
-        if (mLoginWebView != null && event.getAction() == KeyEvent.ACTION_DOWN &&
+        if (accountSetupWebviewBinding != null && event.getAction() == KeyEvent.ACTION_DOWN &&
             keyCode == KeyEvent.KEYCODE_BACK) {
-            if (mLoginWebView.canGoBack()) {
-                mLoginWebView.goBack();
+            if (accountSetupWebviewBinding.loginWebview.canGoBack()) {
+                accountSetupWebviewBinding.loginWebview.goBack();
             } else {
                 finish();
             }
@@ -400,8 +397,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         return super.onKeyDown(keyCode, event);
     }
 
-    private void setClient(ProgressBar progressBar) {
-        mLoginWebView.setWebViewClient(new WebViewClient() {
+    private void setClient() {
+        accountSetupWebviewBinding.loginWebview.setWebViewClient(new WebViewClient() {
             @Override
             public WebResourceResponse shouldInterceptRequest(WebView view, WebResourceRequest request) {
                 webViewFidoU2fBridge.delegateShouldInterceptRequest(view, request);
@@ -429,8 +426,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             public void onPageFinished(WebView view, String url) {
                 super.onPageFinished(view, url);
 
-                progressBar.setVisibility(View.GONE);
-                mLoginWebView.setVisibility(View.VISIBLE);
+                accountSetupWebviewBinding.loginWebviewProgressBar.setVisibility(View.GONE);
+                accountSetupWebviewBinding.loginWebview.setVisibility(View.VISIBLE);
 
                 ThemeUtils.colorStatusBar(AuthenticatorActivity.this, primaryColor);
                 getWindow().setNavigationBarColor(primaryColor);
@@ -452,14 +449,14 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             }
 
             public void onReceivedError(WebView view, int errorCode, String description, String failingUrl) {
-                progressBar.setVisibility(View.GONE);
-                mLoginWebView.setVisibility(View.VISIBLE);
+                accountSetupWebviewBinding.loginWebviewProgressBar.setVisibility(View.GONE);
+                accountSetupWebviewBinding.loginWebview.setVisibility(View.VISIBLE);
 
                 InputStream resources = getResources().openRawResource(R.raw.custom_error);
                 String customError = DisplayUtils.getData(resources);
 
                 if (!customError.isEmpty()) {
-                    mLoginWebView.loadData(customError, "text/html; charset=UTF-8", null);
+                    accountSetupWebviewBinding.loginWebview.loadData(customError, "text/html; charset=UTF-8", null);
                 }
             }
         });
@@ -470,8 +467,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             String prefix = getString(R.string.login_data_own_scheme) + PROTOCOL_SUFFIX + "login/";
             LoginUrlInfo loginUrlInfo = parseLoginDataUrl(prefix, dataString);
 
-            if (mHostUrlInput != null) {
-                mHostUrlInput.setText("");
+            if (accountSetupBinding != null) {
+                accountSetupBinding.hostUrlInput.setText("");
             }
             mServerInfo.mBaseUrl = AuthenticatorUrlUtils.normalizeUrlSuffix(loginUrlInfo.serverAddress);
             webViewUser = loginUrlInfo.username;
@@ -530,23 +527,20 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * Configures elements in the user interface under direct control of the Activity.
      */
     private void initOverallUi() {
-        mHostUrlInput = findViewById(R.id.host_url_input);
-        mAuthStatusView = findViewById(R.id.auth_status_text);
-        mServerStatusView = findViewById(R.id.server_status_text);
+        accountSetupBinding.hostUrlContainer.setEndIconOnClickListener(v -> checkOcServer());
+
+        accountSetupBinding.hostUrlInputHelperText.setText(
+            String.format(getString(R.string.login_url_helper_text), getString(R.string.app_name)));
 
-        ImageView scanQR = findViewById(R.id.scan_qr);
         if (deviceInfo.hasCamera(this)) {
-            scanQR.setOnClickListener(v -> onScan());
-            ThemeUtils.tintDrawable(scanQR.getDrawable(), getResources().getColor(R.color.login_text_color));
+            accountSetupBinding.scanQr.setOnClickListener(v -> onScan());
+            ThemeUtils.tintDrawable(accountSetupBinding.scanQr.getDrawable(),
+                                    getResources().getColor(R.color.login_text_color));
         } else {
-            scanQR.setVisibility(View.GONE);
+            accountSetupBinding.scanQr.setVisibility(View.GONE);
         }
     }
 
-    public void onTestServerConnectionClick(View v) {
-        checkOcServer();
-    }
-
     /**
      * @param savedInstanceState Saved activity state, as in {{@link #onCreate(Bundle)}
      */
@@ -588,10 +582,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * @param savedInstanceState Saved activity state, as in {{@link #onCreate(Bundle)}
      */
     private void initAuthorizationPreFragment(Bundle savedInstanceState) {
-
-        /// step 0 - get UI elements in layout
-        mAuthStatusView = findViewById(R.id.auth_status_text);
-
         /// step 1 - load and process relevant inputs (resources, intent, savedInstanceState)
         if (savedInstanceState != null) {
             mAuthStatusText = savedInstanceState.getString(KEY_AUTH_STATUS_TEXT);
@@ -601,8 +591,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         /// step 2 - set properties of UI elements (text, visibility, enabled...)
         showAuthStatus();
 
-        mHostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
-        mHostUrlInput.setOnEditorActionListener(this);
+        accountSetupBinding.hostUrlInput.setImeOptions(EditorInfo.IME_ACTION_NEXT);
+        accountSetupBinding.hostUrlInput.setOnEditorActionListener(this);
     }
 
     /**
@@ -689,8 +679,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
 
         if (intent.getBooleanExtra(EXTRA_USE_PROVIDER_AS_WEBLOGIN, false)) {
-            setContentView(R.layout.account_setup_webview);
-            mLoginWebView = findViewById(R.id.login_webview);
+            accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater());
+            setContentView(accountSetupWebviewBinding.getRoot());
             initWebViewLogin(getString(R.string.provider_registration_server), true);
         }
     }
@@ -719,7 +709,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         if (!bindService(new Intent(this, OperationsService.class),
                          mOperationsServiceConnection,
                          Context.BIND_AUTO_CREATE)) {
-            DisplayUtils.showSnackMessage(findViewById(R.id.scroll), R.string.error_cant_bind_to_operations_service);
+            DisplayUtils.showSnackMessage(accountSetupBinding.scroll, R.string.error_cant_bind_to_operations_service);
             finish();
         }
 
@@ -753,8 +743,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
     private void checkOcServer() {
         String uri;
-        if (mHostUrlInput != null && !mHostUrlInput.getText().toString().isEmpty()) {
-            uri = mHostUrlInput.getText().toString().trim();
+        if (accountSetupBinding != null && accountSetupBinding.hostUrlInput.getText()!= null &&
+            !accountSetupBinding.hostUrlInput.getText().toString().isEmpty()) {
+            uri = accountSetupBinding.hostUrlInput.getText().toString().trim();
         } else {
             uri = mServerInfo.mBaseUrl;
         }
@@ -762,9 +753,9 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         mServerInfo = new GetServerInfoOperation.ServerInfo();
 
         if (uri.length() != 0) {
-            if (mHostUrlInput != null) {
+            if (accountSetupBinding != null) {
                 uri = AuthenticatorUrlUtils.stripIndexPhpOrAppsFiles(uri);
-                mHostUrlInput.setText(uri);
+                accountSetupBinding.hostUrlInput.setText(uri);
             }
 
             // Handle internationalized domain names
@@ -775,7 +766,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 Log_OC.e(TAG, "Error converting internationalized domain name " + uri, ex);
             }
 
-            if (mHostUrlInput != null) {
+            if (accountSetupBinding != null) {
                 mServerStatusText = getResources().getString(R.string.auth_testing_connection);
                 mServerStatusIcon = R.drawable.progress_small;
                 showServerStatus();
@@ -852,7 +843,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
                 } catch (AccountNotFoundException e) {
                     Log_OC.e(TAG, "Account " + mAccount + " was removed!", e);
-                    DisplayUtils.showSnackMessage(findViewById(R.id.scroll), R.string.auth_account_does_not_exist);
+                    DisplayUtils.showSnackMessage(accountSetupBinding.scroll, R.string.auth_account_does_not_exist);
                     finish();
                 }
             }
@@ -915,8 +906,8 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                     }
                 }).start();
 
-                setContentView(R.layout.account_setup_webview);
-                mLoginWebView = findViewById(R.id.login_webview);
+                accountSetupWebviewBinding = AccountSetupWebviewBinding.inflate(getLayoutInflater());
+                setContentView(accountSetupWebviewBinding.getRoot());
                 initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false);
             }
         } else {
@@ -946,7 +937,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
             case OK_NO_SSL:
             case OK:
-                if (mHostUrlInput.getText().toString().trim().toLowerCase(Locale.ROOT).startsWith(HTTP_PROTOCOL)) {
+                if (accountSetupBinding.hostUrlInput.getText() != null &&
+                    accountSetupBinding.hostUrlInput
+                    .getText()
+                    .toString()
+                    .trim()
+                    .toLowerCase(Locale.ROOT)
+                    .startsWith(HTTP_PROTOCOL)) {
                     mServerStatusText = getResources().getString(R.string.auth_connection_established);
                     mServerStatusIcon = R.drawable.ic_ok;
                 } else {
@@ -1044,7 +1041,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             case OK_NO_SSL:
             case OK:
                 if (showWebViewLoginUrl) {
-                    if (mHostUrlInput.getText().toString().trim().toLowerCase(Locale.ROOT).startsWith(HTTP_PROTOCOL)) {
+                    if (accountSetupBinding.hostUrlInput.getText() != null &&
+                        accountSetupBinding.hostUrlInput
+                        .getText()
+                        .toString()
+                        .trim()
+                        .toLowerCase(Locale.ROOT)
+                        .startsWith(HTTP_PROTOCOL)) {
                         mAuthStatusText = getResources().getString(R.string.auth_connection_established);
                         mAuthStatusIcon = R.drawable.ic_ok;
                     } else {
@@ -1115,7 +1118,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
 
                 } catch (AccountNotFoundException e) {
                     Log_OC.e(TAG, "Account " + mAccount + " was removed!", e);
-                    DisplayUtils.showSnackMessage(findViewById(R.id.scroll), R.string.auth_account_does_not_exist);
+                    DisplayUtils.showSnackMessage(accountSetupBinding.scroll, R.string.auth_account_does_not_exist);
                     finish();
                 }
             }
@@ -1137,15 +1140,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
                 }
             } else {
                 // init webView again
-                if (mLoginWebView != null) {
-                    mLoginWebView.setVisibility(View.GONE);
+                if (accountSetupWebviewBinding != null) {
+                    accountSetupWebviewBinding.loginWebview.setVisibility(View.GONE);
                 }
-                setContentView(R.layout.account_setup);
+                accountSetupBinding = AccountSetupBinding.inflate(getLayoutInflater());
+                setContentView(accountSetupBinding.getRoot());
                 initOverallUi();
 
-                mAuthStatusView = findViewById(R.id.auth_status_text);
-                mHostUrlInput.setText(mServerInfo.mBaseUrl);
-                mServerStatusView.setVisibility(View.GONE);
+                accountSetupBinding.hostUrlInput.setText(mServerInfo.mBaseUrl);
+                accountSetupBinding.serverStatusText.setVisibility(View.GONE);
                 showAuthStatus();
             }
 
@@ -1165,17 +1168,15 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
             }
 
         } else {    // authorization fail due to client side - probably wrong credentials
-            mLoginWebView = findViewById(R.id.login_webview);
-
-            if (mLoginWebView != null) {
+            if (accountSetupWebviewBinding != null) {
                 initWebViewLogin(mServerInfo.mBaseUrl + WEB_LOGIN, false);
-                DisplayUtils.showSnackMessage(this, mLoginWebView, R.string.auth_access_failed,
+                DisplayUtils.showSnackMessage(this,
+                                              accountSetupWebviewBinding.loginWebview, R.string.auth_access_failed,
                                               result.getLogMessage());
             } else {
                 DisplayUtils.showSnackMessage(this, R.string.auth_access_failed, result.getLogMessage());
 
                 // init webView again
-                mLoginWebView.setVisibility(View.GONE);
                 updateAuthStatusIconAndText(result);
             }
 
@@ -1331,11 +1332,11 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      */
     private void showServerStatus() {
         if (mServerStatusIcon == NO_ICON && EMPTY_STRING.equals(mServerStatusText)) {
-            mServerStatusView.setVisibility(View.INVISIBLE);
+            accountSetupBinding.serverStatusText.setVisibility(View.INVISIBLE);
         } else {
-            mServerStatusView.setText(mServerStatusText);
-            mServerStatusView.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);
-            mServerStatusView.setVisibility(View.VISIBLE);
+            accountSetupBinding.serverStatusText.setText(mServerStatusText);
+            accountSetupBinding.serverStatusText.setCompoundDrawablesWithIntrinsicBounds(mServerStatusIcon, 0, 0, 0);
+            accountSetupBinding.serverStatusText.setVisibility(View.VISIBLE);
         }
     }
 
@@ -1344,13 +1345,13 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
      * authorization server.
      */
     private void showAuthStatus() {
-        if (mAuthStatusView != null) {
+        if (accountSetupBinding != null) {
             if (mAuthStatusIcon == NO_ICON && EMPTY_STRING.equals(mAuthStatusText)) {
-                mAuthStatusView.setVisibility(View.INVISIBLE);
+                accountSetupBinding.authStatusText.setVisibility(View.INVISIBLE);
             } else {
-                mAuthStatusView.setText(mAuthStatusText);
-                mAuthStatusView.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);
-                mAuthStatusView.setVisibility(View.VISIBLE);
+                accountSetupBinding.authStatusText.setText(mAuthStatusText);
+                accountSetupBinding.authStatusText.setCompoundDrawablesWithIntrinsicBounds(mAuthStatusIcon, 0, 0, 0);
+                accountSetupBinding.authStatusText.setVisibility(View.VISIBLE);
             }
         }
     }
@@ -1364,7 +1365,7 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
     @Override
     public boolean onEditorAction(TextView inputField, int actionId, KeyEvent event) {
         if ((actionId == EditorInfo.IME_ACTION_NEXT || actionId == EditorInfo.IME_NULL)
-            && inputField != null && inputField.equals(mHostUrlInput)) {
+            && inputField != null && inputField.equals(accountSetupBinding.hostUrlInput)) {
             checkOcServer();
         }
         return false;   // always return false to grant that the software keyboard is hidden anyway
@@ -1469,13 +1470,6 @@ public class AuthenticatorActivity extends AccountAuthenticatorActivity
         }
     }
 
-    /**
-     * For retrieving the clicking on authentication cancel button.
-     */
-    public void doNegativeAuthenticationDialogClick() {
-        mIsFirstAuthAttempt = true;
-    }
-
     @Override
     protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
         super.onActivityResult(requestCode, resultCode, data);

+ 0 - 1
src/main/java/com/owncloud/android/ui/activity/EditorWebView.java

@@ -22,7 +22,6 @@
 
 package com.owncloud.android.ui.activity;
 
-import android.annotation.SuppressLint;
 import android.app.DownloadManager;
 import android.content.Context;
 import android.content.Intent;

+ 5 - 0
src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java

@@ -50,6 +50,7 @@ import java.util.Arrays;
 import javax.inject.Inject;
 
 import androidx.annotation.NonNull;
+import androidx.annotation.VisibleForTesting;
 import androidx.appcompat.app.AppCompatActivity;
 
 public class PassCodeActivity extends AppCompatActivity implements Injectable {
@@ -174,6 +175,10 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
         }
     }
 
+    @VisibleForTesting
+    public PasscodelockBinding getBinding() {
+        return binding;
+    }
 
     /**
      * Binds the appropriate listeners to the input boxes receiving each digit of the pass code.

+ 13 - 26
src/main/java/com/owncloud/android/ui/dialog/NoteDialogFragment.java

@@ -21,7 +21,6 @@
 
 package com.owncloud.android.ui.dialog;
 
-import android.annotation.SuppressLint;
 import android.app.Dialog;
 import android.content.DialogInterface;
 import android.os.Bundle;
@@ -30,9 +29,8 @@ import android.view.View;
 import android.view.Window;
 import android.view.WindowManager.LayoutParams;
 
-import com.google.android.material.textfield.TextInputEditText;
-import com.google.android.material.textfield.TextInputLayout;
 import com.owncloud.android.R;
+import com.owncloud.android.databinding.NoteDialogBinding;
 import com.owncloud.android.lib.resources.shares.OCShare;
 import com.owncloud.android.ui.activity.ComponentsGetter;
 import com.owncloud.android.utils.DisplayUtils;
@@ -42,9 +40,6 @@ import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
 import androidx.appcompat.app.AlertDialog;
 import androidx.fragment.app.DialogFragment;
-import butterknife.BindView;
-import butterknife.ButterKnife;
-import butterknife.Unbinder;
 
 /**
  * Dialog to input a multiline note for a share
@@ -56,13 +51,7 @@ public class NoteDialogFragment extends DialogFragment implements DialogInterfac
     public static final String NOTE_FRAGMENT = "NOTE_FRAGMENT";
 
     private OCShare share;
-    private Unbinder unbinder;
-
-    @BindView(R.id.user_input_container)
-    public TextInputLayout noteEditTextInputLayout;
-
-    @BindView(R.id.user_input)
-    public TextInputEditText noteEditText;
+    private NoteDialogBinding binding;
 
     public static NoteDialogFragment newInstance(OCShare share) {
         NoteDialogFragment frag = new NoteDialogFragment();
@@ -103,15 +92,14 @@ public class NoteDialogFragment extends DialogFragment implements DialogInterfac
 
         // Inflate the layout for the dialog
         LayoutInflater inflater = requireActivity().getLayoutInflater();
-        @SuppressLint("InflateParams") View view = inflater.inflate(R.layout.note_dialog, null, false);
-
-        unbinder = ButterKnife.bind(this, view);
+        binding = NoteDialogBinding.inflate(inflater, null, false);
+        View view = binding.getRoot();
 
         // Setup layout
-        noteEditText.setText(share.getNote());
-        noteEditText.setHighlightColor(ThemeUtils.primaryColor(getActivity()));
-        noteEditText.requestFocus();
-        ThemeUtils.colorTextInputLayout(noteEditTextInputLayout, accentColor);
+        binding.noteText.setText(share.getNote());
+        binding.noteText.setHighlightColor(ThemeUtils.primaryColor(getActivity()));
+        binding.noteText.requestFocus();
+        ThemeUtils.colorTextInputLayout(binding.noteContainer, accentColor);
 
         // Build the dialog
         AlertDialog.Builder builder = new AlertDialog.Builder(requireActivity());
@@ -138,8 +126,8 @@ public class NoteDialogFragment extends DialogFragment implements DialogInterfac
             if (componentsGetter != null) {
                 String note = "";
 
-                if (noteEditText.getText() != null) {
-                    note = noteEditText.getText().toString().trim();
+                if (binding.noteText.getText() != null) {
+                    note = binding.noteText.getText().toString().trim();
                 }
 
                 componentsGetter.getFileOperationsHelper().updateNoteToShare(share, note);
@@ -150,9 +138,8 @@ public class NoteDialogFragment extends DialogFragment implements DialogInterfac
     }
 
     @Override
-    public void onStop() {
-        unbinder.unbind();
-
-        super.onStop();
+    public void onDestroyView() {
+        super.onDestroyView();
+        binding = null;
     }
 }

+ 43 - 43
src/main/res/layout-land/account_setup.xml

@@ -55,83 +55,83 @@
             <FrameLayout
                 android:id="@+id/host_url_frame"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/zero"
-                android:background="@color/primary">
+                android:layout_height="wrap_content">
 
-                <LinearLayout
+                <com.google.android.material.textfield.TextInputLayout
+                    android:id="@+id/host_url_container"
+                    style="@style/Widget.App.Login.TextInputLayout"
                     android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:orientation="vertical">
-
-                    <TextView
-                        android:id="@+id/edit_text"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:ems="10"
-                        android:text="@string/auth_host_url"
-                        android:labelFor="@+id/host_url_input"
-                        android:textColor="@color/login_text_color" />
+                    android:layout_height="wrap_content"
+                    android:hint="@string/auth_host_url"
+                    android:theme="@style/TextInputLayoutLogin"
+                    app:endIconContentDescription="@string/test_server_button"
+                    app:endIconDrawable="@drawable/arrow_right"
+                    app:endIconMode="custom"
+                    app:endIconTint="@color/login_text_color">
 
                     <com.google.android.material.textfield.TextInputEditText
                         android:id="@+id/host_url_input"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
-                        android:minHeight="@dimen/minimum_size_for_touchable_area"
-                        android:layout_gravity="bottom"
-                        android:drawablePadding="@dimen/alternate_half_padding"
-                        android:inputType="textUri"
-                        android:paddingStart="@dimen/zero"
+                        android:gravity="top"
+                        android:importantForAutofill="no"
+                        android:inputType="textCapSentences|textMultiLine|textNoSuggestions"
+                        android:lines="1"
+                        android:minLines="1"
+                        android:paddingStart="@dimen/standard_padding"
                         android:paddingEnd="@dimen/alternate_padding_right"
-                        android:textColor="@color/login_text_color"
-                        android:textColorHint="@color/login_text_hint_color">
+                        android:scrollbars="vertical">
 
                         <requestFocus />
 
                     </com.google.android.material.textfield.TextInputEditText>
 
-                </LinearLayout>
-
-                <ImageButton
-                    android:id="@+id/test_server_button"
-                    android:layout_width="wrap_content"
-                    android:layout_height="wrap_content"
-                    android:layout_gravity="center_vertical|end"
-                    android:layout_marginEnd="@dimen/alternate_half_padding"
-                    android:background="@android:color/transparent"
-                    android:contentDescription="@string/test_server_button"
-                    android:onClick="onTestServerConnectionClick"
-                    android:padding="@dimen/zero"
-                    android:scaleType="fitCenter"
-                    android:src="@drawable/arrow_right"
-                    android:tint="@color/login_btn_tint" />
-
+                </com.google.android.material.textfield.TextInputLayout>
             </FrameLayout>
 
+            <TextView
+                android:id="@+id/host_url_input_helper_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:lineSpacingMultiplier="1.2"
+                android:paddingStart="@dimen/standard_padding"
+                android:paddingTop="@dimen/standard_quarter_padding"
+                android:paddingEnd="@dimen/standard_half_padding"
+                android:paddingBottom="@dimen/standard_eight_padding"
+                android:text="@string/login_url_helper_text"
+                android:textColor="@color/white_helper_text" />
+
             <TextView
                 android:id="@+id/server_status_text"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/alternate_half_margin"
                 android:contentDescription="@string/auth_testing_connection"
-                app:drawableStartCompat="@android:drawable/stat_notify_sync"
                 android:drawablePadding="@dimen/alternate_half_padding"
                 android:gravity="center_vertical"
                 android:minHeight="@dimen/display_text_min_height"
+                android:paddingStart="@dimen/standard_padding"
+                android:paddingTop="@dimen/standard_eight_padding"
+                android:paddingEnd="@dimen/standard_eight_padding"
+                android:paddingBottom="@dimen/standard_eight_padding"
                 android:text="@string/auth_testing_connection"
                 android:textColor="@color/login_text_color"
-                android:visibility="invisible" />
+                android:visibility="invisible"
+                app:drawableStartCompat="@android:drawable/stat_notify_sync" />
 
             <TextView
                 android:id="@+id/auth_status_text"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:contentDescription="@string/auth_unauthorized"
-                app:drawableStartCompat="@android:drawable/stat_notify_sync"
                 android:drawablePadding="@dimen/alternate_half_padding"
                 android:gravity="center_vertical"
+                android:paddingStart="@dimen/standard_padding"
+                android:paddingTop="@dimen/standard_eight_padding"
+                android:paddingEnd="@dimen/standard_eight_padding"
+                android:paddingBottom="@dimen/standard_eight_padding"
                 android:text="@string/auth_unauthorized"
-                android:textColor="@color/login_text_color" />
+                android:textColor="@color/login_text_color"
+                app:drawableStartCompat="@android:drawable/stat_notify_sync" />
 
             <ImageButton
                 android:id="@+id/scan_qr"

+ 43 - 43
src/main/res/layout/account_setup.xml

@@ -54,83 +54,83 @@
             <FrameLayout
                 android:id="@+id/host_url_frame"
                 android:layout_width="match_parent"
-                android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/zero"
-                android:background="@color/primary">
+                android:layout_height="wrap_content">
 
-                <LinearLayout
+                <com.google.android.material.textfield.TextInputLayout
+                    android:id="@+id/host_url_container"
+                    style="@style/Widget.App.Login.TextInputLayout"
                     android:layout_width="match_parent"
-                    android:layout_height="match_parent"
-                    android:orientation="vertical">
-
-                    <TextView
-                        android:id="@+id/edit_text"
-                        android:layout_width="match_parent"
-                        android:layout_height="wrap_content"
-                        android:ems="10"
-                        android:text="@string/auth_host_url"
-                        android:labelFor="@+id/host_url_input"
-                        android:textColor="@color/login_text_color" />
+                    android:layout_height="wrap_content"
+                    android:hint="@string/auth_host_url"
+                    android:theme="@style/TextInputLayoutLogin"
+                    app:endIconContentDescription="@string/test_server_button"
+                    app:endIconDrawable="@drawable/arrow_right"
+                    app:endIconMode="custom"
+                    app:endIconTint="@color/login_text_color">
 
                     <com.google.android.material.textfield.TextInputEditText
                         android:id="@+id/host_url_input"
                         android:layout_width="match_parent"
                         android:layout_height="wrap_content"
-                        android:minHeight="@dimen/minimum_size_for_touchable_area"
-                        android:layout_gravity="bottom"
-                        android:drawablePadding="@dimen/alternate_half_padding"
-                        android:inputType="textUri"
-                        android:paddingStart="@dimen/zero"
+                        android:gravity="top"
+                        android:importantForAutofill="no"
+                        android:inputType="textCapSentences|textMultiLine|textNoSuggestions"
+                        android:lines="1"
+                        android:minLines="1"
+                        android:paddingStart="@dimen/standard_padding"
                         android:paddingEnd="@dimen/alternate_padding_right"
-                        android:textColor="@color/login_text_color"
-                        android:textColorHint="@color/login_text_color">
+                        android:scrollbars="vertical">
 
                         <requestFocus />
 
                     </com.google.android.material.textfield.TextInputEditText>
 
-                </LinearLayout>
-
-                <ImageButton
-                    android:id="@+id/test_server_button"
-                    android:layout_width="@dimen/minimum_size_for_touchable_area"
-                    android:layout_height="@dimen/minimum_size_for_touchable_area"
-                    android:layout_gravity="center_vertical|end"
-                    android:layout_marginEnd="@dimen/alternate_half_padding"
-                    android:background="@android:color/transparent"
-                    android:contentDescription="@string/test_server_button"
-                    android:onClick="onTestServerConnectionClick"
-                    android:padding="12dp"
-                    android:scaleType="fitCenter"
-                    android:src="@drawable/arrow_right"
-                    android:tint="@color/login_text_color" />
-
+                </com.google.android.material.textfield.TextInputLayout>
             </FrameLayout>
 
+            <TextView
+                android:id="@+id/host_url_input_helper_text"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:lineSpacingMultiplier="1.2"
+                android:paddingStart="@dimen/standard_padding"
+                android:paddingTop="@dimen/standard_quarter_padding"
+                android:paddingEnd="@dimen/standard_half_padding"
+                android:paddingBottom="@dimen/standard_eight_padding"
+                android:text="@string/login_url_helper_text"
+                android:textColor="@color/white_helper_text" />
+
             <TextView
                 android:id="@+id/server_status_text"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
-                android:layout_marginBottom="@dimen/alternate_margin"
                 android:contentDescription="@string/auth_testing_connection"
-                app:drawableStartCompat="@android:drawable/stat_notify_sync"
                 android:drawablePadding="@dimen/alternate_half_padding"
                 android:gravity="center_vertical"
                 android:minHeight="@dimen/display_text_min_height"
+                android:paddingStart="@dimen/standard_padding"
+                android:paddingTop="@dimen/standard_eight_padding"
+                android:paddingEnd="@dimen/standard_eight_padding"
+                android:paddingBottom="@dimen/standard_eight_padding"
                 android:text="@string/auth_testing_connection"
                 android:textColor="@color/login_text_color"
-                android:visibility="invisible" />
+                android:visibility="invisible"
+                app:drawableStartCompat="@android:drawable/stat_notify_sync" />
 
             <TextView
                 android:id="@+id/auth_status_text"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
                 android:contentDescription="@string/auth_unauthorized"
-                app:drawableStartCompat="@android:drawable/stat_notify_sync"
                 android:drawablePadding="@dimen/alternate_half_padding"
                 android:gravity="center_vertical"
+                android:paddingStart="@dimen/standard_padding"
+                android:paddingTop="@dimen/standard_eight_padding"
+                android:paddingEnd="@dimen/standard_eight_padding"
+                android:paddingBottom="@dimen/standard_eight_padding"
                 android:text="@string/auth_unauthorized"
-                android:textColor="@color/login_text_color" />
+                android:textColor="@color/login_text_color"
+                app:drawableStartCompat="@android:drawable/stat_notify_sync" />
 
             <ImageButton
                 android:id="@+id/scan_qr"

+ 2 - 2
src/main/res/layout/note_dialog.xml

@@ -27,14 +27,14 @@
     android:padding="@dimen/standard_padding">
 
     <com.google.android.material.textfield.TextInputLayout
-        android:id="@+id/user_input_container"
+        android:id="@+id/note_container"
         android:layout_width="match_parent"
         android:layout_height="wrap_content"
         style="@style/TextInputLayout"
         android:hint="@string/hint_note">
 
         <com.google.android.material.textfield.TextInputEditText
-            android:id="@+id/user_input"
+            android:id="@+id/note_text"
             android:layout_width="match_parent"
             android:layout_height="wrap_content"
             android:importantForAutofill="no"

+ 1 - 0
src/main/res/values/colors.xml

@@ -22,6 +22,7 @@
     <color name="list_item_lastmod_and_filesize_text">@color/secondary_text_color</color>
     <color name="black">#000000</color>
     <color name="white">#ffffff</color>
+    <color name="white_helper_text">#B3FFFFFF</color>
     <color name="text_color">#333333</color>
     <color name="drawer_text_color">@color/secondary_text_color</color>
     <color name="text_color_inverse">#ffffff</color>

+ 1 - 0
src/main/res/values/strings.xml

@@ -952,4 +952,5 @@
     <string name="wrong_storage_path_desc">This might be due to a backup restore on another device. Falling back to default. Please check settings to adjust data storage folder.</string>
     <string name="dialog_close">Close</string>
     <string name="direct_login_failed">Login via direct link failed!</string>
+    <string name="login_url_helper_text">The link to your %1$s web interface when you open it in the browser.</string>
 </resources>

+ 31 - 0
src/main/res/values/styles.xml

@@ -313,9 +313,40 @@
 	<style name="NextcloudTextAppearanceMedium" parent="@style/TextAppearance.AppCompat.Medium">
 	</style>
 
+    <style name="Widget.App.Login.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+        <item name="materialThemeOverlay">@style/ThemeOverlay.App.Login.TextInputLayout</item>
+        <item name="shapeAppearance">@style/ShapeAppearance.MaterialComponents.SmallComponent</item>
+        <item name="hintTextColor">?attr/colorOnSurface</item>
+    </style>
+
+    <style name="ThemeOverlay.App.Login.TextInputLayout" parent="">
+        <item name="colorPrimary">@color/white</item>
+        <item name="colorOnSurface">@color/white</item>
+        <item name="colorError">@color/hwSecurityRed</item>
+        <item name="textAppearanceSubtitle1">@style/TextAppearance.MaterialComponents.Subtitle1</item>
+        <item name="textAppearanceCaption">@style/TextAppearance.MaterialComponents.Caption</item>
+        <item name="editTextStyle">@style/Widget.MaterialComponents.TextInputEditText.OutlinedBox</item>
+    </style>
+
 	<style name="TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
     </style>
 
+    <style name="TextInputLayoutLogin" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
+        <item name="boxStrokeColor">@color/white</item>
+        <item name="boxStrokeErrorColor">@color/white</item>
+        <item name="hintTextAppearance">@style/HintTextLogin</item>
+        <item name="errorTextAppearance">@style/HintTextLogin</item>
+        <item name="android:colorPrimary">@color/white</item>
+        <!-- Theme attributes -->
+        <item name="android:textColorHint">#80FFFFFF</item>
+        <item name="colorControlNormal">@color/white</item>
+        <item name="colorControlActivated">@color/white</item>
+    </style>
+
+    <style name="HintTextLogin" parent="TextAppearance.AppCompat">
+        <item name="android:textColor">@color/white</item>
+    </style>
+
 	<style name="AppTabTextAppearance" parent="@style/TextAppearance.Design.Tab">
 		<item name="android:textSize">16sp</item>
 		<item name="textAllCaps">false</item>