Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // Edistynyt mobiiliohjelmointi 12.4.2023
- Tehdään tänään oma MQTT-klusteri HiveMQ-palveluun, ja testataan yhteydet MQTTX:llä. Tämän jälkeen ohjelmoidaan Androidiin uusi fragment, joka osaa lähettää ja vastaanottaa tekstimuotoista dataa.
- 1. Tee Harjoitus 4 ohjeen mukaisesti HiveMQ-palveluun oma klusteri, tee klusterille käyttäjätunnus ja testaa datan liikkuminen (sekä lähetys että vastaanotto) molempiin suuntiin ohjeen mukaisella topicilla MQTTX-ohjelmassa
- 2. Tee mobile_navigation-työkalulla uusi Fragment Android-projektiin nimeltä RemoteMessageFragment (CloudMessageFragment myös ok). Lisää tämä fragment päävalikkoon, ja ota fragmentissa binding-layer käyttöön (pohja esim. DataFragmentista tai vastaavasta)
- 3. Lisää local.properties -tiedostoon tarvittavat muuttujat HiveMQ:ta varten (ei lainausmerkkejä tai välilyöntejä mihinkään arvoon):
- HIVEMQ_BROKER=oman_klusterin_osoite
- HIVEMQ_USERNAME=oman_klusterin_username
- HIVEMQ_PASSWORD=oman_klusterin_salasana
- HIVEMQ_TOPIC=koodin_käyttämä_topic
- 4. Jatketaan tästä vaiheesta eteenpäin yhdessä klo 13:30.
- // RemoteMessageFragment.kt, siivoamalla aiempaa sääasema-fragmentia, saadaan melkein samalla koodilla toimiva pohja:
- class RemoteMessageFragment : Fragment() {
- // change this to match your fragment name
- private var _binding: FragmentRemoteMessageBinding? = null
- // This property is only valid between onCreateView and
- // onDestroyView.
- private val binding get() = _binding!!
- private lateinit var client: Mqtt3AsyncClient
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- _binding = FragmentRemoteMessageBinding.inflate(inflater, container, false)
- val root: View = binding.root
- // version 3, IBM Cloud, weather station
- client = MqttClient.builder()
- .useMqttVersion3()
- .sslWithDefaultConfig()
- // käytetään samaa client id:tä tällä kertaa aina, ettei HiveMQ:n ilmainen raja tule täyteen
- .identifier("jkdghsfkd567jkafdghsw3s32")
- .serverHost(BuildConfig.HIVEMQ_BROKER)
- .serverPort(8883)
- .buildAsync()
- client.connectWith()
- .simpleAuth()
- .username(BuildConfig.HIVEMQ_USERNAME)
- .password(BuildConfig.HIVEMQ_PASSWORD.toByteArray())
- .applySimpleAuth()
- .send()
- .whenComplete { connAck: Mqtt3ConnAck?, throwable: Throwable? ->
- if (throwable != null) {
- Log.d("ADVTECH", "Connection failure.")
- } else {
- // Setup subscribes or start publishing
- subscribeToTopic()
- }
- }
- return root
- }
- fun subscribeToTopic()
- {
- client.subscribeWith()
- .topicFilter(BuildConfig.HIVEMQ_TOPIC)
- .callback { publish ->
- // this callback runs everytime your code receives new data payload
- var result = String(publish.getPayloadAsBytes())
- Log.d("ADVTECH", result)
- }
- .send()
- .whenComplete { subAck, throwable ->
- if (throwable != null) {
- // Handle failure to subscribe
- Log.d("ADVTECH", "Subscribe failed.")
- } else {
- // Handle successful subscription, e.g. logging or incrementing a metric
- Log.d("ADVTECH", "Subscribed!")
- }
- }
- }
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- client.disconnect()
- }
- }
- // muokataan ulkoasua niin että siinä on TextView + Button sekä molemmilla oma id:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_margin="20dp"
- tools:context=".RemoteMessageFragment">
- <TextView
- android:id="@+id/textView_cloud_message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="Waiting..."
- android:textSize="26sp"
- android:textStyle="bold" />
- <Button
- android:id="@+id/button_send_cloud_message"
- android:layout_width="match_parent"
- android:layout_height="wrap_content"
- android:text="SEND MESSAGE" />
- </LinearLayout>
- // tämän jälkeen kun data saapuu subscriben callbackissa, voidaan se asettaa myös ulkoasuun:
- // this callback runs everytime your code receives new data payload
- var result = String(publish.getPayloadAsBytes())
- Log.d("ADVTECH", result)
- // jotta koodi varmasti ajaa binding-layeria koskevan
- // koodin oikeassa threadissa, käytetään runOnUiThreadia:
- activity?.runOnUiThread {
- binding.textViewCloudMessage.text = result
- }
- // onCreateViewiin ennen return rootia voidaan kytketä Buttoniin datan lähettäminen samaan topiciin:
- // kun nappia painetaan, lähetetään samaan topiciin satunnainen Hello World-viesti
- // jos Random näkyy punaisella, käy poistamassa ylhäältä java.util.* import
- // ja importaa Kotlinin versio Randomista
- binding.buttonSendCloudMessage.setOnClickListener {
- var randomNumber = Random.nextInt(0, 100)
- var stringPayload = "Hello world! " + randomNumber.toString()
- client.publishWith()
- .topic(BuildConfig.HIVEMQ_TOPIC)
- .payload(stringPayload.toByteArray())
- .send()
- }
- // Custom viewit, kokeillaan olemassa olevia:
- 1. https://github.com/ybq/Android-SpinKit
- Jotta Android-projekti löytää kyseisen (ja monen muun) kustomikomponentin, tarvitaan settings.gradleen jitpack.io:n osoite:
- (molemmat repositories-kohdat)
- pluginManagement {
- repositories {
- gradlePluginPortal()
- google()
- mavenCentral()
- maven { url "https://jitpack.io" }
- }
- }
- dependencyResolutionManagement {
- repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
- repositories {
- google()
- mavenCentral()
- maven { url "https://jitpack.io" }
- }
- }
- // tämän jälkeen import build.gradle -moduleen:
- implementation 'com.github.ybq:Android-SpinKit:1.4.0'
- // kokeillaan ohjeen mukaisesti laittaa fragment_home.xml:ään yksi SpinKit-elementti:
- <com.github.ybq.android.spinkit.SpinKitView
- android:id="@+id/spin_kit"
- style="@style/SpinKitView.Large.Circle"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:layout_gravity="center"
- app:SpinKit_Color="@color/purple_500"
- app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
- app:layout_constraintStart_toStartOf="parent"
- app:layout_constraintTop_toBottomOf="@+id/text_home" />
- 2. https://github.com/anastr/SpeedView
- build.gradle -moduleen:
- implementation 'com.github.anastr:speedviewlib:1.6.0'
- // jos haluat, voit tehdä mobile_navigationilla erillisen testifragmentin kustomikomponenteille:
- class CustomViewTesterFragment : Fragment() {
- // change this to match your fragment name
- private var _binding: FragmentCustomViewTesterBinding? = null
- // This property is only valid between onCreateView and
- // onDestroyView.
- private val binding get() = _binding!!
- override fun onCreateView(
- inflater: LayoutInflater,
- container: ViewGroup?,
- savedInstanceState: Bundle?
- ): View? {
- _binding = FragmentCustomViewTesterBinding.inflate(inflater, container, false)
- val root: View = binding.root
- // the binding -object allows you to access views in the layout, textviews etc.
- return root
- }
- override fun onDestroyView() {
- super.onDestroyView()
- _binding = null
- }
- }
- muista lisätä myös päävalikkoon!
- ulkoasuksi:
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:orientation="vertical"
- android:layout_margin="10dp"
- tools:context=".CustomViewTesterFragment">
- </LinearLayout>
- Fragment-koodissa, muutetaan "lämpötila" eli nopeus:
- binding.speedView.speedTo(20f)
- // kokeillaan SpeedViewiä, otetaan myös mittarin heiluminen pois (sv_withTremble="false")
- <?xml version="1.0" encoding="utf-8"?>
- <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:tools="http://schemas.android.com/tools"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- android:orientation="vertical"
- android:layout_margin="10dp"
- tools:context=".CustomViewTesterFragment">
- <com.github.anastr.speedviewlib.SpeedView
- android:id="@+id/speedView"
- app:sv_withTremble="false"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- </LinearLayout>
- // säädetään lisää, muutetaan min ja max -arvot sekä vaihdetaan yksikkö Celsiukseen
- <com.github.anastr.speedviewlib.SpeedView
- android:id="@+id/speedView"
- app:sv_withTremble="false"
- app:sv_unit=" ℃"
- app:sv_minSpeed="-50"
- app:sv_maxSpeed="50"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content" />
- // jos tällainen SpeedView lisätään myös WeatherStationFragmenttiin (eri id:llä), voidaan se helposti kytkeä oikeaan säädataan,
- // esim. subscriben callbackissa: (temperature -muuttujassa on uusin lämpötila Double-formaatissa, esim. var temperature = item.d.get1().v
- // item on luotu GSONin avulla WeatherStation-luokan ja raaka-JSONin pohjalta
- activity?.runOnUiThread {
- // asetetaan tekstimuuttuja TextViewiin, joka on käyttöliittymässä
- binding.textViewWeatherstationText.text = text
- binding.speedViewWeatherTemperature.speedTo(temperature.toFloat())
- }
- 3. kokeillaan nopeasti kalenteri-moduulia
- https://github.com/npanigrahy/Custom-Calendar-View
- muuta seuraavaa importia niin että compilen sijasta implementation ja versiona 1.1
- compile 'com.github.npanigrahy:Custom-Calendar-View:v1.0'
- Uusi fragment: CalendarFragment, lisätään päävalikkoon
- Sivujen esimerkki on Javalla, käännettynä Kotliniin, voidaan tehdä jotain tällaista (muista lisätä ulkoasuun CalendarView ohjeen mukaan)
- // the binding -object allows you to access views in the layout, textviews etc.
- //Initialize calendar with date
- val currentCalendar: Calendar = Calendar.getInstance(Locale.getDefault())
- //Show Monday as first date of week
- binding.calendarView.setFirstDayOfWeek(Calendar.MONDAY)
- //Show/hide overflow days of a month
- binding.calendarView.setShowOverflowDate(false)
- //call refreshCalendar to update calendar the view
- binding.calendarView.refreshCalendar(currentCalendar)
- //Handling custom calendar events
- binding.calendarView.setCalendarListener(object : CalendarListener {
- override fun onDateSelected(date: Date?) {
- val df = SimpleDateFormat("dd-MM-yyyy")
- Toast.makeText(activity, df.format(date), Toast.LENGTH_SHORT).show()
- }
- override fun onMonthChanged(date: Date?) {
- val df = SimpleDateFormat("MM-yyyy")
- Toast.makeText(activity, df.format(date), Toast.LENGTH_SHORT).show()
- }
- })
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement