Advertisement
tuomasvaltanen

Untitled

Apr 12th, 2023 (edited)
101
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 12.11 KB | None | 0 0
  1. // Edistynyt mobiiliohjelmointi 12.4.2023
  2.  
  3. 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.
  4.  
  5. 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
  6.  
  7. 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)
  8.  
  9. 3. Lisää local.properties -tiedostoon tarvittavat muuttujat HiveMQ:ta varten (ei lainausmerkkejä tai välilyöntejä mihinkään arvoon):
  10.  
  11. HIVEMQ_BROKER=oman_klusterin_osoite
  12. HIVEMQ_USERNAME=oman_klusterin_username
  13. HIVEMQ_PASSWORD=oman_klusterin_salasana
  14. HIVEMQ_TOPIC=koodin_käyttämä_topic
  15.  
  16. 4. Jatketaan tästä vaiheesta eteenpäin yhdessä klo 13:30.
  17.  
  18.  
  19. // RemoteMessageFragment.kt, siivoamalla aiempaa sääasema-fragmentia, saadaan melkein samalla koodilla toimiva pohja:
  20.  
  21. class RemoteMessageFragment : Fragment() {
  22. // change this to match your fragment name
  23. private var _binding: FragmentRemoteMessageBinding? = null
  24.  
  25. // This property is only valid between onCreateView and
  26. // onDestroyView.
  27. private val binding get() = _binding!!
  28.  
  29. private lateinit var client: Mqtt3AsyncClient
  30.  
  31. override fun onCreateView(
  32. inflater: LayoutInflater,
  33. container: ViewGroup?,
  34. savedInstanceState: Bundle?
  35. ): View? {
  36. _binding = FragmentRemoteMessageBinding.inflate(inflater, container, false)
  37. val root: View = binding.root
  38.  
  39. // version 3, IBM Cloud, weather station
  40. client = MqttClient.builder()
  41. .useMqttVersion3()
  42. .sslWithDefaultConfig()
  43. // käytetään samaa client id:tä tällä kertaa aina, ettei HiveMQ:n ilmainen raja tule täyteen
  44. .identifier("jkdghsfkd567jkafdghsw3s32")
  45. .serverHost(BuildConfig.HIVEMQ_BROKER)
  46. .serverPort(8883)
  47. .buildAsync()
  48.  
  49. client.connectWith()
  50. .simpleAuth()
  51. .username(BuildConfig.HIVEMQ_USERNAME)
  52. .password(BuildConfig.HIVEMQ_PASSWORD.toByteArray())
  53. .applySimpleAuth()
  54. .send()
  55. .whenComplete { connAck: Mqtt3ConnAck?, throwable: Throwable? ->
  56. if (throwable != null) {
  57. Log.d("ADVTECH", "Connection failure.")
  58. } else {
  59. // Setup subscribes or start publishing
  60. subscribeToTopic()
  61. }
  62. }
  63.  
  64. return root
  65. }
  66.  
  67. fun subscribeToTopic()
  68. {
  69. client.subscribeWith()
  70. .topicFilter(BuildConfig.HIVEMQ_TOPIC)
  71. .callback { publish ->
  72.  
  73. // this callback runs everytime your code receives new data payload
  74. var result = String(publish.getPayloadAsBytes())
  75. Log.d("ADVTECH", result)
  76.  
  77.  
  78. }
  79. .send()
  80. .whenComplete { subAck, throwable ->
  81. if (throwable != null) {
  82. // Handle failure to subscribe
  83. Log.d("ADVTECH", "Subscribe failed.")
  84. } else {
  85. // Handle successful subscription, e.g. logging or incrementing a metric
  86. Log.d("ADVTECH", "Subscribed!")
  87. }
  88. }
  89. }
  90.  
  91. override fun onDestroyView() {
  92. super.onDestroyView()
  93. _binding = null
  94. client.disconnect()
  95. }
  96. }
  97.  
  98. // muokataan ulkoasua niin että siinä on TextView + Button sekä molemmilla oma id:
  99.  
  100. <?xml version="1.0" encoding="utf-8"?>
  101. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  102. xmlns:tools="http://schemas.android.com/tools"
  103. android:layout_width="match_parent"
  104. android:layout_height="match_parent"
  105. android:orientation="vertical"
  106. android:layout_margin="20dp"
  107. tools:context=".RemoteMessageFragment">
  108.  
  109. <TextView
  110. android:id="@+id/textView_cloud_message"
  111. android:layout_width="match_parent"
  112. android:layout_height="wrap_content"
  113. android:text="Waiting..."
  114. android:textSize="26sp"
  115. android:textStyle="bold" />
  116.  
  117. <Button
  118. android:id="@+id/button_send_cloud_message"
  119. android:layout_width="match_parent"
  120. android:layout_height="wrap_content"
  121. android:text="SEND MESSAGE" />
  122. </LinearLayout>
  123.  
  124.  
  125. // tämän jälkeen kun data saapuu subscriben callbackissa, voidaan se asettaa myös ulkoasuun:
  126.  
  127. // this callback runs everytime your code receives new data payload
  128. var result = String(publish.getPayloadAsBytes())
  129. Log.d("ADVTECH", result)
  130.  
  131. // jotta koodi varmasti ajaa binding-layeria koskevan
  132. // koodin oikeassa threadissa, käytetään runOnUiThreadia:
  133. activity?.runOnUiThread {
  134. binding.textViewCloudMessage.text = result
  135. }
  136.  
  137.  
  138. // onCreateViewiin ennen return rootia voidaan kytketä Buttoniin datan lähettäminen samaan topiciin:
  139.  
  140. // kun nappia painetaan, lähetetään samaan topiciin satunnainen Hello World-viesti
  141. // jos Random näkyy punaisella, käy poistamassa ylhäältä java.util.* import
  142. // ja importaa Kotlinin versio Randomista
  143. binding.buttonSendCloudMessage.setOnClickListener {
  144. var randomNumber = Random.nextInt(0, 100)
  145. var stringPayload = "Hello world! " + randomNumber.toString()
  146.  
  147. client.publishWith()
  148. .topic(BuildConfig.HIVEMQ_TOPIC)
  149. .payload(stringPayload.toByteArray())
  150. .send()
  151. }
  152.  
  153. // Custom viewit, kokeillaan olemassa olevia:
  154.  
  155. 1. https://github.com/ybq/Android-SpinKit
  156.  
  157. Jotta Android-projekti löytää kyseisen (ja monen muun) kustomikomponentin, tarvitaan settings.gradleen jitpack.io:n osoite:
  158. (molemmat repositories-kohdat)
  159.  
  160. pluginManagement {
  161. repositories {
  162. gradlePluginPortal()
  163. google()
  164. mavenCentral()
  165. maven { url "https://jitpack.io" }
  166. }
  167. }
  168. dependencyResolutionManagement {
  169. repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
  170. repositories {
  171. google()
  172. mavenCentral()
  173. maven { url "https://jitpack.io" }
  174. }
  175. }
  176.  
  177. // tämän jälkeen import build.gradle -moduleen:
  178.  
  179. implementation 'com.github.ybq:Android-SpinKit:1.4.0'
  180.  
  181. // kokeillaan ohjeen mukaisesti laittaa fragment_home.xml:ään yksi SpinKit-elementti:
  182.  
  183. <com.github.ybq.android.spinkit.SpinKitView
  184. android:id="@+id/spin_kit"
  185. style="@style/SpinKitView.Large.Circle"
  186. android:layout_width="wrap_content"
  187. android:layout_height="wrap_content"
  188. android:layout_gravity="center"
  189. app:SpinKit_Color="@color/purple_500"
  190. app:layout_constraintBottom_toBottomOf="parent"
  191. app:layout_constraintEnd_toEndOf="parent"
  192. app:layout_constraintStart_toStartOf="parent"
  193. app:layout_constraintTop_toBottomOf="@+id/text_home" />
  194.  
  195. 2. https://github.com/anastr/SpeedView
  196.  
  197. build.gradle -moduleen:
  198. implementation 'com.github.anastr:speedviewlib:1.6.0'
  199.  
  200. // jos haluat, voit tehdä mobile_navigationilla erillisen testifragmentin kustomikomponenteille:
  201.  
  202. class CustomViewTesterFragment : Fragment() {
  203. // change this to match your fragment name
  204. private var _binding: FragmentCustomViewTesterBinding? = null
  205.  
  206. // This property is only valid between onCreateView and
  207. // onDestroyView.
  208. private val binding get() = _binding!!
  209.  
  210. override fun onCreateView(
  211. inflater: LayoutInflater,
  212. container: ViewGroup?,
  213. savedInstanceState: Bundle?
  214. ): View? {
  215. _binding = FragmentCustomViewTesterBinding.inflate(inflater, container, false)
  216. val root: View = binding.root
  217.  
  218.  
  219. // the binding -object allows you to access views in the layout, textviews etc.
  220.  
  221. return root
  222. }
  223.  
  224. override fun onDestroyView() {
  225. super.onDestroyView()
  226. _binding = null
  227. }
  228. }
  229.  
  230. muista lisätä myös päävalikkoon!
  231.  
  232. ulkoasuksi:
  233.  
  234. <?xml version="1.0" encoding="utf-8"?>
  235. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  236. xmlns:tools="http://schemas.android.com/tools"
  237. android:layout_width="match_parent"
  238. android:layout_height="match_parent"
  239. android:orientation="vertical"
  240. android:layout_margin="10dp"
  241. tools:context=".CustomViewTesterFragment">
  242.  
  243. </LinearLayout>
  244.  
  245.  
  246. Fragment-koodissa, muutetaan "lämpötila" eli nopeus:
  247.  
  248. binding.speedView.speedTo(20f)
  249.  
  250. // kokeillaan SpeedViewiä, otetaan myös mittarin heiluminen pois (sv_withTremble="false")
  251.  
  252. <?xml version="1.0" encoding="utf-8"?>
  253. <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
  254. xmlns:tools="http://schemas.android.com/tools"
  255. android:layout_width="match_parent"
  256. android:layout_height="match_parent"
  257. xmlns:app="http://schemas.android.com/apk/res-auto"
  258. android:orientation="vertical"
  259. android:layout_margin="10dp"
  260. tools:context=".CustomViewTesterFragment">
  261.  
  262. <com.github.anastr.speedviewlib.SpeedView
  263. android:id="@+id/speedView"
  264. app:sv_withTremble="false"
  265. android:layout_width="wrap_content"
  266. android:layout_height="wrap_content" />
  267.  
  268. </LinearLayout>
  269.  
  270. // säädetään lisää, muutetaan min ja max -arvot sekä vaihdetaan yksikkö Celsiukseen
  271.  
  272. <com.github.anastr.speedviewlib.SpeedView
  273. android:id="@+id/speedView"
  274. app:sv_withTremble="false"
  275. app:sv_unit=" ℃"
  276. app:sv_minSpeed="-50"
  277. app:sv_maxSpeed="50"
  278. android:layout_width="wrap_content"
  279. android:layout_height="wrap_content" />
  280.  
  281. // jos tällainen SpeedView lisätään myös WeatherStationFragmenttiin (eri id:llä), voidaan se helposti kytkeä oikeaan säädataan,
  282. // esim. subscriben callbackissa: (temperature -muuttujassa on uusin lämpötila Double-formaatissa, esim. var temperature = item.d.get1().v
  283. // item on luotu GSONin avulla WeatherStation-luokan ja raaka-JSONin pohjalta
  284.  
  285. activity?.runOnUiThread {
  286. // asetetaan tekstimuuttuja TextViewiin, joka on käyttöliittymässä
  287. binding.textViewWeatherstationText.text = text
  288. binding.speedViewWeatherTemperature.speedTo(temperature.toFloat())
  289. }
  290.  
  291. 3. kokeillaan nopeasti kalenteri-moduulia
  292.  
  293. https://github.com/npanigrahy/Custom-Calendar-View
  294.  
  295. muuta seuraavaa importia niin että compilen sijasta implementation ja versiona 1.1
  296.  
  297. compile 'com.github.npanigrahy:Custom-Calendar-View:v1.0'
  298.  
  299. Uusi fragment: CalendarFragment, lisätään päävalikkoon
  300.  
  301. Sivujen esimerkki on Javalla, käännettynä Kotliniin, voidaan tehdä jotain tällaista (muista lisätä ulkoasuun CalendarView ohjeen mukaan)
  302.  
  303. // the binding -object allows you to access views in the layout, textviews etc.
  304. //Initialize calendar with date
  305. val currentCalendar: Calendar = Calendar.getInstance(Locale.getDefault())
  306.  
  307. //Show Monday as first date of week
  308. binding.calendarView.setFirstDayOfWeek(Calendar.MONDAY)
  309.  
  310. //Show/hide overflow days of a month
  311. binding.calendarView.setShowOverflowDate(false)
  312.  
  313. //call refreshCalendar to update calendar the view
  314. binding.calendarView.refreshCalendar(currentCalendar)
  315.  
  316. //Handling custom calendar events
  317. binding.calendarView.setCalendarListener(object : CalendarListener {
  318. override fun onDateSelected(date: Date?) {
  319. val df = SimpleDateFormat("dd-MM-yyyy")
  320. Toast.makeText(activity, df.format(date), Toast.LENGTH_SHORT).show()
  321. }
  322.  
  323. override fun onMonthChanged(date: Date?) {
  324. val df = SimpleDateFormat("MM-yyyy")
  325. Toast.makeText(activity, df.format(date), Toast.LENGTH_SHORT).show()
  326. }
  327. })
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement