Skip to content

Commit c102afd

Browse files
JaylinYuzibuyu1995
authored andcommitted
* NEW add new kotlin example code that compatible with Android12 (credit to https://github.com/hannesa2/paho.mqtt.android)
1 parent 781dcc0 commit c102afd

File tree

172 files changed

+10545
-0
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

172 files changed

+10545
-0
lines changed

paho.mqtt.android-kotlin/README.md

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
[![](https://jitpack.io/v/hannesa2/paho.mqtt.android.svg)](https://jitpack.io/#hannesa2/paho.mqtt.android)
2+
# MQTT Android Service
3+
4+
5+
The MQTT Android Service is an MQTT client library written in Kotlin.
6+
It has been created to provide reliable open-source implementations of open and standard messaging protocols aimed at new, existing, and emerging
7+
applications for Machine-to-Machine (M2M) and Internet of Things (IoT).
8+
MQTT reflects the inherent physical and cost constraints of device connectivity. Its objectives include effective levels of decoupling between devices and applications, designed to keep markets open and encourage the rapid growth of scalable Web and Enterprise middleware and applications.
9+
10+
11+
## Features
12+
| | | | | |
13+
|---------------------|--------------------|---|----------------------|--------------------|
14+
| MQTT 3.1 | :heavy_check_mark: | | Automatic Reconnect | :heavy_check_mark: |
15+
| MQTT 3.1.1 | :heavy_check_mark: | | Offline Buffering | :heavy_check_mark: |
16+
| LWT | :heavy_check_mark: | | WebSocket Support | :heavy_check_mark: |
17+
| SSL / TLS | :heavy_check_mark: | | Standard TCP Support | :heavy_check_mark: |
18+
| Message Persistence | :heavy_check_mark: | |
19+
20+
## Using this MQTT Android Client
21+
22+
#### Jitpack.io
23+
24+
More details are here https://jitpack.io/#hannesa2/paho.mqtt.android
25+
26+
```
27+
allprojects {
28+
repositories {
29+
...
30+
maven { url 'https://jitpack.io' }
31+
}
32+
}
33+
```
34+
35+
```
36+
dependencies {
37+
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
38+
implementation 'com.github.hannesa2:paho.mqtt.android:$latestVersion'
39+
}
40+
```
41+
42+
#### Android O foreground service
43+
44+
Android >= O you should use it as foreground service
45+
46+
```
47+
val client = MqttAndroidClient(context, uri, clientId).apply {
48+
setForegroundService(foregroundNotification, 3)
49+
}
50+
```
51+
52+
## License
53+
54+
```
55+
Copyright 2021
56+
57+
Licensed under the Apache License, Version 2.0 (the "License");
58+
you may not use this file except in compliance with the License.
59+
You may obtain a copy of the License at
60+
61+
http://www.apache.org/licenses/LICENSE-2.0
62+
63+
Unless required by applicable law or agreed to in writing, software
64+
distributed under the License is distributed on an "AS IS" BASIS,
65+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
66+
See the License for the specific language governing permissions and
67+
limitations under the License.
68+
```
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
plugins {
2+
id 'com.android.application'
3+
id 'kotlin-android'
4+
}
5+
6+
android {
7+
compileSdkVersion rootProject.ext.compileSdkVersion
8+
9+
defaultConfig {
10+
applicationId "info.mqtt.java.example"
11+
minSdkVersion 21
12+
targetSdkVersion 33
13+
// versionCode getGitCommitCount()
14+
// versionName getTag()
15+
16+
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
17+
testInstrumentationRunnerArguments useTestStorageService: 'true'
18+
}
19+
20+
buildFeatures {
21+
viewBinding true
22+
}
23+
namespace 'info.mqtt.java.example'
24+
25+
}
26+
27+
dependencies {
28+
implementation project(':serviceLibrary')
29+
implementation 'androidx.appcompat:appcompat:1.6.1'
30+
implementation 'com.google.android.material:material:1.9.0'
31+
implementation 'androidx.recyclerview:recyclerview:1.3.0'
32+
implementation "com.github.AppDevNext.Logcat:LogcatCoreLib:$logcatVersion"
33+
34+
implementation "androidx.core:core-ktx:1.10.0"
35+
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version"
36+
37+
testImplementation 'junit:junit:4.13.2'
38+
androidTestImplementation 'com.github.AppDevNext:Moka:1.6'
39+
androidTestImplementation "androidx.test.ext:junit-ktx:1.1.5"
40+
androidTestUtil "androidx.test.services:test-services:1.4.2"
41+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package info.mqtt.java.example
2+
3+
import androidx.test.core.graphics.writeToTestStorage
4+
import androidx.test.espresso.Espresso
5+
import androidx.test.espresso.matcher.ViewMatchers
6+
import androidx.test.espresso.matcher.ViewMatchers.isDisplayed
7+
import androidx.test.espresso.screenshot.captureToBitmap
8+
import androidx.test.ext.junit.rules.activityScenarioRule
9+
import androidx.test.ext.junit.runners.AndroidJUnit4
10+
import com.moka.lib.assertions.MatchOperator
11+
import com.moka.lib.assertions.WaitingAssertion
12+
import org.junit.Ignore
13+
import org.junit.Rule
14+
import org.junit.Test
15+
import org.junit.rules.TestName
16+
import org.junit.runner.RunWith
17+
18+
@RunWith(AndroidJUnit4::class)
19+
class ConnectTest {
20+
21+
// a handy JUnit rule that stores the method name, so it can be used to generate unique screenshot files per test method
22+
@get:Rule
23+
var nameRule = TestName()
24+
25+
@get:Rule
26+
val activityScenarioRule = activityScenarioRule<MQTTExampleActivity>()
27+
28+
@Test
29+
@Ignore("On CI it doesn't work anymore")
30+
fun basicSmokeTest() {
31+
WaitingAssertion.checkAssertion(R.id.history_recycler_view, isDisplayed(), 1500)
32+
Espresso.onView(ViewMatchers.isRoot())
33+
.captureToBitmap()
34+
.writeToTestStorage("${javaClass.simpleName}_${nameRule.methodName}-Step1")
35+
WaitingAssertion.assertRecyclerAdapterItemsCount(R.id.history_recycler_view, 3, MatchOperator.GREATER_EQUAL, 5500)
36+
Espresso.onView(ViewMatchers.isRoot())
37+
.captureToBitmap()
38+
.writeToTestStorage("${javaClass.simpleName}_${nameRule.methodName}-End")
39+
}
40+
41+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
<?xml version="1.0" encoding="utf-8"?>
2+
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3+
xmlns:tools="http://schemas.android.com/tools">
4+
5+
6+
<!-- Permissions the Application Requires -->
7+
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
8+
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
9+
10+
<application
11+
android:allowBackup="false"
12+
android:icon="@mipmap/ic_launcher"
13+
android:label="@string/app_name"
14+
android:supportsRtl="true"
15+
android:name="info.hannes.logcat.LoggingApplication"
16+
android:theme="@style/AppTheme"
17+
tools:ignore="GoogleAppIndexingWarning">
18+
<activity
19+
android:name="info.mqtt.java.example.MQTTExampleActivity"
20+
android:label="@string/app_name"
21+
android:theme="@style/AppTheme.NoActionBar"
22+
android:exported="true">
23+
<intent-filter>
24+
<action android:name="android.intent.action.MAIN" />
25+
26+
<category android:name="android.intent.category.LAUNCHER" />
27+
</intent-filter>
28+
</activity>
29+
30+
</application>
31+
32+
</manifest>
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package info.mqtt.java.example
2+
3+
import android.view.LayoutInflater
4+
import android.view.View
5+
import android.view.ViewGroup
6+
import android.widget.TextView
7+
import androidx.recyclerview.widget.RecyclerView
8+
import java.util.*
9+
10+
class HistoryAdapter : RecyclerView.Adapter<HistoryAdapter.ViewHolder>() {
11+
12+
private val history: ArrayList<String> = ArrayList()
13+
14+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
15+
val view = LayoutInflater.from(parent.context).inflate(R.layout.history_row, parent, false)
16+
return ViewHolder(view)
17+
}
18+
19+
fun add(data: String) {
20+
history.add(data)
21+
notifyItemInserted(history.size - 1)
22+
}
23+
24+
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
25+
holder.mTextView.text = history[position]
26+
}
27+
28+
override fun getItemCount() = history.size
29+
30+
class ViewHolder(view: View) : RecyclerView.ViewHolder(view) {
31+
var mTextView: TextView = view.findViewById(R.id.row_text)
32+
}
33+
}
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package info.mqtt.java.example
2+
3+
import android.annotation.SuppressLint
4+
import android.os.Bundle
5+
import androidx.appcompat.app.AppCompatActivity
6+
import androidx.recyclerview.widget.LinearLayoutManager
7+
import androidx.recyclerview.widget.RecyclerView.LayoutManager
8+
import com.google.android.material.snackbar.Snackbar
9+
import info.mqtt.android.service.MqttAndroidClient
10+
import info.mqtt.android.service.QoS
11+
import info.mqtt.java.example.databinding.ActivityScrollingBinding
12+
import org.eclipse.paho.client.mqttv3.*
13+
import timber.log.Timber
14+
import java.text.SimpleDateFormat
15+
import java.util.*
16+
17+
class MQTTExampleActivity : AppCompatActivity() {
18+
19+
private lateinit var mqttAndroidClient: MqttAndroidClient
20+
private lateinit var adapter: HistoryAdapter
21+
private lateinit var binding: ActivityScrollingBinding
22+
23+
override fun onCreate(savedInstanceState: Bundle?) {
24+
super.onCreate(savedInstanceState)
25+
binding = ActivityScrollingBinding.inflate(layoutInflater)
26+
val view = binding.root
27+
setContentView(view)
28+
29+
setSupportActionBar(binding.toolbar)
30+
31+
binding.fab.setOnClickListener { publishMessage() }
32+
val mLayoutManager: LayoutManager = LinearLayoutManager(this)
33+
binding.historyRecyclerView.layoutManager = mLayoutManager
34+
adapter = HistoryAdapter()
35+
binding.historyRecyclerView.adapter = adapter
36+
clientId += System.currentTimeMillis()
37+
mqttAndroidClient = MqttAndroidClient(applicationContext, serverUri, clientId)
38+
mqttAndroidClient.setCallback(object : MqttCallbackExtended {
39+
override fun connectComplete(reconnect: Boolean, serverURI: String) {
40+
if (reconnect) {
41+
addToHistory("Reconnected: $serverURI")
42+
// Because Clean Session is true, we need to re-subscribe
43+
subscribeToTopic()
44+
} else {
45+
addToHistory("Connected: $serverURI")
46+
}
47+
}
48+
49+
override fun connectionLost(cause: Throwable?) {
50+
addToHistory("The Connection was lost.")
51+
}
52+
53+
override fun messageArrived(topic: String, message: MqttMessage) {
54+
addToHistory("Incoming message: " + String(message.payload))
55+
}
56+
57+
override fun deliveryComplete(token: IMqttDeliveryToken) {}
58+
})
59+
val mqttConnectOptions = MqttConnectOptions()
60+
mqttConnectOptions.isAutomaticReconnect = true
61+
mqttConnectOptions.isCleanSession = false
62+
addToHistory("Connecting: $serverUri")
63+
mqttAndroidClient.connect(mqttConnectOptions, null, object : IMqttActionListener {
64+
override fun onSuccess(asyncActionToken: IMqttToken) {
65+
val disconnectedBufferOptions = DisconnectedBufferOptions()
66+
disconnectedBufferOptions.isBufferEnabled = true
67+
disconnectedBufferOptions.bufferSize = 100
68+
disconnectedBufferOptions.isPersistBuffer = false
69+
disconnectedBufferOptions.isDeleteOldestMessages = false
70+
mqttAndroidClient.setBufferOpts(disconnectedBufferOptions)
71+
subscribeToTopic()
72+
}
73+
74+
override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
75+
addToHistory("Failed to connect: $serverUri")
76+
}
77+
})
78+
}
79+
80+
private fun addToHistory(mainText: String) {
81+
Timber.d(mainText)
82+
@SuppressLint("SimpleDateFormat")
83+
val timestamp = SimpleDateFormat("HH:mm.ss.SSS").format(Date(System.currentTimeMillis()))
84+
adapter.add("$timestamp $mainText")
85+
Snackbar.make(findViewById(android.R.id.content), mainText, Snackbar.LENGTH_LONG).setAction("Action", null).show()
86+
}
87+
88+
fun subscribeToTopic() {
89+
mqttAndroidClient.subscribe(subscriptionTopic, QoS.AtMostOnce.value, null, object : IMqttActionListener {
90+
override fun onSuccess(asyncActionToken: IMqttToken) {
91+
addToHistory("Subscribed! $subscriptionTopic")
92+
}
93+
94+
override fun onFailure(asyncActionToken: IMqttToken?, exception: Throwable?) {
95+
addToHistory("Failed to subscribe $exception")
96+
}
97+
})
98+
99+
// THIS DOES NOT WORK!
100+
mqttAndroidClient.subscribe(subscriptionTopic, QoS.AtMostOnce.value) { topic, message ->
101+
Timber.d("Message arrived $topic : ${String(message.payload)}")
102+
addToHistory("Message arrived $message")
103+
}
104+
}
105+
106+
private fun publishMessage() {
107+
val message = MqttMessage()
108+
message.payload = publishMessage.toByteArray()
109+
if (mqttAndroidClient.isConnected) {
110+
mqttAndroidClient.publish(publishTopic, message)
111+
addToHistory("Message Published >$publishMessage<")
112+
if (!mqttAndroidClient.isConnected) {
113+
addToHistory(mqttAndroidClient.bufferedMessageCount.toString() + " messages in buffer.")
114+
}
115+
} else {
116+
Snackbar.make(findViewById(android.R.id.content), "Not connected", Snackbar.LENGTH_SHORT).setAction("Action", null).show()
117+
}
118+
}
119+
120+
companion object {
121+
private const val serverUri = "tcp://broker.hivemq.com:1883"
122+
private const val subscriptionTopic = "exampleAndroidTopic"
123+
private const val publishTopic = "exampleAndroidPublishTopic"
124+
private const val publishMessage = "Hello World"
125+
private var clientId = "BasicSample"
126+
}
127+
}

0 commit comments

Comments
 (0)