How to check user name and last name length in one textfield in android jetpack compose? - kotlin

There is an outlinedtextfield where the user enters his/her name and surname, and this outlinetextfield enters the user name and surname, but if the user's name and surname are less than 3 characters, I want to make the border of the outlinetextfield red, but I couldn't figure out how to do this control because space intervened.
for example :
a(space)b wrong
jac(space)jac correct
tony(space)stark correct
this is my example code:
OutlinedTextField(
value = state.name,
onValueChange = { onChangeName(it) },
modifier = Modifier
.fillMaxWidth(),
shape = RoundedCornerShape(25.dp),
label = {
Text(
text = "name and lastname",
fontSize = 14.sp,
color = Color.White
)
},
colors = TextFieldDefaults.outlinedTextFieldColors(
focusedBorderColor = if (state.name.length < 7) Color.Red else DefaultDYTColor,
unfocusedBorderColor = Color.Transparent
)
)
When I do it this way, it accepts it as correct even if I type 7 characters without spaces, but it should not be like this.
for example:
tonystark
According to the code I wrote, this is correct because it is greater than 7 characters
How to achieve this issue ? Do you have any suggestion or solve ?

This is just an algorithmic problem, you can solve it like this:
fun isValidName(name: String): Boolean {
// split name on each space and filter out those that are blank (consecutive spaces)
val splits = name.split(" ").filter { it.isNotBlank() }
// we need at least 2 strings (name and surname)
// you can also use != 2 if you want exactly 2
if (splits.size < 2) return false
// if any name is less than 3 chars, return false
for (split in splits) {
if (split.trim().length < 3) {
return false
}
}
// now we have at least 2 names and all of them have 3 or more chars
return true
}

Related

way to Check null objects in json api array

I'm trying a lot of things but i still can not figure out a way to Check if an object is null.
This is the Api response
Api response
The problem sometimes the object shortname Doesn't exist in the teamInfo
so there will be just "name" and "img" . i want to show some other text if shortname Doesn't exist in teaminfo.
I've tried something like:
Text(
text = if(data.teamInfo[1].shortname != null){data.teamInfo[1].shortname}
else{data.teams[1]},
modifier = Modifier
.weight(1f)
.padding(5.dp)
.align(Alignment.CenterVertically),
fontWeight = FontWeight.Bold,
maxLines = 1
)
Firstly, I see you are hardcoding the array index always to be 1. Is that right for your case?
The relevant part is this
text = if (data.teamInfo[1].shortname != null) {
data.teamInfo[1].shortname
} else {
data.teams[1]
},
Your null check is valid, the fragment of the JSON response you gave doesn't seem to have a teams array so the second part may not work/compile/produce a non-null. You do say each teaminfo will have a name so try this first
text = if (data.teamInfo[1].shortname != null) {
data.teamInfo[1].shortname
} else {
data.teamInfo[1].name
},
And you can simply this with the "Elvis" operator
text = data.teamInfo[1].shortname ?: data.teamInfo[1].name

how to read mutliple lines of string into one variable using readln() in kotlin?

example:
a variable
val str = readln().replace("[^A-Za-z0-9 ] \\s+".toRegex(),"").trim()
should read multiple lines of input value, input value will be like this
heading
----------
topic1
topic2
or like this
heading
-------
a) topic1
b) topic2
input may contain special characters or tabs or spaces we need to remove them also
I don't know what your Regex is trying to do, but that's not really your question.
How do you know when the user has finished their input - a special word or an empty line?
Assuming an empty line, here's how you can get all the content
println("Enter something:")
var lines = ""
do {
val line = readLine()
lines += "${clean(line)}\n"
} while (!line.isNullOrBlank())
println("User input:\n$lines")
private fun clean(line: String?): String? {
return line?.replace("[^A-Za-z0-9 ] \\s+".toRegex(),"")?.trim()
}

Jetpack compose how to animate multiple values

I have couple of Path elements in my Canvas and would like to do some complex animations with every one of the Path lines. I am not sure how to approach this. Let's take a look at a simple path.
val line1Path = Path()
line1Path.moveTo(maxTopLeftX, 0f) // top left
line1Path.lineTo(maxBottomLeftX, size.height) // bottom left
line1Path.lineTo(maxBottomLeftX+lineWidth, size.height) // bottom right
line1Path.lineTo(maxTopLeftX+lineWidth, 0f) // top right
Currently I am using updateTransition with animateFloat but this way if I have to make animations for every one of the points only for this Path I would have to have 8 variables just for this 1 object.
Also I would like to do more than just a single value to value animation so something like animateFloatAsState where there are keyframes and I can order my animations seems better for the job, but the issue again is I have to create 8 variables that hold every one of the line positions to just animate this object.
What will be the best way to approach this?
I have been having same issue for days. In my case I use a data class for input data, so I just added an Animatable variable with default initialization to my data class. Then launch it from coroutine scope forEaching every item.
Not sure if this is the correct way to approach such issue, but hope it helps!
Here you have an example of multiple values animated at the same time. A transition is used to orchestrate the value animations.
val interactionSource = remember { MutableInteractionSource() }
val isPressed by interactionSource.collectIsPressedAsState()
val transition = updateTransition(targetState = isPressed, label = "")
val angle by transition.animateFloat(transitionSpec = {
tween(durationMillis = 180, easing = FastOutSlowInEasing)
}, label = ""){
when(it){
true -> 90f
false -> 0f
}
}
val x by transition.animateDp(transitionSpec = {
tween(durationMillis = 180, easing = FastOutSlowInEasing)
}, label = ""){
when(it){
true -> 85.dp
false -> 0.dp
}
}
Column(modifier = Modifier.fillMaxSize().background(Color(0xFFF0F8FF))
.padding(80.dp).wrapContentSize(align = Alignment.BottomCenter),
verticalArrangement = Arrangement.spacedBy(16.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
// THIS IS THE ANIMATED BOX
Box(Modifier.rotate(angle).offset(x = x)
.background(Color.Red)
.width(20.dp)
.height(150.dp)
)
Box(modifier = Modifier.clickable(interactionSource = interactionSource, indication = null) {}
.hoverable(interactionSource)
.focusable(interactionSource = interactionSource)
.size(100.dp).background(Color.Blue),
)
}

Dynamic form with composable-form

I'm trying to implement a dynamic form in Elm 0.19 using hecrj/composable-form.
I receive a json with the fields, their descriptions, etc, so I don't know beforehand how many fields it will have.
So the traditional way of defining a form:
Form.succeed OutputValues
|> Form.append field1
|> Form.append field2
doesn't work because I don't know the OutputValues structure beforehand.
I've seen there is a function Form.list which looks like a promising path, though it seems to expect all fields equal, which is not my case, I may have a text field and a select field for example.
Is there any straight forward way of doing this with this library?
Thank you.
The form library doesn't explicitly support what you're trying to do, but we can make it work!
tldr;
Here's my example of how you can take JSON and create a form: https://ellie-app.com/bJqNh29qnsva1
How to get there
Form.list is definitely the promising path. You're also exactly right that Form.list requires all of the fields to be of the same type. So let's start there! We can make one data structure that can hold them by making a custom type. In my example, I called it DynamicFormFieldValue. We'll make a variant for each kind of field. I created ones for text, integer, and select list. Each one will need to hold the value of the field and all of the extras (like title and default value) to make it show up nicely. This will be what we decode the JSON into, what the form value is, and what the form output will be. The resulting types looks like this:
type alias TextFieldRequirements =
{ name : String
, default : Maybe String
}
type alias IntFieldRequirements =
{ name : String
, default : Maybe Int
}
type alias SelectFieldRequirements =
{ name : String
, default : Maybe String
, options : List ( String, String )
}
type DynamicFormFieldValue
= TextField String TextFieldRequirements
| IntField Int IntFieldRequirements
| SelectField String SelectFieldRequirements
To display the form, you just need a function that can take the form value and display the appropriate form widget. The form library provides Form.meta to change the form based on the value. So, we will pattern match on the custom type and return Form.textField, Form.numberField, or Form.selectField. Something like this:
dynamicFormField : Int -> Form DynamicFormFieldValue DynamicFormFieldValue
dynamicFormField fieldPosition =
Form.meta
(\field ->
case field of
TextField textValue ({ name } as requirements) ->
Form.textField
{ parser = \_ -> Ok field
, value = \_ -> textValue
, update = \value oldValue -> TextField value requirements
, error = always Nothing
, attributes =
{ label = name
, placeholder = ""
}
}
IntField intValue ({ name } as requirements) ->
Form.numberField
{ parser = \_ -> Ok field
, value = \_ -> String.fromInt intValue
, update = \value oldValue -> IntField (Maybe.withDefault intValue (String.toInt value)) requirements
, error = always Nothing
, attributes =
{ label = name
, placeholder = ""
, step = Nothing
, min = Nothing
, max = Nothing
}
}
SelectField selectValue ({ name, options } as requirements) ->
Form.selectField
{ parser = \_ -> Ok field
, value = \_ -> selectValue
, update = \value oldValue -> SelectField value requirements
, error = always Nothing
, attributes =
{ label = name
, placeholder = ""
, options = options
}
}
)
Hooking this display function up is a bit awkward with the library. Form.list wasn't designed with use-case in mind. We want the list to stay the same length and just be iterated over. To achieve this, we will remove the "add" and "delete" buttons and be forced to provide a dummy default value (which will never get used).
dynamicForm : Form (List DynamicFormFieldValue) (List DynamicFormFieldValue)
dynamicForm =
Form.list
{ default =
-- This will never get used
TextField "" { name = "", default = Nothing }
, value = \value -> value
, update = \value oldValue -> value
, attributes =
{ label = "Dynamic Field Example"
, add = Nothing
, delete = Nothing
}
}
dynamicFormField
Hopefully the ellie example demonstrates the rest and you can adapt it to your needs!

Can't invoke remove() method on a list of Strings in groovy

I am trying to remove a String at a certain index in my list of Strings however I can't seem to invoke the list.remove() method in groovy.
public List getCassetteTypes(socket, numOfSlots){
//get the cassettes layout
sendCommand(socket, 'syst:layout? ')
String systLayoutStr = readCommand(socket)
//this String looks like: '1 ABC, 2 DEF, 3 SPN, ....'
List listOfCassetteTypes = new ArrayList<String>()
//I split the String at ',' because for each cassetteName, I want to remove the number before it
listOfCassetteTypes = systLayoutStr.split(',')
for(int i = 0; i < numOfSlots; i++){
//remove any white spaces
listOfCassetteTypes[i] = listOfCassetteTypes[i].trim()
//remove the numerical value
listOfCassetteTypes[i] = listOfCassetteTypes[i].replace((i + 1) + ' ', '')
/* if the cassette name is 'SPN',
I want to remove it and append '-EXT' to the cassetteName before it,
because 'SPN' means the previous slot is extended,
'SPN' itself isn't a cassette */
if(listOfCassetteTypes[i].equalsIgnoreCase('spn')){
listOfCassetteTypes[i - 1] = listOfCassetteTypes[i - 1].concat('-EXT')
//this is what is not working for me, everything else is fine.
listOfCassetteTypes = listOfCassetteTypes.remove(i)
}
}
return listOfCassetteTypes
}
I've tried several different ways but none of them seem to work.
Instead of manipulating the list, you could process each entry in a pair with it's successor... I believe this does what you're after?
def layoutStr = '1 ABC, 2 DEF, 3 SPN, 4 GHI'
def splitted = layoutStr.split(',')
*.trim() // remove white space from all the entries (note *)
*.dropWhile { it ==~ /[0-9 ]/ } // drop until you hit a char that isn't a number or space
.collate(2, 1, true) // group them with the next element in the list
.findAll { it[0] != 'SPN' } // if a group starts with SPN, drop it
.collect {
// If the successor is SPN add -EXT, otherwise, just return the element
it[1] == 'SPN' ? "${it[0]}-EXT" : it[0]
}
assert splitted == ['ABC', 'DEF-EXT', 'GHI']
Followup question
To just get the numbers of the ones not SPN:
def layoutStr = '1 ABC, 2 DEF, 3 SPN, 4 GHI'
def splitted = layoutStr.split(',')
*.trim() // remove white space from all the entries (note *)
*.split(/\s+/) // Split all the entries on whitespace
.findResults { it[1] == 'SPN' ? null : it[0] } // Only keep those that don't have SPN
Note that this is a list of Strings, not Integers... If you need integers then:
.findResults { it[1] == 'SPN' ? null : it[0] as Integer }