FileChannel.truncate(...) method truncates at wrong position - android

I am using the FileChannel.truncate() method to remove the last part of a file, from the text "metadata" until the end:
override fun truncate(file: File) {
val fileAsText = FileUtils.readFileToString(file, Charset.defaultCharset())
val metadataPosition = fileAsText.indexOf("metadata")
val channel = FileOutputStream(file, true).channel
channel.truncate(metadataPosition.toLong())
channel.close()
}
While debugging I can see that the variable metadataPosition has a certain value, which is correct when checking it in the fileAsText String. Note that the "metadata" text only appears once in the file. However, when the channel.truncate(metadataPosition.toLong()) line is executed, the truncation is performed in a previous position and the file is smaller than expected, losing some of the information.
I have no clue about the reason for this, what could it be?

Related

How can I prevent repeating numbers in a random range operator Kotlin?

I have an app in which gives you a certain photo based on an integer being fetched via
class RandomImageLogic(){
fun retrive(): Int{
return (1..9).random()
}
}
However, it is not professional to have a repeated outcome, as I desire a random integer to be fetched each time I call the function in order for the image to be different each time my button is pressed. How can I fetch a new random integer whenever the button calls the function?
The easy way is to pass in the last random number you received and filter it out.
fun retrive(except: Int): Int{
return ((1..9).filter {it != except}).random();
}
In your case, this method might not be called extremely often (only when the user clicks a button).
If this method was to be called more often, filter on an IntRange should be used with care (as proposed in #avalerio's answer).
This iterates the whole range (unnecessarily costing time) and it would create a temporary ArrayList on every call (creating unnecessary garbage and triggering the garbage collector more often than needed).
Here is a sample object NonRepeatingRandom (you can also implement it as a class if you wish). retrieve (which has been expanded with a max parameter and basic sanity checks) recursivly calls itself again if the same number would be generated twice in a row:
object NonRepeatingRandom {
private var previous = -1
fun retrieve(max : Int = 9): Int {
if(max < 0) {
error("Only positive numbers")
}
if(max <= 1) {
// There is nothing random about 0 or 1, do not check against previous, just return
previous = max
return max
}
val rand = (1..9).random()
return if(rand == previous) {
retrieve(max) // recursive call if two subsequent retrieve() calls would return the same number
} else {
previous = rand // remember last random number
rand
}
}
}
fun main(args: Array<String>) {
repeat(1000) {
println(NonRepeatingRandom.retrieve())
}
}
I made a quick-and-dirty performance test, calling my "recursive" method 10 million times and calling the "filter" method 10 million times.
Recursive: 125 ms (10 mio calls)
Filter: 864 ms (10 mio calls)
Pre-fill & random shuffle approach:
class RandomIntIterator(
private val range: IntRange
) : Iterator<Int> {
private lateinit var iterator: Iterator<Int>
init { randomize() }
fun randomize() {
iterator = range.shuffled().iterator()
}
override fun hasNext() = iterator.hasNext()
override fun next() = iterator.next()
}
...
val rnd = RandomIntIterator(1..9)
...
// on button click
if (rnd.hasNext()) {
val num = rnd.next()
// use num
} else {
// renew (if needed)
rnd.randomize()
}
I like using Sequences to generate unending streams of values.
In this case we'll have to write custom code, as checking for repeated values is a stateful operation, and while Sequences has a distinct() stateful filter, it applies to all generated values - we only want it to apply to a limited window.
TL;DR:
class RandomImageLogic(
private val random: Random,
/** The number of sequential values that must be distinct */
noRepeatsLimit: Int = 2
) {
private val sourceValues: List<Int> = (0..9).toList()
private fun nextValue(vararg exclusions: Int): Int =
(sourceValues - exclusions.asList()).random(random)
private val randomInts: Iterator<Int> =
generateSequence({
// the initial value just has one random int
val next = nextValue()
ArrayDeque(listOf(next))
}) { previousValues ->
// generate the next value, excluding previous values
val nextValue = nextValue(*previousValues.toIntArray())
// limit the size of previousValues, if necessary
if (previousValues.size >= noRepeatsLimit)
previousValues.removeLastOrNull()
// add the generated value to the beginning of the deque
previousValues.addFirst(nextValue)
previousValues
}
.map {
// convert the Sequence to a list of ints,
// each element is the first item in the deque
it.first()
}
.iterator()
fun retrieve(): Int {
return randomInts.next()
}
}
Testing
Let's write a test first, to make sure our solution works. Kotest has a specific property based testing subproject, and this will let us cover a wide range of test cases very quickly.
So, I ran through the setup, and started setting up the test case.
Seeding RandomImageLogic
First I modified the RandomImageLogic class so that the random selection could be seeded with a provided Random.
import kotlin.random.Random
class RandomImageLogic(private val random: Random) {
fun retrieve(): Int {
return (1..9).random(random = random)
}
}
This will help us to create a Generator for the RandomImageLogic.
Testing all values
Now we can use Kotest to write a property-based test that will assert "for all sequential values, they are different"
import io.kotest.core.spec.style.FunSpec
import io.kotest.property.arbitrary.arbitrary
import io.kotest.property.forAll
class RandomImageLogicTest: FunSpec({
// This generator will create a new instance of `RandomImageLogic`
// and generate two sequential values.
val sequentialValuesArb = arbitrary { rs ->
val randomImageLogic = RandomImageLogic(rs.random)
val firstValue = randomImageLogic.retrieve()
val secondValue = randomImageLogic.retrieve()
firstValue to secondValue
}
test("expect sequential values are different") {
forAll(sequentialValuesArb) { (firstValue, secondValue) ->
firstValue != secondValue
}
}
})
Of course, the test fails.
Property failed after 4 attempts
Arg 0: (1, 1)
Repeat this test by using seed 1210584330919845105
Caused by org.opentest4j.AssertionFailedError: expected:<true> but was:<false>
So let's fix it!
Generating Sequences
As I said earlier, I really like Sequences. They're perfect for this use-case, where we have an infinite source of values.
To demonstrate how to make a Sequence, let's convert the existing code, and use an Iterator to fetch values.
class RandomImageLogic(private val random: Random) {
private val randomInts =
// generate a sequence using values from this lambda
generateSequence { (1..9).random(random = random) }
// use an iterator to fetch values
.iterator()
fun retrieve(): Int {
return randomInts.next()
}
}
This hasn't solved the problem yet - the Sequence only generates and provides one value at a time and so cannot do any filtering. Fortunately generateSequence() has an variant with nextFunction: (T) -> T?, where we can determine the next value based on the previous value.
If we use this constructor, and do a bit of refactoring to share the source values, and a util method to generate a next value while filtering out previous values...
private val sourceValues: List<Int> = (0..9).toList()
private fun nextValue(vararg exclusions: Int): Int =
(sourceValues - exclusions.asList()).random(random)
private val randomInts: Iterator<Int> =
generateSequence({ nextValue() }) { previousValue ->
nextValue(previousValue)
}
.iterator()
and now if we run the test, it passes!
Test Duration Result
expect sequential values are different 0.077s passed
Improvement: more than two distinct sequential values
What happens if you don't just want two sequential values to be distinct, but 3? Or even more? Let's make the 'no-repeated-values' limit configurable, and I think this will demonstrate why Sequences are a good solution.
class RandomImageLogic(
private val random: Random,
/** The number of sequential values that must be distinct */
noRepeatsLimit: Int = 2
) {
// ...
}
Testing
Once again, let's write a test to make sure things work as expected.
import io.kotest.core.spec.style.FunSpec
import io.kotest.matchers.collections.shouldHaveSize
import io.kotest.matchers.collections.shouldNotContainDuplicates
import io.kotest.property.Arb
import io.kotest.property.arbitrary.int
import io.kotest.property.checkAll
import kotlin.random.Random
class RandomImageLogicTest : FunSpec({
test("expect arbitrary sequential values are different") {
checkAll(Arb.int(), Arb.int(1..10)) { seed, noRepeatsLimit->
val randomImageLogic = RandomImageLogic(Random(seed), noRepeatsLimit)
val result = List(noRepeatsLimit) { randomImageLogic.retrieve() }
withClue("Result: $result") {
result shouldHaveSize noRepeatsLimit
result.shouldNotContainDuplicates()
}
}
}
})
And of course the test fails.
Property test failed for inputs
0) -459964888
1) 5
Caused by java.lang.AssertionError: Result: [3, 8, 0, 2, 8]
Collection should not contain duplicates
There's lots of options to make a Sequence stateful - again let's just look at one.
Sequence of values
Instead of a sequence of individual values, we can have a sequence where each element is a list of not only the current value, but also previously seen values.
Let's use ArrayDeque to store these values, because it's easy to add and remove values from the start and end.
Again, we use the same generateSequence constructor with a seedFunction and nextFunction - except this time each element is deque which stores all values, and in nextFunction we add new values to the start of the deque, trimming it if it's larger than the window size noRepeatsLimit
private val randomInts: Iterator<Int> =
generateSequence({
// the initial value just has one random int
val next = nextValue()
ArrayDeque(listOf(next))
}) { previousValues ->
// generate the next value, excluding previous values
val nextValue = nextValue(*previousValues.toIntArray())
// limit the size of previousValues, if necessary
if (previousValues.size >= noRepeatsLimit)
previousValues.removeLastOrNull()
// add the generated value to the beginning of the deque
previousValues.addFirst(nextValue)
previousValues
}
.map {
// convert the Sequence to a list of ints,
// each element is the first item in the deque
it.first()
}
.iterator()
And yup, the test passes!
Test Duration Result
expect arbitrary sequential values are different 0.210s passed
Storing state
It's important to think about how state will be stored. 'State' is required by RandomImageLogic to know whether a generated value is distinct.
In the Sequence implementation, it's stored internally, and so is specifically associated with an instance of RandomImageLogic. Maybe your application only has one instance of RandomImageLogic at any one time, in which case the state will always be up to date and will be shared between all invocations.
But what happens if there's more than one instance of RandomImageLogic? Or if there's multi-threading? Or if the RandomImageLogic instance is recreated?
The answers to these depend on the implementation and situation. From your question I suspect that it's not critically important that images never repeat, but I bring this up because it is important to think about.

Not getting a new line in kotlin text view on android studio

fun SubmitOrder(view: View) {
/* pricing of coffee */
val total = quantity * 5
val s: String = ("$$total.00")
money.text = ("Total : $s\nThank You!").toString()
//This is calling On click listener
Toast.makeText(this, "order_Submitted", Toast.LENGTH_SHORT).show()
}
In this code, I need a new line before Thank You! in money.text but I am not getting any new line I am new in android development so, am not able to point out the mistake.
Let's go thru your code line by line:
val s: String = ("$$total.00")
s is a bad variable name, as it's not descriptive at all
you don't need the (braces)
the :String here is optional. In such an obvious case i would emit it.
A $ is a sign to the kotlin compiler to include the following variable. Therefore, you can't use the $ when you mean "US-Dollar". See this post on how to escape it
While ".00" works, it's no good style. I suggest you use string formatting as described here.
can be written as val s = "\$ ${String.format("%.2f", total)}"
you should wherever possible use string resources, but thats out of the scope of this answer
money.text = ("Total : $s\nThank You!").toString()
this is correct, but unnecessary verbose:
"Total : $s\nThank You!" is already a string, so there's no need to .toString()
braces are not needed
can be written as money.text = "Total : $s\nThank You!"

removeAt removes elements in passed ArrayList too

I've never saw this
#RequiresApi(Build.VERSION_CODES.LOLLIPOP)
private fun eraseOldLocations() {
val a: ArrayList<Ub> =myUbs
val minTime: Long = (now- tenDaysago)
val z = misUbs.size
for(i in 0 until z){
if( myUbs[i].time <minTime) {
a.removeAt(i)
}
}
myUbs = a
}
each time the condition is fulfilled, the element in the position i of a is removed ... BUT also from the myUbs !!! myUbs is a public object, so as the loop continues, appears an error because the variable i exceeds the size of myUbs ...
The function eraseOldLocations() must delete elements with date older than ten days ago. Why is this happening?
Do a shallow copy of myUbs, so that references of list will differ and changes won't reflect to each other:
val a: MutableList<Ub> = myUbs.toMutableList()
MutableList is essentially the same, but if you want ArrayList explicitly (althrough not recommended in Kotlin) you can call ArrayList constructor to delegate to the Collection specified. That essentially does the shallow copy as well:
val a: ArrayList<Ub> = ArrayList(myUbs)

Palette API returning negative values?

So I have to use the palette API in this app I am building to dynamically change the status bar color and when I pass the value I get to the method to change the status bar color of the app crashes with a java.lang.IllegalArgumentException: Unknown color
I am formatting it correctly regardless by adding the requires "#" in front of the number and also passing it as a String but the app still crashes.
This is the code snippet I used to do it.
binding.viewpagerMain.registerOnPageChangeCallback(
object: ViewPager2.OnPageChangeCallback(){
override fun onPageSelected(position: Int) {
var headerImage : Int? = null
when(position){
0 -> headerImage = R.drawable.character
1 -> headerImage = R.drawable.planets
2 -> headerImage = R.drawable.films
3 -> headerImage = R.drawable.species
4 -> headerImage = R.drawable.vehicles
5 -> headerImage = R.drawable.starships
}
if (headerImage != null) {
imageview_main.setImageResource(headerImage)
val bitmap = BitmapFactory.decodeResource(resources, headerImage)
Palette.from(bitmap).generate {
#SuppressLint("ResourceAsColor")
val paletteColor: Int? = it?.getDarkVibrantColor(R.color.colorPrimaryDark)
Log.e("MainFragment", "#${paletteColor}")
(activity as MainActivity).updateStatusBarColor("#${paletteColor.toString()}")
}
}
}
}
)
This is the activity function that I run
open fun updateStatusBarColor(color: String?) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
val window: Window = window
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = Color.parseColor(color)
}
}
Added a log call to know what the output was, and I got values like which were negative
#2131034159
#-14678008
#-15191992
#-7325688
I don't why the values are negative or if they can be, I assumed they where hex values and passed them , but they still don't work.
Color are stored as 32-bit integers on Android, as explained in the Color ints documentation. If the most significant bit is 1, the value is interpreted negative because the integer is signed. However interpreting it as signed makes no sense in the context of colors. In fact, all values that can be taken by an integer are valid colors.
Here's two solutions for fixing your logging statment:
"#${(paletteColor.toLong() and 0xFFFFFFFF).toString(16)}". Increase the bit width of your color and apply 32-bit mask so the most significant bit is 0 and value appears as positive.
"#${paletteColor.toUInt().toString(16)}". Use an unsigned integer. (requires having experimental unsigned numbers compiler option in Kotlin)
I prefer the first one.
For actually changing the color, I don't know if you realized what you're here: you're getting a color int, converting it to a string, then parsing it back to the original int! Why add an unnecessary step? Just use the int returned from getDarkVibrantColor to update window.statusBarColor.

Kotlin check for words in string

I have a NSFW class that scans texts like item names and descriptions against a list of known NSFW-words.
That would be the best approach to test a list of strings like
let nsfw = listof(
"badword",
"curseword",
"ass",
... 200+ more
)
against a string like:
This is the text that contains a badword // returns true
Please note that i need to check for full words. not parts of words.
so the sentence:
The grass is grean // returns false
Because grass is not a bad word.
Ive tried something like this but it doesnt check for full words.
val result = nsfw.filter { it in sentence.toLowerCase() }
You may build a regex like
\b(?:word1|word2|word3...)\b
See the regex demo. Then, use it with the Regex.containsMatchIn method:
val nsfw = listOf(
"badword",
"curseword",
"ass"
)
val s1 = "This is the text that contains a badword"
val s2 = "The grass is grean"
val rx = Regex("\\b(?:${nsfw.joinToString(separator="|")})\\b")
println(rx.containsMatchIn(s1)) // => true
println(rx.containsMatchIn(s2)) // => false
See this Kotlin demo.
Here, nsfw.joinToString(separator="|") joins the words with a pipe (the alternation operator) and the "\\b(?:${nsfw.joinToString(separator="|")})\\b" creates the correct regex.
If your words may contain special regex metacharacters, like +, ?, (, ), etc., you need to "preprocess" the nsfw values with the Regex.escape method:
val rx = Regex("\\b(?:${nsfw.map{Regex.escape(it)}.joinToString("|")})\\b")
^^^^^^^^^^^^^^^^^^^^^^
See the Kotlin demo.
AND one more thing: if the keywords may start/end with chars other than letters, digits and underscores, you cannot rely on \b word boundaries. You may
Use whitespace boundaries: val rx = Regex("(?<!\\S)(?:${nsfw.map{Regex.escape(it)}.joinToString("|")})(?!\\S)")
Use unambiguous word boundaries: val rx = Regex("(?<!\\w)(?:${nsfw.map{Regex.escape(it)}.joinToString("|")})(?!\\w)")
You can use split() on the string that you want to check, with space as a delimiter, so you create a list of its words, although this does not always guarantee that all words will be extracted successfully, since there could exist other word separators like dots or commas etc. If that suits you, do this:
val nsfw = listOf(
"badword",
"curseword",
"ass"
)
val str = "This is the text that contains a badword"
val words = str.toLowerCase().split("\\s+".toRegex())
val containsBadWords = words.firstOrNull { it in nsfw } != null
println(containsBadWords)
will print
true
If you want a list of the "bad words":
val badWords = words.filter { it in nsfw }

Categories

Resources