com.shrlnm.android.erm.ElmRenaultMonitor_Main.java Source code

Java tutorial

Introduction

Here is the source code for com.shrlnm.android.erm.ElmRenaultMonitor_Main.java

Source

/*
 * Copyright (C) 2009 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.shrlnm.android.erm;

//import android.annotation.SuppressLint;
import android.app.ActionBar;
import android.app.Activity;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.os.PowerManager;
import android.support.v4.content.LocalBroadcastManager;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
//import android.view.SurfaceHolder;
//import android.view.SurfaceView;
import android.view.Window;
import android.view.WindowManager;
//import android.widget.EditText;
//import android.widget.TextView;
import android.widget.Toast;

import java.util.ArrayList;
import java.util.List;

import static com.shrlnm.android.erm.ElmRenaultMonitor_ElmThread.STATE_CONNECTED;
import static com.shrlnm.android.erm.ElmRenaultMonitor_ElmThread.STATE_CONNECTING;
import static com.shrlnm.android.erm.ElmRenaultMonitor_ElmThread.STATE_LISTEN;
import static com.shrlnm.android.erm.ElmRenaultMonitor_ElmThread.STATE_NONE;

//import com.shrlnm.android.erm.R;

//import android.os.Environment;

/**
 * This is the main Activity that displays main window.
 */
public class ElmRenaultMonitor_Main extends Activity {
    // Debugging
    private static final String TAG = "ERM_main_debug";
    private static final boolean D = true;

    // Message types sent from the BluetoothChatService Handler
    public static final int MESSAGE_STATE_CHANGE = 1;
    public static final int MESSAGE_READ = 2;
    public static final int MESSAGE_WRITE = 3;
    public static final int MESSAGE_DEVICE_NAME = 4;
    public static final int MESSAGE_TOAST = 5;

    // Message types sent from other activities
    public static final int MESSAGE_ACT_STOP_REPEAT = 1;
    public static final int MESSAGE_ACT_SND_CMD = 2;
    public static final int MESSAGE_ACT_SEND_ME_NEW_DATA = 3;

    // Key names received from the BluetoothChatService Handler
    public static final String DEVICE_NAME = "device_name";
    public static final String TOAST = "toast";

    // Intent request codes
    private static final int REQUEST_CONNECT_DEVICE = 1;
    private static final int REQUEST_ENABLE_BT = 3;

    // Layout Views
    //private TextView m_par_RPM;
    //private EditText mConsoleOut;
    //private EditText mOutEditText;

    // Name of the connected device
    private String mConnectedDeviceName = null;
    // Local Bluetooth adapter
    private BluetoothAdapter mBluetoothAdapter = null;
    // Member object for the chat services
    private ElmRenaultMonitor_ElmThread mChatService = null;

    // Additional constants
    private static boolean waitcmd = false;

    // Last sent command
    private String lastCommand = null;
    // Command queue, to send new command just add it there
    private List<String> cmd_queue = new ArrayList<String>();
    // to repeat command continuously put it there and set reapeatCmd
    private String repetitiveCmd = "";
    private boolean repeatCmd = false;

    SharedPreferences sharedPref;

    private PowerManager powerManager;
    private PowerManager.WakeLock wakeLock;

    private InstrumentsView iv;

    private ecu eRsp;

    private int errorPollingPeriod = 60000;

    // Starts first. Enable bluetooth, load previous MAC and register receiver
    @Override
    public void onCreate(Bundle savedInstanceState) {

        //Enter full screen mode
        requestWindowFeature(Window.FEATURE_NO_TITLE);
        getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
                WindowManager.LayoutParams.FLAG_FULLSCREEN);

        super.onCreate(savedInstanceState);
        if (D)
            Log.e(TAG, "+++ ON CREATE +++");

        powerManager = (PowerManager) getSystemService(Context.POWER_SERVICE);
        wakeLock = powerManager.newWakeLock(PowerManager.FULL_WAKE_LOCK, "MyLock");

        // Get local Bluetooth adapter
        mBluetoothAdapter = BluetoothAdapter.getDefaultAdapter();

        // If the adapter is null, then Bluetooth is not supported
        if (mBluetoothAdapter == null) {
            Toast.makeText(this, "Bluetooth is not available", Toast.LENGTH_LONG).show();
            finish();
            return;
        }

        LocalBroadcastManager.getInstance(this).registerReceiver(localMessageReceiver,
                new IntentFilter("ElmRenaultMonitor_Main"));

        // there we will save the mac address of BT device for not to ask it again
        sharedPref = this.getSharedPreferences(getString(R.string.preference_addr_key), MODE_PRIVATE);

        // Set up the window layout
        //setContentView(R.layout.main);
        iv = new InstrumentsView(this);
        setContentView(iv);

        eRsp = new ecu();

    }

    // Starts second. Start and connect bluetooth
    @Override
    public void onStart() {
        super.onStart();
        if (D)
            Log.e(TAG, "++ ON START ++");

        // If BT is not on, request that it be enabled.
        // setupChat() will then be called during onActivityResult
        if (!mBluetoothAdapter.isEnabled()) {
            Intent enableIntent = new Intent(BluetoothAdapter.ACTION_REQUEST_ENABLE);
            startActivityForResult(enableIntent, REQUEST_ENABLE_BT);
            // Otherwise, setup the chat session
        } else {
            if (mChatService == null)
                setupChat();
        }

        if (STATE_CONNECTED != mChatService.getState()) {
            // load saved mac address.
            String mac_address = sharedPref.getString(getString(R.string.preference_addr_key), "");
            if ((mac_address != null ? mac_address.length() : 0) == 17) {
                // it is and we just connect to it
                connectDevice(mac_address);
            } else {
                // there is no saved address then ask it
                try {
                    Intent serverIntent = new Intent(this, DeviceListActivity.class);
                    startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
                } catch (android.content.ActivityNotFoundException e) {
                    if (D)
                        Log.e(TAG, "+++ ActivityNotFoundException +++");
                }

            }
        }
    }

    @Override
    public synchronized void onResume() {
        super.onResume();
        if (D)
            Log.e(TAG, "+ ON RESUME +");

        // Acquire wakeLock
        wakeLock.acquire();

        // resume rendering thread
        //iv.resume();

        // Performing this check in onResume() covers the case in which BT was
        // not enabled during onStart(), so we were paused to enable it...
        // onResume() will be called when ACTION_REQUEST_ENABLE activity returns.
        if (mChatService != null) {
            // Only if the state is STATE_NONE, do we know that we haven't started already
            if (STATE_NONE == mChatService.getState()) {
                // Start the Bluetooth chat services
                mChatService.start();
            }
        }
    }

    // Handler for received Intents. This will be called whenever an Intent
    // with an action named "msg" is broadcasted.
    private BroadcastReceiver localMessageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            int msg = intent.getIntExtra("msg", MESSAGE_ACT_STOP_REPEAT);
            if (D)
                Log.d(TAG, "Got message: " + msg);

            switch (msg) {
            case MESSAGE_ACT_STOP_REPEAT:
                repetitiveCmd = "";
                repeatCmd = false;
                break;
            case MESSAGE_ACT_SND_CMD:
                repetitiveCmd = intent.getStringExtra("cmd");
                repeatCmd = true;
                sendCmd(""); //just for check the queue
                break;
            case MESSAGE_ACT_SEND_ME_NEW_DATA:
                sendCmd("");
                break;
            }
        }
    };

    private void setupChat() {
        Log.d(TAG, "setupChat()");

        // Initialize the BluetoothChatService to perform bluetooth connections
        mChatService = new ElmRenaultMonitor_ElmThread(mHandler);
    }

    @Override
    public synchronized void onPause() {
        super.onPause();
        if (D)
            Log.e(TAG, "- ON PAUSE -");

        //release wakeLock
        wakeLock.release();

        //pause rendering thread
        //iv.pause();
    }

    @Override
    public void onStop() {
        super.onStop();
        if (D)
            Log.e(TAG, "-- ON STOP --");
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        // Stop the Bluetooth chat services
        if (mChatService != null)
            mChatService.stop();
        if (D)
            Log.e(TAG, "--- ON DESTROY ---");
        LocalBroadcastManager.getInstance(this).unregisterReceiver(localMessageReceiver);
    }

    private void setStatus(int resId) {
        final ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setSubtitle(resId);
        }
    }

    private void setStatus(CharSequence subTitle) {
        final ActionBar actionBar = getActionBar();
        if (actionBar != null) {
            actionBar.setSubtitle(subTitle);
        }
    }

    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (D)
            Log.d(TAG, "onActivityResult " + resultCode);
        switch (requestCode) {
        case REQUEST_CONNECT_DEVICE:
            // When DeviceListActivity returns with a device to connect
            if (resultCode == Activity.RESULT_OK) {
                String address = data.getExtras().getString(DeviceListActivity.EXTRA_DEVICE_ADDRESS);
                connectDevice(address);
            }
            break;
        case REQUEST_ENABLE_BT:
            // When the request to enable Bluetooth returns
            if (resultCode == Activity.RESULT_OK) {
                // Bluetooth is now enabled, so set up a chat session
                setupChat();
            } else {
                // User did not enable Bluetooth or an error occurred
                Log.d(TAG, "BT not enabled");
                Toast.makeText(this, R.string.bt_not_enabled_leaving, Toast.LENGTH_SHORT).show();
                finish();
            }
        }
    }

    private void connectDevice(String address) {
        // address is the device MAC address
        // Get the BluetoothDevice object
        BluetoothDevice device = mBluetoothAdapter.getRemoteDevice(address);
        // Attempt to connect to the device
        mChatService.connect(device);

        // save mac address for not to ask it again
        SharedPreferences.Editor editor = sharedPref.edit();
        editor.putString(getString(R.string.preference_addr_key), address);
        editor.apply();

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.option_menu, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        Intent serverIntent;
        switch (item.getItemId()) {
        case R.id.bt_connect_scan:
            // Launch the DeviceListActivity to see devices and do scan
            serverIntent = new Intent(this, DeviceListActivity.class);
            startActivityForResult(serverIntent, REQUEST_CONNECT_DEVICE);
            return true;
        case R.id.init_elm:
            // initialize ELM
            cmd_queue.clear();
            repetitiveCmd = "";
            repeatCmd = false;
            waitcmd = false;
            initELM();
            return true;
        case R.id.exit_from_program:
            finish();
        }
        return false;
    }

    // The Handler for the periodic error poling
    Handler hErrorsPoling = new Handler();
    private Runnable errorPoller = new Runnable() {
        @Override
        public void run() {
            sendCmd("at caf1"); // disable frame auto formatting
            sendCmd("10C0"); // open communication session
            sendCmd("17FF00"); // read errors STD-A
            sendCmd("1902AF"); // read errors STD-B
            sendCmd("at caf0"); // disable frame auto formatting
            sendCmd("at ma");
            hErrorsPoling.postDelayed(errorPoller, errorPollingPeriod);
        }
    };

    // The Handler that gets information back from the BluetoothChatService
    final Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            switch (msg.what) {
            case MESSAGE_STATE_CHANGE:
                if (D)
                    Log.i(TAG, "MESSAGE_STATE_CHANGE: " + msg.arg1);
                switch (msg.arg1) {
                case STATE_CONNECTED:
                    setStatus(getString(R.string.title_connected_to, mConnectedDeviceName));
                    initELM();
                    break;
                case STATE_CONNECTING:
                    setStatus(R.string.title_connecting);
                    break;
                case STATE_LISTEN:
                case STATE_NONE:
                    setStatus(R.string.title_not_connected);
                    break;
                }
                break;
            case MESSAGE_WRITE:
                byte[] writeBuf = (byte[]) msg.obj;
                // construct a string from the buffer
                String writeMessage = new String(writeBuf);
                if (writeMessage.length() > 2) {
                    lastCommand = writeMessage.replace("\r", "");
                }
                break;
            case MESSAGE_READ:
                byte[] readBuf = (byte[]) msg.obj;
                // construct a string from the valid bytes in the buffer
                String readenMessage = new String(readBuf, 0, msg.arg1);
                if (D)
                    Log.i(TAG, "handler resp: " + msg.arg1 + " : " + readenMessage);

                for (int i = 0; i < readenMessage.length(); i++) {
                    if (readenMessage.charAt(i) == '>')
                        waitcmd = false;
                }

                //if (readenMessage.length()==1 ) break; // to short (probably \n)

                //if (!repeatCmd) {
                //    //mConsoleOut.append(readenMessage);
                //    waitcmd = false;
                //}

                if (readenMessage.charAt(0) == 'B' && readenMessage.charAt(1) == 'U') { //BUFFER FULL
                    sendCmd("at ma");
                    break;
                }

                //readenMessage = normalizeResponse( readenMessage );
                parseResult(lastCommand, readenMessage);
                sendCmd(""); //check if cmd_queue is not empty
                break;
            case MESSAGE_DEVICE_NAME:
                // save the connected device's name
                mConnectedDeviceName = msg.getData().getString(DEVICE_NAME);
                Toast.makeText(getApplicationContext(), "Connected to " + mConnectedDeviceName, Toast.LENGTH_SHORT)
                        .show();
                break;
            case MESSAGE_TOAST:
                Toast.makeText(getApplicationContext(), msg.getData().getString(TOAST), Toast.LENGTH_SHORT).show();
                break;
            }
        }
    };

    private void sendCmd(String cmd) {

        if (D)
            Log.d(TAG, "sendCmd cmd:" + cmd + " waitcmd:" + waitcmd + " cmd_queue.len:" + cmd_queue.size());

        // Check that we're actually connected before trying anything
        if (mChatService.getState() != STATE_CONNECTED) {
            Toast.makeText(this, R.string.not_connected, Toast.LENGTH_SHORT).show();
            return;
        }

        if (cmd.length() > 0)
            cmd_queue.add(cmd);

        if (waitcmd)
            return; // we are waiting results from previous command

        if (cmd_queue.size() == 0 && repetitiveCmd.length() == 0)
            return; // nothing to send

        if (cmd_queue.size() > 0) {
            // cmd_queue not empty - it has priority
            cmd = cmd_queue.get(0);
            cmd_queue.remove(0);
        } else {
            if (repeatCmd && repetitiveCmd.equals(lastCommand)) {
                cmd = "\r";
            } else {
                if (repetitiveCmd.length() > 0) {
                    cmd = repetitiveCmd;
                    repeatCmd = true;
                }
            }
        }

        waitcmd = true;
        // Add \r
        if (!cmd.equals("\r"))
            cmd = cmd + "\r";
        byte[] send = cmd.getBytes();
        mChatService.write(send);

    }

    private static boolean is0x(char c) {
        return ((c >= '0' && c <= '9') || (c >= 'A' && c <= 'F'));
    }

    private void parseResult(String cmd, String rsp) {
        if (D)
            Log.d(TAG, "parseResult cmd:" + cmd + " resp:" + rsp);

        if (rsp.length() < 8)
            return;
        if (!(is0x(rsp.charAt(0)) && is0x(rsp.charAt(1)) && is0x(rsp.charAt(2))))
            return;

        try {
            //extract address
            Integer address = Integer.parseInt(rsp.substring(0, 3), 16);

            // check if elm is appropriate initialized
            if (rsp.charAt(3) == ' ' && is0x(rsp.charAt(4)) && rsp.charAt(5) != ' ') {
                initELM();
                return;
            }

            //extract frame length
            Integer length = Integer.parseInt(rsp.substring(4, 5), 16);

            //extract frame data
            String df = rsp.substring(6, rsp.length());
            df = df.replace(" ", "");

            if (address != 0x7E8 && df.length() < length * 2)
                return;

            Integer tmp;
            String errRsp;

            switch (address) {
            case 0x7E8:
                eRsp.nextFrame(rsp);
                break;
            case 0xC2:
                tmp = Integer.parseInt(df.substring(0, 4), 16);
                iv.par_w_angle = (tmp - 0x7fff) / 10;
                break;
            case 0x161:
                tmp = Integer.parseInt(df.substring(0, 2), 16);
                iv.par_torque = tmp * 2 - 100;
                break;
            case 0x1F9:
                tmp = Integer.parseInt(df.substring(4, 8), 16);
                iv.par_rpm = tmp / 80 * 10;
                break;
            case 0x284:
                tmp = Integer.parseInt(df.substring(0, 4), 16);
                iv.par_speed_rw = tmp;
                tmp = Integer.parseInt(df.substring(4, 8), 16);
                iv.par_speed_lw = tmp;
                tmp = Integer.parseInt(df.substring(8, 12), 16);
                iv.par_speed = tmp / 100;
                break;
            case 0x285:
                tmp = Integer.parseInt(df.substring(0, 4), 16);
                iv.par_speed_rrw = tmp;
                tmp = Integer.parseInt(df.substring(4, 8), 16);
                iv.par_speed_rlw = tmp;
                iv.tireAssess();
                break;
            case 0x354:
                //tmp = Integer.parseInt(df.substring(0, 4), 16);
                //iv.par_speed = tmp / 100;
                break;
            case 0x551:
                tmp = Integer.parseInt(df.substring(0, 2), 16);
                iv.par_temp = tmp - 40;
                tmp = Integer.parseInt(df.substring(2, 3), 16);
                iv.consumptionAssess(tmp);
                break;
            }
        } catch (Exception e) {
            if (D)
                Log.d(TAG, "parseResult error resp:" + rsp);
        }
    }

    private void initELM() {
        sendCmd("at z"); // reset ELM
        sendCmd("at e1"); // echo on
        sendCmd("at l1"); // line feed
        sendCmd("at h1"); // show header (address DLC PCI)
        sendCmd("at d1"); // show DLC
        sendCmd("at sp6"); // choose CAN protocol
        sendCmd("at al"); // permit long frames
        sendCmd("at sh 7E0"); // set address for sending frames
        hErrorsPoling.post(errorPoller);
        //sendCmd("100210C0");        // open communication session
        //sendCmd("100317FF00");      // read errors STD-A
        //sendCmd("10031902AF");      // read errors STD-B
        //sendCmd("at ma");       // goto monitor mode
    }

}