How to send and receive strings through TCP connection using kotlin - android

I have a TCP Server on Windows, and I want to send and receive text strings between the server and my Android device.
I spent alot of time searching for an example using Kotlin but I didn't find any useful code, so I'm now only able to create the socket and connect.
fun connect() {
try{
val soc = Socket("192.168.1.5", 1419)
val dout = DataOutputStream(soc.getOutputStream())
dout.writeUTF("1")
dout.flush()
dout.close()
soc.close()
}
catch (e:Exception){
e.printStackTrace()
}
}

You can check this simple example. Hope it'll help you!
Server:
fun main() {
val server = ServerSocket(9999)
println("Server running on port ${server.localPort}")
val client = server.accept()
println("Client connected : ${client.inetAddress.hostAddress}")
val scanner = Scanner(client.inputStream)
while (scanner.hasNextLine()) {
println(scanner.nextLine())
break
}
server.close()
}
Client:
fun main() {
val client = Socket("127.0.0.1", 9999)
client.outputStream.write("Hello from the client!".toByteArray())
client.close()
}

You can also do it with ktor, it's a kotlin based asynchronous framework. It uses coroutines natively which allow concurrency.
Use Kotlin 1.4 and ktor 1.6.0, add it to your build.gradle.kts:
plugins {
kotlin("jvm") version "1.4.32"
}
dependencies {
implementation("io.ktor:ktor-server-netty:1.6.0")
implementation("io.ktor:ktor-network:1.6.0")
}
Then you can use the sockets, it's still a bit experimental but it's getting there, with newer version ktor-network is now necessary.
Here is the code:
Server:
suspend fun server() {
val server = aSocket(ActorSelectorManager(Executors.newCachedThreadPool().asCoroutineDispatcher())).tcp()
.bind(InetSocketAddress("127.0.0.1", 2323))
println("Server running: ${server.localAddress}")
val socket = server.accept()
println("Socket accepted: ${socket.remoteAddress}")
val input = socket.openReadChannel()
val output = socket.openWriteChannel(autoFlush = true)
val line = input.readUTF8Line()
println("received '$line' from ${socket.remoteAddress}")
output.writeFully("$line back\r\n".toByteArray())
}
Client:
suspend fun client() {
val socket = aSocket(ActorSelectorManager(Dispatchers.IO)).tcp()
.connect(InetSocketAddress("127.0.0.1", 2323))
val input = socket.openReadChannel()
val output = socket.openWriteChannel(autoFlush = true)
output.writeFully("hello\r\n".toByteArray())
println("Server said: '${input.readUTF8Line()}'")
}
Run them both:
fun main() {
CoroutineScope(Dispatchers.Default).launch { server() }
runBlocking { client() }
}
When you run them, the client will send a message, the server will respond and you should see something like this:
Server running: /127.0.0.1:2323
Socket accepted: /127.0.0.1:56215
received 'hello' from /127.0.0.1:56215
Server said: 'hello back'
Find more example on their documentation simple echo server

There are 2 important things based on my experiment:
get permission in AndroidManifest.xml
<uses-permission android:name="android.permission.INTERNET" />
create the socket from a background thread, the following works for me:
Executors.newSingleThreadExecutor().execute {
val socket = Socket("192.168.0.15", 50000)
val scanner = Scanner(socket.getInputStream())
val printWriter = PrintWriter(socket.getOutputStream())
while (scanner.hasNextLine()) {
Log.d(TAG, "${ scanner.nextLine() }")
}
}
This is the source code in GitHub.
There is a video of my experiment.

Related

coroutines behave differently on Android/JVM (for windows)

I'm creating a second version of an Android/Windows remote control app using kotlin for client and server. I'm familiar with the basics of coroutines on android but to my surprise/unfortunateness, coroutines have behaved differently on the two platforms. I tested the EXACT same code and on android it just works but on the computer nothing happens
Main.kt
fun main(): Unit = runBlocking {
CoroutineScope(Job()).launch {
println("connecting server")
Servidor.ligar()
}
CoroutineScope(Job()).launch {
println("connecting client")
Cliente.ligar()
}
}
Server.kt
object Servidor {
suspend fun ligar() = withContext(Dispatchers.IO) {
val porta = 1777
var mensagem: String
val server = ServerSocket(porta)
println("Trying to connect through $porta...")
val client = server.accept()
val mPrintWriter = PrintWriter(client.getOutputStream(), true)
val br = BufferedReader(InputStreamReader(client.getInputStream()))
println("Connected by port: $porta")
while (br.readLine().also { mensagem = it } != null) {
println("The message: $mensagem")
if (mensagem == "bye") {
mPrintWriter.println("bye")
break
} else {
mensagem = "Server returns $mensagem"
mPrintWriter.println(mensagem)
}
}
mPrintWriter.close()
br.close()
client.close()
}
}
Client.kt
object Cliente {
suspend fun ligar() = withContext(Dispatchers.IO) {
val portNumber = 1777
var str = "initialize"
val mSocket = Socket(InetAddress.getLocalHost(), portNumber)
val br = BufferedReader(InputStreamReader(mSocket.getInputStream()))
val pw = PrintWriter(mSocket.getOutputStream(), true)
pw.println(str)
println(str)
while (br.readLine().also { str = it } != null) {
println(str)
pw.println("bye")
if (str == "bye") break
}
br.close()
pw.close()
mSocket.close()
}
}
The code for client/server connection works perfectly, when executed from an activity in android.
This is the output from Logcat when the code is run on android (Android Studio):
connecting server
connecting client
Trying to connect through 1777...
initialize
Connected by port: 1777
The message: initialize
Server returns initialize
The message: bye
bye
This is the Logcat output when the code is run on windows (Intellij IDEA)
connecting server
calling customer
Process finished with exit code 0
I'm clearly not knowing how to deal with coroutines outside the android environment, how can i make this work as intended?
Ps: Here is the list of libraries i'm using on desktop version on app:
org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4
org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4
You are not waiting for your launched coroutines. When you are in a runBlocking, it only blocks to wait for it’s launched children. But you are creating new CoroutineScopes for launching coroutines, so they are not children of the runBlocking coroutine. They are fired off asynchronously. Then runBlocking returns and your app terminates immediately.

POS doesn't start printing

I am building an Android Application that helps users to print a receipt. What I've done so far:
Right now, I can find the device/printer(DATECS DP-25) through Bluetooth and connect it to the printer. The connection flow works perfectly. Problem is that my printer doesn't start printing at all with the commands that I've introduced so far.
Probably commands are wrong, or I don't really know exactly what is the problem. I tried a lot of apps but none of them with success in printing. Just one app from PlayStore it's working but no access to the code of that app.
Code for the printing flow:
Initializing variables:
private suspend fun init(): Boolean {
return withContext(Dispatchers.IO) {
return#withContext try {
uuidSting = UUID.fromString("00001101-0000-1000-8000-00805f9b34fb")
bluetoothSocket = bluetoothDevice.createRfcommSocketToServiceRecord(uuidSting)
bluetoothSocket.connect()
outputStream = bluetoothSocket.outputStream
inputStream = bluetoothSocket.inputStream
printWriter = PrintWriter(outputStream.bufferedWriter(Charsets.ISO_8859_1))
true
} catch (e: Exception) {
println(e.stackTraceToString())
false
}
}
}
Print function:
fun print(text: String){
printerController.apply {
initPrinter()
patchText(text)
nextLine()
nextLine()
}
}
#Throws(IOException::class)
fun initPrinter() {
printWriter.write(0x1B)
printWriter.write(0x40)
printWriter.flush()
}
#Throws(IOException::class)
fun patchText(text: String) {
printWriter.println(text)
printWriter.flush()
}
#Throws(IOException::class)
fun nextLine() {
printWriter.write("\n")
printWriter.flush()
}
I would be more than happy if anybody can help me with some advice or directions.

accessing aidl to consume the custom service apis + android

There was a service in framework level, they are binding and starting that service from the framework only. I need to access that service and consume those APIs from the client (Android) side. I have gone through the most of the examples which are having code for create/start service connection from the client and whenever service connected in that connected listener using IBinder, we can access those APIs.
I tried, like adding aidl file at client side with the same package structure and added following code.
val DevManager =applicationContext.getSystemService("servicename")
val clazz = Class.forName(DevManager.javaClass.name)
val method = DevManager.javaClass.getDeclaredMethod(
"getIDevManager",
Context::class.java
)
method.isAccessible = true
val DevService: IDevManager =
method.invoke(DevManager) as IDevManager
var status = DevManager.devStatus
We are getting NoSuchMethodException.
Please suggest how can we achieve this, thanks in advance.
try {
val testManager = TestApplication.context!!.getSystemService(SERVICE_NAME)
val status = testManager.javaClass.getDeclaredMethod("methodName")
status.isAccessible = true
result = (status.invoke(testManager)).toString()
} catch (e: Exception) {
Log.d(TAG, " exception: ${e.message}")
e.printStackTrace()
}

Unit test fail with Error(MockKException) for MockResponse()

I have a Client that will make a request to a GraphQL end point as below
import com.apollographql.apollo.ApolloClient
internal class GraphQLClient(apolloClient:ApolloClient, retryStrategy:RetryStrategy){
override fun <D : Operation.Data, T, V : Operation.Variables> mutate(mutate: Mutation<D, T, V>): Flow<T> {
val response = apolloClient.mutate(mutate)
val responseFlow = response.toFlow()
return responseFlow.map<Response<T>, T> {
it.data ?: throw ResponseError("Data Null")
}.retryWhen { cause, attempt ->
retryStrategy.retry(cause,attempt)
}
}
}
I'm testing the above using MockWebServer to create mock responses
JUnit test
Using Turbine to test Flow
I'm trying to validate that for a successful update request the retry logic doesn't get executed
#Test
fun `GIVEN a successful update, THEN don't retry`() = runBlocking {
val server = MockWebServer()
val mockResponse = MockResponse()
//Successful response in json format. It's the correct format.
mockResponse.setBody(readFileFromResources("mock_success_response.json"))
mockResponse.setResponseCode(200)
server.enqueue(mockResponse)
server.start()
val url = server.url("http://loalhost:8080")
val apolloClient: ApolloClient = ApolloClient.builder()
.okHttpClient(OkHttpClient())
.serverUrl(url.toString())
.addCustomAdapters()
.build()
val retryStrategy = mockk<RetryStrategy>()
val graphQLClient = GraphQLClient(apolloClient)
//The mutation of intrest
val mutation = UpdateMutation(
SomeInput(
"123"
)
)
//note how i haven't mocked anything related to retry strategy cause this test doesn't need that
graphQLClient.mutate(mutation).test {
verify(exactly = 0) { retryStrategy.retry(any(),any()) }
expectComplete()
}
server.shutdown()
}
However, my test fails with
app.cash.turbine.AssertionError: Expected complete but found Error(MockKException)
Further down the stack trace, I can see the complaint over the lack of answer for some re-try logic related things
But i think that's a cause of the above exception being thrown and in reality, shouldn't even be executed
Caused by: io.mockk.MockKException: no answer found for: RetryStrategy(#1).isError(com.apollographql.apollo.exception.ApolloNetworkException: Failed to execute http call)
P:S- I'm probably testing a little too much here too but keen to understand what's going on
Things I've tried
Just changed the response to an empty string if that has an impact but no change in error. Which makes me think it probably has nothing to do with the response data,
Thanks
The problem was with this line
val url = server.url("http://loalhost:8080")
MockServer is not expecting host or port
val url = server.url("/somepath")

How to take screenshots and send them by e-mail as part of a Continuous Integration process?

I'm developing an Android Library Module which is highly customizable in terms of UI. It would be really nice to have a script or some sort of automated process that takes screen shots of the running app, concatenate them and send by e-mail - so then I could quickly check if some change has messed with some UI component and/or have the most recent assets to update library READ-ME.
Any idea on how this could be performed?
My current idea
So far I've thought in adding code to programmatically take SS, store them on a temporary folder and, when all images has been collected, send them via some REST API to a server. I'd like to know if there is a better way to do that.
I ended up following my initial idea:
Based on this answer I've implemented a method that takes screenshots;
Base on this answer, I've implemented the API JavaMail capable of sending e-mails without the need of user interaction;
The combination of 1 and 2 can be found on my util library kotlin-components
Finally I've implemented UI tests that enters the desired state, takes the screen shots - saving them on external SD card - and, on the last step, it adds the SS as e-mail attachments sending to whatever I want to:
#RunWith(AndroidJUnit4::class)
#LargeTest
class UITestSearchSamples {
companion object {
private val SCREENSHOTS_DIRECTORY = "search-interface"
private val TIME_OUT = 3000L
private val WAITING_TIME = 1000L
#get:ClassRule
var disableAnimationsRule = DisableAnimationsRule()
}
private var finished = false
#get:Rule
var mActivityRule = ActivityTestRule(ActivityHomepage::class.java)
private var mMonitor: Instrumentation.ActivityMonitor? = null
#Before
fun setup() {
setWaitingPolice()
mMonitor = getInstrumentation().addMonitor(ActivitySearch::class.java.name, null, false)
}
private fun performWaitingTime() {
val idlingResource = ElapsedTimeIdlingResource(WAITING_TIME)
Espresso.registerIdlingResources(idlingResource)
}
private fun setWaitingPolice() {
IdlingPolicies.setMasterPolicyTimeout(TIME_OUT, TimeUnit.MILLISECONDS);
IdlingPolicies.setIdlingResourceTimeout(TIME_OUT, TimeUnit.MILLISECONDS);
}
#After
fun tearDown() {
closeSoftKeyboard()
performWaitingTime()
val activitySearch = getInstrumentation().waitForMonitorWithTimeout(mMonitor, TIME_OUT) as AppCompatActivity
activitySearch.takeScreenShot(location = DirectoryPath.EXTERNAL, path = SCREENSHOTS_DIRECTORY, openScreenShot = false, showToast = false)
activitySearch.finish()
if (finished) {
sendNotificationEmail(activitySearch)
}
}
private fun sendNotificationEmail(activitySearch: AppCompatActivity) {
try {
val sender = Sender("sender_email", "sender_password")
val email = Email(
"Hello world: SMTP Server from Android with Attachments",
"This is a sample e-mail sent via SMTP server from Android without the need of user interaction.",
mutableListOf("recipient_01", "recipient_02"),
File("${DirectoryPath.EXTERNAL.getValue(activitySearch)}/search-interface").listFiles()
)
activitySearch.sendEmail(sender, email)
} catch (e: Exception) {
Log.e("SENDER E-MAIL SLAVE", e.message, e)
}
}
#Test
fun launchSample01() {
onView(withId(R.id.btn_sample_01)).perform(click())
onView(withId(R.id.input)).perform(typeText("Diana"))
}
#Test
fun launchSample02() {
onView(withId(R.id.btn_sample_02)).perform(click())
onView(withId(R.id.input)).perform(typeText("Clark"))
}
#Test
fun launchSample03() {
onView(withId(R.id.btn_sample_03)).perform(click())
onView(withId(R.id.input)).perform(typeText("Diana"))
onView(withId(R.id.wrapper)).perform(click())
performWaitingTime()
onView(withId(R.id.input)).perform(typeText("a"))
finished = true
}
}

Categories

Resources