Răsfoiți Sursa

Merge master

Signed-off-by: alperozturk <alper_ozturk@proton.me>
alperozturk 1 an în urmă
părinte
comite
21be7c3ec9

+ 4 - 2
.drone.yml

@@ -32,7 +32,7 @@ services:
     image: ghcr.io/nextcloud/continuous-integration-shallow-server:latest # also change in updateScreenshots.sh
     environment:
       EVAL: true
-      SERVER_VERSION: 'stable25'
+      SERVER_VERSION: 'stable27'
     commands:
       - BRANCH="$SERVER_VERSION" /usr/local/bin/initnc.sh
       - echo 127.0.0.1 server >> /etc/hosts
@@ -171,4 +171,6 @@ name: GIT_TOKEN
 data: XIoa9IYq+xQ+N5iln8dlpWv0jV6ROr7HuE24ioUr4uQ8m8SjyH0yognWYLYLqnbTKrFWlFZiEMQTH/sZiWjRFvV1iL0=
 ---
 kind: signature
-hmac: aec1cabcb7b4f98f1be1d5b7a052a629ce3193e19a3692dd52ada48b6e72a1c9
+hmac: b78dcc477ff74ccbd7877df011090783847f8b5215a994be6597408bd735b524
+
+...

+ 0 - 159
app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.java

@@ -1,159 +0,0 @@
-/*
- * Nextcloud Android client application
- *
- * @author Bartosz Przybylski
- * @author Chris Narkiewicz
- * Copyright (C) 2015 Bartosz Przybylski
- * Copyright (C) 2015 ownCloud Inc.
- * Copyright (C) 2016 Nextcloud.
- * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
- * License as published by the Free Software Foundation; either
- * version 3 of the License, or 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.nextcloud.client.onboarding;
-
-import android.os.Bundle;
-import android.view.View;
-import android.widget.Button;
-import android.widget.ImageButton;
-import android.widget.TextView;
-
-import com.google.android.material.button.MaterialButton;
-import com.nextcloud.android.common.ui.theme.utils.ColorRole;
-import com.nextcloud.client.appinfo.AppInfo;
-import com.nextcloud.client.di.Injectable;
-import com.nextcloud.client.preferences.AppPreferences;
-import com.owncloud.android.BuildConfig;
-import com.owncloud.android.R;
-import com.owncloud.android.databinding.WhatsNewActivityBinding;
-import com.owncloud.android.ui.adapter.FeaturesViewAdapter;
-import com.owncloud.android.ui.adapter.FeaturesWebViewAdapter;
-import com.owncloud.android.ui.whatsnew.ProgressIndicator;
-import com.owncloud.android.utils.theme.ViewThemeUtils;
-
-import javax.inject.Inject;
-
-import androidx.fragment.app.FragmentActivity;
-import androidx.viewpager.widget.ViewPager;
-
-/**
- * Activity displaying new features after an update.
- */
-public class WhatsNewActivity extends FragmentActivity implements ViewPager.OnPageChangeListener, Injectable {
-
-    @Inject AppPreferences preferences;
-    @Inject AppInfo appInfo;
-    @Inject OnboardingService onboarding;
-    @Inject ViewThemeUtils.Factory viewThemeUtilsFactory;
-    private ViewThemeUtils viewThemeUtils;
-    
-    private WhatsNewActivityBinding binding;
-
-    @Override
-    protected void onCreate(Bundle savedInstanceState) {
-        super.onCreate(savedInstanceState);
-        binding = WhatsNewActivityBinding.inflate(getLayoutInflater());
-        setContentView(binding.getRoot());
-
-        viewThemeUtils = viewThemeUtilsFactory.withPrimaryAsBackground();
-        viewThemeUtils.platform.themeStatusBar(this, ColorRole.PRIMARY);
-
-        
-        String[] urls = getResources().getStringArray(R.array.whatsnew_urls);
-
-        boolean showWebView = urls.length > 0;
-
-        if (showWebView) {
-            FeaturesWebViewAdapter featuresWebViewAdapter = new FeaturesWebViewAdapter(getSupportFragmentManager(),
-                                                                                       urls);
-            binding.progressIndicator.setNumberOfSteps(featuresWebViewAdapter.getCount());
-            binding.contentPanel.setAdapter(featuresWebViewAdapter);
-        } else {
-            FeaturesViewAdapter featuresViewAdapter = new FeaturesViewAdapter(getSupportFragmentManager(),
-                                                                              onboarding.getWhatsNew());
-            binding.progressIndicator.setNumberOfSteps(featuresViewAdapter.getCount());
-            binding.contentPanel.setAdapter(featuresViewAdapter);
-        }
-
-        binding.contentPanel.addOnPageChangeListener(this);
-
-        viewThemeUtils.platform.colorImageView(binding.forward, ColorRole.ON_PRIMARY);
-
-        binding.forward.setOnClickListener(view -> {
-            if (binding.progressIndicator.hasNextStep()) {
-                binding.contentPanel.setCurrentItem(binding.contentPanel.getCurrentItem() + 1, true);
-                binding.progressIndicator.animateToStep(binding.contentPanel.getCurrentItem() + 1);
-            } else {
-                onFinish();
-                finish();
-            }
-            updateNextButtonIfNeeded();
-        });
-
-        binding.forward.setBackground(null);
-
-        viewThemeUtils.platform.colorTextView(binding.skip, ColorRole.ON_PRIMARY);
-        binding.skip.setOnClickListener(view -> {
-            onFinish();
-            finish();
-        });
-
-        viewThemeUtils.platform.colorTextView(binding.welcomeText, ColorRole.ON_PRIMARY);
-
-        if (showWebView) {
-            binding.welcomeText.setText(R.string.app_name);
-        } else {
-            binding.welcomeText.setText(String.format(getString(R.string.whats_new_title), appInfo.getVersionName()));
-        }
-
-        updateNextButtonIfNeeded();
-    }
-
-    @Override
-    public void onBackPressed() {
-        onFinish();
-        super.onBackPressed();
-    }
-
-    private void updateNextButtonIfNeeded() {
-        if (!binding.progressIndicator.hasNextStep()) {
-            binding.forward.setImageResource(R.drawable.ic_ok);
-            binding.skip.setVisibility(View.INVISIBLE);
-        } else {
-            binding.forward.setImageResource(R.drawable.arrow_right);
-            binding.skip.setVisibility(View.VISIBLE);
-        }
-    }
-
-    private void onFinish() {
-        preferences.setLastSeenVersionCode(BuildConfig.VERSION_CODE);
-    }
-
-    @Override
-    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
-        // unused but to be implemented due to abstract parent
-    }
-
-    @Override
-    public void onPageSelected(int position) {
-        binding.progressIndicator.animateToStep(position + 1);
-        updateNextButtonIfNeeded();
-    }
-
-    @Override
-    public void onPageScrollStateChanged(int state) {
-        // unused but to be implemented due to abstract parent
-    }
-}
-

+ 169 - 0
app/src/main/java/com/nextcloud/client/onboarding/WhatsNewActivity.kt

@@ -0,0 +1,169 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Bartosz Przybylski
+ * @author Chris Narkiewicz
+ * Copyright (C) 2015 Bartosz Przybylski
+ * Copyright (C) 2015 ownCloud Inc.
+ * Copyright (C) 2016 Nextcloud.
+ * Copyright (C) 2019 Chris Narkiewicz <hello@ezaquarii.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or 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.nextcloud.client.onboarding
+
+import android.os.Bundle
+import android.view.View
+import androidx.activity.OnBackPressedCallback
+import androidx.fragment.app.FragmentActivity
+import androidx.viewpager.widget.ViewPager
+import com.nextcloud.android.common.ui.theme.utils.ColorRole
+import com.nextcloud.client.appinfo.AppInfo
+import com.nextcloud.client.di.Injectable
+import com.nextcloud.client.preferences.AppPreferences
+import com.owncloud.android.BuildConfig
+import com.owncloud.android.R
+import com.owncloud.android.databinding.WhatsNewActivityBinding
+import com.owncloud.android.ui.adapter.FeaturesViewAdapter
+import com.owncloud.android.ui.adapter.FeaturesWebViewAdapter
+import com.owncloud.android.utils.theme.ViewThemeUtils
+import javax.inject.Inject
+
+/**
+ * Activity displaying new features after an update.
+ */
+class WhatsNewActivity : FragmentActivity(), ViewPager.OnPageChangeListener, Injectable {
+
+    @JvmField
+    @Inject
+    var preferences: AppPreferences? = null
+
+    @JvmField
+    @Inject
+    var appInfo: AppInfo? = null
+
+    @JvmField
+    @Inject
+    var onboarding: OnboardingService? = null
+
+    @JvmField
+    @Inject
+    var viewThemeUtilsFactory: ViewThemeUtils.Factory? = null
+
+    private var viewThemeUtils: ViewThemeUtils? = null
+
+    private lateinit var binding: WhatsNewActivityBinding
+
+    override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
+        binding = WhatsNewActivityBinding.inflate(layoutInflater)
+        setContentView(binding.root)
+
+        viewThemeUtils = viewThemeUtilsFactory?.withPrimaryAsBackground()
+        viewThemeUtils?.platform?.themeStatusBar(this, ColorRole.PRIMARY)
+
+        val urls = resources.getStringArray(R.array.whatsnew_urls)
+        val showWebView = urls.isNotEmpty()
+
+        setupFeatureViewAdapter(showWebView, urls)
+        binding.contentPanel.addOnPageChangeListener(this)
+        setupForwardImageButton()
+        setupSkipImageButton()
+        setupWelcomeText(showWebView)
+        updateNextButtonIfNeeded()
+        handleOnBackPressed()
+    }
+
+    @Suppress("SpreadOperator")
+    private fun setupFeatureViewAdapter(showWebView: Boolean, urls: Array<String>) {
+        val adapter = if (showWebView) {
+            FeaturesWebViewAdapter(supportFragmentManager, *urls)
+        } else {
+            onboarding?.let {
+                FeaturesViewAdapter(supportFragmentManager, *it.whatsNew)
+            }
+        }
+
+        adapter?.let {
+            binding.progressIndicator.setNumberOfSteps(it.count)
+            binding.contentPanel.adapter = it
+        }
+    }
+
+    private fun setupForwardImageButton() {
+        viewThemeUtils?.platform?.colorImageView(binding.forward, ColorRole.ON_PRIMARY)
+        binding.forward.setOnClickListener {
+            if (binding.progressIndicator.hasNextStep()) {
+                binding.contentPanel.setCurrentItem(binding.contentPanel.currentItem + 1, true)
+                binding.progressIndicator.animateToStep(binding.contentPanel.currentItem + 1)
+            } else {
+                onFinish()
+                finish()
+            }
+            updateNextButtonIfNeeded()
+        }
+        binding.forward.background = null
+    }
+
+    private fun setupSkipImageButton() {
+        viewThemeUtils?.platform?.colorTextView(binding.skip, ColorRole.ON_PRIMARY)
+        binding.skip.setOnClickListener {
+            onFinish()
+            finish()
+        }
+    }
+
+    private fun setupWelcomeText(showWebView: Boolean) {
+        viewThemeUtils?.platform?.colorTextView(binding.welcomeText, ColorRole.ON_PRIMARY)
+        binding.welcomeText.text = if (showWebView) {
+            getString(R.string.app_name)
+        } else {
+            String.format(getString(R.string.whats_new_title), appInfo?.versionName)
+        }
+    }
+
+    private fun handleOnBackPressed() {
+        onBackPressedDispatcher.addCallback(
+            this,
+            object : OnBackPressedCallback(true) {
+                override fun handleOnBackPressed() {
+                    onFinish()
+                    onBackPressedDispatcher.onBackPressed()
+                }
+            }
+        )
+    }
+
+    private fun updateNextButtonIfNeeded() {
+        val hasNextStep = binding.progressIndicator.hasNextStep()
+        binding.forward.setImageResource(if (hasNextStep) R.drawable.arrow_right else R.drawable.ic_ok)
+        binding.skip.visibility = if (hasNextStep) View.VISIBLE else View.INVISIBLE
+    }
+
+    private fun onFinish() {
+        preferences?.lastSeenVersionCode = BuildConfig.VERSION_CODE
+    }
+
+    override fun onPageSelected(position: Int) {
+        binding.progressIndicator.animateToStep(position + 1)
+        updateNextButtonIfNeeded()
+    }
+
+    @Suppress("EmptyFunctionBlock")
+    override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
+
+    @Suppress("EmptyFunctionBlock")
+    override fun onPageScrollStateChanged(state: Int) {}
+}

+ 35 - 46
app/src/main/java/com/owncloud/android/ui/activity/PassCodeActivity.java

@@ -30,7 +30,6 @@ import android.text.TextUtils;
 import android.text.TextWatcher;
 import android.view.KeyEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.Window;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.EditText;
@@ -42,6 +41,7 @@ import com.owncloud.android.R;
 import com.owncloud.android.authentication.PassCodeManager;
 import com.owncloud.android.databinding.PasscodelockBinding;
 import com.owncloud.android.lib.common.utils.Log_OC;
+import com.owncloud.android.ui.components.PassCodeEditText;
 import com.owncloud.android.utils.theme.ViewThemeUtils;
 
 import java.util.Arrays;
@@ -74,7 +74,7 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
     @Inject PassCodeManager passCodeManager;
     @Inject ViewThemeUtils viewThemeUtils;
     private PasscodelockBinding binding;
-    private final EditText[] passCodeEditTexts = new EditText[4];
+    private final PassCodeEditText[] passCodeEditTexts = new PassCodeEditText[4];
     private String[] passCodeDigits = {"", "", "", ""};
     private boolean confirmingPassCode;
     private boolean changed = true; // to control that only one blocks jump
@@ -91,7 +91,6 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
         binding = PasscodelockBinding.inflate(getLayoutInflater());
         setContentView(binding.getRoot());
 
-
         viewThemeUtils.platform.colorTextButtons(binding.cancel);
 
         passCodeEditTexts[0] = binding.txt0;
@@ -105,7 +104,6 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
 
         passCodeEditTexts[0].requestFocus();
 
-
         Window window = getWindow();
         if (window != null) {
             window.setSoftInputMode(android.view.WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
@@ -125,7 +123,7 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
                 passCodeDigits = savedInstanceState.getStringArray(PassCodeActivity.KEY_PASSCODE_DIGITS);
             }
             if (confirmingPassCode) {
-                // the app was in the passcodeconfirmation
+                // the app was in the passcode confirmation
                 requestPassCodeConfirmation();
             } else {
                 // pass code preference has just been activated in SettingsActivity;
@@ -158,12 +156,7 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
     protected void setCancelButtonEnabled(boolean enabled) {
         if (enabled) {
             binding.cancel.setVisibility(View.VISIBLE);
-            binding.cancel.setOnClickListener(new OnClickListener() {
-                @Override
-                public void onClick(View v) {
-                    finish();
-                }
-            });
+            binding.cancel.setOnClickListener(v -> finish());
         } else {
             binding.cancel.setVisibility(View.INVISIBLE);
             binding.cancel.setOnClickListener(null);
@@ -179,20 +172,18 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
      * Binds the appropriate listeners to the input boxes receiving each digit of the pass code.
      */
     protected void setTextListeners() {
-        passCodeEditTexts[0].addTextChangedListener(new PassCodeDigitTextWatcher(0, false));
-        passCodeEditTexts[1].addTextChangedListener(new PassCodeDigitTextWatcher(1, false));
-        passCodeEditTexts[2].addTextChangedListener(new PassCodeDigitTextWatcher(2, false));
-        passCodeEditTexts[3].addTextChangedListener(new PassCodeDigitTextWatcher(3, true));
-
-        setOnKeyListener(1);
-        setOnKeyListener(2);
-        setOnKeyListener(3);
+        for (int i = 0; i < passCodeEditTexts.length; i++) {
+            final PassCodeEditText editText = passCodeEditTexts[i];
+            boolean isLast = (i == 3);
 
-        passCodeEditTexts[1].setOnFocusChangeListener((v, hasFocus) -> onPassCodeEditTextFocusChange(1));
-
-        passCodeEditTexts[2].setOnFocusChangeListener((v, hasFocus) -> onPassCodeEditTextFocusChange(2));
+            editText.addTextChangedListener(new PassCodeDigitTextWatcher(i, isLast));
+            if (i > 0) {
+                setOnKeyListener(i);
+            }
 
-        passCodeEditTexts[3].setOnFocusChangeListener((v, hasFocus) -> onPassCodeEditTextFocusChange(3));
+            int finalIndex = i;
+            editText.setOnFocusChangeListener((v, hasFocus) -> onPassCodeEditTextFocusChange(finalIndex));
+        }
     }
 
     private void onPassCodeEditTextFocusChange(final int passCodeIndex) {
@@ -265,9 +256,7 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
                 savePassCodeAndExit();
 
             } else {
-                showErrorAndRestart(
-                    R.string.pass_code_mismatch, R.string.pass_code_configure_your_pass_code, View.VISIBLE
-                                   );
+                showErrorAndRestart(R.string.pass_code_mismatch, R.string.pass_code_configure_your_pass_code, View.VISIBLE);
             }
         }
     }
@@ -279,13 +268,11 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
                 (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
             inputMethodManager.hideSoftInputFromWindow(
                 focusedView.getWindowToken(),
-                0
-                                                      );
+                0);
         }
     }
 
-    private void showErrorAndRestart(int errorMessage, int headerMessage,
-                                     int explanationVisibility) {
+    private void showErrorAndRestart(int errorMessage, int headerMessage, int explanationVisibility) {
         Arrays.fill(passCodeDigits, null);
         Snackbar.make(findViewById(android.R.id.content), getString(errorMessage), Snackbar.LENGTH_LONG).show();
         binding.header.setText(headerMessage);                          // TODO check if really needed
@@ -312,8 +299,6 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
      * @return 'True' if entered pass code equals to the saved one.
      */
     protected boolean checkPassCode() {
-
-
         String[] savedPassCodeDigits = preferences.getPassCode();
 
         boolean result = true;
@@ -331,11 +316,13 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
     protected boolean confirmPassCode() {
         confirmingPassCode = false;
 
-        boolean result = true;
-        for (int i = 0; i < passCodeEditTexts.length && result; i++) {
-            result = passCodeEditTexts[i].getText().toString().equals(passCodeDigits[i]);
+        for (int i = 0; i < passCodeEditTexts.length; i++) {
+            Editable passCodeText = passCodeEditTexts[i].getText();
+            if (passCodeText == null || !passCodeText.toString().equals(passCodeDigits[i])) {
+                return false;
+            }
         }
-        return result;
+        return true;
     }
 
     /**
@@ -401,7 +388,7 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
                 @Override
                 public void run() {
                     try {
-                        Thread.sleep(delay * 1000);
+                        Thread.sleep(delay * 1000L);
 
                         runOnUiThread(() -> {
                             binding.explanation.setVisibility(View.INVISIBLE);
@@ -418,7 +405,6 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
         }
     }
 
-
     @Override
     public void onSaveInstanceState(@NonNull Bundle outState) {
         super.onSaveInstanceState(outState);
@@ -440,6 +426,7 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
         PassCodeDigitTextWatcher(int index, boolean lastOne) {
             mIndex = index;
             mLastOne = lastOne;
+
             if (mIndex < 0) {
                 throw new IllegalArgumentException(
                     "Invalid index in " + PassCodeDigitTextWatcher.class.getSimpleName() +
@@ -463,12 +450,17 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
         public void afterTextChanged(Editable s) {
             if (s.length() > 0) {
                 if (!confirmingPassCode) {
-                    passCodeDigits[mIndex] = passCodeEditTexts[mIndex].getText().toString();
+                    Editable passCodeText = passCodeEditTexts[mIndex].getText();
+
+                    if (passCodeText != null) {
+                        passCodeDigits[mIndex] = passCodeText.toString();
+                    }
                 }
-                passCodeEditTexts[next()].requestFocus();
 
                 if (mLastOne) {
                     processFullPassCode();
+                } else {
+                    passCodeEditTexts[next()].requestFocus();
                 }
 
             } else {
@@ -477,13 +469,10 @@ public class PassCodeActivity extends AppCompatActivity implements Injectable {
         }
 
         @Override
-        public void beforeTextChanged(CharSequence s, int start, int count, int after) {
-            // nothing to do
-        }
+        public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
 
         @Override
-        public void onTextChanged(CharSequence s, int start, int before, int count) {
-            // nothing to do
-        }
+        public void onTextChanged(CharSequence s, int start, int before, int count) {}
     }
+
 }

+ 52 - 0
app/src/main/java/com/owncloud/android/ui/components/PassCodeEditText.kt

@@ -0,0 +1,52 @@
+/*
+ * Nextcloud Android client application
+ *
+ * @author Alper Ozturk
+ * Copyright (C) 2023 Alper Ozturk
+ * Copyright (C) 2023 Nextcloud GmbH
+ *
+ * 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 <https://www.gnu.org/licenses/>.
+ */
+package com.owncloud.android.ui.components
+
+import android.annotation.SuppressLint
+import android.content.Context
+import android.util.AttributeSet
+import android.view.KeyEvent
+import android.view.MotionEvent
+import android.view.View
+import androidx.appcompat.widget.AppCompatEditText
+
+@SuppressLint("ClickableViewAccessibility")
+class PassCodeEditText(context: Context, attrs: AttributeSet?) : AppCompatEditText(context, attrs) {
+
+    init {
+        disableFocusChangeViaTap()
+    }
+
+    private fun disableFocusChangeViaTap() {
+        setSelectAllOnFocus(false)
+        setTextIsSelectable(false)
+        setOnTouchListener { _: View?, _: MotionEvent? -> true }
+    }
+
+    override fun onKeyPreIme(keyCode: Int, event: KeyEvent): Boolean {
+        val isBackButtonPressed = (event.keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_UP)
+        if (isBackButtonPressed) {
+            // Override default behaviour and prevent dismissing the keyboard
+            return true
+        }
+        return super.dispatchKeyEvent(event)
+    }
+}

+ 5 - 20
app/src/main/res/layout/passcodelock.xml

@@ -69,41 +69,26 @@
                     android:layout_height="wrap_content"
                     android:gravity="center_horizontal">
 
-                    <com.google.android.material.textfield.TextInputEditText
+                    <com.owncloud.android.ui.components.PassCodeEditText
                         android:id="@+id/txt0"
                         style="@style/PassCodeStyle"
-                        android:cursorVisible="false"
-                        android:focusable="true"
-                        android:hint="@string/hidden_character"
-                        android:imeOptions="flagNoExtractUi"
                         android:importantForAutofill="no"
                         tools:text="123">
+                    </com.owncloud.android.ui.components.PassCodeEditText>
 
-                        <requestFocus />
-                    </com.google.android.material.textfield.TextInputEditText>
-
-                    <com.google.android.material.textfield.TextInputEditText
+                    <com.owncloud.android.ui.components.PassCodeEditText
                         android:id="@+id/txt1"
                         style="@style/PassCodeStyle"
-                        android:cursorVisible="false"
-                        android:hint="@string/hidden_character"
-                        android:imeOptions="flagNoExtractUi"
                         android:importantForAutofill="no" />
 
-                    <com.google.android.material.textfield.TextInputEditText
+                    <com.owncloud.android.ui.components.PassCodeEditText
                         android:id="@+id/txt2"
                         style="@style/PassCodeStyle"
-                        android:cursorVisible="false"
-                        android:hint="@string/hidden_character"
-                        android:imeOptions="flagNoExtractUi"
                         android:importantForAutofill="no" />
 
-                    <com.google.android.material.textfield.TextInputEditText
+                    <com.owncloud.android.ui.components.PassCodeEditText
                         android:id="@+id/txt3"
                         style="@style/PassCodeStyle"
-                        android:cursorVisible="false"
-                        android:hint="@string/hidden_character"
-                        android:imeOptions="flagNoExtractUi"
                         android:importantForAutofill="no" />
                 </LinearLayout>
 

+ 23 - 0
app/src/main/res/values-da/strings.xml

@@ -18,6 +18,7 @@
     <string name="actionbar_copy">Kopiér</string>
     <string name="actionbar_mkdir">Ny mappe</string>
     <string name="actionbar_move">Flyt</string>
+    <string name="actionbar_move_or_copy">Flyt eller kopier</string>
     <string name="actionbar_open_with">Åbn med</string>
     <string name="actionbar_search">Søg</string>
     <string name="actionbar_see_details">Detaljer</string>
@@ -204,6 +205,7 @@
     <string name="did_not_check_for_dupes">Checkede ikke for duplikater.</string>
     <string name="digest_algorithm_not_available">Denne fordøgelsesalgorytme er ikke tilgængelig på din telefon.</string>
     <string name="direct_login_failed">Login via direkte link mislykkedes!</string>
+    <string name="direct_login_text">Login med %1$s til %2$s</string>
     <string name="disable_new_media_folder_detection_notifications">Deaktiver</string>
     <string name="dismiss">Afvis</string>
     <string name="dismiss_notification_description">Fjern notifikation</string>
@@ -251,8 +253,11 @@
     <string name="e2e_not_yet_setup">E2E er endnu ikke konfigureret</string>
     <string name="e2e_offline">Ikke muligt uden internetforbindelse</string>
     <string name="ecosystem_apps_display_more">Mere</string>
+    <string name="ecosystem_apps_display_notes">Noter</string>
     <string name="ecosystem_apps_display_talk">Snak</string>
+    <string name="ecosystem_apps_more">Flere Nextcloud apps</string>
     <string name="ecosystem_apps_notes">Nextcloud noter</string>
+    <string name="ecosystem_apps_talk">Nextcloud Snak</string>
     <string name="encrypted">Angiv som krypteret</string>
     <string name="end_to_end_encryption_confirm_button">Indstil kryptering</string>
     <string name="end_to_end_encryption_decrypting">Dekrypterer...</string>
@@ -320,6 +325,7 @@
     <string name="file_delete">Slet</string>
     <string name="file_detail_activity_error">Fejl ved indlæsning af aktiviteter for fil</string>
     <string name="file_details_no_content">Fejl ved indlæsning af detaljer</string>
+    <string name="file_downloader_notification_title_prefix">Downloader \u0020</string>
     <string name="file_icon">Fil</string>
     <string name="file_keep">Behold</string>
     <string name="file_list_empty">Upload indhold eller synkronisér med dine enheder.</string>
@@ -421,6 +427,13 @@
     <string name="image_editor_rotate_ccw">Roter mod uret</string>
     <string name="image_editor_rotate_cw">Roter med uret</string>
     <string name="image_editor_unable_to_edit_image">Kunne ikke redigere billede.</string>
+    <string name="image_preview_filedetails">Fil detaljer</string>
+    <string name="image_preview_image_taking_conditions">Billede optagelsestilstande</string>
+    <string name="image_preview_unit_fnumber">ƒ/%s</string>
+    <string name="image_preview_unit_iso">ISO %s</string>
+    <string name="image_preview_unit_megapixel">%s MP</string>
+    <string name="image_preview_unit_millimetres">%s mm</string>
+    <string name="image_preview_unit_seconds">%s s</string>
     <string name="in_folder">i mappen %1$s</string>
     <string name="instant_upload_existing">Send også eksisterende filer</string>
     <string name="instant_upload_on_charging">Upload kun under opladning</string>
@@ -448,6 +461,7 @@
     <string name="locked_by_app">Låst af app\'en %1$s </string>
     <string name="log_send_mail_subject">%1$s Android-app - logge</string>
     <string name="log_send_no_mail_app">Der er ingen app tilgængelig til at sende log filer. Venligst installér en email klient.</string>
+    <string name="logged_in_as">Logget på som %1$s</string>
     <string name="login">Log ind</string>
     <string name="login_url_helper_text">Linket til dit %1$s web interface når du åbner den i browseren.</string>
     <string name="logs_menu_delete">Slet logs</string>
@@ -500,6 +514,7 @@
     <string name="no_calendar_exists">Ingen kalender eksisterer</string>
     <string name="no_email_app_available">Ingen app tilgængelig til at håndtere mailadresser</string>
     <string name="no_items">Ingen elementer</string>
+    <string name="no_map_app_availble">Ingen App tilgængelig til håndtering af Kort</string>
     <string name="no_mutliple_accounts_allowed">kun en konto tilladt</string>
     <string name="no_pdf_app_available">Ingen App tilgængelig til håndtering af PDF</string>
     <string name="no_send_app">Der er ingen tilgængelig app til at sende de valgte filer</string>
@@ -586,7 +601,9 @@
     <string name="prefs_imprint">Aftryk</string>
     <string name="prefs_instant_behaviour_dialogTitle">Oprindelig fil vil blive...</string>
     <string name="prefs_instant_behaviour_title">Oprindelig fil vil blive...</string>
+    <string name="prefs_instant_upload_path_use_date_subfolders_summary">Gem i undermapper baseret på dato</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">Benyt undermapper</string>
+    <string name="prefs_instant_upload_subfolder_rule_title">Undermappe muligheder</string>
     <string name="prefs_keys_exist">Tilføj end-to-end kryptering for denne klient</string>
     <string name="prefs_license">Licens</string>
     <string name="prefs_lock">App adgangskode</string>
@@ -602,6 +619,8 @@ Enheds legitimationsoplysninger er sat op
     <string name="prefs_recommend">Foreslå til en ven</string>
     <string name="prefs_remove_e2e">Fjern kryptering lokalt</string>
     <string name="prefs_setup_e2e">Opsæt end-to-end kryptering</string>
+    <string name="prefs_show_ecosystem_apps">Vis app skifter</string>
+    <string name="prefs_show_ecosystem_apps_summary">Nextcloud app anbefalinger i navigationspanelet</string>
     <string name="prefs_show_hidden_files">Vis skjulte filer</string>
     <string name="prefs_sourcecode">Hent kildetekst</string>
     <string name="prefs_storage_path">Lagringsmappe for data</string>
@@ -776,6 +795,8 @@ Enheds legitimationsoplysninger er sat op
     <string name="stream_not_possible_headline">Intern streaming ikke mulig</string>
     <string name="stream_not_possible_message">Hent venligst media i stedet, eller brug ekstern app.</string>
     <string name="strict_mode">Streng tilstand: Ingen http forbindelser tilladt!</string>
+    <string name="sub_folder_rule_day">År/Måned/Dag</string>
+    <string name="sub_folder_rule_month">År/Måned</string>
     <string name="sub_folder_rule_year">År</string>
     <string name="subject_shared_with_you">\"%1$s\" er blevet delt med dig</string>
     <string name="subject_user_shared_with_you">%1$s delte \"%2$s\" med dig</string>
@@ -927,7 +948,9 @@ Enheds legitimationsoplysninger er sat op
     <string name="wait_a_moment">Vent et øjeblik...</string>
     <string name="wait_checking_credentials">Undersøger lagrede certificeringer</string>
     <string name="wait_for_tmp_copy_from_private_storage">Kopierer fil fra privat lager.</string>
+    <string name="webview_version_check_alert_dialog_message">Opdater Android System WebView appen for at logge på</string>
     <string name="webview_version_check_alert_dialog_positive_button_title">Opdatér</string>
+    <string name="webview_version_check_alert_dialog_title">Opdater Android System WebView</string>
     <string name="what_s_new_image">Hvad nyt billede</string>
     <string name="whats_new_skip">Spring over</string>
     <string name="whats_new_title">Nyt i %1$s</string>

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

@@ -18,6 +18,7 @@
     <string name="actionbar_copy">Copiar</string>
     <string name="actionbar_mkdir">Nueva carpeta</string>
     <string name="actionbar_move">Mover</string>
+    <string name="actionbar_move_or_copy">Mover o Copiar</string>
     <string name="actionbar_open_with">Abrir con</string>
     <string name="actionbar_search">Buscar</string>
     <string name="actionbar_see_details">Detalles</string>

+ 10 - 4
app/src/main/res/values-ja-rJP/strings.xml

@@ -14,10 +14,11 @@
     <string name="action_send_share">送信/共有</string>
     <string name="action_switch_grid_view">グリッド表示</string>
     <string name="action_switch_list_view">リスト表示</string>
-    <string name="actionbar_calendar_contacts_restore">連絡先とカレンダーを復元する</string>
+    <string name="actionbar_calendar_contacts_restore">連絡先とカレンダーを復元</string>
     <string name="actionbar_copy">コピー</string>
     <string name="actionbar_mkdir">新しいフォルダー</string>
     <string name="actionbar_move">移動</string>
+    <string name="actionbar_move_or_copy">移動またはコピー</string>
     <string name="actionbar_open_with">次で開く</string>
     <string name="actionbar_search">検索</string>
     <string name="actionbar_see_details">詳細</string>
@@ -30,12 +31,12 @@
     <string name="activity_chooser_send_file_title">送信</string>
     <string name="activity_chooser_title">リンク送信…</string>
     <string name="activity_icon">アクティビティ</string>
-    <string name="add_another_public_share_link">別のリンクを作成</string>
+    <string name="add_another_public_share_link">別のリンクを追加</string>
     <string name="add_new_public_share">新規公開共有リンクを追加</string>
     <string name="add_new_secure_file_drop">新しいセキュアなファイルドロップを追加</string>
     <string name="add_to_cloud">%1$s に追加</string>
     <string name="advanced_settings">高度な設定</string>
-    <string name="allow_resharing">再共有を許可</string>
+    <string name="allow_resharing">再共有を許可する</string>
     <string name="app_widget_description">ダッシュボードから一つのウィジェットを表示</string>
     <string name="appbar_search_in">%s の中を検索</string>
     <string name="associated_account_not_found">関連付けられたアカウントが見つかりません!</string>
@@ -45,7 +46,7 @@
     <string name="auth_account_not_the_same">入力されたユーザーはこのアカウントのユーザーと一致しません</string>
     <string name="auth_bad_oc_version_title">認識できないサーバーのバージョンです</string>
     <string name="auth_connection_established">接続が確立しました</string>
-    <string name="auth_fail_get_user_name">サーバーが正しいユーザーIDを返していない場合、管理者に連絡してください。</string>
+    <string name="auth_fail_get_user_name">サーバーが正しいユーザーIDを返していません。管理者に連絡してください。</string>
     <string name="auth_host_url">サーバーアドレス https://…</string>
     <string name="auth_incorrect_address_title">サーバーアドレスの書式が違います</string>
     <string name="auth_incorrect_path_title">サーバーが見つかりません</string>
@@ -550,6 +551,7 @@
     <string name="permission_storage_access">ファイルをダウンロードとアップロードする追加の権限が必要です。</string>
     <string name="picture_set_as_no_app">画像を設定するアプリが見つかりませんでした</string>
     <string name="pin_home">ホームスクリーンにピン留めする</string>
+    <string name="pin_shortcut_label">%1$sを開く</string>
     <string name="placeholder_fileSize">389 KB</string>
     <string name="placeholder_filename">placeholder.txt</string>
     <string name="placeholder_media_time">12:23:45</string>
@@ -591,6 +593,7 @@
     <string name="prefs_instant_behaviour_title">元のファイルになります…</string>
     <string name="prefs_instant_upload_path_use_date_subfolders_summary">日付を基にしたサブフォルダーに保存</string>
     <string name="prefs_instant_upload_path_use_subfolders_title">サブフォルダーを利用</string>
+    <string name="prefs_instant_upload_subfolder_rule_title">サブフォルダーのオプション</string>
     <string name="prefs_keys_exist">このクライアントに End-to-End 暗号化を追加</string>
     <string name="prefs_license">ライセンス</string>
     <string name="prefs_lock">アプリパスコード</string>
@@ -603,6 +606,7 @@
     <string name="prefs_manage_accounts">アカウント管理</string>
     <string name="prefs_recommend">友達にすすめる</string>
     <string name="prefs_setup_e2e">end-to-end 暗号化を設定</string>
+    <string name="prefs_show_ecosystem_apps">アップスイッチャーを表示</string>
     <string name="prefs_show_hidden_files">隠しファイルを表示</string>
     <string name="prefs_sourcecode">ソースコードを入手</string>
     <string name="prefs_storage_path">データ保存フォルダー</string>
@@ -925,7 +929,9 @@
     <string name="wait_a_moment">少々お待ちください…</string>
     <string name="wait_checking_credentials">保存された資格情報をチェック</string>
     <string name="wait_for_tmp_copy_from_private_storage">プライベートストレージからファイルをコピー中</string>
+    <string name="webview_version_check_alert_dialog_message">ログインするにはAndroidシステムのWebViewを更新してください</string>
     <string name="webview_version_check_alert_dialog_positive_button_title">更新</string>
+    <string name="webview_version_check_alert_dialog_title">AndroidシステムのWebViewをアップデート</string>
     <string name="what_s_new_image">新しいイメージとは</string>
     <string name="whats_new_skip">スキップ</string>
     <string name="whats_new_title">%1$sの新機能</string>

+ 5 - 0
app/src/main/res/values-nl/strings.xml

@@ -205,6 +205,7 @@
     <string name="dismiss">Weigeren</string>
     <string name="dismiss_notification_description">Handel melding af</string>
     <string name="dnd">Niet storen</string>
+    <string name="document_scan_export_dialog_pdf">PDF-bestand</string>
     <string name="done">Klaar</string>
     <string name="dontClear">Niet opruimen</string>
     <string name="download_cannot_create_file">Kan geen lokaal bestand aanmaken</string>
@@ -240,6 +241,7 @@
     <string name="ecosystem_apps_display_more">Meer</string>
     <string name="ecosystem_apps_display_notes">Notities</string>
     <string name="ecosystem_apps_display_talk">Talk</string>
+    <string name="ecosystem_apps_more">Meer Nextcloud Apps</string>
     <string name="ecosystem_apps_notes">Nextcloud Notities</string>
     <string name="encrypted">Instellen als versleuteld</string>
     <string name="end_to_end_encryption_confirm_button">Instellen versleuteling</string>
@@ -398,6 +400,7 @@
     <string name="host_your_own_server">Host je eigen server</string>
     <string name="icon_of_dashboard_widget">Pictogram van dashboard widget</string>
     <string name="icon_of_widget_entry">Pictogram van widget vermelding</string>
+    <string name="image_preview_filedetails">Bestandsdetails</string>
     <string name="in_folder">in map %1$s</string>
     <string name="instant_upload_existing">Upload ook bestaande bestanden</string>
     <string name="instant_upload_on_charging">Alleen uploaden bij opladen</string>
@@ -739,6 +742,8 @@
     <string name="stream_not_possible_headline">Intern streamen niet mogelijk</string>
     <string name="stream_not_possible_message">Download media in plaats van gebruik externe app.</string>
     <string name="strict_mode">Strict modus: geen HTTP-verbinding toegestaan!</string>
+    <string name="sub_folder_rule_day">Jaar/Maand/Dag</string>
+    <string name="sub_folder_rule_month">Jaar/Maand</string>
     <string name="sub_folder_rule_year">Jaar</string>
     <string name="subject_shared_with_you">\"%1$s\" is met je gedeeld</string>
     <string name="subject_user_shared_with_you">%1$s deelde \"%2$s\" met jou</string>

+ 9 - 9
app/src/main/res/values-sk-rSK/strings.xml

@@ -335,7 +335,7 @@
     <string name="file_management_permission_text">%1$s potrebuje na nahrávanie súborov oprávnenie na správu súborov. Môžete si vybrať úplný prístup ku všetkým súborom alebo prístup iba na čítanie k fotografiám a videám.</string>
     <string name="file_migration_checking_destination">Kontrolujem umiestnenie…</string>
     <string name="file_migration_cleaning">Čistenie…</string>
-    <string name="file_migration_dialog_title">Aktualizujem adresár dátového úložiska</string>
+    <string name="file_migration_dialog_title">Aktualizujem priečinok dátového úložiska</string>
     <string name="file_migration_directory_already_exists">Dátový priečinok uz existuje. Vyberte jednu z možností:</string>
     <string name="file_migration_failed_dir_already_exists">Nextcloud priečinok už existuje</string>
     <string name="file_migration_failed_not_enough_space">Nedostatok priestoru</string>
@@ -349,7 +349,7 @@
     <string name="file_migration_preparing">Pripravujem migráciu…</string>
     <string name="file_migration_restoring_accounts_configuration">Obnovujem konfiguráciu účtu…</string>
     <string name="file_migration_saving_accounts_configuration">Ukladám konfiguráciu účtov…</string>
-    <string name="file_migration_source_not_readable">Ste si istý že chcete zmeniť adresár úložiska na %1$s?\n\nVšetky údaje musia byť znova stiahnuté.</string>
+    <string name="file_migration_source_not_readable">Ste si istý že chcete zmeniť priečinok úložiska na %1$s?\n\nVšetky údaje musia byť znova stiahnuté.</string>
     <string name="file_migration_source_not_readable_title">Zdrojový priečinok nie je čitateľný!</string>
     <string name="file_migration_updating_index">Aktualizujem zoznam…</string>
     <string name="file_migration_use_data_folder">Použi</string>
@@ -402,7 +402,7 @@
     <string name="icon_for_empty_list">Ikona pre prázdny zoznam</string>
     <string name="icon_of_dashboard_widget">Ikona widgetu na hlavnom paneli</string>
     <string name="icon_of_widget_entry">Ikona položky ovládacieho panelu</string>
-    <string name="in_folder">v adresári %1$s</string>
+    <string name="in_folder">v priečinku %1$s</string>
     <string name="instant_upload_existing">Nahrať aj existujúce súbory</string>
     <string name="instant_upload_on_charging">Nahrať iba počas nabíjania</string>
     <string name="instant_upload_path">/InstantUpload</string>
@@ -581,8 +581,8 @@
     <string name="prefs_setup_e2e">Nastaviť šifrovanie end-to-end</string>
     <string name="prefs_show_hidden_files">Zobraziť skryté súbory</string>
     <string name="prefs_sourcecode">Získajte zdrojový kód</string>
-    <string name="prefs_storage_path">Adresár dátového úložiska</string>
-    <string name="prefs_sycned_folders_summary">Spravovať adresár pre automatické nahrávanie</string>
+    <string name="prefs_storage_path">Priečinok dátového úložiska</string>
+    <string name="prefs_sycned_folders_summary">Spravovať priečinky pre automatické nahrávanie</string>
     <string name="prefs_synced_folders_local_path_title">Lokálny priečinok</string>
     <string name="prefs_synced_folders_remote_path_title">Vzdialený priečinok</string>
     <string name="prefs_theme_title">Motív vzhľadu</string>
@@ -658,7 +658,7 @@
     <string name="share_group_clarification">%1$s (skupina)</string>
     <string name="share_internal_link">Sprístupniť interný odkaz</string>
     <string name="share_internal_link_to_file_text">Interný odkaz na zdieľanie funguje iba pre používateľov s prístupom k tomuto súboru</string>
-    <string name="share_internal_link_to_folder_text">Interný odkaz na zdieľanie funguje iba pre používateľov s prístupom k tomuto adresáru</string>
+    <string name="share_internal_link_to_folder_text">Interný odkaz na zdieľanie funguje iba pre používateľov s prístupom k tomuto priečinku</string>
     <string name="share_known_remote_on_clarification">na %1$s</string>
     <string name="share_link">Sprístupniť odkaz</string>
     <string name="share_link_empty_password">Musíte vložiť heslo</string>
@@ -763,7 +763,7 @@
     <string name="sync_fail_ticker">Synchronizácia neúspešná</string>
     <string name="sync_fail_ticker_unauthorized">Synchronizácia neúspešná, je potrebné sa znovu prihlásiť</string>
     <string name="sync_file_nothing_to_do_msg">Obsah súboru je zosynchronizovaný</string>
-    <string name="sync_folder_failed_content">Synchronizáciu adresára %1$s sa nedarí dokončiť</string>
+    <string name="sync_folder_failed_content">Synchronizáciu priečinka %1$s sa nedarí dokončiť</string>
     <string name="sync_foreign_files_forgotten_explanation">Od verzie 1.3.16 sú súbory nahrané z tohoto zariadenia kopírované do lokálneho priečinka %1$s, aby sa zabránilo strate dát pri synchronizácii jedného súboru s viacerými účtami.\n\nVšetky súbory nahraté predchádzajúcimi verziami aplikácie boli z tohoto dôvodu prekopírované do priečinka %2$s. Bohužiaľ sa objavila chyba zabraňujúca dokončeniu tejto operácie v priebehu synchronizácie účtu. Buď môžete súbor(y) ponechať ako sú a odobrať odkaz z priečinka %3$s, alebo presunúť súbor(y) do priečinka %1$s a zachovať odkaz na %4$s.\n\nNižšie je uvedený lokálny súbor(y) a vzdialený súbor(y) v %5$s, s ktorým je prepojený.</string>
     <string name="sync_foreign_files_forgotten_ticker">Niektoré lokálne súbory boli zabudnuté</string>
     <string name="sync_in_progress">Načítavam najnovšiu verziu súboru.</string>
@@ -906,8 +906,8 @@
     <string name="widgets_not_available">Widgety sú dostupné iba na %1$s 25 alebo vyšší</string>
     <string name="widgets_not_available_title">Nie je k dispozícií</string>
     <string name="write_email">Odoslať email</string>
-    <string name="wrong_storage_path">Adresár pre úložisko dát neexistuje!</string>
-    <string name="wrong_storage_path_desc">Môže to byť spôsobené obnovením zálohy na inom zariadení. Návrat na predvolené hodnoty. Skontrolujte adresár a upravte adresár pre ukladanie údajov.</string>
+    <string name="wrong_storage_path">Priečinok pre úložisko dát neexistuje!</string>
+    <string name="wrong_storage_path_desc">Môže to byť spôsobené obnovením zálohy na inom zariadení. Návrat na predvolené hodnoty. Skontrolujte priečinok a upravte adresár pre ukladanie údajov.</string>
     <plurals name="sync_fail_in_favourites_content">
         <item quantity="one">Nie je možné synchronizovať %1$d súbor (konflikty: %2$d)</item>
         <item quantity="few">Nie je možné synchronizovať %1$d súbory (konflikty: %2$d)</item>

+ 2 - 0
app/src/main/res/values/styles.xml

@@ -324,9 +324,11 @@
     </style>
 
     <style name="PassCodeStyle">
+        <item name="android:hint">@string/hidden_character</item>
         <item name="android:layout_width">50dp</item>
         <item name="android:layout_height">50dp</item>
         <item name="android:gravity">center</item>
+        <item name="android:cursorVisible">false</item>
         <item name="android:layout_margin">10dp</item>
         <item name="android:inputType">numberDecimal</item>
         <item name="android:numeric">decimal</item>