Add Enable Air option support
Use proper method to pass client address to server. Fix possible memory leak.pull/1/head
parent
815cf3cadf
commit
45bc2fa054
|
@ -13,6 +13,16 @@ class BrokenithmApplication : Application() {
|
||||||
config.edit().putString("server", value).apply()
|
config.edit().putString("server", value).apply()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var enableAir: Boolean
|
||||||
|
get() {
|
||||||
|
val config = getSharedPreferences(settings_preference, MODE_PRIVATE)
|
||||||
|
return config.getBoolean("enable_air", true)
|
||||||
|
}
|
||||||
|
set(value) {
|
||||||
|
val config = getSharedPreferences(settings_preference, MODE_PRIVATE)
|
||||||
|
config.edit().putBoolean("enable_air", value).apply()
|
||||||
|
}
|
||||||
|
|
||||||
var simpleAir: Boolean
|
var simpleAir: Boolean
|
||||||
get() {
|
get() {
|
||||||
val config = getSharedPreferences(settings_preference, MODE_PRIVATE)
|
val config = getSharedPreferences(settings_preference, MODE_PRIVATE)
|
||||||
|
|
|
@ -46,7 +46,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
private var mTestButton = false
|
private var mTestButton = false
|
||||||
private var mServiceButton = false
|
private var mServiceButton = false
|
||||||
private data class InputEvent(val keys: MutableSet<Int>? = null, val airHeight : Int = 6, val testButton: Boolean = false, val serviceButton: Boolean = false)
|
private data class InputEvent(val keys: MutableSet<Int>? = null, val airHeight : Int = 6, val testButton: Boolean = false, val serviceButton: Boolean = false)
|
||||||
private var mInputQueue = ArrayDeque<InputEvent>()
|
//private var mInputQueue = ArrayDeque<InputEvent>()
|
||||||
|
|
||||||
// LEDs
|
// LEDs
|
||||||
private lateinit var mLEDBitmap: Bitmap
|
private lateinit var mLEDBitmap: Bitmap
|
||||||
|
@ -241,26 +241,38 @@ class MainActivity : AppCompatActivity() {
|
||||||
|
|
||||||
findViewById<Button>(R.id.button_coin).setOnClickListener {
|
findViewById<Button>(R.id.button_coin).setOnClickListener {
|
||||||
if(!mExitFlag)
|
if(!mExitFlag)
|
||||||
sendFunctionKey(parseAddress(editServer.text.toString()), FUNCTION_BUTTON.FUNCTION_COIN)
|
sendFunctionKey(parseAddress(editServer.text.toString()), FunctionButton.FUNCTION_COIN)
|
||||||
}
|
}
|
||||||
findViewById<Button>(R.id.button_card).setOnClickListener {
|
findViewById<Button>(R.id.button_card).setOnClickListener {
|
||||||
if(!mExitFlag)
|
if(!mExitFlag)
|
||||||
sendFunctionKey(parseAddress(editServer.text.toString()), FUNCTION_BUTTON.FUNCTION_CARD)
|
sendFunctionKey(parseAddress(editServer.text.toString()), FunctionButton.FUNCTION_CARD)
|
||||||
}
|
}
|
||||||
findViewById<CheckBox>(R.id.check_simple_air).apply {
|
|
||||||
|
val checkSimpleAir = findViewById<CheckBox>(R.id.check_simple_air)
|
||||||
|
findViewById<CheckBox>(R.id.check_enable_air).apply {
|
||||||
|
setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
mEnableAir = isChecked
|
||||||
|
checkSimpleAir.isEnabled = isChecked
|
||||||
|
app.enableAir = isChecked
|
||||||
|
}
|
||||||
|
isChecked = app.enableAir
|
||||||
|
}
|
||||||
|
checkSimpleAir.apply {
|
||||||
setOnCheckedChangeListener { _, isChecked ->
|
setOnCheckedChangeListener { _, isChecked ->
|
||||||
mSimpleAir = isChecked
|
mSimpleAir = isChecked
|
||||||
app.simpleAir = isChecked
|
app.simpleAir = isChecked
|
||||||
}
|
}
|
||||||
isChecked = app.simpleAir
|
isChecked = app.simpleAir
|
||||||
}
|
}
|
||||||
|
mEnableAir = app.enableAir
|
||||||
|
mSimpleAir = app.simpleAir
|
||||||
|
|
||||||
findViewById<View>(R.id.button_test).setOnTouchListener { view, event ->
|
findViewById<View>(R.id.button_test).setOnTouchListener { view, event ->
|
||||||
mTestButton = when(event.actionMasked) {
|
mTestButton = when(event.actionMasked) {
|
||||||
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_DOWN -> true
|
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_DOWN -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
mInputQueue.add(InputEvent(serviceButton = mServiceButton, testButton = mTestButton))
|
//mInputQueue.add(InputEvent(serviceButton = mServiceButton, testButton = mTestButton))
|
||||||
view.performClick()
|
view.performClick()
|
||||||
}
|
}
|
||||||
findViewById<View>(R.id.button_service).setOnTouchListener { view, event ->
|
findViewById<View>(R.id.button_service).setOnTouchListener { view, event ->
|
||||||
|
@ -268,7 +280,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_DOWN -> true
|
MotionEvent.ACTION_MOVE, MotionEvent.ACTION_DOWN -> true
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
mInputQueue.add(InputEvent(serviceButton = mServiceButton, testButton = mTestButton))
|
//mInputQueue.add(InputEvent(serviceButton = mServiceButton, testButton = mTestButton))
|
||||||
view.performClick()
|
view.performClick()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,7 +394,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val socket = try {
|
val socket = try {
|
||||||
DatagramSocket(52468).apply {
|
DatagramSocket(serverPort).apply {
|
||||||
reuseAddress = true
|
reuseAddress = true
|
||||||
soTimeout = 1000
|
soTimeout = 1000
|
||||||
}
|
}
|
||||||
|
@ -497,7 +509,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class FUNCTION_BUTTON {
|
enum class FunctionButton {
|
||||||
UNDEFINED, FUNCTION_COIN, FUNCTION_CARD
|
UNDEFINED, FUNCTION_COIN, FUNCTION_CARD
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -510,33 +522,20 @@ class MainActivity : AppCompatActivity() {
|
||||||
var serviceBtn = false
|
var serviceBtn = false
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendAir(server: String, port: Int) {
|
private fun getLocalIPAddress(useIPv4: Boolean): ByteArray {
|
||||||
val address = InetSocketAddress(server, port)
|
|
||||||
val socket = DatagramSocket()
|
|
||||||
val buffer = byteArrayOf(0x04, 'A'.toByte(), 'I'.toByte(), 'R'.toByte(), if (mEnableAir) 0x01 else 0x00)
|
|
||||||
val packet = DatagramPacket(buffer, buffer.size)
|
|
||||||
socket.apply {
|
|
||||||
connect(address)
|
|
||||||
send(packet)
|
|
||||||
close()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun getLocalIPAddress(useIPv4: Boolean): String {
|
|
||||||
try {
|
try {
|
||||||
val interfaces: List<NetworkInterface> = Collections.list(NetworkInterface.getNetworkInterfaces())
|
val interfaces: List<NetworkInterface> = Collections.list(NetworkInterface.getNetworkInterfaces())
|
||||||
for (intf in interfaces) {
|
for (intf in interfaces) {
|
||||||
val addrs: List<InetAddress> = Collections.list(intf.inetAddresses)
|
val addrs: List<InetAddress> = Collections.list(intf.inetAddresses)
|
||||||
for (addr in addrs) {
|
for (addr in addrs) {
|
||||||
if (!addr.isLoopbackAddress) {
|
if (!addr.isLoopbackAddress) {
|
||||||
val sAddr = addr.hostAddress
|
val sAddr = addr.address
|
||||||
val isIPv4 = sAddr.indexOf(':') < 0
|
val isIPv4 = sAddr.size == 4
|
||||||
if (useIPv4) {
|
if (useIPv4) {
|
||||||
if (isIPv4) return sAddr
|
if (isIPv4) return sAddr
|
||||||
} else {
|
} else {
|
||||||
if (!isIPv4) {
|
if (!isIPv4) {
|
||||||
val delim = sAddr.indexOf('%') // drop ip6 zone suffix
|
return sAddr
|
||||||
return if (delim < 0) sAddr.toUpperCase(Locale.ROOT) else sAddr.substring(0, delim).toUpperCase(Locale.ROOT)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -544,17 +543,21 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
}
|
}
|
||||||
return ""
|
return byteArrayOf()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendConnect(address: InetSocketAddress?) {
|
private fun sendConnect(address: InetSocketAddress?) {
|
||||||
address ?: return
|
address ?: return
|
||||||
thread {
|
thread {
|
||||||
val selfAddress = getLocalIPAddress(true)
|
val selfAddress = getLocalIPAddress(true)
|
||||||
val buffer = ByteArray(24)
|
if (selfAddress.isEmpty()) return@thread
|
||||||
byteArrayOf(23, 'C'.toByte(), 'O'.toByte(), 'N'.toByte()).copyInto(buffer)
|
val buffer = ByteArray(21)
|
||||||
selfAddress.toByteArray().copyInto(buffer, 4)
|
byteArrayOf('C'.toByte(), 'O'.toByte(), 'N'.toByte()).copyInto(buffer, 1)
|
||||||
serverPort.toString().toByteArray().copyInto(buffer, 4 + 15)
|
ByteBuffer.wrap(buffer)
|
||||||
|
.put(4, if (selfAddress.size == 4) 1.toByte() else 2.toByte())
|
||||||
|
.putShort(5, serverPort.toShort())
|
||||||
|
selfAddress.copyInto(buffer, 7)
|
||||||
|
buffer[0] = (3 + 1 + 2 + selfAddress.size).toByte()
|
||||||
try {
|
try {
|
||||||
val socket = DatagramSocket()
|
val socket = DatagramSocket()
|
||||||
val packet = DatagramPacket(buffer, buffer.size)
|
val packet = DatagramPacket(buffer, buffer.size)
|
||||||
|
@ -596,7 +599,7 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun sendFunctionKey(address: InetSocketAddress?, function: FUNCTION_BUTTON) {
|
private fun sendFunctionKey(address: InetSocketAddress?, function: FunctionButton) {
|
||||||
address ?: return
|
address ?: return
|
||||||
thread {
|
thread {
|
||||||
val buffer = byteArrayOf(4, 'F'.toByte(), 'N'.toByte(), 'C'.toByte(), function.ordinal.toByte())
|
val buffer = byteArrayOf(4, 'F'.toByte(), 'N'.toByte(), 'C'.toByte(), function.ordinal.toByte())
|
||||||
|
@ -667,7 +670,8 @@ class MainActivity : AppCompatActivity() {
|
||||||
val realBuf = ByteArray(44)
|
val realBuf = ByteArray(44)
|
||||||
realBuf[0] = buffer.length.toByte()
|
realBuf[0] = buffer.length.toByte()
|
||||||
buffer.header.copyInto(realBuf, 1)
|
buffer.header.copyInto(realBuf, 1)
|
||||||
buffer.air.copyInto(realBuf, 4)
|
if (mEnableAir)
|
||||||
|
buffer.air.copyInto(realBuf, 4)
|
||||||
buffer.slider.copyInto(realBuf, 10)
|
buffer.slider.copyInto(realBuf, 10)
|
||||||
realBuf[42] = if (buffer.testBtn) 0x01 else 0x00
|
realBuf[42] = if (buffer.testBtn) 0x01 else 0x00
|
||||||
realBuf[43] = if (buffer.serviceBtn) 0x01 else 0x00
|
realBuf[43] = if (buffer.serviceBtn) 0x01 else 0x00
|
||||||
|
@ -684,7 +688,6 @@ class MainActivity : AppCompatActivity() {
|
||||||
private var mLastAirUpdateTime = 0L
|
private var mLastAirUpdateTime = 0L
|
||||||
private fun applyKeys(event: InputEvent, buffer: IoBuffer): IoBuffer {
|
private fun applyKeys(event: InputEvent, buffer: IoBuffer): IoBuffer {
|
||||||
return buffer.apply {
|
return buffer.apply {
|
||||||
buffer.length = 43
|
|
||||||
buffer.header = byteArrayOf('I'.toByte(), 'N'.toByte(), 'P'.toByte())
|
buffer.header = byteArrayOf('I'.toByte(), 'N'.toByte(), 'P'.toByte())
|
||||||
if (event.keys != null && event.keys.isNotEmpty()) {
|
if (event.keys != null && event.keys.isNotEmpty()) {
|
||||||
for (i in 0 until 32) {
|
for (i in 0 until 32) {
|
||||||
|
@ -692,16 +695,20 @@ class MainActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val currentTime = System.currentTimeMillis()
|
buffer.length = if (mEnableAir) {
|
||||||
if (currentTime - mLastAirUpdateTime > airUpdateInterval) {
|
val currentTime = System.currentTimeMillis()
|
||||||
mLastAirHeight += if (mLastAirHeight < mCurrentAirHeight) 1 else if (mLastAirHeight > mCurrentAirHeight) -1 else 0
|
if (currentTime - mLastAirUpdateTime > airUpdateInterval) {
|
||||||
mLastAirUpdateTime = currentTime
|
mLastAirHeight += if (mLastAirHeight < mCurrentAirHeight) 1 else if (mLastAirHeight > mCurrentAirHeight) -1 else 0
|
||||||
}
|
mLastAirUpdateTime = currentTime
|
||||||
if (mLastAirHeight != 6) {
|
|
||||||
for (i in mLastAirHeight..5) {
|
|
||||||
buffer.air[mAirIdx[i]] = 1
|
|
||||||
}
|
}
|
||||||
}
|
if (mLastAirHeight != 6) {
|
||||||
|
for (i in mLastAirHeight..5) {
|
||||||
|
buffer.air[mAirIdx[i]] = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
43
|
||||||
|
} else 37
|
||||||
|
|
||||||
buffer.serviceBtn = event.serviceButton
|
buffer.serviceBtn = event.serviceButton
|
||||||
buffer.testBtn = event.testButton
|
buffer.testBtn = event.testButton
|
||||||
}
|
}
|
||||||
|
|
|
@ -111,12 +111,12 @@
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<CheckBox
|
<CheckBox
|
||||||
android:id="@+id/check_simple_air"
|
android:id="@+id/check_enable_air"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:layout_marginTop="8dp"
|
android:layout_marginTop="8dp"
|
||||||
android:text="@string/simple_air"
|
android:text="Enable Air"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
app:layout_constraintStart_toEndOf="@+id/button_card"
|
app:layout_constraintStart_toEndOf="@+id/button_card"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
@ -168,10 +168,10 @@
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="8dp"
|
||||||
android:background="@drawable/highlight_border_1dp"
|
android:background="@drawable/highlight_border_1dp"
|
||||||
android:text="@string/test"
|
|
||||||
android:textColor="@color/white"
|
|
||||||
android:clickable="true"
|
android:clickable="true"
|
||||||
android:focusable="true"
|
android:focusable="true"
|
||||||
|
android:text="@string/test"
|
||||||
|
android:textColor="@color/white"
|
||||||
app:layout_constraintStart_toEndOf="@id/check_debug"
|
app:layout_constraintStart_toEndOf="@id/check_debug"
|
||||||
app:layout_constraintTop_toTopOf="@+id/check_debug" />
|
app:layout_constraintTop_toTopOf="@+id/check_debug" />
|
||||||
|
|
||||||
|
@ -192,7 +192,7 @@
|
||||||
android:id="@+id/check_show_delay"
|
android:id="@+id/check_show_delay"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="4dp"
|
||||||
android:text="@string/show_delay"
|
android:text="@string/show_delay"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
app:layout_constraintStart_toEndOf="@+id/button_service"
|
app:layout_constraintStart_toEndOf="@+id/button_service"
|
||||||
|
@ -202,12 +202,22 @@
|
||||||
android:id="@+id/check_vibrate"
|
android:id="@+id/check_vibrate"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_marginStart="8dp"
|
android:layout_marginStart="4dp"
|
||||||
android:text="@string/enable_vibration"
|
android:text="@string/enable_vibration"
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
app:layout_constraintStart_toEndOf="@+id/check_show_delay"
|
app:layout_constraintStart_toEndOf="@+id/check_show_delay"
|
||||||
app:layout_constraintTop_toTopOf="@+id/check_show_delay" />
|
app:layout_constraintTop_toTopOf="@+id/check_show_delay" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/check_simple_air"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginStart="4dp"
|
||||||
|
android:text="@string/simple_air"
|
||||||
|
android:textColor="@color/white"
|
||||||
|
app:layout_constraintStart_toEndOf="@+id/check_vibrate"
|
||||||
|
app:layout_constraintTop_toTopOf="@+id/check_vibrate" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/text_mode"
|
android:id="@+id/text_mode"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
|
@ -222,9 +232,9 @@
|
||||||
android:textColor="@color/white"
|
android:textColor="@color/white"
|
||||||
android:textSize="16sp"
|
android:textSize="16sp"
|
||||||
android:textStyle="bold"
|
android:textStyle="bold"
|
||||||
app:layout_constraintBottom_toBottomOf="@+id/check_vibrate"
|
app:layout_constraintBottom_toBottomOf="@+id/check_simple_air"
|
||||||
app:layout_constraintStart_toEndOf="@+id/check_vibrate"
|
app:layout_constraintStart_toEndOf="@+id/check_simple_air"
|
||||||
app:layout_constraintTop_toTopOf="@+id/check_vibrate" />
|
app:layout_constraintTop_toTopOf="@+id/check_simple_air" />
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||||
</net.cachapa.expandablelayout.ExpandableLayout>
|
</net.cachapa.expandablelayout.ExpandableLayout>
|
||||||
|
|
|
@ -11,13 +11,13 @@
|
||||||
<string name="collapse"> Control ↑ </string>
|
<string name="collapse"> Control ↑ </string>
|
||||||
<string name="test">Test</string>
|
<string name="test">Test</string>
|
||||||
<string name="service">Service</string>
|
<string name="service">Service</string>
|
||||||
<string name="show_debug_info">Show Debug Info</string>
|
<string name="show_debug_info">Debug</string>
|
||||||
<string name="debug_info">touched: air:%d %s\n%s</string>
|
<string name="debug_info">touched: air:%d %s\n%s</string>
|
||||||
<string name="current_latency">Current Latency: %f ms</string>
|
<string name="current_latency">Current Latency: %f ms</string>
|
||||||
<string name="press_again_to_exit">Press again to exit</string>
|
<string name="press_again_to_exit">Press again to exit</string>
|
||||||
<string name="show_delay">Show Delay</string>
|
<string name="show_delay">Show Delay</string>
|
||||||
<string name="address">Address</string>
|
<string name="address">Address</string>
|
||||||
<string name="enable_vibration">Enable Vibration</string>
|
<string name="enable_vibration">Vibration</string>
|
||||||
|
|
||||||
<string name="tcp">TCP</string>
|
<string name="tcp">TCP</string>
|
||||||
<string name="udp">UDP</string>
|
<string name="udp">UDP</string>
|
||||||
|
|
Loading…
Reference in New Issue