├── .gitignore ├── LICENSE ├── README.md ├── Screenshot.png ├── btcar.svg ├── build.gradle ├── circle.yml ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── pom.xml ├── proguard-rules.pro ├── settings.gradle └── src └── main ├── AndroidManifest.xml ├── ic_launcher-web.png ├── java └── com │ └── github │ └── pires │ └── obd │ └── reader │ ├── activity │ ├── ConfigActivity.java │ ├── ConfirmDialog.java │ ├── MainActivity.java │ ├── TripListActivity.java │ └── TroubleCodesActivity.java │ ├── config │ └── ObdConfig.java │ ├── io │ ├── AbstractGatewayService.java │ ├── BluetoothManager.java │ ├── LogCSVWriter.java │ ├── MockObdGatewayService.java │ ├── ObdCommandJob.java │ ├── ObdGatewayService.java │ └── ObdProgressListener.java │ ├── net │ ├── ObdReading.java │ └── ObdService.java │ └── trips │ ├── TripListAdapter.java │ ├── TripLog.java │ ├── TripLogOpenHelper.java │ └── TripRecord.java └── res ├── drawable-hdpi ├── ic_btcar.png └── ic_launcher.png ├── drawable-mdpi ├── ic_btcar.png └── ic_launcher.png ├── drawable-xhdpi ├── ic_btcar.png └── ic_launcher.png ├── drawable-xxhdpi ├── ic_btcar.png └── ic_launcher.png ├── layout ├── activity_trips_list.xml ├── main.xml ├── row_trip_list.xml └── trouble_codes.xml ├── menu ├── context_trip_list.xml ├── main.xml ├── menu_trips_list.xml └── trouble_codes.xml ├── values-de └── strings.xml ├── values-es └── strings.xml ├── values-pt └── strings.xml ├── values-sw600dp └── dimens.xml ├── values-sw720dp-land └── dimens.xml ├── values-v11 └── styles.xml ├── values-v14 └── styles.xml ├── values-v21 └── styles.xml ├── values-w820dp └── dimens.xml ├── values-zh └── strings.xml ├── values ├── arrays.xml ├── color.xml ├── dimens.xml ├── strings.xml └── styles.xml └── xml └── preferences.xml /.gitignore: -------------------------------------------------------------------------------- 1 | # built application files 2 | *.apk 3 | *.ap_ 4 | 5 | # files for the dex VM 6 | *.dex 7 | 8 | # Java class files 9 | *.class 10 | 11 | # generated files 12 | bin/ 13 | build/ 14 | gen/ 15 | 16 | # Local configuration file (sdk path, etc) 17 | local.properties 18 | 19 | # Eclipse project files 20 | .classpath 21 | .project 22 | 23 | # Idea project files 24 | *.iml 25 | .idea 26 | 27 | .gradle 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, and 10 | distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 13 | owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all other entities 16 | that control, are controlled by, or are under common control with that entity. 17 | For the purposes of this definition, "control" means (i) the power, direct or 18 | indirect, to cause the direction or management of such entity, whether by 19 | contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the 20 | outstanding shares, or (iii) beneficial ownership of such entity. 21 | 22 | "You" (or "Your") shall mean an individual or Legal Entity exercising 23 | permissions granted by this License. 24 | 25 | "Source" form shall mean the preferred form for making modifications, including 26 | but not limited to software source code, documentation source, and configuration 27 | files. 28 | 29 | "Object" form shall mean any form resulting from mechanical transformation or 30 | translation of a Source form, including but not limited to compiled object code, 31 | generated documentation, and conversions to other media types. 32 | 33 | "Work" shall mean the work of authorship, whether in Source or Object form, made 34 | available under the License, as indicated by a copyright notice that is included 35 | in or attached to the work (an example is provided in the Appendix below). 36 | 37 | "Derivative Works" shall mean any work, whether in Source or Object form, that 38 | is based on (or derived from) the Work and for which the editorial revisions, 39 | annotations, elaborations, or other modifications represent, as a whole, an 40 | original work of authorship. For the purposes of this License, Derivative Works 41 | shall not include works that remain separable from, or merely link (or bind by 42 | name) to the interfaces of, the Work and Derivative Works thereof. 43 | 44 | "Contribution" shall mean any work of authorship, including the original version 45 | of the Work and any modifications or additions to that Work or Derivative Works 46 | thereof, that is intentionally submitted to Licensor for inclusion in the Work 47 | by the copyright owner or by an individual or Legal Entity authorized to submit 48 | on behalf of the copyright owner. For the purposes of this definition, 49 | "submitted" means any form of electronic, verbal, or written communication sent 50 | to the Licensor or its representatives, including but not limited to 51 | communication on electronic mailing lists, source code control systems, and 52 | issue tracking systems that are managed by, or on behalf of, the Licensor for 53 | the purpose of discussing and improving the Work, but excluding communication 54 | that is conspicuously marked or otherwise designated in writing by the copyright 55 | owner as "Not a Contribution." 56 | 57 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 58 | of whom a Contribution has been received by Licensor and subsequently 59 | incorporated within the Work. 60 | 61 | 2. Grant of Copyright License. 62 | 63 | Subject to the terms and conditions of this License, each Contributor hereby 64 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 65 | irrevocable copyright license to reproduce, prepare Derivative Works of, 66 | publicly display, publicly perform, sublicense, and distribute the Work and such 67 | Derivative Works in Source or Object form. 68 | 69 | 3. Grant of Patent License. 70 | 71 | Subject to the terms and conditions of this License, each Contributor hereby 72 | grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, 73 | irrevocable (except as stated in this section) patent license to make, have 74 | made, use, offer to sell, sell, import, and otherwise transfer the Work, where 75 | such license applies only to those patent claims licensable by such Contributor 76 | that are necessarily infringed by their Contribution(s) alone or by combination 77 | of their Contribution(s) with the Work to which such Contribution(s) was 78 | submitted. If You institute patent litigation against any entity (including a 79 | cross-claim or counterclaim in a lawsuit) alleging that the Work or a 80 | Contribution incorporated within the Work constitutes direct or contributory 81 | patent infringement, then any patent licenses granted to You under this License 82 | for that Work shall terminate as of the date such litigation is filed. 83 | 84 | 4. Redistribution. 85 | 86 | You may reproduce and distribute copies of the Work or Derivative Works thereof 87 | in any medium, with or without modifications, and in Source or Object form, 88 | provided that You meet the following conditions: 89 | 90 | You must give any other recipients of the Work or Derivative Works a copy of 91 | this License; and 92 | You must cause any modified files to carry prominent notices stating that You 93 | changed the files; and 94 | You must retain, in the Source form of any Derivative Works that You distribute, 95 | all copyright, patent, trademark, and attribution notices from the Source form 96 | of the Work, excluding those notices that do not pertain to any part of the 97 | Derivative Works; and 98 | If the Work includes a "NOTICE" text file as part of its distribution, then any 99 | Derivative Works that You distribute must include a readable copy of the 100 | attribution notices contained within such NOTICE file, excluding those notices 101 | that do not pertain to any part of the Derivative Works, in at least one of the 102 | following places: within a NOTICE text file distributed as part of the 103 | Derivative Works; within the Source form or documentation, if provided along 104 | with the Derivative Works; or, within a display generated by the Derivative 105 | Works, if and wherever such third-party notices normally appear. The contents of 106 | the NOTICE file are for informational purposes only and do not modify the 107 | License. You may add Your own attribution notices within Derivative Works that 108 | You distribute, alongside or as an addendum to the NOTICE text from the Work, 109 | provided that such additional attribution notices cannot be construed as 110 | modifying the License. 111 | You may add Your own copyright statement to Your modifications and may provide 112 | additional or different license terms and conditions for use, reproduction, or 113 | distribution of Your modifications, or for any such Derivative Works as a whole, 114 | provided Your use, reproduction, and distribution of the Work otherwise complies 115 | with the conditions stated in this License. 116 | 117 | 5. Submission of Contributions. 118 | 119 | Unless You explicitly state otherwise, any Contribution intentionally submitted 120 | for inclusion in the Work by You to the Licensor shall be under the terms and 121 | conditions of this License, without any additional terms or conditions. 122 | Notwithstanding the above, nothing herein shall supersede or modify the terms of 123 | any separate license agreement you may have executed with Licensor regarding 124 | such Contributions. 125 | 126 | 6. Trademarks. 127 | 128 | This License does not grant permission to use the trade names, trademarks, 129 | service marks, or product names of the Licensor, except as required for 130 | reasonable and customary use in describing the origin of the Work and 131 | reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. 134 | 135 | Unless required by applicable law or agreed to in writing, Licensor provides the 136 | Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, 137 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, 138 | including, without limitation, any warranties or conditions of TITLE, 139 | NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are 140 | solely responsible for determining the appropriateness of using or 141 | redistributing the Work and assume any risks associated with Your exercise of 142 | permissions under this License. 143 | 144 | 8. Limitation of Liability. 145 | 146 | In no event and under no legal theory, whether in tort (including negligence), 147 | contract, or otherwise, unless required by applicable law (such as deliberate 148 | and grossly negligent acts) or agreed to in writing, shall any Contributor be 149 | liable to You for damages, including any direct, indirect, special, incidental, 150 | or consequential damages of any character arising as a result of this License or 151 | out of the use or inability to use the Work (including but not limited to 152 | damages for loss of goodwill, work stoppage, computer failure or malfunction, or 153 | any and all other commercial damages or losses), even if such Contributor has 154 | been advised of the possibility of such damages. 155 | 156 | 9. Accepting Warranty or Additional Liability. 157 | 158 | While redistributing the Work or Derivative Works thereof, You may choose to 159 | offer, and charge a fee for, acceptance of support, warranty, indemnity, or 160 | other liability obligations and/or rights consistent with this License. However, 161 | in accepting such obligations, You may act only on Your own behalf and on Your 162 | sole responsibility, not on behalf of any other Contributor, and only if You 163 | agree to indemnify, defend, and hold each Contributor harmless for any liability 164 | incurred by, or claims asserted against, such Contributor by reason of your 165 | accepting any such warranty or additional liability. 166 | 167 | END OF TERMS AND CONDITIONS 168 | 169 | APPENDIX: How to apply the Apache License to your work 170 | 171 | To apply the Apache License to your work, attach the following boilerplate 172 | notice, with the fields enclosed by brackets "[]" replaced with your own 173 | identifying information. (Don't include the brackets!) The text should be 174 | enclosed in the appropriate comment syntax for the file format. We also 175 | recommend that a file or class name and description of purpose be included on 176 | the same "printed page" as the copyright notice for easier identification within 177 | third-party archives. 178 | 179 | Copyright [yyyy] [name of copyright owner] 180 | 181 | Licensed under the Apache License, Version 2.0 (the "License"); 182 | you may not use this file except in compliance with the License. 183 | You may obtain a copy of the License at 184 | 185 | http://www.apache.org/licenses/LICENSE-2.0 186 | 187 | Unless required by applicable law or agreed to in writing, software 188 | distributed under the License is distributed on an "AS IS" BASIS, 189 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 190 | See the License for the specific language governing permissions and 191 | limitations under the License. 192 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | android-obd-reader 2 | ======================== 3 | 4 | ## NOTICE 5 | 6 | **I am no longer involved in any way with OBD and related activities, so don't expect my feedback on issues, pull-requests and most of all, email.** 7 | 8 | I can't even remember when I first picked this project from Brice Lambi (the original author). But one thing I'm sure, it was a time my interests changed quite frequently and I'd contribute simultaneously to totally unrelated projects. But for some reason this project stuck with me the longest. 9 | 10 | Initially, it was more of an Android hack (sorry, Brice!). With time, I've redesigned the code and split it into two: [a Java API library](https://github.com/pires/obd-java-api/) that could run anywhere the JVM ran without concerning about which transport protocol one would use (because it just asks for one `InputStream/OutputStream` pair) and, after learning about Android development, a revamped Android app. 11 | 12 | I know, the UI sucks, but I've never had the eye for UI/UX, I'll admit! 13 | 14 | Years went by and a few contributors jumped in with amazing, smart features and fixes. To those fine people, **Thank you**! This is your _baby_, too. 15 | 16 | Now, it's time to say goodbye. 17 | Pires 18 | 19 | [![CircleCI](https://circleci.com/gh/pires/android-obd-reader.svg?style=svg)](https://circleci.com/gh/pires/android-obd-reader) 20 | 21 | Android OBD-II reader designed to connect with Bluetooth Elm327 OBD reader. 22 | 23 | ![screenshot](/Screenshot.png) 24 | 25 | The latest release can be found [here](https://github.com/pires/android-obd-reader/releases/). 26 | 27 | ## Prerequisites ## 28 | - JDK 8 29 | - Android Studio 1.5.x or newer 30 | - Android SDK (API 22, Build tools 23.0.1) 31 | - [OBD Java API](https://github.com/pires/obd-java-api/) (already included) 32 | 33 | ## Test with device ## 34 | 35 | Be sure to have the device connected to your computer. 36 | 37 | ``` 38 | cd whatever_directory_you_cloned_this_repository 39 | gradle clean build installDebug 40 | ``` 41 | 42 | ## Test with OBD Server ## 43 | 44 | If you want to upload data to a server, for now, check the following: 45 | * [OBD Server](https://github.com/pires/obd-server/) - a simple implementation of a RESTful app, compiled into a runnable JAR. 46 | * Enable the upload functionality in preferences 47 | * Set proper endpoint address and port in preferences. 48 | 49 | ## Troubleshooting ## 50 | 51 | As *@dembol* noted: 52 | 53 | Have you checked your ELM327 adapter with Torque or Scanmaster to see if it works with your car? Maybe the problem is with your device? 54 | 55 | Popular OBD diagnostic tools reset state and disable echo, spaces etc before protocol selection. Download some elm327 terminal for android and try following commands in order: 56 | ``` 57 | ATD 58 | ATZ 59 | AT E0 60 | AT L0 61 | AT S0 62 | AT H0 63 | AT SP 0 64 | ``` 65 | 66 | One may need to turn off echo and headers depending on the dongle in use: 67 | ``` 68 | AT E0 - Turn echo off. Characters sent to ElmScan are not retransmitted back to the host computer. 69 | AT E1 - Turn echo on. This is the default state, characters are echoed back to the host computer. 70 | AT H0 - Turn headers off. This is the default state, header information and CRC byte are omitted. 71 | AT H1 - Turn headers on. Header information and CRC byte are displayed. 72 | ``` 73 | 74 | ## Building with custom `obd-java-api` 75 | 76 | This project depends on a [pure-Java OBD library](https://github.com/pires/obd-java-api/). For testing with a custom version of it, do the following: 77 | 78 | * Clone obd-java-api it into your project folder: 79 | 80 | ``` 81 | git clone https://github.com/pires/obd-java-api.git 82 | ``` 83 | 84 | * Create `obd-java-api/build.gradle` with the following content: 85 | 86 | ``` 87 | apply plugin: 'java' 88 | ``` 89 | 90 | * Edit `main build.gradle` and change: 91 | 92 | ``` 93 | compile 'com.github.pires:obd-java-api:1.0-RC14'` 94 | ``` 95 | 96 | to 97 | 98 | ``` 99 | compile project(':obd-java-api') 100 | ``` 101 | 102 | * Edit `settings.gradle` and add: 103 | 104 | ``` 105 | include ':obd-java-api' 106 | ``` 107 | 108 | ## Tested on ## 109 | 110 | * Samsung Galaxy Nexus (Android 4.4.1) 111 | * LG Nexus 5 (Android 6.0 Preview 3) 112 | * Nexus 7 2013 WiFi (Android 4.4.4) 113 | * Samsung Galaxy S4 Active I9295 (Android 5.0.2) 114 | * Samsung Galaxy S6 Edge SM-925F (Android 5.0.2) 115 | * Samsung Galaxy Note 3 116 | -------------------------------------------------------------------------------- /Screenshot.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/Screenshot.png -------------------------------------------------------------------------------- /btcar.svg: -------------------------------------------------------------------------------- 1 | 2 | 23 | btcar 25 | 27 | 28 | 30 | image/svg+xml 31 | 33 | btcar 34 | 35 | 36 | nomwerp 37 | 38 | 39 | 41 | 42 | 43 | basic sprites from https://github.com/google/material-design-icons " released under an Attribution 4.0 International license." 44 | 45 | 46 | com.github.pires.obd.reader 47 | 48 | 50 | 52 | 54 | 56 | 58 | 60 | 61 | 62 | 63 | 65 | 67 | 71 | 75 | 79 | 83 | 84 | 87 | 91 | 92 | 94 | 98 | 102 | 103 | 105 | 109 | 113 | 114 | 118 | 123 | 129 | 134 | 139 | 145 | 146 | 148 | 152 | 156 | 157 | 168 | 170 | 174 | 178 | 179 | 190 | 194 | 199 | 205 | 210 | 215 | 221 | 222 | 225 | 234 | 235 | 244 | 253 | 262 | 271 | 274 | 285 | 286 | 295 | 299 | 304 | 310 | 315 | 320 | 326 | 331 | 337 | 343 | 348 | 353 | 359 | 360 | 369 | 373 | 378 | 384 | 389 | 394 | 400 | 401 | 405 | 410 | 416 | 421 | 426 | 432 | 433 | 434 | 467 | 477 | 478 | 483 | 494 | 495 | 500 | 503 | 510 | 511 | 512 | 518 | 534 | 538 | 539 | 555 | 559 | 565 | 566 | 567 | 568 | 574 | 581 | 582 | 592 | 594 | 595 | 602 | 615 | 616 | 617 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath 'com.android.tools.build:gradle:2.1.2' 7 | } 8 | } 9 | 10 | repositories { 11 | mavenLocal() 12 | mavenCentral() 13 | } 14 | 15 | apply plugin: 'com.android.application' 16 | 17 | repositories { 18 | mavenLocal() 19 | mavenCentral() 20 | } 21 | 22 | android { 23 | compileSdkVersion 22 24 | android.buildToolsVersion "23.0.1" 25 | defaultConfig { 26 | minSdkVersion 14 27 | targetSdkVersion 21 28 | applicationId 'com.github.pires.obd.reader' 29 | } 30 | 31 | lintOptions { 32 | abortOnError false 33 | } 34 | 35 | buildTypes { 36 | release { 37 | minifyEnabled false 38 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 39 | } 40 | } 41 | } 42 | 43 | dependencies { 44 | compile 'com.android.support:appcompat-v7:22.1.1' 45 | compile 'com.github.pires:obd-java-api:1.0-RC16' 46 | compile 'org.roboguice:roboguice:3.+' 47 | provided 'org.roboguice:roboblender:3.+' 48 | compile 'com.squareup.retrofit:retrofit:1.9.0' 49 | } 50 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | test: 2 | override: 3 | - ./gradlew assembleDebug 4 | -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Wed Apr 10 15:27:10 PDT 2013 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.10-all.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | 5 | com.obdreader.android 6 | android-obd-reader 7 | 2.0-RC2 8 | apk 9 | 10 | OBD Java Reader 11 | https://github.com/pires/android-obd-reader 12 | 13 | 14 | 21 15 | 1.7 16 | 17 | 18 | ${project.artifactId} 19 | 20 | 21 | org.apache.maven.plugins 22 | maven-compiler-plugin 23 | 3.2 24 | 25 | ${java.version} 26 | ${java.version} 27 | -proc:none 28 | 29 | 30 | 31 | com.simpligility.maven.plugins 32 | android-maven-plugin 33 | 4.3.0 34 | true 35 | 36 | 37 | ${android-platform} 38 | 39 | ignored 40 | true 41 | true 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | android 51 | android 52 | 5.1.1_r2 53 | provided 54 | 55 | 56 | 57 | com.github.pires 58 | obd-java-api 59 | 1.0-RC9 60 | 61 | 62 | 63 | 64 | com.android.support 65 | appcompat-v7 66 | 22.2.1 67 | aar 68 | 69 | 70 | 71 | org.roboguice 72 | roboguice 73 | 3.0.1 74 | 75 | 76 | 77 | com.squareup.retrofit 78 | retrofit 79 | 1.9.0 80 | 81 | 82 | 83 | org.roboguice 84 | roboblender 85 | 3.0.1 86 | provided 87 | 88 | 89 | 90 | 91 | -------------------------------------------------------------------------------- /proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in /Users/tveloso/Tools/android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the ProGuard 5 | # include property in project.properties. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | 19 | -keep public class AnnotationDatabaseImpl 20 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/settings.gradle -------------------------------------------------------------------------------- /src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 23 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 38 | 42 | 43 | 47 | 48 | 52 | 56 | 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/activity/ConfigActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.activity; 2 | 3 | import android.app.Activity; 4 | import android.bluetooth.BluetoothAdapter; 5 | import android.bluetooth.BluetoothDevice; 6 | import android.content.Context; 7 | import android.content.SharedPreferences; 8 | import android.location.LocationManager; 9 | import android.location.LocationProvider; 10 | import android.os.Bundle; 11 | import android.preference.CheckBoxPreference; 12 | import android.preference.EditTextPreference; 13 | import android.preference.ListPreference; 14 | import android.preference.Preference; 15 | import android.preference.Preference.OnPreferenceChangeListener; 16 | import android.preference.Preference.OnPreferenceClickListener; 17 | import android.preference.PreferenceActivity; 18 | import android.preference.PreferenceCategory; 19 | import android.preference.PreferenceScreen; 20 | import android.widget.Toast; 21 | 22 | import com.github.pires.obd.commands.ObdCommand; 23 | import com.github.pires.obd.enums.ObdProtocols; 24 | import com.github.pires.obd.reader.R; 25 | import com.github.pires.obd.reader.config.ObdConfig; 26 | 27 | import java.util.ArrayList; 28 | import java.util.Set; 29 | 30 | /** 31 | * Configuration com.github.pires.obd.reader.activity. 32 | */ 33 | public class ConfigActivity extends PreferenceActivity implements OnPreferenceChangeListener { 34 | 35 | public static final String BLUETOOTH_LIST_KEY = "bluetooth_list_preference"; 36 | public static final String UPLOAD_URL_KEY = "upload_url_preference"; 37 | public static final String UPLOAD_DATA_KEY = "upload_data_preference"; 38 | public static final String OBD_UPDATE_PERIOD_KEY = "obd_update_period_preference"; 39 | public static final String VEHICLE_ID_KEY = "vehicle_id_preference"; 40 | public static final String ENGINE_DISPLACEMENT_KEY = "engine_displacement_preference"; 41 | public static final String VOLUMETRIC_EFFICIENCY_KEY = "volumetric_efficiency_preference"; 42 | public static final String IMPERIAL_UNITS_KEY = "imperial_units_preference"; 43 | public static final String COMMANDS_SCREEN_KEY = "obd_commands_screen"; 44 | public static final String PROTOCOLS_LIST_KEY = "obd_protocols_preference"; 45 | public static final String ENABLE_GPS_KEY = "enable_gps_preference"; 46 | public static final String GPS_UPDATE_PERIOD_KEY = "gps_update_period_preference"; 47 | public static final String GPS_DISTANCE_PERIOD_KEY = "gps_distance_period_preference"; 48 | public static final String ENABLE_BT_KEY = "enable_bluetooth_preference"; 49 | public static final String MAX_FUEL_ECON_KEY = "max_fuel_econ_preference"; 50 | public static final String CONFIG_READER_KEY = "reader_config_preference"; 51 | public static final String ENABLE_FULL_LOGGING_KEY = "enable_full_logging"; 52 | public static final String DIRECTORY_FULL_LOGGING_KEY = "dirname_full_logging"; 53 | public static final String DEV_EMAIL_KEY = "dev_email"; 54 | 55 | /** 56 | * @param prefs 57 | * @return 58 | */ 59 | public static int getObdUpdatePeriod(SharedPreferences prefs) { 60 | String periodString = prefs. 61 | getString(ConfigActivity.OBD_UPDATE_PERIOD_KEY, "4"); // 4 as in seconds 62 | int period = 4000; // by default 4000ms 63 | 64 | try { 65 | period = (int) (Double.parseDouble(periodString) * 1000); 66 | } catch (Exception e) { 67 | } 68 | 69 | if (period <= 0) { 70 | period = 4000; 71 | } 72 | 73 | return period; 74 | } 75 | 76 | /** 77 | * @param prefs 78 | * @return 79 | */ 80 | public static double getVolumetricEfficieny(SharedPreferences prefs) { 81 | String veString = prefs.getString(ConfigActivity.VOLUMETRIC_EFFICIENCY_KEY, ".85"); 82 | double ve = 0.85; 83 | try { 84 | ve = Double.parseDouble(veString); 85 | } catch (Exception e) { 86 | } 87 | return ve; 88 | } 89 | 90 | /** 91 | * @param prefs 92 | * @return 93 | */ 94 | public static double getEngineDisplacement(SharedPreferences prefs) { 95 | String edString = prefs.getString(ConfigActivity.ENGINE_DISPLACEMENT_KEY, "1.6"); 96 | double ed = 1.6; 97 | try { 98 | ed = Double.parseDouble(edString); 99 | } catch (Exception e) { 100 | } 101 | return ed; 102 | } 103 | 104 | /** 105 | * @param prefs 106 | * @return 107 | */ 108 | public static ArrayList getObdCommands(SharedPreferences prefs) { 109 | ArrayList cmds = ObdConfig.getCommands(); 110 | ArrayList ucmds = new ArrayList<>(); 111 | for (int i = 0; i < cmds.size(); i++) { 112 | ObdCommand cmd = cmds.get(i); 113 | boolean selected = prefs.getBoolean(cmd.getName(), true); 114 | if (selected) 115 | ucmds.add(cmd); 116 | } 117 | return ucmds; 118 | } 119 | 120 | /** 121 | * @param prefs 122 | * @return 123 | */ 124 | public static double getMaxFuelEconomy(SharedPreferences prefs) { 125 | String maxStr = prefs.getString(ConfigActivity.MAX_FUEL_ECON_KEY, "70"); 126 | double max = 70; 127 | try { 128 | max = Double.parseDouble(maxStr); 129 | } catch (Exception e) { 130 | } 131 | return max; 132 | } 133 | 134 | /** 135 | * @param prefs 136 | * @return 137 | */ 138 | public static String[] getReaderConfigCommands(SharedPreferences prefs) { 139 | String cmdsStr = prefs.getString(CONFIG_READER_KEY, "atsp0\natz"); 140 | String[] cmds = cmdsStr.split("\n"); 141 | return cmds; 142 | } 143 | 144 | /** 145 | * Minimum time between location updates, in milliseconds 146 | * 147 | * @param prefs 148 | * @return 149 | */ 150 | public static int getGpsUpdatePeriod(SharedPreferences prefs) { 151 | String periodString = prefs 152 | .getString(ConfigActivity.GPS_UPDATE_PERIOD_KEY, "1"); // 1 as in seconds 153 | int period = 1000; // by default 1000ms 154 | 155 | try { 156 | period = (int) (Double.parseDouble(periodString) * 1000); 157 | } catch (Exception e) { 158 | } 159 | 160 | if (period <= 0) { 161 | period = 1000; 162 | } 163 | 164 | return period; 165 | } 166 | 167 | /** 168 | * Min Distance between location updates, in meters 169 | * 170 | * @param prefs 171 | * @return 172 | */ 173 | public static float getGpsDistanceUpdatePeriod(SharedPreferences prefs) { 174 | String periodString = prefs 175 | .getString(ConfigActivity.GPS_DISTANCE_PERIOD_KEY, "5"); // 5 as in meters 176 | float period = 5; // by default 5 meters 177 | 178 | try { 179 | period = Float.parseFloat(periodString); 180 | } catch (Exception e) { 181 | } 182 | 183 | if (period <= 0) { 184 | period = 5; 185 | } 186 | 187 | return period; 188 | } 189 | 190 | 191 | public void onCreate(Bundle savedInstanceState) { 192 | super.onCreate(savedInstanceState); 193 | 194 | /* 195 | * Read preferences resources available at res/xml/preferences.xml 196 | */ 197 | addPreferencesFromResource(R.xml.preferences); 198 | 199 | checkGps(); 200 | 201 | ArrayList pairedDeviceStrings = new ArrayList<>(); 202 | ArrayList vals = new ArrayList<>(); 203 | ListPreference listBtDevices = (ListPreference) getPreferenceScreen() 204 | .findPreference(BLUETOOTH_LIST_KEY); 205 | ArrayList protocolStrings = new ArrayList<>(); 206 | ListPreference listProtocols = (ListPreference) getPreferenceScreen() 207 | .findPreference(PROTOCOLS_LIST_KEY); 208 | String[] prefKeys = new String[]{ENGINE_DISPLACEMENT_KEY, 209 | VOLUMETRIC_EFFICIENCY_KEY, OBD_UPDATE_PERIOD_KEY, MAX_FUEL_ECON_KEY}; 210 | for (String prefKey : prefKeys) { 211 | EditTextPreference txtPref = (EditTextPreference) getPreferenceScreen() 212 | .findPreference(prefKey); 213 | txtPref.setOnPreferenceChangeListener(this); 214 | } 215 | 216 | /* 217 | * Available OBD commands 218 | * 219 | * TODO This should be read from preferences database 220 | */ 221 | ArrayList cmds = ObdConfig.getCommands(); 222 | PreferenceScreen cmdScr = (PreferenceScreen) getPreferenceScreen() 223 | .findPreference(COMMANDS_SCREEN_KEY); 224 | for (ObdCommand cmd : cmds) { 225 | CheckBoxPreference cpref = new CheckBoxPreference(this); 226 | cpref.setTitle(cmd.getName()); 227 | cpref.setKey(cmd.getName()); 228 | cpref.setChecked(true); 229 | cmdScr.addPreference(cpref); 230 | } 231 | 232 | /* 233 | * Available OBD protocols 234 | * 235 | */ 236 | for (ObdProtocols protocol : ObdProtocols.values()) { 237 | protocolStrings.add(protocol.name()); 238 | } 239 | listProtocols.setEntries(protocolStrings.toArray(new CharSequence[0])); 240 | listProtocols.setEntryValues(protocolStrings.toArray(new CharSequence[0])); 241 | 242 | /* 243 | * Let's use this device Bluetooth adapter to select which paired OBD-II 244 | * compliant device we'll use. 245 | */ 246 | final BluetoothAdapter mBtAdapter = BluetoothAdapter.getDefaultAdapter(); 247 | if (mBtAdapter == null) { 248 | listBtDevices 249 | .setEntries(pairedDeviceStrings.toArray(new CharSequence[0])); 250 | listBtDevices.setEntryValues(vals.toArray(new CharSequence[0])); 251 | 252 | // we shouldn't get here, still warn user 253 | Toast.makeText(this, "This device does not support Bluetooth.", 254 | Toast.LENGTH_LONG).show(); 255 | 256 | return; 257 | } 258 | 259 | /* 260 | * Listen for preferences click. 261 | * 262 | * TODO there are so many repeated validations :-/ 263 | */ 264 | final Activity thisActivity = this; 265 | listBtDevices.setEntries(new CharSequence[1]); 266 | listBtDevices.setEntryValues(new CharSequence[1]); 267 | listBtDevices.setOnPreferenceClickListener(new OnPreferenceClickListener() { 268 | public boolean onPreferenceClick(Preference preference) { 269 | // see what I mean in the previous comment? 270 | if (mBtAdapter == null || !mBtAdapter.isEnabled()) { 271 | Toast.makeText(thisActivity, 272 | "This device does not support Bluetooth or it is disabled.", 273 | Toast.LENGTH_LONG).show(); 274 | return false; 275 | } 276 | return true; 277 | } 278 | }); 279 | 280 | /* 281 | * Get paired devices and populate preference list. 282 | */ 283 | Set pairedDevices = mBtAdapter.getBondedDevices(); 284 | if (pairedDevices.size() > 0) { 285 | for (BluetoothDevice device : pairedDevices) { 286 | pairedDeviceStrings.add(device.getName() + "\n" + device.getAddress()); 287 | vals.add(device.getAddress()); 288 | } 289 | } 290 | listBtDevices.setEntries(pairedDeviceStrings.toArray(new CharSequence[0])); 291 | listBtDevices.setEntryValues(vals.toArray(new CharSequence[0])); 292 | } 293 | 294 | /** 295 | * OnPreferenceChangeListener method that will validate a preferencen new 296 | * value when it's changed. 297 | * 298 | * @param preference the changed preference 299 | * @param newValue the value to be validated and set if valid 300 | */ 301 | public boolean onPreferenceChange(Preference preference, Object newValue) { 302 | 303 | if (OBD_UPDATE_PERIOD_KEY.equals(preference.getKey()) 304 | || VOLUMETRIC_EFFICIENCY_KEY.equals(preference.getKey()) 305 | || ENGINE_DISPLACEMENT_KEY.equals(preference.getKey()) 306 | || MAX_FUEL_ECON_KEY.equals(preference.getKey()) 307 | || GPS_UPDATE_PERIOD_KEY.equals(preference.getKey()) 308 | || GPS_DISTANCE_PERIOD_KEY.equals(preference.getKey())) { 309 | try { 310 | Double.parseDouble(newValue.toString().replace(",", ".")); 311 | return true; 312 | } catch (Exception e) { 313 | Toast.makeText(this, 314 | "Couldn't parse '" + newValue.toString() + "' as a number.", 315 | Toast.LENGTH_LONG).show(); 316 | } 317 | } 318 | return false; 319 | } 320 | 321 | private void checkGps() { 322 | LocationManager mLocService = (LocationManager) getSystemService(Context.LOCATION_SERVICE); 323 | if (mLocService != null) { 324 | LocationProvider mLocProvider = mLocService.getProvider(LocationManager.GPS_PROVIDER); 325 | if (mLocProvider == null) { 326 | hideGPSCategory(); 327 | } 328 | } 329 | } 330 | 331 | private void hideGPSCategory() { 332 | PreferenceScreen preferenceScreen = getPreferenceScreen(); 333 | PreferenceCategory preferenceCategory = (PreferenceCategory) findPreference(getResources().getString(R.string.pref_gps_category)); 334 | if (preferenceCategory != null) { 335 | preferenceCategory.removeAll(); 336 | preferenceScreen.removePreference((Preference) preferenceCategory); 337 | } 338 | } 339 | } 340 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/activity/ConfirmDialog.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.activity; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Dialog; 5 | import android.content.Context; 6 | import android.content.DialogInterface; 7 | 8 | /** 9 | * Created by elagin on 19.03.15. 10 | */ 11 | public class ConfirmDialog { 12 | 13 | public static final int DIALOG_CONFIRM_DELETE_ID = 1; 14 | 15 | public static Dialog createDialog(final int id, Context context, final Listener listener) { 16 | String title; 17 | String message; 18 | 19 | switch (id) { 20 | case DIALOG_CONFIRM_DELETE_ID: 21 | title = "Delete trip record"; 22 | message = "Are you sure?"; 23 | return getDialog(id, title, message, context, listener); 24 | default: 25 | return null; 26 | } 27 | } 28 | 29 | public static Dialog getDialog(final int id, String title, String message, Context context, final Listener listener) { 30 | AlertDialog.Builder builder = new AlertDialog.Builder(context); 31 | builder.setTitle(title); 32 | builder.setMessage(message); 33 | builder.setCancelable(true); 34 | builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { 35 | @Override 36 | public void onClick(DialogInterface dialog, int which) { 37 | listener.onConfirmationDialogResponse(id, true); 38 | } 39 | }); 40 | builder.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() { 41 | @Override 42 | public void onClick(DialogInterface dialog, int which) { 43 | listener.onConfirmationDialogResponse(id, false); 44 | } 45 | }); 46 | Dialog dialog = builder.create(); 47 | dialog.setCanceledOnTouchOutside(false); 48 | return dialog; 49 | } 50 | 51 | public interface Listener { 52 | public void onConfirmationDialogResponse(int id, boolean confirmed); 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/activity/TripListActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.activity; 2 | 3 | import android.app.Dialog; 4 | import android.os.Bundle; 5 | import android.view.ContextMenu; 6 | import android.view.Menu; 7 | import android.view.MenuItem; 8 | import android.view.View; 9 | import android.widget.AdapterView; 10 | import android.widget.ListView; 11 | 12 | import com.github.pires.obd.reader.R; 13 | import com.github.pires.obd.reader.trips.TripListAdapter; 14 | import com.github.pires.obd.reader.trips.TripLog; 15 | import com.github.pires.obd.reader.trips.TripRecord; 16 | 17 | import java.util.List; 18 | 19 | import roboguice.activity.RoboActivity; 20 | 21 | import static com.github.pires.obd.reader.activity.ConfirmDialog.createDialog; 22 | 23 | /** 24 | * Some code taken from https://github.com/wdkapps/FillUp 25 | */ 26 | 27 | public class TripListActivity 28 | extends RoboActivity 29 | implements ConfirmDialog.Listener { 30 | 31 | private List records; 32 | private TripLog triplog = null; 33 | private TripListAdapter adapter = null; 34 | 35 | /// the currently selected row from the list of records 36 | private int selectedRow; 37 | 38 | @Override 39 | protected void onCreate(Bundle savedInstanceState) { 40 | super.onCreate(savedInstanceState); 41 | setContentView(R.layout.activity_trips_list); 42 | 43 | ListView lv = (ListView) findViewById(R.id.tripList); 44 | 45 | triplog = TripLog.getInstance(this.getApplicationContext()); 46 | records = triplog.readAllRecords(); 47 | adapter = new TripListAdapter(this, records); 48 | lv.setAdapter(adapter); 49 | registerForContextMenu(lv); 50 | } 51 | 52 | @Override 53 | public boolean onCreateOptionsMenu(Menu menu) { 54 | // Inflate the menu; this adds items to the action bar if it is present. 55 | //getMenuInflater().inflate(R.menu.menu_trips_list, menu); 56 | return true; 57 | } 58 | 59 | @Override 60 | public boolean onOptionsItemSelected(MenuItem item) { 61 | // Handle action bar item clicks here. The action bar will 62 | // automatically handle clicks on the Home/Up button, so long 63 | // as you specify a parent activity in AndroidManifest.xml. 64 | int id = item.getItemId(); 65 | 66 | //noinspection SimplifiableIfStatement 67 | if (id == R.id.action_settings) { 68 | return true; 69 | } 70 | 71 | return super.onOptionsItemSelected(item); 72 | } 73 | 74 | @Override 75 | public void onCreateContextMenu(ContextMenu menu, View v, 76 | ContextMenu.ContextMenuInfo menuInfo) { 77 | // create the menu 78 | getMenuInflater().inflate(R.menu.context_trip_list, menu); 79 | 80 | // get index of currently selected row 81 | AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) menuInfo; 82 | selectedRow = (int) info.id; 83 | 84 | // get record that is currently selected 85 | TripRecord record = records.get(selectedRow); 86 | } 87 | 88 | private void deleteTrip() { 89 | // get the record to delete from our list of records 90 | TripRecord record = records.get(selectedRow); 91 | 92 | // attempt to remove the record from the log 93 | if (triplog.deleteTrip(record.getID())) { 94 | 95 | // remove the record from our list of records 96 | records.remove(selectedRow); 97 | 98 | // update the list view 99 | adapter.notifyDataSetChanged(); 100 | } else { 101 | //Utilities.toast(this,getString(R.string.toast_delete_failed)); 102 | } 103 | } 104 | 105 | public boolean onContextItemSelected(MenuItem item) { 106 | 107 | // get index of currently selected row 108 | AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo(); 109 | selectedRow = (int) info.id; 110 | 111 | switch (item.getItemId()) { 112 | case R.id.itemDelete: 113 | showDialog(ConfirmDialog.DIALOG_CONFIRM_DELETE_ID); 114 | return true; 115 | 116 | default: 117 | return super.onContextItemSelected(item); 118 | } 119 | } 120 | 121 | protected Dialog onCreateDialog(int id) { 122 | return createDialog(id, this, this); 123 | } 124 | 125 | /** 126 | * DESCRIPTION: 127 | * Called when the user has selected a gasoline record to delete 128 | * from the log and has confirmed deletion. 129 | */ 130 | protected void deleteRow() { 131 | 132 | // get the record to delete from our list of records 133 | TripRecord record = records.get(selectedRow); 134 | 135 | // attempt to remove the record from the log 136 | if (triplog.deleteTrip(record.getID())) { 137 | records.remove(selectedRow); 138 | adapter.notifyDataSetChanged(); 139 | } else { 140 | //Utilities.toast(this,getString(R.string.toast_delete_failed)); 141 | } 142 | } 143 | 144 | @Override 145 | public void onConfirmationDialogResponse(int id, boolean confirmed) { 146 | removeDialog(id); 147 | if (!confirmed) return; 148 | 149 | switch (id) { 150 | case ConfirmDialog.DIALOG_CONFIRM_DELETE_ID: 151 | deleteRow(); 152 | break; 153 | 154 | default: 155 | //Utilities.toast(this,"Invalid dialog id."); 156 | } 157 | 158 | } 159 | } 160 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/activity/TroubleCodesActivity.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.activity; 2 | 3 | import android.app.Activity; 4 | import android.app.ProgressDialog; 5 | import android.bluetooth.BluetoothAdapter; 6 | import android.bluetooth.BluetoothDevice; 7 | import android.bluetooth.BluetoothSocket; 8 | import android.content.Intent; 9 | import android.content.SharedPreferences; 10 | import android.content.pm.ActivityInfo; 11 | import android.content.res.Configuration; 12 | import android.os.AsyncTask; 13 | import android.os.Bundle; 14 | import android.os.Handler; 15 | import android.os.Message; 16 | import android.preference.PreferenceManager; 17 | import android.util.Log; 18 | import android.view.Menu; 19 | import android.view.MenuInflater; 20 | import android.view.MenuItem; 21 | import android.widget.ArrayAdapter; 22 | import android.widget.ListView; 23 | import android.widget.Toast; 24 | 25 | import com.github.pires.obd.commands.control.TroubleCodesCommand; 26 | import com.github.pires.obd.commands.protocol.EchoOffCommand; 27 | import com.github.pires.obd.commands.protocol.LineFeedOffCommand; 28 | import com.github.pires.obd.commands.protocol.ObdResetCommand; 29 | import com.github.pires.obd.commands.protocol.ResetTroubleCodesCommand; 30 | import com.github.pires.obd.commands.protocol.SelectProtocolCommand; 31 | import com.github.pires.obd.enums.ObdProtocols; 32 | import com.github.pires.obd.exceptions.MisunderstoodCommandException; 33 | import com.github.pires.obd.exceptions.NoDataException; 34 | import com.github.pires.obd.exceptions.UnableToConnectException; 35 | import com.github.pires.obd.reader.R; 36 | import com.github.pires.obd.reader.io.BluetoothManager; 37 | import com.google.inject.Inject; 38 | 39 | import java.io.IOException; 40 | import java.lang.reflect.Method; 41 | import java.util.ArrayList; 42 | import java.util.HashMap; 43 | import java.util.Map; 44 | import java.util.UUID; 45 | 46 | public class TroubleCodesActivity extends Activity { 47 | 48 | private static final String TAG = TroubleCodesActivity.class.getName(); 49 | private static final int NO_BLUETOOTH_DEVICE_SELECTED = 0; 50 | private static final int CANNOT_CONNECT_TO_DEVICE = 1; 51 | private static final int NO_DATA = 3; 52 | private static final int DATA_OK = 4; 53 | private static final int CLEAR_DTC = 5; 54 | private static final int OBD_COMMAND_FAILURE = 10; 55 | private static final int OBD_COMMAND_FAILURE_IO = 11; 56 | private static final int OBD_COMMAND_FAILURE_UTC = 12; 57 | private static final int OBD_COMMAND_FAILURE_IE = 13; 58 | private static final int OBD_COMMAND_FAILURE_MIS = 14; 59 | private static final int OBD_COMMAND_FAILURE_NODATA = 15; 60 | @Inject 61 | SharedPreferences prefs; 62 | private ProgressDialog progressDialog; 63 | private String remoteDevice; 64 | private GetTroubleCodesTask gtct; 65 | private BluetoothDevice dev = null; 66 | private BluetoothSocket sock = null; 67 | private Handler mHandler = new Handler(new Handler.Callback() { 68 | 69 | 70 | public boolean handleMessage(Message msg) { 71 | Log.d(TAG, "Message received on handler"); 72 | switch (msg.what) { 73 | case NO_BLUETOOTH_DEVICE_SELECTED: 74 | makeToast(getString(R.string.text_bluetooth_nodevice)); 75 | finish(); 76 | break; 77 | case CANNOT_CONNECT_TO_DEVICE: 78 | makeToast(getString(R.string.text_bluetooth_error_connecting)); 79 | finish(); 80 | break; 81 | 82 | case OBD_COMMAND_FAILURE: 83 | makeToast(getString(R.string.text_obd_command_failure)); 84 | finish(); 85 | break; 86 | case OBD_COMMAND_FAILURE_IO: 87 | makeToast(getString(R.string.text_obd_command_failure) + " IO"); 88 | finish(); 89 | break; 90 | case OBD_COMMAND_FAILURE_IE: 91 | makeToast(getString(R.string.text_obd_command_failure) + " IE"); 92 | finish(); 93 | break; 94 | case OBD_COMMAND_FAILURE_MIS: 95 | makeToast(getString(R.string.text_obd_command_failure) + " MIS"); 96 | finish(); 97 | break; 98 | case OBD_COMMAND_FAILURE_UTC: 99 | makeToast(getString(R.string.text_obd_command_failure) + " UTC"); 100 | finish(); 101 | break; 102 | case OBD_COMMAND_FAILURE_NODATA: 103 | makeToastLong(getString(R.string.text_noerrors)); 104 | //finish(); 105 | break; 106 | 107 | case NO_DATA: 108 | makeToast(getString(R.string.text_dtc_no_data)); 109 | ///finish(); 110 | break; 111 | case DATA_OK: 112 | dataOk((String) msg.obj); 113 | break; 114 | 115 | } 116 | return false; 117 | } 118 | }); 119 | 120 | @Override 121 | protected void onCreate(Bundle savedInstanceState) { 122 | super.onCreate(savedInstanceState); 123 | prefs = PreferenceManager.getDefaultSharedPreferences(this); 124 | 125 | if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { 126 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE); 127 | } else { 128 | setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT); 129 | } 130 | 131 | remoteDevice = prefs.getString(ConfigActivity.BLUETOOTH_LIST_KEY, null); 132 | if (remoteDevice == null || "".equals(remoteDevice)) { 133 | Log.e(TAG, "No Bluetooth device has been selected."); 134 | mHandler.obtainMessage(NO_BLUETOOTH_DEVICE_SELECTED).sendToTarget(); 135 | } else { 136 | gtct = new GetTroubleCodesTask(); 137 | gtct.execute(remoteDevice); 138 | } 139 | } 140 | 141 | @Override 142 | public boolean onCreateOptionsMenu(Menu menu) { 143 | // Inflate the menu items for use in the action bar 144 | MenuInflater inflater = getMenuInflater(); 145 | inflater.inflate(R.menu.trouble_codes, menu); 146 | return super.onCreateOptionsMenu(menu); 147 | } 148 | 149 | @Override 150 | public boolean onOptionsItemSelected(MenuItem item) { 151 | // Handle presses on the action bar items 152 | switch (item.getItemId()) { 153 | case R.id.action_clear_codes: 154 | try { 155 | sock = BluetoothManager.connect(dev); 156 | } catch (Exception e) { 157 | Log.e( 158 | TAG, 159 | "There was an error while establishing connection. -> " 160 | + e.getMessage() 161 | ); 162 | Log.d(TAG, "Message received on handler here"); 163 | mHandler.obtainMessage(CANNOT_CONNECT_TO_DEVICE).sendToTarget(); 164 | return true; 165 | } 166 | try { 167 | 168 | Log.d("TESTRESET", "Trying reset"); 169 | //new ObdResetCommand().run(sock.getInputStream(), sock.getOutputStream()); 170 | ResetTroubleCodesCommand clear = new ResetTroubleCodesCommand(); 171 | clear.run(sock.getInputStream(), sock.getOutputStream()); 172 | String result = clear.getFormattedResult(); 173 | Log.d("TESTRESET", "Trying reset result: " + result); 174 | } catch (Exception e) { 175 | Log.e( 176 | TAG, 177 | "There was an error while establishing connection. -> " 178 | + e.getMessage() 179 | ); 180 | } 181 | gtct.closeSocket(sock); 182 | // Refresh main activity upon close of dialog box 183 | Intent refresh = new Intent(this, TroubleCodesActivity.class); 184 | startActivity(refresh); 185 | this.finish(); // 186 | return true; 187 | default: 188 | return super.onOptionsItemSelected(item); 189 | } 190 | } 191 | 192 | Map getDict(int keyId, int valId) { 193 | String[] keys = getResources().getStringArray(keyId); 194 | String[] vals = getResources().getStringArray(valId); 195 | 196 | Map dict = new HashMap(); 197 | for (int i = 0, l = keys.length; i < l; i++) { 198 | dict.put(keys[i], vals[i]); 199 | } 200 | 201 | return dict; 202 | } 203 | 204 | public void makeToast(String text) { 205 | Toast toast = Toast.makeText(getApplicationContext(), text, Toast.LENGTH_SHORT); 206 | toast.show(); 207 | } 208 | public void makeToastLong(String text) { 209 | Toast toast = Toast.makeText(getApplicationContext(), text, Toast.LENGTH_LONG); 210 | toast.show(); 211 | } 212 | private void dataOk(String res) { 213 | ListView lv = (ListView) findViewById(R.id.listView); 214 | Map dtcVals = getDict(R.array.dtc_keys, R.array.dtc_values); 215 | //TODO replace below codes (res) with aboce dtcVals 216 | //String tmpVal = dtcVals.get(res.split("\n")); 217 | //String[] dtcCodes = new String[]{}; 218 | ArrayList dtcCodes = new ArrayList(); 219 | //int i =1; 220 | if (res != null) { 221 | for (String dtcCode : res.split("\n")) { 222 | dtcCodes.add(dtcCode + " : " + dtcVals.get(dtcCode)); 223 | Log.d("TEST", dtcCode + " : " + dtcVals.get(dtcCode)); 224 | } 225 | } else { 226 | dtcCodes.add("There are no errors"); 227 | } 228 | ArrayAdapter myarrayAdapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, dtcCodes); 229 | lv.setAdapter(myarrayAdapter); 230 | lv.setTextFilterEnabled(true); 231 | } 232 | 233 | 234 | public class ModifiedTroubleCodesObdCommand extends TroubleCodesCommand { 235 | @Override 236 | public String getResult() { 237 | // remove unwanted response from output since this results in erroneous error codes 238 | return rawData.replace("SEARCHING...", "").replace("NODATA", ""); 239 | } 240 | } 241 | 242 | public class ClearDTC extends ResetTroubleCodesCommand { 243 | @Override 244 | public String getResult() { 245 | return rawData; 246 | } 247 | } 248 | 249 | 250 | private class GetTroubleCodesTask extends AsyncTask { 251 | 252 | @Override 253 | protected void onPreExecute() { 254 | //Create a new progress dialog 255 | progressDialog = new ProgressDialog(TroubleCodesActivity.this); 256 | //Set the progress dialog to display a horizontal progress bar 257 | progressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); 258 | //Set the dialog title to 'Loading...' 259 | progressDialog.setTitle(getString(R.string.dialog_loading_title)); 260 | //Set the dialog message to 'Loading application View, please wait...' 261 | progressDialog.setMessage(getString(R.string.dialog_loading_body)); 262 | //This dialog can't be canceled by pressing the back key 263 | progressDialog.setCancelable(false); 264 | //This dialog isn't indeterminate 265 | progressDialog.setIndeterminate(false); 266 | //The maximum number of items is 100 267 | progressDialog.setMax(5); 268 | //Set the current progress to zero 269 | progressDialog.setProgress(0); 270 | //Display the progress dialog 271 | progressDialog.show(); 272 | } 273 | 274 | @Override 275 | protected String doInBackground(String... params) { 276 | String result = ""; 277 | 278 | //Get the current thread's token 279 | synchronized (this) { 280 | Log.d(TAG, "Starting service.."); 281 | // get the remote Bluetooth device 282 | 283 | final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); 284 | dev = btAdapter.getRemoteDevice(params[0]); 285 | 286 | Log.d(TAG, "Stopping Bluetooth discovery."); 287 | btAdapter.cancelDiscovery(); 288 | 289 | Log.d(TAG, "Starting OBD connection.."); 290 | 291 | // Instantiate a BluetoothSocket for the remote device and connect it. 292 | try { 293 | sock = BluetoothManager.connect(dev); 294 | } catch (Exception e) { 295 | Log.e( 296 | TAG, 297 | "There was an error while establishing connection. -> " 298 | + e.getMessage() 299 | ); 300 | Log.d(TAG, "Message received on handler here"); 301 | mHandler.obtainMessage(CANNOT_CONNECT_TO_DEVICE).sendToTarget(); 302 | return null; 303 | } 304 | 305 | try { 306 | // Let's configure the connection. 307 | Log.d(TAG, "Queueing jobs for connection configuration.."); 308 | 309 | onProgressUpdate(1); 310 | 311 | new ObdResetCommand().run(sock.getInputStream(), sock.getOutputStream()); 312 | 313 | 314 | onProgressUpdate(2); 315 | 316 | new EchoOffCommand().run(sock.getInputStream(), sock.getOutputStream()); 317 | 318 | onProgressUpdate(3); 319 | 320 | new LineFeedOffCommand().run(sock.getInputStream(), sock.getOutputStream()); 321 | 322 | onProgressUpdate(4); 323 | 324 | new SelectProtocolCommand(ObdProtocols.AUTO).run(sock.getInputStream(), sock.getOutputStream()); 325 | 326 | onProgressUpdate(5); 327 | 328 | ModifiedTroubleCodesObdCommand tcoc = new ModifiedTroubleCodesObdCommand(); 329 | tcoc.run(sock.getInputStream(), sock.getOutputStream()); 330 | result = tcoc.getFormattedResult(); 331 | 332 | onProgressUpdate(6); 333 | 334 | } catch (IOException e) { 335 | e.printStackTrace(); 336 | Log.e("DTCERR", e.getMessage()); 337 | mHandler.obtainMessage(OBD_COMMAND_FAILURE_IO).sendToTarget(); 338 | return null; 339 | } catch (InterruptedException e) { 340 | e.printStackTrace(); 341 | Log.e("DTCERR", e.getMessage()); 342 | mHandler.obtainMessage(OBD_COMMAND_FAILURE_IE).sendToTarget(); 343 | return null; 344 | } catch (UnableToConnectException e) { 345 | e.printStackTrace(); 346 | Log.e("DTCERR", e.getMessage()); 347 | mHandler.obtainMessage(OBD_COMMAND_FAILURE_UTC).sendToTarget(); 348 | return null; 349 | } catch (MisunderstoodCommandException e) { 350 | e.printStackTrace(); 351 | Log.e("DTCERR", e.getMessage()); 352 | mHandler.obtainMessage(OBD_COMMAND_FAILURE_MIS).sendToTarget(); 353 | return null; 354 | } catch (NoDataException e) { 355 | Log.e("DTCERR", e.getMessage()); 356 | mHandler.obtainMessage(OBD_COMMAND_FAILURE_NODATA).sendToTarget(); 357 | return null; 358 | } catch (Exception e) { 359 | Log.e("DTCERR", e.getMessage()); 360 | mHandler.obtainMessage(OBD_COMMAND_FAILURE).sendToTarget(); 361 | } finally { 362 | 363 | // close socket 364 | closeSocket(sock); 365 | } 366 | 367 | } 368 | 369 | return result; 370 | } 371 | 372 | public void closeSocket(BluetoothSocket sock) { 373 | if (sock != null) 374 | // close socket 375 | try { 376 | sock.close(); 377 | } catch (IOException e) { 378 | Log.e(TAG, e.getMessage()); 379 | } 380 | } 381 | 382 | @Override 383 | protected void onProgressUpdate(Integer... values) { 384 | super.onProgressUpdate(values); 385 | progressDialog.setProgress(values[0]); 386 | } 387 | 388 | @Override 389 | protected void onPostExecute(String result) { 390 | progressDialog.dismiss(); 391 | 392 | 393 | mHandler.obtainMessage(DATA_OK, result).sendToTarget(); 394 | setContentView(R.layout.trouble_codes); 395 | 396 | } 397 | } 398 | 399 | } 400 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/config/ObdConfig.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.config; 2 | 3 | import com.github.pires.obd.commands.ObdCommand; 4 | import com.github.pires.obd.commands.SpeedCommand; 5 | import com.github.pires.obd.commands.control.DistanceMILOnCommand; 6 | import com.github.pires.obd.commands.control.DtcNumberCommand; 7 | import com.github.pires.obd.commands.control.EquivalentRatioCommand; 8 | import com.github.pires.obd.commands.control.ModuleVoltageCommand; 9 | import com.github.pires.obd.commands.control.TimingAdvanceCommand; 10 | import com.github.pires.obd.commands.control.TroubleCodesCommand; 11 | import com.github.pires.obd.commands.control.VinCommand; 12 | import com.github.pires.obd.commands.engine.LoadCommand; 13 | import com.github.pires.obd.commands.engine.MassAirFlowCommand; 14 | import com.github.pires.obd.commands.engine.OilTempCommand; 15 | import com.github.pires.obd.commands.engine.RPMCommand; 16 | import com.github.pires.obd.commands.engine.RuntimeCommand; 17 | import com.github.pires.obd.commands.engine.ThrottlePositionCommand; 18 | import com.github.pires.obd.commands.fuel.AirFuelRatioCommand; 19 | import com.github.pires.obd.commands.fuel.ConsumptionRateCommand; 20 | import com.github.pires.obd.commands.fuel.FindFuelTypeCommand; 21 | import com.github.pires.obd.commands.fuel.FuelLevelCommand; 22 | import com.github.pires.obd.commands.fuel.FuelTrimCommand; 23 | import com.github.pires.obd.commands.fuel.WidebandAirFuelRatioCommand; 24 | import com.github.pires.obd.commands.pressure.BarometricPressureCommand; 25 | import com.github.pires.obd.commands.pressure.FuelPressureCommand; 26 | import com.github.pires.obd.commands.pressure.FuelRailPressureCommand; 27 | import com.github.pires.obd.commands.pressure.IntakeManifoldPressureCommand; 28 | import com.github.pires.obd.commands.temperature.AirIntakeTemperatureCommand; 29 | import com.github.pires.obd.commands.temperature.AmbientAirTemperatureCommand; 30 | import com.github.pires.obd.commands.temperature.EngineCoolantTemperatureCommand; 31 | import com.github.pires.obd.enums.FuelTrim; 32 | 33 | import java.util.ArrayList; 34 | 35 | /** 36 | * TODO put description 37 | */ 38 | public final class ObdConfig { 39 | 40 | public static ArrayList getCommands() { 41 | ArrayList cmds = new ArrayList<>(); 42 | 43 | // Control 44 | cmds.add(new ModuleVoltageCommand()); 45 | cmds.add(new EquivalentRatioCommand()); 46 | cmds.add(new DistanceMILOnCommand()); 47 | cmds.add(new DtcNumberCommand()); 48 | cmds.add(new TimingAdvanceCommand()); 49 | cmds.add(new TroubleCodesCommand()); 50 | cmds.add(new VinCommand()); 51 | 52 | // Engine 53 | cmds.add(new LoadCommand()); 54 | cmds.add(new RPMCommand()); 55 | cmds.add(new RuntimeCommand()); 56 | cmds.add(new MassAirFlowCommand()); 57 | cmds.add(new ThrottlePositionCommand()); 58 | 59 | // Fuel 60 | cmds.add(new FindFuelTypeCommand()); 61 | cmds.add(new ConsumptionRateCommand()); 62 | // cmds.add(new AverageFuelEconomyObdCommand()); 63 | //cmds.add(new FuelEconomyCommand()); 64 | cmds.add(new FuelLevelCommand()); 65 | // cmds.add(new FuelEconomyMAPObdCommand()); 66 | // cmds.add(new FuelEconomyCommandedMAPObdCommand()); 67 | cmds.add(new FuelTrimCommand(FuelTrim.LONG_TERM_BANK_1)); 68 | cmds.add(new FuelTrimCommand(FuelTrim.LONG_TERM_BANK_2)); 69 | cmds.add(new FuelTrimCommand(FuelTrim.SHORT_TERM_BANK_1)); 70 | cmds.add(new FuelTrimCommand(FuelTrim.SHORT_TERM_BANK_2)); 71 | cmds.add(new AirFuelRatioCommand()); 72 | cmds.add(new WidebandAirFuelRatioCommand()); 73 | cmds.add(new OilTempCommand()); 74 | 75 | // Pressure 76 | cmds.add(new BarometricPressureCommand()); 77 | cmds.add(new FuelPressureCommand()); 78 | cmds.add(new FuelRailPressureCommand()); 79 | cmds.add(new IntakeManifoldPressureCommand()); 80 | 81 | // Temperature 82 | cmds.add(new AirIntakeTemperatureCommand()); 83 | cmds.add(new AmbientAirTemperatureCommand()); 84 | cmds.add(new EngineCoolantTemperatureCommand()); 85 | 86 | // Misc 87 | cmds.add(new SpeedCommand()); 88 | 89 | 90 | return cmds; 91 | } 92 | 93 | } 94 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/io/AbstractGatewayService.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.io; 2 | 3 | import android.app.Notification; 4 | import android.app.NotificationManager; 5 | import android.app.PendingIntent; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.os.Binder; 9 | import android.os.IBinder; 10 | import android.support.v4.app.NotificationCompat; 11 | import android.util.Log; 12 | 13 | import com.github.pires.obd.reader.activity.MainActivity; 14 | import com.google.inject.Inject; 15 | 16 | import java.io.IOException; 17 | import java.util.concurrent.BlockingQueue; 18 | import java.util.concurrent.LinkedBlockingQueue; 19 | 20 | import roboguice.service.RoboService; 21 | 22 | 23 | public abstract class AbstractGatewayService extends RoboService { 24 | public static final int NOTIFICATION_ID = 1; 25 | private static final String TAG = AbstractGatewayService.class.getName(); 26 | private final IBinder binder = new AbstractGatewayServiceBinder(); 27 | @Inject 28 | protected NotificationManager notificationManager; 29 | protected Context ctx; 30 | protected boolean isRunning = false; 31 | protected Long queueCounter = 0L; 32 | protected BlockingQueue jobsQueue = new LinkedBlockingQueue<>(); 33 | // Run the executeQueue in a different thread to lighten the UI thread 34 | Thread t = new Thread(new Runnable() { 35 | @Override 36 | public void run() { 37 | try { 38 | executeQueue(); 39 | } catch (InterruptedException e) { 40 | t.interrupt(); 41 | } 42 | } 43 | }); 44 | 45 | @Override 46 | public IBinder onBind(Intent intent) { 47 | return binder; 48 | } 49 | 50 | @Override 51 | public void onCreate() { 52 | super.onCreate(); 53 | Log.d(TAG, "Creating service.."); 54 | t.start(); 55 | Log.d(TAG, "Service created."); 56 | } 57 | 58 | @Override 59 | public void onDestroy() { 60 | super.onDestroy(); 61 | Log.d(TAG, "Destroying service..."); 62 | notificationManager.cancel(NOTIFICATION_ID); 63 | t.interrupt(); 64 | Log.d(TAG, "Service destroyed."); 65 | } 66 | 67 | public boolean isRunning() { 68 | return isRunning; 69 | } 70 | 71 | public boolean queueEmpty() { 72 | return jobsQueue.isEmpty(); 73 | } 74 | 75 | /** 76 | * This method will add a job to the queue while setting its ID to the 77 | * internal queue counter. 78 | * 79 | * @param job the job to queue. 80 | */ 81 | public void queueJob(ObdCommandJob job) { 82 | queueCounter++; 83 | Log.d(TAG, "Adding job[" + queueCounter + "] to queue.."); 84 | 85 | job.setId(queueCounter); 86 | try { 87 | jobsQueue.put(job); 88 | Log.d(TAG, "Job queued successfully."); 89 | } catch (InterruptedException e) { 90 | job.setState(ObdCommandJob.ObdCommandJobState.QUEUE_ERROR); 91 | Log.e(TAG, "Failed to queue job."); 92 | } 93 | } 94 | 95 | /** 96 | * Show a notification while this service is running. 97 | */ 98 | protected void showNotification(String contentTitle, String contentText, int icon, boolean ongoing, boolean notify, boolean vibrate) { 99 | final PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0, new Intent(ctx, MainActivity.class), 0); 100 | final NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(ctx); 101 | notificationBuilder.setContentTitle(contentTitle) 102 | .setContentText(contentText).setSmallIcon(icon) 103 | .setContentIntent(contentIntent) 104 | .setWhen(System.currentTimeMillis()); 105 | // can cancel? 106 | if (ongoing) { 107 | notificationBuilder.setOngoing(true); 108 | } else { 109 | notificationBuilder.setAutoCancel(true); 110 | } 111 | if (vibrate) { 112 | notificationBuilder.setDefaults(Notification.DEFAULT_VIBRATE); 113 | } 114 | if (notify) { 115 | notificationManager.notify(NOTIFICATION_ID, notificationBuilder.getNotification()); 116 | } 117 | } 118 | 119 | public void setContext(Context c) { 120 | ctx = c; 121 | } 122 | 123 | abstract protected void executeQueue() throws InterruptedException; 124 | 125 | abstract public void startService() throws IOException; 126 | 127 | abstract public void stopService(); 128 | 129 | public class AbstractGatewayServiceBinder extends Binder { 130 | public AbstractGatewayService getService() { 131 | return AbstractGatewayService.this; 132 | } 133 | } 134 | } 135 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/io/BluetoothManager.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.io; 2 | 3 | import java.io.IOException; 4 | import java.lang.reflect.Method; 5 | import java.util.UUID; 6 | 7 | import android.bluetooth.BluetoothDevice; 8 | import android.bluetooth.BluetoothSocket; 9 | import android.util.Log; 10 | 11 | public class BluetoothManager { 12 | 13 | private static final String TAG = BluetoothManager.class.getName(); 14 | /* 15 | * http://developer.android.com/reference/android/bluetooth/BluetoothDevice.html 16 | * #createRfcommSocketToServiceRecord(java.util.UUID) 17 | * 18 | * "Hint: If you are connecting to a Bluetooth serial board then try using the 19 | * well-known SPP UUID 00001101-0000-1000-8000-00805F9B34FB. However if you 20 | * are connecting to an Android peer then please generate your own unique 21 | * UUID." 22 | */ 23 | private static final UUID MY_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB"); 24 | 25 | /** 26 | * Instantiates a BluetoothSocket for the remote device and connects it. 27 | *

28 | * See http://stackoverflow.com/questions/18657427/ioexception-read-failed-socket-might-closed-bluetooth-on-android-4-3/18786701#18786701 29 | * 30 | * @param dev The remote device to connect to 31 | * @return The BluetoothSocket 32 | * @throws IOException 33 | */ 34 | public static BluetoothSocket connect(BluetoothDevice dev) throws IOException { 35 | BluetoothSocket sock = null; 36 | BluetoothSocket sockFallback = null; 37 | 38 | Log.d(TAG, "Starting Bluetooth connection.."); 39 | try { 40 | sock = dev.createRfcommSocketToServiceRecord(MY_UUID); 41 | sock.connect(); 42 | } catch (Exception e1) { 43 | Log.e(TAG, "There was an error while establishing Bluetooth connection. Falling back..", e1); 44 | Class clazz = sock.getRemoteDevice().getClass(); 45 | Class[] paramTypes = new Class[]{Integer.TYPE}; 46 | try { 47 | Method m = clazz.getMethod("createRfcommSocket", paramTypes); 48 | Object[] params = new Object[]{Integer.valueOf(1)}; 49 | sockFallback = (BluetoothSocket) m.invoke(sock.getRemoteDevice(), params); 50 | sockFallback.connect(); 51 | sock = sockFallback; 52 | } catch (Exception e2) { 53 | Log.e(TAG, "Couldn't fallback while establishing Bluetooth connection.", e2); 54 | throw new IOException(e2.getMessage()); 55 | } 56 | } 57 | return sock; 58 | } 59 | } -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/io/LogCSVWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.io; 2 | 3 | import android.os.Environment; 4 | import android.util.Log; 5 | 6 | import com.github.pires.obd.reader.net.ObdReading; 7 | 8 | import java.io.BufferedWriter; 9 | import java.io.File; 10 | import java.io.FileNotFoundException; 11 | import java.io.FileOutputStream; 12 | import java.io.IOException; 13 | import java.io.OutputStreamWriter; 14 | import java.util.Map; 15 | 16 | public class LogCSVWriter { 17 | 18 | private static final String TAG = LogCSVWriter.class.getName(); 19 | private static final String HEADER_CSV = "This is a logfile generated by pires.obd.reader"; 20 | private static final String[] NAMES_COLUMNS = {"TIME", "LATITUDE", "LONGITUDE", "ALTITUDE", "VEHICLE_ID", 21 | "BAROMETRIC_PRESSURE", "ENGINE_COOLANT_TEMP", "FUEL_LEVEL", "ENGINE_LOAD", "AMBIENT_AIR_TEMP", 22 | "ENGINE_RPM", "INTAKE_MANIFOLD_PRESSURE", "MAF", "Term Fuel Trim Bank 1", 23 | "FUEL_ECONOMY", "Long Term Fuel Trim Bank 2", "FUEL_TYPE", "AIR_INTAKE_TEMP", 24 | "FUEL_PRESSURE", "SPEED", "Short Term Fuel Trim Bank 2", 25 | "Short Term Fuel Trim Bank 1", "ENGINE_RUNTIME", "THROTTLE_POS", "DTC_NUMBER", 26 | "TROUBLE_CODES", "TIMING_ADVANCE", "EQUIV_RATIO"}; 27 | private static final String[] NAMES_COLUMNS_ONLY_READINGS = { 28 | "BAROMETRIC_PRESSURE", "ENGINE_COOLANT_TEMP", "FUEL_LEVEL", "ENGINE_LOAD", "AMBIENT_AIR_TEMP", 29 | "ENGINE_RPM", "INTAKE_MANIFOLD_PRESSURE", "MAF", "Term Fuel Trim Bank 1", 30 | "FUEL_ECONOMY", "Long Term Fuel Trim Bank 2", "FUEL_TYPE", "AIR_INTAKE_TEMP", 31 | "FUEL_PRESSURE", "SPEED", "Short Term Fuel Trim Bank 2", 32 | "Short Term Fuel Trim Bank 1", "ENGINE_RUNTIME", "THROTTLE_POS", "DTC_NUMBER", 33 | "TROUBLE_CODES", "TIMING_ADVANCE", "EQUIV_RATIO"}; 34 | private boolean isFirstLine; 35 | private BufferedWriter buf; 36 | 37 | public LogCSVWriter(String filename, String dirname) throws FileNotFoundException, RuntimeException { 38 | try{ 39 | File sdCard = Environment.getExternalStorageDirectory(); 40 | File dir = new File(sdCard.getAbsolutePath() + File.separator + dirname); 41 | if (!dir.exists()) dir.mkdirs(); 42 | Log.d(TAG, "Path is " + sdCard.getAbsolutePath() + File.separator + dirname); 43 | File file = new File(dir, filename); 44 | FileOutputStream fos = new FileOutputStream(file); 45 | OutputStreamWriter osw = new OutputStreamWriter(fos); 46 | this.buf = new BufferedWriter(osw); 47 | this.isFirstLine = true; 48 | Log.d(TAG, "Constructed the LogCSVWriter"); 49 | } 50 | catch (Exception e) { 51 | Log.e(TAG, "LogCSVWriter constructor failed"); 52 | } 53 | } 54 | 55 | public void closeLogCSVWriter() { 56 | try { 57 | buf.flush(); 58 | buf.close(); 59 | Log.d(TAG, "Flushed and closed"); 60 | } catch (IOException e) { 61 | e.printStackTrace(); 62 | } 63 | } 64 | 65 | public void writeLineCSV(ObdReading reading) { 66 | String crl; 67 | 68 | if (isFirstLine) { 69 | crl = HEADER_CSV + reading.toString(); 70 | addLine(crl); 71 | isFirstLine = false; 72 | 73 | // Add line with the columns 74 | crl = ""; 75 | for (String ccln : NAMES_COLUMNS) { 76 | crl += ccln + ";"; 77 | } 78 | addLine(crl.substring(0, crl.length() - 1)); // remove last ";" 79 | 80 | } else { 81 | 82 | crl = reading.getTimestamp() + ";" + 83 | reading.getLatitude() + ";" + 84 | reading.getLongitude() + ";" + 85 | reading.getAltitude() + ";" + 86 | reading.getVin() + ";"; 87 | 88 | 89 | Map read = reading.getReadings(); 90 | 91 | for (String ccln : NAMES_COLUMNS_ONLY_READINGS) { 92 | crl += read.get(ccln) + ";"; 93 | } 94 | 95 | addLine(crl.substring(0, crl.length() - 1)); 96 | } 97 | } 98 | 99 | 100 | private void addLine(String line) { 101 | if (line != null) { 102 | try { 103 | buf.write(line, 0, line.length()); 104 | buf.newLine(); 105 | Log.d(TAG, "LogCSVWriter: Wrote" + line); 106 | } catch (IOException e) { 107 | e.printStackTrace(); 108 | } 109 | } 110 | } 111 | } 112 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/io/MockObdGatewayService.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.io; 2 | 3 | import android.util.Log; 4 | 5 | import com.github.pires.obd.commands.protocol.EchoOffCommand; 6 | import com.github.pires.obd.commands.protocol.LineFeedOffCommand; 7 | import com.github.pires.obd.commands.protocol.ObdResetCommand; 8 | import com.github.pires.obd.commands.protocol.SelectProtocolCommand; 9 | import com.github.pires.obd.commands.protocol.TimeoutCommand; 10 | import com.github.pires.obd.commands.temperature.AmbientAirTemperatureCommand; 11 | import com.github.pires.obd.enums.ObdProtocols; 12 | import com.github.pires.obd.reader.activity.MainActivity; 13 | import com.github.pires.obd.reader.io.ObdCommandJob.ObdCommandJobState; 14 | 15 | import java.io.ByteArrayInputStream; 16 | import java.io.ByteArrayOutputStream; 17 | 18 | /** 19 | * This service is primarily responsible for establishing and maintaining a 20 | * permanent connection between the device where the application runs and a more 21 | * OBD Bluetooth interface. 22 | *

23 | * Secondarily, it will serve as a repository of ObdCommandJobs and at the same 24 | * time the application state-machine. 25 | */ 26 | public class MockObdGatewayService extends AbstractGatewayService { 27 | 28 | private static final String TAG = MockObdGatewayService.class.getName(); 29 | 30 | public void startService() { 31 | Log.d(TAG, "Starting " + this.getClass().getName() + " service.."); 32 | 33 | // Let's configure the connection. 34 | Log.d(TAG, "Queing jobs for connection configuration.."); 35 | queueJob(new ObdCommandJob(new ObdResetCommand())); 36 | queueJob(new ObdCommandJob(new EchoOffCommand())); 37 | 38 | /* 39 | * Will send second-time based on tests. 40 | * 41 | * TODO this can be done w/o having to queue jobs by just issuing 42 | * command.run(), command.getResult() and validate the result. 43 | */ 44 | queueJob(new ObdCommandJob(new EchoOffCommand())); 45 | queueJob(new ObdCommandJob(new LineFeedOffCommand())); 46 | queueJob(new ObdCommandJob(new TimeoutCommand(62))); 47 | 48 | // For now set protocol to AUTO 49 | queueJob(new ObdCommandJob(new SelectProtocolCommand(ObdProtocols.AUTO))); 50 | 51 | // Job for returning dummy data 52 | queueJob(new ObdCommandJob(new AmbientAirTemperatureCommand())); 53 | 54 | queueCounter = 0L; 55 | Log.d(TAG, "Initialization jobs queued."); 56 | 57 | isRunning = true; 58 | } 59 | 60 | 61 | /** 62 | * Runs the queue until the service is stopped 63 | */ 64 | protected void executeQueue() { 65 | Log.d(TAG, "Executing queue.."); 66 | while (!Thread.currentThread().isInterrupted()) { 67 | ObdCommandJob job = null; 68 | try { 69 | job = jobsQueue.take(); 70 | 71 | Log.d(TAG, "Taking job[" + job.getId() + "] from queue.."); 72 | 73 | if (job.getState().equals(ObdCommandJobState.NEW)) { 74 | Log.d(TAG, "Job state is NEW. Run it.."); 75 | job.setState(ObdCommandJobState.RUNNING); 76 | Log.d(TAG, job.getCommand().getName()); 77 | job.getCommand().run(new ByteArrayInputStream("41 00 00 00>41 00 00 00>41 00 00 00>".getBytes()), new ByteArrayOutputStream()); 78 | } else { 79 | Log.e(TAG, "Job state was not new, so it shouldn't be in queue. BUG ALERT!"); 80 | } 81 | } catch (InterruptedException i) { 82 | Thread.currentThread().interrupt(); 83 | } catch (Exception e) { 84 | e.printStackTrace(); 85 | if (job != null) { 86 | job.setState(ObdCommandJobState.EXECUTION_ERROR); 87 | } 88 | Log.e(TAG, "Failed to run command. -> " + e.getMessage()); 89 | } 90 | 91 | if (job != null) { 92 | Log.d(TAG, "Job is finished."); 93 | job.setState(ObdCommandJobState.FINISHED); 94 | final ObdCommandJob job2 = job; 95 | ((MainActivity) ctx).runOnUiThread(new Runnable() { 96 | @Override 97 | public void run() { 98 | ((MainActivity) ctx).stateUpdate(job2); 99 | } 100 | }); 101 | 102 | } 103 | } 104 | } 105 | 106 | 107 | /** 108 | * Stop OBD connection and queue processing. 109 | */ 110 | public void stopService() { 111 | Log.d(TAG, "Stopping service.."); 112 | 113 | notificationManager.cancel(NOTIFICATION_ID); 114 | jobsQueue.clear(); 115 | isRunning = false; 116 | 117 | // kill service 118 | stopSelf(); 119 | } 120 | 121 | } 122 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/io/ObdCommandJob.java: -------------------------------------------------------------------------------- 1 | /* 2 | * TODO put header 3 | */ 4 | package com.github.pires.obd.reader.io; 5 | 6 | import com.github.pires.obd.commands.ObdCommand; 7 | 8 | /** 9 | * This class represents a job that ObdGatewayService will have to execute and 10 | * maintain until the job is finished. It is, thereby, the application 11 | * representation of an ObdCommand instance plus a state that will be 12 | * interpreted and manipulated by ObdGatewayService. 13 | */ 14 | public class ObdCommandJob { 15 | 16 | private Long _id; 17 | private ObdCommand _command; 18 | private ObdCommandJobState _state; 19 | 20 | /** 21 | * Default ctor. 22 | * 23 | * @param command the ObCommand to encapsulate. 24 | */ 25 | public ObdCommandJob(ObdCommand command) { 26 | _command = command; 27 | _state = ObdCommandJobState.NEW; 28 | } 29 | 30 | public Long getId() { 31 | return _id; 32 | } 33 | 34 | public void setId(Long id) { 35 | _id = id; 36 | } 37 | 38 | public ObdCommand getCommand() { 39 | return _command; 40 | } 41 | 42 | /** 43 | * @return job current state. 44 | */ 45 | public ObdCommandJobState getState() { 46 | return _state; 47 | } 48 | 49 | /** 50 | * Sets a new job state. 51 | * 52 | * @param state the new job state. 53 | */ 54 | public void setState(ObdCommandJobState state) { 55 | _state = state; 56 | } 57 | 58 | /** 59 | * The state of the command. 60 | */ 61 | public enum ObdCommandJobState { 62 | NEW, 63 | RUNNING, 64 | FINISHED, 65 | EXECUTION_ERROR, 66 | BROKEN_PIPE, 67 | QUEUE_ERROR, 68 | NOT_SUPPORTED 69 | } 70 | 71 | } 72 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/io/ObdGatewayService.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.io; 2 | 3 | import android.bluetooth.BluetoothAdapter; 4 | import android.bluetooth.BluetoothDevice; 5 | import android.bluetooth.BluetoothSocket; 6 | import android.content.Context; 7 | import android.content.Intent; 8 | import android.content.SharedPreferences; 9 | import android.net.Uri; 10 | import android.os.Build; 11 | import android.os.Environment; 12 | import android.util.Log; 13 | import android.widget.Toast; 14 | 15 | import com.github.pires.obd.commands.protocol.EchoOffCommand; 16 | import com.github.pires.obd.commands.protocol.LineFeedOffCommand; 17 | import com.github.pires.obd.commands.protocol.ObdResetCommand; 18 | import com.github.pires.obd.commands.protocol.SelectProtocolCommand; 19 | import com.github.pires.obd.commands.protocol.TimeoutCommand; 20 | import com.github.pires.obd.commands.temperature.AmbientAirTemperatureCommand; 21 | import com.github.pires.obd.enums.ObdProtocols; 22 | import com.github.pires.obd.exceptions.UnsupportedCommandException; 23 | import com.github.pires.obd.reader.R; 24 | import com.github.pires.obd.reader.activity.ConfigActivity; 25 | import com.github.pires.obd.reader.activity.MainActivity; 26 | import com.github.pires.obd.reader.io.ObdCommandJob.ObdCommandJobState; 27 | import com.google.inject.Inject; 28 | 29 | import java.io.File; 30 | import java.io.IOException; 31 | 32 | /** 33 | * This service is primarily responsible for establishing and maintaining a 34 | * permanent connection between the device where the application runs and a more 35 | * OBD Bluetooth interface. 36 | *

37 | * Secondarily, it will serve as a repository of ObdCommandJobs and at the same 38 | * time the application state-machine. 39 | */ 40 | public class ObdGatewayService extends AbstractGatewayService { 41 | 42 | private static final String TAG = ObdGatewayService.class.getName(); 43 | @Inject 44 | SharedPreferences prefs; 45 | 46 | private BluetoothDevice dev = null; 47 | private BluetoothSocket sock = null; 48 | 49 | public void startService() throws IOException { 50 | Log.d(TAG, "Starting service.."); 51 | 52 | // get the remote Bluetooth device 53 | final String remoteDevice = prefs.getString(ConfigActivity.BLUETOOTH_LIST_KEY, null); 54 | if (remoteDevice == null || "".equals(remoteDevice)) { 55 | Toast.makeText(ctx, getString(R.string.text_bluetooth_nodevice), Toast.LENGTH_LONG).show(); 56 | 57 | // log error 58 | Log.e(TAG, "No Bluetooth device has been selected."); 59 | 60 | // TODO kill this service gracefully 61 | stopService(); 62 | throw new IOException(); 63 | } else { 64 | 65 | final BluetoothAdapter btAdapter = BluetoothAdapter.getDefaultAdapter(); 66 | dev = btAdapter.getRemoteDevice(remoteDevice); 67 | 68 | 69 | /* 70 | * Establish Bluetooth connection 71 | * 72 | * Because discovery is a heavyweight procedure for the Bluetooth adapter, 73 | * this method should always be called before attempting to connect to a 74 | * remote device with connect(). Discovery is not managed by the Activity, 75 | * but is run as a system service, so an application should always call 76 | * cancel discovery even if it did not directly request a discovery, just to 77 | * be sure. If Bluetooth state is not STATE_ON, this API will return false. 78 | * 79 | * see 80 | * http://developer.android.com/reference/android/bluetooth/BluetoothAdapter 81 | * .html#cancelDiscovery() 82 | */ 83 | Log.d(TAG, "Stopping Bluetooth discovery."); 84 | btAdapter.cancelDiscovery(); 85 | 86 | showNotification(getString(R.string.notification_action), getString(R.string.service_starting), R.drawable.ic_btcar, true, true, false); 87 | 88 | try { 89 | startObdConnection(); 90 | } catch (Exception e) { 91 | Log.e( 92 | TAG, 93 | "There was an error while establishing connection. -> " 94 | + e.getMessage() 95 | ); 96 | 97 | // in case of failure, stop this service. 98 | stopService(); 99 | throw new IOException(); 100 | } 101 | showNotification(getString(R.string.notification_action), getString(R.string.service_started), R.drawable.ic_btcar, true, true, false); 102 | } 103 | } 104 | 105 | /** 106 | * Start and configure the connection to the OBD interface. 107 | *

108 | * See http://stackoverflow.com/questions/18657427/ioexception-read-failed-socket-might-closed-bluetooth-on-android-4-3/18786701#18786701 109 | * 110 | * @throws IOException 111 | */ 112 | private void startObdConnection() throws IOException { 113 | Log.d(TAG, "Starting OBD connection.."); 114 | isRunning = true; 115 | try { 116 | sock = BluetoothManager.connect(dev); 117 | } catch (Exception e2) { 118 | Log.e(TAG, "There was an error while establishing Bluetooth connection. Stopping app..", e2); 119 | stopService(); 120 | throw new IOException(); 121 | } 122 | 123 | // Let's configure the connection. 124 | Log.d(TAG, "Queueing jobs for connection configuration.."); 125 | queueJob(new ObdCommandJob(new ObdResetCommand())); 126 | 127 | //Below is to give the adapter enough time to reset before sending the commands, otherwise the first startup commands could be ignored. 128 | try { Thread.sleep(500); } catch (InterruptedException e) { e.printStackTrace(); } 129 | 130 | queueJob(new ObdCommandJob(new EchoOffCommand())); 131 | 132 | /* 133 | * Will send second-time based on tests. 134 | * 135 | * TODO this can be done w/o having to queue jobs by just issuing 136 | * command.run(), command.getResult() and validate the result. 137 | */ 138 | queueJob(new ObdCommandJob(new EchoOffCommand())); 139 | queueJob(new ObdCommandJob(new LineFeedOffCommand())); 140 | queueJob(new ObdCommandJob(new TimeoutCommand(62))); 141 | 142 | // Get protocol from preferences 143 | final String protocol = prefs.getString(ConfigActivity.PROTOCOLS_LIST_KEY, "AUTO"); 144 | queueJob(new ObdCommandJob(new SelectProtocolCommand(ObdProtocols.valueOf(protocol)))); 145 | 146 | // Job for returning dummy data 147 | queueJob(new ObdCommandJob(new AmbientAirTemperatureCommand())); 148 | 149 | queueCounter = 0L; 150 | Log.d(TAG, "Initialization jobs queued."); 151 | 152 | 153 | } 154 | 155 | /** 156 | * This method will add a job to the queue while setting its ID to the 157 | * internal queue counter. 158 | * 159 | * @param job the job to queue. 160 | */ 161 | @Override 162 | public void queueJob(ObdCommandJob job) { 163 | // This is a good place to enforce the imperial units option 164 | job.getCommand().useImperialUnits(prefs.getBoolean(ConfigActivity.IMPERIAL_UNITS_KEY, false)); 165 | 166 | // Now we can pass it along 167 | super.queueJob(job); 168 | } 169 | 170 | /** 171 | * Runs the queue until the service is stopped 172 | */ 173 | protected void executeQueue() throws InterruptedException { 174 | Log.d(TAG, "Executing queue.."); 175 | while (!Thread.currentThread().isInterrupted()) { 176 | ObdCommandJob job = null; 177 | try { 178 | job = jobsQueue.take(); 179 | 180 | // log job 181 | Log.d(TAG, "Taking job[" + job.getId() + "] from queue.."); 182 | 183 | if (job.getState().equals(ObdCommandJobState.NEW)) { 184 | Log.d(TAG, "Job state is NEW. Run it.."); 185 | job.setState(ObdCommandJobState.RUNNING); 186 | if (sock.isConnected()) { 187 | job.getCommand().run(sock.getInputStream(), sock.getOutputStream()); 188 | } else { 189 | job.setState(ObdCommandJobState.EXECUTION_ERROR); 190 | Log.e(TAG, "Can't run command on a closed socket."); 191 | } 192 | } else 193 | // log not new job 194 | Log.e(TAG, 195 | "Job state was not new, so it shouldn't be in queue. BUG ALERT!"); 196 | } catch (InterruptedException i) { 197 | Thread.currentThread().interrupt(); 198 | } catch (UnsupportedCommandException u) { 199 | if (job != null) { 200 | job.setState(ObdCommandJobState.NOT_SUPPORTED); 201 | } 202 | Log.d(TAG, "Command not supported. -> " + u.getMessage()); 203 | } catch (IOException io) { 204 | if (job != null) { 205 | if(io.getMessage().contains("Broken pipe")) 206 | job.setState(ObdCommandJobState.BROKEN_PIPE); 207 | else 208 | job.setState(ObdCommandJobState.EXECUTION_ERROR); 209 | } 210 | Log.e(TAG, "IO error. -> " + io.getMessage()); 211 | } catch (Exception e) { 212 | if (job != null) { 213 | job.setState(ObdCommandJobState.EXECUTION_ERROR); 214 | } 215 | Log.e(TAG, "Failed to run command. -> " + e.getMessage()); 216 | } 217 | 218 | if (job != null) { 219 | final ObdCommandJob job2 = job; 220 | ((MainActivity) ctx).runOnUiThread(new Runnable() { 221 | @Override 222 | public void run() { 223 | ((MainActivity) ctx).stateUpdate(job2); 224 | } 225 | }); 226 | } 227 | } 228 | } 229 | 230 | /** 231 | * Stop OBD connection and queue processing. 232 | */ 233 | public void stopService() { 234 | Log.d(TAG, "Stopping service.."); 235 | 236 | notificationManager.cancel(NOTIFICATION_ID); 237 | jobsQueue.clear(); 238 | isRunning = false; 239 | 240 | if (sock != null) 241 | // close socket 242 | try { 243 | sock.close(); 244 | } catch (IOException e) { 245 | Log.e(TAG, e.getMessage()); 246 | } 247 | 248 | // kill service 249 | stopSelf(); 250 | } 251 | 252 | public boolean isRunning() { 253 | return isRunning; 254 | } 255 | 256 | public static void saveLogcatToFile(Context context, String devemail) { 257 | Intent emailIntent = new Intent(Intent.ACTION_SEND); 258 | emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 259 | emailIntent.setType("text/plain"); 260 | emailIntent.putExtra(Intent.EXTRA_EMAIL, new String[]{devemail}); 261 | emailIntent.putExtra(Intent.EXTRA_SUBJECT, "OBD2 Reader Debug Logs"); 262 | 263 | StringBuilder sb = new StringBuilder(); 264 | sb.append("\nManufacturer: ").append(Build.MANUFACTURER); 265 | sb.append("\nModel: ").append(Build.MODEL); 266 | sb.append("\nRelease: ").append(Build.VERSION.RELEASE); 267 | 268 | emailIntent.putExtra(Intent.EXTRA_TEXT, sb.toString()); 269 | 270 | String fileName = "OBDReader_logcat_" + System.currentTimeMillis() + ".txt"; 271 | File sdCard = Environment.getExternalStorageDirectory(); 272 | File dir = new File(sdCard.getAbsolutePath() + File.separator + "OBD2Logs"); 273 | if (dir.mkdirs()) { 274 | File outputFile = new File(dir, fileName); 275 | Uri uri = Uri.fromFile(outputFile); 276 | emailIntent.putExtra(Intent.EXTRA_STREAM, uri); 277 | 278 | Log.d("savingFile", "Going to save logcat to " + outputFile); 279 | //emailIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK); 280 | context.startActivity(Intent.createChooser(emailIntent, "Pick an Email provider").addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)); 281 | 282 | try { 283 | @SuppressWarnings("unused") 284 | Process process = Runtime.getRuntime().exec("logcat -f " + outputFile.getAbsolutePath()); 285 | } catch (IOException e) { 286 | e.printStackTrace(); 287 | } 288 | } 289 | } 290 | 291 | } 292 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/io/ObdProgressListener.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.io; 2 | 3 | public interface ObdProgressListener { 4 | 5 | void stateUpdate(final ObdCommandJob job); 6 | 7 | } -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/net/ObdReading.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 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, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | 14 | package com.github.pires.obd.reader.net; 15 | 16 | import java.util.HashMap; 17 | import java.util.Map; 18 | 19 | /** 20 | * DTO for OBD readings. 21 | */ 22 | public class ObdReading { 23 | private double latitude, longitude, altitude; 24 | private long timestamp; 25 | private String vin; // vehicle id 26 | private Map readings; 27 | 28 | public ObdReading() { 29 | readings = new HashMap<>(); 30 | } 31 | 32 | public ObdReading(double latitude, double longitude, double altitude, long timestamp, 33 | String vin, Map readings) { 34 | this.latitude = latitude; 35 | this.longitude = longitude; 36 | this.altitude = altitude; 37 | this.timestamp = timestamp; 38 | this.vin = vin; 39 | this.readings = readings; 40 | } 41 | 42 | public double getLatitude() { 43 | return latitude; 44 | } 45 | 46 | public void setLatitude(double latitude) { 47 | this.latitude = latitude; 48 | } 49 | 50 | public double getAltitude() { 51 | return altitude; 52 | } 53 | 54 | public void setAltitude(double altitude) { 55 | this.altitude = altitude; 56 | } 57 | 58 | public double getLongitude() { 59 | return longitude; 60 | } 61 | 62 | public void setLongitude(double longitude) { 63 | this.longitude = longitude; 64 | } 65 | 66 | public long getTimestamp() { 67 | return timestamp; 68 | } 69 | 70 | public void setTimestamp(long timestamp) { 71 | this.timestamp = timestamp; 72 | } 73 | 74 | public String getVin() { 75 | return vin; 76 | } 77 | 78 | public void setVin(String vehicleid) { 79 | this.vin = vehicleid; 80 | } 81 | 82 | public Map getReadings() { 83 | return readings; 84 | } 85 | 86 | public void setReadings(Map readings) { 87 | this.readings = readings; 88 | } 89 | 90 | public String toString() { 91 | 92 | return "lat:" + latitude + ";" + 93 | "long:" + longitude + ";" + 94 | "alt:" + altitude + ";" + 95 | "vin:" + vin + ";" + 96 | "readings:" + readings.toString().substring(10).replace("}", "").replace(",", ";"); 97 | } 98 | 99 | } 100 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/net/ObdService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Licensed under the Apache License, Version 2.0 (the "License"); you may not 3 | * use this file except in compliance with the License. You may obtain a copy of 4 | * the License at 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, WITHOUT 9 | * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the 10 | * License for the specific language governing permissions and limitations under 11 | * the License. 12 | */ 13 | 14 | package com.github.pires.obd.reader.net; 15 | 16 | import retrofit.client.Response; 17 | import retrofit.http.Body; 18 | import retrofit.http.POST; 19 | 20 | /** 21 | * Definition of REST service available in OBD Server. 22 | */ 23 | public interface ObdService { 24 | 25 | @POST("/") 26 | Response uploadReading(@Body ObdReading reading); 27 | 28 | } -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/trips/TripListAdapter.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.trips; 2 | 3 | import android.app.Activity; 4 | import android.view.LayoutInflater; 5 | import android.view.View; 6 | import android.view.ViewGroup; 7 | import android.widget.ArrayAdapter; 8 | import android.widget.TextView; 9 | 10 | import com.github.pires.obd.reader.R; 11 | 12 | import java.util.Date; 13 | import java.util.List; 14 | 15 | public class TripListAdapter extends ArrayAdapter { 16 | /// the Android Activity owning the ListView 17 | private final Activity activity; 18 | 19 | /// a list of trip records for display 20 | private final List records; 21 | 22 | /** 23 | * DESCRIPTION: 24 | * Constructs an instance of TripListAdapter. 25 | * 26 | * @param activity - the Android Activity instance that owns the ListView. 27 | * @param records - the List of TripRecord instances for display in the ListView. 28 | */ 29 | public TripListAdapter(Activity activity, List records) { 30 | super(activity, R.layout.row_trip_list, records); 31 | this.activity = activity; 32 | this.records = records; 33 | } 34 | 35 | /** 36 | * DESCRIPTION: 37 | * Constructs and populates a View for display of the TripRecord at the index 38 | * of the List specified by the position parameter. 39 | * 40 | * @see android.widget.ArrayAdapter#getView(int, android.view.View, android.view.ViewGroup) 41 | */ 42 | @Override 43 | public View getView(int position, View view, ViewGroup parent) { 44 | 45 | // create a view for the row if it doesn't already exist 46 | if (view == null) { 47 | LayoutInflater inflater = activity.getLayoutInflater(); 48 | view = inflater.inflate(R.layout.row_trip_list, null); 49 | } 50 | 51 | // get widgets from the view 52 | TextView startDate = (TextView) view.findViewById(R.id.startDate); 53 | TextView columnDuration = (TextView) view.findViewById(R.id.columnDuration); 54 | TextView rowEngine = (TextView) view.findViewById(R.id.rowEngine); 55 | TextView rowOther = (TextView) view.findViewById(R.id.rowOther); 56 | 57 | // populate row widgets from record data 58 | TripRecord record = records.get(position); 59 | 60 | // date 61 | startDate.setText(record.getStartDateString()); 62 | columnDuration.setText(calcDiffTime(record.getStartDate(), record.getEndDate())); 63 | 64 | String rpmMax = String.valueOf(record.getEngineRpmMax()); 65 | 66 | String engineRuntime = record.getEngineRuntime(); 67 | if (engineRuntime == null) 68 | engineRuntime = "None"; 69 | rowEngine.setText("Engine Runtime: " + engineRuntime + "\tMax RPM: " + rpmMax); 70 | 71 | rowOther.setText("Max speed: " + String.valueOf(record.getSpeedMax())); 72 | return view; 73 | } 74 | 75 | private String calcDiffTime(Date start, Date end) { 76 | long diff = end.getTime() - start.getTime(); 77 | long diffSeconds = diff / 1000 % 60; 78 | long diffMinutes = diff / (60 * 1000) % 60; 79 | long diffHours = diff / (60 * 60 * 1000) % 24; 80 | long diffDays = diff / (24 * 60 * 60 * 1000); 81 | 82 | StringBuffer res = new StringBuffer(); 83 | 84 | if (diffDays > 0) 85 | res.append(diffDays + "d"); 86 | 87 | if (diffHours > 0) { 88 | if (res.length() > 0) { 89 | res.append(" "); 90 | } 91 | res.append(diffHours + "h"); 92 | } 93 | 94 | if (diffMinutes > 0) { 95 | if (res.length() > 0) { 96 | res.append(" "); 97 | } 98 | res.append(diffMinutes + "m"); 99 | } 100 | 101 | if (diffSeconds > 0) { 102 | if (res.length() > 0) { 103 | res.append(" "); 104 | } 105 | 106 | res.append(diffSeconds + "s"); 107 | } 108 | return res.toString(); 109 | } 110 | 111 | /** 112 | * DESCRIPTION: 113 | * Called by parent when the underlying data set changes. 114 | * 115 | * @see android.widget.ArrayAdapter#notifyDataSetChanged() 116 | */ 117 | @Override 118 | public void notifyDataSetChanged() { 119 | 120 | // configuration may have changed - get current settings 121 | //todo 122 | //getSettings(); 123 | 124 | super.notifyDataSetChanged(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/trips/TripLog.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.trips; 2 | 3 | import android.content.ContentValues; 4 | import android.content.Context; 5 | import android.database.Cursor; 6 | import android.database.SQLException; 7 | import android.database.sqlite.SQLiteConstraintException; 8 | import android.database.sqlite.SQLiteDatabase; 9 | import android.util.Log; 10 | 11 | import java.util.ArrayList; 12 | import java.util.Date; 13 | import java.util.List; 14 | 15 | /** 16 | * Some code taken from https://github.com/wdkapps/FillUp 17 | */ 18 | public class TripLog { 19 | 20 | /// the database version number 21 | public static final int DATABASE_VERSION = 1; 22 | /// the name of the database 23 | public static final String DATABASE_NAME = "tripslog.db"; 24 | /// a tag string for debug logging (the name of this class) 25 | private static final String TAG = TripLog.class.getName(); 26 | /// database table names 27 | private static final String RECORDS_TABLE = "Records"; 28 | /// SQL commands to delete the database 29 | public static final String[] DATABASE_DELETE = new String[]{ 30 | "drop table if exists " + RECORDS_TABLE + ";", 31 | }; 32 | /// column names for RECORDS_TABLE 33 | private static final String RECORD_ID = "id"; 34 | private static final String RECORD_START_DATE = "startDate"; 35 | private static final String RECORD_END_DATE = "endDate"; 36 | private static final String RECORD_RPM_MAX = "rmpMax"; 37 | private static final String RECORD_SPEED_MAX = "speedMax"; 38 | private static final String RECORD_ENGINE_RUNTIME = "engineRuntime"; 39 | /// SQL commands to create the database 40 | public static final String[] DATABASE_CREATE = new String[]{ 41 | "create table " + RECORDS_TABLE + " ( " + 42 | RECORD_ID + " integer primary key autoincrement, " + 43 | RECORD_START_DATE + " integer not null, " + 44 | RECORD_END_DATE + " integer, " + 45 | RECORD_SPEED_MAX + " integer, " + 46 | RECORD_RPM_MAX + " integer, " + 47 | RECORD_ENGINE_RUNTIME + " text" + 48 | ");" 49 | }; 50 | /// array of all column names for RECORDS_TABLE 51 | private static final String[] RECORDS_TABLE_COLUMNS = new String[]{ 52 | RECORD_ID, 53 | RECORD_START_DATE, 54 | RECORD_END_DATE, 55 | RECORD_SPEED_MAX, 56 | RECORD_ENGINE_RUNTIME, 57 | RECORD_RPM_MAX 58 | }; 59 | /// singleton instance 60 | private static TripLog instance; 61 | /// context of the instance creator 62 | private final Context context; 63 | /// a helper instance used to open and close the database 64 | private final TripLogOpenHelper helper; 65 | /// the database 66 | private final SQLiteDatabase db; 67 | 68 | private TripLog(Context context) { 69 | this.context = context; 70 | this.helper = new TripLogOpenHelper(this.context); 71 | this.db = helper.getWritableDatabase(); 72 | } 73 | 74 | /** 75 | * DESCRIPTION: 76 | * Returns a single instance, creating it if necessary. 77 | * 78 | * @return GasLog - singleton instance. 79 | */ 80 | public static TripLog getInstance(Context context) { 81 | if (instance == null) { 82 | instance = new TripLog(context); 83 | } 84 | return instance; 85 | } 86 | 87 | /** 88 | * DESCRIPTION: 89 | * Convenience method to test assertion. 90 | * 91 | * @param assertion - an asserted boolean condition. 92 | * @param tag - a tag String identifying the calling method. 93 | * @param msg - an error message to display/log. 94 | * @throws RuntimeException if the assertion is false 95 | */ 96 | private void ASSERT(boolean assertion, String tag, String msg) { 97 | if (!assertion) { 98 | String assert_msg = "ASSERT failed: " + msg; 99 | Log.e(tag, assert_msg); 100 | throw new RuntimeException(assert_msg); 101 | } 102 | } 103 | 104 | public TripRecord startTrip() { 105 | final String tag = TAG + ".createRecord()"; 106 | 107 | try { 108 | TripRecord record = new TripRecord(); 109 | long rowID = db.insertOrThrow(RECORDS_TABLE, null, getContentValues(record)); 110 | record.setID((int) rowID); 111 | return record; 112 | } catch (SQLiteConstraintException e) { 113 | Log.e(tag, "SQLiteConstraintException: " + e.getMessage()); 114 | } catch (SQLException e) { 115 | Log.e(tag, "SQLException: " + e.getMessage()); 116 | } 117 | return null; 118 | } 119 | 120 | /** 121 | * DESCRIPTION: 122 | * Updates a trip record in the log. 123 | * 124 | * @param record - the TripRecord to update. 125 | * @return boolean flag indicating success/failure (true=success) 126 | */ 127 | public boolean updateRecord(TripRecord record) { 128 | final String tag = TAG + ".updateRecord()"; 129 | ASSERT((record.getID() != null), tag, "record id cannot be null"); 130 | boolean success = false; 131 | try { 132 | ContentValues values = getContentValues(record); 133 | values.remove(RECORD_ID); 134 | String whereClause = RECORD_ID + "=" + record.getID(); 135 | int count = db.update(RECORDS_TABLE, values, whereClause, null); 136 | success = (count > 0); 137 | } catch (SQLiteConstraintException e) { 138 | Log.e(tag, "SQLiteConstraintException: " + e.getMessage()); 139 | } catch (SQLException e) { 140 | Log.e(tag, "SQLException: " + e.getMessage()); 141 | } 142 | return success; 143 | } 144 | 145 | /** 146 | * DESCRIPTION: 147 | * Convenience method to convert a TripRecord instance to a set of key/value 148 | * pairs in a ContentValues instance utilized by SQLite access methods. 149 | * 150 | * @param record - the GasRecord to convert. 151 | * @return a ContentValues instance representing the specified GasRecord. 152 | */ 153 | private ContentValues getContentValues(TripRecord record) { 154 | ContentValues values = new ContentValues(); 155 | values.put(RECORD_ID, record.getID()); 156 | values.put(RECORD_START_DATE, record.getStartDate().getTime()); 157 | if (record.getEndDate() != null) 158 | values.put(RECORD_END_DATE, record.getEndDate().getTime()); 159 | values.put(RECORD_RPM_MAX, record.getEngineRpmMax()); 160 | values.put(RECORD_SPEED_MAX, record.getSpeedMax()); 161 | if (record.getEngineRuntime() != null) 162 | values.put(RECORD_ENGINE_RUNTIME, record.getEngineRuntime()); 163 | return values; 164 | } 165 | 166 | private void update() { 167 | String sql = "ALTER TABLE " + RECORDS_TABLE + " ADD COLUMN " + RECORD_ENGINE_RUNTIME + " integer;"; 168 | db.execSQL(sql); 169 | } 170 | 171 | public List readAllRecords() { 172 | 173 | //update(); 174 | 175 | final String tag = TAG + ".readAllRecords()"; 176 | List list = new ArrayList<>(); 177 | Cursor cursor = null; 178 | 179 | try { 180 | String orderBy = RECORD_START_DATE; 181 | cursor = db.query( 182 | RECORDS_TABLE, 183 | RECORDS_TABLE_COLUMNS, 184 | null, 185 | null, null, null, 186 | orderBy, 187 | null 188 | ); 189 | 190 | // create a list of TripRecords from the data 191 | if (cursor != null) { 192 | if (cursor.moveToFirst()) { 193 | do { 194 | TripRecord record = getRecordFromCursor(cursor); 195 | list.add(record); 196 | } while (cursor.moveToNext()); 197 | } 198 | } 199 | 200 | } catch (SQLException e) { 201 | Log.e(tag, "SQLException: " + e.getMessage()); 202 | list.clear(); 203 | } finally { 204 | if (cursor != null) cursor.close(); 205 | } 206 | return list; 207 | } 208 | 209 | /** 210 | * DESCRIPTION: 211 | * Deletes a specified trip record from the log. 212 | * 213 | * @param id - the TripRecord to delete. 214 | * @return boolean flag indicating success/failure (true=success) 215 | */ 216 | public boolean deleteTrip(long id) { 217 | 218 | final String tag = TAG + ".deleteRecord()"; 219 | 220 | boolean success = false; 221 | 222 | try { 223 | String whereClause = RECORD_ID + "=" + id; 224 | String[] whereArgs = null; 225 | int count = db.delete(RECORDS_TABLE, whereClause, whereArgs); 226 | success = (count == 1); 227 | } catch (SQLException e) { 228 | Log.e(tag, "SQLException: " + e.getMessage()); 229 | } 230 | 231 | return success; 232 | } 233 | 234 | /** 235 | * DESCRIPTION: 236 | * Convenience method to create a TripRecord instance from values read 237 | * from the database. 238 | * 239 | * @param c - a Cursor containing results of a database query. 240 | * @return a GasRecord instance (null if no data). 241 | */ 242 | private TripRecord getRecordFromCursor(Cursor c) { 243 | final String tag = TAG + ".getRecordFromCursor()"; 244 | TripRecord record = null; 245 | if (c != null) { 246 | record = new TripRecord(); 247 | int id = c.getInt(c.getColumnIndex(RECORD_ID)); 248 | long startDate = c.getLong(c.getColumnIndex(RECORD_START_DATE)); 249 | long endTime = c.getLong(c.getColumnIndex(RECORD_END_DATE)); 250 | int engineRpmMax = c.getInt(c.getColumnIndex(RECORD_RPM_MAX)); 251 | int speedMax = c.getInt(c.getColumnIndex(RECORD_SPEED_MAX)); 252 | record.setID(id); 253 | record.setStartDate(new Date(startDate)); 254 | record.setEndDate(new Date(endTime)); 255 | record.setEngineRpmMax(engineRpmMax); 256 | record.setSpeedMax(speedMax); 257 | if (!c.isNull(c.getColumnIndex(RECORD_ENGINE_RUNTIME))) 258 | record.setEngineRuntime(c.getString(c.getColumnIndex(RECORD_ENGINE_RUNTIME))); 259 | } 260 | return record; 261 | } 262 | } 263 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/trips/TripLogOpenHelper.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.trips; 2 | 3 | import android.content.Context; 4 | import android.database.sqlite.SQLiteDatabase; 5 | import android.database.sqlite.SQLiteOpenHelper; 6 | import android.util.Log; 7 | 8 | /** 9 | * Created by elagin on 13.03.15. 10 | */ 11 | public class TripLogOpenHelper extends SQLiteOpenHelper { 12 | /// tag for logging 13 | private static final String TAG = TripLogOpenHelper.class.getName(); 14 | 15 | public TripLogOpenHelper(Context context) { 16 | super(context, TripLog.DATABASE_NAME, null, TripLog.DATABASE_VERSION); 17 | } 18 | 19 | @Override 20 | public void onCreate(SQLiteDatabase db) { 21 | execSQL(db, TripLog.DATABASE_CREATE); 22 | } 23 | 24 | @Override 25 | public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { 26 | 27 | } 28 | 29 | private void execSQL(SQLiteDatabase db, String[] statements) { 30 | final String tag = TAG + ".execSQL()"; 31 | for (String sql : statements) { 32 | Log.d(tag, sql); 33 | db.execSQL(sql); 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/github/pires/obd/reader/trips/TripRecord.java: -------------------------------------------------------------------------------- 1 | package com.github.pires.obd.reader.trips; 2 | 3 | import java.text.SimpleDateFormat; 4 | import java.util.Date; 5 | import java.util.Locale; 6 | 7 | public class TripRecord { 8 | 9 | /// record id for database use (primary key) 10 | private Integer id; 11 | 12 | /// the date the trip started 13 | private Date startDate; 14 | 15 | /// the date the trip ended 16 | private Date endDate; 17 | 18 | private Integer engineRpmMax = 0; 19 | 20 | private Integer speed = 0; 21 | 22 | private String engineRuntime; 23 | 24 | public TripRecord() { 25 | startDate = new Date(); 26 | } 27 | 28 | public Integer getSpeedMax() { 29 | return speed; 30 | } 31 | 32 | public void setSpeedMax(int value) { 33 | if (this.speed < value) 34 | speed = value; 35 | } 36 | 37 | public void setSpeedMax(String value) { 38 | setSpeedMax(Integer.parseInt(value)); 39 | } 40 | 41 | /** 42 | * DESCRIPTION: 43 | * Getter method for the id attribute. 44 | * 45 | * @return Integer - the id value. 46 | */ 47 | public Integer getID() { 48 | return id; 49 | } 50 | 51 | /** 52 | * DESCRIPTION: 53 | * Setter method for the id attribute. 54 | * 55 | * @param id - the Integer id value. 56 | */ 57 | public void setID(Integer id) { 58 | this.id = id; 59 | } 60 | 61 | /** 62 | * DESCRIPTION: 63 | * Getter method for the date attribute. 64 | * 65 | * @return Date - the start date value 66 | */ 67 | public Date getStartDate() { 68 | return startDate; 69 | } 70 | 71 | /** 72 | * DESCRIPTION: 73 | * Setter method for the date attribute. 74 | * 75 | * @param date - the Date value. 76 | */ 77 | public void setStartDate(Date date) { 78 | this.startDate = date; 79 | } 80 | 81 | /** 82 | * DESCRIPTION: 83 | * Getter method for the date attribute. 84 | * 85 | * @return Date - the end date value 86 | */ 87 | public Date getEndDate() { 88 | return endDate; 89 | } 90 | 91 | /** 92 | * DESCRIPTION: 93 | * Setter method for the date attribute. 94 | * 95 | * @param date - the Date value. 96 | */ 97 | public void setEndDate(Date date) { 98 | this.endDate = date; 99 | } 100 | 101 | public Integer getEngineRpmMax() { 102 | return this.engineRpmMax; 103 | } 104 | 105 | public void setEngineRpmMax(Integer value) { 106 | if (this.engineRpmMax < value) { 107 | this.engineRpmMax = value; 108 | } 109 | } 110 | 111 | public void setEngineRpmMax(String value) { 112 | setEngineRpmMax(Integer.parseInt(value)); 113 | } 114 | 115 | /** 116 | * DESCRIPTION: 117 | * Getter method for the date attribute as a String value. 118 | * 119 | * @return String - the date value (MM/dd/yyyy). 120 | */ 121 | public String getStartDateString() { 122 | //todo 123 | //return dateFormatter.format(this.startDate); 124 | SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.US); 125 | return sdf.format(this.startDate); 126 | } 127 | 128 | public String getEngineRuntime() { 129 | return engineRuntime; 130 | } 131 | 132 | public void setEngineRuntime(String value) { 133 | if (!value.equals("00:00:00")) { 134 | this.engineRuntime = value; 135 | } 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/ic_btcar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-hdpi/ic_btcar.png -------------------------------------------------------------------------------- /src/main/res/drawable-hdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-hdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/ic_btcar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-mdpi/ic_btcar.png -------------------------------------------------------------------------------- /src/main/res/drawable-mdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-mdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_btcar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-xhdpi/ic_btcar.png -------------------------------------------------------------------------------- /src/main/res/drawable-xhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-xhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/drawable-xxhdpi/ic_btcar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-xxhdpi/ic_btcar.png -------------------------------------------------------------------------------- /src/main/res/drawable-xxhdpi/ic_launcher.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pires/android-obd-reader/5333b2585372286e5ebc763997ac2060358481c8/src/main/res/drawable-xxhdpi/ic_launcher.png -------------------------------------------------------------------------------- /src/main/res/layout/activity_trips_list.xml: -------------------------------------------------------------------------------- 1 | 10 | 11 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /src/main/res/layout/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 9 | 10 | 13 | 14 | 21 | 22 | 30 | 31 | 32 | 35 | 36 | 43 | 44 | 51 | 52 | 59 | 60 | 61 | 70 | 71 | 76 | 77 | 78 | 81 | 82 | 89 | 90 | 97 | 98 | 105 | 106 | 107 | 108 | 109 | 112 | 113 | 120 | 121 | 128 | 129 | 136 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /src/main/res/layout/row_trip_list.xml: -------------------------------------------------------------------------------- 1 | 2 | 8 | 9 | 13 | 14 | 19 | 20 | 25 | 26 | 27 | 28 | 35 | 36 | 43 | 44 | -------------------------------------------------------------------------------- /src/main/res/layout/trouble_codes.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | 13 | 14 | -------------------------------------------------------------------------------- /src/main/res/menu/context_trip_list.xml: -------------------------------------------------------------------------------- 1 | 2 |

3 | 6 | -------------------------------------------------------------------------------- /src/main/res/menu/main.xml: -------------------------------------------------------------------------------- 1 | 2 | 7 | 8 | -------------------------------------------------------------------------------- /src/main/res/menu/menu_trips_list.xml: -------------------------------------------------------------------------------- 1 | 5 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/res/menu/trouble_codes.xml: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /src/main/res/values-de/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Einstellungen 4 | Berühre den Bildschirm um den OBD-Reader zu öffnen. 5 | Wähle einen Befehl 6 | Starte OBD Verbindung.. 7 | OBD-Verbindung hergestellt 8 | OBD-Verbindung gestoppt. 9 | Berühre den Bildschirm um den OBD-Reader zu öffnen. 10 | Fehlercodes 11 | Daten Upload 12 | Liste der Trips 13 | Trips 14 | Löschen 15 | Lade… 16 | Lade App, bitte warten 17 | Starte Live Daten 18 | Stope Live Daten 19 | Einstellungen 20 | Hole DTC 21 | OBD Einstellungen 22 | OBD Kommandos 23 | Sorry, dein Gerät unterstützt kein Bluetooth 24 | Bluetooth ist nicht aktivert. Bitte aktiveren (Nutze Mock service) 25 | Kein Bluetooth Adapter ausgewählt. 26 | Kann nicht zum Bluetooth Adapter verbinden! 27 | Kein Kompass vorhanden? 28 | Sorry, dein Gerät unterstützt kein GPS oder es ist ausgeschaltet! 29 | Sorry, der Trip wird nicht gespeichert. 30 | OBD Kommando Fehler! 31 | keine DTC gespeichert 32 | nicht verfügbar 33 | nicht verfügbar 34 | bereit 35 | verbindet… 36 | verbunden 37 | Verbindung fehlgeschlagen 38 | Fix erhalten 39 | Fix not erhalten 40 | gestartet 41 | gestoppt 42 | bereit 43 | nicht verfügbar 44 | nicht benutzt 45 | OK 46 | erhalte Daten… 47 | nicht verbunden 48 | Aktiviere Datenupload per http 49 | Deaktiviere Datenupload per http 50 | Aktiviere Datenupload 51 | Eine POST URL, die Echtzeitdaten akzeptiert 52 | Upload URL 53 | Wähle OBD Kommandos, die in Echtzeit angezeigt werden sollen 54 | OBD Kommandos 55 | Liste gekoppelter Bluetoothgeräte 56 | Bluetoothgeräte 57 | Schalte Bluetooth an 58 | Schalte Bluetooth aus 59 | Verwende Bluetooth 60 | Wird für die Verbrauchsberechnung bei Fahrzeuge ohne Luftmassensensor benötigt 61 | Hubraum (in Litern) 62 | Aktiviere GPS 63 | Deaktiviere GPS 64 | Verwende GPS 65 | Mindestdistanz zwischen Ortupdates (in Metern) 66 | Updatefrequenz (in Meter) 67 | Dauer zwischen Statusabfragen (in Sekunden) 68 | Updatefrequenz (in Sekunden) 69 | Verwende angelsächsisches Maß 70 | Verwende wieder metrisches Maß 71 | Angelsächsische Einheiten 72 | Alle höheren Verbrauchswerte werden nicht berücksichtigt 73 | Wähle ein OBD Protokoll 74 | Maximaler Verbrauch 75 | OBD Protokoll 76 | Dauer zwischen Fahrzeugdatenabfragen (in Sekunden) 77 | Reader Konfiguration Kommandos 78 | Fahrzeug ID 79 | Volumetrischer Wirkungsgrad (z.B. 0.85) 80 | Wird für die Verbrauchsberechnung bei Fahrzeuge ohne Luftmassensensor benötigt 81 | Updatefrequenz (in Sekunden) 82 | Konfiguration des Bluetooth Readers. Je eine Einstellung pro Zeile 83 | Optionaler Key für dieses Fahrzeug, wird beim Upload der Daten genutzt 84 | Volumetrischer Wirkungsgrad 85 | 86 | -------------------------------------------------------------------------------- /src/main/res/values-es/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Configuración 4 | Elegir comando 5 | Borrar 6 | Presionar para abrir OBD-Reader 7 | La conexión OBD ha comenzado. 8 | La conexión OBD se ha detenido. 9 | Lista de Viajes 10 | Códigos de Error 11 | Viajes 12 | Carga de datos 13 | Cargando aplicación, por favor espere… 14 | Cargando… 15 | Configuración 16 | Obtener DTC 17 | Iniciar datos en vivo 18 | Detener datos en vivo 19 | Comandos OBD 20 | Preferencias OBD 21 | Presionar para abrir OBD-Reader 22 | Iniciando conexión OBD... 23 | conectado 24 | deshabilitado 25 | disponible 26 | no disponible 27 | disponible 28 | OK 29 | conexión fallida 30 | detenido 31 | no disponible 32 | desconectado 33 | Lo sentidos, su dispositivo no soporta Bluetooth 34 | Lo sentidos, su dispositivo no soporta Bluetooth 35 | Dispositivo Bluetooth no seleccionado 36 | No se puede conectar con el dispositivo Bluetooth 37 | conecting... 38 | Usted tiene Bluetooth deshabilitado. Por favor habilitelo (usando servicio Mock) 39 | No hay DTC guardado 40 | Disculpas, el viaje no será guardado. 41 | Error en comando OBD 42 | fix adquirida 43 | fix no adquirida 44 | no utilizado 45 | iniciado 46 | recibiendo información... 47 | Sensor de orientación no encontrado 48 | -------------------------------------------------------------------------------- /src/main/res/values-pt/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Limpar códigos 4 | Configurações 5 | Escolher comando 6 | Carregando app, por favor aguarde… 7 | Carregando… 8 | Obter DTC 9 | Apagar 10 | Configurações 11 | Iniciar monitoramento em tempo real 12 | Parar monitoramento em tempo real 13 | Viagens 14 | Toque para abrir OBD-Reader 15 | Toque para abrir OBD-Reader. 16 | Lista de dispositivos bluetooth pareados. 17 | Dispositivos Bluetooth 18 | Ligar Bluetooth 19 | Desligar Bluetooth 20 | Ativar Bluetooth 21 | Os logs irão ser enviados para este email 22 | Email do desenvolvedor 23 | Escolha o nome do diretório onde os logs serão salvos 24 | Nome do diretório para salvar os logs 25 | Nome do diretório 26 | Preferências OBD 27 | Configurações de log completo 28 | Comandos OBD 29 | Selecione os comandos que você deseja ver em tempo real. 30 | Comandos OBD 31 | Códigos de Erro 32 | Lista de viagens 33 | Desculpe, a viagem não será armazenada. 34 | Erro no comando OBD! 35 | Nenhum erro encontrado! 36 | Sensor de orientação não encontrado 37 | Desculpe, seu dispositivo não suporta ou está com o GPS desabilitado. 38 | Desculpe, seu dispositivo não suporta Bluetooth 39 | Nenhum DTC armazenado 40 | Nenhum dispositivo Bluetooth selecionado 41 | Habilita o envio completo de logs offline 42 | Habilita log completo 43 | Capacidade do motor (em litros) 44 | Ligar GPS 45 | Desligar GPS 46 | Ativar GPS 47 | Distância mínima entre atualizações de localização, em metros 48 | Período de atualização em metros 49 | Intervalo de tempo entre solicitações de atualização de posição em segundos 50 | Intervalo de atualização em segundos 51 | Ativar unidade imperial 52 | Voltar para unidade métrica 53 | Unidade imperial 54 | Selecione o protocolo OBD a ser utilizado 55 | Protocolo OBD 56 | Intervalo para solicitar o dados ao veículo em segundos 57 | Intervalo de atualização em segundos 58 | Comandos para configurar o leitor bluetooth, separados por nova linha 59 | Configuração de comandos de leitura 60 | Envio de dados 61 | Habilitar envio de dados http 62 | Desabilitar envio de dados http 63 | Habilitar envio de dados 64 | URL POST que irá receber os dados em tempo real 65 | URL de upload 66 | Identificador único opcional deste veículo, utilizado para enviar os dados 67 | ID do veículo 68 | Eficiência Volumétrica (ex. 0.85) 69 | Eficiência Volumétrica 70 | A conexão OBD foi iniciada. 71 | Iniciando conexão OBD… 72 | A conexão OBD foi finalizada. 73 | conectado 74 | conectando… 75 | dasabilitado 76 | falha na conexão 77 | pronto 78 | posição obtida 79 | posição não obtida 80 | não disponível 81 | não utilizado 82 | pronto 83 | iniciado 84 | parado 85 | indisponível 86 | recebendo dados… 87 | desconectado 88 | NA 89 | Ok 90 | O Bluetooth está desabilitado. Por favor habilite ele (utilizando o serviço Mock) 91 | Não foi possível se conectar ao dispositivo bluetooth! 92 | Utilizado para economia de combustível em veículos sem sensor MAF 93 | Qualquer economia de combustível maior do que isso será jogada fora 94 | Valor Máximo de Economia de Combustível 95 | Utilizado para economia de combustível em veículos sem sensor MAF 96 | -------------------------------------------------------------------------------- /src/main/res/values-sw600dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | -------------------------------------------------------------------------------- /src/main/res/values-sw720dp-land/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 128dp 5 | 6 | -------------------------------------------------------------------------------- /src/main/res/values-v11/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/main/res/values-v14/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /src/main/res/values-v21/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/res/values-w820dp/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 64dp 6 | 7 | -------------------------------------------------------------------------------- /src/main/res/values-zh/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 设置 4 | 清理代码 5 | 点击打开 Android OBD-II Reader 6 | 选择一条指令 7 | 正在开启 OBD 连接.. 8 | OBD 连接已经开启. 9 | OBD 连接已经关闭. 10 | 点击打开 OBD-Reader. 11 | 故障码 12 | 数据上传 13 | 上传参数 14 | 行程列表 15 | 行程 16 | 删除 17 | GPS参数 18 | 载入中… 19 | 正在载入应用, 请等待… 20 | 开启实时数据 21 | 停止实时数据 22 | 设置 23 | 获取 DTC 信息 24 | 蓝牙 25 | 26 | 0 27 | 00:00:00 28 | OBD 设置 29 | OBD 指令 30 | 蓝牙 31 | 对不起, 您的设备不支持蓝牙 32 | 您的蓝牙还没有打开. 请打卡它 (使用模拟服务) 33 | 没有蓝牙设备可选 34 | 不能连接到蓝牙设备! 35 | 方向传感器不存在? 36 | 对不起, 您的设备不支持或者您还没有打开GPS. 37 | 对不起, 行程没有保存. 38 | OBD 指令失败! 39 | 没有 DTC 存储 40 | 不可用的 41 | 未打开的 42 | 准备中 43 | 连接中… 44 | 已连接 45 | 连接失败 46 | 已确定 47 | 未确定 48 | 开始 49 | 结束 50 | 准备中 51 | 不可用 52 | 未使用 53 | OK 54 | 接收数据… 55 | 未连接 56 | NA 57 | 58 | 59 | 数据下载 60 | 打开网络数据下载 61 | 关闭网络数据下载 62 | 下载链接 63 | 用于接收更新的数据 64 | 设备 ID 65 | 用于上传数据的唯一设备id 66 | 67 | 蓝牙 68 | 打开蓝牙 69 | 关闭蓝牙 70 | 蓝牙设备 71 | 已配对的蓝牙设备 72 | 73 | GPS 74 | 打开GPS 75 | 关闭GPS 76 | 更新周期(秒) 77 | 查询新位置之间的时间(秒) 78 | 更新距离(米) 79 | 查询新位置之间的距离(米) 80 | 81 | OBD协议 82 | 选择OBD协议 83 | 英制单位 84 | 关闭英制单位 85 | 打开公制单位 86 | 更新周期(秒) 87 | 查询车辆数据的间隔时间(秒) 88 | 最大燃油经济性的价值 89 | 任何燃油经济性值大于该会抛出 90 | 容积效率(如0.85) 91 | 用于没有MAF传感器的经济性燃油车辆 92 | 容积效率 93 | 发动机排量(升) 94 | 用于没有MAF传感器的经济性燃油车辆 95 | 阅读器配置命令 96 | 配置蓝牙读写器命令 97 | 选择你想实时显示的汽车数据 98 | 99 | OBD指令 100 | 完整日志记录配置 101 | 开启离线日志记录并上传 102 | 开启离线日志记录 103 | OBDReader日志 104 | 选择需要保存日志的目录名 105 | 目录名 106 | 保存文件的目录名 107 | 108 | -------------------------------------------------------------------------------- /src/main/res/values/color.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #607d8b 5 | 6 | #455a64 7 | 8 | #ff5252 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/res/values/dimens.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 16dp 4 | 16dp 5 | 6 | -------------------------------------------------------------------------------- /src/main/res/values/strings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | OBD-II Reader 4 | Settings 5 | Clear Codes 6 | Tap to open OBD-Reader 7 | Choose a command 8 | Starting OBD connection.. 9 | OBD connection has started. 10 | OBD connection has stopped. 11 | Tap to open OBD-Reader. 12 | Trouble Codes 13 | Data Upload 14 | upload_category 15 | TripsList 16 | Trips 17 | Delete 18 | gps_category 19 | Loading… 20 | Loading application View, please wait… 21 | Start Live Data 22 | Stop Live Data 23 | Settings 24 | Get DTC 25 | GPS 26 | OBD 27 | Bluetooth 28 | N 29 | 0 30 | 0l/100km 31 | 00:00:00 32 | OBD Preferences 33 | OBD Commands 34 | Bluetooth 35 | GPS 36 | Sorry, your device doesn\'t support Bluetooth 37 | You have Bluetooth disabled. Please enable it (using Mock service) 38 | No Bluetooth device selected 39 | Cannot connect to bluetooth device! 40 | Orientation sensor missing? 41 | Sorry, your device doesn\'t support or has disabled GPS. 42 | Sorry, trip will not be saved. 43 | OBD command failure! 44 | No DTC stored 45 | unavailable 46 | disabled 47 | ready 48 | connecting… 49 | connected 50 | connection failed 51 | fix acquired 52 | fix not acquired 53 | started 54 | stopped 55 | ready 56 | not available 57 | not used 58 | OK 59 | receiving data… 60 | disconnected 61 | NA 62 | Enable Data Upload 63 | Enable http data upload 64 | Disable http data upload 65 | Upload URL 66 | POST URL that will accept real-time data 67 | Vehicle ID 68 | Optional unique id of this vehicle, used with uploaded data 69 | Enable Bluetooth 70 | Turn on Bluetooth 71 | Turn off Bluetooth 72 | List of paired bluetooth devices. 73 | Bluetooth Devices 74 | Enable GPS 75 | Turn on GPS 76 | Turn off GPS 77 | Update Period in Seconds 78 | The length of time between querying new position in seconds 79 | Update Period in Meters 80 | Min Distance between location updates, in meters 81 | OBD Protocol 82 | Select OBD Protocol to use 83 | Imperial Units 84 | Enable imperial units 85 | Go back to metric units 86 | Update Period in Seconds 87 | The length of time between querying vehicle data in seconds 88 | Maximum Fuel Economy Value 89 | Any fuel economy values larger than this will be thrown out 90 | Volumetric Efficiency (eg 0.85) 91 | Used for fuel economy on vehicles without MAF sensor 92 | Volumetric Efficiency 93 | Engine Displacement (liters) 94 | Used for fuel economy on vehicles without MAF sensor 95 | Reader Config Commands 96 | Commands to configure bluetooth reader, separate with new line 97 | Select the commands you would like to see in real-time. 98 | OBD Commands 99 | Full logging configuration 100 | Enables full offline logging with upload 101 | Enable full logging 102 | OBDReaderLogs 103 | Choose the name of the directory to save the logs 104 | Directory name 105 | Name of the directory where to save the files 106 | Developer Email 107 | Logs will be emailed to this address 108 | There were no errors found.! 109 | 110 | -------------------------------------------------------------------------------- /src/main/res/values/styles.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 7 | 14 | 15 | 16 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/main/res/xml/preferences.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 6 | 13 | 19 | 25 | 26 | 27 | 34 | 39 | 40 | 43 | 50 | 56 | 62 | 63 | 64 | 70 | 77 | 83 | 89 | 95 | 101 | 107 | 113 | 114 | 115 | 119 | 120 | 121 | 126 | 132 | 133 | 134 | --------------------------------------------------------------------------------