Drone3D-Team/Drone3D

View on GitHub
app/src/main/java/ch/epfl/sdp/drone3d/ui/drone/DroneConnectActivity.kt

Summary

Maintainability
A
0 mins
Test Coverage
A
100%
/*
 * Copyright (C) 2021  Drone3D-Team
 * The license can be found in LICENSE at root of the repository
 */

package ch.epfl.sdp.drone3d.ui.drone

import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.view.View
import android.widget.Button
import android.widget.EditText
import android.widget.ProgressBar
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
import ch.epfl.sdp.drone3d.R
import ch.epfl.sdp.drone3d.service.api.drone.DroneService
import ch.epfl.sdp.drone3d.ui.ToastHandler
import ch.epfl.sdp.drone3d.ui.Utils
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.util.regex.Matcher
import java.util.regex.Pattern
import javax.inject.Inject


@AndroidEntryPoint
class DroneConnectActivity : AppCompatActivity() {

    companion object {
        /**
         * Values used to check if an ip has a valid format or not
         */
        private const val REGEX = "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
                "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
                "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." +
                "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"
        private val pattern: Pattern = Pattern.compile(REGEX)
        private const val CONNECTION_DELAY = 3000L //in ms
    }

    @Inject
    lateinit var droneService: DroneService

    private lateinit var droneConnectButton: Button
    private lateinit var simulationConnectButton: Button
    private lateinit var loadingProgressBar: ProgressBar
    private lateinit var ipText: EditText
    private lateinit var portText: EditText
    private lateinit var waitingText: TextView
    private lateinit var divider: View

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_drone_connect)

        droneConnectButton = findViewById(R.id.connect_drone_button)
        simulationConnectButton = findViewById(R.id.connect_simulation_button)
        ipText = findViewById(R.id.text_IP_address)
        portText = findViewById(R.id.text_port)
        divider = findViewById(R.id.divider)

        loadingProgressBar = findViewById(R.id.progress_bar_drone)
        waitingText = findViewById(R.id.waiting_connection)

        Utils.pressButtonWhenTextIsDone(portText, simulationConnectButton)

        showConnectionOptions()
    }

    /**
     * Connect a simulation to the application using an ip address and a port
     */
    fun connectSimulatedDrone(view: View) {

        Utils.closeKeyboard(view, this)

        val ip = findViewById<EditText>(R.id.text_IP_address).text.toString()

        if (verifyIp(ip)) {
            val port = findViewById<EditText>(R.id.text_port).text.toString()
            showWaiting()

            //Launch this task asynchronously
            GlobalScope.launch {
                droneService.setSimulation(ip, port)
            }

            //Launch this task asynchronously
            GlobalScope.launch {

                checkIfDroneConnected(CONNECTION_DELAY)
                val mainHandler = Handler(applicationContext.mainLooper)

                val myRunnable = Runnable {
                    if (droneService.isConnected()) {
                        val intent = Intent(applicationContext, ConnectedDroneActivity::class.java)
                        startActivity(intent)
                    } else {
                        droneService.disconnect()
                        showConnectionOptions()
                        ToastHandler.showToastAsync(
                            applicationContext,
                            R.string.ip_connection_timeout
                        )
                    }
                }
                mainHandler.post(myRunnable)
            }
        } else {
            ToastHandler.showToastAsync(this, R.string.ip_format_invalid)
        }
    }

    /**
     * Connect a drone to the application
     */
    fun connectDrone(view: View) {

        Utils.closeKeyboard(view, this)

        showWaiting()
        droneService.setDrone()

        GlobalScope.launch {

            checkIfDroneConnected(CONNECTION_DELAY)
            val mainHandler = Handler(applicationContext.mainLooper)

            val myRunnable = Runnable {
                if (droneService.isConnected()) {
                    val intent = Intent(applicationContext, ConnectedDroneActivity::class.java)
                    startActivity(intent)
                } else {
                    droneService.disconnect()
                    showConnectionOptions()
                    ToastHandler.showToastAsync(applicationContext, R.string.no_drone_detected)
                }
            }
            mainHandler.post(myRunnable)
        }
    }

    /**
     * Change the view to hide connection options and show waiting connection interface
     */
    private fun showWaiting() {
        droneConnectButton.visibility = View.GONE
        simulationConnectButton.visibility = View.GONE
        ipText.visibility = View.GONE
        portText.visibility = View.GONE
        divider.visibility = View.GONE

        loadingProgressBar.visibility = View.VISIBLE
        waitingText.visibility = View.VISIBLE
    }

    /**
     * Change the view to hide waiting connection interface and show connection options
     */
    private fun showConnectionOptions() {
        droneConnectButton.visibility = View.VISIBLE
        simulationConnectButton.visibility = View.VISIBLE
        ipText.visibility = View.VISIBLE
        portText.visibility = View.VISIBLE
        divider.visibility = View.VISIBLE

        loadingProgressBar.visibility = View.GONE
        waitingText.visibility = View.GONE
    }

    /**
     * Check if a drone was connected on the application after [waitingTime] in millis
     */
    private suspend fun checkIfDroneConnected(waitingTime: Long) {
        var remainingTime = waitingTime
        val delay = 100L
        while (!droneService.isConnected() && remainingTime > 0) {
            delay(delay)
            remainingTime -= delay
        }
    }

    /**
     * Checks if an ip has a valid format or not
     */
    private fun verifyIp(ip: String): Boolean {
        val matcher: Matcher = pattern.matcher(ip)
        return matcher.matches()
    }
}