So I have a textField where you should enter your "coded" text and get it translated back to non-coded language by using .replace to remove certain characters. But I can't get it to work.
There is a kids "code-language" where you take a word, like cat and for every consonant you add an "o" and the consonant again. So a "b" would be "bob". With vowels they just stay as they are. Cat would be cocatot.
fun translateBack(view : View) {
val konsonanter = "bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ"
var input = editText.text.toString()
var emptyString = ""
for(i in konsonanter) {
val find_text = i + "o" + i
var conso = i.toString()
textView.text = input.replace(find_text, conso, false)
}
}
Would like for it to remove the two following letters for every consonant (if possible). So if i enter "cocowow" I should get out "cow". Right now I just get back what I enter in the textField...
Use a forEach loop through the chars of input and replace:
konsonanter.forEach { input = input.replace("${it}o${it}", "${it}", false) }
textView.text = input
The issue is that you're setting the text in textView in every loop, and never updating input. So you're essentially only seeing the result of the replace call that happens with the "ZoZ" and "Z" parameters at the end of the loop, with input still being the original string.
Instead, you can keep updating input and then set the text when you're done:
val konsonanter = "bcdfghjklmnpqrstvwxzBCDFGHJKLMNPQRSTVWXZ"
var input = editText.text.toString()
var emptyString = ""
for (i in konsonanter) {
val find_text = i + "o" + i
val conso = i.toString()
input = input.replace(find_text, conso, false)
}
textView.text = input
If you use the replace function with a Regex and a transform function as parameters you can create a really concise completely self-containing extension function:
fun String.translateBack() = with(Regex("([bcdfghjklmnpqrstvwxz])o\\1", RegexOption.IGNORE_CASE)) {
replace(this) { "${it.value.first()}" }
}
Explanation:
The Regex will match all same consonants (no matter the case) around an "o".
To ensure that the consonant before and after the "o" are the same a backreference to the first group was used.
So, this will also work for cases like "coCatot".
Usage:
println("bob".translateBack()) // b
println("cocatot".translateBack()) // cat
println("coCatot".translateBack()) // cat
Related
I have the task to create a password validation that has to consider some things. The only problem I have is that one of the criteria of the password validation is that the password must not contain any sequences, e.g. (12345), (abcdef), (asdfghjk). I have already searched a lot and do not know how to implement this. Can anyone help.
This is how I implemented it.
I also check that there is no sequence in backwards, for example (4321, dcba).
private fun noSequenzes(password: String) : Boolean {
val charRuns = listOf(
'0'..'9',
'a'..'z',
'A'..'Z',
"qwertzuiop".asIterable(),
"asdfghjklöä".asIterable(),
"yxcvbnm".asIterable()
)
var map = emptyMap<Char, MutableSet<Char?>>()
charRuns.forEach { run ->
run.forEach { char ->
val charsToAdd = mutableSetOf(run.elementAtOrNull(run.indexOf(char) + 1))
if (run is CharRange) {
charsToAdd.add(run.elementAtOrNull(run.indexOf(char) - 1))
}
if (map.contains(char)) {
map.get(char)!!.addAll(charsToAdd)
}
else {
map = map.plus(Pair(char, charsToAdd))
}
}
}
var sequenceCounter = 1
var recentChar: Char? = null
password.toCharArray().forEach { c ->
recentChar?.let { rc ->
val isSequence = map.any { me -> me.key == rc && me.value.contains(c) }
if (isSequence) {
sequenceCounter = sequenceCounter + 1
}
else {
sequenceCounter = 1
}
if (sequenceCounter >= 3) {
return false
}
}
recentChar = c
}
return true
}
Since you didn't give much detail into what code you already have and what you're stuck on about the logic, here's a very generalized description of a strategy you could use to do this:
Create a List<Iterable<Char>> that contains all the possible strings of characters that could be considered a range. For example:
val charRuns = listOf(
'0'..'9',
'a'..'z',
'A'..'Z',
"qwertyuiop".asIterable(),
//...
)
Iterate these runs to fill a MutableMap<Char, MutableSet<Char>>, where the keys are any of the characters from the runs, and the values are sets of all the chars that if they appear next in a string should be considered a consecutive sequence.
Iterate the potential password String, using the map to check the subsequent Char of each Char to see if it should be considered part of a sequence. Use a counter variable to count the current size of sequence found so far, and reset it whenever a non-sequence is found. If it ever rises above your threshold for allowable sequence size, reject the password immediately.
I'm trying to write some test cases for my compose functions.
I have an outlined Text field with a maximum value of 16 characters.
So I want to test this feature. Here is the test:
#Test
fun checkMaxTaxCodeLength_16Character() {
val taxCode = composeRule.onNodeWithTag(testTag = AUTHENTICATION_SCREEN_TAX_CODE_EDIT_TEXT)
for (i in 'A'..'Z')
taxCode.performTextInput(i.toString())
taxCode.assertTextEquals("ABCDEFGHIJKLMNOP")
}
But although I can see the input is correct, the test fails, and it seems assertTextEquals doesn't work correctly. So:
first of all, what am I doing wrong?
Second, is there any way to, instead of checking the equality, check the text does not contain specific characters?
here is the code of text field:
OutlinedTextField(
value = state.taxCode,
maxLines = 1,
onValueChange = { string ->
viewModel.onEvent(
AuthenticationEvent.TaxCodeChanged(string)
)
},
label = {
Text(text = stringResource(id = R.string.tax_code))
},
modifier = Modifier
.fillMaxWidth()
.testTag(TestingConstant.AUTHENTICATION_SCREEN_TAX_CODE_EDIT_TEXT)
)
The maximum length is handled in the view model. If the user adds more characters than 16, the view model won't update the state and keep the old value.
first of all, what am I doing wrong?
assertTextEquals() takes the value of Text and EditableText in your semantics node combines them and then does a check against the values you pass in. The order does not matter, just make sure to pass in the value of the Text as one of the arguments.
val mNode = composeTestRule.onNodeWithText("Email"))
mNode.performTextInput("test#mail.com")
mNode.assertTextEquals("Email", "test#mail.com")
Please note the text Email is the label for the textfield composable.
To get the semantic information about your nodes you can have
#Test
fun print_semantics_tree() {
composeTestRule.onRoot(useUnmergedTree = true).printToLog(TAG)
}
For the TAG you can use any string. After running the above test you can search the logcat with the specified TAG. You should see something like
|-Node #3 at (l=155.0, t=105.0, r=925.0, b=259.0)px
| Focused = 'false'
| ImeAction = 'Default'
| EditableText = 'test#mail.com'
| TextSelectionRange = 'TextRange(0, 0)'
| Text = '[Email]'
| Actions = [RequestFocus, GetTextLayoutResult, SetText, SetSelection,
OnClick, OnLongClick, PasteText]
Please note you can also obtain the semantics node object with an index operation rather than iterating through all the values.
val value = fetchSemanticsNode().config[EditableText]
assertEquals("test#mail.com", value.toString())
Ok, still, the problem is open, but I achieved what I wanted another way. I used semantic nodes to get what is in edit text and compared it with what it should be:
#Test
fun checkMaxTaxCodeLength_16Character() {
val taxCode = composeRule.onNodeWithTag(testTag = AUTHENTICATION_SCREEN_TAX_CODE_EDIT_TEXT)
for (i in 'A'..'Z')
taxCode.performTextInput(i.toString())
for ((key,value) in taxCode.fetchSemanticsNode().config)
if (key.name =="EditableText")
assertEquals("ABCDEFGHIJKLMNOP",value.toString())
}
I'm a beginner in Android software development. I'm making a calculator app to test my skills. In a calculator there are 0-9 numeric and some others operators like +, -, *, / etc.
In the time of Equal functionality, I have to make sure that the last string of the TextView has any number (0-9), not any operators.
My equal fun is like that:
fun onEqual(view: View) {
if (tv_input.text.contains("0-9")) {
Toast.makeText(this, "Last string is a number, not operator", Toast.LENGTH_SHORT).show()
}
}
You need to use Kotlin regular expression matches
val lastString = "573" // input
val pattern = "-?\\d+(\\.\\d+)?".toRegex()
/**
-? allows zero or more - for negative numbers in the string.
\\d+ checks the string must have at least 1 or more numbers (\\d).
(\\.\\d+)? allows zero or more of the given pattern (\\.\\d+)
In which \\. checks if the string contains . (decimal points) or not
If yes, it should be followed by at least one or more number \\d+.
**/
val isLastString = pattern.matches(lastString)
https://kotlinlang.org/api/latest/jvm/stdlib/kotlin.text/ends-with.html
fun String.endsWith(
suffix: String,
ignoreCase: Boolean = false
): Boolean
Returns true if this string ends with the specified suffix.
Thanks everyone.
I did my own solution.
Here is the code I've wrote:
fun onEqual(view: View) {
if (!tv_input.text.toString().equals("")) {
val last = tv_input.text.toString()
val lastNumber: String = last.get(last.length - 1).toString()
Toast.makeText(this, lastNumber, Toast.LENGTH_SHORT).show()
}else {
return
}
}
I am trying to implement a TextField which inputs an amount and formats it as soon as it is typed and also limits it to 100,000.
#Composable
fun MainScreen(
viewModel: MyViewModel
) {
val uiState by viewModel.uiState.collectAsState()
Column {
AmountSection(
uiState.amount,
viewModel::updateAmount
)
Text(text = viewModel.logs)
}
}
#Composable
fun AmountSection(
amount: TextFieldValue,
updateAmount: (TextFieldValue) -> Unit
) {
BasicTextField(
value = amount,
onValueChange = updateAmount,
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number
)
)
MyViewModel:
class MyViewModel: ViewModel() {
private val _uiState = MutableStateFlow(MyUiState())
val uiState: StateFlow<MyUiState> = _uiState
var logs by mutableStateOf("")
var text = ""
fun updateAmount(amount: TextFieldValue) {
val formattedAmount: String = amount.text.getFormattedAmount()
text += "input = ${amount.text}\n"
text += "output = $formattedAmount \n"
logs = text
_uiState.update {
it.copy(amount = TextFieldValue(formattedAmount, TextRange(formattedAmount.length))
}
}
}
data class MyUiState(val amount: TextFieldValue = TextFieldValue())
(logs and text are just for logging purpose. Was finding it difficult to share the logcat output so presented it this way)
Result:
When I press 6, the input is "12,3456" which is expected (ignore the currency)
My getFormattedAmount() function removes the last six as ( 123456 > 100000). It outputs "12,345" which is also correct. "12,345" is what gets displayed on the screen.
But when I press 7, I get the input "12,34567". Where did that 6 come from?? It was not in uiState.amount.
(Please ignore the last output line. getFormattedAmount only removes the last character if the amount exceeds the limit and it gave wrong output because it didn't expect that input)
I feel that I making some really silly mistake here and would be really thankful if somecome could help me find that out.
Edit based on the edit:-
From the looks of the question, it isn't much clear what you wish to achieve here, but this is my deduction -
You just want a TextField that allows numbers to be input, but only up to a maximum value (VALUE, not characters). When a digit-press by a user leads to the value exceeding your max value, you want that digit to be not entered of course, but you wish to reflect no changes at all in this case, i.e., the field value should remain intact.
Based on the above deduction, here is an example:-
First of all, f your uiState variable. I'm keeping it simple for the sake of clarity.
class VM: ViewModel(){
var fieldValue by mutableStateOf("")
fun onFieldUpdate(newValue){
if(newValue.toDouble() > 999999999999999)
return
else
fieldValue = newValue
}
}
#Composable
fun CrazyField(fieldValue: String, onFieldUpdate: (String) -> Unit){
TextField(value = fieldValue, onValueChange = onFieldUpdate)
}
Do not comment further without actually running this.
Original answer:-
Use a doubles parser.
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = {
if (it.toDouble() <= 100000)
text = it //Based on your use-case - it won't cut off text or limit the amount of characters
else text = it.subString(0,7) //In-case of pasting or fast typing
},
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number)
)
Found this comment on Compose slack channel by Sean.
As a model, you should assume that the keyboard may make aribitrary and large
edits each onValueChange. This may happen, for example, if the user uses
autocorrect, replaces a word with an emoji, or other smart editing features. To
correctly handle this, write any transformation logic with the assumption that
the current text passed to onValueChange is unrelated to the previous or next
values that will be passed to onValueChange.
So this is some issue with TextField & IME relationship.
I rewrote my getFormattedAmount function to format the given string without any assumptions (earlier it was assuming that amount is formatted till the last second character). Everything seems fixed now.
I try to get the text from table in a roomDatabase, I access it by looping, and I use a foreach loop to access each item in the database
it manage work if I access the stringbuilder object inside the loop
var text = StringBuilder()
db.adblogDao().getADBlog().forEach {
it.forEach {
text.append("${it.log}")
text.append('\n')
// This work, why??
Timber.d("SUNLIGHT: ${text}")
}
}
//text is always empty, why???
Timber.d("MOONLIGHT: ${text}")
I want to access the string outside of the loop, but it always return null, why?
Which one of MOONLIGHT and SUNLIGHT are logged first? When forEach is called by a worker thread, MOONLIGHT may be performed first, and then forEach may be performed.
my code:
class MyLog(val log: String)
val log1 = arrayOf(MyLog("1"), MyLog("2"), MyLog("3"), MyLog("4"), MyLog("5"))
val log2 = arrayOf(MyLog("10"), MyLog("20"), MyLog("30"), MyLog("40"), MyLog("50"))
val log3 = arrayOf(MyLog("100"), MyLog("200"), MyLog("300"), MyLog("400"), MyLog("500"))
val list = arrayOf(log1, log2, log3)
val text = StringBuilder()
list.forEach {
it.forEach {
text.append("${it.log}")
text.append('\n')
Log.d(TAG,"SUNLIGHT: ${text}")
}
}
Log.d(TAG,"MOONLIGHT: ${text}")