├── settings.gradle
├── .gitignore
├── gradle
├── wrapper
│ ├── gradle-wrapper.jar
│ └── gradle-wrapper.properties
├── checkstyle.gradle
├── javadoc.gradle
├── mavenize.gradle
└── bintray-upload.gradle
├── android-net
├── src
│ ├── main
│ │ ├── res
│ │ │ ├── values
│ │ │ │ └── strings.xml
│ │ │ └── layout
│ │ │ │ └── activity_vinli_sign_in.xml
│ │ ├── java
│ │ │ └── li
│ │ │ │ └── vin
│ │ │ │ └── net
│ │ │ │ ├── VinliItem.java
│ │ │ │ ├── Users.java
│ │ │ │ ├── DistanceUnit.java
│ │ │ │ ├── Devices.java
│ │ │ │ ├── ObjectRef.java
│ │ │ │ ├── VinliError.java
│ │ │ │ ├── Vehicles.java
│ │ │ │ ├── DataItem.java
│ │ │ │ ├── AutoParcelAdapter.java
│ │ │ │ ├── Locations.java
│ │ │ │ ├── Diagnostics.java
│ │ │ │ ├── Trips.java
│ │ │ │ ├── Snapshots.java
│ │ │ │ ├── Messages.java
│ │ │ │ ├── Collisions.java
│ │ │ │ ├── Notifications.java
│ │ │ │ ├── Dummies.java
│ │ │ │ ├── VinliBaseActivity.java
│ │ │ │ ├── User.java
│ │ │ │ ├── Events.java
│ │ │ │ ├── Rules.java
│ │ │ │ ├── ReportCards.java
│ │ │ │ ├── VinliSignInHandler.java
│ │ │ │ ├── BatteryStatus.java
│ │ │ │ ├── VinliEndpoint.java
│ │ │ │ ├── utils
│ │ │ │ ├── RxAdapter.java
│ │ │ │ └── PageAdapter.java
│ │ │ │ ├── Subscriptions.java
│ │ │ │ ├── Wrapped.java
│ │ │ │ ├── Distances.java
│ │ │ │ ├── ObserverManager.java
│ │ │ │ ├── DistanceList.java
│ │ │ │ ├── Coordinate.java
│ │ │ │ ├── Dtc.java
│ │ │ │ ├── Duktaper.java
│ │ │ │ ├── Collision.java
│ │ │ │ ├── Event.java
│ │ │ │ ├── BearingCalculator.java
│ │ │ │ ├── Vinli.java
│ │ │ │ ├── Notification.java
│ │ │ │ ├── ReportCard.java
│ │ │ │ ├── Odometer.java
│ │ │ │ ├── SignInActivity.java
│ │ │ │ ├── Dummy.java
│ │ │ │ ├── SupportedPids.java
│ │ │ │ ├── Location.java
│ │ │ │ ├── OdometerTrigger.java
│ │ │ │ └── TimeSeries.java
│ │ └── AndroidManifest.xml
│ ├── test
│ │ └── java
│ │ │ └── li
│ │ │ └── vin
│ │ │ └── net
│ │ │ ├── UsersIntegrationTests.java
│ │ │ ├── DevicesIntegrationTests.java
│ │ │ ├── DiagnosticsIntegrationTests.java
│ │ │ ├── CollisionsIntegrationTests.java
│ │ │ ├── VehiclesIntegrationTests.java
│ │ │ └── NotificationsIntegrationTests.java
│ └── androidTest
│ │ └── java
│ │ └── li
│ │ └── vin
│ │ └── net
│ │ ├── InstTestHelper.java
│ │ └── StreamingInstTest.java
├── integration.properties
├── proguard-rules.pro
└── build.gradle
├── .travis.yml
├── README.md
├── deploy_website.sh
├── gradle.properties
├── LICENSE.txt
├── gradlew.bat
├── gradlew
├── checkstyle.xml
└── circle.yml
/settings.gradle:
--------------------------------------------------------------------------------
1 | include ':android-net'
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 |
4 | .gradle
5 | local.properties
6 | build
7 |
8 | .DS_Store
9 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.jar:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/vinli/android-net/HEAD/gradle/wrapper/gradle-wrapper.jar
--------------------------------------------------------------------------------
/android-net/src/main/res/values/strings.xml:
--------------------------------------------------------------------------------
1 |
2 | Vinli Sign In
3 |
4 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/VinliItem.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 |
5 | public interface VinliItem extends Parcelable {
6 |
7 | String id();
8 |
9 | }
10 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Users.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import retrofit2.http.GET;
4 | import rx.Observable;
5 |
6 | /*package*/ interface Users {
7 |
8 | @GET("users/_current")
9 | Observable> currentUser();
10 |
11 | }
12 |
--------------------------------------------------------------------------------
/gradle/wrapper/gradle-wrapper.properties:
--------------------------------------------------------------------------------
1 | #Wed Sep 14 12:00:20 CDT 2016
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.14.1-all.zip
7 |
--------------------------------------------------------------------------------
/android-net/src/main/res/layout/activity_vinli_sign_in.xml:
--------------------------------------------------------------------------------
1 |
7 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: android
2 | android:
3 | components:
4 | # Uncomment the lines below if you want to
5 | # use the latest revision of Android SDK Tools
6 | - platform-tools
7 | - tools
8 |
9 | # The BuildTools version used by your project
10 | - build-tools-22.0.1
11 |
12 | # The SDK version used to compile your project
13 | - android-22
14 |
15 | # Additional components
16 | # - extra-google-google_play_services
17 | # - extra-google-m2repository
18 | - extra-android-m2repository
19 |
20 | before_script:
21 | - chmod +x gradlew
22 |
23 | script: "./gradlew build"
24 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/DistanceUnit.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | public enum DistanceUnit {
4 | KILOMETERS("km"),
5 | METERS("m"),
6 | MILES("mi");
7 |
8 | private String unitStr;
9 |
10 | private DistanceUnit(String unit){
11 | this.unitStr = unit;
12 | }
13 |
14 | /*package*/ String getDistanceUnitStr(){
15 | return this.unitStr;
16 | }
17 |
18 | /*package*/ static DistanceUnit parse(String str) {
19 | if ("km".equals(str)) return KILOMETERS;
20 | if ("m".equals(str)) return METERS;
21 | if ("mi".equals(str)) return MILES;
22 | return null;
23 | }
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/android-net/integration.properties:
--------------------------------------------------------------------------------
1 | ACCESS_TOKEN=DEFAULT_ACCESS_TOKEN
2 | VEHICULARIZATION_ACCESS_TOKEN=DEFAULT_ACCESS_TOKEN
3 | DEVICE_ID=DEFAULT_DEVICE_ID
4 | SUBSCRIPTION_ID=DEFAULT_SUBSCRIPTION_ID
5 | RULE_ID=DEFAULT_RULE_ID
6 | VEHICLE_RULE_ID=DEFAULT_RULE_ID
7 | VEHICLE_ID=DEFAULT_VEHICLE_ID
8 | SECOND_VEHICLE_ID=DEFAULT_VEHICLE_ID
9 | EVENT_ID=DEFAULT_EVENT_ID
10 | NOTIFICATION_ID=DEFAULT_NOTIFICATION_ID
11 | MESSAGE_ID=DEFAULT_MESSAGE_ID
12 | TRIP_ID=DEFAULT_TRIP_ID
13 | DUMMY_ID=DEFAULT_DUMMY_ID
14 | ROUTE_ID=DEFAULT_ROUTE_ID
15 | VIN=DEFAULT_VIN
16 | ODO_ID=DEFAULT_ODO_ID
17 | ODO_TRIGGER_ID=DEFAULT_ODO_TRIGGER_ID
18 | REPORT_CARD_ID=DEFAULT_REPORT_CARD_ID
19 | COLLISION_ID=DEFAULT_COLLISION_ID
--------------------------------------------------------------------------------
/android-net/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/kyle/dev/android/android-sdk-macosx/tools/proguard/proguard-android.txt
4 | # You can edit the include path and order by changing the proguardFiles
5 | # directive in build.gradle.
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 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Devices.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import retrofit2.http.GET;
7 | import retrofit2.http.Path;
8 | import retrofit2.http.Query;
9 | import retrofit2.http.Url;
10 | import rx.Observable;
11 |
12 | /*package*/ interface Devices {
13 |
14 | @GET("devices")
15 | Observable> devices(
16 | @Nullable @Query("limit") Integer limit,
17 | @Nullable @Query("offset") Integer offset);
18 |
19 | @GET("devices/{deviceId}")
20 | Observable> device(
21 | @NonNull @Path("deviceId") String deviceId);
22 |
23 | @GET Observable> devicesForUrl(@NonNull @Url String url);
24 |
25 | }
26 |
--------------------------------------------------------------------------------
/android-net/src/main/AndroidManifest.xml:
--------------------------------------------------------------------------------
1 |
2 |
4 |
5 |
6 |
7 |
8 |
16 |
17 |
20 |
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Vinli Android Net SDK
2 | =====================
3 |
4 | An Android client for interacting with the Vinli backend from within your application. [Sample App](https://github.com/vinli/android-net-demo)
5 |
6 | Download
7 | --------
8 |
9 | You can also depend on this library through Gradle from jcenter:
10 | ```groovy
11 | compile 'li.vin:android-net:1.0.17'
12 | ```
13 |
14 | Conventions
15 | -----------
16 | ### [RxJava](https://github.com/ReactiveX/RxJava/wiki)
17 | The developer interfaces with the SDK using reactive Observables and Subscriptions.
18 | All data from the device is streamed via Observable, and the data stream is stopped by unsubscribing from the subscription.
19 | All models are immutable.
20 |
21 | Docs
22 | ----
23 |
24 | ### [JavaDocs](http://vinli.github.io/android-net/)
25 |
--------------------------------------------------------------------------------
/gradle/checkstyle.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'checkstyle'
2 |
3 | checkstyle {
4 | configFile rootProject.file('checkstyle.xml')
5 | showViolations true
6 | toolVersion = '6.6'
7 | ignoreFailures = true
8 | }
9 |
10 | def variants = android.hasProperty('applicationVariants') \
11 | ? android.applicationVariants \
12 | : android.libraryVariants
13 |
14 | variants.all { variant ->
15 | def name = variant.buildType.name
16 |
17 | def checkstyle = project.tasks.create "checkstyle${name.capitalize()}", Checkstyle
18 | checkstyle.dependsOn variant.javaCompile
19 | checkstyle.source variant.javaCompile.source
20 | checkstyle.classpath = project.fileTree(variant.javaCompile.destinationDir)
21 | checkstyle.exclude('**/BuildConfig.java')
22 | checkstyle.exclude('**/R.java')
23 | project.tasks.getByName("check").dependsOn checkstyle
24 | }
25 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/ObjectRef.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import com.google.gson.GsonBuilder;
4 |
5 | import auto.parcel.AutoParcel;
6 |
7 | @AutoParcel
8 | public abstract class ObjectRef implements VinliItem {
9 | /*package*/ static final void registerGson(GsonBuilder gb) {
10 | gb.registerTypeAdapter(ObjectRef.class, AutoParcelAdapter.create(AutoParcel_ObjectRef.class));
11 | }
12 |
13 | public static final Builder builder() {
14 | return new AutoParcel_ObjectRef.Builder();
15 | }
16 |
17 | public abstract String type();
18 |
19 | /*package*/ ObjectRef() { }
20 |
21 | @AutoParcel.Builder
22 | public static abstract class Builder {
23 | public abstract Builder id(String s);
24 | public abstract Builder type(String s);
25 |
26 | public abstract ObjectRef build();
27 |
28 | /*package*/ Builder() { }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/VinliError.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | public final class VinliError extends RuntimeException {
4 |
5 | /*package*/ static VinliError serverError(ServerError err) {
6 | return new VinliError(err.message, err);
7 | }
8 |
9 | private final ServerError mServerError;
10 |
11 | /*package*/ VinliError(String message, ServerError err) {
12 | super(message);
13 | mServerError = err;
14 | }
15 |
16 | public int getStatusCode() {
17 | return mServerError == null ? -1 : mServerError.statusCode;
18 | }
19 |
20 | /*package*/ final class ServerError {
21 | private final int statusCode;
22 | private final String error, message;
23 |
24 | /*package*/ ServerError(int statusCode, String message) {
25 | this.statusCode = statusCode;
26 | this.message = message;
27 | this.error = null; // set by GSON
28 | }
29 | }
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/gradle/javadoc.gradle:
--------------------------------------------------------------------------------
1 | def variants = android.hasProperty('applicationVariants') \
2 | ? android.applicationVariants \
3 | : android.libraryVariants
4 |
5 | variants.all { variant ->
6 | def name = variant.buildType.name
7 |
8 | task("generate${name.capitalize()}JavaDoc", type: Javadoc) {
9 | description "Generates Javadoc for $name."
10 | source = variant.javaCompile.source
11 |
12 | if (project.hasProperty('destinationDir')) {
13 | destinationDir = file("${project.rootDir}/${project.destinationDir}");
14 | }
15 |
16 | classpath = files(variant.javaCompile.classpath.files, android.bootClasspath)
17 | options.links("https://docs.oracle.com/javase/7/docs/api/");
18 | options.linksOffline("http://d.android.com/reference", "${android.sdkDirectory}/docs/reference");
19 | exclude '**/BuildConfig.java'
20 | exclude '**/R.java'
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/deploy_website.sh:
--------------------------------------------------------------------------------
1 | #!/bin/bash
2 |
3 | # taken from https://github.com/square/retrofit/blob/master/deploy_website.sh
4 |
5 | set -ex
6 |
7 | REPO="git@github.com:vinli/android-net.git"
8 |
9 | DIR=temp-clone
10 |
11 | # Delete any existing temporary website clone
12 | rm -rf $DIR
13 |
14 | # Clone the current repo into temp folder
15 | git clone $REPO $DIR
16 |
17 | # Move working directory into temp folder
18 | cd $DIR
19 |
20 | # Checkout and track the gh-pages branch
21 | git checkout -t origin/gh-pages
22 |
23 | # Delete everything
24 | rm -rf *
25 |
26 | # back to project
27 | cd ..
28 |
29 | # Generate the latest javadoc
30 | ./gradlew task generateReleaseJavaDoc '-PdestinationDir='$DIR
31 | cd $DIR
32 |
33 | # Stage all files in git and create a commit
34 | git add .
35 | git add -u
36 | git commit -m "Website at $(date)"
37 |
38 | # Push the new files up to GitHub
39 | git push origin gh-pages
40 |
41 | # Delete our temp folder
42 | cd ..
43 | # rm -rf $DIR
44 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Vehicles.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import retrofit2.http.GET;
7 | import retrofit2.http.Path;
8 | import retrofit2.http.Query;
9 | import retrofit2.http.Url;
10 | import rx.Observable;
11 |
12 | /*package*/ interface Vehicles {
13 |
14 | @GET("devices/{deviceId}/vehicles")
15 | Observable> vehicles(
16 | @NonNull @Path("deviceId") String deviceId,
17 | @Nullable @Query("limit") Integer limit,
18 | @Nullable @Query("offset") Integer offset);
19 |
20 | @GET("devices/{deviceId}/vehicles/_latest")
21 | Observable> latestVehicle(
22 | @NonNull @Path("deviceId") String deviceId);
23 |
24 | @GET("vehicles/{vehicleId}")
25 | Observable> vehicle(
26 | @NonNull @Path("vehicleId") String vehicleId);
27 |
28 | @GET Observable> vehiclesForUrl(@NonNull @Url String url);
29 |
30 | }
31 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/DataItem.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import java.util.Collections;
7 | import java.util.Map;
8 | import java.util.Set;
9 |
10 | /*package*/ abstract class DataItem implements VinliItem {
11 |
12 | @Nullable /*package*/ abstract Map data();
13 |
14 | public boolean hasValue(@NonNull String name) {
15 | final Map data = data();
16 | if (data == null) {
17 | return false;
18 | }
19 |
20 | return data.containsKey(name);
21 | }
22 |
23 | @Nullable public String value(@NonNull String name) {
24 | final Map data = data();
25 | if (data == null) {
26 | return null;
27 | }
28 |
29 | return data.get(name);
30 | }
31 |
32 | public Set dataKeys() {
33 | final Map data = data();
34 | if (data == null) {
35 | return Collections.emptySet();
36 | }
37 |
38 | return Collections.unmodifiableSet(data.keySet());
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/gradle.properties:
--------------------------------------------------------------------------------
1 | # Project-wide Gradle settings.
2 |
3 | # IDE (e.g. Android Studio) users:
4 | # Gradle settings configured through the IDE *will override*
5 | # any settings specified in this file.
6 |
7 | # For more details on how to configure your build environment visit
8 | # http://www.gradle.org/docs/current/userguide/build_environment.html
9 |
10 | # Specifies the JVM arguments used for the daemon process.
11 | # The setting is particularly useful for tweaking memory settings.
12 | # Default value: -Xmx10248m -XX:MaxPermSize=256m
13 | # org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
14 |
15 | # When configured, Gradle will run in incubating parallel mode.
16 | # This option should only be used with decoupled projects. More details, visit
17 | # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
18 | # org.gradle.parallel=true
19 |
20 | vinli.groupId=li.vin
21 | vinli.artifactId=android-net
22 | vinli.githubRepo=android-net
23 | vinli.version=1.1.1
24 | vinli.name=Android Net SDK
25 | vinli.desc=Vinli Android Network SDK
26 | vinli.publish=true
27 |
--------------------------------------------------------------------------------
/LICENSE.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c)
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/AutoParcelAdapter.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import com.google.gson.Gson;
4 | import com.google.gson.TypeAdapter;
5 | import com.google.gson.stream.JsonReader;
6 | import com.google.gson.stream.JsonWriter;
7 |
8 | import java.io.IOException;
9 |
10 | /*package*/ class AutoParcelAdapter extends TypeAdapter {
11 | public static final AutoParcelAdapter create(Class extends T> autoParcelClass) {
12 | return new AutoParcelAdapter(autoParcelClass);
13 | }
14 |
15 | private final Class extends T> autoParcelClass;
16 | private Gson gson;
17 |
18 | private AutoParcelAdapter(Class extends T> autoParcelClass) {
19 | this.autoParcelClass = autoParcelClass;
20 | }
21 |
22 | @Override public void write(JsonWriter out, T value) throws IOException {
23 | if (gson == null) {
24 | gson = Vinli.curApp().gson();
25 | }
26 |
27 | gson.toJson(value, autoParcelClass, out);
28 | }
29 |
30 | @Override public T read(JsonReader in) throws IOException {
31 | if (gson == null) {
32 | gson = Vinli.curApp().gson();
33 | }
34 |
35 | return gson.fromJson(in, autoParcelClass);
36 | }
37 | }
38 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Locations.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Path;
7 | import retrofit2.http.Query;
8 | import retrofit2.http.Url;
9 | import rx.Observable;
10 |
11 | /*package*/ interface Locations {
12 |
13 | @GET("devices/{deviceId}/locations")
14 | Observable> locations(
15 | @NonNull @Path("deviceId") String deviceId,
16 | @Nullable @Query("since") Long since,
17 | @Nullable @Query("until") Long until,
18 | @Nullable @Query("limit") Integer limit,
19 | @Nullable @Query("sortDir") String sortDir);
20 |
21 | @GET("vehicles/{vehicleId}/locations")
22 | Observable> vehicleLocations(
23 | @NonNull @Path("vehicleId") String vehicleId,
24 | @Nullable @Query("since") Long since,
25 | @Nullable @Query("until") Long until,
26 | @Nullable @Query("limit") Integer limit,
27 | @Nullable @Query("sortDir") String sortDir);
28 |
29 | @GET Observable> locationsForUrl(@NonNull @Url String url);
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Diagnostics.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Path;
7 | import retrofit2.http.Query;
8 | import retrofit2.http.Url;
9 | import rx.Observable;
10 |
11 | /*package*/ interface Diagnostics {
12 |
13 | @GET("vehicles/{vehicleId}/codes")
14 | Observable> codes(@NonNull @Path("vehicleId") String vehicleId,
15 | @Nullable @Query("since") Long since,
16 | @Nullable @Query("until") Long until,
17 | @Nullable @Query("limit") Integer limit,
18 | @Nullable @Query("sortDir") String sortDir,
19 | @Nullable @Query("state") String state);
20 |
21 | @GET("codes")
22 | Observable> diagnose(@NonNull @Query("number") String number);
23 |
24 | @GET("vehicles/{vehicleId}/battery_statuses/_current")
25 | Observable> currentBatteryStatus(@NonNull @Path("vehicleId") String vehicleId);
26 |
27 | @GET Observable> codesForUrl(@NonNull @Url String url);
28 |
29 | @GET Observable> rawCodesForUrl(@NonNull @Url String url);
30 |
31 | }
32 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Trips.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Path;
7 | import retrofit2.http.Query;
8 | import retrofit2.http.Url;
9 | import rx.Observable;
10 |
11 | /*package*/ interface Trips {
12 |
13 | @GET("devices/{deviceId}/trips")
14 | Observable> trips(
15 | @NonNull @Path("deviceId") String deviceId,
16 | @Nullable @Query("since") Long since,
17 | @Nullable @Query("until") Long until,
18 | @Nullable @Query("limit") Integer limit,
19 | @Nullable @Query("sortDir") String sortDir);
20 |
21 | @GET("vehicles/{vehicleId}/trips")
22 | Observable> vehicleTrips(
23 | @NonNull @Path("vehicleId") String vehicleId,
24 | @Nullable @Query("since") Long since,
25 | @Nullable @Query("until") Long until,
26 | @Nullable @Query("limit") Integer limit,
27 | @Nullable @Query("sortDir") String sortDir);
28 |
29 | @GET("trips/{tripId}")
30 | Observable> trip(@NonNull @Path("tripId") String tripId);
31 |
32 | @GET Observable> tripsForUrl(@NonNull @Url String url);
33 |
34 | }
35 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Snapshots.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Path;
7 | import retrofit2.http.Query;
8 | import retrofit2.http.Url;
9 | import rx.Observable;
10 |
11 | /*package*/ interface Snapshots {
12 |
13 | @GET("devices/{deviceId}/snapshots")
14 | Observable> snapshots(
15 | @NonNull @Path("deviceId") String deviceId,
16 | @NonNull @Query("fields") String fields,
17 | @Nullable @Query("since") Long since,
18 | @Nullable @Query("until") Long until,
19 | @Nullable @Query("limit") Integer limit,
20 | @Nullable @Query("sortDir") String sortDir);
21 |
22 |
23 | @GET("vehicles/{vehicleId}/snapshots")
24 | Observable> vehicleSnapshots(
25 | @NonNull @Path("vehicleId") String vehicleId,
26 | @NonNull @Query("fields") String fields,
27 | @Nullable @Query("since") Long since,
28 | @Nullable @Query("until") Long until,
29 | @Nullable @Query("limit") Integer limit,
30 | @Nullable @Query("sortDir") String sortDir);
31 |
32 | @GET Observable> snapshotsForUrl(@NonNull @Url String url);
33 | }
34 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Messages.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Path;
7 | import retrofit2.http.Query;
8 | import retrofit2.http.Url;
9 | import rx.Observable;
10 |
11 | public interface Messages {
12 |
13 | @GET("devices/{deviceId}/messages")
14 | Observable> messages(
15 | @NonNull @Path("deviceId") String deviceId,
16 | @Nullable @Query("since") Long since,
17 | @Nullable @Query("until") Long until,
18 | @Nullable @Query("limit") Integer limit,
19 | @Nullable @Query("sortDir") String sortDir);
20 |
21 |
22 | @GET("vehicles/{vehicleId}/messages")
23 | Observable> vehicleMessages(
24 | @NonNull @Path("vehicleId") String vehicleId,
25 | @Nullable @Query("since") Long since,
26 | @Nullable @Query("until") Long until,
27 | @Nullable @Query("limit") Integer limit,
28 | @Nullable @Query("sortDir") String sortDir);
29 |
30 | @GET("messages/{messageId}")
31 | Observable> message(
32 | @NonNull @Path("messageId") String messageId);
33 |
34 | @GET Observable> messagesForUrl(@NonNull @Url String url);
35 | }
36 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Collisions.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import retrofit2.http.GET;
7 | import retrofit2.http.Path;
8 | import retrofit2.http.Query;
9 | import retrofit2.http.Url;
10 | import rx.Observable;
11 |
12 | public interface Collisions {
13 |
14 | @GET("collisions/{collisionId}")
15 | Observable> collision(
16 | @NonNull @Path("collisionId") String collisionId);
17 |
18 | @GET("vehicles/{vehicleId}/collisions")
19 | Observable> collisionsForVehicle(
20 | @NonNull @Path("vehicleId") String vehicleId,
21 | @Nullable @Query("since") Long since,
22 | @Nullable @Query("until") Long until,
23 | @Nullable @Query("limit") Integer limit,
24 | @Nullable @Query("sortDir") String sortDir);
25 |
26 | @GET("devices/{deviceId}/collisions")
27 | Observable> collisionsForDevice(
28 | @NonNull @Path("deviceId") String deviceId,
29 | @Nullable @Query("since") Long since,
30 | @Nullable @Query("until") Long until,
31 | @Nullable @Query("limit") Integer limit,
32 | @Nullable @Query("sortDir") String sortDir);
33 |
34 | @GET Observable> collisionsForUrl(@NonNull @Url String url);
35 |
36 | }
37 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Notifications.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Path;
7 | import retrofit2.http.Query;
8 | import retrofit2.http.Url;
9 | import rx.Observable;
10 |
11 | public interface Notifications {
12 |
13 | @GET("notifications/{notificationId}") Observable> notification(
14 | @NonNull @Path("notificationId") String notificationId);
15 |
16 | @GET("subscriptions/{subscriptionId}/notifications")
17 | Observable> notificationsForSubscription(
18 | @NonNull @Path("subscriptionId") String subscriptionId, @Nullable @Query("since") Long since,
19 | @Nullable @Query("until") Long until, @Nullable @Query("limit") Integer limit,
20 | @Nullable @Query("sortDir") String sortDir);
21 |
22 | @GET("events/{eventId}/notifications")
23 | Observable> notificationsForEvent(
24 | @NonNull @Path("eventId") String eventId, @Nullable @Query("since") Long since,
25 | @Nullable @Query("until") Long until, @Nullable @Query("limit") Integer limit,
26 | @Nullable @Query("sortDir") String sortDir);
27 |
28 | @GET Observable> notificationsForUrl(@NonNull @Url String url);
29 | }
30 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Dummies.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.Body;
6 | import retrofit2.http.DELETE;
7 | import retrofit2.http.GET;
8 | import retrofit2.http.POST;
9 | import retrofit2.http.Path;
10 | import retrofit2.http.Query;
11 | import retrofit2.http.Url;
12 | import rx.Observable;
13 |
14 | /**
15 | * Created by JoshBeridon on 11/18/16.
16 | */
17 |
18 | /*package*/ interface Dummies {
19 |
20 | @GET("dummies")
21 | Observable> dummies(
22 | @Nullable @Query("limit") Integer limit,
23 | @Nullable @Query("offset") Integer offset);
24 |
25 | @POST("dummies/{dummyId}/runs")
26 | Observable> create(
27 | @NonNull @Path("dummyId") String dummyId,
28 | @NonNull @Body Dummy.Run.Seed runSeed);
29 |
30 | @GET("dummies/{dummyId}/runs/_current")
31 | Observable> currentRun(
32 | @NonNull @Path("dummyId") String dummyId);
33 |
34 | @GET("dummies/{dummyId}")
35 | Observable> trip(@NonNull @Path("dummyId") String dummyId);
36 |
37 | @DELETE("dummies/{dummyId}/runs/_current")
38 | Observable deleteRun(@NonNull @Path("dummyId") String dummyId);
39 |
40 | @GET Observable> dummiesForUrl(@NonNull @Url String url);
41 | }
42 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/VinliBaseActivity.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.app.Activity;
4 | import android.app.PendingIntent;
5 | import android.content.Intent;
6 | import android.os.Bundle;
7 | import android.support.annotation.NonNull;
8 | import android.support.v7.app.AppCompatActivity;
9 |
10 | public abstract class VinliBaseActivity extends AppCompatActivity{
11 |
12 |
13 | private VinliSignInHandler signInHandler;
14 | public VinliApp vinliApp;
15 |
16 | @Override
17 | protected void onCreate(Bundle savedInstanceState) {
18 | super.onCreate(savedInstanceState);
19 |
20 | signInHandler = new VinliSignInHandler();
21 | }
22 |
23 | @Override
24 | protected void onResume(){
25 | super.onResume();
26 | vinliApp = signInHandler.handleOnResume(this, getIntent());
27 | }
28 |
29 | @Override
30 | protected void onNewIntent(Intent intent){
31 | super.onNewIntent(intent);
32 | vinliApp = signInHandler.handleOnNewIntent(this, intent);
33 | }
34 |
35 | @Override
36 | protected void onDestroy(){
37 | super.onDestroy();
38 | }
39 |
40 | public void signIn(@NonNull String clientId, @NonNull String redirectURI, @NonNull PendingIntent pendingIntent){
41 | signInHandler.signIn(this, clientId, redirectURI, pendingIntent);
42 | }
43 |
44 | public boolean signedIn(){
45 | return signInHandler.signedIn();
46 | }
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/User.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 |
7 | import com.google.gson.GsonBuilder;
8 | import com.google.gson.reflect.TypeToken;
9 |
10 | import java.lang.reflect.Type;
11 |
12 | import auto.parcel.AutoParcel;
13 |
14 | @AutoParcel
15 | public abstract class User implements VinliItem {
16 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() { }.getType();
17 |
18 | /*package*/ static final void registerGson(GsonBuilder gb) {
19 | gb.registerTypeAdapter(User.class, AutoParcelAdapter.create(AutoParcel_User.class));
20 | gb.registerTypeAdapter(Settings.class, AutoParcelAdapter.create(AutoParcel_User_Settings.class));
21 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(User.class));
22 | }
23 |
24 | @NonNull public abstract String firstName();
25 | @NonNull public abstract String lastName();
26 | @NonNull public abstract String email();
27 | @Nullable public abstract String image();
28 | @NonNull public abstract String phone();
29 | @NonNull public abstract String createdAt();
30 | @Nullable public abstract Settings settings();
31 |
32 | /*package*/ User() { }
33 |
34 | @AutoParcel
35 | public static abstract class Settings implements Parcelable {
36 | @Nullable public abstract String unit();
37 |
38 | /*package*/ Settings() { }
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Events.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.GET;
6 | import retrofit2.http.Path;
7 | import retrofit2.http.Query;
8 | import retrofit2.http.Url;
9 | import rx.Observable;
10 |
11 | /*package*/ interface Events {
12 |
13 | @GET("devices/{deviceId}/events")
14 | Observable> events(
15 | @NonNull @Path("deviceId") String deviceId,
16 | @Nullable @Query("type") String type,
17 | @Nullable @Query("objectId") String objectId,
18 | @Nullable @Query("since") Long since,
19 | @Nullable @Query("until") Long until,
20 | @Nullable @Query("limit") Integer limit,
21 | @Nullable @Query("sortDir") String sortDir);
22 |
23 | @GET("events/{eventId}")
24 | Observable> event(@NonNull @Path("eventId") String eventId);
25 |
26 | @GET Observable> eventsForUrl(@NonNull @Url String url);
27 |
28 |
29 |
30 | @GET("vehicles/{vehicleId}/events")
31 | Observable> vehicleEvents(
32 | @NonNull @Path("vehicleId") String vehicleId,
33 | @Nullable @Query("type") String type,
34 | @Nullable @Query("objectId") String objectId,
35 | @Nullable @Query("since") Long since,
36 | @Nullable @Query("until") Long until,
37 | @Nullable @Query("limit") Integer limit,
38 | @Nullable @Query("sortDir") String sortDir);
39 |
40 | }
41 |
--------------------------------------------------------------------------------
/android-net/src/test/java/li/vin/net/UsersIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.robolectric.RobolectricTestRunner;
7 | import org.robolectric.annotation.Config;
8 |
9 | import rx.Subscriber;
10 |
11 | import static junit.framework.Assert.assertTrue;
12 |
13 | @RunWith(RobolectricTestRunner.class)
14 | @Config(constants = BuildConfig.class, sdk = 22)
15 | public class UsersIntegrationTests {
16 |
17 | public VinliApp vinliApp;
18 |
19 | @Before
20 | public void setup(){
21 | assertTrue(TestHelper.getAccessToken() != null);
22 |
23 | vinliApp = TestHelper.getVinliApp();
24 | }
25 |
26 | @Test
27 | public void testGetUser(){
28 | vinliApp.currentUser().toBlocking().subscribe(new Subscriber() {
29 | @Override
30 | public void onCompleted() {
31 |
32 | }
33 |
34 | @Override
35 | public void onError(Throwable e) {
36 | System.out.println("Error: " + e.getMessage());
37 | e.printStackTrace();
38 | assertTrue(false);
39 | }
40 |
41 | @Override
42 | public void onNext(User user) {
43 | assertTrue(user.id() != null && user.id().length() > 0);
44 | assertTrue(user.firstName().length() > 0);
45 | assertTrue(user.lastName().length() > 0);
46 | assertTrue(user.email().length() > 0);
47 | assertTrue(user.phone().length() > 0);
48 | }
49 | });
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Rules.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import retrofit2.http.Body;
6 | import retrofit2.http.DELETE;
7 | import retrofit2.http.GET;
8 | import retrofit2.http.POST;
9 | import retrofit2.http.Path;
10 | import retrofit2.http.Query;
11 | import retrofit2.http.Url;
12 | import rx.Observable;
13 |
14 | /*package*/ interface Rules {
15 |
16 | @GET("devices/{deviceId}/rules")
17 | Observable> rules(
18 | @NonNull @Path("deviceId") String deviceId,
19 | @Nullable @Query("limit") Integer limit,
20 | @Nullable @Query("offset") Integer offset);
21 |
22 | @GET("vehicles/{vehicleId}/rules")
23 | Observable> vehicleRules(
24 | @NonNull @Path("vehicleId") String vehicleId,
25 | @Nullable @Query("limit") Integer limit,
26 | @Nullable @Query("offset") Integer offset);
27 |
28 | @GET("rules/{ruleId}")
29 | Observable> rule(
30 | @NonNull @Path("ruleId") String ruleId);
31 |
32 | @POST("devices/{deviceId}/rules")
33 | Observable> create(
34 | @NonNull @Path("deviceId") String deviceId,
35 | @NonNull @Body Rule.Seed ruleSeed);
36 |
37 | @POST("vehicles/{vehicleId}/rules")
38 | Observable> vehicleCreate(
39 | @NonNull @Path("vehicleId") String vehicleId,
40 | @NonNull @Body Rule.Seed ruleSeed);
41 |
42 | @DELETE("rules/{ruleId}")
43 | Observable delete(@NonNull @Path("ruleId") String ruleId);
44 |
45 | @GET Observable> rulesForUrl(@NonNull @Url String url);
46 | }
47 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/ReportCards.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import retrofit2.http.GET;
7 | import retrofit2.http.Path;
8 | import retrofit2.http.Query;
9 | import retrofit2.http.Url;
10 | import rx.Observable;
11 |
12 | public interface ReportCards {
13 |
14 | @GET("report_cards/{reportCardId}") Observable> reportCard(
15 | @NonNull @Path("reportCardId") String reportCardId);
16 |
17 | @GET("vehicles/{vehicleId}/report_cards")
18 | Observable> reportCardsForVehicle(
19 | @NonNull @Path("vehicleId") String vehicleId, @Nullable @Query("since") Long since,
20 | @Nullable @Query("until") Long until, @Nullable @Query("limit") Integer limit,
21 | @Nullable @Query("sortDir") String sortDir);
22 |
23 | @GET("devices/{deviceId}/report_cards") Observable> reportCardsForDevice(
24 | @NonNull @Path("deviceId") String deviceId, @Nullable @Query("since") Long since,
25 | @Nullable @Query("until") Long until, @Nullable @Query("limit") Integer limit,
26 | @Nullable @Query("sortDir") String sortDir);
27 |
28 | @GET("devices/{deviceId}/report_cards/overall")
29 | Observable overallReportCardForDevice(
30 | @NonNull @Path("deviceId") String deviceId);
31 |
32 | @GET("trips/{tripId}/report_cards/_current") Observable> reportCardForTrip(
33 | @NonNull @Path("tripId") String tripId);
34 |
35 | @GET Observable> reportCardsForUrl(@NonNull @Url String url);
36 | }
37 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/VinliSignInHandler.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.app.Activity;
4 | import android.app.PendingIntent;
5 | import android.content.Intent;
6 | import android.support.annotation.NonNull;
7 | import android.support.annotation.Nullable;
8 |
9 | public class VinliSignInHandler {
10 |
11 | private boolean signInRequested;
12 | private VinliApp vinliApp;
13 |
14 | public VinliApp handleOnResume(@NonNull Activity context, @Nullable Intent intent){
15 | loadApp(context, intent);
16 | return vinliApp;
17 | }
18 |
19 | public VinliApp handleOnNewIntent(@NonNull Activity context, @Nullable Intent intent){
20 | loadApp(context, intent);
21 | return vinliApp;
22 | }
23 |
24 | public void handleOnDestroy(Activity context){
25 |
26 | }
27 |
28 | private void loadApp(@NonNull Activity context, @Nullable Intent intent){
29 | if (vinliApp == null) {
30 | vinliApp = intent == null
31 | ? Vinli.loadApp(context)
32 | : Vinli.initApp(context, intent);
33 | if (vinliApp == null) {
34 | if (signInRequested) {
35 | // If a sign in was already requested, it failed or was canceled - finish.
36 | context.finish();
37 | }
38 | }
39 | }
40 | }
41 |
42 | public void signIn(@NonNull Activity context, @NonNull String clientId, @NonNull String redirectURI, @NonNull PendingIntent pendingIntent){
43 | signInRequested = true;
44 | context.setIntent(new Intent());
45 | Vinli.clearApp(context);
46 | vinliApp = null;
47 | Vinli.signIn(context, clientId, redirectURI, pendingIntent);
48 | }
49 |
50 | public VinliApp getVinliApp(){
51 | return vinliApp;
52 | }
53 |
54 | public boolean signedIn(){
55 | return (vinliApp != null);
56 | }
57 |
58 | }
59 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/BatteryStatus.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import auto.parcel.AutoParcel;
7 | import com.google.gson.GsonBuilder;
8 | import com.google.gson.annotations.SerializedName;
9 | import com.google.gson.reflect.TypeToken;
10 | import java.lang.reflect.Type;
11 | import rx.Observable;
12 |
13 | @AutoParcel public abstract class BatteryStatus implements Parcelable {
14 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() {
15 | }.getType();
16 |
17 | /*package*/
18 | static final void registerGson(GsonBuilder gb) {
19 | gb.registerTypeAdapter(BatteryStatus.class,
20 | AutoParcelAdapter.create(AutoParcel_BatteryStatus.class));
21 | gb.registerTypeAdapter(WRAPPED_TYPE,
22 | Wrapped.Adapter.create(BatteryStatus.class, "batteryStatus"));
23 | }
24 |
25 | @Nullable public abstract BatteryStatusColor status();
26 |
27 | @NonNull public abstract String timestamp();
28 |
29 | /*package*/ BatteryStatus() {
30 | }
31 |
32 | public static Observable currentBatteryStatusForVehicle(
33 | @NonNull String vehicleId) {
34 | return Vinli.curApp()
35 | .diagnostics()
36 | .currentBatteryStatus(vehicleId)
37 | .map(Wrapped.pluckItem());
38 | }
39 |
40 | public enum BatteryStatusColor {
41 | @SerializedName("green")GREEN, // indicates a battery voltage reading greater than 11.75 volts
42 | @SerializedName("yellow")YELLOW, // indicates a battery voltage reading less than 11.75 volts and greater than 11.31 volts
43 | @SerializedName("red")RED // indicates a batter voltage reading less than or equal to 11.31 volts
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/VinliEndpoint.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import okhttp3.HttpUrl;
6 |
7 | public enum VinliEndpoint {
8 | AUTH("auth"),
9 | DIAGNOSTICS("diagnostic"),
10 | EVENTS("events"),
11 | PLATFORM("platform"),
12 | RULES("rules"),
13 | TELEMETRY("telemetry"),
14 | TRIPS("trips"),
15 | SAFETY("safety"),
16 | BEHAVIORAL("behavioral"),
17 | DISTANCE("distance"),
18 | DUMMY("dummies");
19 |
20 | static final String DOMAIN_QA = "-qa.";
21 | static final String DOMAIN_DEV = "-dev.";
22 | static final String DOMAIN_DEMO = "-demo.";
23 | static final String DOMAIN_PROD = ".";
24 |
25 | static private String host = "vin.li";
26 | static private String domain = DOMAIN_PROD;
27 |
28 | static synchronized String domain() {
29 | return domain + host;
30 | }
31 |
32 | public static synchronized void setHost(@NonNull String host) {
33 | VinliEndpoint.host = host;
34 | }
35 |
36 | public static synchronized void setDomain(@NonNull String domain) {
37 | VinliEndpoint.domain = domain;
38 | }
39 |
40 | private final HttpUrl mUrl;
41 | private final String subDomain;
42 |
43 | VinliEndpoint(String subDomain) {
44 | this.subDomain = subDomain;
45 | mUrl = new HttpUrl.Builder()
46 | .scheme("https")
47 | .host(subDomain + domain())
48 | .addPathSegment("api")
49 | .addPathSegment("v1")
50 | .addPathSegment("")
51 | .build();
52 | }
53 |
54 | public String getName() {
55 | return this.name();
56 | }
57 |
58 | public String getUrl() {
59 | return mUrl.newBuilder().host(subDomain + domain()).toString();
60 | }
61 |
62 | }
63 |
--------------------------------------------------------------------------------
/gradle/mavenize.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.github.dcendents.android-maven'
2 |
3 | install {
4 | repositories.mavenInstaller {
5 | pom.project {
6 | packaging project.hasProperty('vinli.packaging') \
7 | ? project.property('vinli.packaging') \
8 | : 'aar'
9 | groupId project.property('vinli.groupId')
10 | artifactId project.property('vinli.artifactId')
11 | version project.property('vinli.version')
12 |
13 | name project.property('vinli.name')
14 | description project.property('vinli.desc')
15 | url "https://github.com/vinli/${project.property('vinli.githubRepo')}"
16 |
17 | scm {
18 | url "https://github.com/vinli/${project.property('vinli.githubRepo')}"
19 | connection "scm:git:git://github.com/vinli/${project.property('vinli.githubRepo')}.git"
20 | developerConnection "scm:git:ssh://git@github.com/vinli/${project.property('vinli.githubRepo')}.git"
21 | tag project.property('vinli.version')
22 | }
23 |
24 | issueManagement {
25 | system 'GitHub Issues'
26 | url "https://github.com/vinli/${project.property('vinli.githubRepo')}/issues"
27 | }
28 |
29 | licenses {
30 | license {
31 | name 'MIT'
32 | url 'http://opensource.org/licenses/MIT'
33 | }
34 | }
35 |
36 | organization {
37 | name 'Vinli, Inc.'
38 | url 'https://vin.li'
39 | }
40 |
41 | developers {
42 | developer {
43 | id 'kturney'
44 | name 'Kyle Turney'
45 | }
46 | developer {
47 | id 'cmc5788'
48 | name 'Christopher Casey'
49 | }
50 | }
51 | }
52 | }
53 | }
54 |
55 | uploadArchives {
56 | repositories.mavenDeployer {
57 | pom.artifactId = project.property('vinli.artifactId')
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/utils/RxAdapter.java:
--------------------------------------------------------------------------------
1 | package li.vin.net.utils;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.util.Log;
5 | import android.widget.BaseAdapter;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import rx.Observable;
9 | import rx.Subscriber;
10 | import rx.android.schedulers.AndroidSchedulers;
11 |
12 | public abstract class RxAdapter extends BaseAdapter {
13 | private Observable observable;
14 | private rx.Subscription curSubscription;
15 | private List items = new ArrayList<>();
16 |
17 | private final Subscriber subscriber = new Subscriber() {
18 | @Override public void onCompleted() {
19 | RxAdapter.this.onComplete();
20 | }
21 |
22 | @Override public void onError(Throwable e) {
23 | RxAdapter.this.onError(e);
24 | }
25 |
26 | @Override public void onNext(T t) {
27 | RxAdapter.this.onNext(t);
28 | items.add(t);
29 | RxAdapter.this.notifyDataSetChanged();
30 | }
31 | };
32 |
33 | @Override public int getCount() {
34 | return items.size();
35 | }
36 |
37 | @Override public T getItem(int position) {
38 | return items.get(position);
39 | }
40 |
41 | protected void onComplete() {
42 | Log.e(this.getClass().getSimpleName(), "onComplete");
43 | }
44 |
45 | protected void onError(Throwable e) {
46 | Log.e(this.getClass().getSimpleName(), "onError", e);
47 | }
48 |
49 | protected void onNext(T t) { }
50 |
51 | public rx.Subscription subscribe(@NonNull Observable observable) {
52 | return subscribe(null, observable);
53 | }
54 |
55 | /** Use {@link #subscribe(Observable)} instead. */
56 | @Deprecated
57 | public rx.Subscription subscribe(Object context, @NonNull Observable observable) {
58 | if (curSubscription != null && !curSubscription.isUnsubscribed()) {
59 | curSubscription.unsubscribe();
60 | }
61 |
62 | this.observable = bind(observable);
63 |
64 | curSubscription = bind(observable).subscribe(subscriber);
65 |
66 | if (!items.isEmpty()) {
67 | items.clear();
68 | this.notifyDataSetChanged();
69 | }
70 |
71 | return curSubscription;
72 | }
73 |
74 | private static final Observable bind(@NonNull Observable observable) {
75 | return observable.observeOn(AndroidSchedulers.mainThread());
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Subscriptions.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import retrofit2.http.Body;
7 | import retrofit2.http.DELETE;
8 | import retrofit2.http.GET;
9 | import retrofit2.http.POST;
10 | import retrofit2.http.PUT;
11 | import retrofit2.http.Path;
12 | import retrofit2.http.Query;
13 | import retrofit2.http.Url;
14 | import rx.Observable;
15 |
16 | /*package*/ interface Subscriptions {
17 |
18 | @GET("devices/{deviceId}/subscriptions")
19 | Observable> subscriptions(
20 | @NonNull @Path("deviceId") String deviceId,
21 | @Nullable @Query("limit") Integer limit,
22 | @Nullable @Query("offset") Integer offset,
23 | @Nullable @Query("objectId") String objectId,
24 | @Nullable @Query("objectType") String objectType);
25 |
26 | @GET("vehicles/{vehicleId}/subscriptions")
27 | Observable> vehicleSubscriptions(
28 | @NonNull @Path("vehicleId") String vehicleId,
29 | @Nullable @Query("limit") Integer limit,
30 | @Nullable @Query("offset") Integer offset,
31 | @Nullable @Query("objectId") String objectId,
32 | @Nullable @Query("objectType") String objectType);
33 |
34 | @POST("vehicles/{vehicleId}/subscriptions")
35 | Observable> vehicleCreate(
36 | @NonNull @Path("vehicleId") String vehicleId,
37 | @NonNull @Body Subscription.SeedCreate seedCreate);
38 |
39 | @GET("subscriptions/{subscriptionId}")
40 | Observable> subscription(
41 | @NonNull @Path("subscriptionId") String subscriptionId);
42 |
43 | @POST("devices/{deviceId}/subscriptions")
44 | Observable> create(
45 | @NonNull @Path("deviceId") String deviceId,
46 | @NonNull @Body Subscription.SeedCreate seedCreate);
47 |
48 | @PUT("devices/{deviceId}/subscriptions/{subscriptionId}")
49 | Observable> edit(
50 | @NonNull @Path("deviceId") String deviceId,
51 | @NonNull @Path("subscriptionId") String subscriptionId,
52 | @NonNull @Body Subscription.SeedEdit seedEdit);
53 |
54 | @DELETE("subscriptions/{subscriptionId}")
55 | Observable delete(@NonNull @Path("subscriptionId") String subscriptionId);
56 |
57 | @GET Observable> subscriptionsForUrl(@NonNull @Url String url);
58 |
59 | }
60 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Wrapped.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import com.google.gson.Gson;
7 | import com.google.gson.TypeAdapter;
8 | import com.google.gson.stream.JsonReader;
9 | import com.google.gson.stream.JsonWriter;
10 |
11 | import java.io.IOException;
12 | import java.util.Locale;
13 |
14 | import auto.parcel.AutoParcel;
15 | import rx.functions.Func1;
16 |
17 | @AutoParcel
18 | /*package*/ abstract class Wrapped {
19 | public static final Func1 PLUCK_ITEM = new Func1, Object>() {
20 | @Override public Object call(Wrapped> tWrapped) {
21 | return tWrapped.item();
22 | }
23 | };
24 |
25 | @SuppressWarnings("unchecked")
26 | public static final Func1, T> pluckItem() {
27 | return PLUCK_ITEM;
28 | }
29 |
30 | public static final Wrapped create(@NonNull T item) {
31 | return new AutoParcel_Wrapped<>(item);
32 | }
33 |
34 | @Nullable public abstract T item();
35 |
36 | /*package*/ static final class Adapter extends TypeAdapter> {
37 | public static final Adapter create(Class itemCls) {
38 | return create(itemCls, itemCls.getSimpleName().toLowerCase(Locale.US));
39 | }
40 |
41 | public static final Adapter create(Class itemCls, String itemName) {
42 | return new Adapter<>(itemCls, itemName);
43 | }
44 |
45 | private final String itemName;
46 | private final Class wrappedCls;
47 |
48 | private Gson gson;
49 |
50 | private Adapter(Class wrappedCls, String itemName) {
51 | this.itemName = itemName;
52 | this.wrappedCls = wrappedCls;
53 | }
54 |
55 | @Override public void write(JsonWriter out, Wrapped value) throws IOException {
56 | if (gson == null) {
57 | gson = Vinli.curApp().gson();
58 | }
59 |
60 | out.beginObject();
61 | out.name(itemName); gson.toJson(value.item(), wrappedCls, out);
62 | out.endObject();
63 | }
64 |
65 | @Override public Wrapped read(JsonReader in) throws IOException {
66 | if (gson == null) {
67 | gson = Vinli.curApp().gson();
68 | }
69 |
70 | in.beginObject();
71 |
72 | final String name = in.nextName();
73 | if (!itemName.equals(name)) {
74 | throw new IOException(name + " does not match expected name " + itemName);
75 | }
76 |
77 | final T item = gson.fromJson(in, wrappedCls);
78 |
79 | in.endObject();
80 |
81 | return new AutoParcel_Wrapped<>(item);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Distances.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import retrofit2.http.Body;
5 | import retrofit2.http.DELETE;
6 | import retrofit2.http.GET;
7 | import retrofit2.http.Header;
8 | import retrofit2.http.POST;
9 | import retrofit2.http.Path;
10 | import retrofit2.http.Query;
11 | import retrofit2.http.Url;
12 | import rx.Observable;
13 |
14 | public interface Distances {
15 |
16 | @GET("vehicles/{vehicleId}/distances")
17 | Observable distances(
18 | @Path("vehicleId") String vehicleId,
19 | @Query("since") Long since,
20 | @Query("until") Long until,
21 | @Header("x-vinli-unit") String unit);
22 |
23 | @GET("vehicles/{vehicleId}/distances/_best")
24 | Observable> bestDistance(
25 | @Path("vehicleId") String vehicleId,
26 | @Header("x-vinli-unit") String unit);
27 |
28 | @POST("vehicles/{vehicleId}/odometers")
29 | Observable> createOdometerReport(
30 | @Path("vehicleId") String vehicleId,
31 | @Body Odometer.Seed odometerSeed);
32 |
33 | @GET("vehicles/{vehicleId}/odometers")
34 | Observable> odometerReports(
35 | @Path("vehicleId") String vehicleId,
36 | @Query("since") Long since,
37 | @Query("until") Long until,
38 | @Query("limit") Integer limit,
39 | @Query("sortDir") String sortDir);
40 |
41 | @GET("odometers/{odometerId}")
42 | Observable> odometerReport(
43 | @Path("odometerId") String odometerId);
44 |
45 | @DELETE("odometers/{odometerId}")
46 | Observable deleteOdometerReport(
47 | @Path("odometerId") String odometerId);
48 |
49 | @GET Observable> odometerReportsForUrl(@NonNull @Url String url);
50 |
51 | @POST("vehicles/{vehicleId}/odometer_triggers")
52 | Observable> createOdometerTrigger(
53 | @Path("vehicleId") String vehicleId,
54 | @Body OdometerTrigger.Seed odometerTrigger);
55 |
56 | @GET("odometer_triggers/{odometerTriggerId}")
57 | Observable> odometerTrigger(
58 | @Path("odometerTriggerId") String odometerTriggerId);
59 |
60 | @DELETE("odometer_triggers/{odometerTriggerId}")
61 | Observable deleteOdometerTrigger(
62 | @Path("odometerTriggerId") String odometerTriggerId);
63 |
64 | @GET("vehicles/{vehicleId}/odometer_triggers")
65 | Observable> odometerTriggers(
66 | @Path("vehicleId") String vehicleId,
67 | @Query("since") Long since,
68 | @Query("until") Long until,
69 | @Query("limit") Integer limit,
70 | @Query("sortDir") String sortDir);
71 |
72 | @GET Observable> odometerTriggersForUrl(@NonNull @Url String url);
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/ObserverManager.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Looper;
4 | import android.support.annotation.NonNull;
5 | import java.util.HashSet;
6 | import java.util.Iterator;
7 | import java.util.Set;
8 | import rx.Observable;
9 | import rx.Observer;
10 | import rx.Subscription;
11 | import rx.android.schedulers.AndroidSchedulers;
12 |
13 | /**
14 | * Created by christophercasey on 8/3/15.
15 | *
16 | * Wraps Observable subscriptions in simple managed observables that can automatically bind to
17 | * Activities or Fragments and observe on the UI thread.
18 | */
19 | /*package*/ final class ObserverManager {
20 |
21 | /** Register an observer. Always observes on the UI thread. Will throw an unchecked exception
22 | * if observer is already registered. */
23 | /*package*/ static void registerObserver(@NonNull Observer cb,
24 | @NonNull Observable observable) {
25 | if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
26 | throw new IllegalStateException("Must be called on UI thread.");
27 | }
28 | Subscription sub = observable.observeOn(AndroidSchedulers.mainThread()).subscribe(cb);
29 | if (!callbacks().add(new CallbackSubscriptionTuple(cb, sub))) {
30 | throw new IllegalStateException("callback already registered.");
31 | }
32 | }
33 |
34 | /** Permissively attempt to unregister an already-registered observer. */
35 | /*package*/ static void unregisterObserver(@NonNull Observer cb) {
36 | if (Thread.currentThread() != Looper.getMainLooper().getThread()) {
37 | throw new IllegalStateException("Must be called on UI thread.");
38 | }
39 | for (Iterator i=callbacks().iterator(); i.hasNext(); ) {
40 | CallbackSubscriptionTuple cbSub = i.next();
41 | if (cbSub.cb.equals(cb)) {
42 | cbSub.sub.unsubscribe();
43 | i.remove();
44 | return;
45 | }
46 | }
47 | }
48 |
49 | private static final class InitOnDemandHolder {
50 | private static final Set sCallbacks = new HashSet<>();
51 | }
52 |
53 | private static Set callbacks() {
54 | return InitOnDemandHolder.sCallbacks;
55 | }
56 |
57 | private static final class CallbackSubscriptionTuple {
58 | final Observer> cb;
59 | final rx.Subscription sub;
60 |
61 | CallbackSubscriptionTuple(Observer> cb, rx.Subscription sub) {
62 | this.cb = cb;
63 | this.sub = sub;
64 | }
65 |
66 | @Override public boolean equals(Object o) {
67 | if (this == o) return true;
68 | if (o == null || getClass() != o.getClass()) return false;
69 |
70 | CallbackSubscriptionTuple that = (CallbackSubscriptionTuple) o;
71 |
72 | return cb.equals(that.cb);
73 | }
74 |
75 | @Override public int hashCode() {
76 | return cb.hashCode();
77 | }
78 | }
79 |
80 | private ObserverManager() { }
81 | }
82 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/DistanceList.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import auto.parcel.AutoParcel;
7 | import com.google.gson.GsonBuilder;
8 | import com.google.gson.reflect.TypeToken;
9 |
10 | import java.lang.reflect.Type;
11 | import java.util.Date;
12 | import java.util.List;
13 | import rx.Observable;
14 |
15 | @AutoParcel
16 | public abstract class DistanceList implements VinliItem{
17 |
18 | /*package*/ static final void registerGson(GsonBuilder gb) {
19 | gb.registerTypeAdapter(DistanceList.class, AutoParcelAdapter.create(AutoParcel_DistanceList.class));
20 |
21 | Distance.registerGson(gb);
22 | }
23 |
24 | public abstract List distances();
25 |
26 | public static Observable distancesWithVehicleId(@NonNull String vehicleId) {
27 | return distancesWithVehicleId(vehicleId, (Long) null, null, null);
28 | }
29 |
30 | public static Observable distancesWithVehicleId(@NonNull String vehicleId,
31 | @Nullable Long sinceMs, @Nullable Long untilMs, @Nullable DistanceUnit unit) {
32 | return Vinli.curApp()
33 | .distances()
34 | .distances(vehicleId, sinceMs, untilMs, (unit == null) ? null : unit.getDistanceUnitStr());
35 | }
36 |
37 | @Deprecated
38 | public static Observable distancesWithVehicleId(@NonNull String vehicleId,
39 | @Nullable Date since, @Nullable Date until, @Nullable DistanceUnit unit) {
40 | Long sinceMs = since == null ? null : since.getTime();
41 | Long untilMs = until == null ? null : until.getTime();
42 | return Vinli.curApp()
43 | .distances()
44 | .distances(vehicleId, sinceMs, untilMs, (unit == null) ? null : unit.getDistanceUnitStr());
45 | }
46 |
47 | @AutoParcel
48 | public static abstract class Distance implements Parcelable{
49 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() { }.getType();
50 |
51 | /*package*/ static final void registerGson(GsonBuilder gb) {
52 | gb.registerTypeAdapter(Distance.class, AutoParcelAdapter.create(AutoParcel_DistanceList_Distance.class));
53 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(Distance.class));
54 | }
55 |
56 | public abstract Double confidenceMin();
57 | public abstract Double confidenceMax();
58 | public abstract Double value();
59 | public abstract String lastOdometerDate();
60 |
61 | public static Observable bestDistanceWithVehicleId(@NonNull String vehicleId) {
62 | return bestDistanceWithVehicleId(vehicleId, null);
63 | }
64 |
65 | public static Observable bestDistanceWithVehicleId(@NonNull String vehicleId,
66 | @Nullable DistanceUnit unit) {
67 | return Vinli.curApp()
68 | .distances()
69 | .bestDistance(vehicleId, (unit == null) ? null : unit.getDistanceUnitStr())
70 | .map(Wrapped.pluckItem());
71 | }
72 | }
73 |
74 | }
75 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Coordinate.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import android.support.annotation.NonNull;
5 |
6 | import com.google.gson.Gson;
7 | import com.google.gson.GsonBuilder;
8 | import com.google.gson.TypeAdapter;
9 | import com.google.gson.stream.JsonReader;
10 | import com.google.gson.stream.JsonWriter;
11 |
12 | import java.io.IOException;
13 |
14 | import auto.parcel.AutoParcel;
15 |
16 | @AutoParcel
17 | public abstract class Coordinate implements Parcelable {
18 | /*package*/ static final void registerGson(GsonBuilder gb) {
19 | gb.registerTypeAdapter(
20 | Coordinate.class,
21 | new CoordinateAdapter());
22 |
23 | gb.registerTypeAdapter(AutoParcel_Coordinate.Seed.class, new Seed.Adapter());
24 | }
25 |
26 | /*package*/ static final Builder builder() {
27 | return new AutoParcel_Coordinate.Builder();
28 | }
29 |
30 | public abstract float lon();
31 | public abstract float lat();
32 |
33 | public static final Seed.Builder create(){
34 | return new AutoParcel_Coordinate_Seed.Builder();
35 | }
36 |
37 | @AutoParcel.Builder
38 | /*package*/ interface Builder {
39 | Builder lon(float f);
40 | Builder lat(float f);
41 |
42 | Coordinate build();
43 | }
44 |
45 | /*package*/ Coordinate() { }
46 |
47 | @AutoParcel
48 | public static abstract class Seed{
49 | @NonNull public abstract float lon();
50 | @NonNull public abstract float lat();
51 |
52 | /*package*/ Seed() {}
53 |
54 | @AutoParcel.Builder
55 | public static abstract class Builder{
56 | public abstract Builder lat(@NonNull float latitude);
57 | public abstract Builder lon(@NonNull float longitude);
58 |
59 | public abstract Seed build();
60 |
61 | /*package*/ Builder(){}
62 | }
63 |
64 | /*package */ static final class Adapter extends TypeAdapter{
65 |
66 | @Override
67 | public void write(com.google.gson.stream.JsonWriter out, Seed value) throws IOException {
68 | out.beginArray();
69 | out.value(value.lon());
70 | out.value(value.lat());
71 | out.endArray();
72 | }
73 |
74 | @Override public Coordinate.Seed read(JsonReader in) throws IOException {
75 | throw new UnsupportedOperationException("reading a Coordinate.Seed is not supported");
76 | }
77 | }
78 | }
79 |
80 | private static final class CoordinateAdapter extends TypeAdapter {
81 |
82 | @Override public void write(JsonWriter out, Coordinate value) throws IOException {
83 | out.beginArray();
84 | out.value(value.lon());
85 | out.value(value.lat());
86 | out.endArray();
87 | }
88 |
89 | @Override public Coordinate read(JsonReader in) throws IOException {
90 | final Coordinate.Builder b = Coordinate.builder();
91 |
92 | in.beginArray();
93 | b.lon((float) in.nextDouble());
94 | b.lat((float) in.nextDouble());
95 | in.endArray();
96 |
97 | return b.build();
98 | }
99 | }
100 | }
101 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Dtc.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import auto.parcel.AutoParcel;
5 | import com.google.gson.GsonBuilder;
6 | import com.google.gson.reflect.TypeToken;
7 | import java.lang.reflect.Type;
8 | import rx.Observable;
9 |
10 | @AutoParcel
11 | public abstract class Dtc implements VinliItem {
12 | /*package*/ static final Type TIME_SERIES_TYPE = new TypeToken>() { }.getType();
13 |
14 | /*package*/ static final void registerGson(GsonBuilder gb) {
15 | gb.registerTypeAdapter(Dtc.class, AutoParcelAdapter.create(AutoParcel_Dtc.class));
16 | gb.registerTypeAdapter(Links.class, AutoParcelAdapter.create(AutoParcel_Dtc_Links.class));
17 | gb.registerTypeAdapter(Code.class, AutoParcelAdapter.create(AutoParcel_Dtc_Code.class));
18 | gb.registerTypeAdapter(Code.TwoByte.class, AutoParcelAdapter.create(AutoParcel_Dtc_Code_TwoByte.class));
19 | gb.registerTypeAdapter(Code.ThreeByte.class, AutoParcelAdapter.create(AutoParcel_Dtc_Code_ThreeByte.class));
20 | gb.registerTypeAdapter(Code.WRAPPED_TYPE, Wrapped.Adapter.create(Code.class, "code"));
21 | gb.registerTypeAdapter(Code.PAGE_TYPE, Page.Adapter.create(Code.PAGE_TYPE, Code.class, "codes"));
22 | gb.registerTypeAdapter(
23 | TIME_SERIES_TYPE, TimeSeries.Adapter.create(TIME_SERIES_TYPE, Dtc.class, "codes"));
24 | }
25 |
26 | public abstract String start();
27 | public abstract String stop();
28 | public abstract String number();
29 | public abstract String vehicleId();
30 | public abstract String deviceId();
31 | public abstract String description();
32 |
33 | public Observable device() {
34 | return Vinli.curApp().device(deviceId());
35 | }
36 |
37 | public Observable vehicle() {
38 | return Vinli.curApp().vehicle(vehicleId());
39 | }
40 |
41 | public Observable diagnose() {
42 | return Vinli.curApp().diagnoseDtcCode(number());
43 | }
44 |
45 | /*package*/ abstract Links links();
46 |
47 | /*package*/ Dtc() { }
48 |
49 | @AutoParcel
50 | /*package*/ static abstract class Links implements Parcelable {
51 | public abstract String self();
52 | public abstract String device();
53 | public abstract String vehicle();
54 |
55 | /*package*/ Links() { }
56 | }
57 |
58 | @AutoParcel
59 | public static abstract class Code implements VinliItem {
60 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() { }.getType();
61 | /*package*/ static final Type PAGE_TYPE = new TypeToken>() { }.getType();
62 |
63 | public abstract String make();
64 |
65 | /*package*/ Code() { }
66 |
67 | @AutoParcel
68 | /*package*/ static abstract class TwoByte implements Parcelable {
69 | public abstract String number();
70 | public abstract String description();
71 |
72 | /*package*/ TwoByte() { }
73 | }
74 |
75 | @AutoParcel
76 | /*package*/ static abstract class ThreeByte implements Parcelable {
77 | public abstract String number();
78 | public abstract String ftb();
79 | public abstract String fault();
80 | public abstract String description();
81 |
82 | /*package*/ ThreeByte() { }
83 | }
84 | }
85 | }
86 |
87 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Duktaper.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import java.lang.reflect.Method;
5 | import rx.exceptions.Exceptions;
6 |
7 | /**
8 | * Wrap around Duktape JS interpreter using dynamic class loading and reflection to avoid potential
9 | * issues with the the native libs not loading properly on some devices. Also, this way, if devs
10 | * choose to they can manually exclude the somewhat bloated Duktape deps from gradle and our SDK
11 | * won't blow up - it will gracefully fail, as long as exceptions around creation are caught.
12 | */
13 | final class Duktaper {
14 |
15 | @NonNull
16 | public static Duktaper create() {
17 | try {
18 | Class> duktapeCls = Class.forName("com.squareup.duktape.Duktape");
19 | if (duktapeCls == null) throw new NullPointerException();
20 | Object duktapeInst = duktapeCls.getDeclaredMethod("create").invoke(null);
21 | if (duktapeInst == null) throw new NullPointerException();
22 | return new Duktaper(duktapeCls, duktapeInst);
23 | } catch (UnsatisfiedLinkError ule) {
24 | throw Exceptions.propagate(new RuntimeException("cannot link Duktape lib naturally."));
25 | } catch (Exception e) {
26 | throw Exceptions.propagate(e);
27 | }
28 | }
29 |
30 | @NonNull private final Class> duktapeCls;
31 | @NonNull private final Object duktapeInst;
32 |
33 | // cache reflective method lookups that might be called frequently
34 | private volatile Method evaluateStrStr;
35 | private volatile Method evaluateStr;
36 |
37 | private Duktaper(@NonNull Class> duktapeCls, @NonNull Object duktapeInst) {
38 | this.duktapeCls = duktapeCls;
39 | this.duktapeInst = duktapeInst;
40 | }
41 |
42 | public String evaluate(String script, String fileName) {
43 | try {
44 | return (String) evaluateStrStr().invoke(duktapeInst, script, fileName);
45 | } catch (Exception e) {
46 | throw Exceptions.propagate(e);
47 | }
48 | }
49 |
50 | public String evaluate(String script) {
51 | try {
52 | return (String) evaluateStr().invoke(duktapeInst, script);
53 | } catch (Exception e) {
54 | throw Exceptions.propagate(e);
55 | }
56 | }
57 |
58 | public void close() {
59 | try {
60 | duktapeCls.getDeclaredMethod("close").invoke(duktapeInst);
61 | } catch (Exception e) {
62 | throw Exceptions.propagate(e);
63 | }
64 | }
65 |
66 | // lazy init with DCL:
67 |
68 | private Method evaluateStrStr() throws NoSuchMethodException {
69 | Method result = evaluateStrStr;
70 | if (result == null) {
71 | synchronized (this) {
72 | result = evaluateStrStr;
73 | if (result == null) {
74 | evaluateStrStr = result = //
75 | duktapeCls.getDeclaredMethod("evaluate", String.class, String.class);
76 | }
77 | }
78 | }
79 | return result;
80 | }
81 |
82 | private Method evaluateStr() throws NoSuchMethodException {
83 | Method result = evaluateStr;
84 | if (result == null) {
85 | synchronized (this) {
86 | result = evaluateStr;
87 | if (result == null) {
88 | evaluateStr = result = //
89 | duktapeCls.getDeclaredMethod("evaluate", String.class);
90 | }
91 | }
92 | }
93 | return result;
94 | }
95 | }
96 |
--------------------------------------------------------------------------------
/android-net/src/androidTest/java/li/vin/net/InstTestHelper.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import java.security.cert.CertificateException;
4 |
5 | import javax.net.ssl.HostnameVerifier;
6 | import javax.net.ssl.SSLContext;
7 | import javax.net.ssl.SSLSession;
8 | import javax.net.ssl.SSLSocketFactory;
9 | import javax.net.ssl.TrustManager;
10 | import javax.net.ssl.X509TrustManager;
11 |
12 | import okhttp3.OkHttpClient;
13 |
14 | /**
15 | * Created by JoshBeridon on 12/2/16.
16 | */
17 |
18 | public class InstTestHelper {
19 | private static VinliApp vinliApp;
20 |
21 | public static VinliApp getVinliApp() {
22 |
23 | if (vinliApp == null) {
24 | VinliApp.clientBuilder = generateUnsafeBuilder();
25 |
26 | VinliEndpoint.setDomain(VinliEndpoint.DOMAIN_DEV);
27 |
28 | vinliApp = new VinliApp(getAccessToken());
29 | Vinli.setCurrentApp(vinliApp);
30 | }
31 |
32 | return vinliApp;
33 | }
34 |
35 | public static String getAccessToken() {
36 | String accessToken = BuildConfig.ACCESS_TOKEN;
37 | return accessToken.equals("DEFAULT_ACCESS_TOKEN") ? null : accessToken;
38 | }
39 |
40 | public static String getDummyId() {
41 | String dummyId = BuildConfig.DUMMY_ID;
42 | return dummyId.equals("DEFAULT_DUMMY_ID") ? null : dummyId;
43 | }
44 |
45 | public static String getRouteId() {
46 | String routeId = BuildConfig.ROUTE_ID;
47 | return routeId.equals("DEFAULT_ROUTE_ID") ? null : routeId;
48 | }
49 |
50 | public static String getVIN() {
51 | String vin = BuildConfig.VIN;
52 | return vin.equals("DEFAULT_VIN") ? null : vin;
53 | }
54 |
55 | public static OkHttpClient.Builder generateUnsafeBuilder() {
56 | try {
57 | // Create a trust manager that does not validate certificate chains
58 |
59 | final X509TrustManager x509TrustManager = new X509TrustManager() {
60 | @Override
61 | public void checkClientTrusted(java.security.cert.X509Certificate[] chain, String authType)
62 | throws CertificateException {
63 | }
64 |
65 | @Override
66 | public void checkServerTrusted(java.security.cert.X509Certificate[] chain, String authType)
67 | throws CertificateException {
68 | }
69 |
70 | @Override public java.security.cert.X509Certificate[] getAcceptedIssuers() {
71 | return new java.security.cert.X509Certificate[] {};
72 | }
73 | };
74 |
75 | final TrustManager[] trustAllCerts = new TrustManager[] {
76 | x509TrustManager
77 | };
78 |
79 | // Install the all-trusting trust manager
80 | final SSLContext sslContext = SSLContext.getInstance("TLS");
81 | sslContext.init(null, trustAllCerts, new java.security.SecureRandom());
82 | // Create an ssl socket factory with our all-trusting manager
83 | final SSLSocketFactory sslSocketFactory = sslContext.getSocketFactory();
84 |
85 | final HostnameVerifier hostnameVerifier = new HostnameVerifier() {
86 | @Override public boolean verify(final String hostname, final SSLSession session) {
87 | return true;
88 | }
89 | };
90 |
91 | return new OkHttpClient.Builder().sslSocketFactory(sslSocketFactory, x509TrustManager)
92 | .hostnameVerifier(hostnameVerifier);
93 | } catch (Exception e) {
94 | throw new RuntimeException(e);
95 | }
96 | }
97 | }
98 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Collision.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 |
6 | import com.google.gson.GsonBuilder;
7 | import com.google.gson.reflect.TypeToken;
8 |
9 | import java.lang.reflect.Type;
10 |
11 | import auto.parcel.AutoParcel;
12 | import java.util.Date;
13 | import rx.Observable;
14 |
15 | @AutoParcel
16 | public abstract class Collision implements VinliItem{
17 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() { }.getType();
18 | /*package*/ static final Type TIME_SERIES_TYPE = new TypeToken>() { }.getType();
19 |
20 | /*package*/ static final void registerGson(GsonBuilder gb) {
21 | gb.registerTypeAdapter(Collision.class, AutoParcelAdapter.create(AutoParcel_Collision.class));
22 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(Collision.class));
23 | gb.registerTypeAdapter(TIME_SERIES_TYPE, TimeSeries.Adapter.create(TIME_SERIES_TYPE, Collision.class));
24 | }
25 |
26 | @NonNull public abstract String deviceId();
27 | @NonNull public abstract String vehicleId();
28 | @NonNull public abstract String timestamp();
29 | @Nullable public abstract Location location();
30 |
31 | public static Observable collisionWithId(@NonNull String collisionId) {
32 | return Vinli.curApp().collision(collisionId);
33 | }
34 |
35 | public static Observable> collisionsWithDeviceId(@NonNull String deviceId) {
36 | return collisionsWithDeviceId(deviceId, (Long) null, null, null, null);
37 | }
38 |
39 | @Deprecated
40 | public static Observable> collisionsWithDeviceId(@NonNull String deviceId,
41 | @Nullable Date since, @Nullable Date until, @Nullable Integer limit,
42 | @Nullable String sortDir) {
43 | Long sinceMs = since == null ? null : since.getTime();
44 | Long untilMs = until == null ? null : until.getTime();
45 | return Vinli.curApp()
46 | .collisions()
47 | .collisionsForDevice(deviceId, sinceMs, untilMs, limit, sortDir);
48 | }
49 |
50 | public static Observable> collisionsWithDeviceId(@NonNull String deviceId,
51 | @Nullable Long sinceMs, @Nullable Long untilMs, @Nullable Integer limit,
52 | @Nullable String sortDir) {
53 | return Vinli.curApp().collisions().collisionsForDevice(deviceId, sinceMs, untilMs, limit, sortDir);
54 | }
55 |
56 | public static Observable> collisionsWithVehicleId(
57 | @NonNull String vehicleId) {
58 | return collisionsWithVehicleId(vehicleId, (Long) null, null, null, null);
59 | }
60 |
61 | @Deprecated
62 | public static Observable> collisionsWithVehicleId(@NonNull String vehicleId,
63 | @Nullable Date since, @Nullable Date until, @Nullable Integer limit,
64 | @Nullable String sortDir) {
65 | Long sinceMs = since == null ? null : since.getTime();
66 | Long untilMs = until == null ? null : until.getTime();
67 | return collisionsWithVehicleId(vehicleId, sinceMs, untilMs, limit, sortDir);
68 | }
69 |
70 | public static Observable> collisionsWithVehicleId(@NonNull String vehicleId,
71 | @Nullable Long sinceMs, @Nullable Long untilMs, @Nullable Integer limit,
72 | @Nullable String sortDir) {
73 | return Vinli.curApp()
74 | .collisions()
75 | .collisionsForVehicle(vehicleId, sinceMs, untilMs, limit, sortDir);
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/utils/PageAdapter.java:
--------------------------------------------------------------------------------
1 | package li.vin.net.utils;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.util.Log;
5 | import android.widget.BaseAdapter;
6 | import java.util.ArrayList;
7 | import java.util.List;
8 | import li.vin.net.Page;
9 | import li.vin.net.VinliItem;
10 | import rx.Observable;
11 | import rx.Subscriber;
12 | import rx.Subscription;
13 | import rx.android.schedulers.AndroidSchedulers;
14 | import rx.subscriptions.Subscriptions;
15 |
16 | public abstract class PageAdapter extends BaseAdapter {
17 | private final List> pages = new ArrayList<>();
18 | private final Subscriber> subscriber = new Subscriber>() {
19 | @Override public void onCompleted() { }
20 |
21 | @Override public void onError(Throwable e) {
22 | PageAdapter.this.onPageError(e);
23 | }
24 |
25 | @Override public void onNext(Page page) {
26 | PageAdapter.this.onPageLoaded(page);
27 | pages.add(page);
28 | count += page.size();
29 | PageAdapter.this.notifyDataSetChanged();
30 | }
31 | };
32 |
33 | private int count = 0;
34 |
35 | protected void onPageLoaded(Page page) { }
36 |
37 | protected void onPageError(Throwable e) {
38 | Log.e(this.getClass().getSimpleName(), "onPageError", e);
39 | }
40 |
41 | @Override public int getCount() {
42 | return count;
43 | }
44 |
45 | @Override public T getItem(int position) {
46 | if (position < 0 || position >= count) {
47 | throw new IllegalArgumentException(position + " is outside of the acceptable range 0.." + count);
48 | }
49 |
50 | for (int i = 0, il = pages.size(), pos = 0; i < il; ++i) {
51 | final Page page = pages.get(i);
52 | final int nextPos = pos + page.size();
53 | if (nextPos <= position) {
54 | pos = nextPos;
55 | continue;
56 | }
57 |
58 | final int itemPos = position - pos;
59 | return page.getItems().get(itemPos);
60 | }
61 |
62 | throw new AssertionError("should never get here");
63 | }
64 |
65 | @Override public long getItemId(int position) {
66 | return getItem(position).id().hashCode();
67 | }
68 |
69 | public boolean hasNext() {
70 | return pages.isEmpty() || pages.get(pages.size() - 1).hasNextPage();
71 | }
72 |
73 | public Subscription subscribe(@NonNull Observable> observable) {
74 | return subscribe(null, observable);
75 | }
76 |
77 | /** Use {@link #subscribe(Observable)} instead. */
78 | @Deprecated
79 | public Subscription subscribe(Object context, @NonNull Observable> observable) {
80 | if (!pages.isEmpty()) {
81 | pages.clear();
82 | count = 0;
83 | this.notifyDataSetChanged();
84 | }
85 |
86 | return bind(observable).subscribe(subscriber);
87 | }
88 |
89 | public Subscription loadNext() {
90 | return loadNext(null);
91 | }
92 |
93 | /** Use {@link #loadNext()} instead. */
94 | @Deprecated
95 | public Subscription loadNext(Object context) {
96 | if (hasNext()) {
97 | return bind(pages.get(pages.size() - 1).loadNextPage()).subscribe(subscriber);
98 | } else {
99 | return Subscriptions.empty();
100 | }
101 | }
102 |
103 | private static Observable> bind(@NonNull Observable> observable) {
104 | return observable.observeOn(AndroidSchedulers.mainThread());
105 | }
106 | }
107 |
--------------------------------------------------------------------------------
/android-net/src/androidTest/java/li/vin/net/StreamingInstTest.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.test.filters.SmallTest;
4 | import android.support.test.runner.AndroidJUnit4;
5 | import java.util.concurrent.atomic.AtomicInteger;
6 | import org.junit.Before;
7 | import org.junit.Test;
8 | import org.junit.runner.RunWith;
9 | import rx.Subscriber;
10 | import rx.android.schedulers.AndroidSchedulers;
11 |
12 | import static junit.framework.Assert.assertTrue;
13 |
14 | /**
15 | * Created by JoshBeridon on 12/2/16.
16 | */
17 |
18 | @RunWith(AndroidJUnit4.class) @SmallTest public class StreamingInstTest {
19 |
20 | public VinliApp vinliApp;
21 |
22 | @Before public void setup() {
23 | assertTrue(InstTestHelper.getAccessToken() != null);
24 |
25 | vinliApp = InstTestHelper.getVinliApp();
26 | }
27 |
28 | @Test public void streamTest() {
29 | final AtomicInteger messages = new AtomicInteger(0);
30 | final String deviceId;
31 | deviceId =
32 | vinliApp.dummies().dummies(null, null).toBlocking().first().getItems().get(0).deviceId();
33 | System.out.println(deviceId);
34 | vinliApp.dummies()
35 | .currentRun(InstTestHelper.getDummyId())
36 | .toBlocking()
37 | .subscribe(new Subscriber>() {
38 | @Override public void onCompleted() {
39 |
40 | }
41 |
42 | @Override public void onError(Throwable e) {
43 | System.out.println("Error: " + e.getMessage());
44 | e.printStackTrace();
45 | assertTrue(false);
46 | }
47 |
48 | @Override public void onNext(Wrapped runWrapped) {
49 | if (runWrapped.item() != null) {
50 | vinliApp.dummies()
51 | .deleteRun(InstTestHelper.getDummyId())
52 | .toBlocking()
53 | .subscribe(new Subscriber() {
54 | @Override public void onCompleted() {
55 | }
56 |
57 | @Override public void onError(Throwable e) {
58 | System.out.println("Error: " + e.getMessage());
59 | e.printStackTrace();
60 | assertTrue(false);
61 | }
62 |
63 | @Override public void onNext(Void aVoid) {
64 |
65 | }
66 | });
67 | }
68 | }
69 | });
70 |
71 | Dummy.Run.create()
72 | .routeId(InstTestHelper.getRouteId())
73 | .vin(InstTestHelper.getVIN())
74 | .save(InstTestHelper.getDummyId())
75 | .toBlocking()
76 | .subscribe(new Subscriber() {
77 | @Override public void onCompleted() {
78 | }
79 |
80 | @Override public void onError(Throwable e) {
81 | System.out.println("Error: " + e.getMessage());
82 | e.printStackTrace();
83 | assertTrue(false);
84 | }
85 |
86 | @Override public void onNext(Dummy.Run run) {
87 | System.out.println(run.status().state());
88 | }
89 | });
90 |
91 | vinliApp.device(deviceId).toBlocking().subscribe(new Subscriber() {
92 | @Override public void onCompleted() {
93 | }
94 |
95 | @Override public void onError(Throwable e) {
96 | System.out.println("Error: " + e.getMessage());
97 | e.printStackTrace();
98 | assertTrue(false);
99 | }
100 |
101 | @Override public void onNext(Device device) {
102 | System.out.println(device.id());
103 | device.stream()
104 | .observeOn(AndroidSchedulers.mainThread())
105 | .toBlocking()
106 | .subscribe(new Subscriber() {
107 | @Override public void onCompleted() {
108 |
109 | }
110 |
111 | @Override public void onError(Throwable e) {
112 | System.out.println("Error: " + e.getMessage());
113 | e.printStackTrace();
114 | assertTrue(false);
115 | }
116 |
117 | @Override public void onNext(StreamMessage streamMessage) {
118 | if (messages.get() > 5) {
119 | unsubscribe();
120 | }
121 | messages.addAndGet(1);
122 | System.out.println(streamMessage.toString());
123 | assertTrue(streamMessage.getType() != null);
124 | }
125 | });
126 | }
127 | });
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Event.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 |
7 | import com.google.gson.GsonBuilder;
8 | import com.google.gson.reflect.TypeToken;
9 |
10 | import java.lang.reflect.Type;
11 |
12 | import auto.parcel.AutoParcel;
13 | import java.util.Date;
14 | import rx.Observable;
15 |
16 | @AutoParcel
17 | public abstract class Event implements VinliItem {
18 | /*package*/ static final Type TIME_SERIES_TYPE = new TypeToken>() { }.getType();
19 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() { }.getType();
20 |
21 | /*package*/ static final void registerGson(GsonBuilder gb) {
22 | gb.registerTypeAdapter(Event.class, AutoParcelAdapter.create(AutoParcel_Event.class));
23 | gb.registerTypeAdapter(Links.class, AutoParcelAdapter.create(AutoParcel_Event_Links.class));
24 | gb.registerTypeAdapter(Meta.class, AutoParcelAdapter.create(AutoParcel_Event_Meta.class));
25 | gb.registerTypeAdapter(TIME_SERIES_TYPE, TimeSeries.Adapter.create(TIME_SERIES_TYPE, Event.class));
26 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(Event.class));
27 | }
28 |
29 | public static Observable eventWithId(@NonNull String eventId){
30 | return Vinli.curApp().event(eventId);
31 | }
32 |
33 | public static Observable> eventsWithDeviceId(@NonNull String deviceId){
34 | return eventsWithDeviceId(deviceId, null, null, (Long) null, null, null, null);
35 | }
36 |
37 |
38 | public static Observable> eventsWithVehicleId(@NonNull String vehicleId) {
39 | return Vinli.curApp()
40 | .events()
41 | .vehicleEvents(vehicleId, null, null, null, null, null, null);
42 | }
43 |
44 |
45 | public static Observable> eventsWithDeviceId(@NonNull String deviceId,
46 | @Nullable String type, @Nullable String objectId, @Nullable Long sinceMs,
47 | @Nullable Long untilMs, @Nullable Integer limit, @Nullable String sortDir) {
48 | return Vinli.curApp()
49 | .events()
50 | .events(deviceId, type, objectId, sinceMs, untilMs, limit, sortDir);
51 | }
52 |
53 | public static Observable> eventsWithVehicleId(@NonNull String vehicleId,
54 | @Nullable String type, @Nullable String objectId, @Nullable Long sinceMs,
55 | @Nullable Long untilMs, @Nullable Integer limit, @Nullable String sortDir) {
56 | return Vinli.curApp()
57 | .events()
58 | .vehicleEvents(vehicleId, type, objectId, sinceMs, untilMs, limit, sortDir);
59 | }
60 |
61 | @Deprecated
62 | public static Observable> eventsWithDeviceId(@NonNull String deviceId,
63 | @Nullable String type, @Nullable String objectId, @Nullable Date since, @Nullable Date until,
64 | @Nullable Integer limit, @Nullable String sortDir) {
65 | Long sinceMs = since == null ? null : since.getTime();
66 | Long untilMs = until == null ? null : until.getTime();
67 | return Vinli.curApp()
68 | .events()
69 | .events(deviceId, type, objectId, sinceMs, untilMs, limit, sortDir);
70 | }
71 |
72 | public abstract String eventType();
73 | public abstract String timestamp();
74 | public abstract String deviceId();
75 | public abstract Meta meta();
76 | @Nullable public abstract ObjectRef object();
77 |
78 | public Observable> notifications() {
79 | return notifications(null, null, null, null);
80 | }
81 |
82 | public Observable> notifications(@Nullable Long sinceMs,
83 | @Nullable Long untilMs, @Nullable Integer limit, @Nullable String sortDir) {
84 | return Vinli.curApp()
85 | .notifications()
86 | .notificationsForEvent(this.id(), sinceMs, untilMs, limit, sortDir);
87 | }
88 |
89 | /*package*/ abstract Links links();
90 |
91 | @AutoParcel
92 | /*package*/ static abstract class Links implements Parcelable {
93 | public abstract String self();
94 | // public abstract String rules();
95 | // public abstract String vehicles();
96 | // public abstract String latestVehicle();
97 | public abstract String notifications();
98 |
99 | /*package*/ Links() { }
100 | }
101 |
102 | @AutoParcel
103 | public static abstract class Meta implements Parcelable {
104 | public abstract String direction();
105 | public abstract boolean firstEval();
106 | public abstract Rule rule();
107 | public abstract Message message();
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/BearingCalculator.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import java.text.DateFormat;
4 | import java.text.ParseException;
5 | import java.text.SimpleDateFormat;
6 | import java.util.Date;
7 | import java.util.LinkedList;
8 | import java.util.Locale;
9 |
10 | import li.vin.net.Coordinate;
11 |
12 | /**
13 | * Created by tommy on 6/7/16.
14 | */
15 | /*package*/ class BearingCalculator {
16 |
17 | private static final double DISTANCE_THRESHOLD = 0.00025;
18 |
19 | private BearingFilter bearingFilter;
20 | private Coordinate previousLatLng;
21 |
22 | public BearingCalculator(){
23 | bearingFilter = new BearingFilter();
24 | previousLatLng = null;
25 | }
26 |
27 | public void addCoordinate(Coordinate coordinate, String timestamp){
28 | if(previousLatLng == null){
29 | previousLatLng = coordinate;
30 | return;
31 | }
32 |
33 | double distance = Math.sqrt(Math.pow(coordinate.lat() - previousLatLng.lat(), 2) + Math.pow(coordinate.lon()- previousLatLng.lon(), 2));
34 | if(distance > DISTANCE_THRESHOLD){
35 | calcBearing(timestamp, coordinate, previousLatLng);
36 | previousLatLng = coordinate;
37 | }
38 | }
39 |
40 | private void calcBearing(String newTimestamp, Coordinate newCoord, Coordinate prevCoord){
41 | double dLat = newCoord.lat() - prevCoord.lat();
42 | double dLon = newCoord.lon() - prevCoord.lon();
43 |
44 | double bearing = Math.atan2(Math.abs(dLat), Math.abs(dLon));
45 | bearing = Math.toDegrees(bearing);
46 |
47 | if(dLat > 0 && dLon == 0.0){
48 | bearing = 0.0;
49 | }else if(dLat == 0.0 && dLon > 0.0){
50 | bearing = 90.0;
51 | }else if(dLat < 0.0 && dLon == 0){
52 | bearing = 180;
53 | }else if(dLat == 0.0 && dLon < 0.0){
54 | bearing = 270;
55 | }else if(dLat > 0.0 && dLon > 0.0){
56 | bearing = 90.0 - bearing;
57 | }else if(dLat > 0.0 && dLon < 0.0){
58 | bearing = bearing + 270;
59 | }else if(dLat < 0.0 && dLon > 0.0){
60 | bearing = bearing + 90;
61 | }else if(dLat < 0.0 && dLon < 0.0){
62 | bearing = 180 + (90 - bearing);
63 | }
64 |
65 | bearingFilter.addBearing(bearing, posixFromISO(newTimestamp));
66 | }
67 |
68 | public double currentBearing(){
69 | return bearingFilter.getFilteredBearing();
70 | }
71 |
72 | private static long posixFromISO(String isoDate){
73 | DateFormat fromFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.getDefault());
74 | Date date = null;
75 |
76 | try {
77 | date = fromFormat.parse(isoDate);
78 | } catch (ParseException e) {
79 | e.printStackTrace();
80 | return 0;
81 | }
82 |
83 | return date.getTime();
84 | }
85 |
86 | private static class BearingFilter {
87 |
88 | private static final int SIZE = 32;
89 | private static final long SHORTENED_TIME = 4000;
90 |
91 | private LinkedList bearingList;
92 |
93 | public BearingFilter(){
94 | bearingList = new LinkedList<>();
95 | }
96 |
97 | public void addBearing(double bearing, long posixTimestamp){
98 | bearingList.addLast(new Bearing(bearing, posixTimestamp));
99 | if(bearingList.size() > SIZE){
100 | bearingList.removeFirst();
101 | }
102 | }
103 |
104 | public double getFilteredBearing(){
105 | if(bearingList.size() == 0){
106 | return 0.0;
107 | }
108 |
109 | LinkedList recentBearings = new LinkedList<>();
110 | Bearing latestBearing = bearingList.getLast();
111 | for(Bearing bearing : bearingList){
112 | if((latestBearing.timestamp - bearing.timestamp) <= SHORTENED_TIME){
113 | recentBearings.addLast(bearing);
114 | }
115 | }
116 |
117 | double x = 0;
118 | double y = 0;
119 | Bearing previous = null;
120 |
121 | for(Bearing bearing : recentBearings){
122 | long timestampDiff = bearing.timestamp - recentBearings.getFirst().timestamp;
123 | if(timestampDiff == 0){
124 | timestampDiff = 1;
125 | }
126 |
127 | x += Math.cos(Math.toRadians(bearing.bearing)) * ((previous == null) ? 1 : timestampDiff);
128 | y += Math.sin(Math.toRadians(bearing.bearing)) * ((previous == null) ? 1 : timestampDiff);
129 |
130 | previous = bearing;
131 | }
132 |
133 | return Math.toDegrees(Math.atan2(y, x));
134 | }
135 |
136 | public static class Bearing{
137 | public double bearing;
138 | public long timestamp;
139 |
140 | public Bearing(double bearing, long timestamp){
141 | this.bearing = bearing;
142 | this.timestamp = timestamp;
143 | }
144 | }
145 |
146 | }
147 |
148 | }
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Vinli.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.app.Activity;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.os.Build;
8 | import android.os.Bundle;
9 | import android.support.annotation.NonNull;
10 | import android.support.annotation.Nullable;
11 | import android.webkit.CookieManager;
12 | import android.webkit.CookieSyncManager;
13 |
14 | import rx.Observable;
15 | import rx.Observer;
16 |
17 | public final class Vinli {
18 | /*protected*/ static final String VINLI_PREFS = "li.vin.net.Vinli";
19 | /*protected*/ static final String SIGN_IN_ERROR = "li.vin.net.Vinli#SIGN_IN_ERROR";
20 | /*protected*/ static final String ACCESS_TOKEN = "li.vin.net.Vinli#ACCESS_TOKEN";
21 |
22 | @SuppressWarnings("deprecation")
23 | public static void signIn(@NonNull Activity context, @NonNull String clientId,
24 | @NonNull String redirectUri, @NonNull PendingIntent pendingIntent) {
25 |
26 | CookieSyncManager cookieSyncManager = CookieSyncManager.createInstance(context);
27 | CookieManager cookieManager = CookieManager.getInstance();
28 | cookieManager.removeAllCookie();
29 | if (Build.VERSION.SDK_INT >= 21) cookieManager.removeAllCookies(null);
30 | cookieSyncManager.sync();
31 | if (Build.VERSION.SDK_INT >= 21) cookieManager.flush();
32 |
33 | context.startActivity(SignInActivity.newIntent(context, clientId, redirectUri, pendingIntent));
34 | }
35 |
36 | public static @Nullable String getSignInError(@NonNull Intent intent) {
37 | final Bundle extras = intent.getExtras();
38 | if (extras == null) {
39 | return null;
40 | }
41 |
42 | return extras.getString(SIGN_IN_ERROR);
43 | }
44 |
45 | private static VinliApp sApp = null;
46 |
47 | /*package*/ static void setCurrentApp(@NonNull VinliApp vinliApp){
48 | sApp = vinliApp;
49 | }
50 |
51 | public static void clearApp(@NonNull Context context) {
52 | context.getSharedPreferences(VINLI_PREFS, Context.MODE_PRIVATE)
53 | .edit()
54 | .putString(ACCESS_TOKEN, null)
55 | .apply();
56 | sApp = null;
57 | }
58 |
59 | public static @Nullable VinliApp initApp(Context context, @NonNull Intent intent) {
60 | if (sApp == null) {
61 | final Bundle extras = intent.getExtras();
62 | if (extras != null) {
63 | String accessToken = extras.getString("li.vin.my.access_token");
64 | if (accessToken == null) {
65 | // fallback for backwards compat
66 | accessToken = extras.getString(ACCESS_TOKEN);
67 | }
68 | if (accessToken != null) {
69 | context.getSharedPreferences(VINLI_PREFS, Context.MODE_PRIVATE)
70 | .edit()
71 | .putString(ACCESS_TOKEN, accessToken)
72 | .apply();
73 |
74 | sApp = new VinliApp(accessToken);
75 | }
76 | }
77 | }
78 |
79 | return loadApp(context);
80 | }
81 |
82 | public static @Nullable VinliApp initApp(Context context, @NonNull String accessToken) {
83 | if (sApp == null) {
84 | context.getSharedPreferences(VINLI_PREFS, Context.MODE_PRIVATE)
85 | .edit()
86 | .putString(ACCESS_TOKEN, accessToken)
87 | .apply();
88 |
89 | sApp = new VinliApp(accessToken);
90 | }
91 |
92 | return loadApp(context);
93 | }
94 |
95 | public static @Nullable VinliApp loadApp(@NonNull Context context) {
96 | if (sApp == null) {
97 | final String accessToken = context
98 | .getSharedPreferences(VINLI_PREFS, Context.MODE_PRIVATE)
99 | .getString(ACCESS_TOKEN, null);
100 |
101 | if (accessToken != null) {
102 | sApp = new VinliApp(accessToken);
103 | }
104 | }
105 |
106 | return sApp;
107 | }
108 |
109 | /*package*/ static VinliApp curApp() {
110 | if (sApp == null) {
111 | throw new IllegalStateException("no current app exists");
112 | }
113 | return sApp;
114 | }
115 |
116 | public static void registerObserver(@NonNull Observer observer,
117 | @NonNull Observable observable) {
118 | ObserverManager.registerObserver(observer, observable);
119 | }
120 |
121 | public static void unregisterObserver(@NonNull Observer> observer) {
122 | ObserverManager.unregisterObserver(observer);
123 | }
124 |
125 | private static void setEnvironmentToDev() {
126 | VinliEndpoint.setDomain(VinliEndpoint.DOMAIN_DEV);
127 | }
128 |
129 | private static void setEnvironmentToDemo() {
130 | VinliEndpoint.setDomain(VinliEndpoint.DOMAIN_DEMO);
131 | }
132 |
133 | private static void setEnvironmentToQA() {
134 | VinliEndpoint.setDomain(VinliEndpoint.DOMAIN_QA);
135 | }
136 |
137 | private Vinli() { }
138 | }
139 |
--------------------------------------------------------------------------------
/gradle/bintray-upload.gradle:
--------------------------------------------------------------------------------
1 | apply plugin: 'com.jfrog.bintray'
2 | apply plugin: 'com.github.dcendents.android-maven'
3 |
4 | android {
5 | compileSdkVersion 24
6 | buildToolsVersion '24.0.2'
7 | }
8 | task sourcesJar(type: Jar) {
9 | from android.sourceSets.main.java.srcDirs
10 | classifier = 'sources'
11 | }
12 |
13 | task javadoc(type: Javadoc) {
14 | def variants = android.hasProperty('applicationVariants') \
15 | ? android.applicationVariants \
16 | : android.libraryVariants
17 |
18 | source = variants.release.javaCompile.source
19 | classpath = files(variants.release.javaCompile.classpath.files, android.bootClasspath)
20 |
21 | options.links("https://docs.oracle.com/javase/7/docs/api/");
22 | options.linksOffline("http://d.android.com/reference", "${android.sdkDirectory}/docs/reference");
23 | exclude '**/BuildConfig.java'
24 | exclude '**/R.java'
25 | }
26 |
27 | task javadocJar(type: Jar, dependsOn: javadoc) {
28 | classifier = 'javadoc'
29 | from javadoc.destinationDir
30 | }
31 |
32 | artifacts {
33 | archives javadocJar
34 | archives sourcesJar
35 | }
36 |
37 | // Maven
38 | install {
39 | repositories.mavenInstaller {
40 | pom.project {
41 | packaging project.hasProperty('vinli.packaging') \
42 | ? project.property('vinli.packaging') \
43 | : 'aar'
44 | groupId project.property('vinli.groupId')
45 | artifactId project.property('vinli.artifactId')
46 | version project.property('vinli.version')
47 |
48 | name project.property('vinli.name')
49 | description project.property('vinli.desc')
50 | url "https://github.com/vinli/${project.property('vinli.githubRepo')}"
51 |
52 | scm {
53 | url "https://github.com/vinli/${project.property('vinli.githubRepo')}"
54 | connection "scm:git:git://github.com/vinli/${project.property('vinli.githubRepo')}.git"
55 | developerConnection "scm:git:ssh://git@github.com/vinli/${project.property('vinli.githubRepo')}.git"
56 | tag project.property('vinli.version')
57 | }
58 |
59 | issueManagement {
60 | system 'GitHub Issues'
61 | url "https://github.com/vinli/${project.property('vinli.githubRepo')}/issues"
62 | }
63 |
64 | licenses {
65 | license {
66 | name 'MIT'
67 | url 'http://opensource.org/licenses/MIT'
68 | }
69 | }
70 |
71 | organization {
72 | name 'Vinli, Inc.'
73 | url 'https://vin.li'
74 | }
75 |
76 | developers {
77 | developer {
78 | id 'kturney'
79 | name 'Kyle Turney'
80 | }
81 | developer {
82 | id 'cmc5788'
83 | name 'Christopher Casey'
84 | }
85 | }
86 | }
87 | }
88 | }
89 |
90 | uploadArchives {
91 | repositories.mavenDeployer {
92 | pom.artifactId = project.property('vinli.artifactId')
93 | }
94 | }
95 |
96 | // Bintray
97 | Properties properties = new Properties()
98 | properties.load(project.rootProject.file('local.properties').newDataInputStream())
99 |
100 | bintray {
101 | user = properties.getProperty('bintray.user')
102 | key = properties.getProperty('bintray.apiKey')
103 |
104 | configurations = ['archives']
105 |
106 | dryRun = project.hasProperty('vinli.dryRun') \
107 | ? project.property('vinli.dryRun') \
108 | : false
109 | publish = project.hasProperty('vinli.publish') \
110 | ? project.property('vinli.publish') \
111 | : false
112 |
113 | pkg {
114 | repo = project.property('vinli.groupId')
115 | name = project.property('vinli.artifactId')
116 | userOrg = 'vinli'
117 | desc = project.property('vinli.desc')
118 | websiteUrl = "https://github.com/vinli/${project.property('vinli.githubRepo')}"
119 | vcsUrl = "git://github.com/vinli/${project.property('vinli.githubRepo')}.git"
120 | issueTrackerUrl = "https://github.com/vinli/${project.property('vinli.githubRepo')}/issues"
121 | licenses = ['MIT']
122 | labels = ['android', 'vinli']
123 | publicDownloadNumbers = project.hasProperty('vinli.publicDownloadNumbers') \
124 | ? project.property('vinli.publicDownloadNumbers') \
125 | : false
126 | version {
127 | name = project.property('vinli.version')
128 | vcsTag = project.property('vinli.version')
129 | released = new Date()
130 | }
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Notification.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import android.support.annotation.Nullable;
5 | import com.google.gson.GsonBuilder;
6 | import com.google.gson.reflect.TypeToken;
7 |
8 | import java.lang.reflect.Type;
9 |
10 | import auto.parcel.AutoParcel;
11 | import java.util.Date;
12 | import rx.Observable;
13 |
14 | @AutoParcel public abstract class Notification implements VinliItem {
15 | /*package*/ static final Type PAGE_TYPE = new TypeToken>() {
16 | }.getType();
17 | /*package*/ static final Type TIME_SERIES_TYPE = new TypeToken>() {
18 | }.getType();
19 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() {
20 | }.getType();
21 |
22 | /*package*/
23 | static final void registerGson(GsonBuilder gb) {
24 | gb.registerTypeAdapter(Notification.class,
25 | AutoParcelAdapter.create(AutoParcel_Notification.class));
26 | gb.registerTypeAdapter(PAGE_TYPE, Page.Adapter.create(PAGE_TYPE, Notification.class));
27 | gb.registerTypeAdapter(TIME_SERIES_TYPE,
28 | TimeSeries.Adapter.create(TIME_SERIES_TYPE, Notification.class));
29 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(Notification.class));
30 |
31 | gb.registerTypeAdapter(Links.class,
32 | AutoParcelAdapter.create(AutoParcel_Notification_Links.class));
33 | }
34 |
35 | public static Observable notificationWithId(@NonNull String notificationId) {
36 | return Vinli.curApp().notification(notificationId);
37 | }
38 |
39 | public static Observable> notificationsWithSubscriptionId(
40 | @NonNull String subscriptionId) {
41 | return notificationsWithSubscriptionId(subscriptionId, (Long) null, null, null, null);
42 | }
43 |
44 | public static Observable> notificationsWithSubscriptionId(
45 | @NonNull String subscriptionId, @Nullable Long sinceMs, @Nullable Long untilMs,
46 | @Nullable Integer limit, @Nullable String sortDir) {
47 | return Vinli.curApp()
48 | .notifications()
49 | .notificationsForSubscription(subscriptionId, sinceMs, untilMs, limit, sortDir);
50 | }
51 |
52 | @Deprecated
53 | public static Observable> notificationsWithSubscriptionId(
54 | @NonNull String subscriptionId, @Nullable Date since, @Nullable Date until,
55 | @Nullable Integer limit, @Nullable String sortDir) {
56 | Long sinceMs = since == null ? null : since.getTime();
57 | Long untilMs = until == null ? null : until.getTime();
58 | return Vinli.curApp()
59 | .notifications()
60 | .notificationsForSubscription(subscriptionId, sinceMs, untilMs, limit, sortDir);
61 | }
62 |
63 | public static Observable> notificationsWithEventId(
64 | @NonNull String eventId) {
65 | return notificationsWithEventId(eventId, (Long) null, null, null, null);
66 | }
67 |
68 | public static Observable> notificationsWithEventId(
69 | @NonNull String eventId, @Nullable Long sinceMs, @Nullable Long untilMs,
70 | @Nullable Integer limit, @Nullable String sortDir) {
71 | return Vinli.curApp()
72 | .notifications()
73 | .notificationsForEvent(eventId, sinceMs, untilMs, limit, sortDir);
74 | }
75 |
76 | @Deprecated
77 | public static Observable> notificationsWithEventId(
78 | @NonNull String eventId, @Nullable Date since, @Nullable Date until, @Nullable Integer limit,
79 | @Nullable String sortDir) {
80 | Long sinceMs = since == null ? null : since.getTime();
81 | Long untilMs = until == null ? null : until.getTime();
82 | return Vinli.curApp()
83 | .notifications()
84 | .notificationsForEvent(eventId, sinceMs, untilMs, limit, sortDir);
85 | }
86 |
87 | public abstract String createdAt();
88 |
89 | public abstract String eventId();
90 |
91 | public abstract String eventTimestamp();
92 |
93 | public abstract String eventType();
94 |
95 | public abstract String notifiedAt();
96 |
97 | public abstract String payload();
98 |
99 | public abstract String respondedAt();
100 |
101 | public abstract String response();
102 |
103 | public abstract int responseCode();
104 |
105 | public abstract String state();
106 |
107 | public abstract String subscriptionId();
108 |
109 | public abstract String url();
110 |
111 | /*package*/
112 | abstract Links links();
113 |
114 | @AutoParcel
115 | /*package*/ static abstract class Links {
116 | public abstract String self();
117 |
118 | public abstract String subscription();
119 |
120 | public abstract String event();
121 |
122 | /*package*/ Links() {
123 | }
124 | }
125 | }
126 |
--------------------------------------------------------------------------------
/android-net/src/test/java/li/vin/net/DevicesIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.robolectric.RobolectricTestRunner;
7 | import org.robolectric.annotation.Config;
8 |
9 | import rx.Subscriber;
10 |
11 | import static junit.framework.Assert.assertTrue;
12 |
13 | @RunWith(RobolectricTestRunner.class)
14 | @Config(constants = BuildConfig.class, sdk = 22)
15 | public class DevicesIntegrationTests {
16 |
17 | public VinliApp vinliApp;
18 |
19 | @Before
20 | public void setup(){
21 | assertTrue(TestHelper.getAccessToken() != null);
22 |
23 | vinliApp = TestHelper.getVinliApp();
24 | }
25 |
26 | @Test public void getPagedDevices() {
27 | vinliApp.devices(1, 0)
28 | .toBlocking()
29 | .subscribe(new Subscriber>() {
30 | @Override public void onCompleted() {
31 |
32 | }
33 |
34 | @Override public void onError(Throwable e) {
35 | e.printStackTrace();
36 | assertTrue(false);
37 | }
38 |
39 | @Override public void onNext(Page devicePage) {
40 | assertTrue(devicePage.getItems().size() > 0);
41 |
42 | for(Device device : devicePage.getItems()){
43 | assertTrue(device.id() != null && device.id().length() > 0);
44 | assertTrue(device.name() != null && device.name().length() > 0);
45 | }
46 |
47 | if (devicePage.hasNextPage()) {
48 | devicePage.loadNextPage().toBlocking()
49 | .subscribe(new Subscriber>() {
50 | @Override public void onCompleted() {
51 |
52 | }
53 |
54 | @Override public void onError(Throwable e) {
55 | e.printStackTrace();
56 | assertTrue(false);
57 | }
58 |
59 | @Override public void onNext(Page devicePage) {
60 | assertTrue(devicePage.getItems().size() > 0);
61 |
62 | for(Device device : devicePage.getItems()){
63 | assertTrue(device.id() != null && device.id().length() > 0);
64 | assertTrue(device.name() != null && device.name().length() > 0);
65 | }
66 | }
67 | });
68 | }
69 | }
70 | });
71 | }
72 |
73 | @Test
74 | public void testGetDevices(){
75 | vinliApp.devices().toBlocking().subscribe(new Subscriber>() {
76 | @Override
77 | public void onCompleted() {
78 |
79 | }
80 |
81 | @Override
82 | public void onError(Throwable e) {
83 | e.printStackTrace();
84 | assertTrue(false);
85 | }
86 |
87 | @Override
88 | public void onNext(Page devicePage) {
89 | assertTrue(devicePage.getItems().size() > 0);
90 |
91 | for(Device device : devicePage.getItems()){
92 | assertTrue(device.id() != null && device.id().length() > 0);
93 | assertTrue(device.name() != null && device.name().length() > 0);
94 | }
95 | }
96 | });
97 | }
98 |
99 | @Test
100 | public void testGetDevicesByUrl(){
101 | vinliApp.devicesSvc().devicesForUrl(String.format("%sdevices", VinliEndpoint.PLATFORM.getUrl()))
102 | .toBlocking().subscribe(new Subscriber>() {
103 | @Override
104 | public void onCompleted() {
105 |
106 | }
107 |
108 | @Override
109 | public void onError(Throwable e) {
110 | e.printStackTrace();
111 | assertTrue(false);
112 | }
113 |
114 | @Override
115 | public void onNext(Page devicePage) {
116 | assertTrue(devicePage.getItems().size() > 0);
117 |
118 | for(Device device : devicePage.getItems()){
119 | assertTrue(device.id() != null && device.id().length() > 0);
120 | assertTrue(device.name() != null && device.name().length() > 0);
121 | }
122 | }
123 | });
124 | }
125 |
126 | @Test
127 | public void testGetDeviceById(){
128 | assertTrue(TestHelper.getDeviceId() != null);
129 |
130 | Device.deviceWithId(TestHelper.getDeviceId()).toBlocking().subscribe(new Subscriber() {
131 | @Override
132 | public void onCompleted() {
133 |
134 | }
135 |
136 | @Override
137 | public void onError(Throwable e) {
138 | e.printStackTrace();
139 | assertTrue(false);
140 | }
141 |
142 | @Override
143 | public void onNext(Device device) {
144 | assertTrue(device.id() != null && device.id().length() > 0);
145 | assertTrue(device.name() != null && device.name().length() > 0);
146 | }
147 | });
148 | }
149 |
150 | }
151 |
--------------------------------------------------------------------------------
/android-net/build.gradle:
--------------------------------------------------------------------------------
1 | def integrationPropertiesFile = file("integration.properties");
2 | def integrationProperties = new Properties();
3 | integrationProperties.load(new FileInputStream(integrationPropertiesFile));
4 |
5 | buildscript {
6 | repositories {
7 | mavenCentral()
8 | jcenter()
9 | }
10 | dependencies {
11 | classpath 'com.neenbedankt.gradle.plugins:android-apt:1.4'
12 | }
13 | }
14 |
15 | group = project.property('vinli.groupId')
16 | version = project.property('vinli.version')
17 |
18 | apply plugin: 'com.android.library'
19 | apply from: file('../gradle/checkstyle.gradle')
20 | apply from: file('../gradle/javadoc.gradle')
21 | apply from: file('../gradle/bintray-upload.gradle')
22 | apply from: file('../gradle/mavenize.gradle')
23 | apply plugin: 'android-apt'
24 |
25 | android {
26 | compileSdkVersion 24
27 | buildToolsVersion '24.0.2'
28 |
29 | defaultConfig {
30 | minSdkVersion 14
31 | targetSdkVersion 24
32 | versionCode 1
33 | versionName "1.0"
34 | testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
35 | }
36 |
37 | buildTypes {
38 | debug {
39 | buildConfigField("String", "ACCESS_TOKEN", "\"" + integrationProperties['ACCESS_TOKEN'] + "\"")
40 | buildConfigField("String", "VEHICULARIZATION_ACCESS_TOKEN", "\"" + integrationProperties['VEHICULARIZATION_ACCESS_TOKEN'] + "\"")
41 | buildConfigField("String", "DEVICE_ID", "\"" + integrationProperties['DEVICE_ID'] + "\"")
42 | buildConfigField("String", "SUBSCRIPTION_ID", "\"" + integrationProperties['SUBSCRIPTION_ID'] + "\"")
43 | buildConfigField("String", "RULE_ID", "\"" + integrationProperties['RULE_ID'] + "\"")
44 | buildConfigField("String", "VEHICLE_RULE_ID", "\"" + integrationProperties['VEHICLE_RULE_ID'] + "\"")
45 | buildConfigField("String", "VEHICLE_ID", "\"" + integrationProperties['VEHICLE_ID'] + "\"")
46 | buildConfigField("String", "SECOND_VEHICLE_ID", "\"" + integrationProperties['SECOND_VEHICLE_ID'] + "\"")
47 | buildConfigField("String", "EVENT_ID", "\"" + integrationProperties['EVENT_ID'] + "\"")
48 | buildConfigField("String", "NOTIFICATION_ID", "\"" + integrationProperties['NOTIFICATION_ID'] + "\"")
49 | buildConfigField("String", "MESSAGE_ID", "\"" + integrationProperties['MESSAGE_ID'] + "\"")
50 | buildConfigField("String", "TRIP_ID", "\"" + integrationProperties['TRIP_ID'] + "\"")
51 | buildConfigField("String", "DUMMY_ID", "\"" + integrationProperties['DUMMY_ID'] + "\"")
52 | buildConfigField("String", "ROUTE_ID", "\"" + integrationProperties['ROUTE_ID'] + "\"")
53 | buildConfigField("String", "VIN", "\"" + integrationProperties['VIN'] + "\"")
54 | buildConfigField("String", "ODO_ID", "\"" + integrationProperties['ODO_ID'] + "\"")
55 | buildConfigField("String", "ODO_TRIGGER_ID", "\"" + integrationProperties['ODO_TRIGGER_ID'] + "\"")
56 | buildConfigField("String", "COLLISION_ID", "\"" + integrationProperties['COLLISION_ID'] + "\"")
57 | buildConfigField("String", "REPORT_CARD_ID", "\"" + integrationProperties['REPORT_CARD_ID'] + "\"")
58 | }
59 | release {
60 | minifyEnabled false
61 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
62 | }
63 | }
64 |
65 | compileOptions {
66 | sourceCompatibility JavaVersion.VERSION_1_7
67 | targetCompatibility JavaVersion.VERSION_1_7
68 | }
69 |
70 | lintOptions {
71 | abortOnError false
72 | }
73 | }
74 |
75 | dependencies {
76 |
77 | compile 'com.squareup.duktape:duktape-android:0.9.5'
78 |
79 |
80 | provided 'com.android.support:appcompat-v7:24.+'
81 |
82 | compile 'com.squareup.retrofit2:retrofit:2.1.0'
83 | compile 'com.squareup.retrofit2:converter-gson:2.0.2'
84 | compile 'com.squareup.okhttp3:logging-interceptor:3.4.1'
85 | compile 'com.squareup.retrofit2:adapter-rxjava:2.1.0'
86 | compile 'com.squareup.okhttp3:okhttp-ws:3.4.+'
87 | compile 'io.reactivex:rxandroid:1.0.1'
88 | compile 'io.reactivex:rxjava:1.1.+'
89 | compile 'com.google.code.gson:gson:2.+'
90 | provided 'com.android.support:support-annotations:24.2.1'
91 | compile 'com.github.frankiesardo:auto-parcel:0.3'
92 | apt 'com.github.frankiesardo:auto-parcel-processor:0.3'
93 |
94 | testCompile "org.robolectric:robolectric:3.1.2"
95 | testCompile 'org.mockito:mockito-core:1.10.19'
96 | testCompile 'junit:junit:4.12'
97 | testCompile group: 'org.robolectric', name: 'shadows-httpclient', version: '3.0'
98 |
99 | androidTestCompile 'com.android.support:support-annotations:25.0.0'
100 | androidTestCompile 'com.android.support.test:runner:0.5'
101 | androidTestCompile 'com.android.support.test:rules:0.5'
102 | androidTestCompile 'org.hamcrest:hamcrest-library:1.3'
103 | }
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/ReportCard.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 |
5 | import android.support.annotation.Nullable;
6 | import com.google.gson.GsonBuilder;
7 | import com.google.gson.internal.LinkedTreeMap;
8 | import com.google.gson.reflect.TypeToken;
9 |
10 | import java.lang.reflect.Type;
11 |
12 | import auto.parcel.AutoParcel;
13 | import java.util.Date;
14 | import rx.Observable;
15 |
16 | @AutoParcel public abstract class ReportCard implements VinliItem {
17 | /*package*/ static final Type TIME_SERIES_TYPE = new TypeToken>() {
18 | }.getType();
19 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() {
20 | }.getType();
21 |
22 | /*package*/
23 | static final void registerGson(GsonBuilder gb) {
24 | gb.registerTypeAdapter(ReportCard.class, AutoParcelAdapter.create(AutoParcel_ReportCard.class));
25 | gb.registerTypeAdapter(OverallReportCard.class,
26 | AutoParcelAdapter.create(AutoParcel_ReportCard_OverallReportCard.class));
27 | gb.registerTypeAdapter(OverallReportCard.InnerReportCard.class,
28 | AutoParcelAdapter.create(AutoParcel_ReportCard_OverallReportCard_InnerReportCard.class));
29 | gb.registerTypeAdapter(TIME_SERIES_TYPE,
30 | TimeSeries.Adapter.create(TIME_SERIES_TYPE, ReportCard.class, "reportCards"));
31 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(ReportCard.class, "reportCard"));
32 | }
33 |
34 | @NonNull public abstract String deviceId();
35 |
36 | @NonNull public abstract String vehicleId();
37 |
38 | @NonNull public abstract String tripId();
39 |
40 | @NonNull public abstract String grade();
41 |
42 | public static Observable reportCardWithId(@NonNull String reportCardId) {
43 | return Vinli.curApp().reportCard(reportCardId);
44 | }
45 |
46 | public static Observable> reportCardsWithDeviceId(
47 | @NonNull String deviceId) {
48 | return reportCardsWithDeviceId(deviceId, (Long) null, null, null, null);
49 | }
50 |
51 | public static Observable> reportCardsWithDeviceId(@NonNull String deviceId,
52 | @Nullable Long sinceMs, @Nullable Long untilMs, @Nullable Integer limit,
53 | @Nullable String sortDir) {
54 | return Vinli.curApp()
55 | .reportCards()
56 | .reportCardsForDevice(deviceId, sinceMs, untilMs, limit, sortDir);
57 | }
58 |
59 | @Deprecated
60 | public static Observable> reportCardsWithDeviceId(@NonNull String deviceId,
61 | @Nullable Date since, @Nullable Date until, @Nullable Integer limit,
62 | @Nullable String sortDir) {
63 | Long sinceMs = since == null ? null : since.getTime();
64 | Long untilMs = until == null ? null : until.getTime();
65 | return Vinli.curApp()
66 | .reportCards()
67 | .reportCardsForDevice(deviceId, sinceMs, untilMs, limit, sortDir);
68 | }
69 |
70 | public static Observable> reportCardsWithVehicleId(
71 | @NonNull String vehicleId) {
72 | return reportCardsWithVehicleId(vehicleId, null, null, null, null);
73 | }
74 |
75 | public static Observable> reportCardsWithVehicleId(
76 | @NonNull String vehicleId, @Nullable Long sinceMs, @Nullable Long untilMs,
77 | @Nullable Integer limit, @Nullable String sortDir) {
78 | return Vinli.curApp()
79 | .reportCards()
80 | .reportCardsForVehicle(vehicleId, sinceMs, untilMs, limit, sortDir);
81 | }
82 |
83 | @Deprecated
84 | public static Observable> reportCardWithVehicleId(
85 | @NonNull String vehicleId, @Nullable Date since, @Nullable Date until,
86 | @Nullable Integer limit, @Nullable String sortDir) {
87 | Long sinceMs = since == null ? null : since.getTime();
88 | Long untilMs = until == null ? null : until.getTime();
89 | return Vinli.curApp()
90 | .reportCards()
91 | .reportCardsForVehicle(vehicleId, sinceMs, untilMs, limit, sortDir);
92 | }
93 |
94 | public static Observable reportCardWithTripId(@NonNull String tripId) {
95 | return Vinli.curApp()
96 | .reportCards()
97 | .reportCardForTrip(tripId)
98 | .map(Wrapped.pluckItem());
99 | }
100 |
101 | @AutoParcel public static abstract class OverallReportCard {
102 |
103 | @NonNull public abstract Integer tripSampleSize();
104 |
105 | @Nullable public abstract LinkedTreeMap gradeCount();
106 |
107 | @NonNull /*package*/ abstract InnerReportCard reportCard();
108 |
109 | public String overallGrade() {
110 | return reportCard().overallGrade();
111 | }
112 |
113 | @AutoParcel
114 | /*package*/ static abstract class InnerReportCard {
115 | @NonNull public abstract String overallGrade();
116 |
117 | /*package*/ InnerReportCard() {
118 | }
119 | }
120 |
121 | public static Observable overallReportCardForDevice(
122 | @NonNull String deviceId) {
123 | return Vinli.curApp().reportCards().overallReportCardForDevice(deviceId);
124 | }
125 | }
126 | }
127 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Odometer.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import auto.parcel.AutoParcel;
7 | import com.google.gson.Gson;
8 | import com.google.gson.GsonBuilder;
9 | import com.google.gson.TypeAdapter;
10 | import com.google.gson.reflect.TypeToken;
11 | import com.google.gson.stream.JsonReader;
12 | import com.google.gson.stream.JsonWriter;
13 | import java.io.IOException;
14 | import java.lang.reflect.Type;
15 | import java.util.Date;
16 | import rx.Observable;
17 |
18 | @AutoParcel
19 | public abstract class Odometer implements VinliItem{
20 | /*package*/ static final Type TIME_SERIES_TYPE = new TypeToken>() { }.getType();
21 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() { }.getType();
22 |
23 | /*package*/ static final void registerGson(GsonBuilder gb) {
24 | gb.registerTypeAdapter(Odometer.class, AutoParcelAdapter.create(AutoParcel_Odometer.class));
25 | gb.registerTypeAdapter(Links.class, AutoParcelAdapter.create(AutoParcel_Odometer_Links.class));
26 | gb.registerTypeAdapter(Odometer.Seed.class, new Seed.Adapter());
27 |
28 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(Odometer.class));
29 | gb.registerTypeAdapter(TIME_SERIES_TYPE, TimeSeries.Adapter.create(TIME_SERIES_TYPE, Odometer.class));
30 | }
31 |
32 | public static Observable odometerWithId(@NonNull String odometerId) {
33 | return Vinli.curApp().odometerReport(odometerId);
34 | }
35 |
36 | public static Observable> odometersWithVehicleId(@NonNull String vehicleId) {
37 | return odometersWithVehicleId(vehicleId, (Long) null, null, null, null);
38 | }
39 |
40 | public static Observable> odometersWithVehicleId(@NonNull String vehicleId,
41 | @Nullable Long sinceMs, @Nullable Long untilMs, @Nullable Integer limit,
42 | @Nullable String sortDir) {
43 | return Vinli.curApp().distances().odometerReports(vehicleId, sinceMs, untilMs, limit, sortDir);
44 | }
45 |
46 | @Deprecated
47 | public static Observable> odometersWithVehicleId(@NonNull String vehicleId,
48 | @Nullable Date since, @Nullable Date until, @Nullable Integer limit,
49 | @Nullable String sortDir) {
50 | Long sinceMs = since == null ? null : since.getTime();
51 | Long untilMs = until == null ? null : until.getTime();
52 | return Vinli.curApp().distances().odometerReports(vehicleId, sinceMs, untilMs, limit, sortDir);
53 | }
54 |
55 | public abstract String vehicleId();
56 | public abstract Double reading();
57 | public abstract String date();
58 |
59 | /*package*/ abstract Links links();
60 |
61 | public static final Seed.Saver create() {
62 | return new AutoParcel_Odometer_Seed.Builder();
63 | }
64 |
65 | public Observable delete(){
66 | return Vinli.curApp().distances().deleteOdometerReport(id());
67 | }
68 |
69 | @AutoParcel
70 | /*package*/ static abstract class Links implements Parcelable {
71 | public abstract String vehicle();
72 |
73 | /*package*/ Links() { }
74 | }
75 |
76 | @AutoParcel
77 | public static abstract class Seed{
78 | @NonNull public abstract Double reading();
79 | @Nullable public abstract String date();
80 | @NonNull public abstract DistanceUnit unit();
81 | @NonNull public abstract String vehicleId();
82 |
83 | /*package*/ Seed() { }
84 |
85 | @AutoParcel.Builder
86 | public static abstract class Saver{
87 | public abstract Saver reading(@NonNull Double reading);
88 | public abstract Saver date(@Nullable String date);
89 | public abstract Saver unit(@NonNull DistanceUnit unit);
90 | public abstract Saver vehicleId(@NonNull String vehicleId);
91 |
92 | /*package*/ Saver() {}
93 |
94 | /*package*/ abstract Seed autoBuild();
95 |
96 | public Observable save() {
97 | final Seed s = autoBuild();
98 |
99 | return Vinli.curApp().distances().createOdometerReport(s.vehicleId(), s)
100 | .map(Wrapped.pluckItem());
101 | }
102 | }
103 |
104 | /*package*/ static final class Adapter extends TypeAdapter {
105 | private Gson gson;
106 |
107 | @Override public void write(JsonWriter out, Seed value) throws IOException {
108 | if (gson == null) {
109 | gson = Vinli.curApp().gson();
110 | }
111 |
112 | out.beginObject();
113 | out.name("odometer").beginObject();
114 | out.name("reading").value(value.reading());
115 |
116 | final String date = value.date();
117 | if(date != null){
118 | out.name("date").value(value.date());
119 | }
120 | out.name("unit").value(value.unit().getDistanceUnitStr());
121 | out.endObject();
122 | out.endObject();
123 | }
124 |
125 | @Override public Seed read(JsonReader in) throws IOException {
126 | throw new UnsupportedOperationException("reading a OdometerSeed is not supported");
127 | }
128 | }
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/SignInActivity.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.app.Activity;
4 | import android.app.PendingIntent;
5 | import android.content.Context;
6 | import android.content.Intent;
7 | import android.net.Uri;
8 | import android.os.Bundle;
9 | import android.support.annotation.NonNull;
10 | import android.util.Log;
11 | import android.webkit.WebView;
12 | import android.webkit.WebViewClient;
13 |
14 | import okhttp3.HttpUrl;
15 | //import com.squareup.okhttp.HttpUrl;
16 |
17 | public class SignInActivity extends Activity {
18 | private static final String TAG = SignInActivity.class.getSimpleName();
19 |
20 | private static final String CLIENT_ID = "li.vin.net.SignInActivity#CLIENT_ID";
21 | private static final String REDIRECT_URI = "li.vin.net.SignInActivity#REDIRECT_URI";
22 | private static final String PENDING_INTENT = "li.vin.net.SignInActivity#PENDING_INTENT";
23 |
24 | private static final String ACTION_ERROR = "li.vin.net.signIn.ERROR";
25 | private static final String ACTION_APPROVED = "li.vin.net.signIn.APPROVED";
26 |
27 | private static final HttpUrl OAUTH_ENPOINT = new HttpUrl.Builder().scheme("https")
28 | .host("auth" + VinliEndpoint.domain())
29 | .addPathSegment("oauth")
30 | .addPathSegment("authorization")
31 | .addPathSegment("new")
32 | .addQueryParameter("response_type", "token")
33 | .build();
34 |
35 | /*protected*/
36 | static final Intent newIntent(@NonNull Context context, @NonNull String clientId,
37 | @NonNull String redirectUri, @NonNull PendingIntent pendingIntent) {
38 | final Intent signInIntent = new Intent(context, SignInActivity.class);
39 |
40 | signInIntent.putExtra(CLIENT_ID, clientId);
41 | signInIntent.putExtra(REDIRECT_URI, redirectUri);
42 | signInIntent.putExtra(PENDING_INTENT, pendingIntent);
43 |
44 | return signInIntent;
45 | }
46 |
47 | @Override protected void onCreate(Bundle savedInstanceState) {
48 | super.onCreate(savedInstanceState);
49 |
50 | final Bundle extras = getIntent().getExtras();
51 | if (extras == null) {
52 | throw new AssertionError("missing app info extras");
53 | }
54 |
55 | final String clientId = extras.getString(CLIENT_ID);
56 | if (clientId == null) {
57 | throw new AssertionError("missing client ID");
58 | }
59 |
60 | final String redirectUri = extras.getString(REDIRECT_URI);
61 | if (redirectUri == null) {
62 | throw new AssertionError("missing redirect URI");
63 | }
64 |
65 | final PendingIntent pendingIntent = extras.getParcelable(PENDING_INTENT);
66 | if (pendingIntent == null) {
67 | throw new AssertionError("missing pending intent");
68 | }
69 |
70 | setContentView(R.layout.activity_vinli_sign_in);
71 |
72 | final WebView wv = (WebView) this.findViewById(li.vin.net.R.id.sign_in);
73 |
74 | wv.setWebViewClient(new WebViewClient() {
75 | @Override public boolean shouldOverrideUrlLoading(WebView view, String url) {
76 | Log.d(TAG, "shouldOverrideUrlLoading: " + url);
77 |
78 | if (url.startsWith(redirectUri)) {
79 |
80 | String error = null;
81 | String accessToken = null;
82 |
83 | try {
84 | final HttpUrl uri = HttpUrl.parse(url);
85 | final String[] fragmentPieces = uri.fragment().split("&");
86 | for (String piece : fragmentPieces) {
87 | if (piece.startsWith("access_token=")) {
88 | accessToken = piece.substring("access_token=".length());
89 | break;
90 | } else if (piece.startsWith("error=")) {
91 | error = piece.substring("error=".length());
92 | break;
93 | }
94 | }
95 | } catch (Exception e) {
96 | error = "redirect parse error: " + e;
97 | }
98 |
99 | Intent resultIntent;
100 |
101 | if (error == null) {
102 | if (accessToken == null) {
103 | resultIntent = new Intent(ACTION_ERROR);
104 | resultIntent.putExtra(Vinli.SIGN_IN_ERROR, "missing access_token");
105 | } else {
106 | Log.d(TAG, "oauth accessToken: " + accessToken);
107 | resultIntent = new Intent(ACTION_APPROVED);
108 | resultIntent.putExtra(Vinli.ACCESS_TOKEN, accessToken);
109 | }
110 | } else {
111 | Log.d(TAG, "oauth error: " + error);
112 | resultIntent = new Intent(ACTION_ERROR);
113 | resultIntent.putExtra(Vinli.SIGN_IN_ERROR, error);
114 | }
115 |
116 | try {
117 | pendingIntent.send(SignInActivity.this, 0, resultIntent);
118 | finish();
119 | } catch (Exception e) {
120 | Log.d(TAG, "pending intent send error: " + e);
121 | }
122 | return true;
123 | }
124 |
125 | if (url.toLowerCase().contains("sign-up")) {
126 | try {
127 | startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
128 | } catch (Exception e) {
129 | Log.d(TAG, "failed to launch sign up url", e);
130 | }
131 | return true;
132 | }
133 | return false;
134 | }
135 | });
136 |
137 | final String url = OAUTH_ENPOINT.newBuilder()
138 | .host("auth" + VinliEndpoint.domain())
139 | .setQueryParameter("client_id", clientId)
140 | .setQueryParameter("redirect_uri", redirectUri)
141 | .toString();
142 |
143 | Log.d("SignInActivity", "loading url: " + url);
144 | wv.loadUrl(url);
145 | }
146 | }
147 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/checkstyle.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
--------------------------------------------------------------------------------
/circle.yml:
--------------------------------------------------------------------------------
1 | # machine:
2 | # environment:
3 | # GRADLE_OPTS: '-Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"'
4 |
5 | # dependencies:
6 | # pre:
7 | # - git clone git@github.com:vinli/build.git .build
8 | # override:
9 | # - .build/android-net/pretest.sh
10 |
11 | # test:
12 | # pre:
13 | # - mksdcard -l e 512M mysdcard.img
14 | # - emulator -avd circleci-android22 -no-audio -no-window -sdcard mysdcard.img:
15 | # background: true
16 | # parallel: true
17 | # - circle-android wait-for-boot
18 | # override:
19 | # - .build/android-net/test.sh
20 | # post:
21 | # - .build/android-net/posttest.sh
22 |
23 | # This configuration was automatically generated from a CircleCI 1.0 config.
24 | # It should include any build commands you had along with commands that CircleCI
25 | # inferred from your project structure. We strongly recommend you read all the
26 | # comments in this file to understand the structure of CircleCI 2.0, as the idiom
27 | # for configuration has changed substantially in 2.0 to allow arbitrary jobs rather
28 | # than the prescribed lifecycle of 1.0. In general, we recommend using this generated
29 | # configuration as a reference rather than using it in production, though in most
30 | # cases it should duplicate the execution of your original 1.0 config.
31 | version: 2
32 | jobs:
33 | build:
34 | parallelism: 1
35 | shell: /bin/bash --login
36 | docker:
37 | - image: circleci/android:api-24-alpha
38 | # CircleCI 2.0 does not support environment variables that refer to each other the same way as 1.0 did.
39 | # If any of these refer to each other, rewrite them so that they don't or see https://circleci.com/docs/2.0/env-vars/#interpolating-environment-variables-to-set-other-environment-variables .
40 | environment:
41 | CIRCLE_ARTIFACTS: /tmp/circleci-artifacts
42 | CIRCLE_TEST_REPORTS: /tmp/circleci-test-results
43 | GRADLE_OPTS: -Dorg.gradle.jvmargs="-Xmx2048m -XX:+HeapDumpOnOutOfMemoryError"
44 | # In CircleCI 1.0 we used a pre-configured image with a large number of languages and other packages.
45 | # In CircleCI 2.0 you can now specify your own image, or use one of our pre-configured images.
46 | # The following configuration line tells CircleCI to use the specified docker image as the runtime environment for you job.
47 | # We have selected a pre-built image that mirrors the build environment we use on
48 | # the 1.0 platform, but we recommend you choose an image more tailored to the needs
49 | # of each job. For more information on choosing an image (or alternatively using a
50 | # VM instead of a container) see https://circleci.com/docs/2.0/executor-types/
51 | # To see the list of pre-built images that CircleCI provides for most common languages see
52 | # https://circleci.com/docs/2.0/circleci-images/
53 |
54 | steps:
55 | # Machine Setup
56 | # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each
57 | # The following `checkout` command checks out your code to your working directory. In 1.0 we did this implicitly. In 2.0 you can choose where in the course of a job your code should be checked out.
58 | - checkout
59 | # Prepare for artifact and test results collection equivalent to how it was done on 1.0.
60 | # In many cases you can simplify this from what is generated here.
61 | # 'See docs on artifact collection here https://circleci.com/docs/2.0/artifacts/'
62 | - run: mkdir -p $CIRCLE_ARTIFACTS $CIRCLE_TEST_REPORTS
63 | # Dependencies
64 | # This would typically go in either a build or a build-and-test job when using workflows
65 | # Restore the dependency cache
66 | - restore_cache:
67 | keys:
68 | # This branch if available
69 | - v1-dep-{{ .Branch }}-
70 | # Default branch if not
71 | - v1-dep-master-
72 | # Any branch if there are none on the default branch - this should be unnecessary if you have your default branch configured correctly
73 | - v1-dep-
74 | # This is based on your 1.0 configuration file or project settings
75 | - run: git clone git@github.com:vinli/build.git .build
76 | # This is based on your 1.0 configuration file or project settings
77 | - run: .build/android-net/pretest.sh
78 | # Save dependency cache
79 | - save_cache:
80 | key: v1-dep-{{ .Branch }}-{{ epoch }}
81 | paths:
82 | # This is a broad list of cache paths to include many possible development environments
83 | # You can probably delete some of these entries
84 | - vendor/bundle
85 | - ~/virtualenvs
86 | - ~/.m2
87 | - ~/.ivy2
88 | - ~/.bundle
89 | - ~/.go_workspace
90 | - ~/.gradle
91 | - ~/.cache/bower
92 | # Test
93 |
94 | # ******** Circle 2 currently does not support emulator testing, see more at link below: **********************
95 | # https://support.circleci.com/hc/en-us/articles/360000028928-Testing-with-Android-emulator-on-CircleCI-2-0
96 | # - run: mksdcard -l e 512M mysdcard.img
97 | # - run:
98 | # command: emulator -avd circleci-android22 -no-audio -no-window -sdcard mysdcard.img
99 | # background: true
100 | # - run: circle-android wait-for-boot
101 | # This is based on your 1.0 configuration file or project settings
102 | # - run: .build/android-net/test.sh
103 |
104 | # This is based on your 1.0 configuration file or project settings
105 |
106 |
107 | # - run: export TERM=${TERM:-dumb} && ./gradlew clean connectedAndroidTest
108 | # Teardown
109 | # If you break your build into multiple jobs with workflows, you will probably want to do the parts of this that are relevant in each
110 | # Save test results
111 | - store_test_results:
112 | path: /tmp/circleci-test-results
113 | # Save artifacts
114 | - store_artifacts:
115 | path: /tmp/circleci-artifacts
116 | - store_artifacts:
117 | path: /tmp/circleci-test-results
118 |
--------------------------------------------------------------------------------
/android-net/src/test/java/li/vin/net/DiagnosticsIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import java.util.concurrent.atomic.AtomicBoolean;
4 | import org.junit.Before;
5 | import org.junit.Test;
6 | import org.junit.runner.RunWith;
7 | import org.robolectric.RobolectricTestRunner;
8 | import org.robolectric.annotation.Config;
9 |
10 | import rx.Subscriber;
11 |
12 | import static junit.framework.Assert.assertTrue;
13 |
14 | @RunWith(RobolectricTestRunner.class)
15 | @Config(constants = BuildConfig.class, sdk = 22)
16 | public class DiagnosticsIntegrationTests {
17 |
18 | public VinliApp vinliApp;
19 |
20 | @Before
21 | public void setup(){
22 | assertTrue(TestHelper.getAccessToken() != null);
23 |
24 | vinliApp = TestHelper.getVinliApp();
25 | }
26 |
27 | @Test
28 | public void testGetPagedCodes(){
29 | assertTrue(TestHelper.getVehicleId() != null);
30 |
31 | vinliApp.diagnostics().codes(TestHelper.getVehicleId(), null, null, 1, null,null)
32 | .toBlocking().subscribe(new Subscriber>() {
33 | @Override
34 | public void onCompleted() {
35 | }
36 |
37 | @Override
38 | public void onError(Throwable e) {
39 | System.out.println("Error: " + e.getMessage());
40 | e.printStackTrace();
41 | assertTrue(false);
42 | }
43 |
44 | @Override
45 | public void onNext(TimeSeries dtcTimeSeries) {
46 | assertTrue(dtcTimeSeries.getItems().size() > 0);
47 |
48 | for(Dtc dtc : dtcTimeSeries.getItems()){
49 | assertTrue(dtc.start() != null && dtc.start().length() > 0);
50 | assertTrue(dtc.vehicleId() != null && dtc.vehicleId().length() > 0);
51 | assertTrue(dtc.deviceId() != null && dtc.deviceId().length() > 0);
52 | assertTrue(dtc.number() != null && dtc.number().length() > 0);
53 | assertTrue(dtc.description() != null && dtc.description().length() > 0);
54 | }
55 |
56 | if (dtcTimeSeries.hasPrior()) {
57 | dtcTimeSeries.loadPrior().toBlocking().subscribe(new Subscriber>() {
58 | @Override public void onCompleted() {
59 |
60 | }
61 |
62 | @Override public void onError(Throwable e) {
63 | System.out.println("Error: " + e.getMessage());
64 | e.printStackTrace();
65 | assertTrue(false);
66 | }
67 |
68 | @Override public void onNext(TimeSeries dtcTimeSeries) {
69 | // TODO - uncomment this when platform bug is resolved or explained
70 | //assertTrue(dtcTimeSeries.getItems().size() > 0);
71 |
72 | for(Dtc dtc : dtcTimeSeries.getItems()){
73 | assertTrue(dtc.start() != null && dtc.start().length() > 0);
74 | assertTrue(dtc.vehicleId() != null && dtc.vehicleId().length() > 0);
75 | assertTrue(dtc.deviceId() != null && dtc.deviceId().length() > 0);
76 | assertTrue(dtc.number() != null && dtc.number().length() > 0);
77 | assertTrue(dtc.description() != null && dtc.description().length() > 0);
78 | }
79 | }
80 | });
81 | }
82 | }
83 | });
84 | }
85 |
86 | @Test
87 | public void testGetCodesByVehicleId(){
88 | assertTrue(TestHelper.getVehicleId() != null);
89 |
90 | vinliApp.diagnostics().codes(TestHelper.getVehicleId(), null, null, null, null, null)
91 | .toBlocking().subscribe(new Subscriber>() {
92 | @Override
93 | public void onCompleted() {
94 | }
95 |
96 | @Override
97 | public void onError(Throwable e) {
98 | System.out.println("Error: " + e.getMessage());
99 | e.printStackTrace();
100 | assertTrue(false);
101 | }
102 |
103 | @Override
104 | public void onNext(TimeSeries dtcPage) {
105 | assertTrue(dtcPage.getItems().size() > 0);
106 |
107 | for(Dtc dtc : dtcPage.getItems()){
108 | assertTrue(dtc.start() != null && dtc.start().length() > 0);
109 | assertTrue(dtc.vehicleId() != null && dtc.vehicleId().length() > 0);
110 | assertTrue(dtc.deviceId() != null && dtc.deviceId().length() > 0);
111 | assertTrue(dtc.number() != null && dtc.number().length() > 0);
112 | assertTrue(dtc.description() != null && dtc.description().length() > 0);
113 | }
114 | }
115 | });
116 | }
117 |
118 | @Test
119 | public void testDiagnoseCode(){
120 | final AtomicBoolean codeFound = new AtomicBoolean(false);
121 | vinliApp.diagnostics().diagnose("P0301")
122 | .flatMap(Page.allItems())
123 | .toBlocking().subscribe(new Subscriber() {
124 | @Override
125 | public void onCompleted() {
126 |
127 | }
128 |
129 | @Override
130 | public void onError(Throwable e) {
131 | System.out.println("Error: " + e.getMessage());
132 | e.printStackTrace();
133 | assertTrue(false);
134 | }
135 |
136 | @Override
137 | public void onNext(Dtc.Code code) {
138 | codeFound.set(true);
139 | assertTrue(code.make() != null && code.make().length() > 0);
140 | }
141 | });
142 | assertTrue(codeFound.get());
143 | }
144 |
145 | @Test public void getCurrentBatteryStatus() {
146 | assertTrue(TestHelper.getVehicleId() != null);
147 |
148 | BatteryStatus.currentBatteryStatusForVehicle(TestHelper.getVehicleId())
149 | .toBlocking()
150 | .subscribe(new Subscriber() {
151 | @Override public void onCompleted() {
152 |
153 | }
154 |
155 | @Override public void onError(Throwable e) {
156 | e.printStackTrace();
157 | assertTrue(false);
158 | }
159 |
160 | @Override public void onNext(BatteryStatus batteryStatus) {
161 | if (batteryStatus != null) {
162 | assertTrue(batteryStatus.status() != null);
163 | assertTrue(batteryStatus.timestamp().length() > 0);
164 | }
165 | }
166 | });
167 | }
168 |
169 | }
170 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/Dummy.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.os.Parcelable;
4 | import android.support.annotation.NonNull;
5 | import android.support.annotation.Nullable;
6 | import auto.parcel.AutoParcel;
7 | import com.google.gson.Gson;
8 | import com.google.gson.GsonBuilder;
9 | import com.google.gson.TypeAdapter;
10 | import com.google.gson.reflect.TypeToken;
11 | import com.google.gson.stream.JsonReader;
12 | import com.google.gson.stream.JsonWriter;
13 | import java.io.IOException;
14 | import java.lang.reflect.Type;
15 | import rx.Observable;
16 |
17 | /**
18 | * Created by JoshBeridon on 11/18/16.
19 | */
20 | @AutoParcel public abstract class Dummy implements VinliItem {
21 | /*package*/ static final Type PAGE_TYPE = new TypeToken>() {
22 | }.getType();
23 | /*package*/ static final Type WRAPPED_TYPE = new TypeToken>() {
24 | }.getType();
25 | /*package*/ static final Type WRAPPED_TYPE_RUN = new TypeToken>() {
26 | }.getType();
27 |
28 | static final void registerGson(GsonBuilder gb) {
29 | gb.registerTypeAdapter(Dummy.class, AutoParcelAdapter.create(AutoParcel_Dummy.class));
30 | gb.registerTypeAdapter(Links.class, AutoParcelAdapter.create(AutoParcel_Dummy_Links.class));
31 | gb.registerTypeAdapter(Run.class, AutoParcelAdapter.create(AutoParcel_Dummy_Run.class));
32 | gb.registerTypeAdapter(Run.Status.class,
33 | AutoParcelAdapter.create(AutoParcel_Dummy_Run_Status.class));
34 | gb.registerTypeAdapter(Run.Links.class,
35 | AutoParcelAdapter.create(AutoParcel_Dummy_Run_Links.class));
36 | gb.registerTypeAdapter(WRAPPED_TYPE, Wrapped.Adapter.create(Dummy.class, "dummies"));
37 | gb.registerTypeAdapter(PAGE_TYPE, Page.Adapter.create(PAGE_TYPE, Dummy.class, "dummies"));
38 | gb.registerTypeAdapter(WRAPPED_TYPE_RUN, Wrapped.Adapter.create(Run.class, "run"));
39 |
40 | gb.registerTypeAdapter(Run.Seed.class, new Dummy.Run.Seed.Adapter());
41 | }
42 |
43 | public static Observable currentRun(@NonNull String dummyId) {
44 | return Vinli.curApp().run(dummyId);
45 | }
46 |
47 | @NonNull public abstract String id();
48 |
49 | @NonNull public abstract String name();
50 |
51 | @NonNull public abstract String caseId();
52 |
53 | @NonNull public abstract String deviceId();
54 |
55 | /*package*/
56 | abstract Links links();
57 |
58 | @AutoParcel
59 | /*package*/ static abstract class Links {
60 | public abstract String self();
61 |
62 | public abstract String runs();
63 |
64 | public abstract String device();
65 |
66 | public abstract String messages();
67 |
68 | public abstract String events();
69 |
70 | /*package*/ Links() {
71 | }
72 | }
73 |
74 | @AutoParcel public static abstract class Run implements VinliItem {
75 |
76 | @NonNull public abstract String id();
77 |
78 | /*package*/
79 | @NonNull public abstract Status status();
80 |
81 | /*package*/Run() {
82 |
83 | }
84 |
85 | @AutoParcel
86 | /*package*/ static abstract class Status implements Parcelable {
87 |
88 | public abstract String routeId();
89 |
90 | public abstract boolean repeat();
91 |
92 | public abstract String state();
93 |
94 | // public abstract String lastLocation();//TODO this is not a string
95 |
96 | @Nullable public abstract String lastSpeed();
97 |
98 | @Nullable public abstract Double lastRPM();
99 |
100 | @Nullable public abstract Long lastMessageTime();
101 |
102 | @Nullable public abstract Integer totalMessages();
103 |
104 | @Nullable public abstract Integer sentMessages();
105 |
106 | @Nullable public abstract Integer remaningMessages();
107 |
108 | @Nullable public abstract Double remaningSeconds();
109 |
110 | /*package*/ Status() {
111 | }
112 | }
113 |
114 | /*package*/
115 | abstract Links links();
116 |
117 | @AutoParcel
118 | /*package*/ static abstract class Links {
119 | public abstract String self();
120 |
121 | /*package*/ Links() {
122 | }
123 | }
124 |
125 | @AutoParcel public static abstract class Seed {
126 | @NonNull public abstract String vin();
127 |
128 | @NonNull public abstract String routeId();
129 |
130 | @Nullable public abstract String repeat();
131 |
132 | /*package*/ Seed() {
133 | }
134 |
135 | @AutoParcel.Builder public static abstract class Saver {
136 | public abstract Saver vin(@NonNull String s);
137 |
138 | public abstract Saver routeId(@NonNull String s);
139 |
140 | public abstract Saver repeat(@Nullable String s);
141 |
142 | /*package*/ Saver() {
143 | }
144 |
145 | /*package*/
146 | abstract Seed autoBuild();
147 |
148 | public Observable save(String dummyId) {
149 | final Seed s = autoBuild();
150 | return Vinli.curApp()
151 | .dummies()
152 | .create(dummyId, s)
153 | .map(Wrapped.pluckItem());
154 | }
155 | }
156 |
157 | /*package*/ static final class Adapter extends TypeAdapter {
158 | private Gson gson;
159 |
160 | @Override public void write(JsonWriter out, Run.Seed value) throws IOException {
161 | if (gson == null) {
162 | gson = Vinli.curApp().gson();
163 | }
164 |
165 | out.beginObject();
166 | out.name("run").beginObject();
167 | out.name("vin").value(value.vin());
168 | out.name("routeId").value(value.routeId());
169 | out.name("repeat").value(value.repeat());
170 | out.endObject();
171 | out.endObject();
172 | }
173 |
174 | @Override public Run.Seed read(JsonReader in) throws IOException {
175 | throw new UnsupportedOperationException("reading a RunSeed is not supported");
176 | }
177 | }
178 | }
179 |
180 | public static final Run.Seed.Saver create() {
181 | return new AutoParcel_Dummy_Run_Seed.Builder();
182 | }
183 |
184 | public Observable delete(String dummyId) {
185 | return Vinli.curApp().dummies().deleteRun(dummyId);
186 | }
187 | }
188 | }
189 |
--------------------------------------------------------------------------------
/android-net/src/main/java/li/vin/net/SupportedPids.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import android.support.annotation.NonNull;
4 | import java.util.ArrayList;
5 | import java.util.HashMap;
6 | import java.util.HashSet;
7 | import java.util.Locale;
8 | import java.util.Map;
9 | import java.util.Set;
10 |
11 | final class SupportedPids {
12 |
13 | private static final Set KNOWN_UNSUPPORTED_PIDS;
14 |
15 | static {
16 | KNOWN_UNSUPPORTED_PIDS = new HashSet<>();
17 |
18 | // markers for supported pids
19 | KNOWN_UNSUPPORTED_PIDS.add("01-00");
20 | KNOWN_UNSUPPORTED_PIDS.add("01-20");
21 | KNOWN_UNSUPPORTED_PIDS.add("01-40");
22 | KNOWN_UNSUPPORTED_PIDS.add("01-60");
23 | KNOWN_UNSUPPORTED_PIDS.add("01-80");
24 |
25 | // dtcs
26 | KNOWN_UNSUPPORTED_PIDS.add("01-01");
27 |
28 | // accelerometer
29 | KNOWN_UNSUPPORTED_PIDS.add("01-0c");
30 |
31 | // >2 byte pids from https://en.wikipedia.org/wiki/OBD-II_PIDs#Standard_PIDs
32 | KNOWN_UNSUPPORTED_PIDS.add("01-24");
33 | KNOWN_UNSUPPORTED_PIDS.add("01-25");
34 | KNOWN_UNSUPPORTED_PIDS.add("01-26");
35 | KNOWN_UNSUPPORTED_PIDS.add("01-27");
36 | KNOWN_UNSUPPORTED_PIDS.add("01-28");
37 | KNOWN_UNSUPPORTED_PIDS.add("01-29");
38 | KNOWN_UNSUPPORTED_PIDS.add("01-2a");
39 | KNOWN_UNSUPPORTED_PIDS.add("01-2b");
40 |
41 | KNOWN_UNSUPPORTED_PIDS.add("01-34");
42 | KNOWN_UNSUPPORTED_PIDS.add("01-35");
43 | KNOWN_UNSUPPORTED_PIDS.add("01-36");
44 | KNOWN_UNSUPPORTED_PIDS.add("01-37");
45 | KNOWN_UNSUPPORTED_PIDS.add("01-38");
46 | KNOWN_UNSUPPORTED_PIDS.add("01-39");
47 | KNOWN_UNSUPPORTED_PIDS.add("01-3a");
48 | KNOWN_UNSUPPORTED_PIDS.add("01-3b");
49 |
50 | KNOWN_UNSUPPORTED_PIDS.add("01-41");
51 | KNOWN_UNSUPPORTED_PIDS.add("01-4f");
52 | KNOWN_UNSUPPORTED_PIDS.add("01-50");
53 |
54 | KNOWN_UNSUPPORTED_PIDS.add("01-64");
55 | //KNOWN_UNSUPPORTED_PIDS.add("01-66");
56 | KNOWN_UNSUPPORTED_PIDS.add("01-67");
57 | KNOWN_UNSUPPORTED_PIDS.add("01-68");
58 | KNOWN_UNSUPPORTED_PIDS.add("01-69");
59 | KNOWN_UNSUPPORTED_PIDS.add("01-6a");
60 | KNOWN_UNSUPPORTED_PIDS.add("01-6b");
61 | KNOWN_UNSUPPORTED_PIDS.add("01-6c");
62 | KNOWN_UNSUPPORTED_PIDS.add("01-6d");
63 | KNOWN_UNSUPPORTED_PIDS.add("01-6e");
64 | KNOWN_UNSUPPORTED_PIDS.add("01-6f");
65 |
66 | KNOWN_UNSUPPORTED_PIDS.add("01-70");
67 | KNOWN_UNSUPPORTED_PIDS.add("01-71");
68 | KNOWN_UNSUPPORTED_PIDS.add("01-72");
69 | KNOWN_UNSUPPORTED_PIDS.add("01-73");
70 | KNOWN_UNSUPPORTED_PIDS.add("01-74");
71 | KNOWN_UNSUPPORTED_PIDS.add("01-75");
72 | KNOWN_UNSUPPORTED_PIDS.add("01-76");
73 | KNOWN_UNSUPPORTED_PIDS.add("01-77");
74 | KNOWN_UNSUPPORTED_PIDS.add("01-78");
75 | KNOWN_UNSUPPORTED_PIDS.add("01-79");
76 | KNOWN_UNSUPPORTED_PIDS.add("01-7a");
77 | KNOWN_UNSUPPORTED_PIDS.add("01-7b");
78 | KNOWN_UNSUPPORTED_PIDS.add("01-7c");
79 | KNOWN_UNSUPPORTED_PIDS.add("01-7f");
80 |
81 | KNOWN_UNSUPPORTED_PIDS.add("01-81");
82 | KNOWN_UNSUPPORTED_PIDS.add("01-82");
83 | KNOWN_UNSUPPORTED_PIDS.add("01-83");
84 |
85 | KNOWN_UNSUPPORTED_PIDS.add("01-a0");
86 | KNOWN_UNSUPPORTED_PIDS.add("01-c0");
87 | }
88 |
89 | private final String raw;
90 | private HashMap supportMap;
91 |
92 | /*package*/ SupportedPids(@NonNull String raw) {
93 | this.raw = raw;
94 | }
95 |
96 | public String getRaw() {
97 | return raw;
98 | }
99 |
100 | public boolean supports(@NonNull String code) {
101 | if (!(code = code.toLowerCase(Locale.US)).startsWith("01-")) return false;
102 | if (KNOWN_UNSUPPORTED_PIDS.contains(code)) return false;
103 | if (supportMap == null) buildSupportMap();
104 | Boolean result = supportMap.get(code);
105 | return result == null
106 | ? false
107 | : result;
108 | }
109 |
110 | @NonNull
111 | public String[] getSupport() {
112 | if (supportMap == null) buildSupportMap();
113 | Set support = new HashSet<>();
114 | for (Map.Entry e : supportMap.entrySet()) {
115 | if (e.getValue() != null && e.getValue()) {
116 | support.add(e.getKey().toUpperCase(Locale.US));
117 | }
118 | }
119 | return support.toArray(new String[support.size()]);
120 | }
121 |
122 | @Override
123 | public String toString() {
124 | if (supportMap == null) buildSupportMap();
125 | StringBuilder sb = new StringBuilder();
126 | for (Map.Entry entry : supportMap.entrySet()) {
127 | String key = entry.getKey();
128 | Boolean val = entry.getValue();
129 | if (sb.length() != 0) sb.append("::");
130 | sb.append("key='").append(key).append("',val='").append(val).append("'");
131 | }
132 | return sb.toString();
133 | }
134 |
135 | private void buildSupportMap() {
136 | supportMap = new HashMap<>();
137 | ArrayList groups = new ArrayList<>();
138 | StringBuilder sb = new StringBuilder();
139 | for (int i = 0; i < raw.length(); i++) {
140 | if (sb.length() == 8) {
141 | groups.add(sb.toString());
142 | sb.delete(0, 8);
143 | }
144 | sb.append(raw.charAt(i));
145 | }
146 | if (sb.length() == 8) {
147 | groups.add(sb.toString());
148 | }
149 | for (int i = 0; i < groups.size(); i++) {
150 | parseBitflags(i, groups.get(i));
151 | }
152 | }
153 |
154 | private void parseBitflags(int group, String bitflags) {
155 | int groupStart = group * 32 + 1;
156 | for (int i = 0; i < 8; i++) {
157 | int j = i * 4;
158 | int hexInt = Integer.parseInt(bitflags.substring(i, i + 1), 16);
159 | String bin = Integer.toBinaryString(hexInt);
160 | while (bin.length() < 4) bin = '0' + bin;
161 | putIntoMap(groupStart + j, bin.charAt(0) == '1');
162 | putIntoMap(groupStart + j + 1, bin.charAt(1) == '1');
163 | putIntoMap(groupStart + j + 2, bin.charAt(2) == '1');
164 | putIntoMap(groupStart + j + 3, bin.charAt(3) == '1');
165 | }
166 | }
167 |
168 | private void putIntoMap(int index, boolean flag) {
169 | String hex = Integer.toHexString(index).toLowerCase(Locale.US);
170 | while (hex.length() < 2) hex = '0' + hex;
171 | supportMap.put("01-" + hex, flag);
172 | }
173 | }
174 |
--------------------------------------------------------------------------------
/android-net/src/test/java/li/vin/net/CollisionsIntegrationTests.java:
--------------------------------------------------------------------------------
1 | package li.vin.net;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 | import org.junit.runner.RunWith;
6 | import org.robolectric.RobolectricTestRunner;
7 | import org.robolectric.annotation.Config;
8 |
9 | import rx.Subscriber;
10 |
11 | import static junit.framework.Assert.assertTrue;
12 |
13 | @RunWith(RobolectricTestRunner.class)
14 | @Config(constants = BuildConfig.class, sdk = 22)
15 | public class CollisionsIntegrationTests {
16 |
17 | public VinliApp vinliApp;
18 |
19 | @Before
20 | public void setup(){
21 | assertTrue(TestHelper.getAccessToken() != null);
22 |
23 | vinliApp = TestHelper.getVinliApp();
24 | }
25 |
26 | @Test
27 | public void testGetPagedCollisions(){
28 | Collision.collisionsWithDeviceId(TestHelper.getDeviceId(), (Long) null, null, 1, null)
29 | .toBlocking().subscribe(new Subscriber