com.jaguarlandrover.auto.remote.vehicleentry.RviService.java Source code

Java tutorial

Introduction

Here is the source code for com.jaguarlandrover.auto.remote.vehicleentry.RviService.java

Source

/**
 *  Copyright (C) 2015, Jaguar Land Rover
 *
 *  This program is licensed under the terms and conditions of the
 *  Mozilla Public License, version 2.0.  The full text of the
 *  Mozilla Public License is at https://www.mozilla.org/MPL/2.0/
 *
 */

package com.jaguarlandrover.auto.remote.vehicleentry;

import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.bluetooth.BluetoothAdapter;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder;
import android.os.IBinder;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.util.Log;

import org.json.JSONObject;

import rx.Observable;
import rx.Subscriber;
import rx.schedulers.Schedulers;
import rx.subjects.PublishSubject;

public class RviService extends Service {
    private static final String TAG = "RVI:RVIService";

    private SharedPreferences prefs;

    public RviService() {
    }

    /**
     * Class for clients to access.  Because we know this service always
     * runs in the same process as its clients, we don't need to deal with
     * IPC.
     */
    public class RviBinder extends Binder {
        RviService getService() {
            return RviService.this;
        }
    }

    private BeaconRanger br = null;

    @Override
    public void onCreate() {
        Log.d(TAG, "onCreate Service");

        super.onCreate();

        FobParamsManager.startUpdatingLocation();

        prefs = PreferenceManager.getDefaultSharedPreferences(this);

        //TODO base on VIN instead

        br = new BeaconRanger(this);
        //br.addVinMask("3C3CFFGE8ET291409"); //My Fiat
        //br.addVinMask("3C3CFFGE8ET"); //Lots of fiats
        //br.addVinMask("3"); //Lots of cars
        br.start();
        Observable<RangeObject> obs = br.getRangeStream();
        obs.observeOn(Schedulers.newThread()).subscribeOn(Schedulers.newThread()).subscribe(beaconSubscriber);
    }

    @Override
    public void onDestroy() {
        Log.i(TAG, "onDestroy() Service");

        br.stop();
    }

    private PublishSubject<String> commandSink = PublishSubject.create();

    public Observable<String> servicesAvailable() {
        return commandSink;
    }

    @Override
    public IBinder onBind(Intent intent) {
        Log.d(TAG, "onBind");

        connectServerNode();

        return mBinder;
    }

    private void connectServerNode() {
        ServerNode.connect();
    }

    public void connectVehicleNode(String deviceAddress) {
        VehicleNode.setDeviceAddress(deviceAddress);
        VehicleNode.connect();
    }

    // This is the object that receives interactions from clients.  See
    // RemoteService for a more complete example.
    private final IBinder mBinder = new RviBinder();

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        Log.d(TAG, "onStartCommand");
        connectServerNode();
        starting(intent);
        return START_STICKY;
    }

    protected void starting(Intent intent) {
        if (intent != null && intent.getExtras() != null) {
            int btState = intent.getExtras().getInt("bluetooth", BluetoothAdapter.STATE_OFF);
            if (btState == BluetoothAdapter.STATE_ON) {
                Log.w(TAG, "BT on, start ranging");
                br.start();
            } else if (btState == BluetoothAdapter.STATE_OFF) {
                Log.w(TAG, "BT off, stop ranging");
                br.stop();
            }
        }
    }

    private static final PublishSubject<JSONObject> btSender = PublishSubject.create();

    private Subscriber<RangeObject> beaconSubscriber = new Subscriber<RangeObject>() {
        @Override
        public void onCompleted() {
            Log.i(TAG, "Beacon Ranger DONE");
        }

        @Override
        public void onError(Throwable e) {
            Log.e(TAG, "", e);
        }

        @Override
        public void onNext(final RangeObject ro) {
            final boolean connected = VehicleNode.isConnected();
            final boolean connecting = VehicleNode.isConnecting();
            final boolean unlocked = VehicleNode.isUnlocked();

            final double unlockDistance = Double.parseDouble(prefs.getString("pref_auto_unlock_dist", "1"));
            final double connectDistance = Double.parseDouble(prefs.getString("pref_auto_conn_dist", "3"));

            double grayArea = Double.parseDouble(prefs.getString("pref_auto_lock_unlock_cutoff_gray_area", "0.4"));
            if (grayArea > 1.0 || grayArea < 0.0) {
                Log.d(TAG, "Invalid grayArea: " + grayArea + "! Resetting to default value of 0.4");
                grayArea = 0.4;
            }

            final double weightedCutoff = ((1.0 - grayArea) / 2.0);

            Log.d(TAG, "distance:" + ro.distance + ", weightedDistance:" + ro.weightedDistance + ", unlockDistance:"
                    + unlockDistance + ", connectDistance:" + connectDistance);
            Log.d(TAG, "connected:" + connected + ", connecting:" + connecting + ", unlocked:" + unlocked);

            UserCredentials userCredentials = ServerNode.getUserCredentials();
            if (userCredentials != null) {
                try {
                    if (userCredentials.getUserType().equals("guest")
                            && (!userCredentials.getAuthorizedServices().isLock()
                                    || !userCredentials.isKeyValid())) { // TODO: Test
                        Log.d(TAG, "User is not authorized to lock/unlock car.");

                        return;
                    } else {
                        if (!connected && (ro.distance > connectDistance)) {
                            Log.d(TAG, "Too far out to connect : " + ro.distance);
                            return;
                        }

                        if (connected && (!unlocked) && (ro.weightedDistance > weightedCutoff)) {
                            Log.d(TAG, "Too far out unlock : " + ro.distance);
                            return;
                        }

                        if (connected && (!unlocked) && ro.weightedDistance <= weightedCutoff) {
                            VehicleNode.sendFobSignal(VehicleNode.FOB_SIGNAL_UNLOCK);
                            sendNotification(RviService.this, getResources().getString(R.string.not_auto_unlock));
                            return;
                        }

                        if (connected && unlocked && ro.weightedDistance >= (1.0 - weightedCutoff)) {
                            VehicleNode.sendFobSignal(VehicleNode.FOB_SIGNAL_LOCK);
                            sendNotification(RviService.this, getResources().getString(R.string.not_auto_lock));
                            return;
                        }

                        if (connecting) {
                            Log.d(TAG, "Already connecting to BT endpoint.");
                            return;
                        }

                        if (connected) {
                            Log.d(TAG, "Already connected to BT endpoint.");
                            return;
                        }

                    }
                } catch (Exception e) {
                    e.printStackTrace();

                    return;
                }

                connectVehicleNode(ro.addr);
            }
        }
    };

    static void sendNotification(Context ctx, String action, String... extras) {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ctx);
        NotificationManager nm = (NotificationManager) ctx.getSystemService(Context.NOTIFICATION_SERVICE);
        boolean fire = prefs.getBoolean("pref_fire_notifications", true);

        if (!fire)
            return;

        NotificationCompat.Builder builder = new NotificationCompat.Builder(ctx).setSmallIcon(R.drawable.rvi_not)
                .setAutoCancel(true).setContentTitle(ctx.getResources().getString(R.string.app_name))
                .setContentText(action);

        Intent targetIntent = new Intent(ctx, LockActivity.class);
        int j = 0;
        for (String ex : extras) {
            targetIntent.putExtra("_extra" + (++j), ex);
            targetIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_SINGLE_TOP);
        }
        PendingIntent contentIntent = PendingIntent.getActivity(ctx, 0, targetIntent,
                PendingIntent.FLAG_UPDATE_CURRENT);
        builder.setContentIntent(contentIntent);
        nm.notify(0, builder.build());
    }
}