You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

512 lines
21 KiB
Kotlin

package com.rookiedev.hexapod
import android.annotation.SuppressLint
import android.app.AlertDialog
import android.content.Context
import android.content.DialogInterface
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.view.*
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import com.rookiedev.hexapod.network.BluetoothClient
import com.rookiedev.hexapod.network.TCPClient
import kotlinx.coroutines.*
import kotlin.math.PI
import kotlin.math.atan2
import kotlin.math.pow
import kotlin.math.sqrt
/**
* Behaviors of immersive mode.
*/
enum class BehaviorOption(
val title: String,
val value: Int
) {
// Swipe from the edge to show a hidden bar. Gesture navigation works regardless of visibility
// of the navigation bar.
Default(
"BEHAVIOR_DEFAULT",
WindowInsetsController.BEHAVIOR_DEFAULT
),
// "Sticky immersive mode". Swipe from the edge to temporarily reveal the hidden bar.
ShowTransientBarsBySwipe(
"BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE",
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
)
}
/**
* Type of system bars to hide or show.
*/
enum class TypeOption(
val title: String,
val value: Int
) {
// Both the status bar and the navigation bar
SystemBars(
"systemBars()",
WindowInsets.Type.systemBars()
),
// The status bar only.
StatusBar(
"statusBars()",
WindowInsets.Type.statusBars()
),
// The navigation bar only
NavigationBar(
"navigationBars()",
WindowInsets.Type.navigationBars()
)
}
class ControlActivity : AppCompatActivity() {
companion object {
private const val CMD_STANDBY = "standby:"
private const val CMD_LAYDOWN = "laydown:"
private const val CMD_FORWARD = "forward:"
private const val CMD_BACKWARD = "backward:"
private const val CMD_FASTFORWARD = "fastforward:"
private const val CMD_FASTBACKWARD = "fastbackward:"
private const val CMD_SHIFTLEFT = "shiftleft:"
private const val CMD_SHIFTRIGHT = "shiftright:"
private const val CMD_TURNLEFT = "turnleft:"
private const val CMD_TURNRIGHT = "turnright:"
private const val CMD_CLIMBFORWARD = "climbforward:"
private const val CMD_CLIMBBACKWARD = "climbbackward:"
private const val CMD_ROTATEX = "rotatex:"
private const val CMD_ROTATEY = "rotatey:"
private const val CMD_ROTATEZ = "rotatez:"
private const val CMD_TWIST = "twist:"
}
private var width = 0
private var height = 0
private var radius = 0f
private var connectInterface: String = ""
private var mContext: Context? = null
private var tcpClient: TCPClient? = null
private var ip: String = ""
private var port = 0
private var btClient: BluetoothClient? = null
private var mac: String = ""
private val scope = CoroutineScope(Job() + Dispatchers.IO)
private var currentState: String = CMD_STANDBY
private lateinit var progressBar: ConstraintLayout
private var controlImage: ImageView? = null
private var buttonRotateX: Button? = null
private var buttonRotateY: Button? = null
private var buttonRotateZ: Button? = null
private var buttonClimb: Button? = null
private var buttonTwist: Button? = null
@SuppressLint("ClickableViewAccessibility")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_control)
val myIntent = intent // gets the previously created intent
mContext = applicationContext
connectInterface = myIntent.getStringExtra("interface").toString()
if (connectInterface == "WiFi") {
ip = myIntent.getStringExtra("ip").toString()
port = myIntent.getStringExtra("port").toString().toInt()
} else if (connectInterface == "Bluetooth") {
mac = myIntent.getStringExtra("mac").toString()
}
controlWindowInsets(true)
controlImage = findViewById<ImageView>(R.id.control_image)
progressBar = findViewById<ConstraintLayout>(R.id.progressBar)
buttonRotateX = findViewById(R.id.button_rotatex)
buttonRotateY = findViewById(R.id.button_rotatey)
buttonRotateZ = findViewById(R.id.button_rotatez)
buttonClimb = findViewById(R.id.button_climb)
buttonTwist = findViewById(R.id.button_twist)
val vto: ViewTreeObserver = controlImage!!.viewTreeObserver
vto.addOnPreDrawListener(object : ViewTreeObserver.OnPreDrawListener {
override fun onPreDraw(): Boolean {
controlImage!!.viewTreeObserver.removeOnPreDrawListener(this)
height = controlImage!!.measuredHeight
width = controlImage!!.measuredWidth
radius = width.coerceAtMost(height) / 2f
return true
}
})
controlImage!!.setOnTouchListener(
object : View.OnTouchListener {
override fun onTouch(view: View, motionEvent: MotionEvent): Boolean {
val touchX = motionEvent.x
val touchY = motionEvent.y
if (touchX < 0) {
return false
}
if (touchY < 0) {
return false
}
val coorX = touchX - width / 2
val coorY = touchY - height / 2
val length = sqrt(coorX.pow(2) + coorY.pow(2))
if (length < radius / 3) {
if (currentState != CMD_STANDBY) {
sendMessageAsync(CMD_STANDBY)
currentState = CMD_STANDBY
controlImage!!.setImageResource(R.drawable.ic_control_circle_standby)
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
}
} else if (length >= radius / 3 && length < 2 * radius / 3) {
val angle = atan2(coorY, coorX)
if (angle > -PI / 4 && angle <= PI / 4) {
if (currentState != CMD_SHIFTRIGHT) {
sendMessageAsync(CMD_SHIFTRIGHT)
currentState = CMD_SHIFTRIGHT
controlImage!!.setImageResource(R.drawable.ic_control_circle_right)
}
} else if (angle > PI / 4 && angle <= 3 * PI / 4) {
if (currentState != CMD_BACKWARD) {
sendMessageAsync(CMD_BACKWARD)
currentState = CMD_BACKWARD
controlImage!!.setImageResource(R.drawable.ic_control_circle_backward)
}
} else if (angle > -3 * PI / 4 && angle < -PI / 4) {
if (currentState != CMD_FORWARD) {
sendMessageAsync(CMD_FORWARD)
currentState = CMD_FORWARD
controlImage!!.setImageResource(R.drawable.ic_control_circle_forward)
}
} else {
if (currentState != CMD_SHIFTLEFT) {
sendMessageAsync(CMD_SHIFTLEFT)
currentState = CMD_SHIFTLEFT
controlImage!!.setImageResource(R.drawable.ic_control_circle_left)
}
}
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
} else if (length >= 2 * radius / 3 && length < radius) {
val angle = atan2(coorY, coorX)
if (angle > -PI / 4 && angle <= PI / 4) {
if (currentState != CMD_TURNRIGHT) {
sendMessageAsync(CMD_TURNRIGHT)
currentState = CMD_TURNRIGHT
controlImage!!.setImageResource(R.drawable.ic_control_circle_turnright)
}
} else if (angle > PI / 4 && angle <= 3 * PI / 4) {
if (currentState != CMD_FASTBACKWARD) {
sendMessageAsync(CMD_FASTBACKWARD)
currentState = CMD_FASTBACKWARD
controlImage!!.setImageResource(R.drawable.ic_control_circle_fastbackward)
}
} else if (angle > -3 * PI / 4 && angle < -PI / 4) {
if (currentState != CMD_FASTFORWARD) {
sendMessageAsync(CMD_FASTFORWARD)
currentState = CMD_FASTFORWARD
controlImage!!.setImageResource(R.drawable.ic_control_circle_fastforward)
}
} else {
if (currentState != CMD_TURNLEFT) {
sendMessageAsync(CMD_TURNLEFT)
currentState = CMD_TURNLEFT
controlImage!!.setImageResource(R.drawable.ic_control_circle_turnleft)
}
}
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
}
return true
}
}
)
buttonRotateX!!.setOnClickListener {
if (currentState != CMD_ROTATEX) {
sendMessageAsync(CMD_ROTATEX)
currentState = CMD_ROTATEX
controlImage!!.setImageResource(R.drawable.ic_control_circle)
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.purple_500)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
}
}
buttonRotateY!!.setOnClickListener {
if (currentState != CMD_ROTATEY) {
sendMessageAsync(CMD_ROTATEY)
currentState = CMD_ROTATEY
controlImage!!.setImageResource(R.drawable.ic_control_circle)
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.purple_500)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
}
}
buttonRotateZ!!.setOnClickListener {
if (currentState != CMD_ROTATEZ) {
sendMessageAsync(CMD_ROTATEZ)
currentState = CMD_ROTATEZ
controlImage!!.setImageResource(R.drawable.ic_control_circle)
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.purple_500)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
}
}
buttonClimb!!.setOnClickListener {
if (currentState != CMD_CLIMBFORWARD) {
sendMessageAsync(CMD_CLIMBFORWARD)
currentState = CMD_CLIMBFORWARD
controlImage!!.setImageResource(R.drawable.ic_control_circle)
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.purple_500)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
}
}
buttonTwist!!.setOnClickListener {
if (currentState != CMD_TWIST) {
sendMessageAsync(CMD_TWIST)
currentState = CMD_TWIST
controlImage!!.setImageResource(R.drawable.ic_control_circle)
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.purple_500)
}
}
}
@SuppressLint("MissingPermission")
override fun onResume() {
super.onResume()
progressBar.visibility = View.VISIBLE
if (connectInterface == "WiFi") {
this.tcpClient = TCPClient(ip, port, object : TCPClient.OnMessageReceived {
override fun messageReceived(message: String?) {
if (message == null) {
// alertDialog(DISCONNECTED)
println("no message")
}
}
}, object : TCPClient.OnConnectEstablished {
override fun onConnected() {
// udpClient.start()
println("connected")
Handler(Looper.getMainLooper()).post {
progressBar.visibility = View.GONE
}
}
}, object : TCPClient.OnDisconnected {
override fun onDisconnected() {
Handler(Looper.getMainLooper()).post {
progressBar.visibility = View.GONE
alertDialog(0)
}
}
}
)
this.tcpClient!!.start()
} else if (connectInterface == "Bluetooth") {
println("Bluetooth")
this.btClient = BluetoothClient(mContext, mac, object : BluetoothClient.OnMessageReceived {
override fun messageReceived(message: String?) {
if (message == null) {
// alertDialog(DISCONNECTED)
println("no message")
}
}
}, object : BluetoothClient.OnConnectEstablished {
override fun onConnected() {
// udpClient.start()
println("connected")
Handler(Looper.getMainLooper()).post {
progressBar.visibility = View.GONE
}
}
}, object : BluetoothClient.OnDisconnected {
override fun onDisconnected() {
Handler(Looper.getMainLooper()).post {
progressBar.visibility = View.GONE
alertDialog(0)
}
}
}
)
this.btClient!!.start()
}
currentState = CMD_STANDBY
controlImage!!.setImageResource(R.drawable.ic_control_circle_standby)
buttonRotateX!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateY!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonRotateZ!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonClimb!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
buttonTwist!!.backgroundTintList =
applicationContext.getColorStateList(R.color.grey_700)
}
override fun onPause() {
super.onPause()
println("on Pause")
// saveSharedPref()
if (connectInterface == "WiFi") {
tcpClient!!.stopClient()
tcpClient!!.interrupt()
}else if (connectInterface == "Bluetooth"){
btClient!!.stopClient()
btClient!!.interrupt()
}
}
private fun controlWindowInsets(hide: Boolean) {
// WindowInsetsController can hide or show specified system bars.
val insetsController = window.decorView.windowInsetsController ?: return
// The behavior of the immersive mode.
val behavior = BehaviorOption.values()[1].value
// The type of system bars to hide or show.
val type = TypeOption.values()[0].value
insetsController.systemBarsBehavior = behavior
if (hide) {
insetsController.hide(type)
} else {
insetsController.show(type)
}
}
fun sendMessageAsync(message: String) {
// Starts a new coroutine within the scope
scope.launch {
// New coroutine that can call suspend functions
withContext(Dispatchers.IO) { // Dispatchers.IO (main-safety block)
if (connectInterface == "WiFi") {
tcpClient?.sendMessage(message)
}else if(connectInterface == "Bluetooth"){
btClient?.sendMessage(message)
}
}
}
}
fun alertDialog(type: Int) {
val alert: AlertDialog = AlertDialog.Builder(this).create()
when (type) {
0 -> {
alert.setTitle("Error")
alert.setIcon(R.drawable.ic_baseline_error_24)
alert.setMessage(
"Unable to connect to the Hexapod."
)
alert.setOnCancelListener(DialogInterface.OnCancelListener { finish() })
alert.setButton(AlertDialog.BUTTON_POSITIVE,
"OK",
DialogInterface.OnClickListener { dialog, which -> finish() })
}
}
alert.show()
}
}