diff --git a/app/src/main/java/de/ccc/events/badge/card10/Constants.kt b/app/src/main/java/de/ccc/events/badge/card10/Constants.kt index 18f2f1e9525067faebf65773b31f46ba7951c29c..9131b6115cff996c1b250468054bc73ddd3a2ac2 100644 --- a/app/src/main/java/de/ccc/events/badge/card10/Constants.kt +++ b/app/src/main/java/de/ccc/events/badge/card10/Constants.kt @@ -32,6 +32,7 @@ val DEVICE_NAME_CHARACTERISTIC_UUID = UUID.fromString("00002a00-0000-1000-8000-0 val CARD10_SERVICE_UUID = UUID.fromString("42230200-2342-2342-2342-234223422342") val VIBRA_CHARACTERISTIC_UUID = UUID.fromString("4223020f-2342-2342-2342-234223422342") val ROCKETS_CHARACTERISTIC_UUID = UUID.fromString("42230210-2342-2342-2342-234223422342") +val LEDS_ABOVE_CHARACTERISTIC_UUID = UUID.fromString("42230220-2342-2342-2342-234223422342") val SINGLE_LED_CHARACTERISTIC_UUID = UUID.fromString("42230211-2342-2342-2342-234223422342") val LIGHT_SENSOR_CHARACTERISTIC_UUID = UUID.fromString("422302f0-2342-2342-2342-234223422342") val TIME_CHARACTERISTIC_UUID = UUID.fromString("42230201-2342-2342-2342-234223422342") diff --git a/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt index f4580df0c18bed77f03f4a442088ca494b4ada1a..a9aa628f7874a1a465dbabf6e708570868e5d27f 100644 --- a/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt +++ b/app/src/main/java/de/ccc/events/badge/card10/main/MainFragment.kt @@ -36,6 +36,7 @@ import de.ccc.events.badge.card10.common.ConnectionService import de.ccc.events.badge.card10.hatchery.AppListFragment import de.ccc.events.badge.card10.mood.MoodFragment import de.ccc.events.badge.card10.scanner.ScannerFragment +import de.ccc.events.badge.card10.sparkle.BeautifulFragment import kotlinx.android.synthetic.main.main_fragment.* class MainFragment : Fragment() { @@ -64,6 +65,7 @@ class MainFragment : Fragment() { button_pair.setOnClickListener { startFragment(ScannerFragment()) } button_mood.setOnClickListener { startFragment(MoodFragment()) } + button_beautiful.setOnClickListener { startFragment(BeautifulFragment()) } button_hatchery.setOnClickListener { startFragment(AppListFragment()) } } diff --git a/app/src/main/java/de/ccc/events/badge/card10/sparkle/BeautifulFragment.kt b/app/src/main/java/de/ccc/events/badge/card10/sparkle/BeautifulFragment.kt new file mode 100644 index 0000000000000000000000000000000000000000..fbf7fc4dff93c26fe85bc14a22ec93bd70304d8c --- /dev/null +++ b/app/src/main/java/de/ccc/events/badge/card10/sparkle/BeautifulFragment.kt @@ -0,0 +1,105 @@ +/* + * Copyright by the original author or authors. + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package de.ccc.events.badge.card10.sparkle + +import android.bluetooth.* +import android.os.Bundle +import android.os.Handler +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import de.ccc.events.badge.card10.CARD10_BLUETOOTH_MAC_PREFIX +import de.ccc.events.badge.card10.CARD10_SERVICE_UUID +import de.ccc.events.badge.card10.LEDS_ABOVE_CHARACTERISTIC_UUID +import de.ccc.events.badge.card10.R +import java.util.concurrent.CountDownLatch +import kotlin.random.Random + +class BeautifulFragment : Fragment(), Runnable { + + private val bluetoothAdapter = BluetoothAdapter.getDefaultAdapter() + private lateinit var gatt: BluetoothGatt + private var ledsAboveCharacteristic: BluetoothGattCharacteristic? = null + private @Volatile var writeLatch: CountDownLatch? = null + private val handler = Handler() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + val callback = object : BluetoothGattCallback() { + override fun onConnectionStateChange(gatt: BluetoothGatt, status: Int, newState: Int) { + println("===== onConnectionStateChange ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status} ${if (newState == BluetoothProfile.STATE_CONNECTED) "CONNECTED" else if (newState == BluetoothProfile.STATE_DISCONNECTED) "DISCONNECTED" else newState}") + if (newState == BluetoothGatt.STATE_CONNECTED) + gatt.requestMtu(64) + } + + override fun onMtuChanged(gatt: BluetoothGatt, mtu: Int, status: Int) { + println("===== onMtuChanged ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status} ${mtu}") + gatt.discoverServices() + } + + override fun onServicesDiscovered(gatt: BluetoothGatt, status: Int) { + println("===== onServicesDiscovered ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status}") + val card10Service = gatt.getService(CARD10_SERVICE_UUID) + ledsAboveCharacteristic = card10Service.getCharacteristic(LEDS_ABOVE_CHARACTERISTIC_UUID) + handler.post(this@BeautifulFragment) + } + + override fun onCharacteristicWrite( + gatt: BluetoothGatt, + characteristic: BluetoothGattCharacteristic, + status: Int + ) { + println("=== onCharacteristicWrite ${gatt} ${if (status == BluetoothGatt.GATT_SUCCESS) "SUCCESS" else status} ${characteristic.uuid} ${characteristic.value}") + writeLatch?.countDown(); + } + } + + val remoteDevices = + bluetoothAdapter.bondedDevices.filter { it.address.startsWith(CARD10_BLUETOOTH_MAC_PREFIX, true) } + if (remoteDevices.isEmpty()) + activity!!.finish() + gatt = remoteDevices.get(0).connectGatt(activity, false, callback) + } + + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + return inflater.inflate(R.layout.beautiful_fragment, container, false) + } + + override fun run() { + ledsAboveCharacteristic!!.value = Random.nextBytes(33); + writeLatch = CountDownLatch(1) + val init = gatt.writeCharacteristic(ledsAboveCharacteristic) + if (!init) + println("=== Failed to initiate writing GATT attribute") + writeLatch!!.await(); + handler.postDelayed(this, 100) + } + + override fun onDestroy() { + handler.removeCallbacksAndMessages(null) + gatt.close(); + super.onDestroy() + } +} diff --git a/app/src/main/res/layout/beautiful_fragment.xml b/app/src/main/res/layout/beautiful_fragment.xml new file mode 100644 index 0000000000000000000000000000000000000000..67008c8e4d2b49a5d1e3c8a8ce5441ca4951eec7 --- /dev/null +++ b/app/src/main/res/layout/beautiful_fragment.xml @@ -0,0 +1,14 @@ +<?xml version="1.0" encoding="utf-8"?> +<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" + xmlns:app="http://schemas.android.com/apk/res-auto" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:padding="@dimen/activity_padding"> + <TextView android:layout_width="wrap_content" android:layout_height="wrap_content" + android:text="Sparkling your LEDs, press back to stop…" + android:layout_marginTop="8dp" + app:layout_constraintTop_toTopOf="parent" android:layout_marginBottom="8dp" + app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" + android:layout_marginStart="8dp" app:layout_constraintEnd_toEndOf="parent" + android:layout_marginEnd="8dp"/> +</androidx.constraintlayout.widget.ConstraintLayout> diff --git a/app/src/main/res/layout/main_fragment.xml b/app/src/main/res/layout/main_fragment.xml index 09e55d4910150e1c3cb65f0196e80dfcc6068e66..04ffb99f50b5f19976d426e7296f1475573dfaa3 100644 --- a/app/src/main/res/layout/main_fragment.xml +++ b/app/src/main/res/layout/main_fragment.xml @@ -54,11 +54,22 @@ android:layout_marginTop="@dimen/main_label_margin" android:id="@+id/button_mood" android:text="@string/main_button_mood" + app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintRight_toRightOf="parent" /> + <Button android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:layout_marginTop="@dimen/main_label_margin" + android:id="@+id/button_beautiful" + android:text="Beautiful" + app:layout_constraintTop_toBottomOf="@id/button_mood" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + /> </androidx.constraintlayout.widget.ConstraintLayout> <androidx.constraintlayout.widget.ConstraintLayout android:layout_width="match_parent"