diff --git a/app/build.gradle b/app/build.gradle index 30e53666c..6fa6fcaca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -112,7 +112,7 @@ dependencies { debugImplementation "javax.transaction:transaction-api:1.1-rev-1" - implementation 'com.github.HITGIF:TextFieldBoxes:1.3.4' + implementation 'com.github.HITGIF:TextFieldBoxes:1.3.7' implementation 'eu.davidea:flexible-adapter:5.0.0-rc3' diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dad231963..9b7f16ef8 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -16,6 +16,7 @@ + - diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java index 8e90df0eb..6de939146 100644 --- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java +++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.java @@ -20,9 +20,13 @@ */ package com.nextcloud.talk.application; +import android.annotation.SuppressLint; import android.content.Context; +import android.os.Build; import android.support.multidex.MultiDex; import android.support.multidex.MultiDexApplication; +import android.support.v7.widget.AppCompatDrawableManager; +import android.util.Log; import com.evernote.android.job.JobManager; import com.evernote.android.job.JobRequest; @@ -39,6 +43,9 @@ import com.nextcloud.talk.utils.database.user.UserModule; import com.squareup.leakcanary.LeakCanary; import com.squareup.leakcanary.RefWatcher; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.security.GeneralSecurityException; import javax.inject.Singleton; @@ -76,6 +83,32 @@ public class NextcloudTalkApplication extends MultiDexApplication { } //endregion + //region private methods + // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected + private void useCompatVectorIfNeeded() { + int sdkInt = Build.VERSION.SDK_INT; + if (sdkInt == 21 || sdkInt == 22) { + try { + @SuppressLint("RestrictedApi") AppCompatDrawableManager drawableManager = AppCompatDrawableManager.get(); + Class inflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$InflateDelegate"); + Class vdcInflateDelegateClass = Class.forName("android.support.v7.widget.AppCompatDrawableManager$VdcInflateDelegate"); + + Constructor constructor = vdcInflateDelegateClass.getDeclaredConstructor(); + constructor.setAccessible(true); + Object vdcInflateDelegate = constructor.newInstance(); + + Class args[] = {String.class, inflateDelegateClass}; + Method addDelegate = AppCompatDrawableManager.class.getDeclaredMethod("addDelegate", args); + addDelegate.setAccessible(true); + addDelegate.invoke(drawableManager, "vector", vdcInflateDelegate); + } catch (ClassNotFoundException | NoSuchMethodException | InstantiationException | + InvocationTargetException | IllegalAccessException e) { + Log.d(TAG, "Failed to use reflection to enable proper vector scaling"); + } + } + } + //endregion + //region Overridden methods @Override public void onCreate() { @@ -84,6 +117,8 @@ public class NextcloudTalkApplication extends MultiDexApplication { FirebaseAnalytics.getInstance(this).setAnalyticsCollectionEnabled(false); sharedApplication = this; + useCompatVectorIfNeeded(); + try { buildComponent(); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java index 83924caac..372d18ba8 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/ServerSelectionController.java @@ -22,6 +22,9 @@ package com.nextcloud.talk.controllers; import android.content.pm.ActivityInfo; import android.support.annotation.NonNull; +import android.text.Editable; +import android.text.TextUtils; +import android.text.TextWatcher; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; @@ -84,91 +87,130 @@ public class ServerSelectionController extends BaseController { getActionBar().hide(); } - textFieldBoxes.setLabelText(getResources().getString(R.string.nc_app_name) + " " + getResources().getString(R.string.nc_appended_server_url)); + textFieldBoxes.setLabelText(getResources().getString(R.string.nc_server_url)); + 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()); serverEntry.requestFocus(); + 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())) { + textFieldBoxes.getEndIconImageButton().setEnabled(true); + textFieldBoxes.getEndIconImageButton().setAlpha(1f); + + } else { + textFieldBoxes.getEndIconImageButton().setEnabled(false); + textFieldBoxes.getEndIconImageButton().setAlpha(0.5f); + } + } + }); + serverEntry.setOnEditorActionListener((textView, i, keyEvent) -> { if (i == EditorInfo.IME_ACTION_DONE) { - dispose(); - - String url = serverEntry.getText().toString().trim(); - - if (url.startsWith("http://") || url.startsWith("https://")) { - serverEntry.setEnabled(false); - progressBar.setVisibility(View.VISIBLE); - - if (url.endsWith("/")) { - url = url.substring(0, url.length() - 1); - } - - String queryUrl = url + ApiHelper.getUrlPostfixForStatus(); - final String finalServerUrl = url; - - statusQueryDisposable = ncApi.getServerStatus(queryUrl) - .subscribeOn(Schedulers.newThread()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe(status -> { - String productName = getResources().getString(R.string.nc_server_product_name); - - if (status.isInstalled() && !status.isMaintenance() && - !status.isNeedsUpgrade() && - status.getVersion().startsWith("13.")) { - - getRouter().pushController(RouterTransaction.with( - new WebViewLoginController(finalServerUrl, false)) - .pushChangeHandler(new HorizontalChangeHandler()) - .popChangeHandler(new HorizontalChangeHandler())); - } else if (!status.isInstalled()) { - textFieldBoxes.setError(String.format( - getResources().getString(R.string.nc_server_not_installed), productName), - true); - } else if (status.isNeedsUpgrade()) { - textFieldBoxes.setError(String.format(getResources(). - getString(R.string.nc_server_db_upgrade_needed), - productName), true); - } else if (status.isMaintenance()) { - textFieldBoxes.setError(String.format(getResources(). - getString(R.string.nc_server_maintenance), - productName), - true); - } else if (!status.getVersion().startsWith("13.")) { - textFieldBoxes.setError(String.format(getResources(). - getString(R.string.nc_server_version), - getResources().getString(R.string.nc_app_name) - , productName), true); - } - - }, throwable -> { - if (throwable.getLocalizedMessage() != null) { - textFieldBoxes.setError(throwable.getLocalizedMessage(), true); - } else if (throwable.getCause() instanceof CertificateException) { - textFieldBoxes.setError(getResources().getString(R.string.nc_certificate_error), - true); - } - if (serverEntry != null) { - serverEntry.setEnabled(true); - } - progressBar.setVisibility(View.GONE); - - dispose(); - - }, () -> { - progressBar.setVisibility(View.GONE); - dispose(); - }); - } else { - textFieldBoxes.setError(getResources().getString(R.string.nc_server_url_prefix), true); - serverEntry.setEnabled(true); - return true; - } - + checkServerAndProceed(); } return false; }); } + private void checkServerAndProceed() { + dispose(); + + String url = serverEntry.getText().toString().trim(); + + serverEntry.setEnabled(false); + progressBar.setVisibility(View.VISIBLE); + + if (url.endsWith("/")) { + url = url.substring(0, url.length() - 1); + } + + String queryUrl = url + ApiHelper.getUrlPostfixForStatus(); + + if (url.startsWith("http://") || url.startsWith("https://")) { + checkServer(queryUrl, false); + } else { + checkServer("https://" + queryUrl, true); + } + } + + private void checkServer(String queryUrl, boolean checkForcedHttps) { + statusQueryDisposable = ncApi.getServerStatus(queryUrl) + .subscribeOn(Schedulers.newThread()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe(status -> { + String productName = getResources().getString(R.string.nc_server_product_name); + + if (status.isInstalled() && !status.isMaintenance() && + !status.isNeedsUpgrade() && + status.getVersion().startsWith("13.")) { + + getRouter().pushController(RouterTransaction.with( + new WebViewLoginController(queryUrl.replace("/status.php", ""), + false)) + .pushChangeHandler(new HorizontalChangeHandler()) + .popChangeHandler(new HorizontalChangeHandler())); + } else if (!status.isInstalled()) { + textFieldBoxes.setError(String.format( + getResources().getString(R.string.nc_server_not_installed), productName), + true); + } else if (status.isNeedsUpgrade()) { + textFieldBoxes.setError(String.format(getResources(). + getString(R.string.nc_server_db_upgrade_needed), + productName), true); + } else if (status.isMaintenance()) { + textFieldBoxes.setError(String.format(getResources(). + getString(R.string.nc_server_maintenance), + productName), + true); + } else if (!status.getVersion().startsWith("13.")) { + textFieldBoxes.setError(String.format(getResources(). + getString(R.string.nc_server_version), + getResources().getString(R.string.nc_app_name) + , productName), true); + } + + }, throwable -> { + if (checkForcedHttps && (throwable instanceof Exception)) { + checkServer(queryUrl.replace("https://", "http://"), false); + } else { + if (throwable.getLocalizedMessage() != null) { + textFieldBoxes.setError(throwable.getLocalizedMessage(), true); + } else if (throwable.getCause() instanceof CertificateException) { + textFieldBoxes.setError(getResources().getString(R.string.nc_certificate_error), + true); + } + + if (serverEntry != null) { + serverEntry.setEnabled(true); + } + progressBar.setVisibility(View.GONE); + + dispose(); + } + }, () -> { + progressBar.setVisibility(View.GONE); + dispose(); + }); + } + @Override protected void onAttach(@NonNull View view) { super.onAttach(view); diff --git a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java b/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java index 2cb46ccb9..848186995 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/WebViewLoginController.java @@ -245,7 +245,7 @@ public class WebViewLoginController extends BaseController { // We use the URL user entered because one provided by the server is NOT reliable ErrorMessageHolder.ErrorMessageType finalErrorMessageType = errorMessageType; userQueryDisposable = userUtils.createOrUpdateUser(loginData.getUsername(), loginData.getToken(), - baseUrl, null, null, true). + loginData.getServerUrl(), null, null, true). subscribe(userEntity -> { cookieManager.getCookieStore().removeAll(); if (!isPasswordUpdate && finalErrorMessageType == null) { diff --git a/app/src/main/java/com/nextcloud/talk/utils/ColorUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ColorUtils.java index 2223ae9c3..7e4ab4580 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ColorUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ColorUtils.java @@ -20,6 +20,19 @@ package com.nextcloud.talk.utils; +import android.content.res.Resources; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorRes; +import android.support.annotation.DrawableRes; + public class ColorUtils { public static String colorSeed = "ballast butte permute doxy graham rummage grateful songbook pledge escapade"; + + public static Drawable getTintedDrawable(Resources res, @DrawableRes int drawableResId, @ColorRes int colorResId) { + Drawable drawable = res.getDrawable(drawableResId); + int color = res.getColor(colorResId); + drawable.setColorFilter(color, PorterDuff.Mode.SRC_IN); + return drawable; + } } diff --git a/app/src/main/res/drawable/ic_arrow_forward_white_24px.xml b/app/src/main/res/drawable/ic_arrow_forward_white_24px.xml new file mode 100644 index 000000000..983307760 --- /dev/null +++ b/app/src/main/res/drawable/ic_arrow_forward_white_24px.xml @@ -0,0 +1,25 @@ + + + + + diff --git a/app/src/main/res/layout/controller_server_selection.xml b/app/src/main/res/layout/controller_server_selection.xml index 96636ec66..e197d89d7 100644 --- a/app/src/main/res/layout/controller_server_selection.xml +++ b/app/src/main/res/layout/controller_server_selection.xml @@ -23,7 +23,8 @@ xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" - android:background="@color/colorPrimary"> + android:background="@color/colorPrimary" + android:fitsSystemWindows="true"> + app:panelBackgroundColor="@color/colorPrimary" + app:primaryColor="@color/nc_white_color_complete"> @@ -65,14 +68,15 @@ android:layout_width="@dimen/small_item_height" android:layout_height="@dimen/small_item_height" android:layout_below="@id/text_field_boxes" - android:layout_centerInParent="true" + android:layout_centerHorizontal="true" android:layout_marginEnd="@dimen/activity_horizontal_margin" android:layout_marginLeft="@dimen/activity_horizontal_margin" android:layout_marginRight="@dimen/activity_horizontal_margin" android:layout_marginStart="@dimen/activity_horizontal_margin" - android:layout_marginTop="@dimen/padding_between_elements" + android:layout_marginTop="24dp" android:indeterminate="true" - android:progressTint="@color/nc_white_color" + android:progressDrawable="@color/nc_white_color_complete" + android:progressTint="@color/nc_white_color_complete" android:visibility="invisible"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b55312161..811188776 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -5,7 +5,7 @@ Settings - server address + Server address Please enter http:// or https:// before the hostname Please finish your %1$s installation Please upgrade your %1$s database