Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ class PassCodeActivity :
companion object {
private val TAG = PassCodeActivity::class.java.simpleName

private const val ONE_SECOND_MS = 1000L
private const val KEY_PASSCODE_DIGITS = "PASSCODE_DIGITS"
private const val KEY_CONFIRMING_PASSCODE = "CONFIRMING_PASSCODE"
const val ACTION_REQUEST_WITH_RESULT = "ACTION_REQUEST_WITH_RESULT"
Expand Down Expand Up @@ -79,6 +80,8 @@ class PassCodeActivity :
private var changed = true // to control that only one blocks jump
private var delayInSeconds = 0

private val delayFormatter by lazy { PassCodeDelayFormatter(resources) }

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
delayInSeconds = preferences.passCodeDelay
Expand Down Expand Up @@ -369,45 +372,6 @@ class PassCodeActivity :
}
}

@Suppress("MagicNumber")
private fun getExplanationText(timeInSecond: Int): String = when {
timeInSecond < 60 -> resources.getQuantityString(
R.plurals.delay_message,
timeInSecond,
timeInSecond
)

else -> {
val minutes = timeInSecond / 60
val remainingSeconds = timeInSecond % 60

when {
remainingSeconds == 0 -> resources.getQuantityString(
R.plurals.delay_message_minutes,
minutes,
minutes
)

else -> {
val minuteText = resources.getQuantityString(
R.plurals.delay_message_minutes_part,
minutes,
minutes
)
val secondText = resources.getQuantityString(
R.plurals.delay_message_seconds_part,
remainingSeconds,
remainingSeconds
)

val prefixText = "$minuteText $secondText"
getString(R.string.due_to_too_many_wrong_attempts, prefixText)
}
}
}
}

@Suppress("MagicNumber")
private fun showDelay() {
val pinBruteForceCount = preferences.pinBruteForceDelay()
if (pinBruteForceCount <= 0) {
Expand All @@ -419,8 +383,8 @@ class PassCodeActivity :
var counter = delayInSeconds
lifecycleScope.launch(Dispatchers.Main) {
while (counter != 0) {
binding.explanation.text = getExplanationText(counter)
delay(1000)
binding.explanation.text = delayFormatter.getExplanationText(counter)
delay(ONE_SECOND_MS)
counter -= 1
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.ui.activity

import android.content.res.Resources
import com.owncloud.android.R

class PassCodeDelayFormatter(private val resources: Resources) {

fun getExplanationText(timeInSeconds: Int): String {
val minutes = timeInSeconds / SECONDS_PER_MINUTE
val remainingSeconds = timeInSeconds % SECONDS_PER_MINUTE

return when {
timeInSeconds < SECONDS_PER_MINUTE -> resources.getQuantityString(
R.plurals.passcode_delay_seconds,
timeInSeconds,
timeInSeconds
)

remainingSeconds == 0 -> resources.getQuantityString(
R.plurals.passcode_delay_minutes,
minutes,
minutes
)

else -> resources.getQuantityString(
R.plurals.passcode_delay_minutes_seconds,
minutes,
minutes,
remainingSeconds
)
}
}

companion object {
const val SECONDS_PER_MINUTE = 60
}
}
25 changes: 9 additions & 16 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1276,28 +1276,21 @@

<string name="login_url_helper_text">The link to your %1$s web interface when you open it in the browser.</string>

<plurals name="delay_message">
<item quantity="one">Delayed %d second due to too many wrong attempts</item>
<item quantity="other">Delayed %d seconds due to too many wrong attempts</item>
<plurals name="passcode_delay_seconds">
<item quantity="one">Try again in %d second due to too many wrong attempts</item>
<item quantity="other">Try again in %d seconds due to too many wrong attempts</item>
</plurals>

<plurals name="delay_message_minutes">
<item quantity="one">Delayed %d minute due to too many wrong attempts</item>
<item quantity="other">Delayed %d minutes due to too many wrong attempts</item>
<plurals name="passcode_delay_minutes">
<item quantity="one">Try again in %d minute due to too many wrong attempts</item>
<item quantity="other">Try again in %d minutes due to too many wrong attempts</item>
</plurals>

<plurals name="delay_message_minutes_part">
<item quantity="one">Delayed %d minute</item>
<item quantity="other">Delayed %d minutes</item>
<plurals name="passcode_delay_minutes_seconds">
<item quantity="one">Try again in %1$d minute and %2$d seconds due to too many wrong attempts</item>
<item quantity="other">Try again in %1$d minutes and %2$d seconds due to too many wrong attempts</item>
</plurals>

<plurals name="delay_message_seconds_part">
<item quantity="one">%d second</item>
<item quantity="other">%d seconds</item>
</plurals>

<string name="due_to_too_many_wrong_attempts">%s due to too many wrong attempts</string>


<string name="create">Create</string>
<string name="select_one_template">Please select one template</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
/*
* Nextcloud - Android Client
*
* SPDX-FileCopyrightText: 2026 Alper Ozturk <alper.ozturk@nextcloud.com>
* SPDX-License-Identifier: AGPL-3.0-or-later
*/
package com.owncloud.android.ui.activity

import android.content.res.Resources
import com.owncloud.android.R
import io.mockk.every
import io.mockk.mockk
import org.junit.Assert.assertEquals
import org.junit.Before
import org.junit.Test

class PassCodeDelayFormatterTest {

private lateinit var resources: Resources
private lateinit var sut: PassCodeDelayFormatter

@Before
fun setUp() {
resources = mockk()
sut = PassCodeDelayFormatter(resources)
}

@Test
fun testGetExplanationTextWhenGivenOneSecondShouldReturnSingularSecondsString() {
val expected = "Try again in 1 second due to too many wrong attempts"
every { resources.getQuantityString(R.plurals.passcode_delay_seconds, 1, 1) } returns expected

val result = sut.getExplanationText(1)

assertEquals(expected, result)
}

@Test
fun testGetExplanationTextWhenGivenFiftyNineSecondsShouldReturnPluralSecondsString() {
val expected = "Try again in 59 seconds due to too many wrong attempts"
every { resources.getQuantityString(R.plurals.passcode_delay_seconds, 59, 59) } returns expected

val result = sut.getExplanationText(59)

assertEquals(expected, result)
}

@Test
fun testGetExplanationTextWhenGivenSixtySecondsShouldReturnSingularMinutesString() {
val expected = "Try again in 1 minute due to too many wrong attempts"
every { resources.getQuantityString(R.plurals.passcode_delay_minutes, 1, 1) } returns expected

val result = sut.getExplanationText(60)

assertEquals(expected, result)
}

@Test
fun testGetExplanationTextWhenGivenOneHundredTwentySecondsShouldReturnPluralMinutesString() {
val expected = "Try again in 2 minutes due to too many wrong attempts"
every { resources.getQuantityString(R.plurals.passcode_delay_minutes, 2, 2) } returns expected

val result = sut.getExplanationText(120)

assertEquals(expected, result)
}

@Test
fun testGetExplanationTextWhenGivenNinetySecondsShouldReturnMinutesAndSecondsString() {
val expected = "Try again in 1 minute and 30 seconds due to too many wrong attempts"
every { resources.getQuantityString(R.plurals.passcode_delay_minutes_seconds, 1, 1, 30) } returns expected

val result = sut.getExplanationText(90)

assertEquals(expected, result)
}

@Test
fun testGetExplanationTextWhenGivenOneHundredFortyFiveSecondsShouldReturnMinutesAndSecondsString() {
val expected = "Try again in 2 minutes and 25 seconds due to too many wrong attempts"
every { resources.getQuantityString(R.plurals.passcode_delay_minutes_seconds, 2, 2, 25) } returns expected

val result = sut.getExplanationText(145)

assertEquals(expected, result)
}

@Test
fun testGetExplanationTextWhenGivenThreeHundredSecondsShouldReturnFiveMinutesString() {
val expected = "Try again in 5 minutes due to too many wrong attempts"
every { resources.getQuantityString(R.plurals.passcode_delay_minutes, 5, 5) } returns expected

val result = sut.getExplanationText(300)

assertEquals(expected, result)
}
}
Loading