├── .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 | [](https://circleci.com/gh/pires/android-obd-reader)
20 |
21 | Android OBD-II reader designed to connect with Bluetooth Elm327 OBD reader.
22 |
23 | 
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 |
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 |
--------------------------------------------------------------------------------
/src/main/res/menu/main.xml:
--------------------------------------------------------------------------------
1 |
8 |
--------------------------------------------------------------------------------
/src/main/res/menu/menu_trips_list.xml:
--------------------------------------------------------------------------------
1 |
11 |
--------------------------------------------------------------------------------
/src/main/res/menu/trouble_codes.xml:
--------------------------------------------------------------------------------
1 |
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 |
--------------------------------------------------------------------------------