backport/enhance login screen

Signed-off-by: Andy Scherzinger <info@andy-scherzinger.de>
This commit is contained in:
Andy Scherzinger 2021-04-09 17:07:17 +02:00
parent b9ce0f3487
commit c812372db5
No known key found for this signature in database
GPG Key ID: 6CADC7E3523C308B
8 changed files with 278 additions and 166 deletions

View File

@ -12,6 +12,7 @@ Types of changes can be: Added/Changed/Deprecated/Removed/Fixed/Security
### Changed
- improve conversation list design and dark/light theming (@AndyScherzinger)
- introduce new dark/light toolbar/searchbar design (@AndyScherzinger)
- improve login screen design (@AndyScherzinger)
### Fixed
- @ in username is allowed for phonebook sync

View File

@ -3,6 +3,7 @@
*
* @author Andy Scherzinger
* @author Mario Danic
* Copyright (C) 2021 Andy Scherzinger (info@andy-scherzinger.de)
* Copyright (C) 2017 Mario Danic (mario@lovelyhq.com)
*
* This program is free software: you can redistribute it and/or modify
@ -27,18 +28,18 @@ import android.content.pm.ActivityInfo;
import android.net.Uri;
import android.os.Bundle;
import android.security.KeyChain;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import android.widget.ProgressBar;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.bluelinelabs.conductor.RouterTransaction;
import com.bluelinelabs.conductor.changehandler.HorizontalChangeHandler;
import com.google.android.material.textfield.TextInputEditText;
import com.google.android.material.textfield.TextInputLayout;
import com.nextcloud.talk.R;
import com.nextcloud.talk.api.NcApi;
import com.nextcloud.talk.application.NextcloudTalkApplication;
@ -66,20 +67,22 @@ import butterknife.OnClick;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import studio.carbonylgroup.textfieldboxes.ExtendedEditText;
import studio.carbonylgroup.textfieldboxes.TextFieldBoxes;
@AutoInjector(NextcloudTalkApplication.class)
public class ServerSelectionController extends BaseController {
public static final String TAG = "ServerSelectionController";
@BindView(R.id.extended_edit_text)
ExtendedEditText serverEntry;
@BindView(R.id.text_field_boxes)
TextFieldBoxes textFieldBoxes;
@BindView(R.id.progress_bar)
ProgressBar progressBar;
@BindView(R.id.serverEntryTextInputLayout)
TextInputLayout serverEntryTextInputLayout;
@BindView(R.id.serverEntryTextInputEditText)
TextInputEditText serverEntryTextInputEditText;
@BindView(R.id.serverEntryProgressBar)
LinearLayout progressBar;
@BindView(R.id.error_text)
TextView errorText;
@BindView(R.id.host_url_input_helper_text)
TextView hostUrlInputHelperText;
@BindView(R.id.helper_text_view)
TextView providersTextView;
@BindView(R.id.cert_text_view)
@ -131,12 +134,12 @@ public class ServerSelectionController extends BaseController {
getActionBar().hide();
}
textFieldBoxes.getEndIconImageButton().setBackgroundDrawable(getResources().getDrawable(R.drawable
.ic_arrow_forward_white_24px));
textFieldBoxes.getEndIconImageButton().setAlpha(0.5f);
textFieldBoxes.getEndIconImageButton().setEnabled(false);
textFieldBoxes.getEndIconImageButton().setVisibility(View.VISIBLE);
textFieldBoxes.getEndIconImageButton().setOnClickListener(view1 -> checkServerAndProceed());
hostUrlInputHelperText.setText(String.format(
getResources().getString(R.string.nc_server_helper_text),
getResources().getString(R.string.nc_server_product_name))
);
serverEntryTextInputLayout.setEndIconOnClickListener(view1 -> checkServerAndProceed());
if (getResources().getBoolean(R.bool.hide_auth_cert)) {
certTextView.setVisibility(View.GONE);
@ -192,35 +195,14 @@ public class ServerSelectionController extends BaseController {
}
}
serverEntry.requestFocus();
serverEntryTextInputEditText.requestFocus();
if (!TextUtils.isEmpty(getResources().getString(R.string.weblogin_url))) {
serverEntry.setText(getResources().getString(R.string.weblogin_url));
serverEntryTextInputEditText.setText(getResources().getString(R.string.weblogin_url));
checkServerAndProceed();
}
serverEntry.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void onTextChanged(CharSequence charSequence, int i, int i1, int i2) {
}
@Override
public void afterTextChanged(Editable editable) {
if (!textFieldBoxes.isOnError() && !TextUtils.isEmpty(serverEntry.getText())) {
toggleProceedButton(true);
} else {
toggleProceedButton(false);
}
}
});
serverEntry.setOnEditorActionListener((textView, i, keyEvent) -> {
serverEntryTextInputEditText.setOnEditorActionListener((textView, i, keyEvent) -> {
if (i == EditorInfo.IME_ACTION_DONE) {
checkServerAndProceed();
}
@ -229,23 +211,13 @@ public class ServerSelectionController extends BaseController {
});
}
private void toggleProceedButton(boolean show) {
textFieldBoxes.getEndIconImageButton().setEnabled(show);
if (show) {
textFieldBoxes.getEndIconImageButton().setAlpha(1f);
} else {
textFieldBoxes.getEndIconImageButton().setAlpha(0.5f);
}
}
private void checkServerAndProceed() {
dispose();
String url = serverEntry.getText().toString().trim();
String url = serverEntryTextInputEditText.getText().toString().trim();
serverEntry.setEnabled(false);
progressBar.setVisibility(View.VISIBLE);
serverEntryTextInputEditText.setEnabled(false);
showProgressBar();
if (providersTextView.getVisibility() != View.INVISIBLE) {
providersTextView.setVisibility(View.INVISIBLE);
certTextView.setVisibility(View.INVISIBLE);
@ -283,27 +255,21 @@ public class ServerSelectionController extends BaseController {
.pushChangeHandler(new HorizontalChangeHandler())
.popChangeHandler(new HorizontalChangeHandler()));
} else if (!status.isInstalled()) {
textFieldBoxes.setError(String.format(
getResources().getString(R.string.nc_server_not_installed), productName),
true);
toggleProceedButton(false);
setErrorText(String.format(
getResources().getString(R.string.nc_server_not_installed), productName));
} else if (status.isNeedsUpgrade()) {
textFieldBoxes.setError(String.format(getResources().
setErrorText(String.format(getResources().
getString(R.string.nc_server_db_upgrade_needed),
productName), true);
toggleProceedButton(false);
productName));
} else if (status.isMaintenance()) {
textFieldBoxes.setError(String.format(getResources().
setErrorText(String.format(getResources().
getString(R.string.nc_server_maintenance),
productName),
true);
toggleProceedButton(false);
productName));
} else if (!status.getVersion().startsWith("13.")) {
textFieldBoxes.setError(String.format(getResources().
setErrorText(String.format(getResources().
getString(R.string.nc_server_version),
getResources().getString(R.string.nc_app_name)
, productName), true);
toggleProceedButton(false);
getResources().getString(R.string.nc_app_name),
productName));
}
}, throwable -> {
@ -311,27 +277,26 @@ public class ServerSelectionController extends BaseController {
checkServer(queryUrl.replace("https://", "http://"), false);
} else {
if (throwable.getLocalizedMessage() != null) {
textFieldBoxes.setError(throwable.getLocalizedMessage(), true);
setErrorText(throwable.getLocalizedMessage());
} else if (throwable.getCause() instanceof CertificateException) {
textFieldBoxes.setError(getResources().getString(R.string.nc_certificate_error),
false);
setErrorText(getResources().getString(R.string.nc_certificate_error));
} else {
hideProgressBar();
}
if (serverEntry != null) {
serverEntry.setEnabled(true);
if (serverEntryTextInputEditText != null) {
serverEntryTextInputEditText.setEnabled(true);
}
progressBar.setVisibility(View.INVISIBLE);
if (providersTextView.getVisibility() != View.INVISIBLE) {
providersTextView.setVisibility(View.VISIBLE);
certTextView.setVisibility(View.VISIBLE);
}
toggleProceedButton(false);
dispose();
}
}, () -> {
progressBar.setVisibility(View.INVISIBLE);
hideProgressBar();
if (providersTextView.getVisibility() != View.INVISIBLE) {
providersTextView.setVisibility(View.VISIBLE);
certTextView.setVisibility(View.VISIBLE);
@ -340,23 +305,36 @@ public class ServerSelectionController extends BaseController {
});
}
private void setErrorText(String text) {
errorText.setText(text);
errorText.setVisibility(View.VISIBLE);
progressBar.setVisibility(View.GONE);
}
private void showProgressBar() {
errorText.setVisibility(View.GONE);
progressBar.setVisibility(View.VISIBLE);
}
private void hideProgressBar() {
errorText.setVisibility(View.GONE);
progressBar.setVisibility(View.INVISIBLE);
}
@Override
protected void onAttach(@NonNull View view) {
super.onAttach(view);
if (ApplicationWideMessageHolder.getInstance().getMessageType() != null) {
if (ApplicationWideMessageHolder.getInstance().getMessageType()
.equals(ApplicationWideMessageHolder.MessageType.ACCOUNT_SCHEDULED_FOR_DELETION)) {
textFieldBoxes.setError(getResources().getString(R.string.nc_account_scheduled_for_deletion),
false);
setErrorText(getResources().getString(R.string.nc_account_scheduled_for_deletion));
ApplicationWideMessageHolder.getInstance().setMessageType(null);
} else if (ApplicationWideMessageHolder.getInstance().getMessageType()
.equals(ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK)) {
textFieldBoxes.setError(getResources().getString(R.string.nc_settings_no_talk_installed),
false);
setErrorText(getResources().getString(R.string.nc_settings_no_talk_installed));
} else if (ApplicationWideMessageHolder.getInstance().getMessageType()
.equals(ApplicationWideMessageHolder.MessageType.FAILED_TO_IMPORT_ACCOUNT)) {
textFieldBoxes.setError(getResources().getString(R.string.nc_server_failed_to_import_account),
false);
setErrorText(getResources().getString(R.string.nc_server_failed_to_import_account));
}
ApplicationWideMessageHolder.getInstance().setMessageType(null);
}
@ -378,8 +356,7 @@ public class ServerSelectionController extends BaseController {
certTextView.setText(R.string.nc_configure_cert_auth);
}
textFieldBoxes.setError("", true);
toggleProceedButton(true);
hideProgressBar();
});
}
}

View File

@ -0,0 +1,23 @@
<!--
@author Google LLC
Copyright (C) 2018 Google LLC
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="24dp"
android:width="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path android:fillColor="#FFF" android:pathData="M13 14H11V9H13M13 18H11V16H13M1 21H23L12 2L1 21Z" />
</vector>

View File

@ -2,6 +2,8 @@
~ Nextcloud Talk application
~
~ @author Mario Danic
~ @author Andy Scherzinger
~ Copyright (C) 2021 Andy Scherzinger
~ Copyright (C) 2017 Mario Danic
~
~ This program is free software: you can redistribute it and/or modify
@ -18,94 +20,162 @@
~ along with this program. If not, see <http://www.gnu.org/licenses/>.
-->
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:background="@color/colorPrimary"
android:fitsSystemWindows="true">
android:fillViewport="true"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="vertical"
android:padding="@dimen/standard_padding">
<ImageView
android:id="@+id/image_logo"
android:layout_width="96dp"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_marginTop="92dp"
android:layout_marginBottom="36dp"
android:layout_height="96dp"
android:layout_marginBottom="@dimen/standard_margin"
android:contentDescription="@string/nc_app_name"
android:scaleType="fitXY"
app:srcCompat="@drawable/ic_logo" />
<!-- Server selection window should be ignorant of theme colour
thus colors are set statically -->
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:maxWidth="@dimen/default_login_width"
android:minWidth="@dimen/default_login_width"
android:orientation="vertical"
android:padding="@dimen/standard_half_padding">
<com.google.android.material.textfield.TextInputLayout
android:id="@+id/serverEntryTextInputLayout"
style="@style/Widget.App.Login.TextInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/nc_server_url"
android:theme="@style/TextInputLayoutLogin"
app:endIconContentDescription="@string/nc_server_connect"
app:endIconDrawable="@drawable/ic_arrow_forward_white_24px"
app:endIconMode="custom"
app:endIconTint="@color/white">
<com.google.android.material.textfield.TextInputEditText
android:id="@+id/serverEntryTextInputEditText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="top"
android:importantForAutofill="no"
android:inputType="textUri|textNoSuggestions"
android:lines="1"
android:minLines="1"
android:paddingStart="16dp"
android:paddingEnd="55dp"
android:scrollbars="vertical"
android:textColor="@color/white"
android:textColorHint="#80FFFFFF">
<requestFocus />
</com.google.android.material.textfield.TextInputEditText>
</com.google.android.material.textfield.TextInputLayout>
<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="16dp"
android:paddingTop="4dp"
android:paddingEnd="8dp"
android:paddingBottom="2dp"
android:text="@string/nc_server_helper_text"
android:textColor="@color/nc_login_text_color" />
<LinearLayout
android:id="@+id/serverEntryProgressBar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingStart="16dp"
android:paddingEnd="2dp"
android:visibility="invisible"
tools:visibility="visible">
<ProgressBar
android:id="@+id/progress_bar"
android:layout_width="@dimen/small_item_height"
android:layout_height="@dimen/small_item_height"
android:layout_below="@id/text_field_boxes"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginTop="24dp"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:indeterminate="true"
android:indeterminateTint="@android:color/white"
android:indeterminateTintMode="src_in"
android:keepScreenOn="true"
android:visibility="invisible" />
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="end|center_vertical"
android:indeterminateTint="@color/white" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:gravity="center_vertical"
android:lineSpacingMultiplier="1.2"
android:lines="2"
android:paddingStart="5dp"
android:paddingEnd="8dp"
android:text="@string/nc_server_testing_connection"
android:textColor="@color/white" />
</LinearLayout>
<TextView
android:id="@+id/error_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:ellipsize="end"
android:gravity="center_vertical"
android:lineSpacingMultiplier="1.2"
android:lines="2"
android:paddingStart="16dp"
android:paddingEnd="8dp"
android:text="@string/nc_server_unsupported"
android:textColor="@color/white"
android:visibility="invisible"
app:drawableStartCompat="@android:drawable/stat_sys_warning"
tools:visibility="visible" />
<TextView
android:id="@+id/helper_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/progress_bar"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginTop="56dp"
android:layout_marginTop="58dp"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:lineSpacingMultiplier="1.2"
android:lines="2"
android:text="@string/nc_get_from_provider"
android:textAlignment="center"
android:textAllCaps="true"
android:alpha="0.5"
android:textColor="@color/textColorOnPrimaryBackground" />
android:textColor="@color/nc_login_text_color" />
<TextView
android:id="@+id/cert_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/helper_text_view"
android:layout_centerHorizontal="true"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginTop="16dp"
android:layout_marginTop="32dp"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
android:lineSpacingMultiplier="1.2"
android:lines="2"
android:text="@string/nc_configure_cert_auth"
android:textAlignment="center"
android:textAllCaps="true"
android:alpha="0.5"
android:textColor="@color/textColorOnPrimaryBackground" />
android:textColor="@color/nc_login_text_color" />
<studio.carbonylgroup.textfieldboxes.TextFieldBoxes
android:id="@+id/text_field_boxes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/image_logo"
android:layout_marginStart="@dimen/activity_horizontal_margin"
android:layout_marginEnd="@dimen/activity_horizontal_margin"
app:errorColor="@android:color/white"
app:helperText=" "
app:labelText="@string/nc_server_url"
app:panelBackgroundColor="@color/colorPrimary"
app:primaryColor="@android:color/white"
app:secondaryColor="@android:color/white">
</LinearLayout>
<studio.carbonylgroup.textfieldboxes.ExtendedEditText
android:id="@+id/extended_edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textUri"
android:singleLine="true"
android:textColor="@color/fg_inverse" />
</LinearLayout>
</studio.carbonylgroup.textfieldboxes.TextFieldBoxes>
</RelativeLayout>
</ScrollView>

View File

@ -26,6 +26,7 @@
<color name="colorPrimaryDark">#006AA3</color>
<color name="colorAccent">@color/colorPrimary</color>
<color name="textColorOnPrimaryBackground">#ffffff</color> <!-- white/black depending on primary color -->
<color name="nc_login_text_color">#B3FFFFFF</color>
<!-- App bar -->
<color name="appbar">@android:color/white</color>

View File

@ -54,4 +54,5 @@
<dimen name="standard_padding">16dp</dimen>
<dimen name="standard_half_padding">8dp</dimen>
<dimen name="standard_half_margin">8dp</dimen>
<dimen name="default_login_width">400dp</dimen>
</resources>

View File

@ -32,7 +32,12 @@
<string name="nc_settings">Settings</string>
<!-- Server selection -->
<string name="nc_server_url">Server address</string>
<string name="nc_server_connect">Test server connection</string>
<string name="nc_server_helper_text">The link to your %1$s web interface when you open it in the browser.</string>
<string name="nc_server_testing_connection">Testing connection</string>
<string name="nc_server_unsupported">Server does not have supported Talk app installed</string>
<string name="nc_server_url">Server address https://…</string>
<string name="nc_server_not_installed">Please finish your %1$s installation</string>
<string name="nc_server_db_upgrade_needed">Please upgrade your %1$s database</string>
<string name="nc_server_maintenance">Please bring your %1$s out of maintenance</string>

View File

@ -89,4 +89,38 @@
<item name="iconGravity">textStart</item>
<item name="iconPadding">0dp</item>
</style>
<style name="Widget.App.Login.TextInputLayout" parent="Widget.MaterialComponents.TextInputLayout.OutlinedBox">
<item name="colorControlActivated">@color/white</item>
<item name="colorControlHighlight">@color/white</item>
<item name="materialThemeOverlay">@style/ThemeOverlay.App.Login.TextInputLayout</item>
<item name="shapeAppearance">@style/ShapeAppearance.MaterialComponents.SmallComponent</item>
<item name="hintTextColor">@color/white</item>
<item name="helperTextTextColor">@color/white</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/nc_darkRed</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="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>
</resources>