password validation no Character sequence ( kotlin android studio ) - android

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.

Related

How to detect the last string of a TextView contains 0 to 9

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
}
}

Android Paging 3 how to filter, sort and search my data

I'm trying to implement paging I'm using Room and it took me ages to realize that its all done for me 😆 but what I need to do is be able to filter search and sort my data. I want to keep it as LiveData for now I can swap to flow later.
I had this method to filter search and sort and it worked perfectly,
#SuppressLint("DefaultLocale")
private fun searchAndFilterPokemon(): LiveData<List<PokemonWithTypesAndSpecies>> {
return Transformations.switchMap(search) { search ->
val allPokemon = repository.searchPokemonWithTypesAndSpecies(search)
Transformations.switchMap(filters) { filters ->
val pokemon = when {
filters.isNullOrEmpty() -> allPokemon
else -> {
Transformations.switchMap(allPokemon) { pokemonList ->
val filteredList = pokemonList.filter { pokemon ->
pokemon.matches = 0
val filter = filterTypes(pokemon, filters)
filter
}
maybeSortList(filters, filteredList)
}
}
}
pokemon
}
}
}
It have a few switchmaps here, the first is responding to search updating
var search: MutableLiveData<String> = getSearchState()
the second is responding to filters updating
val filters: MutableLiveData<MutableSet<String>> = getCurrentFiltersState()
and the third is watching the searched list updating, it then calls filterTypes and maybeSortList which are small methods for filtering and sorting
#SuppressLint("DefaultLocale")
private fun filterTypes(
pokemon: PokemonWithTypesAndSpecies,
filters: MutableSet<String>
): Boolean {
var match = false
for (filter in filters) {
for (type in pokemon.types) {
if (type.name.toLowerCase() == filter.toLowerCase()) {
val matches = pokemon.matches.plus(1)
pokemon.apply {
this.matches = matches
}
match = true
}
}
}
return match
}
private fun maybeSortList(
filters: MutableSet<String>,
filteredList: List<PokemonWithTypesAndSpecies>
): MutableLiveData<List<PokemonWithTypesAndSpecies>> {
return if (filters.size > 1)
MutableLiveData(filteredList.sortedByDescending {
Log.d("VM", "SORTING ${it.pokemon.name} ${it.matches}")
it.matches
})
else MutableLiveData(filteredList)
}
as mentioned I want to migrate these to paging 3 and am having difficulty doing it Ive changed my repository and dao to return a PagingSource and I just want to change my view model to return the PagingData as a live data, so far I have this
#SuppressLint("DefaultLocale")
private fun searchAndFilterPokemonPager(): LiveData<PagingData<PokemonWithTypesAndSpecies>> {
val pager = Pager(
config = PagingConfig(
pageSize = 50,
enablePlaceholders = false,
maxSize = 200
)
){
searchAndFilterPokemonWithPaging()
}.liveData.cachedIn(viewModelScope)
Transformations.switchMap(filters){
MutableLiveData<String>()
}
return Transformations.switchMap(search) {search ->
val searchedPokemon =
MutableLiveData<PagingData<PokemonWithTypesAndSpecies>>(pager.value?.filter { it.pokemon.name.contains(search) })
Transformations.switchMap(filters) { filters ->
val pokemon = when {
filters.isNullOrEmpty() -> searchedPokemon
else -> {
Transformations.switchMap(searchedPokemon) { pokemonList ->
val filteredList = pokemonList.filter { pokemon ->
pokemon.matches = 0
val filter = filterTypes(pokemon, filters)
filter
}
maybeSortList(filters, filteredList = filteredList)
}
}
}
pokemon
}
}
}
but the switchmap is giving me an error that
Type inference failed: Cannot infer type parameter Y in
fun <X : Any!, Y : Any!> switchMap
(
source: LiveData<X!>,
switchMapFunction: (input: X!) → LiveData<Y!>!
)
which I think I understand but am not sure how to fix it, also the filter and sort methods won't work anymore and I cant see any good method replacements for it with the PageData, it has a filter but not a sort? any help appreciated
: LiveData<Y!
UPDATE thanks to #Shadow I've rewritten it to implement searching using a mediator live data but im still stuck on filtering
init {
val combinedValues =
MediatorLiveData<Pair<String?, MutableSet<String>?>?>().apply {
addSource(search) {
value = Pair(it, filters.value)
}
addSource(filters) {
value = Pair(search.value, it)
}
}
searchPokemon = Transformations.switchMap(combinedValues) { pair ->
val search = pair?.first
val filters = pair?.second
if (search != null && filters != null) {
searchAndFilterPokemonPager(search)
} else null
}
}
#SuppressLint("DefaultLocale")
private fun searchAndFilterPokemonPager(search: String): LiveData<PagingData<PokemonWithTypesAndSpecies>> {
return Pager(
config = PagingConfig(
pageSize = 50,
enablePlaceholders = false,
maxSize = 200
)
){
searchAllPokemonWithPaging(search)
}.liveData.cachedIn(viewModelScope)
}
#SuppressLint("DefaultLocale")
private fun searchAllPokemonWithPaging(search: String): PagingSource<Int, PokemonWithTypesAndSpecies> {
return repository.searchPokemonWithTypesAndSpeciesWithPaging(search)
}
I do not believe you can sort correctly on the receiving end when you are using a paging source. This is because paging will return a chunk of data from the database in whichever order it is, or in whichever order you specify in its query directly.
Say you have a list of names in the database and you want to display them sorted alphabetically.
Unless you actually specify that sorting in the query itself, the default query will fetch the X first names (X being the paging size you have configured) it finds in whichever order the database is using for the results of that query.
You can sort the results alright, but it will only sort those X first names it returned. That means if you had any names that started with A and they happened to not come in that chunk of names, they will only show when you have scrolled far enough for them to be loaded by the pager, and only then they will show sorted correctly in the present list. You might see names moving around as a result of this whenever a new page is loaded into your list.
That's for sorting, now for search.
What I ended up doing for my search was to just throw away the database's own capability for searching. You can use " LIKE " in queries directly, but unless your search structure is very basic it will be useless. There is also Fts4 available: https://developer.android.com/reference/androidx/room/Fts4
But it is such a PIA to setup and make use of that I ended up seeing absolutely no reward worth the effort for my case.
So I just do the search however I want on the receiving end instead using a Transformations.switchMap to trigger a new data fetch from the database whenever the user input changes coupled with the filtering on the data I receive.
You already have part of this implemented, just take out the contents of the Transformations.switchMap(filters) and simply return the data, then you conduct the search on the results returned in the observer that is attached to the searchAndFilterPokemonPager call.
Filter is the same logic as search too, but I would suggest to make sure to filter first before searching since typically search is input driven and if you don't add a debouncer it will be triggering a new search for every character the user enters or deletes.
In short:
sort in the query directly so the results you receive are already sorted
implement a switchMap attached to the filter value to trigger a new data fetch with the new filter value taken into account
implement a switchMap just like the filter, but for the search input
filter the returned data right before you submit to your list/recyclerview adapter
same as above, but for search
so this is at least partially possible using flows but sorting isnt possible see here https://issuetracker.google.com/issues/175430431, i havent figured out sorting yet but searching and filtering are possible, so for filtering i have it very much the same the query is a LIKE query that triggers by some livedata (the search value) emitting new data heres the search method
#SuppressLint("DefaultLocale")
private fun searchPokemonPager(search: String): LiveData<PagingData<PokemonWithTypesAndSpeciesForList>> {
return Pager(
config = PagingConfig(
pageSize = 50,
enablePlaceholders = false,
maxSize = 200
)
) {
searchAllPokemonWithPaging(search)
}.liveData.cachedIn(viewModelScope)
}
#SuppressLint("DefaultLocale")
private fun searchAllPokemonWithPaging(search: String): PagingSource<Int, PokemonWithTypesAndSpeciesForList> {
return repository.searchPokemonWithTypesAndSpeciesWithPaging(search)
}
and the repo is calling the dao, now to do the filtering #Shadow suggested using the pager this works but is much easier with flow using combine, my filters are live data and flow has some convenient extensions like asFlow so it becomes as easy as
#SuppressLint("DefaultLocale")
private fun searchAndFilterPokemonPager(search: String): Flow<PagingData<PokemonWithTypesAndSpeciesForList>> {
val pager = Pager(
config = PagingConfig(
pageSize = 50,
enablePlaceholders = false,
maxSize = 200
)
) {
searchAllPokemonWithPaging(search)
}.flow.cachedIn(viewModelScope).combine(filters.asFlow()){ pagingData, filters ->
pagingData.filter { filterTypesForFlow(it, filters) }
}
return pager
}
obviously here I've changed the return types so we also have to fix our mediator live data emitting our search and filters as it expects a live data, later ill fix this so it all uses flow
init {
val combinedValues =
MediatorLiveData<Pair<String?, MutableSet<String>?>?>().apply {
addSource(search) {
value = Pair(it, filters.value)
}
addSource(filters) {
value = Pair(search.value, it)
}
}
searchPokemon = Transformations.switchMap(combinedValues) { pair ->
val search = pair?.first
val filters = pair?.second
if (search != null && filters != null) {
searchAndFilterPokemonPager(search).asLiveData()
} else null
}
}
here we just use the asLiveData extesnion

How to combine two kotlin data objects with shared variable into one object?

I'm making a little application which tracks cryptocurrency values at the Bittrex exchange.
For this I'm using Bittrex' public api (https://bittrex.github.io/api/v3)
Unfortunately the api doesn't provide the data I want with just one call, therefore I need to do two api calls.
What I want to achieve is to have one object containing all of the following values:
symbol (This is a shared value between both api calls, so this needs
to match)
quoteVolume
percentChange
lastTradeRate
The bold variable is part of one api call, the other values are part of the other. 'Symbol' is part of both.
I'm using kotlin coroutines and I was hoping that I don't have to use something like RxJava to get this to work.
CoroutineScope(IO).launch {
val tickers = async {
api.getTickers()
}.await()
val markets = async {
api.getMarkets()
}.await()
val result = mutableListOf<Market>()
for (ticker in tickers.data) {
for (market in markets.data) {
if (ticker.symbol == market.symbol) {
result.add(
Market(
ticker.symbol,
ticker.lastTradeRate,
market.quoteVolume,
market.percentChange
)
)
}
}
}
}
You can make the 2 calls in parallel using coroutines.
Assuming firstApi and secondApi are suspend functions that return the data for each of the 2 blobs of info you need,
val data1Deferred = async { firstApi() }
val data2Deferred = async { secondApi() }
val data1 = data1Deferred.await()
val data2 = data2Deferred.await()
val result = Result(
// build result from data1 and data2
)
You would also need to add error handling.
Edit:
you can group your list by the symbol and generate a map:
val marketMap = markets.associateBy { it.symbol }
Then for each ticker you can get the corresponding market
for (ticker in tickers) {
val market = marketMap[ticker.symbol]
if (market != null) {
// join ticker and market
}
}

Stopping an app from crashing by checking if EditText is empty

So i made a Calculator with 2 EditText inputs, but i couldn't stop the app from crashing when the 2 fields are empty. I found some solutions for EditText of Text type but am stuck here because i am using a Number EditText.
Here is the code by the way:
{
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
}
fun buPlusClick(view: View){
var num1:Int=ednum1.text.toString().toInt()
var num2:Int=ednum2.text.toString().toInt()
var result = num1+num2
val resultprint = "The result Is : $result"
tvresult.text=resultprint
}
}
The error you're likely getting is a NumberFormatException. This is because ("" != [0-9]+) && value value !< Int.MAX_VALUE && !> Int.MIN_VALUE.
You have three options:
Check if it's empty first, as sandip mentioned.
Use toIntOrNull
Catch the exception.
toInt throws an exception (NumberFormatException) if the int is invalid. This means:
Not a number with the defined radix (default 10; an actual int. I.e. 16 is hex, but it's not applicable here)
The number is too big or too small (under Int.MIN_VALUE or over Int.MAX_VALUE).
The String is empty.
Checking if it's empty first solves half the problem; it can't be valid if it's empty. However, it doesn't solve it if there are letters or the number is too big.
You can use toIntOrNull() for this. It's just like toInt(), but it returns null if the conversion fails. You can use the elvis operator with a run block, or just use a simple if-statement to check if it's null, and handle the problems accordingly.
val num1: Int = ednum1.text.toString().toIntOrNull() ?: run {
// Notify the user the first number is invalid
return; // return from the method to avoid NPE's
}
val num2: Int = ednum2.text.toString().toIntOrNull() ?: run {
// Notify the user the second number is invalid
return;
}
if you don't understand what run does here, take a look at this question. You can also get rid of run entirely and assign a number value, just let it be null and handle it later, or something else.
The max positive value of an int is about 2.4 billion. If you expect numbers with a higher input than that, use Long, and equivalently toLongOrNull().
The last option is catching the error.
var num1: Int? = try { ednum1.text.toString().toInt() } catch (e: NumberFormatException) {
// You can do a lot of stuff in here; you can notify the user and `return` from the method,
// set it to null and handle it later, or assign an integer value.
null
}
var num2: Int? = try { ednum2.text.toString().toInt() } catch (e: NumberFormatException) {
// see my comments on the first block; same applies here
null
}
It is, in my opinion, slightly more bulky than toIntOrNull()( / toLongOrNull()), but it is an option.
You need to check first editText empty or not.
//Java
private boolean isEmpty(EditText editText) {
if (editText.getText().toString().trim().length() > 0)
return false;
return true;
}
//kotlin
private fun isEmpty(editText: EditText): Boolean {
return if (editText.text.toString().trim { it <= ' ' }.length > 0) false else true
}
Then do the following Code:
fun buPlusClick(view: View){
if(!isEmpty(ednum1) && !isEmpty(ednum2)) {
var num1:Int=ednum1.text.toString().toInt()
var num2:Int=ednum2.text.toString().toInt()
var result = num1+num2
val resultprint = "The result Is : $result"
tvresult.text=resultprint
}
}
Do the above check all of your functions.

Does when comparison ignores case of String?

I have a method which takes String as input and returns an Integer corresponding to it as shown below :
fun getPriority(groupValue: String?): Int {
when (groupValue) {
"one" -> return 10
"Two" -> return 9
"THREE" -> return 8
else -> return 4
}
}
My Question is String comparison in this case takes case of string into consideration or ignores case?
when does a equals-comparison, so it is case-sensitive indeed (see also String.equals).
Ignoring case sensitivity can be accomplished in several ways, one of which is already shown by Willi Mentzel... Some other (depending on what you want to accomplish):
fun getPriority(groupValue : String?) = when {
groupValue == null -> /* handle the null first, so you can concentrate null-safe on the rest later */ 4
groupValue.equals("one", ignoreCase = true) -> 10 /* ignoreCase = false is the default */
/* ... */
else -> 4
}
If it's that simple Willis approach will probably suffice you already.
As other answers has stated, the when-expression uses the equals method for comparison. The equals method on String is case sensitive by default.
If you want to compare objects on something else than its equals method, you could create a small wrapper class with it's own implementation of equals. This might be a little overkill in your particular case, but it might be useful in other cases.
The wrapper class:
// In Kotlin 1.3, this class could probably be inlined.
class CaseInsensitiveString(val s: String) {
fun equals(other: Any) =
(other as? CaseInsensitiveString)?.s?.equals(s, ignoreCase = true) ?: false
// Overriding hashCode() just so it's consistent with the equals method.
override fun hashCode() = s.toLowerCase().hashCode()
}
A convenient extension property to wrap a String:
val String.caseInsensitive
get() = CaseInsensitiveString(this)
Now you can do this:
fun getPriority(groupValue: String?): Int {
when (groupValue?.caseInsensitive) {
"one".caseInsensitive -> return 10
"Two".caseInsensitive -> return 9
"THREE".caseInsensitive -> return 8
else -> return 4
}
}
Yes, it is case sensitive, because String.equals is invoked as Roland already said.
To make it case insensitive:
fun getPriority(groupValue: String?) = when (groupValue?.toLowerCase()) {
"one" -> 10
"two" -> 9
"three" -> 8
else -> 4
}
Tip: since when is an expression, you can use the expression body notation for your function.
Alternative:
Use a Map instead of when. This becomes especially handy if your code should be dynamic.
val priorityMapping = mapOf(
"one" to 10,
"Two" to 9,
"THREE" to 8
)
fun getPriority(groupValue: String?): Int {
groupValue?.let {
priorityMapping.forEach { (key, value) ->
if(key.equals(it, true)) {
return value
}
}
}
return 4 // default value
}
As others already stated, when uses equals to form a boolean expression if a scalar (or a list of scalars) is placed in front of the -> sign. Therefore (snippet from official docs of when - block comments are mine):
when (x) {
1 -> print("x == 1") /* same as x.equals(1) or x == 1 */
2 -> print("x == 2") /* same as x.equals(2) or x == 2 */
else -> { // Note the block
print("x is neither 1 nor 2")
}
}
According to the doc of text.equals:
Parameters
ignoreCase - true to ignore character case when comparing characters. By default
false.
Two characters are considered the same ignoring case if at least one
of the following is true:
The two characters are the same (as compared by the == operator)
Applying the method toUpperCase to each character produces the same result
Applying the method toLowerCase to each character produces the same result
So in you case groupValue.equals("one") is the same as groupValue.equals("one", ignoreCase = false) therefore the answer is yes, when you use when on a String by default the comparison will consider casing.

Categories

Resources