Kotlin not receiving a full packet from an HC-06 Bluetooth module - kotlin

In Kotlin I am having an issue with data not fully loading into the buffer in Android in one packet. I am sending <7:16> data packet from a HC-06 bluetooth module.
When i receive it's coming in as 2 packets. The micro is sending a CR and LF and the end of the transmission.
I/UI_Msg: <
I/UI_Msg: 7:16>
I/UI_Msg: <
I/UI_Msg: 7:16>
etc
If I add a sleep command for a second, then query the buffer it comes in completed in one packet. The problem is I want to send more than one packet in a second..
How can i fix this issue to or can i just build a string until '>' is received from the buffer.
private fun beginListenForData() {
val handler = Handler()
stopThread = false
buffer = ByteArray(10)
val thread = Thread(Runnable {
while (!Thread.currentThread().isInterrupted && !stopThread) {
try {
val byteCount = inputStream!!.available()
if (byteCount > 0) {
//Thread.sleep(1000) // wait for buffer finish filling
val rawBytes = ByteArray(byteCount)
inputStream!!.read(rawBytes)
val string = String(rawBytes, StandardCharsets.UTF_8)
//handler.post {txt_message.text=""}
Log.i(TAG,""+ string)
//handler.post {txt_message.append(string)}
// processInput(string)
}
} catch (ex: IOException) {
stopThread = true
}
}
})
thread.start()
}

Figgured it out. Read the buffer one byte at a time and build the string character at a time. Check for the < and the > character for a valid transmission
private fun beginListenForData() {
stopThread = false
val thread = Thread(Runnable {
while (!Thread.currentThread().isInterrupted && !stopThread) {
try {
val byteCount = inputStream!!.available()
if (byteCount > 0) {
val rawBytes = ByteArray(1)
inputStream!!.read(rawBytes,0,1)
val string = String(rawBytes, StandardCharsets.UTF_8)
if (string== "<") {
m_Headder=true
}
if (m_Headder==true){
stringBuilder = stringBuilder.plus(string)
if (string ==">") {
m_Received=stringBuilder
Log.i(TAG, "string:" + stringBuilder)
stringBuilder = ""
m_Headder = false
ProcessMessage(m_Received)
}
}
}
} catch (ex: IOException) {
stopThread = true
}
}
})
thread.start()
}

Related

Remove redundant also calls

I'm working with TCP communication and use Ktor library.
I have packets that come, and the first byte is the packet's lengths.
Likewise, I read the packet from what is documented on the protocol API, and sometimes I have to skip the remaining bytes of the packet, so I subtract what I have read to this message length.
At the present time, I call also { messageLength -= length } on each reading, but the code is unreadable.
#Test
fun ktorTests() {
runBlocking {
val selectorManager = SelectorManager(Dispatchers.IO)
val socket = aSocket(selectorManager).tcp().connect("server.slsknet.org", 2242)
val openReadChannel = socket.openReadChannel()
val openWriteChannel = socket.openWriteChannel(autoFlush = true)
val login = "Test"
val pwd = "159753"
openWriteChannel.write {
it.put(
ByteMessage().writeInt32(1)
.writeStr(login)
.writeStr(pwd)
.writeInt32(160)
.writeStr((login + pwd).toMD5())
.writeInt32(1)
.getBuff()
)
}
while (true) {
var messageLength = openReadChannel.readIntLittleEndian()
val code = openReadChannel.readIntLittleEndian()
println("ServerClient received: Message code:" + code + " Packet Size:" + (messageLength + 4))
when (code) {
1 -> {
if (openReadChannel.readBoolean().also { messageLength -= 1 }) {
val greetingLength = openReadChannel.readIntLittleEndian().also { messageLength -= 4 }
val greeting = ByteArray(greetingLength)
openReadChannel.readFully(greeting, 0, greetingLength)
.also { messageLength -= greetingLength }
val ip = openReadChannel.readIntLittleEndian().also { messageLength -= 4 }
println("Logged In.")
}
openReadChannel.discardExact(messageLength.toLong())
}
}
}
Any ideas on ho could it be done ?

I tried to run this code and every time I run it shows this result

private fun moveMarkerAnimation(key: String, newData: AnimationModel, marker: Marker?, from: String, to: String) {
if (!newData.isRun)
{
compositeDisposable.add(
iGoogleAPI.getDirections(
"driving",
"less_driving",
from, to,
getString(R.string.google_api_key1)
)!!.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe { returnResult ->
Log.d("API_RETURN",returnResult,)
try {
val jsonObject = JSONObject(returnResult)
val jsonArray = jsonObject.getJSONArray("routes")
for ( i in 0 until jsonArray.length())
{
val route = jsonArray.getJSONObject(i)
val poly = route.getJSONObject("overview_polyLine")
val polyLine = poly.getString("points")
polylineList = Common.decodePoly(polyLine) as java.util.ArrayList<LatLng?>
}
handler = Handler()
index = -1
next = 1
val runnable = object :Runnable{
override fun run() {
if (polylineList!!.size > 1)
{
if (index< polylineList!!.size)
{
index ++
next = index+1
start = polylineList!![index] !!
end = polylineList!![next]!!
}
val valueAnimator = ValueAnimator.ofInt(0,1)
valueAnimator.duration = 3000
valueAnimator.interpolator = LinearInterpolator()
valueAnimator.addUpdateListener { value ->
v = value.animatedFraction
lat = v*end !!.latitude + (1-v) * start!!.latitude
lng = v*end !!.longitude+ (1-v) * start!!.longitude
val newPos = LatLng(lat,lng)
marker!!.position = newPos
marker!!.setAnchor(0.5f,0.5f)
marker!!.rotation = Common.getBearing(start!!,newPos)
}
valueAnimator.start()
if (index < polylineList!!.size-2)
{
handler!!.postDelayed(this,1500)
}else if (index < polylineList!!.size-1)
{
newData.isRun = false
Common.driversSubscrib.put(key,newData)
}
}
}
}
handler!!.postDelayed(runnable,1500)
}
catch (e:java.lang.Exception){
Snackbar.make(requireView(),e.message!!,Snackbar.LENGTH_LONG).show()
}
When the site changes from from firebase this result appears
022-04-26 13:19:30.912 23482-23482/com.example.bustrackerriderapp E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.bustrackerriderapp, PID: 23482
io.reactivex.exceptions.OnErrorNotImplementedException: The exception was not handled due to missing onError handler in the subscribe() method call. Further reading
what should I do to fix this problem

Kotlin multiplatform. Ktor download big file and save to local file [duplicate]

I've been spending way too much time trying to solve this problem. So the code that I posted below does work in terms of downloading a file, but the problem is, the flow has a very unexpected behaviour. The response.content.readAvailable() method call seems to block until it's completely done downloading the whole file at which point the emit progress happens, so you end up waiting a long time for the file to download, and then in a split second you get all of the progress updates. So I'm wondering if there is a way to do this where I read in a certain number of bytes at a time and then emit a progress and then repeat until the file is done downloading? Or maybe a way to hook into the readAvailable() method and update the progress that way? Any help with this would be greatly appreciated.
Here's the code I found and modified, but still does not work right:
suspend fun HttpClient.downloadFile(
output: File,
downloadUrl: String,
md5Hash: String,
) = flow {
try {
val response = get<HttpResponse> { url(downloadUrl) }
val data = ByteArray(response.contentLength()?.toInt() ?: 0)
val contentLn = response.contentLength()?.toInt() ?: 0
var offset = 0
var bytesRemaining = contentLn
do {
val chunkSize = min(maxChunkSize, bytesRemaining)
logger?.d { "Read Available:" }
val result = response.content.readAvailable(data, offset, length = chunkSize)
val progress = ((offset / contentLn.toDouble()) * 100).toInt()
emit(DownloadResult.Progress(progress))
logger?.d { "logged progress: $progress" }
// delay(6000L) this was to test my assumption that the readAvalible was blocking.
offset += chunkSize
bytesRemaining -= chunkSize
} while (result != -1)
if (response.status.isSuccess()) {
if (data.md5().hex == md5Hash) {
output.write(data)
emit(DownloadResult.Success)
} else {
emit(DownloadResult.ErrorCorruptFile)
}
} else {
emit(DownloadResult.ErrorBadResponseCode(response.status.value))
}
} catch (e: TimeoutCancellationException) {
emit(DownloadResult.ErrorRequestTimeout("Connection timed out", e))
}
}
Finally after a stupid amount of time I solved this. What you need to use is this. That gives you access to the byte channel as it is downloading.
and a very crude implementation (that I'm not yet done with) is this:
get<HttpStatement>(url = downloadUrl).execute {
var offset = 0
val byteBufferSize = 1024 * 100
val channel = it.receive<ByteReadChannel>()
val contentLen = it.contentLength()?.toInt() ?: 0
val data = ByteArray(contentLen)
do {
val currentRead = channel.readAvailable(data, offset, byteBufferSize)
val progress = if(contentLen == 0) 0 else ( offset / contentLen.toDouble() ) * 100
logger?.d { "progress: $progress" }
offset += currentRead
} while (currentRead >= 0)
}
two things to not with this solution. 1.) I'm in the context of HttpClient, so that's how I have access to get(). 2.) I'm creating a byte buffer size of 1024 * 100 in order to not let the readAvailable method block for too long, though this might not be necessary... the one nice thing about it is that it determines how frequently you will be publishing your progress updates.

How can I download a large file with Ktor and Kotlin with a progress indicator?

I've been spending way too much time trying to solve this problem. So the code that I posted below does work in terms of downloading a file, but the problem is, the flow has a very unexpected behaviour. The response.content.readAvailable() method call seems to block until it's completely done downloading the whole file at which point the emit progress happens, so you end up waiting a long time for the file to download, and then in a split second you get all of the progress updates. So I'm wondering if there is a way to do this where I read in a certain number of bytes at a time and then emit a progress and then repeat until the file is done downloading? Or maybe a way to hook into the readAvailable() method and update the progress that way? Any help with this would be greatly appreciated.
Here's the code I found and modified, but still does not work right:
suspend fun HttpClient.downloadFile(
output: File,
downloadUrl: String,
md5Hash: String,
) = flow {
try {
val response = get<HttpResponse> { url(downloadUrl) }
val data = ByteArray(response.contentLength()?.toInt() ?: 0)
val contentLn = response.contentLength()?.toInt() ?: 0
var offset = 0
var bytesRemaining = contentLn
do {
val chunkSize = min(maxChunkSize, bytesRemaining)
logger?.d { "Read Available:" }
val result = response.content.readAvailable(data, offset, length = chunkSize)
val progress = ((offset / contentLn.toDouble()) * 100).toInt()
emit(DownloadResult.Progress(progress))
logger?.d { "logged progress: $progress" }
// delay(6000L) this was to test my assumption that the readAvalible was blocking.
offset += chunkSize
bytesRemaining -= chunkSize
} while (result != -1)
if (response.status.isSuccess()) {
if (data.md5().hex == md5Hash) {
output.write(data)
emit(DownloadResult.Success)
} else {
emit(DownloadResult.ErrorCorruptFile)
}
} else {
emit(DownloadResult.ErrorBadResponseCode(response.status.value))
}
} catch (e: TimeoutCancellationException) {
emit(DownloadResult.ErrorRequestTimeout("Connection timed out", e))
}
}
Finally after a stupid amount of time I solved this. What you need to use is this. That gives you access to the byte channel as it is downloading.
and a very crude implementation (that I'm not yet done with) is this:
get<HttpStatement>(url = downloadUrl).execute {
var offset = 0
val byteBufferSize = 1024 * 100
val channel = it.receive<ByteReadChannel>()
val contentLen = it.contentLength()?.toInt() ?: 0
val data = ByteArray(contentLen)
do {
val currentRead = channel.readAvailable(data, offset, byteBufferSize)
val progress = if(contentLen == 0) 0 else ( offset / contentLen.toDouble() ) * 100
logger?.d { "progress: $progress" }
offset += currentRead
} while (currentRead >= 0)
}
two things to not with this solution. 1.) I'm in the context of HttpClient, so that's how I have access to get(). 2.) I'm creating a byte buffer size of 1024 * 100 in order to not let the readAvailable method block for too long, though this might not be necessary... the one nice thing about it is that it determines how frequently you will be publishing your progress updates.

How to change encoding to a downloaded file Kotlin

I'm making an app which will download a .zip file, unzip it and finally take the .srt file (which was zipped) and put it as subtitle file in exoplayer movie.
My problem
The file has greek letters inside and when I put it as a subtitle file I only see corrupted letters
What have I tried
Below is the code to unzip the file
fun writeZipAndUnZip(subtitlesLinks: ArrayList<String>, context: Context, body: ResponseBody?) {
if (body == null) { return }
val file = File(getSubsDirectory(), subtitlesLinks.first())
var inputStream: InputStream? = null
var outputStream: OutputStream? = null
try {
val fileReader = ByteArray(4096)
inputStream = body.byteStream()
outputStream = FileOutputStream(file)
while (true) {
var read = inputStream.read(fileReader)
if (read == -1) {
break
}
outputStream.write(fileReader, 0, read)
}
outputStream.flush()
unzip(context)
} catch (e: IOException) {
return
} finally {
inputStream?.close()
outputStream?.close()
}}
fun unzip(context: Context) {
val unzipPath = getSubsDirectory()
var count: Int
val buffer = ByteArray(4096)
val subFile = File(getSubsDirectory()).listFiles()?.first() ?: return
try {
val stream = FileInputStream(subFile.absolutePath)
ZipInputStream(stream).use { zis ->
var ze = ZipEntry("")
while (zis.nextEntry?.also { ze = it } != null) {
var fileName = ze.name
fileName = fileName.substring(fileName.indexOf("/") + 1)
val file = File(unzipPath, fileName)
val dir = if (ze.isDirectory) file else file.parentFile
if (!dir.isDirectory && !dir.mkdirs())
throw FileNotFoundException("Invalid path: " + dir.absolutePath)
if (ze.isDirectory) continue
val fileOutput = FileOutputStream(file)
try {
while (zis.read(buffer).also { count = it } != -1)
fileOutput.write(buffer, 0, count)
} catch (e: IOException) {
Timber.tag("ErrorZip").d(e.localizedMessage)
} finally {
val fileOutput = context.openFileOutput(fileName, Context.MODE_PRIVATE)
fileOutput.close()
}
}
}
} catch (e: IOException) {
Timber.tag("ErrorZip").d(e.localizedMessage)
}}
Here is the code I put subtitle in exoplayer
val subtitleFormat = Format.createTextSampleFormat(null, MimeTypes.APPLICATION_SUBRIP, Format.NO_VALUE, "el")
val subtitleSource = SingleSampleMediaSource(File(Utils.getSubsDirectory()).listFiles()!![1].toUri(), dataSourceFactory, subtitleFormat, C.TIME_UNSET)
mediaSource = MergingMediaSource(buildMediaSource(videoUrl), subtitleSource)
I also tried to resave the subtitle with Utf-8 encoding but I get some characters in greek and the most missing or incorrect using this code
fun encode() {
val charset = "UTF8"
val subFile = File(getSubsDirectory()).listFiles()!![1] ?: return
val inputStream: InputStream = subFile.absoluteFile.inputStream()
val inputString = inputStream.bufferedReader().use { it.readText() }
val writer = OutputStreamWriter(FileOutputStream(File(getSubsDirectory(), "subs.srt")), charset)
writer.write(inputString)
writer.close()
}
Probably the file you are trying to read is in a different encoding than the System default (Android uses utf-8). You need to read the file at the 'correct' encoding format (for Greek usually is Windows-1253) and then save it to utf-8.