├── .gitignore ├── .idea ├── codeStyles │ ├── Project.xml │ └── codeStyleConfig.xml ├── jarRepositories.xml ├── markdown-navigator-enh.xml └── markdown-navigator.xml ├── .travis.yml ├── README.md ├── app ├── build.gradle ├── libs │ └── org.eclipse.paho.android.service-1.0.2.jar └── src │ └── main │ ├── AndroidManifest.xml │ ├── assets │ └── file │ ├── ic_launcher-playstore.png │ ├── java │ └── io │ │ └── bytehala │ │ └── eclipsemqtt │ │ └── sample │ │ ├── ActionListener.java │ │ ├── ActivityConstants.java │ │ ├── AdvancedActivity.java │ │ ├── CallbackBundle.java │ │ ├── ClientConnections.java │ │ ├── Connection.java │ │ ├── ConnectionDetailsActivity.java │ │ ├── Connections.java │ │ ├── HistoryFragment.java │ │ ├── LastWillActivity.java │ │ ├── Listener.java │ │ ├── MainActivity.java │ │ ├── MqttCallbackHandler.java │ │ ├── MqttTraceCallback.java │ │ ├── NewConnectionActivity.java │ │ ├── Notify.java │ │ ├── OpenFileDialog.java │ │ ├── Persistence.java │ │ ├── PersistenceException.java │ │ ├── PublishFragment.java │ │ └── SubscribeFragment.java │ ├── res │ ├── drawable-hdpi │ │ └── arrow.png │ ├── drawable-ldpi │ │ └── arrow.png │ ├── drawable-mdpi │ │ └── arrow.png │ ├── drawable-xhdpi │ │ └── arrow.png │ ├── drawable │ │ ├── ic_add.xml │ │ ├── ic_cloud_circle.xml │ │ └── ic_launcher_foreground.xml │ ├── layout │ │ ├── activity_advanced.xml │ │ ├── activity_connection_details.xml │ │ ├── activity_last_will.xml │ │ ├── activity_new_connection.xml │ │ ├── activity_publish.xml │ │ ├── activity_subscribe.xml │ │ ├── client_connections.xml │ │ ├── connection_text_view.xml │ │ ├── filedialogitem.xml │ │ └── list_view_text_view.xml │ ├── menu │ │ ├── activity_advanced.xml │ │ ├── activity_client_connections_contextual.xml │ │ ├── activity_connection_details.xml │ │ ├── activity_connection_details_disconnected.xml │ │ ├── activity_connections.xml │ │ ├── activity_connections_logging.xml │ │ ├── activity_last_will.xml │ │ ├── activity_new_connection.xml │ │ ├── activity_publish.xml │ │ ├── activity_publish_disconnected.xml │ │ ├── activity_subscribe.xml │ │ └── activity_subscribe_disconnected.xml │ ├── mipmap-anydpi-v26 │ │ ├── ic_launcher.xml │ │ └── ic_launcher_round.xml │ ├── mipmap-hdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-mdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-xhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-xxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── mipmap-xxxhdpi │ │ ├── ic_launcher.png │ │ └── ic_launcher_round.png │ ├── raw │ │ └── jsr47android.properties │ └── values │ │ ├── attrs_arrow.xml │ │ ├── ic_launcher_background.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── resources │ └── io │ └── bytehala │ └── eclipsemqtt │ └── sample │ └── package.html ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── import-summary.txt └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | # Created by https://www.gitignore.io/api/java,gradle,kotlin,android,androidstudio 3 | # Edit at https://www.gitignore.io/?templates=java,gradle,kotlin,android,androidstudio 4 | 5 | ### Android ### 6 | # Built application files 7 | *.apk 8 | *.ap_ 9 | *.aab 10 | 11 | # Files for the ART/Dalvik VM 12 | *.dex 13 | 14 | # Java class files 15 | *.class 16 | 17 | # Generated files 18 | bin/ 19 | gen/ 20 | out/ 21 | release/ 22 | 23 | # Gradle files 24 | .gradle/ 25 | build/ 26 | 27 | # Local configuration file (sdk path, etc) 28 | local.properties 29 | 30 | # Proguard folder generated by Eclipse 31 | proguard/ 32 | 33 | # Log Files 34 | *.log 35 | 36 | # Android Studio Navigation editor temp files 37 | .navigation/ 38 | 39 | # Android Studio captures folder 40 | captures/ 41 | 42 | # IntelliJ 43 | *.iml 44 | .idea/workspace.xml 45 | .idea/tasks.xml 46 | .idea/gradle.xml 47 | .idea/assetWizardSettings.xml 48 | .idea/dictionaries 49 | .idea/libraries 50 | # Android Studio 3 in .gitignore file. 51 | .idea/caches 52 | .idea/modules.xml 53 | # Comment next line if keeping position of elements in Navigation Editor is relevant for you 54 | .idea/navEditor.xml 55 | 56 | # Keystore files 57 | # Uncomment the following lines if you do not want to check your keystore files in. 58 | #*.jks 59 | #*.keystore 60 | 61 | # External native build folder generated in Android Studio 2.2 and later 62 | .externalNativeBuild 63 | 64 | # Google Services (e.g. APIs or Firebase) 65 | # google-services.json 66 | 67 | # Freeline 68 | freeline.py 69 | freeline/ 70 | freeline_project_description.json 71 | 72 | # fastlane 73 | fastlane/report.xml 74 | fastlane/Preview.html 75 | fastlane/screenshots 76 | fastlane/test_output 77 | fastlane/readme.md 78 | 79 | # Version control 80 | vcs.xml 81 | 82 | # lint 83 | lint/intermediates/ 84 | lint/generated/ 85 | lint/outputs/ 86 | lint/tmp/ 87 | # lint/reports/ 88 | 89 | ### Android Patch ### 90 | gen-external-apklibs 91 | output.json 92 | 93 | # Replacement of .externalNativeBuild directories introduced 94 | # with Android Studio 3.5. 95 | .cxx/ 96 | 97 | ### Java ### 98 | # Compiled class file 99 | 100 | # Log file 101 | 102 | # BlueJ files 103 | *.ctxt 104 | 105 | # Mobile Tools for Java (J2ME) 106 | .mtj.tmp/ 107 | 108 | # Package Files # 109 | *.jar 110 | *.war 111 | *.nar 112 | *.ear 113 | *.zip 114 | *.tar.gz 115 | *.rar 116 | 117 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 118 | hs_err_pid* 119 | 120 | ### Kotlin ### 121 | # Compiled class file 122 | 123 | # Log file 124 | 125 | # BlueJ files 126 | 127 | # Mobile Tools for Java (J2ME) 128 | 129 | # Package Files # 130 | 131 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 132 | 133 | ### Gradle ### 134 | .gradle 135 | 136 | # Ignore Gradle GUI config 137 | gradle-app.setting 138 | 139 | # Avoid ignoring Gradle wrapper jar file (.jar files are usually ignored) 140 | !gradle-wrapper.jar 141 | 142 | # Cache of project 143 | .gradletasknamecache 144 | 145 | # # Work around https://youtrack.jetbrains.com/issue/IDEA-116898 146 | # gradle/wrapper/gradle-wrapper.properties 147 | 148 | ### Gradle Patch ### 149 | **/build/ 150 | 151 | ### AndroidStudio ### 152 | # Covers files to be ignored for android development using Android Studio. 153 | 154 | # Built application files 155 | 156 | # Files for the ART/Dalvik VM 157 | 158 | # Java class files 159 | 160 | # Generated files 161 | 162 | # Gradle files 163 | 164 | # Signing files 165 | .signing/ 166 | 167 | # Local configuration file (sdk path, etc) 168 | 169 | # Proguard folder generated by Eclipse 170 | 171 | # Log Files 172 | 173 | # Android Studio 174 | /*/build/ 175 | /*/local.properties 176 | /*/out 177 | /*/*/build 178 | /*/*/production 179 | *.ipr 180 | *~ 181 | *.swp 182 | 183 | # Android Patch 184 | 185 | # External native build folder generated in Android Studio 2.2 and later 186 | 187 | # NDK 188 | obj/ 189 | 190 | # IntelliJ IDEA 191 | *.iws 192 | /out/ 193 | 194 | # User-specific configurations 195 | .idea/caches/ 196 | .idea/libraries/ 197 | .idea/shelf/ 198 | .idea/.name 199 | .idea/compiler.xml 200 | .idea/copyright/profiles_settings.xml 201 | .idea/encodings.xml 202 | .idea/misc.xml 203 | .idea/scopes/scope_settings.xml 204 | .idea/vcs.xml 205 | .idea/jsLibraryMappings.xml 206 | .idea/datasources.xml 207 | .idea/dataSources.ids 208 | .idea/sqlDataSources.xml 209 | .idea/dynamic.xml 210 | .idea/uiDesigner.xml 211 | 212 | # OS-specific files 213 | .DS_Store 214 | .DS_Store? 215 | ._* 216 | .Spotlight-V100 217 | .Trashes 218 | ehthumbs.db 219 | Thumbs.db 220 | 221 | # Legacy Eclipse project files 222 | .classpath 223 | .project 224 | .cproject 225 | .settings/ 226 | 227 | # Mobile Tools for Java (J2ME) 228 | 229 | # Package Files # 230 | 231 | # virtual machine crash logs (Reference: http://www.java.com/en/download/help/error_hotspot.xml) 232 | 233 | ## Plugin-specific files: 234 | 235 | # mpeltonen/sbt-idea plugin 236 | .idea_modules/ 237 | 238 | # JIRA plugin 239 | atlassian-ide-plugin.xml 240 | 241 | # Mongo Explorer plugin 242 | .idea/mongoSettings.xml 243 | 244 | # Crashlytics plugin (for Android Studio and IntelliJ) 245 | com_crashlytics_export_strings.xml 246 | crashlytics.properties 247 | crashlytics-build.properties 248 | fabric.properties 249 | 250 | ### AndroidStudio Patch ### 251 | 252 | !/gradle/wrapper/gradle-wrapper.jar 253 | 254 | # End of https://www.gitignore.io/api/java,gradle,kotlin,android,androidstudio -------------------------------------------------------------------------------- /.idea/codeStyles/Project.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 10 | 11 | 12 |
13 | 14 | 15 | 16 | xmlns:android 17 | 18 | ^$ 19 | 20 | 21 | 22 |
23 |
24 | 25 | 26 | 27 | xmlns:.* 28 | 29 | ^$ 30 | 31 | 32 | BY_NAME 33 | 34 |
35 |
36 | 37 | 38 | 39 | .*:id 40 | 41 | http://schemas.android.com/apk/res/android 42 | 43 | 44 | 45 |
46 |
47 | 48 | 49 | 50 | .*:name 51 | 52 | http://schemas.android.com/apk/res/android 53 | 54 | 55 | 56 |
57 |
58 | 59 | 60 | 61 | name 62 | 63 | ^$ 64 | 65 | 66 | 67 |
68 |
69 | 70 | 71 | 72 | style 73 | 74 | ^$ 75 | 76 | 77 | 78 |
79 |
80 | 81 | 82 | 83 | .* 84 | 85 | ^$ 86 | 87 | 88 | BY_NAME 89 | 90 |
91 |
92 | 93 | 94 | 95 | .* 96 | 97 | http://schemas.android.com/apk/res/android 98 | 99 | 100 | ANDROID_ATTRIBUTE_ORDER 101 | 102 |
103 |
104 | 105 | 106 | 107 | .* 108 | 109 | .* 110 | 111 | 112 | BY_NAME 113 | 114 |
115 |
116 |
117 |
118 | 119 | 121 |
122 |
-------------------------------------------------------------------------------- /.idea/codeStyles/codeStyleConfig.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | -------------------------------------------------------------------------------- /.idea/jarRepositories.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 9 | 10 | 14 | 15 | 19 | 20 | 24 | 25 | 29 | 30 | -------------------------------------------------------------------------------- /.idea/markdown-navigator-enh.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /.idea/markdown-navigator.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 29 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: android 2 | android: 3 | components: 4 | # Uncomment the lines below if you want to 5 | # use the latest revision of Android SDK Tools 6 | # - platform-tools 7 | - tools 8 | 9 | # The BuildTools version used by your project 10 | - build-tools-28.0.3 11 | 12 | # The SDK version used to compile your project 13 | - android-28 14 | 15 | # Additional components 16 | #- extra-google-m2repository 17 | #- extra-android-m2repository 18 | #- addon-google_apis-google-19 19 | 20 | # Specify at least one system image, 21 | # if you need to run emulator(s) during your tests 22 | #- sys-img-armeabi-v7a-android-19 23 | #- sys-img-x86-android-17 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | > :warning: **Please feel free to create issues if it does not run for you. 2 | # android-mqtt-quickstart [![Build Status](https://travis-ci.org/bytehala/android-mqtt-quickstart.svg?branch=master)](https://travis-ci.org/bytehala/android-mqtt-quickstart) 3 | Android Studio port of the Eclipse paho MQTT sample project. 4 | Beside that you can use subscribe/pulish to a MQTT server, sms functionality is added to this project. 5 | 6 | 7 | ## How sms functionality is working ? 8 | If you want to send sms, you have to first subscribe to sms topic in application 9 | To set your phone number in messages received via sms topic, The thirteen first characters of your message will be the phone number you want to send 10 | example: 11 | ``` 12 | message topic: sms 13 | payload: +989375915077 Hello 14 | ``` 15 | 16 | 17 | It is highly recommended to change the default phone number in ```src\main\java\io\bytehala\eclipsemqtt\sample\MqttCallbackHandler.java``` line 141 18 | 19 | ## Environment Tested On 20 | 1. OpenJDK Java 11 by Amazon (sdkman 11.0.6-amzn) 21 | 2. Samsung galaxy mini s5570 SDK version 18 ( OS android, Cyanogen mod ) 22 | 3. Android Studio 4.1.1 23 | 24 | #### This information are from the forked repo 25 | 26 | I am maintaining this on my own, and as such am unable to test on multiple devices and environments. 27 | 28 | You can support me buy contributing code, filing bugs, asking/answering questions, or buying me a coffee. 29 | 30 | 31 | Buy Me a Coffee at ko-fi.com 32 | 33 | 34 | ## Getting Started 35 | 1. Download the code `git clone http://www.github.com/bytehala/android-mqtt-quickstart` 36 | 2. Open the project in Android Studio 37 | 3. Build and run it. 38 | 39 | ## Using the Sample App 40 | To test the app, you need an MQTT broker. Luckily, HiveMQ provides a free one which we can use for testing. 41 | ![HiveMQ broker](http://i.imgur.com/zStIVr4.png "MQTT Settings") 42 | 43 | After successfully connecting, you can start subscribing and publishing to topics using the app. 44 | ![Subscribing](http://i.imgur.com/dPSryih.png "Subscribing") 45 | 46 | ![Success](http://i.imgur.com/gao1R0x.png "Success") 47 | 48 | ## More About MQTT 49 | MQTT is a messaging protocol which has applications in the Internet of Things (IoT). 50 | This sample project uses Eclipse's open-source implementation called the Paho Project. 51 | If you go to the [original source](https://github.com/eclipse/paho.mqtt.java) where I lifted this project from, there are non-Android sample projects that use the Paho library. 52 | 53 | This youtube video explains the MQTT for IoT at a very basic level. 54 | 55 | [![](http://img.youtube.com/vi/1XzC3WqmiBs/0.jpg)](http://www.youtube.com/watch?v=1XzC3WqmiBs "Basics of MQTT IoT") 56 | 57 | Learn more about the other MQTT options such as QOS, Last Will, etc from this really helpful 12 part series by HiveMQ 58 | http://www.hivemq.com/blog/mqtt-essentials/ 59 | 60 | ## Create Your Own App 61 | All you need is an MQTT broker. 62 | This app is just piggybacking on HiveMQ's free broker. 63 | 64 | Take note of the dependencies in this project. 65 | `org.eclipse.paho.android.service` and `org.eclipse.paho.client.mqttv3` depend on the old android-support-v4, specifically the LocalBroadcastManager class. 66 | 67 | Maybe we can migrate to mqttv5 using the plain Java library at https://github.com/eclipse/paho.mqtt.java 68 | 69 | The eclipse sources can be found at: 70 | https://github.com/eclipse/paho.mqtt.android 71 | 72 | Honestly, when I made an MQTT app for a client, I just built on top of this sample project. 73 | 74 | # Word of Warning 75 | This app was made in 2015-2016, and is a demo of how to use the Eclipse MQTT Libraries, not how to code in Android. 76 | 77 | Architecture components are a thing now, and I strongly advise the use of ViewModel and LifecycleHook. 78 | 79 | # Work In Progress 80 | Definitely look at the "jetpacknav" branch which aims to transform everything into a Single-Activity application, which has been the Android recommendation since 2018. It's currently a work in progress but a clear example of how to transform legacy apps from the old Android paradigm (multiple-Activity) to the newer ones (single-Activity Jetpack). 81 | 82 | # MQTT V3 vs V5 83 | This project uses MQTT v3 and I will be looking into using v3 and v5 in the near future. -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 28 5 | buildToolsVersion '28.0.3' 6 | 7 | defaultConfig { 8 | applicationId "io.bytehala.eclipsemqtt.sample" 9 | minSdkVersion 18 10 | targetSdkVersion 28 11 | multiDexEnabled true 12 | versionCode 3 13 | versionName "1.1-SNAPSHOT" 14 | } 15 | 16 | buildTypes { 17 | release { 18 | minifyEnabled false 19 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt' 20 | } 21 | } 22 | 23 | lintOptions { 24 | abortOnError false 25 | } 26 | 27 | compileOptions { 28 | sourceCompatibility '1.8' 29 | targetCompatibility '1.8' 30 | } 31 | } 32 | 33 | dependencies { 34 | implementation "androidx.core:core:1.2.0" 35 | implementation "com.google.android.material:material:1.1.0" 36 | implementation 'com.android.support:design:28.0.0' 37 | 38 | // Do not remove, even if using AndroidX. Used by :org.eclipse.paho.android.service 39 | implementation 'com.android.support:support-v4:28.0.0' 40 | // Eclipse MQTT libraries 41 | implementation 'org.eclipse.paho:org.eclipse.paho.android.service:1.1.1' 42 | implementation 'org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.2' 43 | 44 | androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0' 45 | } 46 | -------------------------------------------------------------------------------- /app/libs/org.eclipse.paho.android.service-1.0.2.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytehala/android-mqtt-quickstart/4c73f726aff9dc870e187f0c99a9fcade7052fad/app/libs/org.eclipse.paho.android.service-1.0.2.jar -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 43 | 44 | 45 | 46 | 47 | 50 | 51 | 52 | 53 | 56 | 57 | 58 | 59 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /app/src/main/assets/file: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytehala/android-mqtt-quickstart/4c73f726aff9dc870e187f0c99a9fcade7052fad/app/src/main/assets/file -------------------------------------------------------------------------------- /app/src/main/ic_launcher-playstore.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytehala/android-mqtt-quickstart/4c73f726aff9dc870e187f0c99a9fcade7052fad/app/src/main/ic_launcher-playstore.png -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/ActionListener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import io.bytehala.eclipsemqtt.sample.Connection.ConnectionStatus; 16 | import org.eclipse.paho.client.mqttv3.IMqttActionListener; 17 | import org.eclipse.paho.client.mqttv3.IMqttToken; 18 | 19 | import android.content.Context; 20 | import android.widget.Toast; 21 | 22 | /** 23 | * This Class handles receiving information from the 24 | * {@link MqttAndroidClient} and updating the {@link Connection} associated with 25 | * the action 26 | */ 27 | class ActionListener implements IMqttActionListener { 28 | 29 | /** 30 | * Actions that can be performed Asynchronously and associated with a 31 | * {@link ActionListener} object 32 | * 33 | */ 34 | enum Action { 35 | /** Connect Action **/ 36 | CONNECT, 37 | /** Disconnect Action **/ 38 | DISCONNECT, 39 | /** Subscribe Action **/ 40 | SUBSCRIBE, 41 | /** Publish Action **/ 42 | PUBLISH 43 | } 44 | 45 | /** 46 | * The {@link Action} that is associated with this instance of 47 | * ActionListener 48 | **/ 49 | private Action action; 50 | /** The arguments passed to be used for formatting strings**/ 51 | private String[] additionalArgs; 52 | /** Handle of the {@link Connection} this action was being executed on **/ 53 | private String clientHandle; 54 | /** {@link Context} for performing various operations **/ 55 | private Context context; 56 | 57 | /** 58 | * Creates a generic action listener for actions performed form any activity 59 | * 60 | * @param context 61 | * The application context 62 | * @param action 63 | * The action that is being performed 64 | * @param clientHandle 65 | * The handle for the client which the action is being performed 66 | * on 67 | * @param additionalArgs 68 | * Used for as arguments for string formating 69 | */ 70 | public ActionListener(Context context, Action action, 71 | String clientHandle, String... additionalArgs) { 72 | this.context = context; 73 | this.action = action; 74 | this.clientHandle = clientHandle; 75 | this.additionalArgs = additionalArgs; 76 | } 77 | 78 | /** 79 | * The action associated with this listener has been successful. 80 | * 81 | * @param asyncActionToken 82 | * This argument is not used 83 | */ 84 | @Override 85 | public void onSuccess(IMqttToken asyncActionToken) { 86 | switch (action) { 87 | case CONNECT : 88 | connect(); 89 | break; 90 | case DISCONNECT : 91 | disconnect(); 92 | break; 93 | case SUBSCRIBE : 94 | subscribe(); 95 | break; 96 | case PUBLISH : 97 | publish(); 98 | break; 99 | } 100 | 101 | } 102 | 103 | /** 104 | * A publish action has been successfully completed, update connection 105 | * object associated with the client this action belongs to, then notify the 106 | * user of success 107 | */ 108 | private void publish() { 109 | 110 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 111 | String actionTaken = context.getString(R.string.toast_pub_success, 112 | (Object[]) additionalArgs); 113 | c.addAction(actionTaken); 114 | Notify.toast(context, actionTaken, Toast.LENGTH_SHORT); 115 | } 116 | 117 | /** 118 | * A subscribe action has been successfully completed, update the connection 119 | * object associated with the client this action belongs to and then notify 120 | * the user of success 121 | */ 122 | private void subscribe() { 123 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 124 | String actionTaken = context.getString(R.string.toast_sub_success, 125 | (Object[]) additionalArgs); 126 | c.addAction(actionTaken); 127 | Notify.toast(context, actionTaken, Toast.LENGTH_SHORT); 128 | 129 | } 130 | 131 | /** 132 | * A disconnection action has been successfully completed, update the 133 | * connection object associated with the client this action belongs to and 134 | * then notify the user of success. 135 | */ 136 | private void disconnect() { 137 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 138 | c.changeConnectionStatus(ConnectionStatus.DISCONNECTED); 139 | String actionTaken = context.getString(R.string.toast_disconnected); 140 | c.addAction(actionTaken); 141 | 142 | } 143 | 144 | /** 145 | * A connection action has been successfully completed, update the 146 | * connection object associated with the client this action belongs to and 147 | * then notify the user of success. 148 | */ 149 | private void connect() { 150 | 151 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 152 | c.changeConnectionStatus(Connection.ConnectionStatus.CONNECTED); 153 | c.addAction("Client Connected"); 154 | 155 | } 156 | 157 | /** 158 | * The action associated with the object was a failure 159 | * 160 | * @param token 161 | * This argument is not used 162 | * @param exception 163 | * The exception which indicates why the action failed 164 | */ 165 | @Override 166 | public void onFailure(IMqttToken token, Throwable exception) { 167 | switch (action) { 168 | case CONNECT : 169 | connect(exception); 170 | break; 171 | case DISCONNECT : 172 | disconnect(exception); 173 | break; 174 | case SUBSCRIBE : 175 | subscribe(exception); 176 | break; 177 | case PUBLISH : 178 | publish(exception); 179 | break; 180 | } 181 | 182 | } 183 | 184 | /** 185 | * A publish action was unsuccessful, notify user and update client history 186 | * 187 | * @param exception 188 | * This argument is not used 189 | */ 190 | private void publish(Throwable exception) { 191 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 192 | String action = context.getString(R.string.toast_pub_failed, 193 | (Object[]) additionalArgs); 194 | c.addAction(action); 195 | Notify.toast(context, action, Toast.LENGTH_SHORT); 196 | 197 | } 198 | 199 | /** 200 | * A subscribe action was unsuccessful, notify user and update client history 201 | * @param exception This argument is not used 202 | */ 203 | private void subscribe(Throwable exception) { 204 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 205 | String action = context.getString(R.string.toast_sub_failed, 206 | (Object[]) additionalArgs); 207 | c.addAction(action); 208 | Notify.toast(context, action, Toast.LENGTH_SHORT); 209 | 210 | } 211 | 212 | /** 213 | * A disconnect action was unsuccessful, notify user and update client history 214 | * @param exception This argument is not used 215 | */ 216 | private void disconnect(Throwable exception) { 217 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 218 | c.changeConnectionStatus(ConnectionStatus.DISCONNECTED); 219 | c.addAction("Disconnect Failed - an error occured"); 220 | 221 | } 222 | 223 | /** 224 | * A connect action was unsuccessful, notify the user and update client history 225 | * @param exception This argument is not used 226 | */ 227 | private void connect(Throwable exception) { 228 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 229 | c.changeConnectionStatus(Connection.ConnectionStatus.ERROR); 230 | c.addAction("Client failed to connect"); 231 | 232 | } 233 | 234 | } -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/ActivityConstants.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import org.eclipse.paho.client.mqttv3.MqttMessage; 16 | 17 | /** 18 | * This Class provides constants used for returning results from an activity 19 | * 20 | */ 21 | public class ActivityConstants { 22 | 23 | /** Application TAG for logs where class name is not used*/ 24 | static final String TAG = "MQTT Android"; 25 | 26 | /*Default values **/ 27 | 28 | /** Default QOS value*/ 29 | static final int defaultQos = 0; 30 | /** Default timeout*/ 31 | static final int defaultTimeOut = 1000; 32 | /** Default keep alive value*/ 33 | static final int defaultKeepAlive = 10; 34 | /** Default SSL enabled flag*/ 35 | static final boolean defaultSsl = false; 36 | /** Default message retained flag */ 37 | static final boolean defaultRetained = false; 38 | /** Default last will message*/ 39 | static final MqttMessage defaultLastWill = null; 40 | /** Default port*/ 41 | static final int defaultPort = 1883; 42 | 43 | /** Connect Request Code */ 44 | static final int connect = 0; 45 | /** AdvancedActivity Connect Request Code **/ 46 | static final int advancedConnect = 1; 47 | /** Last will Request Code **/ 48 | static final int lastWill = 2; 49 | /** Show History Request Code **/ 50 | static final int showHistory = 3; 51 | 52 | /* Bundle Keys */ 53 | 54 | /** Server Bundle Key **/ 55 | static final String server = "server"; 56 | /** Port Bundle Key **/ 57 | static final String port = "port"; 58 | /** ClientID Bundle Key **/ 59 | static final String clientId = "clientId"; 60 | /** Topic Bundle Key **/ 61 | static final String topic = "topic"; 62 | /** History Bundle Key **/ 63 | static final String history = "history"; 64 | /** Message Bundle Key **/ 65 | static final String message = "message"; 66 | /** Retained Flag Bundle Key **/ 67 | static final String retained = "retained"; 68 | /** QOS Value Bundle Key **/ 69 | static final String qos = "qos"; 70 | /** User name Bundle Key **/ 71 | static final String username = "username"; 72 | /** Password Bundle Key **/ 73 | static final String password = "password"; 74 | /** Keep Alive value Bundle Key **/ 75 | static final String keepalive = "keepalive"; 76 | /** Timeout Bundle Key **/ 77 | static final String timeout = "timeout"; 78 | /** SSL Enabled Flag Bundle Key **/ 79 | static final String ssl = "ssl"; 80 | /** SSL Key File Bundle Key **/ 81 | static final String ssl_key = "ssl_key"; 82 | /** Connections Bundle Key **/ 83 | static final String connections = "connections"; 84 | /** Clean Session Flag Bundle Key **/ 85 | static final String cleanSession = "cleanSession"; 86 | /** Action Bundle Key **/ 87 | static final String action = "action"; 88 | 89 | /* Property names */ 90 | 91 | /** Property name for the history field in {@link Connection} object for use with {@link java.beans.PropertyChangeEvent} **/ 92 | static final String historyProperty = "history"; 93 | 94 | /** Property name for the connection status field in {@link Connection} object for use with {@link java.beans.PropertyChangeEvent} **/ 95 | static final String ConnectionStatusProperty = "connectionStatus"; 96 | 97 | /* Useful constants*/ 98 | 99 | /** Space String Literal **/ 100 | static final String space = " "; 101 | /** Empty String for comparisons **/ 102 | static final String empty = new String(); 103 | 104 | } 105 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/AdvancedActivity.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import java.util.HashMap; 16 | import java.util.Map; 17 | 18 | import android.app.Activity; 19 | import android.app.Dialog; 20 | import android.content.Intent; 21 | import android.os.Bundle; 22 | import android.view.Menu; 23 | import android.view.MenuItem; 24 | import android.view.MenuItem.OnMenuItemClickListener; 25 | import android.view.View; 26 | import android.view.View.OnClickListener; 27 | import android.widget.Button; 28 | import android.widget.CheckBox; 29 | import android.widget.EditText; 30 | 31 | import androidx.appcompat.app.AppCompatActivity; 32 | import androidx.core.app.NavUtils; 33 | 34 | /** 35 | * AdvancedActivity connection options activity 36 | * 37 | */ 38 | public class AdvancedActivity extends AppCompatActivity { 39 | 40 | /** 41 | * Reference to this class used in {@link AdvancedActivity.Listener} methods 42 | */ 43 | private AdvancedActivity advanced = this; 44 | /** 45 | * Holds the result data from activities launched from this activity 46 | */ 47 | private Bundle resultData = null; 48 | 49 | private int openfileDialogId = 0; 50 | 51 | /** 52 | * @see Activity#onCreate(Bundle) 53 | */ 54 | @Override 55 | protected void onCreate(Bundle savedInstanceState) { 56 | super.onCreate(savedInstanceState); 57 | setContentView(R.layout.activity_advanced); 58 | 59 | ((Button) findViewById(R.id.sslKeyBut)).setOnClickListener(new OnClickListener(){ 60 | 61 | @Override 62 | public void onClick(View v) { 63 | //showFileChooser(); 64 | showDialog(openfileDialogId); 65 | }}); 66 | 67 | ((CheckBox) findViewById(R.id.sslCheckBox)).setOnClickListener(new OnClickListener(){ 68 | 69 | @Override 70 | public void onClick(View v) { 71 | if(((CheckBox)v).isChecked()) 72 | { 73 | ((Button)findViewById(R.id.sslKeyBut)).setClickable(true); 74 | }else 75 | { 76 | ((Button)findViewById(R.id.sslKeyBut)).setClickable(false); 77 | } 78 | 79 | }}); 80 | 81 | ((Button)findViewById(R.id.sslKeyBut)).setClickable(false); 82 | } 83 | 84 | /** 85 | * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) 86 | */ 87 | @Override 88 | public boolean onCreateOptionsMenu(Menu menu) { 89 | getMenuInflater().inflate(R.menu.activity_advanced, menu); 90 | 91 | Listener listener = new Listener(); 92 | menu.findItem(R.id.setLastWill).setOnMenuItemClickListener(listener); 93 | menu.findItem(R.id.ok).setOnMenuItemClickListener(listener); 94 | 95 | return true; 96 | } 97 | 98 | /** 99 | * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) 100 | */ 101 | @Override 102 | public boolean onOptionsItemSelected(MenuItem item) { 103 | switch (item.getItemId()) { 104 | case android.R.id.home : 105 | NavUtils.navigateUpFromSameTask(this); 106 | return true; 107 | } 108 | return super.onOptionsItemSelected(item); 109 | } 110 | 111 | /** 112 | * @see android.app.Activity#onActivityResult(int, int, android.content.Intent) 113 | */ 114 | @Override 115 | protected void onActivityResult(int requestCode, int resultCode, 116 | Intent intent) { 117 | // get the last will data 118 | if (resultCode == RESULT_CANCELED) { 119 | return; 120 | } 121 | resultData = intent.getExtras(); 122 | 123 | } 124 | 125 | /** 126 | * @see android.app.Activity#onCreateDialog(int) 127 | */ 128 | @Override 129 | protected Dialog onCreateDialog(int id) { 130 | if (id == openfileDialogId) { 131 | Map images = new HashMap(); 132 | images.put(OpenFileDialog.sRoot, R.mipmap.ic_launcher); 133 | images.put(OpenFileDialog.sParent, R.mipmap.ic_launcher); 134 | images.put(OpenFileDialog.sFolder, R.mipmap.ic_launcher); 135 | images.put("bks", R.mipmap.ic_launcher); 136 | images.put(OpenFileDialog.sEmpty, R.mipmap.ic_launcher); 137 | Dialog dialog = OpenFileDialog.createDialog(id, this, "openfile", 138 | new CallbackBundle() { 139 | @Override 140 | public void callback(Bundle bundle) { 141 | String filepath = bundle.getString("path"); 142 | // setTitle(filepath); 143 | ((EditText) findViewById(R.id.sslKeyLocaltion)) 144 | .setText(filepath); 145 | } 146 | }, ".bks;", images); 147 | return dialog; 148 | } 149 | return null; 150 | } 151 | 152 | /** 153 | * Deals with button clicks for the advanced options page 154 | * 155 | */ 156 | private class Listener implements OnMenuItemClickListener { 157 | 158 | /** 159 | * @see android.view.MenuItem.OnMenuItemClickListener#onMenuItemClick(MenuItem) 160 | */ 161 | @Override 162 | public boolean onMenuItemClick(MenuItem item) { 163 | 164 | int button = item.getItemId(); 165 | 166 | switch (button) { 167 | case R.id.ok : 168 | ok(); 169 | break; 170 | 171 | case R.id.setLastWill : 172 | lastWill(); 173 | break; 174 | } 175 | return false; 176 | } 177 | 178 | /** 179 | * Packs the default options into an intent 180 | * @return intent packed with default options 181 | */ 182 | @SuppressWarnings("unused") 183 | private Intent packDefaults() { 184 | Intent intent = new Intent(); 185 | 186 | // check to see if there is any result data if there is not any 187 | // result data build some with defaults 188 | 189 | intent.putExtras(resultData); 190 | intent.putExtra(ActivityConstants.username, ActivityConstants.empty); 191 | intent.putExtra(ActivityConstants.password, ActivityConstants.empty); 192 | 193 | intent.putExtra(ActivityConstants.timeout, ActivityConstants.defaultTimeOut); 194 | intent.putExtra(ActivityConstants.keepalive, 195 | ActivityConstants.defaultKeepAlive); 196 | intent.putExtra(ActivityConstants.ssl, ActivityConstants.defaultSsl); 197 | 198 | return intent; 199 | } 200 | 201 | /** 202 | * Starts an activity to collect last will options 203 | */ 204 | private void lastWill() { 205 | 206 | Intent intent = new Intent(); 207 | intent.setClassName(advanced, "io.bytehala.eclipsemqtt.sample.LastWillActivity"); 208 | advanced.startActivityForResult(intent, ActivityConstants.lastWill); 209 | 210 | } 211 | 212 | /** 213 | * Packs all the options the user has chosen, along with defaults the user has not chosen 214 | */ 215 | private void ok() { 216 | 217 | int keepalive; 218 | int timeout; 219 | 220 | Intent intent = new Intent(); 221 | 222 | if (resultData == null) { 223 | resultData = new Bundle(); 224 | resultData.putString(ActivityConstants.message, ActivityConstants.empty); 225 | resultData.putString(ActivityConstants.topic, ActivityConstants.empty); 226 | resultData.putInt(ActivityConstants.qos, ActivityConstants.defaultQos); 227 | resultData.putBoolean(ActivityConstants.retained, 228 | ActivityConstants.defaultRetained); 229 | } 230 | 231 | intent.putExtras(resultData); 232 | 233 | // get all advance options 234 | String username = ((EditText) findViewById(R.id.uname)).getText() 235 | .toString(); 236 | String password = ((EditText) findViewById(R.id.password)) 237 | .getText().toString(); 238 | String sslkey = null; 239 | boolean ssl = ((CheckBox) findViewById(R.id.sslCheckBox)).isChecked(); 240 | if(ssl) 241 | { 242 | sslkey = ((EditText) findViewById(R.id.sslKeyLocaltion)) 243 | .getText().toString(); 244 | } 245 | try { 246 | timeout = Integer 247 | .parseInt(((EditText) findViewById(R.id.timeout)) 248 | .getText().toString()); 249 | } 250 | catch (NumberFormatException nfe) { 251 | timeout = ActivityConstants.defaultTimeOut; 252 | } 253 | try { 254 | keepalive = Integer 255 | .parseInt(((EditText) findViewById(R.id.keepalive)) 256 | .getText().toString()); 257 | } 258 | catch (NumberFormatException nfe) { 259 | keepalive = ActivityConstants.defaultKeepAlive; 260 | } 261 | 262 | //put the daya collected into the intent 263 | intent.putExtra(ActivityConstants.username, username); 264 | intent.putExtra(ActivityConstants.password, password); 265 | 266 | intent.putExtra(ActivityConstants.timeout, timeout); 267 | intent.putExtra(ActivityConstants.keepalive, keepalive); 268 | intent.putExtra(ActivityConstants.ssl, ssl); 269 | intent.putExtra(ActivityConstants.ssl_key, sslkey); 270 | //set the result as okay, with the data, and finish 271 | advanced.setResult(RESULT_OK, intent); 272 | advanced.finish(); 273 | } 274 | 275 | } 276 | 277 | } 278 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/CallbackBundle.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | 16 | import android.os.Bundle; 17 | 18 | /** 19 | * For File selector to share data 20 | * 21 | */ 22 | public interface CallbackBundle { 23 | abstract void callback(Bundle bundle); 24 | } 25 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/Connection.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import java.beans.PropertyChangeEvent; 16 | import java.beans.PropertyChangeListener; 17 | import java.text.SimpleDateFormat; 18 | import java.util.ArrayList; 19 | import java.util.Date; 20 | import io.bytehala.eclipsemqtt.sample.R; 21 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 22 | import android.content.Context; 23 | import android.text.Html; 24 | import android.text.Spanned; 25 | import org.eclipse.paho.android.service.MqttAndroidClient; 26 | 27 | /** 28 | * 29 | * Represents a {@link MqttAndroidClient} and the actions it has performed 30 | * 31 | */ 32 | public class Connection { 33 | 34 | /* 35 | * Basic Information about the client 36 | */ 37 | /** ClientHandle for this Connection Object**/ 38 | private String clientHandle = null; 39 | /** The clientId of the client associated with this Connection object **/ 40 | private String clientId = null; 41 | /** The host that the {@link MqttAndroidClient} represented by this Connection is represented by **/ 42 | private String host = null; 43 | /** The port on the server this client is connecting to **/ 44 | private int port = 0; 45 | /** {@link ConnectionStatus} of the {@link MqttAndroidClient} represented by this Connection object. Default value is {@link ConnectionStatus#NONE} **/ 46 | private ConnectionStatus status = ConnectionStatus.NONE; 47 | /** The history of the {@link MqttAndroidClient} represented by this Connection object **/ 48 | private ArrayList history = null; 49 | /** The {@link MqttAndroidClient} instance this class represents**/ 50 | private MqttAndroidClient client = null; 51 | 52 | /** Collection of {@link PropertyChangeListener} **/ 53 | private ArrayList listeners = new ArrayList(); 54 | 55 | /** The {@link Context} of the application this object is part of**/ 56 | private Context context = null; 57 | 58 | /** The {@link MqttConnectOptions} that were used to connect this client**/ 59 | private MqttConnectOptions conOpt; 60 | 61 | /** True if this connection is secured using SSL **/ 62 | private boolean sslConnection = false; 63 | 64 | /** Persistence id, used by {@link Persistence} **/ 65 | private long persistenceId = -1; 66 | 67 | /** 68 | * Connections status for a connection 69 | */ 70 | enum ConnectionStatus { 71 | 72 | /** Client is Connecting **/ 73 | CONNECTING, 74 | /** Client is Connected **/ 75 | CONNECTED, 76 | /** Client is Disconnecting **/ 77 | DISCONNECTING, 78 | /** Client is Disconnected **/ 79 | DISCONNECTED, 80 | /** Client has encountered an Error **/ 81 | ERROR, 82 | /** Status is unknown **/ 83 | NONE 84 | } 85 | 86 | /** 87 | * Creates a connection from persisted information in the database store, attempting 88 | * to create a {@link MqttAndroidClient} and the client handle. 89 | * @param clientId The id of the client 90 | * @param host the server which the client is connecting to 91 | * @param port the port on the server which the client will attempt to connect to 92 | * @param context the application context 93 | * @param sslConnection true if the connection is secured by SSL 94 | * @return a new instance of Connection 95 | */ 96 | public static Connection createConnection(String clientId, String host, 97 | int port, Context context, boolean sslConnection) { 98 | String handle = null; 99 | String uri = null; 100 | if (sslConnection) { 101 | uri = "ssl://" + host + ":" + port; 102 | handle = uri + clientId; 103 | } 104 | else { 105 | uri = "tcp://" + host + ":" + port; 106 | handle = uri + clientId; 107 | } 108 | MqttAndroidClient client = new MqttAndroidClient(context, uri, clientId); 109 | return new Connection(handle, clientId, host, port, context, client, sslConnection); 110 | 111 | } 112 | 113 | /** 114 | * Creates a connection object with the server information and the client 115 | * hand which is the reference used to pass the client around activities 116 | * @param clientHandle The handle to this Connection object 117 | * @param clientId The Id of the client 118 | * @param host The server which the client is connecting to 119 | * @param port The port on the server which the client will attempt to connect to 120 | * @param context The application context 121 | * @param client The MqttAndroidClient which communicates with the service for this connection 122 | * @param sslConnection true if the connection is secured by SSL 123 | */ 124 | public Connection(String clientHandle, String clientId, String host, 125 | int port, Context context, MqttAndroidClient client, boolean sslConnection) { 126 | //generate the client handle from its hash code 127 | this.clientHandle = clientHandle; 128 | this.clientId = clientId; 129 | this.host = host; 130 | this.port = port; 131 | this.context = context; 132 | this.client = client; 133 | this.sslConnection = sslConnection; 134 | history = new ArrayList(); 135 | StringBuffer sb = new StringBuffer(); 136 | sb.append("Client: "); 137 | sb.append(clientId); 138 | sb.append(" created"); 139 | addAction(sb.toString()); 140 | } 141 | 142 | /** 143 | * Add an action to the history of the client 144 | * @param action the history item to add 145 | */ 146 | public void addAction(String action) { 147 | 148 | Object[] args = new String[1]; 149 | SimpleDateFormat sdf = new SimpleDateFormat(context.getString(R.string.dateFormat)); 150 | args[0] = sdf.format(new Date()); 151 | 152 | String timestamp = context.getString(R.string.timestamp, args); 153 | history.add(action + timestamp); 154 | 155 | notifyListeners(new PropertyChangeEvent(this, ActivityConstants.historyProperty, null, null)); 156 | } 157 | 158 | /** 159 | * Generate an array of Spanned items representing the history of this 160 | * connection. 161 | * 162 | * @return an array of history entries 163 | */ 164 | public Spanned[] history() { 165 | 166 | int i = 0; 167 | Spanned[] array = new Spanned[history.size()]; 168 | 169 | for (String s : history) { 170 | if (s != null) { 171 | array[i] = Html.fromHtml(s); 172 | i++; 173 | } 174 | } 175 | 176 | return array; 177 | 178 | } 179 | 180 | /** 181 | * Gets the client handle for this connection 182 | * @return client Handle for this connection 183 | */ 184 | public String handle() { 185 | return clientHandle; 186 | } 187 | 188 | /** 189 | * Determines if the client is connected 190 | * @return is the client connected 191 | */ 192 | public boolean isConnected() { 193 | return status == ConnectionStatus.CONNECTED; 194 | } 195 | 196 | /** 197 | * Changes the connection status of the client 198 | * @param connectionStatus The connection status of this connection 199 | */ 200 | public void changeConnectionStatus(ConnectionStatus connectionStatus) { 201 | status = connectionStatus; 202 | notifyListeners((new PropertyChangeEvent(this, ActivityConstants.ConnectionStatusProperty, null, null))); 203 | } 204 | 205 | /** 206 | * A string representing the state of the client this connection 207 | * object represents 208 | * 209 | * 210 | * @return A string representing the state of the client 211 | */ 212 | @Override 213 | public String toString() { 214 | StringBuffer sb = new StringBuffer(); 215 | sb.append(clientId); 216 | sb.append("\n "); 217 | 218 | switch (status) { 219 | 220 | case CONNECTED : 221 | sb.append(context.getString(R.string.connectedto)); 222 | break; 223 | case DISCONNECTED : 224 | sb.append(context.getString(R.string.disconnected)); 225 | break; 226 | case NONE : 227 | sb.append(context.getString(R.string.no_status)); 228 | break; 229 | case CONNECTING : 230 | sb.append(context.getString(R.string.connecting)); 231 | break; 232 | case DISCONNECTING : 233 | sb.append(context.getString(R.string.disconnecting)); 234 | break; 235 | case ERROR : 236 | sb.append(context.getString(R.string.connectionError)); 237 | } 238 | sb.append(" "); 239 | sb.append(host); 240 | 241 | return sb.toString(); 242 | } 243 | 244 | /** 245 | * Determines if a given handle refers to this client 246 | * @param handle The handle to compare with this clients handle 247 | * @return true if the handles match 248 | */ 249 | public boolean isHandle(String handle) { 250 | return clientHandle.equals(handle); 251 | } 252 | 253 | /** 254 | * Compares two connection objects for equality 255 | * this only takes account of the client handle 256 | * @param o The object to compare to 257 | * @return true if the client handles match 258 | */ 259 | @Override 260 | public boolean equals(Object o) { 261 | if (!(o instanceof Connection)) { 262 | return false; 263 | } 264 | 265 | Connection c = (Connection) o; 266 | 267 | return clientHandle.equals(c.clientHandle); 268 | 269 | } 270 | 271 | /** 272 | * Get the client Id for the client this object represents 273 | * @return the client id for the client this object represents 274 | */ 275 | public String getId() { 276 | return clientId; 277 | } 278 | 279 | /** 280 | * Get the host name of the server that this connection object is associated with 281 | * @return the host name of the server this connection object is associated with 282 | */ 283 | public String getHostName() { 284 | 285 | return host; 286 | } 287 | 288 | /** 289 | * Determines if the client is in a state of connecting or connected. 290 | * @return if the client is connecting or connected 291 | */ 292 | public boolean isConnectedOrConnecting() { 293 | return (status == ConnectionStatus.CONNECTED) || (status == ConnectionStatus.CONNECTING); 294 | } 295 | 296 | /** 297 | * Client is currently not in an error state 298 | * @return true if the client is in not an error state 299 | */ 300 | public boolean noError() { 301 | return status != ConnectionStatus.ERROR; 302 | } 303 | 304 | /** 305 | * Gets the client which communicates with the android service. 306 | * @return the client which communicates with the android service 307 | */ 308 | public MqttAndroidClient getClient() { 309 | return client; 310 | } 311 | 312 | /** 313 | * Add the connectOptions used to connect the client to the server 314 | * @param connectOptions the connectOptions used to connect to the server 315 | */ 316 | public void addConnectionOptions(MqttConnectOptions connectOptions) { 317 | conOpt = connectOptions; 318 | 319 | } 320 | 321 | /** 322 | * Get the connectOptions used to connect this client to the server 323 | * @return The connectOptions used to connect the client to the server 324 | */ 325 | public MqttConnectOptions getConnectionOptions() 326 | { 327 | return conOpt; 328 | } 329 | 330 | /** 331 | * Register a {@link PropertyChangeListener} to this object 332 | * @param listener the listener to register 333 | */ 334 | public void registerChangeListener(PropertyChangeListener listener) 335 | { 336 | listeners.add(listener); 337 | } 338 | 339 | /** 340 | * Remove a registered {@link PropertyChangeListener} 341 | * @param listener A reference to the listener to remove 342 | */ 343 | public void removeChangeListener(PropertyChangeListener listener) 344 | { 345 | if (listener != null) { 346 | listeners.remove(listener); 347 | } 348 | } 349 | 350 | /** 351 | * Notify {@link PropertyChangeListener} objects that the object has been updated 352 | * @param propertyChangeEvent 353 | */ 354 | private void notifyListeners(PropertyChangeEvent propertyChangeEvent) 355 | { 356 | for (PropertyChangeListener listener : listeners) 357 | { 358 | listener.propertyChange(propertyChangeEvent); 359 | } 360 | } 361 | 362 | /** 363 | * Gets the port that this connection connects to. 364 | * @return port that this connection connects to 365 | */ 366 | public int getPort() { 367 | return port; 368 | } 369 | 370 | /** 371 | * Determines if the connection is secured using SSL, returning a C style integer value 372 | * @return 1 if SSL secured 0 if plain text 373 | */ 374 | public int isSSL() { 375 | return sslConnection ? 1 : 0; 376 | } 377 | 378 | /** 379 | * Assign a persistence ID to this object 380 | * @param id the persistence id to assign 381 | */ 382 | public void assignPersistenceId(long id) { 383 | persistenceId = id; 384 | } 385 | 386 | /** 387 | * Returns the persistence ID assigned to this object 388 | * @return the persistence ID assigned to this object 389 | */ 390 | public long persistenceId() { 391 | return persistenceId; 392 | } 393 | } 394 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/ConnectionDetailsActivity.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import java.beans.PropertyChangeEvent; 16 | import java.beans.PropertyChangeListener; 17 | import java.util.ArrayList; 18 | 19 | 20 | import android.app.PendingIntent; 21 | import android.content.Intent; 22 | import android.os.Bundle; 23 | import android.telephony.SmsManager; 24 | import android.util.Log; 25 | import android.view.Menu; 26 | import android.widget.CheckBox; 27 | import android.widget.EditText; 28 | import android.widget.RadioGroup; 29 | 30 | import androidx.appcompat.app.ActionBar; 31 | import androidx.appcompat.app.AppCompatActivity; 32 | import androidx.fragment.app.Fragment; 33 | import androidx.fragment.app.FragmentManager; 34 | import androidx.fragment.app.FragmentPagerAdapter; 35 | import androidx.fragment.app.FragmentTransaction; 36 | import androidx.viewpager.widget.ViewPager; 37 | 38 | import org.eclipse.paho.client.mqttv3.MqttException; 39 | import org.eclipse.paho.client.mqttv3.MqttSecurityException; 40 | 41 | /** 42 | * The connection details activity operates the fragments that make up the 43 | * connection details screen. 44 | *

45 | * The fragments which this FragmentActivity uses are 46 | *

    47 | *
  • {@link HistoryFragment} 48 | *
  • {@link PublishFragment} 49 | *
  • {@link SubscribeFragment} 50 | *
51 | * 52 | */ 53 | public class ConnectionDetailsActivity extends AppCompatActivity implements 54 | ActionBar.TabListener { 55 | 56 | /** 57 | * {@link SectionsPagerAdapter} that is used to get pages to display 58 | */ 59 | SectionsPagerAdapter sectionsPagerAdapter; 60 | /** 61 | * {@link ViewPager} object allows pages to be flipped left and right 62 | */ 63 | ViewPager viewPager; 64 | 65 | /** The currently selected tab **/ 66 | private int selected = 0; 67 | 68 | /** 69 | * The handle to the {@link Connection} which holds the data for the client 70 | * selected 71 | **/ 72 | private String clientHandle = null; 73 | 74 | /** This instance of ConnectionDetailsActivity **/ 75 | private final ConnectionDetailsActivity connectionDetails = this; 76 | 77 | /** 78 | * The instance of {@link Connection} that the clientHandle 79 | * represents 80 | **/ 81 | private Connection connection = null; 82 | 83 | /** 84 | * The {@link ChangeListener} this object is using for the connection 85 | * updates 86 | **/ 87 | private ChangeListener changeListener = null; 88 | 89 | /** 90 | * @see android.support.v4.app.FragmentActivity#onCreate(android.os.Bundle) 91 | */ 92 | @Override 93 | protected void onCreate(Bundle savedInstanceState) { 94 | super.onCreate(savedInstanceState); 95 | 96 | clientHandle = getIntent().getStringExtra("handle"); 97 | 98 | setContentView(R.layout.activity_connection_details); 99 | // Create the adapter that will return a fragment for each of the pages 100 | sectionsPagerAdapter = new SectionsPagerAdapter( 101 | getSupportFragmentManager()); 102 | 103 | // Set up the action bar for tab navigation 104 | final ActionBar actionBar = getSupportActionBar(); 105 | actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS); 106 | 107 | // add the sectionsPagerAdapter 108 | viewPager = (ViewPager) findViewById(R.id.pager); 109 | viewPager.setAdapter(sectionsPagerAdapter); 110 | 111 | viewPager 112 | .setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() { 113 | 114 | @Override 115 | public void onPageSelected(int position) { 116 | // select the tab that represents the current page 117 | actionBar.setSelectedNavigationItem(position); 118 | 119 | } 120 | }); 121 | 122 | // Create the tabs for the screen 123 | for (int i = 0; i < sectionsPagerAdapter.getCount(); i++) { 124 | ActionBar.Tab tab = actionBar.newTab(); 125 | tab.setText(sectionsPagerAdapter.getPageTitle(i)); 126 | tab.setTabListener(this); 127 | actionBar.addTab(tab); 128 | } 129 | 130 | connection = Connections.getInstance(this).getConnection(clientHandle); 131 | changeListener = new ChangeListener(); 132 | connection.registerChangeListener(changeListener); 133 | } 134 | 135 | @Override 136 | protected void onDestroy() { 137 | connection.removeChangeListener(null); 138 | super.onDestroy(); 139 | } 140 | 141 | /** 142 | * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) 143 | */ 144 | @Override 145 | public boolean onCreateOptionsMenu(Menu menu) { 146 | int menuID; 147 | Integer button = null; 148 | boolean connected = Connections.getInstance(this) 149 | .getConnection(clientHandle).isConnected(); 150 | 151 | // Select the correct action bar menu to display based on the 152 | // connectionStatus and which tab is selected 153 | if (connected) { 154 | 155 | switch (selected) { 156 | case 0 : // history view 157 | menuID = R.menu.activity_connection_details; 158 | break; 159 | case 1 : // subscribe view 160 | menuID = R.menu.activity_subscribe; 161 | button = R.id.subscribe; 162 | break; 163 | case 2 : // publish view 164 | menuID = R.menu.activity_publish; 165 | button = R.id.publish; 166 | break; 167 | default : 168 | menuID = R.menu.activity_connection_details; 169 | break; 170 | } 171 | } 172 | else { 173 | switch (selected) { 174 | case 0 : // history view 175 | menuID = R.menu.activity_connection_details_disconnected; 176 | break; 177 | case 1 : // subscribe view 178 | menuID = R.menu.activity_subscribe_disconnected; 179 | button = R.id.subscribe; 180 | break; 181 | case 2 : // publish view 182 | menuID = R.menu.activity_publish_disconnected; 183 | button = R.id.publish; 184 | break; 185 | default : 186 | menuID = R.menu.activity_connection_details_disconnected; 187 | break; 188 | } 189 | } 190 | // inflate the menu selected 191 | getMenuInflater().inflate(menuID, menu); 192 | Listener listener = new Listener(this, clientHandle); 193 | // add listeners 194 | if (button != null) { 195 | // add listeners 196 | menu.findItem(button).setOnMenuItemClickListener(listener); 197 | if (!Connections.getInstance(this).getConnection(clientHandle) 198 | .isConnected()) { 199 | menu.findItem(button).setEnabled(false); 200 | } 201 | } 202 | // add the listener to the disconnect or connect menu option 203 | if (connected) { 204 | menu.findItem(R.id.disconnect).setOnMenuItemClickListener(listener); 205 | } 206 | else { 207 | menu.findItem(R.id.connectMenuOption).setOnMenuItemClickListener( 208 | listener); 209 | } 210 | 211 | return true; 212 | } 213 | 214 | /** 215 | * @see android.app.ActionBar.TabListener#onTabUnselected(android.app.ActionBar.Tab, 216 | * android.app.FragmentTransaction) 217 | */ 218 | @Override 219 | public void onTabUnselected(ActionBar.Tab tab, 220 | FragmentTransaction fragmentTransaction) { 221 | // Don't need to do anything when a tab is unselected 222 | } 223 | 224 | /** 225 | * @see android.app.ActionBar.TabListener#onTabSelected(android.app.ActionBar.Tab, 226 | * android.app.FragmentTransaction) 227 | */ 228 | @Override 229 | public void onTabSelected(ActionBar.Tab tab, 230 | FragmentTransaction fragmentTransaction) { 231 | // When the given tab is selected, switch to the corresponding page in 232 | // the ViewPager. 233 | viewPager.setCurrentItem(tab.getPosition()); 234 | selected = tab.getPosition(); 235 | // invalidate the options menu so it can be updated 236 | invalidateOptionsMenu(); 237 | 238 | updateButtons(); 239 | 240 | 241 | // history fragment is at position zero so get this then refresh its 242 | // view 243 | ((HistoryFragment) sectionsPagerAdapter.getItem(0)).refresh(); 244 | } 245 | 246 | /** 247 | * @see android.app.ActionBar.TabListener#onTabReselected(android.app.ActionBar.Tab, 248 | * android.app.FragmentTransaction) 249 | */ 250 | @Override 251 | public void onTabReselected(ActionBar.Tab tab, 252 | FragmentTransaction fragmentTransaction) { 253 | // Don't need to do anything when the tab is reselected 254 | } 255 | 256 | /** 257 | * Provides the Activity with the pages to display for each tab 258 | * 259 | */ 260 | public class SectionsPagerAdapter extends FragmentPagerAdapter { 261 | 262 | // Stores the instances of the pages 263 | private ArrayList fragments = null; 264 | 265 | /** 266 | * Only Constructor, requires a the activity's fragment managers 267 | * 268 | * @param fragmentManager 269 | */ 270 | public SectionsPagerAdapter(FragmentManager fragmentManager) { 271 | super(fragmentManager); 272 | fragments = new ArrayList(); 273 | // create the history view, passes the client handle as an argument 274 | // through a bundle 275 | Fragment fragment = new HistoryFragment(); 276 | Bundle args = new Bundle(); 277 | args.putString("handle", getIntent().getStringExtra("handle")); 278 | fragment.setArguments(args); 279 | // add all the fragments for the display to the fragments list 280 | fragments.add(fragment); 281 | fragments.add(new SubscribeFragment()); 282 | fragments.add(new PublishFragment()); 283 | 284 | } 285 | 286 | /** 287 | * @see android.support.v4.app.FragmentPagerAdapter#getItem(int) 288 | */ 289 | @Override 290 | public Fragment getItem(int position) { 291 | return fragments.get(position); 292 | } 293 | 294 | /** 295 | * @see android.support.v4.view.PagerAdapter#getCount() 296 | */ 297 | @Override 298 | public int getCount() { 299 | return fragments.size(); 300 | } 301 | 302 | /** 303 | * 304 | * @see FragmentPagerAdapter#getPageTitle(int) 305 | */ 306 | @Override 307 | public CharSequence getPageTitle(int position) { 308 | switch (position) { 309 | case 0 : 310 | return getString(R.string.history).toUpperCase(); 311 | case 1 : 312 | return getString(R.string.subscribe).toUpperCase(); 313 | case 2 : 314 | return getString(R.string.publish).toUpperCase(); 315 | } 316 | // return null if there is no title matching the position 317 | return null; 318 | } 319 | 320 | } 321 | 322 | /** 323 | * ChangeListener updates the UI when the {@link Connection} 324 | * object it is associated with updates 325 | * 326 | */ 327 | private class ChangeListener implements PropertyChangeListener { 328 | 329 | /** 330 | * @see java.beans.PropertyChangeListener#propertyChange(java.beans.PropertyChangeEvent) 331 | */ 332 | @Override 333 | public void propertyChange(PropertyChangeEvent event) { 334 | // connection object has change refresh the UI 335 | 336 | connectionDetails.runOnUiThread(new Runnable() { 337 | 338 | @Override 339 | public void run() { 340 | connectionDetails.invalidateOptionsMenu(); 341 | ((HistoryFragment) connectionDetails.sectionsPagerAdapter 342 | .getItem(0)).refresh(); 343 | 344 | updateButtons(); 345 | 346 | } 347 | }); 348 | 349 | } 350 | } 351 | 352 | private void updateButtons() { 353 | /* 354 | After sending SMS we would crash here , @param clientHandle is null 355 | */ 356 | 357 | boolean connected = Connections.getInstance(connectionDetails) 358 | .getConnection(clientHandle).isConnected(); 359 | 360 | if(selected == 2) { 361 | connectionDetails.findViewById(R.id.publishButton).setEnabled(connected); 362 | connectionDetails.findViewById(R.id.publishButton).setOnClickListener( 363 | view -> publish() 364 | ); 365 | } 366 | if(selected == 1) { 367 | connectionDetails.findViewById(R.id.subscribeButton).setEnabled(connected); 368 | connectionDetails.findViewById(R.id.subscribeButton).setOnClickListener( 369 | view -> subscribe() 370 | ); 371 | } 372 | } 373 | 374 | /** 375 | * Subscribe to a topic that the user has specified 376 | */ 377 | private void subscribe() 378 | { 379 | String topic = ((EditText) connectionDetails.findViewById(R.id.topic)).getText().toString(); 380 | ((EditText) connectionDetails.findViewById(R.id.topic)).getText().clear(); 381 | 382 | RadioGroup radio = (RadioGroup) connectionDetails.findViewById(R.id.qosSubRadio); 383 | int checked = radio.getCheckedRadioButtonId(); 384 | int qos = ActivityConstants.defaultQos; 385 | 386 | switch (checked) { 387 | case R.id.qos0 : 388 | qos = 0; 389 | break; 390 | case R.id.qos1 : 391 | qos = 1; 392 | break; 393 | case R.id.qos2 : 394 | qos = 2; 395 | break; 396 | } 397 | 398 | try { 399 | String[] topics = new String[1]; 400 | topics[0] = topic; 401 | Connections.getInstance(this).getConnection(clientHandle).getClient() 402 | .subscribe(topic, qos, null, new ActionListener(this, ActionListener.Action.SUBSCRIBE, clientHandle, topics)); 403 | } 404 | catch (MqttSecurityException e) { 405 | Log.e(this.getClass().getCanonicalName(), "Failed to subscribe to" + topic + " the client with the handle " + clientHandle, e); 406 | } 407 | catch (MqttException e) { 408 | Log.e(this.getClass().getCanonicalName(), "Failed to subscribe to" + topic + " the client with the handle " + clientHandle, e); 409 | } 410 | } 411 | 412 | /** 413 | * Publish the message the user has specified 414 | */ 415 | private void publish() 416 | { 417 | String topic = ((EditText) connectionDetails.findViewById(R.id.lastWillTopic)) 418 | .getText().toString(); 419 | 420 | ((EditText) connectionDetails.findViewById(R.id.lastWillTopic)).getText().clear(); 421 | 422 | String message = ((EditText) connectionDetails.findViewById(R.id.lastWill)).getText() 423 | .toString(); 424 | 425 | ((EditText) connectionDetails.findViewById(R.id.lastWill)).getText().clear(); 426 | 427 | RadioGroup radio = (RadioGroup) connectionDetails.findViewById(R.id.qosRadio); 428 | int checked = radio.getCheckedRadioButtonId(); 429 | int qos = ActivityConstants.defaultQos; 430 | 431 | switch (checked) { 432 | case R.id.qos0 : 433 | qos = 0; 434 | break; 435 | case R.id.qos1 : 436 | qos = 1; 437 | break; 438 | case R.id.qos2 : 439 | qos = 2; 440 | break; 441 | } 442 | 443 | boolean retained = ((CheckBox) connectionDetails.findViewById(R.id.retained)) 444 | .isChecked(); 445 | 446 | String[] args = new String[2]; 447 | args[0] = message; 448 | args[1] = topic+";qos:"+qos+";retained:"+retained; 449 | 450 | try { 451 | Connections.getInstance(this).getConnection(clientHandle).getClient() 452 | .publish(topic, message.getBytes(), qos, retained, null, new ActionListener(this, ActionListener.Action.PUBLISH, clientHandle, args)); 453 | } catch (MqttException e) { 454 | Log.e(this.getClass().getCanonicalName(), "Failed to publish a messged from the client with the handle " + clientHandle, e); 455 | } 456 | 457 | } 458 | 459 | } 460 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/Connections.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import java.util.HashMap; 16 | import java.util.List; 17 | import java.util.Map; 18 | 19 | import android.content.Context; 20 | 21 | import org.eclipse.paho.android.service.MqttAndroidClient; 22 | 23 | /** 24 | * Connections is a singleton class which stores all the connection objects 25 | * in one central place so they can be passed between activities using a client 26 | * handle 27 | * 28 | */ 29 | public class Connections { 30 | 31 | /** Singleton instance of Connections**/ 32 | private static Connections instance = null; 33 | 34 | /** List of {@link Connection} objects**/ 35 | private HashMap connections = null; 36 | 37 | /** {@link Persistence} object used to save, delete and restore connections**/ 38 | private Persistence persistence = null; 39 | 40 | /** 41 | * Create a Connections object 42 | * @param context Applications context 43 | */ 44 | private Connections(Context context) 45 | { 46 | connections = new HashMap(); 47 | 48 | //attempt to restore state 49 | persistence = new Persistence(context); 50 | try { 51 | List l = persistence.restoreConnections(context); 52 | for (Connection c : l) { 53 | connections.put(c.handle(), c); 54 | } 55 | } 56 | catch (PersistenceException e) { 57 | e.printStackTrace(); 58 | } 59 | 60 | } 61 | 62 | /** 63 | * Returns an already initialised instance of Connections, if Connections has yet to be created, it will 64 | * create and return that instance 65 | * @param context The applications context used to create the Connections object if it is not already initialised 66 | * @return Connections instance 67 | */ 68 | public synchronized static Connections getInstance(Context context) 69 | { 70 | if (instance == null) { 71 | instance = new Connections(context); 72 | } 73 | 74 | return instance; 75 | } 76 | 77 | /** 78 | * Finds and returns a connection object that the given client handle points to 79 | * @param handle The handle to the Connection to return 80 | * @return a connection associated with the client handle, null if one is not found 81 | */ 82 | public Connection getConnection(String handle) 83 | { 84 | 85 | return connections.get(handle); 86 | } 87 | 88 | /** 89 | * Adds a Connection object to the collection of connections associated with this object 90 | * @param connection connection to add 91 | */ 92 | public void addConnection(Connection connection) 93 | { 94 | connections.put(connection.handle(), connection); 95 | try { 96 | persistence.persistConnection(connection); 97 | } 98 | catch (PersistenceException e) 99 | { 100 | //error persisting well lets just swallow this 101 | e.printStackTrace(); 102 | } 103 | } 104 | 105 | /** 106 | * Create a fully initialised MqttAndroidClient for the parameters given 107 | * @param context The Applications context 108 | * @param serverURI The ServerURI to connect to 109 | * @param clientId The clientId for this client 110 | * @return new instance of MqttAndroidClient 111 | */ 112 | public MqttAndroidClient createClient(Context context, String serverURI, String clientId) 113 | { 114 | MqttAndroidClient client = new MqttAndroidClient(context, serverURI, clientId); 115 | return client; 116 | } 117 | 118 | /** 119 | * Get all the connections associated with this Connections object. 120 | * @return Map of connections 121 | */ 122 | public Map getConnections() 123 | { 124 | return connections; 125 | } 126 | 127 | /** 128 | * Removes a connection from the map of connections 129 | * @param connection connection to be removed 130 | */ 131 | public void removeConnection(Connection connection) { 132 | connections.remove(connection.handle()); 133 | persistence.deleteConnection(connection); 134 | } 135 | 136 | } 137 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/HistoryFragment.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import android.os.Bundle; 16 | import android.text.Spanned; 17 | import android.widget.ArrayAdapter; 18 | 19 | import androidx.fragment.app.ListFragment; 20 | import io.bytehala.eclipsemqtt.sample.R; 21 | 22 | /** 23 | * This fragment displays the history information for a client 24 | * 25 | */ 26 | public class HistoryFragment extends ListFragment { 27 | 28 | /** Client handle to a {@link Connection} object **/ 29 | String clientHandle = null; 30 | /** {@link ArrayAdapter} to display the formatted text **/ 31 | ArrayAdapter arrayAdapter = null; 32 | 33 | /** 34 | * @see ListFragment#onCreate(Bundle) 35 | */ 36 | @Override 37 | public void onCreate(Bundle savedInstanceState) { 38 | super.onCreate(savedInstanceState); 39 | 40 | //Pull history information out of bundle 41 | 42 | clientHandle = getArguments().getString("handle"); 43 | Connection connection = Connections.getInstance(getActivity()).getConnection(clientHandle); 44 | 45 | Spanned[] history = connection.history(); 46 | 47 | //Initialise the arrayAdapter, view and add data 48 | arrayAdapter = new ArrayAdapter(getActivity(), R.layout.list_view_text_view); 49 | 50 | arrayAdapter.addAll(history); 51 | setListAdapter(arrayAdapter); 52 | 53 | } 54 | 55 | /** 56 | * Updates the data displayed to match the current history 57 | */ 58 | public void refresh() { 59 | if (arrayAdapter != null) { 60 | arrayAdapter.clear(); 61 | arrayAdapter.addAll(Connections.getInstance(getActivity()).getConnection(clientHandle).history()); 62 | arrayAdapter.notifyDataSetChanged(); 63 | } 64 | 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/LastWillActivity.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import android.app.Activity; 16 | import android.content.Intent; 17 | import android.os.Bundle; 18 | import android.view.Menu; 19 | import android.view.MenuItem; 20 | import android.view.MenuItem.OnMenuItemClickListener; 21 | import android.widget.CheckBox; 22 | import android.widget.EditText; 23 | import android.widget.RadioGroup; 24 | 25 | import androidx.appcompat.app.AppCompatActivity; 26 | 27 | /** 28 | * Activity for setting the last will message for the client 29 | * 30 | */ 31 | public class LastWillActivity extends AppCompatActivity { 32 | 33 | /** 34 | * Reference to the current instance of LastWillActivity for use with anonymous listener 35 | */ 36 | private LastWillActivity last = this; 37 | 38 | /** 39 | * @see Activity#onCreate(Bundle) 40 | */ 41 | @Override 42 | protected void onCreate(Bundle savedInstanceState) { 43 | super.onCreate(savedInstanceState); 44 | setContentView(R.layout.activity_last_will); 45 | 46 | } 47 | 48 | /** 49 | * @see Activity#onCreateOptionsMenu(Menu) 50 | */ 51 | @Override 52 | public boolean onCreateOptionsMenu(Menu menu) { 53 | getMenuInflater().inflate(R.menu.activity_last_will, menu); 54 | 55 | menu.findItem(R.id.publish).setOnMenuItemClickListener(new OnMenuItemClickListener() { 56 | 57 | @Override 58 | public boolean onMenuItemClick(MenuItem item) { 59 | 60 | Intent result = new Intent(); 61 | 62 | String message = ((EditText) findViewById(R.id.lastWill)).getText().toString(); 63 | String topic = ((EditText) findViewById(R.id.lastWillTopic)).getText().toString(); 64 | 65 | RadioGroup radio = (RadioGroup) findViewById(R.id.qosRadio); 66 | int checked = radio.getCheckedRadioButtonId(); 67 | int qos = ActivityConstants.defaultQos; 68 | 69 | //determine which qos value has been selected 70 | switch (checked) 71 | { 72 | case R.id.qos0 : 73 | qos = 0; 74 | break; 75 | case R.id.qos1 : 76 | qos = 1; 77 | break; 78 | case R.id.qos2 : 79 | qos = 2; 80 | break; 81 | } 82 | 83 | boolean retained = ((CheckBox) findViewById(R.id.retained)).isChecked(); 84 | 85 | //package the data collected into the intent 86 | result.putExtra(ActivityConstants.message, message); 87 | result.putExtra(ActivityConstants.topic, topic); 88 | result.putExtra(ActivityConstants.qos, qos); 89 | result.putExtra(ActivityConstants.retained, retained); 90 | 91 | //set the result and finish activity 92 | last.setResult(RESULT_OK, result); 93 | last.finish(); 94 | 95 | return false; 96 | } 97 | 98 | }); 99 | return true; 100 | } 101 | 102 | } 103 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/Listener.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import java.io.IOException; 16 | import java.io.InputStream; 17 | import java.util.HashMap; 18 | import java.util.Map.Entry; 19 | import java.util.logging.LogManager; 20 | 21 | import org.eclipse.paho.client.mqttv3.MqttException; 22 | import org.eclipse.paho.client.mqttv3.MqttSecurityException; 23 | 24 | import android.app.Activity; 25 | import android.content.Context; 26 | import android.content.Intent; 27 | import android.util.Log; 28 | import android.view.MenuItem; 29 | import android.view.MenuItem.OnMenuItemClickListener; 30 | import android.widget.CheckBox; 31 | import android.widget.EditText; 32 | import android.widget.RadioGroup; 33 | 34 | import io.bytehala.eclipsemqtt.sample.ActionListener.Action; 35 | import io.bytehala.eclipsemqtt.sample.Connection.ConnectionStatus; 36 | import org.eclipse.paho.android.service.MqttAndroidClient; 37 | 38 | /** 39 | * Deals with actions performed in the {@link ClientConnections} activity 40 | * and the {@link ConnectionDetailsActivity} activity and associated fragments 41 | * 42 | */ 43 | public class Listener implements OnMenuItemClickListener { 44 | 45 | /** The handle to a {@link Connection} object which contains the {@link MqttAndroidClient} associated with this object **/ 46 | private String clientHandle = null; 47 | 48 | /** {@link ConnectionDetailsActivity} reference used to perform some actions**/ 49 | private ConnectionDetailsActivity connectionDetails = null; 50 | /** {@link ClientConnections} reference used to perform some actions**/ 51 | private Activity clientConnections = null; 52 | /** {@link Context} used to load and format strings **/ 53 | private Context context = null; 54 | 55 | /** Whether Paho is logging is enabled**/ 56 | static boolean logging = false; 57 | 58 | /** 59 | * Constructs a listener object for use with {@link ConnectionDetailsActivity} activity and 60 | * associated fragments. 61 | * @param connectionDetails The instance of {@link ConnectionDetailsActivity} 62 | * @param clientHandle The handle to the client that the actions are to be performed on 63 | */ 64 | public Listener(ConnectionDetailsActivity connectionDetails, String clientHandle) 65 | { 66 | this.connectionDetails = connectionDetails; 67 | this.clientHandle = clientHandle; 68 | context = connectionDetails; 69 | 70 | } 71 | 72 | /** 73 | * Constructs a listener object for use with {@link ClientConnections} activity. 74 | * @param clientConnections The instance of {@link ClientConnections} 75 | */ 76 | public Listener(Activity clientConnections) { 77 | this.clientConnections = clientConnections; 78 | context = clientConnections; 79 | } 80 | 81 | /** 82 | * Perform the needed action required based on the button that 83 | * the user has clicked. 84 | * 85 | * @param item The menu item that was clicked 86 | * @return If there is anymore processing to be done 87 | * 88 | */ 89 | @Override 90 | public boolean onMenuItemClick(MenuItem item) { 91 | 92 | int id = item.getItemId(); 93 | 94 | switch (id) 95 | { 96 | case R.id.publish : 97 | publish(); 98 | break; 99 | case R.id.subscribe : 100 | subscribe(); 101 | break; 102 | case R.id.newConnection : 103 | createAndConnect(); 104 | break; 105 | case R.id.disconnect : 106 | disconnect(); 107 | break; 108 | case R.id.connectMenuOption : 109 | reconnect(); 110 | break; 111 | case R.id.startLogging : 112 | enablePahoLogging(); 113 | break; 114 | case R.id.endLogging : 115 | disablePahoLogging(); 116 | break; 117 | } 118 | 119 | return false; 120 | } 121 | 122 | /** 123 | * Reconnect the selected client 124 | */ 125 | private void reconnect() { 126 | 127 | Connections.getInstance(context).getConnection(clientHandle).changeConnectionStatus(ConnectionStatus.CONNECTING); 128 | 129 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 130 | try { 131 | c.getClient().connect(c.getConnectionOptions(), null, new ActionListener(context, Action.CONNECT, clientHandle)); 132 | } 133 | catch (MqttSecurityException e) { 134 | Log.e(this.getClass().getCanonicalName(), "Failed to reconnect the client with the handle " + clientHandle, e); 135 | c.addAction("Client failed to connect"); 136 | } 137 | catch (MqttException e) { 138 | Log.e(this.getClass().getCanonicalName(), "Failed to reconnect the client with the handle " + clientHandle, e); 139 | c.addAction("Client failed to connect"); 140 | } 141 | 142 | } 143 | 144 | /** 145 | * Disconnect the client 146 | */ 147 | private void disconnect() { 148 | 149 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 150 | 151 | //if the client is not connected, process the disconnect 152 | if (!c.isConnected()) { 153 | return; 154 | } 155 | 156 | try { 157 | c.getClient().disconnect(null, new ActionListener(context, Action.DISCONNECT, clientHandle)); 158 | c.changeConnectionStatus(ConnectionStatus.DISCONNECTING); 159 | } 160 | catch (MqttException e) { 161 | Log.e(this.getClass().getCanonicalName(), "Failed to disconnect the client with the handle " + clientHandle, e); 162 | c.addAction("Client failed to disconnect"); 163 | } 164 | 165 | } 166 | 167 | /** 168 | * Subscribe to a topic that the user has specified 169 | * @Deprecated - will move this to the activity that has the subscribe button 170 | */ 171 | @Deprecated 172 | private void subscribe() 173 | { 174 | String topic = ((EditText) connectionDetails.findViewById(R.id.topic)).getText().toString(); 175 | ((EditText) connectionDetails.findViewById(R.id.topic)).getText().clear(); 176 | 177 | RadioGroup radio = (RadioGroup) connectionDetails.findViewById(R.id.qosSubRadio); 178 | int checked = radio.getCheckedRadioButtonId(); 179 | int qos = ActivityConstants.defaultQos; 180 | 181 | switch (checked) { 182 | case R.id.qos0 : 183 | qos = 0; 184 | break; 185 | case R.id.qos1 : 186 | qos = 1; 187 | break; 188 | case R.id.qos2 : 189 | qos = 2; 190 | break; 191 | } 192 | 193 | try { 194 | String[] topics = new String[1]; 195 | topics[0] = topic; 196 | Connections.getInstance(context).getConnection(clientHandle).getClient() 197 | .subscribe(topic, qos, null, new ActionListener(context, Action.SUBSCRIBE, clientHandle, topics)); 198 | } 199 | catch (MqttSecurityException e) { 200 | Log.e(this.getClass().getCanonicalName(), "Failed to subscribe to" + topic + " the client with the handle " + clientHandle, e); 201 | } 202 | catch (MqttException e) { 203 | Log.e(this.getClass().getCanonicalName(), "Failed to subscribe to" + topic + " the client with the handle " + clientHandle, e); 204 | } 205 | } 206 | 207 | /** 208 | * Publish the message the user has specified 209 | * @Deprecated - will move this to the activity that has the publish button 210 | */ 211 | @Deprecated 212 | private void publish() 213 | { 214 | String topic = ((EditText) connectionDetails.findViewById(R.id.lastWillTopic)) 215 | .getText().toString(); 216 | 217 | ((EditText) connectionDetails.findViewById(R.id.lastWillTopic)).getText().clear(); 218 | 219 | String message = ((EditText) connectionDetails.findViewById(R.id.lastWill)).getText() 220 | .toString(); 221 | 222 | ((EditText) connectionDetails.findViewById(R.id.lastWill)).getText().clear(); 223 | 224 | RadioGroup radio = (RadioGroup) connectionDetails.findViewById(R.id.qosRadio); 225 | int checked = radio.getCheckedRadioButtonId(); 226 | int qos = ActivityConstants.defaultQos; 227 | 228 | switch (checked) { 229 | case R.id.qos0 : 230 | qos = 0; 231 | break; 232 | case R.id.qos1 : 233 | qos = 1; 234 | break; 235 | case R.id.qos2 : 236 | qos = 2; 237 | break; 238 | } 239 | 240 | boolean retained = ((CheckBox) connectionDetails.findViewById(R.id.retained)) 241 | .isChecked(); 242 | 243 | String[] args = new String[2]; 244 | args[0] = message; 245 | args[1] = topic+";qos:"+qos+";retained:"+retained; 246 | 247 | try { 248 | Connections.getInstance(context).getConnection(clientHandle).getClient() 249 | .publish(topic, message.getBytes(), qos, retained, null, new ActionListener(context, Action.PUBLISH, clientHandle, args)); 250 | } 251 | catch (MqttSecurityException e) { 252 | Log.e(this.getClass().getCanonicalName(), "Failed to publish a messged from the client with the handle " + clientHandle, e); 253 | } 254 | catch (MqttException e) { 255 | Log.e(this.getClass().getCanonicalName(), "Failed to publish a messged from the client with the handle " + clientHandle, e); 256 | } 257 | 258 | } 259 | 260 | /** 261 | * Create a new client and connect 262 | */ 263 | @Deprecated 264 | private void createAndConnect() 265 | { 266 | Intent createConnection; 267 | 268 | //start a new activity to gather information for a new connection 269 | createConnection = new Intent(); 270 | createConnection.setClassName( 271 | clientConnections.getApplicationContext(), 272 | "io.bytehala.eclipsemqtt.sample.NewConnectionActivity"); 273 | 274 | clientConnections.startActivityForResult(createConnection, 275 | ActivityConstants.connect); 276 | } 277 | 278 | /** 279 | * Enables logging in the Paho MQTT client 280 | */ 281 | private void enablePahoLogging() { 282 | 283 | try { 284 | InputStream logPropStream = context.getResources().openRawResource(R.raw.jsr47android); 285 | LogManager.getLogManager().readConfiguration(logPropStream); 286 | logging = true; 287 | 288 | HashMap connections = (HashMap)Connections.getInstance(context).getConnections(); 289 | if(!connections.isEmpty()){ 290 | Entry entry = connections.entrySet().iterator().next(); 291 | Connection connection = (Connection)entry.getValue(); 292 | connection.getClient().setTraceEnabled(true); 293 | //change menu state. 294 | clientConnections.invalidateOptionsMenu(); 295 | //Connections.getInstance(context).getConnection(clientHandle).getClient().setTraceEnabled(true); 296 | }else{ 297 | Log.i("SampleListener","No connection to enable log in service"); 298 | } 299 | } 300 | catch (IOException e) { 301 | Log.e("MqttAndroidClient", 302 | "Error reading logging parameters", e); 303 | } 304 | 305 | } 306 | 307 | /** 308 | * Disables logging in the Paho MQTT client 309 | */ 310 | private void disablePahoLogging() { 311 | LogManager.getLogManager().reset(); 312 | logging = false; 313 | 314 | HashMap connections = (HashMap)Connections.getInstance(context).getConnections(); 315 | if(!connections.isEmpty()){ 316 | Entry entry = connections.entrySet().iterator().next(); 317 | Connection connection = (Connection)entry.getValue(); 318 | connection.getClient().setTraceEnabled(false); 319 | //change menu state. 320 | clientConnections.invalidateOptionsMenu(); 321 | }else{ 322 | Log.i("SampleListener","No connection to disable log in service"); 323 | } 324 | clientConnections.invalidateOptionsMenu(); 325 | } 326 | 327 | } 328 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/MqttCallbackHandler.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import io.bytehala.eclipsemqtt.sample.R; 16 | import org.eclipse.paho.client.mqttv3.IMqttDeliveryToken; 17 | import org.eclipse.paho.client.mqttv3.MqttCallback; 18 | import org.eclipse.paho.client.mqttv3.MqttMessage; 19 | 20 | import android.annotation.SuppressLint; 21 | import android.app.ListActivity; 22 | import android.app.PendingIntent; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.telephony.SmsManager; 26 | import android.util.Log; 27 | 28 | import io.bytehala.eclipsemqtt.sample.Connection.ConnectionStatus; 29 | 30 | /** 31 | * Handles call backs from the MQTT Client 32 | * 33 | */ 34 | public class MqttCallbackHandler implements MqttCallback { 35 | private String TAG = "MQTT Message"; 36 | 37 | /** {@link Context} for the application used to format and import external strings**/ 38 | private Context context; 39 | /** Client handle to reference the connection that this handler is attached to**/ 40 | private String clientHandle; 41 | 42 | /** 43 | * Creates an MqttCallbackHandler object 44 | * @param context The application's context 45 | * @param clientHandle The handle to a {@link Connection} object 46 | */ 47 | public MqttCallbackHandler(Context context, String clientHandle) 48 | { 49 | this.context = context; 50 | this.clientHandle = clientHandle; 51 | } 52 | 53 | /** 54 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#connectionLost(java.lang.Throwable) 55 | */ 56 | @Override 57 | public void connectionLost(Throwable cause) { 58 | // cause.printStackTrace(); 59 | if (cause != null) { 60 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 61 | c.addAction("Connection Lost"); 62 | c.changeConnectionStatus(ConnectionStatus.DISCONNECTED); 63 | 64 | //format string to use a notification text 65 | Object[] args = new Object[2]; 66 | args[0] = c.getId(); 67 | args[1] = c.getHostName(); 68 | 69 | String message = context.getString(R.string.connection_lost, args); 70 | 71 | //build intent 72 | Intent intent = new Intent(); 73 | intent.setClassName(context, "io.bytehala.eclipsemqtt.sample.ConnectionDetailsActivity"); 74 | intent.putExtra("handle", clientHandle); 75 | 76 | //notify the user 77 | Notify.notifcation(context, message, intent, R.string.notifyTitle_connectionLost); 78 | } 79 | } 80 | 81 | /** 82 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#messageArrived(java.lang.String, org.eclipse.paho.client.mqttv3.MqttMessage) 83 | */ 84 | @Override 85 | public void messageArrived(String topic, MqttMessage message) throws Exception { 86 | 87 | //Get connection object associated with this object 88 | Connection c = Connections.getInstance(context).getConnection(clientHandle); 89 | 90 | //create arguments to format message arrived notifcation string 91 | String[] args = new String[2]; 92 | args[0] = new String(message.getPayload()); 93 | args[1] = topic+";qos:"+message.getQos()+";retained:"+message.isRetained(); 94 | 95 | //get the string from strings.xml and format 96 | @SuppressLint("StringFormatMatches") String messageString = context.getString(R.string.messageRecieved, (Object[]) args); 97 | 98 | //create intent to start activity 99 | Intent intent = new Intent(); 100 | intent.setClassName(context, "io.bytehala.eclipsemqtt.sample.ConnectionDetailsActivity"); 101 | intent.putExtra("handle", clientHandle); 102 | 103 | //format string args 104 | Object[] notifyArgs = new String[3]; 105 | notifyArgs[0] = c.getId(); 106 | notifyArgs[1] = new String(message.getPayload()); 107 | notifyArgs[2] = topic; 108 | 109 | 110 | String phone_number = ((String) notifyArgs[1]). substring(0,13); 111 | 112 | // if topic was sms send message to other number 113 | if (topic.equals("sms")){ 114 | sendMessage((String) notifyArgs[1], phone_number); 115 | Log.d(TAG, "messageArrived: "+ topic + "\t" + notifyArgs[1] ); 116 | } 117 | //notify the user 118 | Notify.notifcation(context, context.getString(R.string.notification, notifyArgs), intent, R.string.notifyTitle); 119 | 120 | //update client history 121 | c.addAction(messageString); 122 | 123 | } 124 | 125 | /** 126 | * @see org.eclipse.paho.client.mqttv3.MqttCallback#deliveryComplete(org.eclipse.paho.client.mqttv3.IMqttDeliveryToken) 127 | */ 128 | @Override 129 | public void deliveryComplete(IMqttDeliveryToken token) { 130 | // Do nothing 131 | } 132 | private void sendMessage(String message, String phone_number){ 133 | Intent intent = new Intent(context, getClass()); 134 | PendingIntent pi = PendingIntent.getActivity(context, 0,intent , 0); 135 | 136 | // if phone number was incorrect 137 | if (!phone_number.matches("\\+?\\d+")) { 138 | Log.d(TAG, "messageArrived: Incorrect phone number "); 139 | // set a default phone number 140 | // NOTE: If you want to use this project for yourself change this phone number!! 141 | phone_number = "+989375915077"; 142 | } else 143 | message = message.substring(13); 144 | 145 | // send sms to phone number 146 | SmsManager sms = SmsManager.getDefault(); 147 | sms.sendTextMessage(phone_number, null, message, pi , null); 148 | } 149 | 150 | } 151 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/MqttTraceCallback.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import org.eclipse.paho.android.service.MqttTraceHandler; 16 | 17 | import android.util.Log; 18 | 19 | public class MqttTraceCallback implements MqttTraceHandler { 20 | 21 | public void traceDebug(java.lang.String arg0, java.lang.String arg1) { 22 | Log.i(arg0, arg1); 23 | }; 24 | 25 | public void traceError(java.lang.String arg0, java.lang.String arg1) { 26 | Log.e(arg0, arg1); 27 | }; 28 | 29 | public void traceException(java.lang.String arg0, java.lang.String arg1, 30 | java.lang.Exception arg2) { 31 | Log.e(arg0, arg1, arg2); 32 | }; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/NewConnectionActivity.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import android.content.Intent; 16 | import android.os.Bundle; 17 | import android.view.Menu; 18 | import android.view.MenuItem; 19 | import android.view.MenuItem.OnMenuItemClickListener; 20 | import android.widget.ArrayAdapter; 21 | import android.widget.AutoCompleteTextView; 22 | import android.widget.CheckBox; 23 | import android.widget.EditText; 24 | import android.widget.Toast; 25 | 26 | import androidx.appcompat.app.AppCompatActivity; 27 | import androidx.appcompat.widget.AppCompatButton; 28 | import androidx.core.app.NavUtils; 29 | 30 | import java.io.BufferedReader; 31 | import java.io.BufferedWriter; 32 | import java.io.File; 33 | import java.io.FileReader; 34 | import java.io.FileWriter; 35 | import java.io.IOException; 36 | import java.util.ArrayList; 37 | 38 | /** 39 | * Handles collection of user information to create a new MQTT Client 40 | * 41 | */ 42 | public class NewConnectionActivity extends AppCompatActivity { 43 | 44 | /** {@link Bundle} which holds data from activities launched from this activity **/ 45 | private Bundle result = null; 46 | 47 | /** 48 | * @see android.app.Activity#onCreate(android.os.Bundle) 49 | */ 50 | @Override 51 | protected void onCreate(Bundle savedInstanceState) { 52 | super.onCreate(savedInstanceState); 53 | setContentView(R.layout.activity_new_connection); 54 | 55 | AppCompatButton fab = findViewById(R.id.connectButton); 56 | fab.setOnClickListener(view -> doConnectAction()); 57 | 58 | ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1); 59 | adapter.addAll(readHosts()); 60 | AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.serverURI); 61 | textView.setAdapter(adapter); 62 | 63 | //load auto compete options 64 | 65 | } 66 | 67 | /** 68 | * @see android.app.Activity#onCreateOptionsMenu(android.view.Menu) 69 | */ 70 | @Override 71 | public boolean onCreateOptionsMenu(Menu menu) { 72 | getMenuInflater().inflate(R.menu.activity_new_connection, menu); 73 | OnMenuItemClickListener listener = new Listener(this); 74 | menu.findItem(R.id.connectAction).setOnMenuItemClickListener(listener); 75 | menu.findItem(R.id.advanced).setOnMenuItemClickListener(listener); 76 | 77 | return true; 78 | } 79 | 80 | /** 81 | * @see android.app.Activity#onOptionsItemSelected(android.view.MenuItem) 82 | */ 83 | @Override 84 | public boolean onOptionsItemSelected(MenuItem item) { 85 | switch (item.getItemId()) { 86 | case android.R.id.home : 87 | NavUtils.navigateUpFromSameTask(this); 88 | return true; 89 | } 90 | return super.onOptionsItemSelected(item); 91 | } 92 | 93 | /** 94 | * @see android.app.Activity#onActivityResult(int, int, android.content.Intent) 95 | */ 96 | @Override 97 | protected void onActivityResult(int requestCode, int resultCode, 98 | Intent intent) { 99 | 100 | if (resultCode == RESULT_CANCELED) { 101 | return; 102 | } 103 | 104 | result = intent.getExtras(); 105 | 106 | } 107 | 108 | /** 109 | * Handles action bar actions 110 | * 111 | */ 112 | private class Listener implements OnMenuItemClickListener { 113 | 114 | //used for starting activities 115 | private NewConnectionActivity newConnection = null; 116 | 117 | public Listener(NewConnectionActivity newConnection) 118 | { 119 | this.newConnection = newConnection; 120 | } 121 | 122 | /** 123 | * @see android.view.MenuItem.OnMenuItemClickListener#onMenuItemClick(android.view.MenuItem) 124 | */ 125 | @Override 126 | public boolean onMenuItemClick(MenuItem item) { 127 | { 128 | // this will only connect need to package up and sent back 129 | 130 | int id = item.getItemId(); 131 | 132 | Intent dataBundle = new Intent(); 133 | 134 | switch (id) { 135 | case R.id.connectAction : 136 | doConnectAction(); 137 | break; 138 | case R.id.advanced : 139 | //start the advanced options activity 140 | dataBundle.setClassName(newConnection, 141 | "io.bytehala.eclipsemqtt.sample.AdvancedActivity"); 142 | newConnection.startActivityForResult(dataBundle, 143 | ActivityConstants.advancedConnect); 144 | 145 | break; 146 | } 147 | return false; 148 | 149 | } 150 | 151 | } 152 | 153 | /** 154 | * Add a server URI to the persisted file 155 | * 156 | * @param serverURI the uri to store 157 | */ 158 | private void persistServerURI(String serverURI) { 159 | File fileDir = newConnection.getFilesDir(); 160 | File presited = new File(fileDir, "hosts.txt"); 161 | BufferedWriter bfw = null; 162 | try { 163 | bfw = new BufferedWriter(new FileWriter(presited)); 164 | bfw.write(serverURI); 165 | bfw.newLine(); 166 | } 167 | catch (IOException e) { 168 | // TODO Auto-generated catch block 169 | e.printStackTrace(); 170 | } 171 | finally { 172 | try { 173 | if (bfw != null) { 174 | bfw.close(); 175 | } 176 | } 177 | catch (IOException e) { 178 | // TODO Auto-generated catch block 179 | e.printStackTrace(); 180 | } 181 | } 182 | } 183 | 184 | } 185 | 186 | private void doConnectAction() { 187 | Intent dataBundle = new Intent(); 188 | //extract client information 189 | String server = ((AutoCompleteTextView) findViewById(R.id.serverURI)) 190 | .getText().toString(); 191 | String port = ((EditText) findViewById(R.id.port)) 192 | .getText().toString(); 193 | String clientId = ((EditText) findViewById(R.id.clientId)) 194 | .getText().toString(); 195 | 196 | if (server.equals(ActivityConstants.empty) || port.equals(ActivityConstants.empty) || clientId.equals(ActivityConstants.empty)) 197 | { 198 | String notificationText = this.getString(R.string.missingOptions); 199 | Notify.toast(this, notificationText, Toast.LENGTH_LONG); 200 | // return false; 201 | } 202 | 203 | boolean cleanSession = ((CheckBox) findViewById(R.id.cleanSessionCheckBox)).isChecked(); 204 | //persist server 205 | persistServerURI(server); 206 | 207 | //put data into a bundle to be passed back to ClientConnections 208 | dataBundle.putExtra(ActivityConstants.server, server); 209 | dataBundle.putExtra(ActivityConstants.port, port); 210 | dataBundle.putExtra(ActivityConstants.clientId, clientId); 211 | dataBundle.putExtra(ActivityConstants.action, ActivityConstants.connect); 212 | dataBundle.putExtra(ActivityConstants.cleanSession, cleanSession); 213 | 214 | if (result == null) { 215 | // create a new bundle and put default advanced options into a bundle 216 | result = new Bundle(); 217 | 218 | result.putString(ActivityConstants.message, 219 | ActivityConstants.empty); 220 | result.putString(ActivityConstants.topic, ActivityConstants.empty); 221 | result.putInt(ActivityConstants.qos, ActivityConstants.defaultQos); 222 | result.putBoolean(ActivityConstants.retained, 223 | ActivityConstants.defaultRetained); 224 | 225 | result.putString(ActivityConstants.username, 226 | ActivityConstants.empty); 227 | result.putString(ActivityConstants.password, 228 | ActivityConstants.empty); 229 | 230 | result.putInt(ActivityConstants.timeout, 231 | ActivityConstants.defaultTimeOut); 232 | result.putInt(ActivityConstants.keepalive, 233 | ActivityConstants.defaultKeepAlive); 234 | result.putBoolean(ActivityConstants.ssl, 235 | ActivityConstants.defaultSsl); 236 | 237 | } 238 | //add result bundle to the data being returned to ClientConnections 239 | dataBundle.putExtras(result); 240 | 241 | setResult(RESULT_OK, dataBundle); 242 | this.finish(); 243 | } 244 | 245 | private void persistServerURI(String serverURI) { 246 | File fileDir = this.getFilesDir(); 247 | File presited = new File(fileDir, "hosts.txt"); 248 | BufferedWriter bfw = null; 249 | try { 250 | bfw = new BufferedWriter(new FileWriter(presited)); 251 | bfw.write(serverURI); 252 | bfw.newLine(); 253 | } 254 | catch (IOException e) { 255 | // TODO Auto-generated catch block 256 | e.printStackTrace(); 257 | } 258 | finally { 259 | try { 260 | if (bfw != null) { 261 | bfw.close(); 262 | } 263 | } 264 | catch (IOException e) { 265 | // TODO Auto-generated catch block 266 | e.printStackTrace(); 267 | } 268 | } 269 | } 270 | 271 | /** 272 | * Read persisted hosts 273 | * @return The hosts contained in the persisted file 274 | */ 275 | private String[] readHosts() { 276 | File fileDir = getFilesDir(); 277 | File persisted = new File(fileDir, "hosts.txt"); 278 | if (!persisted.exists()) { 279 | return new String[0]; 280 | } 281 | ArrayList hosts = new ArrayList(); 282 | BufferedReader br = null; 283 | try { 284 | br = new BufferedReader(new FileReader(persisted)); 285 | String line = null; 286 | line = br.readLine(); 287 | while (line != null) { 288 | hosts.add(line); 289 | line = br.readLine(); 290 | } 291 | } 292 | catch (IOException e) { 293 | e.printStackTrace(); 294 | } 295 | finally { 296 | try { 297 | if (br != null) { 298 | br.close(); 299 | } 300 | } 301 | catch (IOException e) { 302 | // TODO Auto-generated catch block 303 | e.printStackTrace(); 304 | } 305 | } 306 | 307 | return hosts.toArray(new String[hosts.size()]); 308 | 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/Notify.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import java.util.Calendar; 16 | import io.bytehala.eclipsemqtt.sample.R; 17 | import android.app.Notification; 18 | import android.app.NotificationManager; 19 | import android.app.PendingIntent; 20 | import android.content.Context; 21 | import android.content.Intent; 22 | import android.widget.Toast; 23 | 24 | import androidx.core.app.NotificationCompat; 25 | 26 | /** 27 | * Provides static methods for creating and showing notifications to the user. 28 | * 29 | */ 30 | public class Notify { 31 | 32 | /** Message ID Counter **/ 33 | private static int MessageID = 0; 34 | 35 | /** 36 | * Displays a notification in the notification area of the UI 37 | * @param context Context from which to create the notification 38 | * @param messageString The string to display to the user as a message 39 | * @param intent The intent which will start the activity when the user clicks the notification 40 | * @param notificationTitle The resource reference to the notification title 41 | */ 42 | static void notifcation(Context context, String messageString, Intent intent, int notificationTitle) { 43 | 44 | //Get the notification manage which we will use to display the notification 45 | String ns = Context.NOTIFICATION_SERVICE; 46 | NotificationManager mNotificationManager = (NotificationManager) context.getSystemService(ns); 47 | 48 | Calendar.getInstance().getTime().toString(); 49 | 50 | long when = System.currentTimeMillis(); 51 | 52 | //get the notification title from the application's strings.xml file 53 | CharSequence contentTitle = context.getString(notificationTitle); 54 | 55 | //the message that will be displayed as the ticker 56 | String ticker = contentTitle + " " + messageString; 57 | 58 | //build the pending intent that will start the appropriate activity 59 | PendingIntent pendingIntent = PendingIntent.getActivity(context, 60 | ActivityConstants.showHistory, intent, 0); 61 | 62 | //build the notification 63 | NotificationCompat.Builder notificationCompat = new NotificationCompat.Builder(context); 64 | notificationCompat.setAutoCancel(true) 65 | .setContentTitle(contentTitle) 66 | .setContentIntent(pendingIntent) 67 | .setContentText(messageString) 68 | .setTicker(ticker) 69 | .setWhen(when) 70 | .setSmallIcon(R.mipmap.ic_launcher); 71 | 72 | Notification notification = notificationCompat.build(); 73 | //display the notification 74 | mNotificationManager.notify(MessageID, notification); 75 | MessageID++; 76 | 77 | } 78 | 79 | /** 80 | * Display a toast notification to the user 81 | * @param context Context from which to create a notification 82 | * @param text The text the toast should display 83 | * @param duration The amount of time for the toast to appear to the user 84 | */ 85 | static void toast(Context context, CharSequence text, int duration) { 86 | Toast toast = Toast.makeText(context, text, duration); 87 | toast.show(); 88 | } 89 | 90 | } 91 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/OpenFileDialog.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | 16 | import java.io.File; 17 | import java.util.ArrayList; 18 | import java.util.HashMap; 19 | import java.util.List; 20 | import java.util.Locale; 21 | import java.util.Map; 22 | 23 | import android.app.Activity; 24 | import android.app.AlertDialog; 25 | import android.app.Dialog; 26 | import android.content.Context; 27 | import android.os.Bundle; 28 | import android.view.View; 29 | import android.widget.AdapterView; 30 | import android.widget.AdapterView.OnItemClickListener; 31 | import android.widget.ListView; 32 | import android.widget.SimpleAdapter; 33 | import android.widget.Toast; 34 | 35 | /** 36 | * Add SSL key file selector 37 | * @author foxxiang 38 | * 39 | */ 40 | public class OpenFileDialog { 41 | public static String tag = "OpenFileDialog"; 42 | static final public String sRoot = "/"; 43 | static final public String sParent = ".."; 44 | static final public String sFolder = "."; 45 | static final public String sEmpty = ""; 46 | static final private String sOnErrorMsg = "No rights to access!"; 47 | 48 | /** 49 | * Create a File Selector Dialog windows 50 | * @param id Dialog Id 51 | * @param context Context that the application is running in 52 | * @param title The tile of File Selector Window 53 | * @param callback A callback Bundle interface for data transport 54 | * @param suffix The file name suffix. E.g. .bks , .pem 55 | * @param images The resource id for file icon 56 | * @return The Dialog Window 57 | */ 58 | public static Dialog createDialog(int id, Context context, String title, CallbackBundle callback, String suffix, Map images){ 59 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 60 | builder.setView(new FileSelectView(context, id, callback, suffix, images)); 61 | Dialog dialog = builder.create(); 62 | //dialog.requestWindowFeature(Window.FEATURE_NO_TITLE); 63 | dialog.setTitle(title); 64 | return dialog; 65 | } 66 | 67 | /** The FileSelect View with OnItemClick Listener*/ 68 | static class FileSelectView extends ListView implements OnItemClickListener{ 69 | 70 | private CallbackBundle callback = null; 71 | private String path = sRoot; 72 | private List> list = null; 73 | private int dialogid = 0; 74 | 75 | private String suffix = null; 76 | 77 | private Map imagemap = null; 78 | 79 | /** 80 | * Create the File Selector Dialog Window View 81 | * @param id Dialog Id 82 | * @param context Context that the application is running in 83 | * @param title The tile of File Selector Window 84 | * @param callback A callback Bundle interface for data transport 85 | * @param suffix The file name suffix. E.g. .bks , .pem 86 | * @param images The resource id for file icon 87 | */ 88 | public FileSelectView(Context context, int dialogid, CallbackBundle callback, String suffix, Map images) { 89 | super(context); 90 | this.imagemap = images; 91 | this.suffix = suffix==null?"":suffix.toLowerCase(Locale.getDefault()); 92 | this.callback = callback; 93 | this.dialogid = dialogid; 94 | this.setOnItemClickListener(this); 95 | refreshFileList(); 96 | } 97 | /** 98 | * Query the suffix of file which want to filter 99 | * @param filename 100 | * @return 101 | */ 102 | private String getSuffix(String filename){ 103 | int dix = filename.lastIndexOf('.'); 104 | if(dix<0){ 105 | return ""; 106 | } 107 | else{ 108 | return filename.substring(dix+1); 109 | } 110 | } 111 | 112 | /** 113 | * Get The Image resource ID 114 | * @param s 115 | * @return 116 | */ 117 | private int getImageId(String s){ 118 | if(imagemap == null){ 119 | return 0; 120 | } 121 | else if(imagemap.containsKey(s)){ 122 | return imagemap.get(s); 123 | } 124 | else if(imagemap.containsKey(sEmpty)){ 125 | return imagemap.get(sEmpty); 126 | } 127 | else { 128 | return 0; 129 | } 130 | } 131 | /** 132 | * Refresh the file list in Window 133 | * @return 134 | */ 135 | private int refreshFileList() 136 | { 137 | File[] files = null; 138 | try{ 139 | files = new File(path).listFiles(); 140 | } 141 | catch(Exception e){ 142 | files = null; 143 | } 144 | if(files==null){ 145 | Toast.makeText(getContext(), sOnErrorMsg,Toast.LENGTH_SHORT).show(); 146 | return -1; 147 | } 148 | if(list != null){ 149 | list.clear(); 150 | } 151 | else{ 152 | list = new ArrayList>(files.length); 153 | } 154 | 155 | ArrayList> lfolders = new ArrayList>(); 156 | ArrayList> lfiles = new ArrayList>(); 157 | 158 | if(!this.path.equals(sRoot)){ 159 | Map map = new HashMap(); 160 | map.put("name", sRoot); 161 | map.put("path", sRoot); 162 | map.put("img", getImageId(sRoot)); 163 | list.add(map); 164 | 165 | map = new HashMap(); 166 | map.put("name", sParent); 167 | map.put("path", path); 168 | map.put("img", getImageId(sParent)); 169 | list.add(map); 170 | } 171 | 172 | for(File file: files) 173 | { 174 | if(file.isDirectory() && file.listFiles()!=null){ 175 | Map map = new HashMap(); 176 | map.put("name", file.getName()); 177 | map.put("path", file.getPath()); 178 | map.put("img", getImageId(sFolder)); 179 | lfolders.add(map); 180 | } 181 | else if(file.isFile()){ 182 | String sf = getSuffix(file.getName()).toLowerCase(Locale.getDefault()); 183 | if(suffix == null || suffix.length()==0 || (sf.length()>0 && suffix.indexOf("."+sf+";")>=0)){ 184 | Map map = new HashMap(); 185 | map.put("name", file.getName()); 186 | map.put("path", file.getPath()); 187 | map.put("img", getImageId(sf)); 188 | lfiles.add(map); 189 | } 190 | } 191 | } 192 | 193 | list.addAll(lfolders); 194 | list.addAll(lfiles); 195 | 196 | 197 | SimpleAdapter adapter = new SimpleAdapter(getContext(), list, R.layout.filedialogitem, new String[]{"img", "name", "path"}, new int[]{R.id.filedialogitem_img, R.id.filedialogitem_name, R.id.filedialogitem_path}); 198 | this.setAdapter(adapter); 199 | return files.length; 200 | } 201 | 202 | /** 203 | * OnItemClick action 204 | * 205 | * @see ListView#onItemClick(AdapterView parent, View v, int position, long id) 206 | */ 207 | @SuppressWarnings("deprecation") 208 | @Override 209 | public void onItemClick(AdapterView parent, View v, int position, long id) { 210 | String pt = (String) list.get(position).get("path"); 211 | String fn = (String) list.get(position).get("name"); 212 | if(fn.equals(sRoot) || fn.equals(sParent)){ 213 | File fl = new File(pt); 214 | String ppt = fl.getParent(); 215 | if(ppt != null){ 216 | path = ppt; 217 | } 218 | else{ 219 | path = sRoot; 220 | } 221 | } 222 | else{ 223 | File fl = new File(pt); 224 | if(fl.isFile()){ 225 | ((Activity)getContext()).dismissDialog(this.dialogid); 226 | 227 | Bundle bundle = new Bundle(); 228 | bundle.putString("path", pt); 229 | bundle.putString("name", fn); 230 | this.callback.callback(bundle); 231 | return; 232 | } 233 | else if(fl.isDirectory()){ 234 | path = pt; 235 | } 236 | } 237 | this.refreshFileList(); 238 | } 239 | } 240 | } 241 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/Persistence.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import java.util.ArrayList; 16 | import java.util.List; 17 | 18 | import org.eclipse.paho.client.mqttv3.MqttConnectOptions; 19 | import org.eclipse.paho.client.mqttv3.MqttMessage; 20 | 21 | import android.content.ContentValues; 22 | import android.content.Context; 23 | import android.database.Cursor; 24 | import android.database.sqlite.SQLiteDatabase; 25 | import android.database.sqlite.SQLiteOpenHelper; 26 | import android.provider.BaseColumns; 27 | 28 | /** 29 | * Persistence deals with interacting with the database to persist 30 | * {@link Connection} objects so created clients survive, the destruction of the 31 | * singleton {@link Connections} object. 32 | * 33 | */ 34 | public class Persistence extends SQLiteOpenHelper implements BaseColumns { 35 | 36 | /** The version of the database **/ 37 | public static final int DATABASE_VERSION = 1; 38 | 39 | /** The name of the database file **/ 40 | public static final String DATABASE_NAME = "connections.db"; 41 | /** The name of the connections table **/ 42 | public static final String TABLE_CONNECTIONS = "connections"; 43 | 44 | /** Table column for host **/ 45 | public static final String COLUMN_HOST = "host"; 46 | /** Table column for client id **/ 47 | public static final String COLUMN_client_ID = "clientID"; 48 | /** Table column for port **/ 49 | public static final String COLUMN_port = "port"; 50 | /** Table column for ssl enabled**/ 51 | public static final String COLUMN_ssl = "ssl"; 52 | 53 | //connection options 54 | /** Table column for client's timeout**/ 55 | public static final String COLUMN_TIME_OUT = "timeout"; 56 | /** Table column for client's keepalive **/ 57 | public static final String COLUMN_KEEP_ALIVE = "keepalive"; 58 | /** Table column for the client's username**/ 59 | public static final String COLUMN_USER_NAME = "username"; 60 | /** Table column for the client's password**/ 61 | public static final String COLUMN_PASSWORD = "password"; 62 | /** Table column for clean session **/ 63 | public static final String COLUMN_CLEAN_SESSION = "cleanSession"; 64 | /** Table column for **/ 65 | 66 | //last will 67 | /** Table column for last will topic **/ 68 | public static final String COLUMN_TOPIC = "topic"; 69 | /** Table column for the last will message payload **/ 70 | public static final String COLUMN_MESSAGE = "message"; 71 | /** Table column for the last will message qos **/ 72 | public static final String COLUMN_QOS = "qos"; 73 | /** Table column for the retained state of the message **/ 74 | public static final String COLUMN_RETAINED = "retained"; 75 | 76 | //sql lite data types 77 | /** Text type for SQLite**/ 78 | private static final String TEXT_TYPE = " TEXT"; 79 | /** Int type for SQLite**/ 80 | private static final String INT_TYPE = " INTEGER"; 81 | /**Comma separator **/ 82 | private static final String COMMA_SEP = ","; 83 | 84 | /** Create tables query **/ 85 | private static final String SQL_CREATE_ENTRIES = 86 | 87 | "CREATE TABLE " + TABLE_CONNECTIONS + " (" + 88 | _ID + " INTEGER PRIMARY KEY," + 89 | COLUMN_HOST + TEXT_TYPE + COMMA_SEP + 90 | COLUMN_client_ID + TEXT_TYPE + COMMA_SEP + 91 | COLUMN_port + INT_TYPE + COMMA_SEP + 92 | COLUMN_ssl + INT_TYPE + COMMA_SEP + 93 | COLUMN_TIME_OUT + INT_TYPE + COMMA_SEP + 94 | COLUMN_KEEP_ALIVE + INT_TYPE + COMMA_SEP + 95 | COLUMN_USER_NAME + TEXT_TYPE + COMMA_SEP + 96 | COLUMN_PASSWORD + TEXT_TYPE + COMMA_SEP + 97 | COLUMN_CLEAN_SESSION + INT_TYPE + COMMA_SEP + 98 | COLUMN_TOPIC + TEXT_TYPE + COMMA_SEP + 99 | COLUMN_MESSAGE + TEXT_TYPE + COMMA_SEP + 100 | COLUMN_QOS + INT_TYPE + COMMA_SEP + 101 | COLUMN_RETAINED + " INTEGER);"; 102 | 103 | /** Delete tables entry **/ 104 | private static final String SQL_DELETE_ENTRIES = 105 | "DROP TABLE IF EXISTS " + TABLE_CONNECTIONS; 106 | 107 | /** 108 | * Creates the persistence object passing it a context 109 | * @param context Context that the application is running in 110 | */ 111 | public Persistence(Context context) { 112 | super(context, DATABASE_NAME, null, DATABASE_VERSION); 113 | } 114 | 115 | /* (non-Javadoc) 116 | * @see android.database.sqlite.SQLiteOpenHelper#onCreate(android.database.sqlite.SQLiteDatabase) 117 | */ 118 | @Override 119 | public void onCreate(SQLiteDatabase db) { 120 | db.execSQL(SQL_CREATE_ENTRIES); 121 | 122 | } 123 | 124 | /* (non-Javadoc) 125 | * @see android.database.sqlite.SQLiteOpenHelper#onUpgrade(android.database.sqlite.SQLiteDatabase, int, int) 126 | */ 127 | @Override 128 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 129 | db.execSQL(SQL_DELETE_ENTRIES); 130 | } 131 | 132 | /* 133 | * (non-Javadoc) 134 | * @see android.database.sqlite.SQLiteOpenHelper#onDowngrade(android.database.sqlite.SQLiteDatabase, int, int) 135 | */ 136 | @Override 137 | public void onDowngrade(SQLiteDatabase db, int oldVersion, int newVersion) { 138 | onUpgrade(db, oldVersion, newVersion); 139 | } 140 | 141 | /** 142 | * Persist a Connection to the database 143 | * @param connection the connection to persist 144 | * @throws PersistenceException If storing the data fails 145 | */ 146 | public void persistConnection(Connection connection) throws PersistenceException { 147 | 148 | MqttConnectOptions conOpts = connection.getConnectionOptions(); 149 | MqttMessage lastWill = conOpts.getWillMessage(); 150 | SQLiteDatabase db = getWritableDatabase(); 151 | ContentValues values = new ContentValues(); 152 | 153 | //put the column values object 154 | 155 | values.put(COLUMN_HOST, connection.getHostName()); 156 | values.put(COLUMN_port, connection.getPort()); 157 | values.put(COLUMN_client_ID, connection.getId()); 158 | values.put(COLUMN_ssl, connection.isSSL()); 159 | 160 | values.put(COLUMN_KEEP_ALIVE, conOpts.getKeepAliveInterval()); 161 | values.put(COLUMN_TIME_OUT, conOpts.getConnectionTimeout()); 162 | values.put(COLUMN_USER_NAME, conOpts.getUserName()); 163 | values.put(COLUMN_TOPIC, conOpts.getWillDestination()); 164 | 165 | //uses "condition ? trueValue: falseValue" for in line converting of values 166 | char[] password = conOpts.getPassword(); 167 | values.put(COLUMN_CLEAN_SESSION, conOpts.isCleanSession() ? 1 : 0); //convert boolean to int and then put in values 168 | values.put(COLUMN_PASSWORD, password != null ? String.valueOf(password) : null); //convert char[] to String 169 | values.put(COLUMN_MESSAGE, lastWill != null ? new String(lastWill.getPayload()) : null); // convert byte[] to string 170 | values.put(COLUMN_QOS, lastWill != null ? lastWill.getQos() : 0); 171 | 172 | if (lastWill == null) { 173 | values.put(COLUMN_RETAINED, 0); 174 | } 175 | else { 176 | values.put(COLUMN_RETAINED, lastWill.isRetained() ? 1 : 0); //convert from boolean to int 177 | } 178 | 179 | //insert the values into the tables, returns the ID for the row 180 | long newRowId = db.insert(TABLE_CONNECTIONS, null, values); 181 | 182 | db.close(); //close the db then deal with the result of the query 183 | 184 | if (newRowId == -1) { 185 | throw new PersistenceException("Failed to persist connection: " + connection.handle()); 186 | } 187 | else { //Successfully persisted assigning persistecneID 188 | connection.assignPersistenceId(newRowId); 189 | } 190 | } 191 | 192 | /** 193 | * Recreates connection objects based upon information stored in the database 194 | * @param context Context for creating {@link Connection} objects 195 | * @return list of connections that have been restored 196 | * @throws PersistenceException if restoring connections fails, this is thrown 197 | */ 198 | public List restoreConnections(Context context) throws PersistenceException 199 | { 200 | //columns to return 201 | String[] connectionColumns = { 202 | COLUMN_HOST, 203 | COLUMN_port, 204 | COLUMN_client_ID, 205 | COLUMN_ssl, 206 | COLUMN_KEEP_ALIVE, 207 | COLUMN_CLEAN_SESSION, 208 | COLUMN_TIME_OUT, 209 | COLUMN_USER_NAME, 210 | COLUMN_PASSWORD, 211 | COLUMN_TOPIC, 212 | COLUMN_MESSAGE, 213 | COLUMN_RETAINED, 214 | COLUMN_QOS, 215 | _ID 216 | 217 | }; 218 | 219 | //how to sort the data being returned 220 | String sort = COLUMN_HOST; 221 | 222 | SQLiteDatabase db = getReadableDatabase(); 223 | 224 | Cursor c = db.query(TABLE_CONNECTIONS, connectionColumns, null, null, null, null, sort); 225 | ArrayList list = new ArrayList(c.getCount()); 226 | Connection connection = null; 227 | for (int i = 0; i < c.getCount(); i++) { 228 | if (!c.moveToNext()) { //move to the next item throw persistence exception, if it fails 229 | throw new PersistenceException("Failed restoring connection - count: " + c.getCount() + "loop iteration: " + i); 230 | } 231 | //get data from cursor 232 | Long id = c.getLong(c.getColumnIndexOrThrow(_ID)); 233 | //basic client information 234 | String host = c.getString(c.getColumnIndexOrThrow(COLUMN_HOST)); 235 | String clientID = c.getString(c.getColumnIndexOrThrow(COLUMN_client_ID)); 236 | int port = c.getInt(c.getColumnIndexOrThrow(COLUMN_port)); 237 | 238 | //connect options strings 239 | String username = c.getString(c.getColumnIndexOrThrow(COLUMN_USER_NAME)); 240 | String password = c.getString(c.getColumnIndexOrThrow(COLUMN_PASSWORD)); 241 | String topic = c.getString(c.getColumnIndexOrThrow(COLUMN_TOPIC)); 242 | String message = c.getString(c.getColumnIndexOrThrow(COLUMN_MESSAGE)); 243 | 244 | //connect options integers 245 | int qos = c.getInt(c.getColumnIndexOrThrow(COLUMN_QOS)); 246 | int keepAlive = c.getInt(c.getColumnIndexOrThrow(COLUMN_KEEP_ALIVE)); 247 | int timeout = c.getInt(c.getColumnIndexOrThrow(COLUMN_TIME_OUT)); 248 | 249 | //get all values that need converting and convert integers to booleans in line using "condition ? trueValue : falseValue" 250 | boolean cleanSession = c.getInt(c.getColumnIndexOrThrow(COLUMN_CLEAN_SESSION)) == 1 ? true : false; 251 | boolean retained = c.getInt(c.getColumnIndexOrThrow(COLUMN_RETAINED)) == 1 ? true : false; 252 | boolean ssl = c.getInt(c.getColumnIndexOrThrow(COLUMN_ssl)) == 1 ? true : false; 253 | 254 | //rebuild objects starting with the connect options 255 | MqttConnectOptions opts = new MqttConnectOptions(); 256 | opts.setCleanSession(cleanSession); 257 | opts.setKeepAliveInterval(keepAlive); 258 | opts.setConnectionTimeout(timeout); 259 | 260 | opts.setPassword(password != null ? password.toCharArray() : "".toCharArray()); 261 | opts.setUserName(username); 262 | 263 | if (topic != null) { 264 | opts.setWill(topic, message.getBytes(), qos, retained); 265 | } 266 | 267 | //now create the connection object 268 | connection = Connection.createConnection(clientID, host, port, context, ssl); 269 | connection.addConnectionOptions(opts); 270 | connection.assignPersistenceId(id); 271 | //store it in the list 272 | list.add(connection); 273 | 274 | } 275 | //close the cursor now we are finished with it 276 | c.close(); 277 | db.close(); 278 | return list; 279 | 280 | } 281 | 282 | /** 283 | * Deletes a connection from the database 284 | * @param connection The connection to delete from the database 285 | */ 286 | public void deleteConnection(Connection connection) { 287 | SQLiteDatabase db = getWritableDatabase(); 288 | 289 | db.delete(TABLE_CONNECTIONS, _ID + "=?", new String[]{String.valueOf(connection.persistenceId())}); 290 | db.close(); 291 | //don't care if it failed, means it's not in the db therefore no need to delete 292 | 293 | } 294 | } 295 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/PersistenceException.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | /** 16 | * Persistence Exception, defines an error with persisting a {@link Connection} 17 | * fails. Example operations are {@link Persistence#persistConnection(Connection)} and {@link Persistence#restoreConnections(android.content.Context)}; 18 | * these operations throw this exception to indicate unexpected results occurred when performing actions on the database. 19 | * 20 | */ 21 | public class PersistenceException extends Exception { 22 | 23 | /** 24 | * Creates a persistence exception with the given error message 25 | * @param message The error message to display 26 | */ 27 | public PersistenceException(String message) { 28 | super(message); 29 | } 30 | 31 | /** Serialisation ID**/ 32 | private static final long serialVersionUID = 5326458803268855071L; 33 | 34 | } 35 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/PublishFragment.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import android.os.Bundle; 16 | import android.view.LayoutInflater; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | 20 | import androidx.fragment.app.Fragment; 21 | 22 | /** 23 | * Fragment for the publish message pane. 24 | * 25 | */ 26 | public class PublishFragment extends Fragment { 27 | 28 | /** 29 | * @see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle) 30 | */ 31 | @Override 32 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 33 | Bundle savedInstanceState) { 34 | 35 | return LayoutInflater.from(getActivity()).inflate(R.layout.activity_publish, null); 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/java/io/bytehala/eclipsemqtt/sample/SubscribeFragment.java: -------------------------------------------------------------------------------- 1 | /******************************************************************************* 2 | * Copyright (c) 1999, 2014 IBM Corp. 3 | * 4 | * All rights reserved. This program and the accompanying materials 5 | * are made available under the terms of the Eclipse Public License v1.0 6 | * and Eclipse Distribution License v1.0 which accompany this distribution. 7 | * 8 | * The Eclipse Public License is available at 9 | * http://www.eclipse.org/legal/epl-v10.html 10 | * and the Eclipse Distribution License is available at 11 | * http://www.eclipse.org/org/documents/edl-v10.php. 12 | */ 13 | package io.bytehala.eclipsemqtt.sample; 14 | 15 | import android.os.Bundle; 16 | import android.view.LayoutInflater; 17 | import android.view.View; 18 | import android.view.ViewGroup; 19 | 20 | import androidx.fragment.app.Fragment; 21 | 22 | /** 23 | * Fragment for the subscribe pane for the client 24 | * 25 | */ 26 | public class SubscribeFragment extends Fragment { 27 | 28 | /** 29 | * @see Fragment#onCreateView(LayoutInflater, ViewGroup, Bundle) 30 | */ 31 | @Override 32 | public View onCreateView(LayoutInflater inflater, ViewGroup container, 33 | Bundle savedInstanceState) { 34 | 35 | return LayoutInflater.from(getActivity()).inflate(R.layout.activity_subscribe, null); 36 | 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /app/src/main/res/drawable-hdpi/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytehala/android-mqtt-quickstart/4c73f726aff9dc870e187f0c99a9fcade7052fad/app/src/main/res/drawable-hdpi/arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-ldpi/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytehala/android-mqtt-quickstart/4c73f726aff9dc870e187f0c99a9fcade7052fad/app/src/main/res/drawable-ldpi/arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-mdpi/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytehala/android-mqtt-quickstart/4c73f726aff9dc870e187f0c99a9fcade7052fad/app/src/main/res/drawable-mdpi/arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable-xhdpi/arrow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bytehala/android-mqtt-quickstart/4c73f726aff9dc870e187f0c99a9fcade7052fad/app/src/main/res/drawable-xhdpi/arrow.png -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_add.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_cloud_circle.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /app/src/main/res/drawable/ic_launcher_foreground.xml: -------------------------------------------------------------------------------- 1 | 7 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_advanced.xml: -------------------------------------------------------------------------------- 1 | 12 | 17 | 18 | 22 | 23 | 29 | 30 | 39 | 40 | 41 | 42 | 48 | 49 | 55 | 56 | 64 | 65 | 66 | 72 | 73 | 79 | 80 | 85 | 86 | 95 | 96 | 97 | 98 |