├── .bzrignore ├── .classpath ├── .gitignore ├── .project ├── AUTHORS ├── AndroidStudio.md ├── COPYING ├── README.md ├── bluetoothviewer-full ├── build.gradle └── src │ └── main │ └── AndroidManifest.xml ├── bluetoothviewer-lite ├── build.gradle └── src │ └── main │ └── AndroidManifest.xml ├── bluetoothviewer ├── build.gradle └── src │ ├── main │ ├── AndroidManifest.xml │ ├── assets │ │ └── samples │ │ │ ├── binary │ │ │ └── echo │ │ │ ├── line-by-line │ │ │ ├── AndroidGpsSample.csv │ │ │ ├── CitySenspodSample.csv │ │ │ └── EcoSenspodSample.csv │ │ │ └── zephyr │ │ │ └── ZephyrBioHarnessSample.base64 │ ├── java │ │ └── net │ │ │ └── bluetoothviewer │ │ │ ├── BluetoothAdapterDelegate.java │ │ │ ├── BluetoothAdapterFactory.java │ │ │ ├── BluetoothAdapterWrapper.java │ │ │ ├── BluetoothDeviceConnector.java │ │ │ ├── BluetoothViewer.java │ │ │ ├── DeviceConnector.java │ │ │ ├── DeviceListActivity.java │ │ │ ├── MessageHandler.java │ │ │ ├── MessageHandlerImpl.java │ │ │ ├── MockBinaryConnector.java │ │ │ ├── MockLineByLineConnector.java │ │ │ ├── NullBluetoothWrapper.java │ │ │ ├── NullDeviceConnector.java │ │ │ ├── SettingsActivity.java │ │ │ ├── application │ │ │ ├── BluetoothViewerApplication.java │ │ │ ├── BluetoothViewerFullApplication.java │ │ │ └── BluetoothViewerLiteApplication.java │ │ │ └── util │ │ │ ├── ApplicationUtils.java │ │ │ ├── AssetUtils.java │ │ │ ├── EmailUtils.java │ │ │ ├── IOUtils.java │ │ │ └── TextUtils.java │ └── res │ │ ├── drawable-hdpi │ │ ├── launcher_main.png │ │ ├── toolbar_btn_connect.png │ │ ├── toolbar_btn_disconnect.png │ │ ├── toolbar_btn_pause.png │ │ └── toolbar_btn_play.png │ │ ├── drawable-land │ │ └── background_main.png │ │ ├── drawable-ldpi │ │ ├── launcher_main.png │ │ ├── toolbar_btn_connect.png │ │ ├── toolbar_btn_disconnect.png │ │ ├── toolbar_btn_pause.png │ │ └── toolbar_btn_play.png │ │ ├── drawable-mdpi │ │ ├── launcher_main.png │ │ ├── toolbar_btn_connect.png │ │ ├── toolbar_btn_disconnect.png │ │ ├── toolbar_btn_pause.png │ │ └── toolbar_btn_play.png │ │ ├── drawable-xhdpi │ │ ├── launcher_main.png │ │ ├── toolbar_btn_connect.png │ │ ├── toolbar_btn_disconnect.png │ │ ├── toolbar_btn_pause.png │ │ └── toolbar_btn_play.png │ │ ├── drawable │ │ ├── background_main.png │ │ └── toolbarbg.xml │ │ ├── layout │ │ ├── bluetoothviewer.xml │ │ ├── device_list.xml │ │ ├── device_name.xml │ │ └── message.xml │ │ ├── menu │ │ └── main.xml │ │ ├── values │ │ ├── strings.xml │ │ └── style.xml │ │ └── xml │ │ └── settings.xml │ └── test │ └── java │ └── net │ └── bluetoothviewer │ └── util │ └── TextUtilsTest.java ├── build.gradle ├── docs └── senspod.md ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── graphics ├── Makefile ├── Makefile.readme ├── app-screenshots │ ├── mock-devices.png │ ├── settings.png │ └── start.png ├── play │ └── feature.png └── src │ ├── launcher_main.png │ ├── toolbar_btn_connect.png │ ├── toolbar_btn_disconnect.png │ ├── toolbar_btn_pause.png │ └── toolbar_btn_play.png ├── scripts ├── build.sh └── config.sh ├── settings.gradle └── todo.md /.bzrignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | bin 3 | gen 4 | launchpad 5 | market 6 | local.properties 7 | build.xml 8 | proguard.cfg 9 | ./keys 10 | ./libs 11 | tmp 12 | -------------------------------------------------------------------------------- /.classpath: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Intellij project files 2 | *.iml 3 | *.ipr 4 | *.iws 5 | .idea 6 | local.properties 7 | 8 | # gradle files 9 | .gradle 10 | build 11 | 12 | # misc 13 | tmp 14 | /keys 15 | /graphics/launchpad/ 16 | /graphics/market/ 17 | .DS_Store 18 | 19 | # SonarCloud 20 | gradle.properties 21 | -------------------------------------------------------------------------------- /.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | BluetoothViewer 4 | 5 | 6 | 7 | 8 | 9 | com.android.ide.eclipse.adt.ResourceManagerBuilder 10 | 11 | 12 | 13 | 14 | com.android.ide.eclipse.adt.PreCompilerBuilder 15 | 16 | 17 | 18 | 19 | org.eclipse.jdt.core.javabuilder 20 | 21 | 22 | 23 | 24 | com.android.ide.eclipse.adt.ApkBuilder 25 | 26 | 27 | 28 | 29 | 30 | com.android.ide.eclipse.adt.AndroidNature 31 | org.eclipse.jdt.core.javanature 32 | 33 | 34 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | BluetoothViewer was originally written in late 2010 in Tsukuba, Japan. 2 | 3 | The PRIMARY AUTHORS are (and/or have been): 4 | 5 | * Janos Gyerik (https://github.com/janosgyerik/) 6 | 7 | Other contributors: 8 | 9 | * Samson Gama (https://github.com/sgama) 10 | 11 | A big THANK YOU goes to: 12 | 13 | * The Android Open Source Project 14 | -------------------------------------------------------------------------------- /AndroidStudio.md: -------------------------------------------------------------------------------- 1 | Android Studio 2 | ============== 3 | I'm still learning to work with Android Studio. This is my cheat sheet. 4 | 5 | 6 | Mac OS X 7 | -------- 8 | Esc switch to editor 9 | Cmd Shift a find action by name 10 | Cmd o open class 11 | Cmd Shift o open file 12 | Cmd Option l reformat code 13 | Control Tab switch between editor tabs 14 | Control o override methods 15 | Option / expand word 16 | -------------------------------------------------------------------------------- /COPYING: -------------------------------------------------------------------------------- 1 | Licensed under the Apache License, Version 2.0 (the "License"); 2 | you may not use this file except in compliance with the License. 3 | You may obtain a copy of the License at 4 | 5 | http://www.apache.org/licenses/LICENSE-2.0 6 | 7 | Unless required by applicable law or agreed to in writing, software 8 | distributed under the License is distributed on an "AS IS" BASIS, 9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 10 | See the License for the specific language governing permissions and 11 | limitations under the License. 12 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | BluetoothViewer 2 | =============== 3 | 4 | This app is a simple Bluetooth connection debugging tool: 5 | 6 | 1. Connect to a Bluetooth device 7 | 2. Display incoming raw data 8 | 3. Send raw data to the Bluetooth device 9 | 10 | You can confirm successful pairing, monitor incoming raw data and see 11 | exactly what is transmitted from the Bluetooth device. 12 | 13 | * Android app on Google Play, with screenshots: 14 | https://play.google.com/store/apps/details?id=net.bluetoothviewer 15 | 16 | 17 | Limitations 18 | ----------- 19 | 20 | The app works only with certain types of Bluetooth devices: 21 | 22 | - Devices that work in *server mode*. That is, devices that 23 | listen to and accept incoming Bluetooth connections. 24 | 25 | - Devices that accept connections on *channel=1* 26 | 27 | - Devices that don't require a specific UUID during connection. 28 | 29 | - Devices that can be paired. 30 | 31 | All these conditions are required, at least for now. 32 | I plan to add in the next release the option to set the channel, 33 | and to set a specific UUID. 34 | 35 | Another limitation is that the current version shows incoming 36 | data in ASCII format. If your device sends binary data, that 37 | won't be very readable. To help with that, I plan to add 38 | a hexadecimal view that can be easily switched on/off. 39 | Another option is to send the received data as an attachment. 40 | 41 | Finally, keep in mind that some devices need some sort of 42 | "activation signal" first before they would start sending data. 43 | This depends on the device, and you would have to look at the 44 | technical documentation of your device to figure this out. 45 | 46 | 47 | Feature ideas 48 | ------------- 49 | 50 | I plan to add the following features in the future: 51 | 52 | * Option to specify channel to use when connecting 53 | 54 | * Option to specify UUID to use when connecting 55 | 56 | * Option to add timestamp to incoming messages 57 | 58 | * Option to add GPS info to incoming messages 59 | 60 | * Design a plugin framework for customized views tailored to specific 61 | Bluetooth sensors 62 | 63 | For more details, see the more detailed (but quite crude) `todo.md` file. 64 | 65 | 66 | Contributing code 67 | ----------------- 68 | 69 | You can contribute improvements in whatever way is convenient for you, for example: 70 | 71 | * Create a Pull Request on GitHub: 72 | https://github.com/janosgyerik/bluetoothviewer 73 | 74 | * Email your patches to info@janosgyerik.com 75 | 76 | 77 | Sponsors 78 | -------- 79 | 80 | * Alan Haddy (www.ipegcorp.com): option to record incoming Bluetooth 81 | data and send as email attachment 82 | 83 | 84 | Disclaimer 85 | ---------- 86 | 87 | The source code is a modified version of the BluetoothChat sample 88 | that is included in the Android SDK. 89 | -------------------------------------------------------------------------------- /bluetoothviewer-full/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | dependencies { 4 | implementation project(':bluetoothviewer') 5 | } 6 | 7 | android { 8 | compileSdkVersion rootProject.compileSdkVersion 9 | 10 | if (rootProject.storeFilePath) { 11 | signingConfigs { 12 | release { 13 | storeFile file(rootProject.storeFilePath) 14 | storePassword rootProject.storePassword 15 | keyAlias rootProject.keyAlias 16 | keyPassword rootProject.keyPassword 17 | } 18 | } 19 | buildTypes { 20 | release { 21 | signingConfig signingConfigs.release 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bluetoothviewer-full/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /bluetoothviewer-lite/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | dependencies { 4 | implementation project(':bluetoothviewer') 5 | } 6 | 7 | android { 8 | compileSdkVersion rootProject.compileSdkVersion 9 | 10 | if (rootProject.storeFilePath) { 11 | signingConfigs { 12 | release { 13 | storeFile file(rootProject.storeFilePath) 14 | storePassword rootProject.storePassword 15 | keyAlias rootProject.keyAlias 16 | keyPassword rootProject.keyPassword 17 | } 18 | } 19 | buildTypes { 20 | release { 21 | signingConfig signingConfigs.release 22 | } 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /bluetoothviewer-lite/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 8 | 9 | 10 | 13 | 14 | 20 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 34 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /bluetoothviewer/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.library' 2 | 3 | android { 4 | compileSdkVersion rootProject.compileSdkVersion 5 | } 6 | 7 | dependencies { 8 | testImplementation 'junit:junit:4.12' 9 | testImplementation 'org.assertj:assertj-core:3.8.0' 10 | } 11 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/assets/samples/binary/echo: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/assets/samples/binary/echo -------------------------------------------------------------------------------- /bluetoothviewer/src/main/assets/samples/line-by-line/AndroidGpsSample.csv: -------------------------------------------------------------------------------- 1 | 48.404539,2.698372 2 | 48.398568,2.694730 3 | 48.398530,2.694241 4 | 48.398600,2.694569 5 | 48.398600,2.694697 6 | 48.398665,2.695068 7 | 48.398654,2.694944 8 | 48.398659,2.694960 9 | 48.398654,2.694907 10 | 48.398649,2.694853 11 | 48.398616,2.694772 12 | 48.398616,2.694821 13 | 48.398600,2.694842 14 | 48.398589,2.694831 15 | 48.398595,2.694880 16 | 48.398573,2.694601 17 | 48.398563,2.694451 18 | 48.398563,2.694392 19 | 48.398579,2.694526 20 | 48.398573,2.694429 21 | 48.398552,2.694166 22 | 48.398530,2.693984 23 | 48.398530,2.693995 24 | 48.398525,2.693984 25 | 48.398536,2.693941 26 | 48.398541,2.693946 27 | 48.398547,2.693979 28 | 48.398547,2.693995 29 | 48.398547,2.694011 30 | 48.398563,2.694129 31 | 48.398579,2.694209 32 | 48.398589,2.694295 33 | 48.398606,2.694386 34 | 48.398611,2.694440 35 | 48.398611,2.694472 36 | 48.398616,2.694499 37 | 48.398616,2.694510 38 | 48.398616,2.694515 39 | 48.398600,2.694424 40 | 48.398600,2.694413 41 | 48.398600,2.694376 42 | 48.398595,2.694354 43 | 48.398595,2.694338 44 | 48.398595,2.694333 45 | 48.398595,2.694343 46 | 48.398600,2.694343 47 | 48.398595,2.694311 48 | 48.398589,2.694263 49 | 48.398584,2.694220 50 | 48.398579,2.694209 51 | 48.398573,2.694172 52 | 48.398573,2.694166 53 | 48.398573,2.694172 54 | 48.398568,2.694161 55 | 48.398568,2.694139 56 | 48.398568,2.694134 57 | 48.398568,2.694129 58 | 48.398563,2.694118 59 | 48.398563,2.694102 60 | 48.398557,2.694075 61 | 48.398557,2.694070 62 | 48.398552,2.694059 63 | 48.398552,2.694038 64 | 48.398547,2.693989 65 | 48.398547,2.693968 66 | 48.398541,2.693962 67 | 48.398541,2.693941 68 | 48.398541,2.693957 69 | 48.398541,2.693952 70 | 48.398541,2.693941 71 | 48.398541,2.693930 72 | 48.398541,2.693925 73 | 48.398536,2.693887 74 | 48.398530,2.693839 75 | 48.398530,2.693834 76 | 48.398530,2.693823 77 | 48.398530,2.693818 78 | 48.398525,2.693802 79 | 48.398525,2.693796 80 | 48.398525,2.693785 81 | 48.398525,2.693753 82 | 48.398520,2.693726 83 | 48.398520,2.693710 84 | 48.398520,2.693700 85 | 48.398520,2.693662 86 | 48.398514,2.693630 87 | 48.398563,2.693946 88 | 48.398584,2.694070 89 | 48.398606,2.694215 90 | 48.398616,2.694290 91 | 48.398627,2.694349 92 | 48.399260,2.694901 93 | 48.399260,2.694917 94 | 48.399271,2.694950 95 | 48.399287,2.694971 96 | 48.399303,2.694987 97 | 48.399314,2.695014 98 | 48.399314,2.695025 99 | 48.399271,2.694987 100 | 48.399287,2.695009 101 | 48.399303,2.695025 102 | 48.399308,2.695057 103 | 48.399303,2.695073 104 | 48.399308,2.695094 105 | 48.399335,2.695137 106 | 48.399346,2.695159 107 | 48.399351,2.695164 108 | 48.399383,2.695202 109 | 48.399410,2.695261 110 | 48.399416,2.695277 111 | 48.399421,2.695293 112 | 48.399437,2.695325 113 | 48.399437,2.695341 114 | 48.399459,2.695389 115 | 48.399453,2.695422 116 | 48.399459,2.695427 117 | 48.399442,2.695438 118 | 48.399459,2.695464 119 | 48.399464,2.695470 120 | 48.399480,2.695502 121 | 48.399485,2.695513 122 | 48.399480,2.695524 123 | 48.399480,2.695540 124 | 48.399475,2.695556 125 | 48.399475,2.695566 126 | 48.399475,2.695572 127 | 48.399475,2.695577 128 | 48.399475,2.695583 129 | 48.399475,2.695588 130 | 48.399475,2.695599 131 | 48.399475,2.695609 132 | 48.399475,2.695615 133 | 48.399480,2.695625 134 | 48.399480,2.695636 135 | 48.399485,2.695642 136 | 48.399491,2.695652 137 | 48.399485,2.695658 138 | 48.399491,2.695663 139 | 48.399491,2.695668 140 | 48.399501,2.695684 141 | 48.399512,2.695717 142 | 48.399512,2.695727 143 | 48.399528,2.695743 144 | 48.399539,2.695749 145 | 48.399544,2.695754 146 | 48.399550,2.695760 147 | 48.399555,2.695765 148 | 48.399566,2.695770 149 | 48.399571,2.695776 150 | 48.399571,2.695781 151 | 48.399577,2.695786 152 | 48.399582,2.695792 153 | 48.399593,2.695802 154 | 48.399598,2.695813 155 | 48.399603,2.695813 156 | 48.399603,2.695819 157 | 48.399609,2.695819 158 | 48.399609,2.695824 159 | 48.399614,2.695829 160 | 48.399625,2.695835 161 | 48.399630,2.695840 162 | 48.399636,2.695845 163 | 48.399652,2.695851 164 | 48.399652,2.695856 165 | 48.399668,2.695861 166 | 48.399673,2.695867 167 | 48.399684,2.695872 168 | 48.399695,2.695878 169 | 48.399705,2.695888 170 | 48.399721,2.695888 171 | 48.399732,2.695894 172 | 48.399743,2.695904 173 | 48.399754,2.695910 174 | 48.399764,2.695915 175 | 48.399764,2.695920 176 | 48.399775,2.695926 177 | 48.399786,2.695937 178 | 48.399791,2.695942 179 | 48.399807,2.695958 180 | 48.399813,2.695969 181 | 48.399823,2.695969 182 | 48.399845,2.695990 183 | 48.399861,2.696001 184 | 48.399877,2.696012 185 | 48.399882,2.696017 186 | 48.399893,2.696028 187 | 48.399909,2.696038 188 | 48.399920,2.696049 189 | 48.400553,2.696409 190 | 48.400601,2.696446 191 | 48.400612,2.696478 192 | 48.400660,2.696543 193 | 48.400666,2.696543 194 | 48.400692,2.696575 195 | 48.400698,2.696591 196 | 48.400698,2.696612 197 | 48.400714,2.696618 198 | 48.400751,2.696639 199 | 48.400789,2.696671 200 | 48.400837,2.696704 201 | 48.400869,2.696747 202 | 48.400896,2.696768 203 | 48.400928,2.696784 204 | 48.400944,2.696795 205 | 48.400977,2.696822 206 | 48.400993,2.696832 207 | 48.401003,2.696865 208 | 48.401014,2.696886 209 | 48.401030,2.696902 210 | 48.401084,2.696961 211 | 48.401111,2.696934 212 | 48.401309,2.697149 213 | 48.401379,2.697192 214 | 48.401465,2.697208 215 | 48.401481,2.697197 216 | 48.401508,2.697176 217 | 48.401518,2.697176 218 | 48.401535,2.697186 219 | 48.401556,2.697192 220 | 48.401572,2.697208 221 | 48.401594,2.697213 222 | 48.401610,2.697203 223 | 48.401663,2.697256 224 | 48.401679,2.697262 225 | 48.401701,2.697262 226 | 48.401712,2.697256 227 | 48.401760,2.697262 228 | 48.401814,2.697267 229 | 48.401856,2.697288 230 | 48.401883,2.697294 231 | 48.401905,2.697288 232 | 48.401926,2.697288 233 | 48.401942,2.697288 234 | 48.401958,2.697267 235 | 48.401969,2.697245 236 | 48.401969,2.697240 237 | 48.401974,2.697224 238 | 48.401980,2.697208 239 | 48.401991,2.697208 240 | 48.402007,2.697208 241 | 48.402017,2.697197 242 | 48.402028,2.697176 243 | 48.402028,2.697154 244 | 48.402039,2.697133 245 | 48.402055,2.697122 246 | 48.402060,2.697122 247 | 48.402071,2.697122 248 | 48.402082,2.697122 249 | 48.402092,2.697127 250 | 48.402109,2.697122 251 | 48.402130,2.697111 252 | 48.402146,2.697101 253 | 48.402157,2.697090 254 | 48.402173,2.697085 255 | 48.402178,2.697079 256 | 48.402189,2.697085 257 | 48.402184,2.697079 258 | 48.402200,2.697074 259 | 48.402216,2.697068 260 | 48.402221,2.697068 261 | 48.402232,2.697063 262 | 48.402237,2.697074 263 | 48.402237,2.697079 264 | 48.402237,2.697085 265 | 48.402243,2.697090 266 | 48.402237,2.697101 267 | 48.402232,2.697106 268 | 48.402227,2.697101 269 | 48.402221,2.697101 270 | 48.402216,2.697095 271 | 48.402216,2.697090 272 | 48.402216,2.697085 273 | 48.402216,2.697079 274 | 48.402216,2.697074 275 | 48.402216,2.697068 276 | 48.402216,2.697063 277 | 48.402269,2.696988 278 | 48.402280,2.696983 279 | 48.402296,2.696983 280 | 48.402312,2.696983 281 | 48.402328,2.696988 282 | 48.402345,2.696988 283 | 48.402366,2.696993 284 | 48.402382,2.696983 285 | 48.402398,2.696983 286 | 48.402414,2.696988 287 | 48.402420,2.696988 288 | 48.402425,2.696993 289 | 48.402441,2.697015 290 | 48.402452,2.697015 291 | 48.402457,2.697026 292 | 48.402463,2.697036 293 | 48.402468,2.697031 294 | 48.402468,2.697036 295 | 48.402473,2.697036 296 | 48.402479,2.697042 297 | 48.402484,2.697042 298 | 48.402495,2.697047 299 | 48.402506,2.697047 300 | 48.402511,2.697047 301 | 48.402607,2.697031 302 | 48.402624,2.697068 303 | 48.402634,2.697090 304 | 48.402629,2.697122 305 | 48.402629,2.697133 306 | 48.402629,2.697144 307 | 48.402629,2.697160 308 | 48.402640,2.697181 309 | 48.402650,2.697192 310 | 48.402656,2.697213 311 | 48.402666,2.697229 312 | 48.402688,2.697229 313 | 48.402720,2.697208 314 | 48.402742,2.697192 315 | 48.402742,2.697203 316 | 48.402758,2.697208 317 | 48.402768,2.697235 318 | 48.402779,2.697262 319 | 48.402790,2.697278 320 | 48.402790,2.697294 321 | 48.402790,2.697321 322 | 48.402795,2.697342 323 | 48.402801,2.697374 324 | 48.402806,2.697396 325 | 48.402822,2.697412 326 | 48.402827,2.697423 327 | 48.402838,2.697439 328 | 48.402854,2.697465 329 | 48.402860,2.697492 330 | 48.402870,2.697514 331 | 48.402886,2.697541 332 | 48.402897,2.697573 333 | 48.402913,2.697594 334 | 48.402919,2.697605 335 | 48.402924,2.697621 336 | 48.402935,2.697637 337 | 48.402945,2.697653 338 | 48.402961,2.697680 339 | 48.402967,2.697701 340 | 48.402972,2.697734 341 | 48.402978,2.697766 342 | 48.402972,2.697803 343 | 48.402983,2.697841 344 | 48.402999,2.697884 345 | 48.403004,2.697900 346 | 48.402999,2.697927 347 | 48.403010,2.697937 348 | 48.403015,2.697948 349 | 48.403026,2.697975 350 | 48.403031,2.697996 351 | 48.403042,2.698023 352 | 48.403047,2.698050 353 | 48.403047,2.698072 354 | 48.403053,2.698098 355 | 48.403058,2.698125 356 | 48.403069,2.698152 357 | 48.403074,2.698163 358 | 48.403085,2.698179 359 | 48.403096,2.698190 360 | 48.403101,2.698206 361 | 48.403112,2.698227 362 | 48.403122,2.698243 363 | 48.403139,2.698265 364 | 48.403144,2.698292 365 | 48.403165,2.698334 366 | 48.403176,2.698361 367 | 48.403181,2.698388 368 | 48.403192,2.698404 369 | 48.403198,2.698415 370 | 48.403208,2.698431 371 | 48.403214,2.698447 372 | 48.403219,2.698479 373 | 48.403230,2.698495 374 | 48.403235,2.698517 375 | 48.403246,2.698533 376 | 48.403251,2.698565 377 | 48.403257,2.698581 378 | 48.403257,2.698603 379 | 48.403273,2.698629 380 | 48.403289,2.698656 381 | 48.403321,2.698694 382 | 48.403332,2.698710 383 | 48.403337,2.698721 384 | 48.403348,2.698748 385 | 48.403358,2.698758 386 | 48.403369,2.698780 387 | 48.403375,2.698801 388 | 48.403385,2.698828 389 | 48.403401,2.698844 390 | 48.403407,2.698849 391 | 48.403417,2.698871 392 | 48.403428,2.698887 393 | 48.403439,2.698903 394 | 48.403450,2.698925 395 | 48.403466,2.698941 396 | 48.403466,2.698951 397 | 48.403466,2.698967 398 | 48.403471,2.699010 399 | 48.403471,2.699048 400 | 48.403482,2.699091 401 | 48.403493,2.699144 402 | 48.403514,2.699182 403 | 48.403519,2.699209 404 | 48.403530,2.699230 405 | 48.403541,2.699257 406 | 48.403557,2.699305 407 | 48.403568,2.699332 408 | 48.403578,2.699359 409 | 48.403589,2.699381 410 | 48.403600,2.699402 411 | 48.403616,2.699413 412 | 48.403621,2.699418 413 | 48.403627,2.699440 414 | 48.403643,2.699456 415 | 48.403643,2.699461 416 | 48.403648,2.699466 417 | 48.403654,2.699472 418 | 48.403659,2.699536 419 | 48.403659,2.699563 420 | 48.403664,2.699584 421 | 48.403680,2.699611 422 | 48.403691,2.699633 423 | 48.403696,2.699665 424 | 48.403713,2.699681 425 | 48.403723,2.699713 426 | 48.403729,2.699745 427 | 48.403739,2.699788 428 | 48.403750,2.699826 429 | 48.403755,2.699842 430 | 48.403761,2.699869 431 | 48.403761,2.699874 432 | 48.403766,2.699879 433 | 48.403772,2.699896 434 | 48.403772,2.699901 435 | 48.403772,2.699906 436 | 48.403772,2.699917 437 | 48.403782,2.699928 438 | 48.403788,2.699944 439 | 48.403798,2.699955 440 | 48.403809,2.699976 441 | 48.403820,2.699997 442 | 48.403831,2.700019 443 | 48.403836,2.700035 444 | 48.403852,2.700062 445 | 48.403863,2.700099 446 | 48.403884,2.700148 447 | 48.403900,2.700164 448 | 48.403916,2.700180 449 | 48.403932,2.700191 450 | 48.403949,2.700207 451 | 48.403965,2.700228 452 | 48.403981,2.700255 453 | 48.403986,2.700271 454 | 48.403991,2.700276 455 | 48.404002,2.700298 456 | 48.404018,2.700319 457 | 48.404040,2.700319 458 | 48.404061,2.700335 459 | 48.404083,2.700357 460 | 48.404099,2.700373 461 | 48.404115,2.700400 462 | 48.404136,2.700410 463 | 48.404152,2.700432 464 | 48.404163,2.700443 465 | 48.404185,2.700453 466 | 48.404185,2.700464 467 | 48.404201,2.700469 468 | 48.404217,2.700480 469 | 48.404254,2.700491 470 | 48.404270,2.700507 471 | 48.404297,2.700523 472 | 48.404308,2.700534 473 | 48.404324,2.700561 474 | 48.404335,2.700593 475 | 48.404340,2.700593 476 | 48.404351,2.700609 477 | 48.404362,2.700625 478 | 48.404378,2.700625 479 | 48.404388,2.700620 480 | 48.404399,2.700625 481 | 48.404410,2.700630 482 | 48.404426,2.700641 483 | 48.404442,2.700663 484 | 48.404447,2.700668 485 | 48.404464,2.700679 486 | 48.404480,2.700689 487 | 48.404501,2.700706 488 | 48.404517,2.700716 489 | 48.404539,2.700722 490 | 48.404549,2.700748 491 | 48.404560,2.700759 492 | 48.404571,2.700770 493 | 48.404587,2.700786 494 | 48.404598,2.700797 495 | 48.404603,2.700807 496 | 48.404614,2.700834 497 | 48.404619,2.700829 498 | 48.404614,2.700834 499 | 48.404624,2.700850 500 | 48.404641,2.700866 501 | 48.404657,2.700877 502 | 48.404678,2.700904 503 | 48.404694,2.700925 504 | 48.404716,2.700947 505 | 48.404732,2.700947 506 | 48.404748,2.700968 507 | 48.404769,2.700984 508 | 48.404791,2.701001 509 | 48.404807,2.701011 510 | 48.404823,2.701017 511 | 48.404844,2.701033 512 | 48.404871,2.701049 513 | 48.404893,2.701065 514 | 48.404909,2.701108 515 | 48.404925,2.701119 516 | 48.404946,2.701124 517 | 48.404962,2.701140 518 | 48.404984,2.701167 519 | 48.404989,2.701172 520 | 48.404989,2.701199 521 | 48.404995,2.701188 522 | 48.404984,2.701172 523 | 48.404973,2.701172 524 | 48.404979,2.701178 525 | 48.404989,2.701199 526 | 48.405005,2.701226 527 | 48.405021,2.701269 528 | 48.405038,2.701296 529 | 48.405043,2.701306 530 | 48.405059,2.701333 531 | 48.405064,2.701344 532 | 48.405086,2.701371 533 | 48.405102,2.701371 534 | 48.405129,2.701387 535 | 48.405156,2.701398 536 | 48.405182,2.701408 537 | 48.405204,2.701424 538 | 48.405231,2.701435 539 | 48.405252,2.701451 540 | 48.405257,2.701457 541 | 48.405279,2.701473 542 | 48.405295,2.701505 543 | 48.405316,2.701516 544 | 48.405338,2.701532 545 | 48.405349,2.701553 546 | 48.405359,2.701569 547 | 48.405370,2.701607 548 | 48.405386,2.701623 549 | 48.405408,2.701634 550 | 48.405413,2.701634 551 | 48.405418,2.701612 552 | 48.405418,2.701617 553 | 48.405413,2.701623 554 | 48.405386,2.701655 555 | 48.405392,2.701655 556 | 48.405392,2.701660 557 | 48.405386,2.701666 558 | 48.405402,2.701666 559 | 48.405402,2.701671 560 | 48.405408,2.701666 561 | 48.405413,2.701671 562 | 48.405418,2.701676 563 | 48.405418,2.701687 564 | 48.405424,2.701687 565 | 48.405429,2.701687 566 | 48.405434,2.701693 567 | 48.405434,2.701687 568 | 48.405434,2.701682 569 | 48.405429,2.701676 570 | 48.405434,2.701676 571 | 48.405440,2.701676 572 | 48.405445,2.701682 573 | 48.405451,2.701676 574 | 48.405451,2.701682 575 | 48.405461,2.701676 576 | 48.405461,2.701682 577 | 48.405461,2.701676 578 | 48.405472,2.701676 579 | 48.405477,2.701682 580 | 48.405483,2.701682 581 | 48.405499,2.701676 582 | 48.405504,2.701671 583 | 48.405708,2.701918 584 | 48.405730,2.701929 585 | 48.405746,2.701902 586 | 48.405751,2.701891 587 | 48.405767,2.701902 588 | 48.405783,2.701923 589 | 48.405805,2.701939 590 | 48.405821,2.701950 591 | 48.405842,2.701966 592 | 48.405864,2.701988 593 | 48.405885,2.702004 594 | 48.405896,2.701998 595 | 48.405912,2.702009 596 | 48.405923,2.702020 597 | 48.405944,2.702057 598 | 48.405960,2.702100 599 | 48.405971,2.702127 600 | 48.405992,2.702149 601 | 48.405992,2.702165 602 | 48.405998,2.702181 603 | 48.406014,2.702202 604 | 48.406035,2.702202 605 | 48.406051,2.702197 606 | 48.406073,2.702208 607 | 48.406089,2.702208 608 | 48.406100,2.702218 609 | 48.406121,2.702245 610 | 48.406143,2.702261 611 | 48.406153,2.702293 612 | 48.406164,2.702320 613 | 48.406169,2.702358 614 | 48.406186,2.702379 615 | 48.406202,2.702395 616 | 48.406218,2.702417 617 | 48.406234,2.702422 618 | 48.406245,2.702422 619 | 48.406255,2.702433 620 | 48.406266,2.702438 621 | 48.406277,2.702422 622 | 48.406298,2.702401 623 | 48.406320,2.702411 624 | 48.406336,2.702433 625 | 48.406363,2.702454 626 | 48.406395,2.702481 627 | 48.406405,2.702503 628 | 48.406422,2.702503 629 | 48.406438,2.702529 630 | 48.406448,2.702546 631 | 48.406459,2.702556 632 | 48.406470,2.702556 633 | 48.406481,2.702583 634 | 48.406507,2.702610 635 | 48.406540,2.702653 636 | 48.406561,2.702658 637 | 48.406577,2.702669 638 | 48.406599,2.702674 639 | 48.406615,2.702690 640 | 48.406636,2.702712 641 | 48.406652,2.702728 642 | 48.406663,2.702723 643 | 48.406679,2.702744 644 | 48.406695,2.702760 645 | 48.406706,2.702814 646 | 48.406717,2.702824 647 | 48.406738,2.702819 648 | 48.406749,2.702841 649 | 48.406760,2.702857 650 | 48.406781,2.702857 651 | 48.406802,2.702857 652 | 48.406819,2.702873 653 | 48.406835,2.702894 654 | 48.406845,2.702921 655 | 48.406861,2.702942 656 | 48.406883,2.702964 657 | 48.406904,2.703001 658 | 48.406937,2.703050 659 | 48.406947,2.703071 660 | 48.406958,2.703109 661 | 48.406958,2.703120 662 | 48.406963,2.703120 663 | 48.406953,2.703114 664 | 48.406947,2.703114 665 | 48.406947,2.703103 666 | 48.406963,2.703130 667 | 48.406963,2.703152 668 | 48.406963,2.703173 669 | 48.406963,2.703189 670 | 48.406963,2.703205 671 | 48.406963,2.703216 672 | 48.406963,2.703221 673 | 48.406969,2.703232 674 | 48.406974,2.703243 675 | 48.406979,2.703248 676 | 48.406985,2.703248 677 | 48.406990,2.703243 678 | 48.406996,2.703238 679 | 48.407001,2.703238 680 | 48.407001,2.703232 681 | 48.407006,2.703232 682 | 48.407006,2.703227 683 | 48.407006,2.703232 684 | 48.407012,2.703227 685 | 48.407017,2.703221 686 | 48.407022,2.703221 687 | 48.407022,2.703227 688 | 48.407028,2.703227 689 | 48.407028,2.703232 690 | 48.407033,2.703238 691 | 48.407033,2.703243 692 | 48.407038,2.703243 693 | 48.407044,2.703248 694 | 48.407049,2.703248 695 | 48.407055,2.703259 696 | 48.407060,2.703264 697 | 48.407060,2.703270 698 | 48.407065,2.703275 699 | 48.407221,2.703425 700 | 48.407232,2.703436 701 | 48.407237,2.703436 702 | 48.407253,2.703436 703 | 48.407258,2.703436 704 | 48.407274,2.703441 705 | 48.407291,2.703447 706 | 48.407296,2.703452 707 | 48.407307,2.703457 708 | 48.407323,2.703468 709 | 48.407355,2.703490 710 | 48.407360,2.703495 711 | 48.407366,2.703500 712 | 48.407371,2.703500 713 | 48.407376,2.703500 714 | 48.407382,2.703506 715 | 48.407393,2.703511 716 | 48.407398,2.703516 717 | 48.407409,2.703522 718 | 48.407414,2.703527 719 | 48.407419,2.703533 720 | 48.407425,2.703538 721 | 48.407441,2.703549 722 | 48.407446,2.703549 723 | 48.407457,2.703554 724 | 48.407462,2.703554 725 | 48.407462,2.703559 726 | 48.407473,2.703559 727 | 48.407671,2.703801 728 | 48.407720,2.703795 729 | 48.407768,2.703806 730 | 48.407779,2.703801 731 | 48.407800,2.703806 732 | 48.407832,2.703828 733 | 48.407848,2.703849 734 | 48.407870,2.703871 735 | 48.407886,2.703892 736 | 48.407897,2.703908 737 | 48.407913,2.703924 738 | 48.407945,2.703946 739 | 48.407966,2.703972 740 | 48.407983,2.703978 741 | 48.408015,2.703999 742 | 48.408031,2.704010 743 | 48.408052,2.704026 744 | 48.408068,2.704053 745 | 48.408079,2.704069 746 | 48.408095,2.704090 747 | 48.408111,2.704107 748 | 48.408127,2.704117 749 | 48.408144,2.704144 750 | 48.408154,2.704149 751 | 48.408176,2.704160 752 | 48.408192,2.704171 753 | 48.408213,2.704198 754 | 48.408229,2.704203 755 | 48.408256,2.704208 756 | 48.408278,2.704219 757 | 48.408299,2.704219 758 | 48.408315,2.704219 759 | 48.408331,2.704246 760 | 48.408342,2.704262 761 | 48.408358,2.704278 762 | 48.408363,2.704300 763 | 48.408374,2.704327 764 | 48.408385,2.704343 765 | 48.408390,2.704375 766 | 48.408401,2.704380 767 | 48.408396,2.704396 768 | 48.408406,2.704412 769 | 48.408417,2.704439 770 | 48.408428,2.704445 771 | 48.408433,2.704445 772 | 48.408444,2.704461 773 | 48.408465,2.704461 774 | 48.408476,2.704477 775 | 48.408498,2.704477 776 | 48.408514,2.704477 777 | 48.408530,2.704487 778 | 48.408546,2.704493 779 | 48.408578,2.704520 780 | 48.408599,2.704552 781 | 48.408621,2.704573 782 | 48.408659,2.704627 783 | 48.408685,2.704648 784 | 48.408707,2.704686 785 | 48.408723,2.704702 786 | 48.408728,2.704718 787 | 48.408793,2.704782 788 | 48.408825,2.704788 789 | 48.408846,2.704804 790 | 48.408862,2.704825 791 | 48.408868,2.704836 792 | 48.408868,2.704841 793 | 48.408878,2.704852 794 | 48.408878,2.704858 795 | 48.408884,2.704868 796 | 48.408889,2.704879 797 | 48.408889,2.704895 798 | 48.408889,2.704906 799 | 48.408900,2.704906 800 | 48.408900,2.704933 801 | 48.408905,2.704960 802 | 48.408911,2.705002 803 | 48.408911,2.705040 804 | 48.408916,2.705078 805 | 48.408911,2.705088 806 | 48.408932,2.705115 807 | 48.408937,2.705120 808 | 48.408937,2.705147 809 | 48.408959,2.705158 810 | 48.408975,2.705174 811 | 48.408980,2.705201 812 | 48.408975,2.705201 813 | 48.408980,2.705212 814 | 48.408980,2.705222 815 | 48.408975,2.705233 816 | 48.408975,2.705244 817 | 48.408980,2.705244 818 | 48.408986,2.705255 819 | 48.408986,2.705260 820 | 48.408996,2.705265 821 | 48.408996,2.705276 822 | 48.409007,2.705292 823 | 48.409013,2.705303 824 | 48.409018,2.705319 825 | 48.409023,2.705330 826 | 48.409029,2.705335 827 | 48.409034,2.705340 828 | 48.409034,2.705346 829 | 48.409039,2.705362 830 | 48.409050,2.705383 831 | 48.409088,2.705442 832 | 48.409109,2.705480 833 | 48.409114,2.705485 834 | 48.409120,2.705491 835 | 48.409141,2.705501 836 | 48.409141,2.705507 837 | 48.409163,2.705517 838 | 48.409200,2.705523 839 | 48.409522,2.705871 840 | 48.409565,2.705963 841 | 48.409592,2.706016 842 | 48.409619,2.706054 843 | 48.409651,2.706048 844 | 48.409672,2.706081 845 | 48.409705,2.706129 846 | 48.409737,2.706172 847 | 48.409764,2.706220 848 | 48.409780,2.706252 849 | 48.409817,2.706274 850 | 48.409860,2.706279 851 | 48.409882,2.706317 852 | 48.409887,2.706338 853 | 48.409892,2.706365 854 | 48.409892,2.706392 855 | 48.409898,2.706403 856 | 48.409898,2.706392 857 | 48.409908,2.706392 858 | 48.409930,2.706403 859 | 48.409935,2.706419 860 | 48.409946,2.706440 861 | 48.409957,2.706462 862 | 48.409962,2.706472 863 | 48.409973,2.706526 864 | 48.409994,2.706580 865 | 48.410010,2.706606 866 | 48.410021,2.706633 867 | 48.410032,2.706655 868 | 48.410043,2.706671 869 | 48.410069,2.706687 870 | 48.410096,2.706703 871 | 48.410112,2.706724 872 | 48.410139,2.706762 873 | 48.410171,2.706821 874 | 48.410198,2.706864 875 | 48.410220,2.706907 876 | 48.410236,2.706928 877 | 48.410268,2.706993 878 | 48.410284,2.707019 879 | 48.410300,2.707052 880 | 48.410321,2.707078 881 | 48.410338,2.707111 882 | 48.410354,2.707137 883 | 48.410375,2.707164 884 | 48.410391,2.707186 885 | 48.410407,2.707202 886 | 48.410418,2.707229 887 | 48.410429,2.707250 888 | 48.410439,2.707261 889 | 48.410461,2.707336 890 | 48.410477,2.707363 891 | 48.410515,2.707373 892 | 48.410541,2.707406 893 | 48.410563,2.707422 894 | 48.410574,2.707438 895 | 48.410584,2.707449 896 | 48.410595,2.707459 897 | 48.410600,2.707486 898 | 48.410617,2.707502 899 | 48.410617,2.707508 900 | 48.410627,2.707524 901 | 48.410638,2.707545 902 | 48.410638,2.707567 903 | 48.410643,2.707588 904 | 48.410643,2.707604 905 | 48.410649,2.707620 906 | 48.410654,2.707647 907 | 48.410665,2.707663 908 | 48.410681,2.707679 909 | 48.410697,2.707717 910 | 48.410735,2.707738 911 | 48.410756,2.707754 912 | 48.410767,2.707776 913 | 48.410783,2.707797 914 | 48.410788,2.707835 915 | 48.410794,2.707872 916 | 48.410826,2.707888 917 | 48.410847,2.707905 918 | 48.410863,2.707921 919 | 48.410879,2.707931 920 | 48.410885,2.707942 921 | 48.410895,2.707953 922 | 48.410922,2.707964 923 | 48.410933,2.707980 924 | 48.410949,2.707990 925 | 48.410971,2.708012 926 | 48.410992,2.708028 927 | 48.411008,2.708049 928 | 48.411024,2.708071 929 | 48.411040,2.708098 930 | 48.411056,2.708141 931 | 48.411072,2.708162 932 | 48.411072,2.708194 933 | 48.411094,2.708210 934 | 48.411142,2.708232 935 | 48.411174,2.708221 936 | 48.411191,2.708243 937 | 48.411191,2.708259 938 | 48.411180,2.708280 939 | 48.411217,2.708312 940 | 48.411223,2.708318 941 | 48.411233,2.708328 942 | 48.411250,2.708344 943 | 48.411282,2.708371 944 | 48.411309,2.708398 945 | 48.411346,2.708425 946 | 48.411378,2.708479 947 | 48.411405,2.708495 948 | 48.411427,2.708521 949 | 48.411443,2.708538 950 | 48.411448,2.708559 951 | 48.411453,2.708586 952 | 48.411469,2.708602 953 | 48.411486,2.708645 954 | 48.411496,2.708672 955 | 48.411486,2.708709 956 | 48.411496,2.708731 957 | 48.411512,2.708720 958 | 48.411539,2.708731 959 | 48.411555,2.708741 960 | 48.411577,2.708763 961 | 48.411593,2.708784 962 | 48.411614,2.708806 963 | 48.411614,2.708811 964 | 48.411625,2.708833 965 | 48.411652,2.708854 966 | 48.411673,2.708865 967 | 48.411679,2.708886 968 | 48.411695,2.708897 969 | 48.411716,2.708913 970 | 48.411727,2.708924 971 | 48.411743,2.708945 972 | 48.411759,2.708961 973 | 48.411765,2.708983 974 | 48.411775,2.709010 975 | 48.411791,2.709026 976 | 48.411807,2.709036 977 | 48.411770,2.709053 978 | 48.411759,2.709036 979 | 48.411754,2.709026 980 | 48.411748,2.709031 981 | 48.411765,2.709058 982 | 48.411807,2.709122 983 | 48.411829,2.709176 984 | 48.411850,2.709197 985 | 48.411856,2.709219 986 | 48.411856,2.709230 987 | 48.411866,2.709246 988 | 48.411915,2.709332 989 | 48.411974,2.709433 990 | 48.412049,2.709433 991 | 48.412070,2.709450 992 | 48.412086,2.709498 993 | 48.412097,2.709525 994 | 48.412108,2.709557 995 | 48.412135,2.709594 996 | 48.412167,2.709653 997 | 48.412183,2.709691 998 | 48.412199,2.709723 999 | 48.412231,2.709750 1000 | 48.412253,2.709766 1001 | 48.412258,2.709787 1002 | 48.412253,2.709836 1003 | 48.412290,2.709873 1004 | 48.412301,2.709911 1005 | 48.412317,2.709938 1006 | 48.412322,2.709948 1007 | 48.412333,2.709981 1008 | 48.412338,2.710013 1009 | 48.412349,2.710040 1010 | 48.412360,2.710066 1011 | 48.412371,2.710093 1012 | 48.412387,2.710120 1013 | 48.412398,2.710136 1014 | 48.412419,2.710142 1015 | 48.412440,2.710152 1016 | 48.412457,2.710163 1017 | 48.412467,2.710179 1018 | 48.412467,2.710211 1019 | 48.412478,2.710238 1020 | 48.412494,2.710270 1021 | 48.412499,2.710302 1022 | 48.412489,2.710340 1023 | 48.412489,2.710372 1024 | 48.412473,2.710420 1025 | 48.412467,2.710453 1026 | 48.412489,2.710479 1027 | 48.412526,2.710501 1028 | 48.412537,2.710522 1029 | 48.412564,2.710549 1030 | 48.412575,2.710576 1031 | 48.412580,2.710614 1032 | 48.412585,2.710640 1033 | 48.412575,2.710673 1034 | 48.412585,2.710699 1035 | 48.412596,2.710726 1036 | 48.412607,2.710758 1037 | 48.412617,2.710801 1038 | 48.412628,2.710844 1039 | 48.412644,2.710882 1040 | 48.412650,2.710903 1041 | 48.412660,2.710935 1042 | 48.412671,2.710968 1043 | 48.412687,2.710973 1044 | 48.412693,2.711005 1045 | 48.412709,2.711032 1046 | 48.412725,2.711059 1047 | 48.412730,2.711080 1048 | 48.412768,2.711091 1049 | 48.412773,2.711107 1050 | 48.412789,2.711129 1051 | 48.412805,2.711145 1052 | 48.412832,2.711145 1053 | 48.412837,2.711161 1054 | 48.412837,2.711177 1055 | 48.412853,2.711188 1056 | 48.412870,2.711204 1057 | 48.412880,2.711214 1058 | 48.412880,2.711241 1059 | 48.412886,2.711268 1060 | 48.412891,2.711295 1061 | 48.412902,2.711316 1062 | 48.412912,2.711332 1063 | 48.412934,2.711354 1064 | 48.412966,2.711365 1065 | 48.412982,2.711370 1066 | 48.413009,2.711386 1067 | 48.413025,2.711413 1068 | 48.413052,2.711429 1069 | 48.413063,2.711450 1070 | 48.413063,2.711509 1071 | 48.413068,2.711520 1072 | 48.413079,2.711536 1073 | 48.413095,2.711547 1074 | 48.413106,2.711574 1075 | 48.413084,2.711719 1076 | 48.413090,2.711751 1077 | 48.413079,2.711794 1078 | 48.413068,2.711853 1079 | 48.413068,2.711928 1080 | 48.413057,2.711965 1081 | 48.413079,2.712014 1082 | 48.413073,2.712083 1083 | 48.413084,2.712110 1084 | 48.413095,2.712132 1085 | 48.413090,2.712159 1086 | 48.413090,2.712185 1087 | 48.413090,2.712212 1088 | 48.413079,2.712244 1089 | 48.413079,2.712271 1090 | 48.413057,2.712303 1091 | 48.413057,2.712325 1092 | 48.413057,2.712330 1093 | 48.413111,2.712341 1094 | 48.413116,2.712373 1095 | 48.413127,2.712400 1096 | 48.413143,2.712427 1097 | 48.413170,2.712454 1098 | 48.413202,2.712502 1099 | 48.413218,2.712518 1100 | 48.413234,2.712539 1101 | 48.413234,2.712561 1102 | 48.413240,2.712572 1103 | 48.413245,2.712604 1104 | 48.413240,2.712657 1105 | 48.413224,2.712727 1106 | 48.413213,2.712786 1107 | 48.413202,2.712824 1108 | 48.413191,2.712883 1109 | 48.413202,2.712920 1110 | 48.413208,2.712969 1111 | 48.413175,2.713033 1112 | 48.413181,2.713081 1113 | 48.413191,2.713103 1114 | 48.413208,2.713130 1115 | 48.413245,2.713178 1116 | 48.413245,2.713194 1117 | 48.413250,2.713221 1118 | 48.413261,2.713248 1119 | 48.413256,2.713274 1120 | 48.413256,2.713301 1121 | 48.413261,2.713328 1122 | 48.413261,2.713355 1123 | 48.413256,2.713392 1124 | 48.413261,2.713425 1125 | 48.413272,2.713457 1126 | 48.413277,2.713494 1127 | 48.413293,2.713516 1128 | 48.413315,2.713543 1129 | 48.413299,2.713580 1130 | 48.413309,2.713612 1131 | 48.413309,2.713645 1132 | 48.413320,2.713687 1133 | 48.413336,2.713725 1134 | 48.413347,2.713763 1135 | 48.413363,2.713789 1136 | 48.413374,2.713811 1137 | 48.413385,2.713827 1138 | 48.413395,2.713838 1139 | 48.413385,2.713864 1140 | 48.413395,2.713881 1141 | 48.413406,2.713897 1142 | 48.413433,2.713913 1143 | 48.413449,2.713929 1144 | 48.413449,2.713950 1145 | 48.413454,2.713966 1146 | 48.413476,2.713988 1147 | 48.413470,2.714020 1148 | 48.413476,2.714031 1149 | 48.413508,2.714047 1150 | 48.413535,2.714068 1151 | 48.413556,2.714095 1152 | 48.413567,2.714111 1153 | 48.413621,2.714127 1154 | 48.413626,2.714165 1155 | 48.413626,2.714192 1156 | 48.413626,2.714202 1157 | 48.413637,2.714229 1158 | 48.413664,2.714256 1159 | 48.413680,2.714294 1160 | 48.413690,2.714315 1161 | 48.413696,2.714337 1162 | 48.413717,2.714363 1163 | 48.413728,2.714379 1164 | 48.413744,2.714406 1165 | 48.413717,2.714492 1166 | 48.413717,2.714514 1167 | 48.413723,2.714562 1168 | 48.413723,2.714599 1169 | 48.413744,2.714626 1170 | 48.413749,2.714685 1171 | 48.413760,2.714733 1172 | 48.413771,2.714755 1173 | 48.413782,2.714798 1174 | 48.413787,2.714819 1175 | 48.413798,2.714862 1176 | 48.413798,2.714911 1177 | 48.413792,2.714932 1178 | 48.413819,2.714970 1179 | 48.413851,2.714996 1180 | 48.413857,2.715029 1181 | 48.413862,2.715050 1182 | 48.413857,2.715093 1183 | 48.413846,2.715120 1184 | 48.413846,2.715147 1185 | 48.413841,2.715184 1186 | 48.413846,2.715211 1187 | 48.413851,2.715232 1188 | 48.413867,2.715270 1189 | 48.413878,2.715313 1190 | 48.413883,2.715350 1191 | 48.413883,2.715383 1192 | 48.413889,2.715409 1193 | 48.413889,2.715431 1194 | 48.413894,2.715484 1195 | 48.413937,2.715511 1196 | 48.413953,2.715533 1197 | 48.413975,2.715560 1198 | 48.413969,2.715597 1199 | 48.413964,2.715624 1200 | 48.413975,2.715645 1201 | 48.413985,2.715672 1202 | 48.414007,2.715726 1203 | 48.414001,2.715758 1204 | 48.413996,2.715780 1205 | 48.413980,2.715796 1206 | 48.413975,2.715822 1207 | 48.413964,2.715849 1208 | 48.413964,2.715871 1209 | 48.413959,2.715892 1210 | 48.413959,2.715914 1211 | 48.413953,2.715935 1212 | 48.413969,2.715962 1213 | 48.413948,2.715994 1214 | 48.413959,2.716042 1215 | 48.413996,2.716085 1216 | 48.414060,2.716128 1217 | 48.414125,2.716160 1218 | 48.414162,2.716187 1219 | 48.414205,2.716225 1220 | 48.414216,2.716252 1221 | 48.414216,2.716262 1222 | 48.414221,2.716305 1223 | 48.414232,2.716332 1224 | 48.414232,2.716370 1225 | 48.414237,2.716391 1226 | 48.414232,2.716445 1227 | 48.414259,2.716477 1228 | 48.414270,2.716498 1229 | 48.414270,2.716514 1230 | 48.414264,2.716536 1231 | 48.414297,2.716557 1232 | 48.414323,2.716584 1233 | 48.414334,2.716606 1234 | 48.414350,2.716632 1235 | 48.414361,2.716665 1236 | 48.414372,2.716702 1237 | 48.414382,2.716724 1238 | 48.414388,2.716761 1239 | 48.414393,2.716799 1240 | 48.414388,2.716826 1241 | 48.414388,2.716858 1242 | 48.414393,2.716869 1243 | 48.414393,2.716901 1244 | 48.414398,2.716928 1245 | 48.414404,2.716954 1246 | 48.414404,2.716981 1247 | 48.414388,2.717003 1248 | 48.414393,2.717035 1249 | 48.414388,2.717051 1250 | 48.414404,2.717078 1251 | 48.414404,2.717110 1252 | 48.414393,2.717142 1253 | 48.414388,2.717164 1254 | 48.414409,2.717190 1255 | 48.414420,2.717201 1256 | 48.414431,2.717228 1257 | 48.414441,2.717265 1258 | 48.414441,2.717298 1259 | 48.414441,2.717324 1260 | 48.414447,2.717357 1261 | 48.414436,2.717384 1262 | 48.414431,2.717410 1263 | 48.414425,2.717432 1264 | 48.414420,2.717464 1265 | 48.414425,2.717491 1266 | 48.414425,2.717512 1267 | 48.414457,2.717534 1268 | 48.414463,2.717550 1269 | 48.414511,2.717555 1270 | 48.414516,2.717598 1271 | 48.414522,2.717614 1272 | 48.414527,2.717641 1273 | 48.414565,2.717652 1274 | 48.414586,2.717673 1275 | 48.414581,2.717721 1276 | 48.414581,2.717764 1277 | 48.414559,2.717850 1278 | 48.414543,2.717915 1279 | 48.414527,2.717963 1280 | 48.414533,2.717979 1281 | 48.414608,2.718027 1282 | 48.414710,2.718038 1283 | 48.414769,2.718076 1284 | 48.414779,2.718092 1285 | 48.414790,2.718097 1286 | 48.414795,2.718118 1287 | 48.414795,2.718135 1288 | 48.414795,2.718140 1289 | 48.414790,2.718151 1290 | 48.414790,2.718161 1291 | 48.414785,2.718167 1292 | 48.414779,2.718183 1293 | 48.414779,2.718194 1294 | 48.414774,2.718204 1295 | 48.414779,2.718210 1296 | 48.414779,2.718220 1297 | 48.414785,2.718226 1298 | 48.414785,2.718236 1299 | 48.414785,2.718247 1300 | 48.414790,2.718258 1301 | 48.414790,2.718263 1302 | 48.414865,2.718413 1303 | 48.414871,2.718440 1304 | 48.414887,2.718467 1305 | 48.414887,2.718478 1306 | 48.414903,2.718505 1307 | 48.414913,2.718526 1308 | 48.414924,2.718548 1309 | 48.414935,2.718580 1310 | 48.414940,2.718601 1311 | 48.414940,2.718628 1312 | 48.414946,2.718639 1313 | 48.414940,2.718644 1314 | 48.414946,2.718655 1315 | 48.414946,2.718676 1316 | 48.414951,2.718709 1317 | 48.414935,2.718730 1318 | 48.414930,2.718757 1319 | 48.414935,2.718784 1320 | 48.414951,2.718810 1321 | 48.414962,2.718827 1322 | 48.414951,2.718853 1323 | 48.414940,2.718875 1324 | 48.414935,2.718907 1325 | 48.414951,2.718918 1326 | 48.414951,2.718939 1327 | 48.414962,2.718955 1328 | 48.414967,2.718977 1329 | 48.414962,2.719014 1330 | 48.414983,2.719041 1331 | 48.414983,2.719073 1332 | 48.414999,2.719100 1333 | 48.415005,2.719122 1334 | 48.415015,2.719143 1335 | 48.415010,2.719175 1336 | 48.415021,2.719207 1337 | 48.415026,2.719240 1338 | 48.415053,2.719250 1339 | 48.415053,2.719277 1340 | 48.415069,2.719304 1341 | 48.415080,2.719331 1342 | 48.415085,2.719368 1343 | 48.415101,2.719390 1344 | 48.415117,2.719417 1345 | 48.415128,2.719449 1346 | 48.415144,2.719508 1347 | 48.415149,2.719524 1348 | 48.415166,2.719545 1349 | 48.415171,2.719572 1350 | 48.415166,2.719604 1351 | 48.415171,2.719637 1352 | 48.415171,2.719669 1353 | 48.415176,2.719749 1354 | 48.415182,2.719771 1355 | 48.415182,2.719797 1356 | 48.415198,2.719814 1357 | 48.415208,2.719835 1358 | 48.415219,2.719851 1359 | 48.415251,2.719878 1360 | 48.415278,2.719953 1361 | 48.415289,2.719996 1362 | 48.415300,2.720028 1363 | 48.415305,2.720055 1364 | 48.415310,2.720082 1365 | 48.415321,2.720109 1366 | 48.415316,2.720135 1367 | 48.415316,2.720168 1368 | 48.415316,2.720205 1369 | 48.415316,2.720221 1370 | 48.415316,2.720237 1371 | 48.415305,2.720270 1372 | 48.415310,2.720296 1373 | 48.415305,2.720334 1374 | 48.415305,2.720366 1375 | 48.415300,2.720393 1376 | 48.415305,2.720430 1377 | 48.415300,2.720457 1378 | 48.415300,2.720484 1379 | 48.415289,2.720495 1380 | 48.415267,2.720500 1381 | 48.415273,2.720495 1382 | 48.415267,2.720500 1383 | 48.415262,2.720506 1384 | 48.415257,2.720511 1385 | 48.415251,2.720516 1386 | 48.415246,2.720516 1387 | 48.415246,2.720522 1388 | 48.415251,2.720522 1389 | 48.415267,2.720527 1390 | 48.415246,2.720522 1391 | 48.415251,2.720522 1392 | 48.415262,2.720522 1393 | 48.415267,2.720527 1394 | 48.415262,2.720522 1395 | 48.415257,2.720522 1396 | 48.415262,2.720522 1397 | 48.415251,2.720522 1398 | 48.415246,2.720522 1399 | 48.415257,2.720522 1400 | 48.415251,2.720522 1401 | 48.415241,2.720516 1402 | 48.415246,2.720516 1403 | 48.415246,2.720522 1404 | 48.415246,2.720516 1405 | 48.415241,2.720516 1406 | 48.415230,2.720516 1407 | 48.415235,2.720516 1408 | 48.415235,2.720511 1409 | 48.415241,2.720511 1410 | 48.415235,2.720511 1411 | 48.415225,2.720511 1412 | 48.415219,2.720511 1413 | 48.415214,2.720511 1414 | 48.415219,2.720516 1415 | 48.415214,2.720516 1416 | 48.415208,2.720511 1417 | 48.415203,2.720511 1418 | 48.415208,2.720506 1419 | 48.415208,2.720511 1420 | 48.415208,2.720506 1421 | 48.415208,2.720511 1422 | 48.415208,2.720506 1423 | 48.415203,2.720506 1424 | 48.415198,2.720506 1425 | 48.415203,2.720511 1426 | 48.415198,2.720511 1427 | 48.415192,2.720511 1428 | 48.415198,2.720511 1429 | 48.415192,2.720511 1430 | 48.415187,2.720511 1431 | 48.415182,2.720511 1432 | 48.415187,2.720511 1433 | 48.415182,2.720511 1434 | 48.415182,2.720506 1435 | 48.415176,2.720506 1436 | 48.415171,2.720506 1437 | 48.415176,2.720506 1438 | 48.415171,2.720506 1439 | 48.415176,2.720506 1440 | 48.415171,2.720506 1441 | 48.415176,2.720500 1442 | 48.415171,2.720495 1443 | 48.415171,2.720490 1444 | 48.415171,2.720495 1445 | 48.415171,2.720490 1446 | 48.415171,2.720495 1447 | 48.415171,2.720484 1448 | 48.415176,2.720468 1449 | 48.415176,2.720447 1450 | 48.415182,2.720430 1451 | 48.415182,2.720425 1452 | 48.415182,2.720420 1453 | 48.415182,2.720404 1454 | 48.415182,2.720398 1455 | 48.415182,2.720393 1456 | 48.415166,2.720420 1457 | 48.415139,2.720468 1458 | 48.415133,2.720479 1459 | 48.415123,2.720490 1460 | 48.415128,2.720479 1461 | 48.415123,2.720484 1462 | 48.415112,2.720490 1463 | 48.415112,2.720484 1464 | 48.415112,2.720506 1465 | 48.415117,2.720490 1466 | 48.415123,2.720484 1467 | 48.415123,2.720479 1468 | 48.415123,2.720473 1469 | 48.415123,2.720479 1470 | 48.415123,2.720473 1471 | 48.415123,2.720468 1472 | 48.415123,2.720473 1473 | 48.415123,2.720463 1474 | 48.415123,2.720457 1475 | 48.415117,2.720463 1476 | 48.415123,2.720452 1477 | 48.415117,2.720447 1478 | 48.415123,2.720441 1479 | 48.415123,2.720430 1480 | 48.415117,2.720430 1481 | 48.415117,2.720425 1482 | 48.415123,2.720420 1483 | 48.415123,2.720414 1484 | 48.415117,2.720420 1485 | 48.415117,2.720414 1486 | 48.415123,2.720414 1487 | 48.415117,2.720414 1488 | 48.415117,2.720420 1489 | 48.415117,2.720425 1490 | 48.415112,2.720425 1491 | 48.415112,2.720430 1492 | 48.415107,2.720430 1493 | 48.415107,2.720436 1494 | 48.415107,2.720430 1495 | 48.415112,2.720468 1496 | 48.415117,2.720495 1497 | 48.415112,2.720500 1498 | 48.415117,2.720516 1499 | 48.415117,2.720538 1500 | 48.415117,2.720532 1501 | 48.415117,2.720538 1502 | 48.415139,2.720516 1503 | 48.415149,2.720581 1504 | 48.415155,2.720597 1505 | 48.415166,2.720613 1506 | 48.415198,2.720608 1507 | 48.415214,2.720618 1508 | 48.415219,2.720624 1509 | 48.415251,2.720591 1510 | 48.415262,2.720608 1511 | 48.415273,2.720613 1512 | 48.415289,2.720634 1513 | 48.415477,2.720811 1514 | 48.415504,2.720838 1515 | 48.415514,2.720860 1516 | 48.415525,2.720865 1517 | 48.415546,2.720881 1518 | 48.415563,2.720886 1519 | 48.415605,2.720870 1520 | 48.415622,2.720892 1521 | 48.415622,2.720935 1522 | 48.415627,2.720945 1523 | 48.415632,2.720988 1524 | 48.415643,2.721058 1525 | 48.415659,2.721074 1526 | 48.415654,2.721106 1527 | 48.415659,2.721112 1528 | 48.415681,2.721123 1529 | 48.415686,2.721160 1530 | 48.415697,2.721176 1531 | 48.415702,2.721203 1532 | 48.415702,2.721230 1533 | 48.415723,2.721257 1534 | 48.415740,2.721294 1535 | 48.415750,2.721332 1536 | 48.415782,2.721369 1537 | 48.415788,2.721412 1538 | 48.415793,2.721455 1539 | 48.415793,2.721487 1540 | 48.415809,2.721525 1541 | 48.415820,2.721546 1542 | 48.415831,2.721562 1543 | 48.415841,2.721589 1544 | 48.415858,2.721616 1545 | 48.415868,2.721648 1546 | 48.415890,2.721686 1547 | 48.415906,2.721707 1548 | 48.415922,2.721729 1549 | 48.415933,2.721750 1550 | 48.415933,2.721788 1551 | 48.415863,2.721879 1552 | 48.415858,2.721922 1553 | 48.415831,2.721975 1554 | 48.415815,2.722029 1555 | 48.415831,2.722056 1556 | 48.415868,2.722067 1557 | 48.415884,2.722110 1558 | 48.415884,2.722136 1559 | 48.415884,2.722174 1560 | 48.415884,2.722206 1561 | 48.415874,2.722233 1562 | 48.415874,2.722260 1563 | 48.415858,2.722276 1564 | 48.415852,2.722292 1565 | 48.415825,2.722351 1566 | 48.415836,2.722356 1567 | 48.415917,2.722356 1568 | 48.415927,2.722405 1569 | 48.415938,2.722437 1570 | 48.415949,2.722474 1571 | 48.415965,2.722523 1572 | 48.415976,2.722582 1573 | 48.416008,2.722619 1574 | 48.416045,2.722651 1575 | 48.416056,2.722678 1576 | 48.416077,2.722689 1577 | 48.416072,2.722743 1578 | 48.416077,2.722807 1579 | 48.416083,2.722850 1580 | 48.416094,2.722893 1581 | 48.416094,2.722936 1582 | 48.416104,2.722968 1583 | 48.416120,2.722989 1584 | 48.416126,2.723005 1585 | 48.416126,2.723022 1586 | 48.416131,2.723043 1587 | 48.416153,2.723059 1588 | 48.416179,2.723054 1589 | 48.416190,2.723054 1590 | 48.416206,2.723075 1591 | 48.416228,2.723081 1592 | 48.416228,2.723113 1593 | 48.416217,2.723177 1594 | 48.416212,2.723215 1595 | 48.416212,2.723241 1596 | 48.416212,2.723274 1597 | 48.416212,2.723317 1598 | 48.416222,2.723338 1599 | 48.416222,2.723359 1600 | 48.416228,2.723424 1601 | 48.416244,2.723488 1602 | 48.416249,2.723531 1603 | 48.416265,2.723574 1604 | 48.416276,2.723622 1605 | 48.416297,2.723638 1606 | 48.416346,2.723644 1607 | 48.416356,2.723655 1608 | 48.416367,2.723676 1609 | 48.416399,2.723708 1610 | 48.416415,2.723730 1611 | 48.416426,2.723740 1612 | 48.416448,2.723773 1613 | 48.416458,2.723789 1614 | 48.416469,2.723815 1615 | 48.416485,2.723826 1616 | 48.416496,2.723853 1617 | 48.416507,2.723874 1618 | 48.416512,2.723896 1619 | 48.416523,2.723923 1620 | 48.416539,2.723939 1621 | 48.416587,2.723944 1622 | 48.416587,2.723982 1623 | 48.416598,2.723998 1624 | 48.416619,2.724014 1625 | 48.416641,2.724019 1626 | 48.416668,2.724041 1627 | 48.416673,2.724057 1628 | 48.416651,2.724062 1629 | 48.416630,2.724100 1630 | 48.416614,2.724132 1631 | 48.416614,2.724148 1632 | 48.416614,2.724175 1633 | 48.416619,2.724202 1634 | 48.416619,2.724234 1635 | 48.416619,2.724266 1636 | 48.416619,2.724293 1637 | 48.416625,2.724314 1638 | 48.416619,2.724357 1639 | 48.416614,2.724406 1640 | 48.416614,2.724448 1641 | 48.416603,2.724497 1642 | 48.416598,2.724540 1643 | 48.416592,2.724609 1644 | 48.416598,2.724642 1645 | 48.416609,2.724684 1646 | 48.416619,2.724717 1647 | 48.416582,2.724776 1648 | 48.416582,2.724797 1649 | 48.416566,2.724819 1650 | 48.416555,2.724840 1651 | 48.416544,2.724888 1652 | 48.416555,2.724926 1653 | 48.416544,2.724958 1654 | 48.416539,2.724974 1655 | 48.416528,2.725017 1656 | 48.416523,2.725055 1657 | 48.416533,2.725081 1658 | 48.416539,2.725130 1659 | 48.416544,2.725173 1660 | 48.416550,2.725210 1661 | 48.416566,2.725248 1662 | 48.416571,2.725280 1663 | 48.416587,2.725312 1664 | 48.416603,2.725344 1665 | 48.416609,2.725382 1666 | 48.416609,2.725409 1667 | 48.416592,2.725457 1668 | 48.416587,2.725484 1669 | 48.416587,2.725505 1670 | 48.416576,2.725559 1671 | 48.416576,2.725575 1672 | 48.416566,2.725618 1673 | 48.416560,2.725655 1674 | 48.416560,2.725682 1675 | 48.416555,2.725693 1676 | 48.416550,2.725731 1677 | 48.416544,2.725752 1678 | 48.416523,2.725838 1679 | 48.416507,2.725950 1680 | 48.416501,2.725972 1681 | 48.416501,2.725999 1682 | 48.416491,2.726020 1683 | 48.416485,2.726047 1684 | 48.416469,2.726090 1685 | 48.416474,2.726090 1686 | 48.416474,2.726095 1687 | 48.416469,2.726095 1688 | 48.416469,2.726101 1689 | 48.416464,2.726117 1690 | 48.416458,2.726122 1691 | 48.416458,2.726138 1692 | 48.416458,2.726144 1693 | 48.416458,2.726149 1694 | 48.416458,2.726160 1695 | 48.416458,2.726165 1696 | 1697 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/assets/samples/line-by-line/EcoSenspodSample.csv: -------------------------------------------------------------------------------- 1 | $PSEN,Hum,H,50.93,T,30.88 2 | $PSEN,Noise,dB,038 3 | $PSEN,NOx,V,2.594 4 | $PSEN,COx,V,1.625 5 | $GPRMC,011514.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*77 6 | $PSEN,Hum,H,50.90,T,30.91 7 | $PSEN,Noise,dB,039 8 | $PSEN,NOx,V,2.590 9 | $PSEN,COx,V,1.627 10 | $GPRMC,011515.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*76 11 | $PSEN,Hum,H,50.90,T,30.91 12 | $PSEN,Noise,dB,041 13 | $PSEN,NOx,V,2.591 14 | $PSEN,COx,V,1.626 15 | $GPRMC,011516.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*75 16 | $PSEN,Hum,H,50.87,T,30.91 17 | $PSEN,Batt,V,3.97 18 | $PSEN,Noise,dB,029 19 | $PSEN,NOx,V,2.591 20 | $PSEN,COx,V,1.627 21 | $GPRMC,011517.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*74 22 | $PSEN,Hum,H,50.84,T,30.93 23 | $PSEN,Noise,dB,030 24 | $PSEN,NOx,V,2.593 25 | $PSEN,COx,V,1.627 26 | $GPRMC,011518.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*7B 27 | $PSEN,Hum,H,50.81,T,30.94 28 | $PSEN,Noise,dB,034 29 | $PSEN,NOx,V,2.587 30 | $PSEN,COx,V,1.625 31 | $GPRMC,011519.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*7A 32 | $PSEN,Hum,H,50.81,T,30.94 33 | $PSEN,Noise,dB,031 34 | $PSEN,NOx,V,2.591 35 | $PSEN,COx,V,1.626 36 | $GPRMC,011520.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*70 37 | $PSEN,Hum,H,50.78,T,30.98 38 | $PSEN,Noise,dB,031 39 | $PSEN,NOx,V,2.592 40 | $PSEN,COx,V,1.627 41 | $GPRMC,011521.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*71 42 | $PSEN,Hum,H,50.78,T,30.97 43 | $PSEN,Batt,V,3.98 44 | $PSEN,Noise,dB,038 45 | $PSEN,NOx,V,2.590 46 | $PSEN,COx,V,1.626 47 | $GPRMC,011522.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*72 48 | $PSEN,Hum,H,50.78,T,30.98 49 | $PSEN,Noise,dB,032 50 | $PSEN,NOx,V,2.592 51 | $PSEN,COx,V,1.628 52 | $PSEN,Hum,H,50.68,T,30.98 53 | $GPRMC,011523.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*73 54 | $PSEN,Noise,dB,034 55 | $PSEN,NOx,V,2.589 56 | $PSEN,COx,V,1.628 57 | $PSEN,Hum,H,50.62,T,30.98 58 | $GPRMC,011524.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*74 59 | $PSEN,Noise,dB,034 60 | $PSEN,NOx,V,2.592 61 | $PSEN,COx,V,1.630 62 | $PSEN,Hum,H,50.59,T,31.00 63 | $GPRMC,011525.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*75 64 | $PSEN,Noise,dB,032 65 | $PSEN,NOx,V,2.588 66 | $PSEN,COx,V,1.627 67 | $PSEN,Hum,H,50.62,T,31.00 68 | $GPRMC,011526.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*76 69 | $PSEN,Batt,V,3.97 70 | $PSEN,Noise,dB,031 71 | $PSEN,NOx,V,2.589 72 | $PSEN,COx,V,1.626 73 | $PSEN,Hum,H,50.59,T,31.01 74 | $GPRMC,011527.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*77 75 | $PSEN,Noise,dB,031 76 | $PSEN,NOx,V,2.590 77 | $PSEN,COx,V,1.626 78 | $PSEN,Hum,H,50.56,T,31.01 79 | $GPRMC,011528.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*78 80 | $PSEN,Noise,dB,034 81 | $PSEN,NOx,V,2.589 82 | $PSEN,COx,V,1.628 83 | $PSEN,Hum,H,50.56,T,31.03 84 | $GPRMC,011529.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*79 85 | $PSEN,Noise,dB,034 86 | $PSEN,NOx,V,2.588 87 | $PSEN,COx,V,1.620 88 | $PSEN,Hum,H,50.66,T,31.01 89 | $GPRMC,011530.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*71 90 | $PSEN,Noise,dB,038 91 | $PSEN,NOx,V,2.589 92 | $PSEN,COx,V,1.615 93 | $PSEN,Hum,H,50.72,T,31.04 94 | $GPRMC,011531.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*70 95 | $PSEN,Batt,V,3.98 96 | $PSEN,Noise,dB,033 97 | $PSEN,NOx,V,2.586 98 | $PSEN,COx,V,1.615 99 | $PSEN,Hum,H,50.76,T,31.05 100 | $GPRMC,011532.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*73 101 | $PSEN,Noise,dB,041 102 | $PSEN,NOx,V,2.586 103 | $PSEN,COx,V,1.616 104 | $PSEN,Hum,H,50.76,T,31.05 105 | $GPRMC,011533.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*72 106 | $PSEN,Noise,dB,037 107 | $PSEN,NOx,V,2.586 108 | $PSEN,COx,V,1.615 109 | $PSEN,Hum,H,50.69,T,31.05 110 | $GPRMC,011534.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*75 111 | $PSEN,Noise,dB,036 112 | $PSEN,NOx,V,2.584 113 | $PSEN,COx,V,1.617 114 | $PSEN,Hum,H,50.66,T,31.07 115 | $GPRMC,011535.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*74 116 | $PSEN,Noise,dB,037 117 | $PSEN,NOx,V,2.581 118 | $PSEN,COx,V,1.618 119 | $PSEN,Hum,H,50.60,T,31.07 120 | $GPRMC,011536.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*77 121 | $PSEN,Batt,V,3.98 122 | $PSEN,Noise,dB,039 123 | $PSEN,NOx,V,2.584 124 | $PSEN,COx,V,1.621 125 | $PSEN,Hum,H,50.57,T,31.08 126 | $GPRMC,011537.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*76 127 | $PSEN,Noise,dB,035 128 | $PSEN,NOx,V,2.584 129 | $PSEN,COx,V,1.621 130 | $PSEN,Hum,H,50.54,T,31.08 131 | $GPRMC,011538.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*79 132 | $PSEN,Noise,dB,037 133 | $PSEN,NOx,V,2.584 134 | $PSEN,COx,V,1.622 135 | $PSEN,Hum,H,50.51,T,31.10 136 | $GPRMC,011539.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*78 137 | $PSEN,Noise,dB,034 138 | $PSEN,NOx,V,2.585 139 | $PSEN,COx,V,1.623 140 | $PSEN,Hum,H,50.50,T,31.08 141 | $GPRMC,011540.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*76 142 | $PSEN,Noise,dB,037 143 | $PSEN,NOx,V,2.580 144 | $PSEN,COx,V,1.623 145 | $PSEN,Hum,H,50.47,T,31.08 146 | $GPRMC,011541.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*77 147 | $PSEN,Batt,V,3.98 148 | $PSEN,Noise,dB,037 149 | $PSEN,NOx,V,2.583 150 | $PSEN,COx,V,1.623 151 | $PSEN,Hum,H,50.44,T,31.10 152 | $GPRMC,011542.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*74 153 | $PSEN,Noise,dB,038 154 | $PSEN,NOx,V,2.580 155 | $PSEN,COx,V,1.621 156 | $PSEN,Hum,H,50.41,T,31.10 157 | $GPRMC,011543.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*75 158 | $PSEN,Noise,dB,040 159 | $PSEN,NOx,V,2.579 160 | $PSEN,COx,V,1.620 161 | $PSEN,Hum,H,50.34,T,31.11 162 | $GPRMC,011544.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*72 163 | $PSEN,Noise,dB,038 164 | $PSEN,NOx,V,2.580 165 | $PSEN,COx,V,1.620 166 | $PSEN,Hum,H,50.28,T,31.11 167 | $GPRMC,011545.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*73 168 | $PSEN,Noise,dB,038 169 | $PSEN,NOx,V,2.581 170 | $PSEN,COx,V,1.621 171 | $PSEN,Hum,H,50.22,T,31.13 172 | $GPRMC,011546.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*70 173 | $PSEN,Batt,V,3.98 174 | $PSEN,Noise,dB,039 175 | $PSEN,NOx,V,2.575 176 | $PSEN,COx,V,1.622 177 | $PSEN,Hum,H,50.09,T,31.13 178 | $GPRMC,011547.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*71 179 | $PSEN,Noise,dB,040 180 | $PSEN,NOx,V,2.580 181 | $PSEN,COx,V,1.615 182 | $PSEN,Hum,H,50.02,T,31.12 183 | $GPRMC,011548.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*7E 184 | $PSEN,Noise,dB,037 185 | $PSEN,NOx,V,2.581 186 | $PSEN,COx,V,1.618 187 | $PSEN,Hum,H,49.99,T,31.13 188 | $GPRMC,011549.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*7F 189 | $PSEN,Noise,dB,037 190 | $PSEN,NOx,V,2.579 191 | $PSEN,COx,V,1.614 192 | $PSEN,Hum,H,49.96,T,31.14 193 | $GPRMC,011550.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*77 194 | $PSEN,Noise,dB,040 195 | $PSEN,NOx,V,2.581 196 | $PSEN,COx,V,1.616 197 | $PSEN,Hum,H,49.89,T,31.14 198 | $GPRMC,011551.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*76 199 | $PSEN,Batt,V,3.98 200 | $PSEN,Noise,dB,037 201 | $PSEN,NOx,V,2.577 202 | $PSEN,COx,V,1.618 203 | $PSEN,Hum,H,49.90,T,31.15 204 | $GPRMC,011552.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*75 205 | $PSEN,Noise,dB,039 206 | $PSEN,NOx,V,2.576 207 | $PSEN,COx,V,1.622 208 | $PSEN,Hum,H,49.90,T,31.15 209 | $PSEN,Noise,dB,039 210 | $PSEN,NOx,V,2.580 211 | $PSEN,COx,V,1.620 212 | $GPRMC,011553.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*74 213 | $PSEN,Hum,H,49.90,T,31.16 214 | $PSEN,Noise,dB,039 215 | $PSEN,NOx,V,2.580 216 | $PSEN,COx,V,1.620 217 | $GPRMC,011554.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*73 218 | $PSEN,Hum,H,49.83,T,31.15 219 | $PSEN,Noise,dB,040 220 | $PSEN,NOx,V,2.583 221 | $PSEN,COx,V,1.622 222 | $GPRMC,011555.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*72 223 | $PSEN,Hum,H,49.80,T,31.16 224 | $PSEN,Batt,V,3.97 225 | $PSEN,Noise,dB,039 226 | $PSEN,NOx,V,2.580 227 | $PSEN,COx,V,1.622 228 | $GPRMC,011556.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*71 229 | $PSEN,Hum,H,49.77,T,31.16 230 | $PSEN,Noise,dB,045 231 | $PSEN,NOx,V,2.572 232 | $PSEN,COx,V,1.623 233 | $GPRMC,011557.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*70 234 | $PSEN,Hum,H,49.70,T,31.16 235 | $PSEN,Noise,dB,040 236 | $PSEN,NOx,V,2.580 237 | $PSEN,COx,V,1.622 238 | $GPRMC,011558.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*7F 239 | $PSEN,Hum,H,49.70,T,31.16 240 | $PSEN,Noise,dB,041 241 | $PSEN,NOx,V,2.580 242 | $PSEN,COx,V,1.624 243 | $GPRMC,011559.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*7E 244 | $PSEN,Hum,H,49.70,T,31.18 245 | $PSEN,Noise,dB,040 246 | $PSEN,NOx,V,2.576 247 | $PSEN,COx,V,1.624 248 | $GPRMC,011600.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*71 249 | $PSEN,Hum,H,49.67,T,31.18 250 | $PSEN,Batt,V,3.98 251 | $PSEN,Noise,dB,040 252 | $PSEN,NOx,V,2.576 253 | $PSEN,COx,V,1.627 254 | $GPRMC,011601.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*70 255 | $PSEN,Hum,H,49.71,T,31.19 256 | $PSEN,Noise,dB,040 257 | $PSEN,NOx,V,2.580 258 | $PSEN,COx,V,1.626 259 | $GPRMC,011602.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*73 260 | $PSEN,Hum,H,49.71,T,31.19 261 | $PSEN,Noise,dB,040 262 | $PSEN,NOx,V,2.580 263 | $PSEN,COx,V,1.626 264 | $GPRMC,011603.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*72 265 | $PSEN,Hum,H,49.70,T,31.18 266 | $PSEN,Noise,dB,039 267 | $PSEN,NOx,V,2.578 268 | $PSEN,COx,V,1.623 269 | $GPRMC,011604.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*75 270 | $PSEN,Hum,H,49.68,T,31.19 271 | $PSEN,Noise,dB,039 272 | $PSEN,NOx,V,2.576 273 | $PSEN,COx,V,1.623 274 | $PSEN,Hum,H,49.61,T,31.22 275 | $GPRMC,011605.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*74 276 | $PSEN,Batt,V,3.97 277 | $PSEN,Noise,dB,038 278 | $PSEN,NOx,V,2.575 279 | $PSEN,COx,V,1.621 280 | $PSEN,Hum,H,49.58,T,31.20 281 | $GPRMC,011606.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*77 282 | $PSEN,Noise,dB,040 283 | $PSEN,NOx,V,2.576 284 | $PSEN,COx,V,1.630 285 | $PSEN,Hum,H,49.55,T,31.20 286 | $GPRMC,011607.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*76 287 | $PSEN,Noise,dB,039 288 | $PSEN,NOx,V,2.574 289 | $PSEN,COx,V,1.627 290 | $PSEN,Hum,H,49.48,T,31.22 291 | $GPRMC,011608.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*79 292 | $PSEN,Noise,dB,040 293 | $PSEN,NOx,V,2.576 294 | $PSEN,COx,V,1.624 295 | $PSEN,Hum,H,49.52,T,31.23 296 | $GPRMC,011609.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*78 297 | $PSEN,Noise,dB,038 298 | $PSEN,NOx,V,2.576 299 | $PSEN,COx,V,1.626 300 | $PSEN,Hum,H,49.48,T,31.23 301 | $GPRMC,011610.127,V,8960.0000,N,00000.0000,E,0.00,0.00,280910,,,N*70 302 | $PSEN,Batt,V,3.97 303 | $PSEN,Noise,dB,043 304 | $PSEN,NOx,V,2.576 305 | $PSEN,COx,V,1.628 306 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/BluetoothAdapterDelegate.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | 6 | import java.util.Set; 7 | 8 | public class BluetoothAdapterDelegate implements BluetoothAdapterWrapper { 9 | 10 | private final BluetoothAdapter adapter; 11 | 12 | public BluetoothAdapterDelegate(BluetoothAdapter adapter) { 13 | assert adapter != null; 14 | this.adapter = adapter; 15 | } 16 | 17 | @Override 18 | public Set getBondedDevices() { 19 | return adapter.getBondedDevices(); 20 | } 21 | 22 | @Override 23 | public void cancelDiscovery() { 24 | adapter.cancelDiscovery(); 25 | } 26 | 27 | @Override 28 | public boolean isDiscovering() { 29 | return adapter.isDiscovering(); 30 | } 31 | 32 | @Override 33 | public void startDiscovery() { 34 | adapter.startDiscovery(); 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/BluetoothAdapterFactory.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | 5 | public class BluetoothAdapterFactory { 6 | private BluetoothAdapterFactory() { 7 | // utility class 8 | } 9 | 10 | public static BluetoothAdapterWrapper getBluetoothAdapterWrapper() { 11 | BluetoothAdapter defaultAdapter = BluetoothAdapter.getDefaultAdapter(); 12 | return defaultAdapter != null ? new BluetoothAdapterDelegate(defaultAdapter) : new NullBluetoothWrapper(); 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/BluetoothAdapterWrapper.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | import android.bluetooth.BluetoothDevice; 4 | 5 | import java.util.Set; 6 | 7 | public interface BluetoothAdapterWrapper { 8 | 9 | Set getBondedDevices(); 10 | 11 | void cancelDiscovery(); 12 | 13 | boolean isDiscovering(); 14 | 15 | void startDiscovery(); 16 | } 17 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/BluetoothDeviceConnector.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.bluetoothviewer; 18 | 19 | import android.bluetooth.BluetoothAdapter; 20 | import android.bluetooth.BluetoothDevice; 21 | import android.bluetooth.BluetoothSocket; 22 | import android.util.Log; 23 | 24 | import java.io.BufferedReader; 25 | import java.io.IOException; 26 | import java.io.InputStream; 27 | import java.io.InputStreamReader; 28 | import java.io.OutputStream; 29 | import java.lang.reflect.InvocationTargetException; 30 | import java.lang.reflect.Method; 31 | 32 | /** 33 | * This class does all the work for setting up and managing Bluetooth 34 | * connections with other devices. It has a thread that listens for 35 | * incoming connections, a thread for connecting with a device, and a 36 | * thread for performing data transmissions when connected. 37 | */ 38 | public class BluetoothDeviceConnector implements DeviceConnector { 39 | 40 | private static final String TAG = BluetoothDeviceConnector.class.getSimpleName(); 41 | private static final boolean D = true; 42 | 43 | public static final int CHANNEL = 1; 44 | 45 | private static final int STATE_NONE = 0; // we're doing nothing 46 | private static final int STATE_CONNECTING = 2; // now initiating an outgoing connection 47 | private static final int STATE_CONNECTED = 3; // now connected to a remote device 48 | 49 | private final BluetoothAdapter mAdapter; 50 | private final MessageHandler mHandler; 51 | private final String mAddress; 52 | private ConnectThread mConnectThread; 53 | private ConnectedThread mConnectedThread; 54 | private int mState; 55 | 56 | 57 | /** 58 | * Prepare a new Bluetooth session. 59 | * 60 | * @param handler A Handler to send messages back to the UI Activity 61 | */ 62 | public BluetoothDeviceConnector(MessageHandler handler, String address) { 63 | mAdapter = BluetoothAdapter.getDefaultAdapter(); 64 | mState = STATE_NONE; 65 | mHandler = handler; 66 | mAddress = address; 67 | } 68 | 69 | /** 70 | * Set the current state of the connection 71 | * 72 | * @param state An integer defining the current connection state 73 | */ 74 | private synchronized void setState(int state) { 75 | if (D) Log.d(TAG, "setState() " + mState + " -> " + state); 76 | mState = state; 77 | } 78 | 79 | private BluetoothAdapter getBluetoothAdapter() { 80 | return BluetoothAdapter.getDefaultAdapter(); 81 | } 82 | 83 | @Override 84 | public synchronized void connect() { 85 | BluetoothDevice device = getBluetoothAdapter().getRemoteDevice(mAddress); 86 | connect(device); 87 | } 88 | 89 | /** 90 | * Start the ConnectThread to initiate a connection to a remote device. 91 | * 92 | * @param device The BluetoothDevice to connect 93 | */ 94 | public synchronized void connect(BluetoothDevice device) { 95 | if (D) Log.d(TAG, "connect to: " + device); 96 | 97 | // Cancel any thread attempting to make a connection 98 | if (mState == STATE_CONNECTING && mConnectThread != null) { 99 | mConnectThread.cancel(); 100 | mConnectThread = null; 101 | } 102 | 103 | // Cancel any thread currently running a connection 104 | if (mConnectedThread != null) { 105 | mConnectedThread.cancel(); 106 | mConnectedThread = null; 107 | } 108 | 109 | // Start the thread to connect with the given device 110 | try { 111 | mConnectThread = new ConnectThread(device); 112 | mConnectThread.start(); 113 | setState(STATE_CONNECTING); 114 | mHandler.sendConnectingTo(device.getName()); 115 | } catch (SecurityException e) { 116 | Log.e(TAG, e.getMessage(), e); 117 | } catch (IllegalArgumentException e) { 118 | Log.e(TAG, e.getMessage(), e); 119 | } catch (NoSuchMethodException e) { 120 | Log.e(TAG, e.getMessage(), e); 121 | } catch (IllegalAccessException e) { 122 | Log.e(TAG, e.getMessage(), e); 123 | } catch (InvocationTargetException e) { 124 | Log.e(TAG, e.getMessage(), e); 125 | } 126 | } 127 | 128 | /** 129 | * Start the ConnectedThread to begin managing a Bluetooth connection 130 | * 131 | * @param socket The BluetoothSocket on which the connection was made 132 | * @param device The BluetoothDevice that has been connected 133 | */ 134 | public synchronized void connected(BluetoothSocket socket, BluetoothDevice device) { 135 | if (D) Log.d(TAG, "connected"); 136 | 137 | // Cancel the thread that completed the connection 138 | if (mConnectThread != null) { 139 | mConnectThread.cancel(); 140 | mConnectThread = null; 141 | } 142 | 143 | // Cancel any thread currently running a connection 144 | if (mConnectedThread != null) { 145 | mConnectedThread.cancel(); 146 | mConnectedThread = null; 147 | } 148 | 149 | // Start the thread to manage the connection and perform transmissions 150 | mConnectedThread = new ConnectedThread(socket); 151 | mConnectedThread.start(); 152 | 153 | setState(STATE_CONNECTED); 154 | mHandler.sendConnectedTo(device.getName()); 155 | } 156 | 157 | /** 158 | * Stop all threads 159 | */ 160 | @Override 161 | public synchronized void disconnect() { 162 | if (D) Log.d(TAG, "shutdown"); 163 | 164 | if (mConnectThread != null) { 165 | mConnectThread.cancel(); 166 | mConnectThread = null; 167 | } 168 | 169 | if (mConnectedThread != null) { 170 | mConnectedThread.shutdown(); 171 | mConnectedThread.cancel(); 172 | mConnectedThread = null; 173 | } 174 | 175 | setState(STATE_NONE); 176 | mHandler.sendNotConnected(); 177 | } 178 | 179 | @Override 180 | public void sendAsciiMessage(CharSequence chars) { 181 | write((chars.toString() + "\n").getBytes()); 182 | } 183 | 184 | /** 185 | * Write to the ConnectedThread in an unsynchronized manner 186 | * 187 | * @param out The bytes to write 188 | * @see ConnectedThread#write(byte[]) 189 | */ 190 | private void write(byte[] out) { 191 | // Create temporary object 192 | ConnectedThread r; 193 | // Synchronize a copy of the ConnectedThread 194 | synchronized (this) { 195 | if (mState != STATE_CONNECTED) return; 196 | r = mConnectedThread; 197 | } 198 | // Perform the write unsynchronized 199 | r.write(out); 200 | } 201 | 202 | /** 203 | * This thread runs while attempting to make an outgoing connection 204 | * with a device. It runs straight through; the connection either 205 | * succeeds or fails. 206 | */ 207 | private class ConnectThread extends Thread { 208 | private final BluetoothSocket mmSocket; 209 | private final BluetoothDevice mmDevice; 210 | 211 | public ConnectThread(BluetoothDevice device) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException { 212 | mmDevice = device; 213 | BluetoothSocket tmp = null; 214 | 215 | Log.i(TAG, "calling device.createRfcommSocket with channel " + CHANNEL + " ..."); 216 | try { 217 | // call hidden method, see BluetoothDevice source code for more details: 218 | // https://android.googlesource.com/platform/frameworks/base/+/master/core/java/android/bluetooth/BluetoothDevice.java 219 | Method m = device.getClass().getMethod("createRfcommSocket", int.class); 220 | tmp = (BluetoothSocket) m.invoke(device, CHANNEL); 221 | Log.i(TAG, "setting socket to result of createRfcommSocket"); 222 | } catch (Exception e) { 223 | Log.e(TAG, e.getMessage(), e); 224 | } 225 | mmSocket = tmp; 226 | } 227 | 228 | @Override 229 | public void run() { 230 | Log.i(TAG, "BEGIN mConnectThread"); 231 | setName("ConnectThread"); 232 | 233 | // Always cancel discovery because it will slow down a connection 234 | mAdapter.cancelDiscovery(); 235 | 236 | // Make a connection to the BluetoothSocket 237 | try { 238 | // This is a blocking call and will only return on a 239 | // successful connection or an exception 240 | mmSocket.connect(); 241 | } catch (IOException e) { 242 | Log.e(TAG, e.getMessage(), e); 243 | setState(STATE_NONE); 244 | mHandler.sendConnectionFailed(); 245 | 246 | try { 247 | mmSocket.close(); 248 | } catch (IOException e2) { 249 | Log.e(TAG, "unable to close() socket during connection failure", e2); 250 | } 251 | return; 252 | } 253 | 254 | // Reset the ConnectThread because we're done 255 | synchronized (BluetoothDeviceConnector.this) { 256 | mConnectThread = null; 257 | } 258 | 259 | // Start the connected thread 260 | connected(mmSocket, mmDevice); 261 | } 262 | 263 | public void cancel() { 264 | try { 265 | mmSocket.close(); 266 | } catch (IOException e) { 267 | Log.e(TAG, "close() of connect socket failed", e); 268 | } 269 | } 270 | } 271 | 272 | /** 273 | * This thread runs during a connection with a remote device. 274 | * It handles all incoming and outgoing transmissions. 275 | */ 276 | private class ConnectedThread extends Thread { 277 | private final BluetoothSocket mmSocket; 278 | private final InputStream mmInStream; 279 | private final OutputStream mmOutStream; 280 | 281 | public ConnectedThread(BluetoothSocket socket) { 282 | Log.d(TAG, "create ConnectedThread"); 283 | mmSocket = socket; 284 | InputStream tmpIn = null; 285 | OutputStream tmpOut = null; 286 | 287 | // Get the BluetoothSocket input and output streams 288 | try { 289 | tmpIn = socket.getInputStream(); 290 | tmpOut = socket.getOutputStream(); 291 | } catch (IOException e) { 292 | Log.e(TAG, "temp sockets not created", e); 293 | } 294 | 295 | mmInStream = tmpIn; 296 | mmOutStream = tmpOut; 297 | } 298 | 299 | private boolean stop = false; 300 | private boolean hasReadAnything = false; 301 | 302 | public void shutdown() { 303 | stop = true; 304 | if (!hasReadAnything) return; 305 | if (mmInStream != null) { 306 | try { 307 | mmInStream.close(); 308 | } catch (IOException e) { 309 | Log.e(TAG, "close() of InputStream failed."); 310 | } 311 | } 312 | } 313 | 314 | @Override 315 | public void run() { 316 | Log.i(TAG, "BEGIN mConnectedThread"); 317 | 318 | BufferedReader reader = new BufferedReader(new InputStreamReader(mmInStream)); 319 | 320 | while (!stop) { 321 | try { 322 | String line = reader.readLine(); 323 | if (line != null) { 324 | mHandler.sendLineRead(line); 325 | } 326 | } catch (IOException e) { 327 | Log.e(TAG, "disconnected", e); 328 | setState(STATE_NONE); 329 | mHandler.sendConnectionLost(); 330 | break; 331 | } 332 | } 333 | } 334 | 335 | /** 336 | * Write to the connected OutStream. 337 | * 338 | * @param bytes The bytes to write 339 | */ 340 | public void write(byte[] bytes) { 341 | try { 342 | mmOutStream.write(bytes); 343 | mHandler.sendBytesWritten(bytes); 344 | } catch (IOException e) { 345 | Log.e(TAG, "Exception during write", e); 346 | } 347 | } 348 | 349 | public void cancel() { 350 | try { 351 | mmSocket.close(); 352 | } catch (IOException e) { 353 | Log.e(TAG, "close() of connect socket failed", e); 354 | } 355 | } 356 | } 357 | } 358 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/BluetoothViewer.java: -------------------------------------------------------------------------------- 1 | /* 2 | Licensed under the Apache License, Version 2.0 (the "License"); 3 | you may not use this file except in compliance with the License. 4 | You may obtain a copy of the License at 5 | 6 | http://www.apache.org/licenses/LICENSE-2.0 7 | 8 | Unless required by applicable law or agreed to in writing, software 9 | distributed under the License is distributed on an "AS IS" BASIS, 10 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11 | See the License for the specific language governing permissions and 12 | limitations under the License. 13 | */ 14 | 15 | package net.bluetoothviewer; 16 | 17 | import android.app.Activity; 18 | import android.bluetooth.BluetoothAdapter; 19 | import android.content.ActivityNotFoundException; 20 | import android.content.Intent; 21 | import android.content.SharedPreferences; 22 | import android.net.Uri; 23 | import android.os.Bundle; 24 | import android.os.Handler; 25 | import android.os.Message; 26 | import android.preference.PreferenceManager; 27 | import android.text.Html; 28 | import android.text.method.LinkMovementMethod; 29 | import android.util.Log; 30 | import android.view.KeyEvent; 31 | import android.view.Menu; 32 | import android.view.MenuInflater; 33 | import android.view.MenuItem; 34 | import android.view.View; 35 | import android.view.View.OnClickListener; 36 | import android.view.inputmethod.EditorInfo; 37 | import android.widget.ArrayAdapter; 38 | import android.widget.Button; 39 | import android.widget.EditText; 40 | import android.widget.ImageButton; 41 | import android.widget.ListView; 42 | import android.widget.TextView; 43 | import android.widget.Toast; 44 | 45 | import net.bluetoothviewer.library.R; 46 | import net.bluetoothviewer.util.ApplicationUtils; 47 | import net.bluetoothviewer.util.EmailUtils; 48 | 49 | public class BluetoothViewer extends Activity implements SharedPreferences.OnSharedPreferenceChangeListener { 50 | 51 | private static final String TAG = BluetoothViewer.class.getSimpleName(); 52 | private static final boolean D = true; 53 | 54 | // Intent request codes 55 | private static final int REQUEST_CONNECT_DEVICE = 1; 56 | private static final int REQUEST_ENABLE_BT = 2; 57 | private static final int REQUEST_LAUNCH_EMAIL_APP = 3; 58 | private static final int MENU_SETTINGS = 4; 59 | 60 | private static final String SAVED_PENDING_REQUEST_ENABLE_BT = "PENDING_REQUEST_ENABLE_BT"; 61 | 62 | // Layout Views 63 | private TextView mStatusView; 64 | private ListView mConversationView; 65 | private EditText mOutEditText; 66 | private View mSendTextContainer; 67 | 68 | // Toolbar 69 | private ImageButton mToolbarConnectButton; 70 | private ImageButton mToolbarDisconnectButton; 71 | private ImageButton mToolbarPauseButton; 72 | private ImageButton mToolbarPlayButton; 73 | 74 | private TextView mWelcomeText; 75 | 76 | private ArrayAdapter mConversationArrayAdapter; 77 | private DeviceConnector mDeviceConnector = new NullDeviceConnector(); 78 | 79 | // State variables 80 | private boolean paused = false; 81 | private boolean connected = false; 82 | 83 | // do not resend request to enable Bluetooth 84 | // if there is a request already in progress 85 | // See: https://code.google.com/p/android/issues/detail?id=24931#c1 86 | private boolean pendingRequestEnableBt = false; 87 | 88 | // controlled by user settings 89 | private boolean recordingEnabled; 90 | private String defaultEmail; 91 | private boolean mockDevicesEnabled; 92 | 93 | private String deviceName; 94 | 95 | private final StringBuilder recording = new StringBuilder(); 96 | 97 | // The Handler that gets information back from the BluetoothService 98 | private final Handler mHandler = new Handler() { 99 | @Override 100 | public void handleMessage(Message msg) { 101 | switch (msg.what) { 102 | case MessageHandler.Constants.MSG_CONNECTED: 103 | mWelcomeText.setVisibility(View.GONE); 104 | connected = true; 105 | mStatusView.setText(formatStatusMessage(R.string.btstatus_connected_to_fmt, msg.obj)); 106 | onBluetoothStateChanged(); 107 | recording.setLength(0); 108 | deviceName = msg.obj.toString(); 109 | break; 110 | case MessageHandler.Constants.MSG_CONNECTING: 111 | connected = false; 112 | mStatusView.setText(formatStatusMessage(R.string.btstatus_connecting_to_fmt, msg.obj)); 113 | onBluetoothStateChanged(); 114 | break; 115 | case MessageHandler.Constants.MSG_NOT_CONNECTED: 116 | case MessageHandler.Constants.MSG_CONNECTION_FAILED: 117 | case MessageHandler.Constants.MSG_CONNECTION_LOST: 118 | connected = false; 119 | mStatusView.setText(R.string.btstatus_not_connected); 120 | onBluetoothStateChanged(); 121 | break; 122 | case MessageHandler.Constants.MSG_BYTES_WRITTEN: 123 | String written = new String((byte[]) msg.obj); 124 | mConversationArrayAdapter.add(">>> " + written); 125 | Log.i(TAG, "written = '" + written + "'"); 126 | break; 127 | case MessageHandler.Constants.MSG_LINE_READ: 128 | if (paused) break; 129 | String line = (String) msg.obj; 130 | if (D) Log.d(TAG, line); 131 | mConversationArrayAdapter.add(line); 132 | if (recordingEnabled) { 133 | recording.append(line).append("\n"); 134 | } 135 | break; 136 | default: 137 | Log.d(TAG, "Unkown message: " + msg.what + ", arg1= " + msg.arg1 + ", arg2= " + msg.arg2); 138 | } 139 | } 140 | 141 | private String formatStatusMessage(int formatResId, Object deviceName) { 142 | return getString(formatResId, (String) deviceName); 143 | } 144 | }; 145 | 146 | private TextView.OnEditorActionListener mWriteListener = 147 | new TextView.OnEditorActionListener() { 148 | public boolean onEditorAction(TextView view, int actionId, KeyEvent event) { 149 | if (actionId == EditorInfo.IME_NULL && event.getAction() == KeyEvent.ACTION_UP) { 150 | sendMessage(view.getText()); 151 | } 152 | return true; 153 | } 154 | }; 155 | 156 | @Override 157 | public void onCreate(Bundle savedInstanceState) { 158 | Log.d(TAG, "++onCreate"); 159 | super.onCreate(savedInstanceState); 160 | 161 | if (savedInstanceState != null) { 162 | pendingRequestEnableBt = savedInstanceState.getBoolean(SAVED_PENDING_REQUEST_ENABLE_BT); 163 | } 164 | 165 | setContentView(R.layout.bluetoothviewer); 166 | 167 | updateParamsFromSettings(); 168 | 169 | mStatusView = (TextView) findViewById(R.id.btstatus); 170 | 171 | mSendTextContainer = findViewById(R.id.send_text_container); 172 | 173 | mToolbarConnectButton = (ImageButton) findViewById(R.id.toolbar_btn_connect); 174 | mToolbarConnectButton.setOnClickListener(new OnClickListener() { 175 | public void onClick(View v) { 176 | startDeviceListActivity(); 177 | } 178 | }); 179 | 180 | mToolbarDisconnectButton = (ImageButton) findViewById(R.id.toolbar_btn_disconnect); 181 | mToolbarDisconnectButton.setOnClickListener(new OnClickListener() { 182 | public void onClick(View v) { 183 | disconnectDevices(); 184 | } 185 | }); 186 | 187 | mToolbarPauseButton = (ImageButton) findViewById(R.id.toolbar_btn_pause); 188 | mToolbarPauseButton.setOnClickListener(new OnClickListener() { 189 | public void onClick(View v) { 190 | paused = true; 191 | onPausedStateChanged(); 192 | } 193 | }); 194 | 195 | mToolbarPlayButton = (ImageButton) findViewById(R.id.toolbar_btn_play); 196 | mToolbarPlayButton.setOnClickListener(new OnClickListener() { 197 | public void onClick(View v) { 198 | paused = false; 199 | onPausedStateChanged(); 200 | } 201 | }); 202 | 203 | mConversationArrayAdapter = new ArrayAdapter(this, R.layout.message); 204 | mConversationView = (ListView) findViewById(R.id.in); 205 | mConversationView.setAdapter(mConversationArrayAdapter); 206 | 207 | boolean isLiteVersion = ApplicationUtils.isLiteVersion(getApplication()); 208 | mWelcomeText = (TextView) findViewById(R.id.msg_welcome); 209 | mWelcomeText.setText(Html.fromHtml(getString(isLiteVersion ? R.string.welcome_lite : R.string.welcome_full))); 210 | mWelcomeText.setMovementMethod(LinkMovementMethod.getInstance()); 211 | 212 | // Initialize the compose field with a listener for the return key 213 | mOutEditText = (EditText) findViewById(R.id.edit_text_out); 214 | mOutEditText.setOnEditorActionListener(mWriteListener); 215 | 216 | // Initialize the send button with a listener for click events 217 | Button mSendButton = (Button) findViewById(R.id.button_send); 218 | mSendButton.setOnClickListener(new OnClickListener() { 219 | public void onClick(View v) { 220 | TextView view = (TextView) findViewById(R.id.edit_text_out); 221 | sendMessage(view.getText()); 222 | } 223 | }); 224 | 225 | onBluetoothStateChanged(); 226 | 227 | if (!mockDevicesEnabled) { 228 | requestEnableBluetooth(); 229 | } 230 | } 231 | 232 | private void updateParamsFromSettings() { 233 | recordingEnabled = getSharedPreferences().getBoolean(getString(R.string.pref_record), false); 234 | defaultEmail = getSharedPreferences().getString(getString(R.string.pref_default_email), ""); 235 | mockDevicesEnabled = getSharedPreferences().getBoolean(getString(R.string.pref_enable_mock_devices), false); 236 | } 237 | 238 | private void startDeviceListActivity() { 239 | Intent intent = new Intent(this, DeviceListActivity.class); 240 | intent.putExtra(DeviceListActivity.EXTRA_MOCK_DEVICES_ENABLED, mockDevicesEnabled); 241 | startActivityForResult(intent, REQUEST_CONNECT_DEVICE); 242 | } 243 | 244 | private void requestEnableBluetooth() { 245 | if (!isBluetoothAdapterEnabled() && !pendingRequestEnableBt) { 246 | pendingRequestEnableBt = true; 247 | Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE); 248 | startActivityForResult(enableIntent, REQUEST_ENABLE_BT); 249 | } 250 | } 251 | 252 | private boolean isBluetoothAdapterEnabled() { 253 | return getBluetoothAdapter().isEnabled(); 254 | } 255 | 256 | private BluetoothAdapter getBluetoothAdapter() { 257 | return BluetoothAdapter.getDefaultAdapter(); 258 | } 259 | 260 | @Override 261 | public void onDestroy() { 262 | super.onDestroy(); 263 | mDeviceConnector.disconnect(); 264 | } 265 | 266 | private void sendMessage(CharSequence chars) { 267 | if (chars.length() > 0) { 268 | mDeviceConnector.sendAsciiMessage(chars); 269 | mOutEditText.setText(""); 270 | } 271 | } 272 | 273 | @Override 274 | public void onActivityResult(int requestCode, int resultCode, Intent data) { 275 | Log.i(TAG, "onActivityResult " + resultCode); 276 | switch (requestCode) { 277 | case REQUEST_CONNECT_DEVICE: 278 | if (resultCode == Activity.RESULT_OK) { 279 | MessageHandler messageHandler = new MessageHandlerImpl(mHandler); 280 | mDeviceConnector = DeviceListActivity.createDeviceConnector(data, messageHandler, getAssets()); 281 | if (mDeviceConnector != null) { 282 | mDeviceConnector.connect(); 283 | } else { 284 | Toast.makeText(this, R.string.error_could_not_create_device, Toast.LENGTH_LONG).show(); 285 | } 286 | } 287 | break; 288 | case REQUEST_ENABLE_BT: 289 | // When the request to enable Bluetooth returns 290 | pendingRequestEnableBt = false; 291 | if (resultCode != Activity.RESULT_OK) { 292 | Log.i(TAG, "BT not enabled"); 293 | } 294 | break; 295 | case REQUEST_LAUNCH_EMAIL_APP: 296 | if (resultCode == Activity.RESULT_OK) { 297 | Toast.makeText(this, R.string.msg_email_sent, Toast.LENGTH_LONG).show(); 298 | } else { 299 | Toast.makeText(this, R.string.msg_email_not_sent, Toast.LENGTH_LONG).show(); 300 | } 301 | break; 302 | case MENU_SETTINGS: 303 | updateParamsFromSettings(); 304 | break; 305 | default: 306 | Log.d(TAG, "Unknown request code: " + requestCode); 307 | } 308 | } 309 | 310 | @Override 311 | public boolean onCreateOptionsMenu(Menu menu) { 312 | MenuInflater inflater = getMenuInflater(); 313 | inflater.inflate(R.menu.main, menu); 314 | 315 | if (ApplicationUtils.isLiteVersion(getApplication())) { 316 | menu.findItem(R.id.menu_buy).setVisible(true); 317 | } 318 | 319 | return true; 320 | } 321 | 322 | @Override 323 | public boolean onOptionsItemSelected(MenuItem item) { 324 | int itemId = item.getItemId(); 325 | if (itemId == R.id.menu_rate) { 326 | openURL(getString(R.string.url_rate)); 327 | } else if (itemId == R.id.menu_buy) { 328 | openURL(getString(R.string.url_full_app)); 329 | } else if (itemId == R.id.menu_settings) { 330 | startActivityForResult(SettingsActivity.class, MENU_SETTINGS); 331 | } else if (itemId == R.id.menu_email_recorded_data) { 332 | if (recording.length() > 0) { 333 | launchEmailApp(EmailUtils.prepareDeviceRecording(this, defaultEmail, deviceName, recording.toString())); 334 | } else if (recordingEnabled) { 335 | Toast.makeText(this, R.string.msg_nothing_recorded, Toast.LENGTH_LONG).show(); 336 | } else { 337 | Toast.makeText(this, R.string.msg_nothing_recorded_recording_disabled, Toast.LENGTH_LONG).show(); 338 | } 339 | } 340 | return false; 341 | } 342 | 343 | private void launchEmailApp(Intent intent) { 344 | try { 345 | startActivityForResult(Intent.createChooser(intent, getString(R.string.email_client_chooser)), REQUEST_LAUNCH_EMAIL_APP); 346 | } catch (ActivityNotFoundException ex) { 347 | Toast.makeText(this, getString(R.string.no_email_client), Toast.LENGTH_SHORT).show(); 348 | } 349 | } 350 | 351 | private void startActivityForResult(Class cls, int requestCode) { 352 | Intent intent = new Intent(getApplicationContext(), cls); 353 | startActivityForResult(intent, requestCode); 354 | } 355 | 356 | private void openURL(String url) { 357 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url))); 358 | } 359 | 360 | private void disconnectDevices() { 361 | mDeviceConnector.disconnect(); 362 | onBluetoothStateChanged(); 363 | } 364 | 365 | private void onBluetoothStateChanged() { 366 | if (connected) { 367 | mToolbarConnectButton.setVisibility(View.GONE); 368 | mToolbarDisconnectButton.setVisibility(View.VISIBLE); 369 | mSendTextContainer.setVisibility(View.VISIBLE); 370 | mWelcomeText.setVisibility(View.GONE); 371 | mConversationView.setVisibility(View.VISIBLE); 372 | } else { 373 | mToolbarConnectButton.setVisibility(View.VISIBLE); 374 | mToolbarDisconnectButton.setVisibility(View.GONE); 375 | mSendTextContainer.setVisibility(View.GONE); 376 | } 377 | paused = false; 378 | onPausedStateChanged(); 379 | } 380 | 381 | private void onPausedStateChanged() { 382 | if (connected) { 383 | if (paused) { 384 | mToolbarPlayButton.setVisibility(View.VISIBLE); 385 | mToolbarPauseButton.setVisibility(View.GONE); 386 | } else { 387 | mToolbarPlayButton.setVisibility(View.GONE); 388 | mToolbarPauseButton.setVisibility(View.VISIBLE); 389 | } 390 | } else { 391 | mToolbarPlayButton.setVisibility(View.GONE); 392 | mToolbarPauseButton.setVisibility(View.GONE); 393 | } 394 | } 395 | 396 | @Override 397 | protected void onSaveInstanceState(Bundle outState) { 398 | Log.d(TAG, "++onSaveInstanceState"); 399 | super.onSaveInstanceState(outState); 400 | outState.putBoolean(SAVED_PENDING_REQUEST_ENABLE_BT, pendingRequestEnableBt); 401 | } 402 | 403 | @Override 404 | public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String prefName) { 405 | Log.d(TAG, "++onSharedPreferenceChanged"); 406 | if (prefName.equals(getString(R.string.pref_record))) { 407 | updateParamsFromSettings(); 408 | } 409 | } 410 | 411 | private SharedPreferences getSharedPreferences() { 412 | return PreferenceManager.getDefaultSharedPreferences(this); 413 | } 414 | 415 | @Override 416 | protected void onResume() { 417 | getSharedPreferences().registerOnSharedPreferenceChangeListener(this); 418 | super.onResume(); 419 | } 420 | 421 | @Override 422 | protected void onPause() { 423 | getSharedPreferences().unregisterOnSharedPreferenceChangeListener(this); 424 | super.onPause(); 425 | } 426 | } 427 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/DeviceConnector.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | /** 4 | * Common interface to connect to a device 5 | */ 6 | public interface DeviceConnector { 7 | 8 | void connect(); 9 | 10 | void disconnect(); 11 | 12 | void sendAsciiMessage(CharSequence chars); 13 | } 14 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/DeviceListActivity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2009 The Android Open Source Project 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.bluetoothviewer; 18 | 19 | import android.app.Activity; 20 | import android.bluetooth.BluetoothAdapter; 21 | import android.bluetooth.BluetoothDevice; 22 | import android.content.BroadcastReceiver; 23 | import android.content.Context; 24 | import android.content.Intent; 25 | import android.content.IntentFilter; 26 | import android.content.res.AssetManager; 27 | import android.os.Bundle; 28 | import android.util.Log; 29 | import android.view.View; 30 | import android.view.View.OnClickListener; 31 | import android.view.Window; 32 | import android.widget.AdapterView; 33 | import android.widget.AdapterView.OnItemClickListener; 34 | import android.widget.ArrayAdapter; 35 | import android.widget.Button; 36 | import android.widget.ListView; 37 | 38 | import net.bluetoothviewer.library.R; 39 | import net.bluetoothviewer.util.AssetUtils; 40 | 41 | import java.util.HashSet; 42 | import java.util.Set; 43 | 44 | /** 45 | * This Activity appears as a dialog. It lists already paired devices, 46 | * and it can scan for devices nearby. When the user selects a device, 47 | * its MAC address is returned to the caller as the result of this activity. 48 | */ 49 | public class DeviceListActivity extends Activity { 50 | 51 | private static final String TAG = "DeviceListActivity"; 52 | private static final boolean D = true; 53 | 54 | protected static final String EXTRA_MOCK_DEVICES_ENABLED = "MOCK_DEVICES_ENABLED"; 55 | 56 | public enum ConnectorType { 57 | BLUETOOTH, 58 | MOCK 59 | } 60 | 61 | public enum MockType { 62 | TEXT, 63 | BINARY 64 | } 65 | 66 | public enum Message { 67 | DEVICE_CONNECTOR_TYPE, 68 | BLUETOOTH_ADDRESS, 69 | MOCK_TYPE, 70 | MOCK_FILENAME, 71 | } 72 | 73 | private abstract static class DeviceListEntry { 74 | @Override 75 | public String toString() { 76 | return String.format("%s%n%s", getFirstLine(), getSecondLine()); 77 | } 78 | 79 | abstract String getFirstLine(); 80 | 81 | abstract String getSecondLine(); 82 | } 83 | 84 | private static class BluetoothDeviceEntry extends DeviceListEntry { 85 | private final String name; 86 | private final String address; 87 | 88 | BluetoothDeviceEntry(String name, String address) { 89 | this.name = name; 90 | this.address = address; 91 | } 92 | 93 | @Override 94 | protected String getFirstLine() { 95 | return name; 96 | } 97 | 98 | @Override 99 | protected String getSecondLine() { 100 | return address; 101 | } 102 | } 103 | 104 | private static class MockDeviceEntry extends DeviceListEntry { 105 | private final MockType mockType; 106 | private final String filename; 107 | 108 | MockDeviceEntry(MockType mockType, String filename) { 109 | this.mockType = mockType; 110 | this.filename = filename; 111 | } 112 | 113 | @Override 114 | String getFirstLine() { 115 | if (mockType == MockType.BINARY) { 116 | return filename + " (bin)"; 117 | } 118 | return filename; 119 | } 120 | 121 | @Override 122 | String getSecondLine() { 123 | return ""; 124 | } 125 | } 126 | 127 | private final BluetoothAdapterWrapper mBtAdapter = BluetoothAdapterFactory.getBluetoothAdapterWrapper(); 128 | private ArrayAdapter mNewDevicesArrayAdapter; 129 | private final Set mNewDevicesSet = new HashSet(); 130 | 131 | private Button scanButton; 132 | 133 | @Override 134 | protected void onCreate(Bundle savedInstanceState) { 135 | super.onCreate(savedInstanceState); 136 | 137 | requestWindowFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 138 | setContentView(R.layout.device_list); 139 | 140 | // Set default result to CANCELED, in case the user backs out 141 | setResult(Activity.RESULT_CANCELED); 142 | 143 | boolean foundMockDevices = false; 144 | if (getIntent().getBooleanExtra(EXTRA_MOCK_DEVICES_ENABLED, false)) { 145 | ArrayAdapter mockDevicesAdapter = new ArrayAdapter(this, R.layout.device_name); 146 | ListView mockListView = (ListView) findViewById(R.id.mock_devices); 147 | mockListView.setAdapter(mockDevicesAdapter); 148 | mockListView.setOnItemClickListener(new MockDeviceClickListener(mockDevicesAdapter)); 149 | 150 | for (String filename : AssetUtils.listFiles(getResources().getAssets(), MockLineByLineConnector.SAMPLES_SUBDIR)) { 151 | mockDevicesAdapter.add(new MockDeviceEntry(MockType.TEXT, filename)); 152 | foundMockDevices = true; 153 | } 154 | 155 | for (String filename : AssetUtils.listFiles(getResources().getAssets(), MockBinaryConnector.SAMPLES_SUBDIR)) { 156 | mockDevicesAdapter.add(new MockDeviceEntry(MockType.BINARY, filename)); 157 | foundMockDevices = true; 158 | } 159 | 160 | if (foundMockDevices) { 161 | findViewById(R.id.title_mock_devices).setVisibility(View.VISIBLE); 162 | } 163 | } 164 | 165 | boolean foundBluetoothDevices = false; 166 | Set pairedDevices = mBtAdapter.getBondedDevices(); 167 | if (pairedDevices != null && !pairedDevices.isEmpty()) { 168 | ArrayAdapter pairedDevicesAdapter = new ArrayAdapter(this, R.layout.device_name); 169 | ListView pairedListView = (ListView) findViewById(R.id.paired_devices); 170 | pairedListView.setAdapter(pairedDevicesAdapter); 171 | pairedListView.setOnItemClickListener(new BluetoothDeviceClickListener(pairedDevicesAdapter)); 172 | 173 | for (BluetoothDevice device : pairedDevices) { 174 | pairedDevicesAdapter.add(new BluetoothDeviceEntry(device.getName(), device.getAddress())); 175 | } 176 | 177 | findViewById(R.id.title_paired_devices).setVisibility(View.VISIBLE); 178 | foundBluetoothDevices = true; 179 | } 180 | 181 | if (!foundBluetoothDevices && !foundMockDevices) { 182 | findViewById(R.id.label_none_found).setVisibility(View.VISIBLE); 183 | } 184 | 185 | mNewDevicesArrayAdapter = new ArrayAdapter(this, R.layout.device_name); 186 | ListView newDevicesListView = (ListView) findViewById(R.id.new_devices); 187 | newDevicesListView.setAdapter(mNewDevicesArrayAdapter); 188 | newDevicesListView.setOnItemClickListener(new BluetoothDeviceClickListener(mNewDevicesArrayAdapter)); 189 | 190 | IntentFilter bluetoothDeviceFoundFilter = new IntentFilter(BluetoothDevice.ACTION_FOUND); 191 | registerReceiver(mReceiver, bluetoothDeviceFoundFilter); 192 | 193 | IntentFilter discoveryFinishedFilter = new IntentFilter(BluetoothAdapter.ACTION_DISCOVERY_FINISHED); 194 | registerReceiver(mReceiver, discoveryFinishedFilter); 195 | 196 | scanButton = (Button) findViewById(R.id.button_scan); 197 | scanButton.setOnClickListener(new OnClickListener() { 198 | public void onClick(View v) { 199 | doDiscovery(); 200 | v.setVisibility(View.GONE); 201 | } 202 | }); 203 | } 204 | 205 | @Override 206 | protected void onDestroy() { 207 | super.onDestroy(); 208 | 209 | mBtAdapter.cancelDiscovery(); 210 | 211 | this.unregisterReceiver(mReceiver); 212 | } 213 | 214 | /** 215 | * Start device discovery with the BluetoothAdapter 216 | */ 217 | private void doDiscovery() { 218 | if (D) Log.d(TAG, "doDiscovery()"); 219 | 220 | mNewDevicesArrayAdapter.clear(); 221 | mNewDevicesSet.clear(); 222 | 223 | setProgressBarIndeterminateVisibility(true); 224 | 225 | findViewById(R.id.label_none_found).setVisibility(View.GONE); 226 | findViewById(R.id.title_new_devices).setVisibility(View.VISIBLE); 227 | findViewById(R.id.label_scanning).setVisibility(View.VISIBLE); 228 | 229 | if (mBtAdapter.isDiscovering()) { 230 | mBtAdapter.cancelDiscovery(); 231 | } 232 | 233 | mBtAdapter.startDiscovery(); 234 | } 235 | 236 | private abstract class AbstractItemClickListener implements OnItemClickListener { 237 | final ArrayAdapter adapter; 238 | 239 | private AbstractItemClickListener(ArrayAdapter adapter) { 240 | this.adapter = adapter; 241 | } 242 | 243 | @Override 244 | public void onItemClick(AdapterView parent, View view, int position, long id) { 245 | T item = adapter.getItem(position); 246 | if (item != null) { 247 | Intent intent = new Intent(); 248 | putExtras(intent, item); 249 | setResult(Activity.RESULT_OK, intent); 250 | } 251 | finish(); 252 | } 253 | 254 | abstract void putExtras(Intent intent, T item); 255 | } 256 | 257 | private class BluetoothDeviceClickListener extends AbstractItemClickListener { 258 | private BluetoothDeviceClickListener(ArrayAdapter adapter) { 259 | super(adapter); 260 | } 261 | 262 | @Override 263 | void putExtras(Intent intent, BluetoothDeviceEntry item) { 264 | intent.putExtra(Message.DEVICE_CONNECTOR_TYPE.toString(), ConnectorType.BLUETOOTH); 265 | intent.putExtra(Message.BLUETOOTH_ADDRESS.toString(), item.address); 266 | } 267 | } 268 | 269 | private class MockDeviceClickListener extends AbstractItemClickListener { 270 | private MockDeviceClickListener(ArrayAdapter adapter) { 271 | super(adapter); 272 | } 273 | 274 | @Override 275 | void putExtras(Intent intent, MockDeviceEntry item) { 276 | intent.putExtra(Message.DEVICE_CONNECTOR_TYPE.toString(), ConnectorType.MOCK); 277 | intent.putExtra(Message.MOCK_TYPE.toString(), item.mockType); 278 | intent.putExtra(Message.MOCK_FILENAME.toString(), item.filename); 279 | } 280 | } 281 | 282 | private final BroadcastReceiver mReceiver = new BroadcastReceiver() { 283 | @Override 284 | public void onReceive(Context context, Intent intent) { 285 | String action = intent.getAction(); 286 | 287 | if (BluetoothDevice.ACTION_FOUND.equals(action)) { 288 | String parcelableExtraName = BluetoothDevice.EXTRA_DEVICE; 289 | BluetoothDevice device = intent.getParcelableExtra(parcelableExtraName); 290 | if (device != null) { 291 | String address = device.getAddress(); 292 | if (!mNewDevicesSet.contains(address)) { 293 | mNewDevicesSet.add(address); 294 | mNewDevicesArrayAdapter.add(new BluetoothDeviceEntry(device.getName(), address)); 295 | } 296 | } else { 297 | Log.e(TAG, "Could not get parcelable extra: " + parcelableExtraName); 298 | } 299 | } else if (BluetoothAdapter.ACTION_DISCOVERY_FINISHED.equals(action)) { 300 | setProgressBarIndeterminateVisibility(false); 301 | findViewById(R.id.label_scanning).setVisibility(View.GONE); 302 | if (mNewDevicesSet.isEmpty()) { 303 | findViewById(R.id.label_none_found).setVisibility(View.VISIBLE); 304 | } else { 305 | findViewById(R.id.label_none_found).setVisibility(View.GONE); 306 | } 307 | scanButton.setVisibility(View.VISIBLE); 308 | } 309 | } 310 | }; 311 | 312 | public static DeviceConnector createDeviceConnector(Intent data, MessageHandler messageHandler, AssetManager assetManager) { 313 | ConnectorType connectorType = 314 | (ConnectorType) data.getSerializableExtra(Message.DEVICE_CONNECTOR_TYPE.toString()); 315 | 316 | switch (connectorType) { 317 | case MOCK: 318 | String filename = data.getStringExtra(Message.MOCK_FILENAME.toString()); 319 | MockType mockType = (MockType) data.getSerializableExtra(Message.MOCK_TYPE.toString()); 320 | if (mockType == MockType.TEXT) { 321 | return new MockLineByLineConnector(messageHandler, assetManager, filename); 322 | } else if (mockType == MockType.BINARY) { 323 | return new MockBinaryConnector(messageHandler, assetManager, filename); 324 | } 325 | return null; 326 | case BLUETOOTH: 327 | String addressMsgId = Message.BLUETOOTH_ADDRESS.toString(); 328 | String address = data.getStringExtra(addressMsgId); 329 | return new BluetoothDeviceConnector(messageHandler, address); 330 | default: 331 | return null; 332 | } 333 | } 334 | } 335 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/MessageHandler.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | public interface MessageHandler { 4 | 5 | class Constants { 6 | private Constants() { 7 | // forbidden constructor 8 | } 9 | 10 | static final int MSG_NOT_CONNECTED = 10; 11 | static final int MSG_CONNECTING = 11; 12 | static final int MSG_CONNECTED = 12; 13 | static final int MSG_CONNECTION_FAILED = 13; 14 | static final int MSG_CONNECTION_LOST = 14; 15 | static final int MSG_LINE_READ = 21; 16 | static final int MSG_BYTES_WRITTEN = 22; 17 | } 18 | 19 | void sendLineRead(String line); 20 | 21 | void sendBytesWritten(byte[] bytes); 22 | 23 | void sendConnectingTo(String deviceName); 24 | 25 | void sendConnectedTo(String deviceName); 26 | 27 | void sendNotConnected(); 28 | 29 | void sendConnectionFailed(); 30 | 31 | void sendConnectionLost(); 32 | 33 | } 34 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/MessageHandlerImpl.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | import android.os.Handler; 4 | 5 | import static net.bluetoothviewer.MessageHandler.Constants.MSG_BYTES_WRITTEN; 6 | import static net.bluetoothviewer.MessageHandler.Constants.MSG_CONNECTED; 7 | import static net.bluetoothviewer.MessageHandler.Constants.MSG_CONNECTING; 8 | import static net.bluetoothviewer.MessageHandler.Constants.MSG_CONNECTION_FAILED; 9 | import static net.bluetoothviewer.MessageHandler.Constants.MSG_CONNECTION_LOST; 10 | import static net.bluetoothviewer.MessageHandler.Constants.MSG_LINE_READ; 11 | import static net.bluetoothviewer.MessageHandler.Constants.MSG_NOT_CONNECTED; 12 | 13 | public class MessageHandlerImpl implements MessageHandler { 14 | private final Handler handler; 15 | 16 | public MessageHandlerImpl(Handler handler) { 17 | this.handler = handler; 18 | } 19 | 20 | @Override 21 | public void sendLineRead(String line) { 22 | handler.obtainMessage(MSG_LINE_READ, -1, -1, line).sendToTarget(); 23 | } 24 | 25 | @Override 26 | public void sendBytesWritten(byte[] bytes) { 27 | handler.obtainMessage(MSG_BYTES_WRITTEN, -1, -1, bytes).sendToTarget(); 28 | } 29 | 30 | @Override 31 | public void sendConnectingTo(String deviceName) { 32 | sendMessage(MSG_CONNECTING, deviceName); 33 | } 34 | 35 | @Override 36 | public void sendConnectedTo(String deviceName) { 37 | sendMessage(MSG_CONNECTED, deviceName); 38 | } 39 | 40 | @Override 41 | public void sendNotConnected() { 42 | sendMessage(MSG_NOT_CONNECTED); 43 | } 44 | 45 | @Override 46 | public void sendConnectionFailed() { 47 | sendMessage(MSG_CONNECTION_FAILED); 48 | } 49 | 50 | @Override 51 | public void sendConnectionLost() { 52 | sendMessage(MSG_CONNECTION_LOST); 53 | } 54 | 55 | private void sendMessage(int messageId, String deviceName) { 56 | handler.obtainMessage(messageId, -1, -1, deviceName).sendToTarget(); 57 | } 58 | 59 | private void sendMessage(int messageId) { 60 | handler.obtainMessage(messageId).sendToTarget(); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/MockBinaryConnector.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | import android.content.res.AssetManager; 4 | 5 | import net.bluetoothviewer.util.AssetUtils; 6 | import net.bluetoothviewer.util.TextUtils; 7 | 8 | import java.io.File; 9 | import java.util.ArrayList; 10 | import java.util.List; 11 | 12 | public class MockBinaryConnector implements DeviceConnector { 13 | 14 | private static final int CHUNK_SIZE = 16; 15 | 16 | public static final String SAMPLES_SUBDIR = "samples/binary"; 17 | 18 | private static final int SLEEP_MILLIS = 1000; 19 | 20 | private final MessageHandler messageHandler; 21 | private final AssetManager assets; 22 | private final String filename; 23 | 24 | private boolean running = false; 25 | 26 | public MockBinaryConnector(MessageHandler messageHandler, AssetManager assets, String filename) { 27 | this.messageHandler = messageHandler; 28 | this.assets = assets; 29 | this.filename = filename; 30 | } 31 | 32 | @Override 33 | public synchronized void connect() { 34 | if (running) { 35 | return; 36 | } 37 | running = true; 38 | new Thread(new MockDeviceRunnable()).start(); 39 | } 40 | 41 | @Override 42 | public synchronized void disconnect() { 43 | running = false; 44 | } 45 | 46 | @Override 47 | public void sendAsciiMessage(CharSequence chars) { 48 | // do nothing 49 | } 50 | 51 | class MockDeviceRunnable implements Runnable { 52 | @Override 53 | public void run() { 54 | messageHandler.sendConnectingTo(filename); 55 | 56 | String mockFilePath = new File(SAMPLES_SUBDIR, filename).toString(); 57 | List chunks = new ArrayList(); 58 | for (byte[] chunk : AssetUtils.readChunksFromStream(assets, mockFilePath, CHUNK_SIZE)) { 59 | chunks.add(TextUtils.bytesToHexDump(chunk)); 60 | } 61 | 62 | if (!chunks.isEmpty()) { 63 | loopChunksUntilStopped(chunks); 64 | } 65 | 66 | messageHandler.sendConnectionLost(); 67 | } 68 | 69 | private void loopChunksUntilStopped(List chunks) { 70 | messageHandler.sendConnectedTo(filename); 71 | 72 | while (running) { 73 | for (String chunk : chunks) { 74 | if (!running) { 75 | break; 76 | } 77 | messageHandler.sendLineRead(chunk); 78 | try { 79 | Thread.sleep(SLEEP_MILLIS); 80 | } catch (InterruptedException e) { 81 | // ok to be interrupted 82 | Thread.currentThread().interrupt(); 83 | } 84 | } 85 | } 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/MockLineByLineConnector.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | import android.content.res.AssetManager; 4 | 5 | import net.bluetoothviewer.util.AssetUtils; 6 | 7 | import java.io.File; 8 | import java.util.List; 9 | 10 | public class MockLineByLineConnector implements DeviceConnector { 11 | 12 | public static final String SAMPLES_SUBDIR = "samples/line-by-line"; 13 | 14 | private static final int SLEEP_MILLIS = 1000; 15 | 16 | private final MessageHandler messageHandler; 17 | private final AssetManager assets; 18 | private final String filename; 19 | 20 | private boolean running = false; 21 | 22 | public MockLineByLineConnector(MessageHandler messageHandler, AssetManager assets, String filename) { 23 | this.messageHandler = messageHandler; 24 | this.assets = assets; 25 | this.filename = filename; 26 | } 27 | 28 | @Override 29 | public synchronized void connect() { 30 | if (running) { 31 | return; 32 | } 33 | running = true; 34 | new Thread(new MockDeviceRunnable()).start(); 35 | } 36 | 37 | @Override 38 | public synchronized void disconnect() { 39 | running = false; 40 | } 41 | 42 | @Override 43 | public void sendAsciiMessage(CharSequence chars) { 44 | // do nothing 45 | } 46 | 47 | class MockDeviceRunnable implements Runnable { 48 | @Override 49 | public void run() { 50 | messageHandler.sendConnectingTo(filename); 51 | 52 | String mockFilePath = new File(SAMPLES_SUBDIR, filename).toString(); 53 | List lines = AssetUtils.readLinesFromStream(assets, mockFilePath); 54 | 55 | if (!lines.isEmpty()) { 56 | loopLinesUntilStopped(lines); 57 | } 58 | 59 | messageHandler.sendConnectionLost(); 60 | } 61 | 62 | private void loopLinesUntilStopped(List lines) { 63 | messageHandler.sendConnectedTo(filename); 64 | 65 | while (running) { 66 | for (String line : lines) { 67 | if (!running) { 68 | break; 69 | } 70 | messageHandler.sendLineRead(line); 71 | try { 72 | Thread.sleep(SLEEP_MILLIS); 73 | } catch (InterruptedException e) { 74 | // ok to be interrupted 75 | Thread.currentThread().interrupt(); 76 | } 77 | } 78 | } 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/NullBluetoothWrapper.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | 4 | import android.bluetooth.BluetoothDevice; 5 | 6 | import java.util.Collections; 7 | import java.util.Set; 8 | 9 | public class NullBluetoothWrapper implements BluetoothAdapterWrapper { 10 | @Override 11 | public Set getBondedDevices() { 12 | return Collections.emptySet(); 13 | } 14 | 15 | @Override 16 | public void cancelDiscovery() { 17 | // nothing to cancel 18 | } 19 | 20 | @Override 21 | public boolean isDiscovering() { 22 | return false; 23 | } 24 | 25 | @Override 26 | public void startDiscovery() { 27 | // nothing to discover 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/NullDeviceConnector.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | public class NullDeviceConnector implements DeviceConnector { 4 | @Override 5 | public void connect() { 6 | // do nothing 7 | } 8 | 9 | @Override 10 | public void disconnect() { 11 | // do nothing 12 | } 13 | 14 | @Override 15 | public void sendAsciiMessage(CharSequence chars) { 16 | // do nothing 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/SettingsActivity.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer; 2 | 3 | import android.os.Bundle; 4 | import android.preference.PreferenceActivity; 5 | import android.preference.PreferenceFragment; 6 | 7 | import net.bluetoothviewer.library.R; 8 | 9 | 10 | public class SettingsActivity extends PreferenceActivity { 11 | 12 | @Override 13 | public void onCreate(Bundle savedInstanceState) { 14 | super.onCreate(savedInstanceState); 15 | getFragmentManager().beginTransaction() 16 | .replace(android.R.id.content, new SettingsFragment()) 17 | .commit(); 18 | } 19 | 20 | public static class SettingsFragment extends PreferenceFragment { 21 | @Override 22 | public void onCreate(Bundle savedInstanceState) { 23 | super.onCreate(savedInstanceState); 24 | addPreferencesFromResource(R.xml.settings); 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/application/BluetoothViewerApplication.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.application; 2 | 3 | import android.app.Application; 4 | 5 | public abstract class BluetoothViewerApplication extends Application { 6 | 7 | public abstract boolean isLiteVersion(); 8 | } 9 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/application/BluetoothViewerFullApplication.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.application; 2 | 3 | public class BluetoothViewerFullApplication extends BluetoothViewerApplication { 4 | 5 | @Override 6 | public boolean isLiteVersion() { 7 | return false; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/application/BluetoothViewerLiteApplication.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.application; 2 | 3 | public class BluetoothViewerLiteApplication extends BluetoothViewerApplication { 4 | 5 | @Override 6 | public boolean isLiteVersion() { 7 | return true; 8 | } 9 | 10 | } 11 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/util/ApplicationUtils.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.util; 2 | 3 | import android.app.Application; 4 | 5 | import net.bluetoothviewer.application.BluetoothViewerApplication; 6 | 7 | public class ApplicationUtils { 8 | private ApplicationUtils() { 9 | // utility class, forbidden constructor 10 | } 11 | 12 | public static boolean isLiteVersion(Application application) { 13 | return ((BluetoothViewerApplication) application).isLiteVersion(); 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/util/AssetUtils.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.util; 2 | 3 | import android.content.res.AssetManager; 4 | import android.util.Log; 5 | 6 | import java.io.IOException; 7 | import java.io.InputStream; 8 | import java.util.Collections; 9 | import java.util.List; 10 | 11 | public class AssetUtils { 12 | 13 | private static final String TAG = AssetUtils.class.getSimpleName(); 14 | 15 | private AssetUtils() { 16 | // utility class, forbidden constructor 17 | } 18 | 19 | public static List readLinesFromStream(AssetManager assets, String filename) { 20 | InputStream inputStream; 21 | try { 22 | inputStream = assets.open(filename); 23 | } catch (IOException e) { 24 | Log.e(TAG, "Could not open asset file: " + filename); 25 | return Collections.emptyList(); 26 | } 27 | 28 | List lines; 29 | try { 30 | lines = IOUtils.readLinesFromStream(inputStream); 31 | } catch (IOException e) { 32 | Log.e(TAG, "Could not read lines from asset file: " + filename); 33 | return Collections.emptyList(); 34 | } 35 | 36 | try { 37 | inputStream.close(); 38 | } catch (IOException e) { 39 | Log.w(TAG, "Could not close asset file: " + filename); 40 | } 41 | return lines; 42 | } 43 | 44 | public static List readChunksFromStream(AssetManager assets, String path, int chunkSize) { 45 | InputStream inputStream; 46 | try { 47 | inputStream = assets.open(path); 48 | } catch (IOException e) { 49 | Log.e(TAG, "Could not open asset file: " + path); 50 | return Collections.emptyList(); 51 | } 52 | 53 | List chunks; 54 | try { 55 | chunks = IOUtils.readChunksFromStream(inputStream, chunkSize); 56 | } catch (IOException e) { 57 | Log.e(TAG, "Could not read chunks from asset file: " + path); 58 | return Collections.emptyList(); 59 | } 60 | 61 | try { 62 | inputStream.close(); 63 | } catch (IOException e) { 64 | Log.w(TAG, "Could not close asset file: " + path); 65 | } 66 | return chunks; 67 | } 68 | 69 | public static String[] listFiles(AssetManager assets, String subdir) { 70 | try { 71 | return assets.list(subdir); 72 | } catch (IOException e) { 73 | return new String[0]; 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/util/EmailUtils.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.util; 2 | 3 | import android.content.Context; 4 | import android.content.Intent; 5 | import android.content.pm.PackageInfo; 6 | import android.content.pm.PackageManager; 7 | import android.net.Uri; 8 | import android.util.Log; 9 | 10 | import net.bluetoothviewer.library.R; 11 | 12 | import java.io.File; 13 | import java.io.FileOutputStream; 14 | import java.io.IOException; 15 | import java.text.SimpleDateFormat; 16 | import java.util.Date; 17 | import java.util.Locale; 18 | 19 | 20 | public class EmailUtils { 21 | 22 | private static final String TAG = EmailUtils.class.getSimpleName(); 23 | 24 | private static final String MESSAGE_TYPE = "message/rfc822"; 25 | 26 | private static final String HORIZONTAL_RULE = "\n\n---\n\n"; 27 | 28 | private EmailUtils() { 29 | // prevent creating utility class 30 | } 31 | 32 | public static Intent prepareDeviceRecording(Context context, String defaultEmail, String deviceName, String recordedContent) { 33 | String subject = String.format(context.getString(R.string.fmt_subject_recorded_data), deviceName); 34 | String messageHeader = String.format(context.getString(R.string.fmt_recorded_from), deviceName); 35 | 36 | StringBuilder builder = new StringBuilder(); 37 | builder.append(messageHeader).append(HORIZONTAL_RULE); 38 | 39 | Intent intent = new Intent(Intent.ACTION_SEND); 40 | intent.setType(MESSAGE_TYPE); 41 | intent.putExtra(Intent.EXTRA_EMAIL, new String[]{defaultEmail}); 42 | intent.putExtra(Intent.EXTRA_SUBJECT, subject); 43 | if (!addAttachmentToIntent(context, deviceName, recordedContent, intent)) { 44 | builder.append(recordedContent).append(HORIZONTAL_RULE); 45 | } 46 | builder.append(getPackageInfoString(context)); 47 | 48 | intent.putExtra(Intent.EXTRA_TEXT, builder.toString()); 49 | return intent; 50 | } 51 | 52 | private static String getPackageInfoString(Context context) { 53 | PackageInfo info = getPackageInfo(context); 54 | return String.format("[App: %s Version: %d/%s]", 55 | context.getPackageName(), info.versionCode, info.versionName); 56 | } 57 | 58 | private static PackageInfo getPackageInfo(Context context) { 59 | String packageName = context.getPackageName(); 60 | PackageManager manager = context.getPackageManager(); 61 | if (manager != null) { 62 | try { 63 | return manager.getPackageInfo(packageName, 0); 64 | } catch (PackageManager.NameNotFoundException e) { 65 | Log.e(TAG, "Could not get package info", e); 66 | } 67 | } 68 | return new PackageInfo(); 69 | } 70 | 71 | private static boolean addAttachmentToIntent(Context context, String deviceName, String recordedContent, Intent intent) { 72 | SimpleDateFormat dateFormat = new SimpleDateFormat("_yyyyMMdd_HHmm", Locale.UK); 73 | String filename = deviceName + dateFormat.format(new Date()) + ".dat"; 74 | File basedir = context.getExternalCacheDir(); 75 | File attachment = new File(basedir, filename); 76 | try { 77 | FileOutputStream ostream = new FileOutputStream(attachment); 78 | ostream.write(recordedContent.getBytes()); 79 | ostream.close(); 80 | intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(attachment)); 81 | return true; 82 | } catch (IOException e) { 83 | Log.e(TAG, "could not create temp file for attachment :(", e); 84 | return false; 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/util/IOUtils.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.util; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.io.InputStreamReader; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | public class IOUtils { 12 | 13 | private IOUtils() { 14 | // utility class, forbidden constructor 15 | } 16 | 17 | public static List readLinesFromStream(InputStream inputStream) throws IOException { 18 | BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream)); 19 | String line; 20 | List lines = new ArrayList(); 21 | while ((line = reader.readLine()) != null) { 22 | lines.add(line); 23 | } 24 | return lines; 25 | } 26 | 27 | public static List readChunksFromStream(InputStream inputStream, int chunkSize) throws IOException { 28 | int bytesRead; 29 | byte[] data = new byte[chunkSize]; 30 | 31 | List chunks = new ArrayList(); 32 | while ((bytesRead = inputStream.read(data, 0, data.length)) != -1) { 33 | chunks.add(Arrays.copyOf(data, bytesRead)); 34 | } 35 | return chunks; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/java/net/bluetoothviewer/util/TextUtils.java: -------------------------------------------------------------------------------- 1 | package net.bluetoothviewer.util; 2 | 3 | public class TextUtils { 4 | private TextUtils() { 5 | // utility class, forbidden constructor 6 | } 7 | 8 | public static String bytesToHexDump(byte[] chunk) { 9 | if (chunk.length == 0) { 10 | return ""; 11 | } 12 | 13 | StringBuilder sb = new StringBuilder(chunk.length * 3); 14 | for (byte b : chunk) { 15 | String hexString = Integer.toHexString(b & 255); 16 | if (hexString.length() == 1) { 17 | sb.append('0'); 18 | } 19 | sb.append(hexString).append(' '); 20 | } 21 | return sb.substring(0, sb.length() - 1); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-hdpi/launcher_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-hdpi/launcher_main.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_connect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_disconnect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_pause.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-hdpi/toolbar_btn_play.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-land/background_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-land/background_main.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-ldpi/launcher_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-ldpi/launcher_main.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_connect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_disconnect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_pause.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-ldpi/toolbar_btn_play.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-mdpi/launcher_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-mdpi/launcher_main.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_connect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_disconnect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_pause.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-mdpi/toolbar_btn_play.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-xhdpi/launcher_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-xhdpi/launcher_main.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_connect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_connect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_disconnect.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_disconnect.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_pause.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_pause.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_play.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable-xhdpi/toolbar_btn_play.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable/background_main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janosgyerik/bluetoothviewer/16cca81a795836ab6193b77992f26ca83356730e/bluetoothviewer/src/main/res/drawable/background_main.png -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/drawable/toolbarbg.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /bluetoothviewer/src/main/res/layout/bluetoothviewer.xml: -------------------------------------------------------------------------------- 1 | 2 | 15 | 16 | 22 | 23 | 32 | 33 | 41 | 42 | 51 | 52 | 58 | 59 | 66 | 67 |