Search code examples
javaandroidcountdowntimer

Android CounDownTimer.cancel() not working


I'm trying make a serial IEC communication for Android and there I need check if a device send some data. I started a timer on the begin of communication a reset it when some data comes. Reset on incoming data is working, but cancelling before data is saved not working. I can't understand why.

Here is my MainActivity.java code

package cz.trisko.jan.amr;

import android.content.Context;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.CountDownTimer;
import android.os.Environment;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;

import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
import android.widget.Toast;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;

import java.math.BigInteger;
import java.text.DateFormat;
import java.text.SimpleDateFormat;

import java.util.Calendar;
import java.util.Date;
import java.util.Map;
import java.util.regex.Pattern;
import java.util.regex.Matcher;

import android.hardware.usb.UsbDevice;
import android.hardware.usb.UsbManager;
import android.hardware.usb.UsbDeviceConnection;

import com.felhr.usbserial.UsbSerialDevice;
import com.felhr.usbserial.UsbSerialInterface;

import static java.lang.Integer.parseInt;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = "AMR";

    private UsbManager usbManager;
    private UsbDevice device;
    private UsbDeviceConnection connection;
    private UsbSerialDevice serialDevice;

    private String handshake = "/?!\r\n";

    MediaPlayer errSnd;         // chyba
    MediaPlayer bdSnd;          // registry
    MediaPlayer lpSnd;          // profil
    MediaPlayer infoSnd;        // info
    ProgressBar progressBar;

    RadioGroup rg;
    RadioButton rb;
    TextView registryNam;       // textview pro nazev registru
    TextView registryVal;       // textview pro hodnotu registru a stavova hlaseni
    String regName;             // nazev registru
    String regValue;            // hodnota registru


    boolean readRegs;           // typy odectu
    boolean readLastMonth;
    boolean readThisMonth;

    Calendar calendar = Calendar.getInstance();
    DateFormat dateFormat = new SimpleDateFormat("yyMMdd");
    int currentMonth;
    int currentYear;
    Date today;
    Date tomorrow;
    String tomorrowAsString;
    String lastMonthInterval;
    String thisMonthInterval;
    String lpInterval;
    int startMonth;
    int startYear;
    int endMonth;
    int endYear;

    private static final Pattern communicationSpeedIndexPattern
            = Pattern.compile("^.*(/)(\\w{3})(\\d{1})(.*)$"); // [0] - komplet, [1] - /, [2] - vyrobce, [3] - baudrate, [4] - model
    private static final Pattern CRpat = Pattern.compile("^(\r)$"); // CR
    private static final Pattern LFpat = Pattern.compile("^(\n)$"); // LF
    private static final Pattern STXpat = Pattern.compile("^(\\x02)$"); // STX
    private static final Pattern ETXpat = Pattern.compile("^(\\x03)$"); // ETX
    private static final Pattern DLEpat = Pattern.compile("^(\\x10)$"); // DLE
    private static final Pattern ETBpat = Pattern.compile("^(\\x17)$"); // ETB
    private static final Pattern regNameValuePattern
            = Pattern.compile("([^\\(\\)]*)\\(([^\\(\\)]*)\\)"); // [0] - komplet, [1] - Registr, [2] - hodnota
    boolean CRLF; // konec radku prijimanych dat
    boolean isCR;
    boolean isLF;
    boolean firstLine;
    boolean isETX;
    String data; // prichozi data na USB
    String dataLine; // radek s prichozimi daty
    String tmpDataLine; // radek prichozich dat, ktery je zpracovavan po znacich
    //String tmpDataLine2; // radek prichozich dat, ktery je zpracovavan po znacich
    String buffer; // buffer pro vystup do souboru
    //String tmpBuffer = "";
    int dataLength;

    private int[] baudrateList = {300, 600, 1200, 2400, 4800, 9600, 19200};

    String meterId;

    Pattern meterIdPattern = Pattern.compile("0\\.0\\.0|0\\.0|0|C\\.1|C\\.1\\.0|0\\.0\\.1|0\\.0\\.2|0\\.0\\.3|C\\.1\\.1|C\\.90\\.1");

    int speed; // komunikacni rychlost seriove link - pocatek 300 Bd
    int readedSpeed; // maximalni komunikacni rychlost v zarizeni
    String askData; // retezec pro odeslani pozadavku dat
    char valueSOHchar = 0x01; // SOH
    String SOH = String.valueOf(valueSOHchar);
    char valueSTXchar = 0x02; // STX
    String STX = String.valueOf(valueSTXchar);
    char valueETXchar = 0x03; // ETX
    String ETX = String.valueOf(valueETXchar);
    char valueACKchar = 0x06; // ACK
    String ACK = String.valueOf(valueACKchar);
    char valueDLEchar = 0x10; // DLE
    String DLE = String.valueOf(valueDLEchar);
    char valueNAKchar = 0x15; // NAK
    String NAK = String.valueOf(valueNAKchar);
    char valueETBchar = 0x17; // ETB
    String ETB = String.valueOf(valueETBchar);
    int etxIndex; // pocet prichozich ETX

    boolean registryReceived;

    String filename;
    String filepath;
    File myExternalFile;
    boolean successCreateDir;
    boolean outputDirOk;

    boolean meterEchoes;
    //String debugStringValue = "";

    long startTime;
    long currentTime;
    long timeLeft;

    MyTimer responseTimeCounter;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        Log.d(TAG, "-------------- APP START --------------");
        setDates(); // nastavime intervaly odectu

        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        progressBar = findViewById(R.id.progressBar);
        errSnd = MediaPlayer.create(this, R.raw.wrong);
        bdSnd = MediaPlayer.create(this, R.raw.case_closed);
        lpSnd = MediaPlayer.create(this, R.raw.beep_beep_beep);
        infoSnd = MediaPlayer.create(this, R.raw.smack_that_bitch);

        rg = findViewById(R.id.readoutType);
        registryNam = findViewById(R.id.registryNames);
        registryVal = findViewById(R.id.registryValues);

        usbManager = (UsbManager) getSystemService(Context.USB_SERVICE);

        // casovac pro prodlevu na vstupu
        responseTimeCounter = new MyTimer(5000,1);
    }

    public void setDefaultValues() {
        // nastaveni vychozich polozek pro odecet
        regName = "";
        regValue = "";
        readRegs = false;
        readLastMonth = false;
        readThisMonth = false;
        CRLF = false; // konec radku prijimanych dat
        isCR = false;
        isLF = false;
        firstLine = true; // nacitani prvniho radku dat
        isETX = false;
        data = null; // prichozi data na USB
        dataLine = ""; // radek s prichozimi daty
        tmpDataLine = ""; // radek prichozich dat, ktery je zpracovavan po znacich
        buffer = ""; // buffer pro vystup do souboru
        dataLength = 0;
        meterId = "";
        speed = 300; // komunikacni rychlost - pocatek 300 Bd
        readedSpeed = 0; // index komunikacni rychlosti nacteny ze zarizeni
        askData = ""; // retezec pro odeslani pozadavku dat
        tvSetContent(registryNam, "");
        tvSetContent(registryVal, "");
        meterEchoes = false;
        registryReceived = false;
        etxIndex = 0;
    }

    public String toHex(String arg) {
        return String.format("%012x", new BigInteger(1, arg.getBytes()));
    }

    public void setDates() {
        // vypocet dat pro interval odectu
        String leadingStartMonthZero = "";
        String leadingEndMonthZero = "";
        String startDate;
        String endDate;

        currentMonth = calendar.get(Calendar.MONTH) + 1;
        currentYear = calendar.get(Calendar.YEAR) % 100;

        // dnes / today
        today = calendar.getTime();
        // zitra / tomorrow
        calendar.add(Calendar.DAY_OF_YEAR, 1);
        tomorrow = calendar.getTime();
        dateFormat = new SimpleDateFormat("yyMMdd");
        tomorrowAsString = dateFormat.format(tomorrow);

        // interval pro odecet pri vymene - od 1. tohoto mesice do zitra / this month
        if (currentMonth < 10) {
            leadingStartMonthZero = "0";
        }

        startDate = "0" + currentYear + leadingStartMonthZero + currentMonth + "010000";
        endDate = "0" + tomorrowAsString + "0000";

        thisMonthInterval = startDate + ";" + endDate;

        // interval pro std odecet - od 1. minuleho mesice do 1. tohoto mesice / last month
        startYear = currentYear;
        endYear = currentYear;
        startMonth = currentMonth - 1;
        endMonth = currentMonth;

        if (currentMonth == 1) {
            startYear = currentYear - 1;
            startMonth = 12;
            endYear = currentYear;
            endMonth = currentMonth;
        }

        if (endMonth < 10) {
            leadingEndMonthZero = "0";
        }

        if (startMonth < 10) {
            leadingStartMonthZero = "0";
        }

        startDate = "0" + startYear + leadingStartMonthZero + startMonth + "010000";
        endDate = "0" + endYear + leadingEndMonthZero + endMonth + "010000";

        lastMonthInterval = startDate + ";" + endDate;
    }

    private boolean checkUSBdevice() {
        boolean pass = false;
        Map<String, UsbDevice> deviceList = usbManager.getDeviceList();

        if (deviceList.isEmpty()) {
            pass = false;
        } else {
            for (Map.Entry<String, UsbDevice> entry : deviceList.entrySet()) {
                device = entry.getValue();
                if (usbManager.hasPermission(device)) {
                    Log.d(TAG, "USB has permission");
                    pass = true;
                } else {
                    Toast.makeText(getApplicationContext(), "Není oprávnění pro přístup k USB zařízení", Toast.LENGTH_LONG).show();
                    errSnd.start(); // chck OK
                    Log.d(TAG, "USB have not permission");
                    pass = false;
                }
            }
        }

        if (pass) {
            return true;
        } else {
            return false;
        }
    }

    public boolean isExternalStorageWritable() {
        String state = Environment.getExternalStorageState();

        if (Environment.MEDIA_MOUNTED.equals(state)) {
            return true;
        } else {
            return false;
        }
    }

    public void getReadoutType() {
        String selectedtext = (String) rb.getText();
        int radioButtonID = rg.getCheckedRadioButtonId();
        View radioButton = rg.findViewById(radioButtonID);
        int rbIndex = rg.indexOfChild(radioButton);

        switch (rbIndex) {
            case 0: // odecet jen registru
                readRegs = true;
                readLastMonth = false;
                readThisMonth = false;
                break;
            case 1: // odecet registru a LP za minuly mesic
                readRegs = true;
                readLastMonth = true;
                readThisMonth = false;
                break;
            case 2: // odecet registru a LP za tent mesic
                readRegs = true;
                readLastMonth = false;
                readThisMonth = true;
                break;
        }

        Log.d(TAG, "Index radiobuttonu: " + rbIndex + " - " + selectedtext);
        Log.d(TAG, "readRegs = " + readRegs);
        Log.d(TAG, "readLastMonth = " + readLastMonth);
        Log.d(TAG, "readThisMonth = " + readThisMonth);
    }

    private void tvSetContent(TextView tv, CharSequence text) {
        final TextView ftv = tv;
        final CharSequence ftext = text;

        runOnUiThread(new Runnable() {
            @Override public void run() {
                ftv.setText(ftext);
            }
        });
    }

    public void getSpeed() throws InterruptedException {
        // nacteni rychlostniho indexu z identifikacniho retezce / get device max speed
        Matcher speedIndex = communicationSpeedIndexPattern.matcher(tmpDataLine);
        Log.d(TAG, "getSpeed processing");

        if (speedIndex.find()) {
            readedSpeed = parseInt(speedIndex.group(3));
            Log.d(TAG, "Speed index " + readedSpeed);

            speed = baudrateList[readedSpeed];
            Log.d(TAG, "New speed will be " + speed + " Bd");
            tvSetContent(registryVal, "Komunikace na " + speed + " Bd");

            if (readThisMonth || readLastMonth) {
                askData = ACK + "0" + readedSpeed + "1\r\n";
                Log.d(TAG, "Profile ACK");
            } else {
                askData = ACK + "0" + readedSpeed + "0\r\n";
                Log.d(TAG, "Registry ACK");
            }

            Log.d(TAG, "Waiting 300 ms");
            Thread.sleep(300);
            Log.d(TAG, "Send to serial " + askData + "|" + toHex(askData));
            serialDevice.write(askData.getBytes());
            Log.d(TAG, "Waiting 300 ms");
            Thread.sleep(300);
            Log.d(TAG, "Serial line switching to " + speed + " Bd");
            serialDevice.setBaudRate(speed);

            Log.d(TAG, "Speed switched");

        } else {
            Log.d(TAG, "No speed index found");
        }
    }

    public void showValues() {
        // rozlozeni na nazev a hodnotu registru / show registry name and value
        Matcher regNamVal = regNameValuePattern.matcher(tmpDataLine);
        if (regNamVal.find()) {
            regName = regNamVal.group(1).toString();
            regValue = regNamVal.group(2).toString();
            tvSetContent(registryNam, regName);
            tvSetContent(registryVal, regValue);
            if (meterId == "") {
                // hledame cislo elektromeru / looking for meter ID
                Matcher metId = meterIdPattern.matcher(regName);
                if (metId.matches()) {
                    meterId = regValue;
                    Log.d(TAG, "meterId = " + meterId);
                }
            }
        } else {
            tvSetContent(registryVal, tmpDataLine);
            Log.d(TAG, "Pickup registry and value fail");
            tvSetContent(registryNam, "");
            tvSetContent(registryVal, "");
        }

    }

    public void chkDirectories() {
        // kontrola/vytvoreni adresaru pro vystup a konfiguraci / chck output dir
        outputDirOk = false;
        File dirDocuments = Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOCUMENTS);
        String dirDocumentsPath = dirDocuments.getPath();
        File dirAmr = new File(dirDocumentsPath+"/AMR");
        filepath = dirAmr.getAbsolutePath();

        if (!dirAmr.exists()) {
            // adresar pro vystup neexistuje, vytvorime ho
            successCreateDir = dirAmr.mkdir();
            if (successCreateDir) {
                Log.d(TAG,"Directory " + filepath + " created successfully");
            } else {
                Log.d(TAG,"Creating od directory " + filepath + " FAILED!");
            }
        } else {
            Log.d(TAG,"AMR directory OK");
            File dirReadouts = new File(filepath + "/Readouts");
            filepath = dirReadouts.getAbsolutePath();
            if (!dirReadouts.exists()) {
                successCreateDir = dirReadouts.mkdir();
                if (successCreateDir) {
                    outputDirOk = true;
                    Log.d(TAG,"Directory " + filepath + " created successfully");
                } else {
                    Log.d(TAG,"Creating od directory " + filepath + " FAILED!");
                }
            } else {
                outputDirOk = true;
                Log.d(TAG,"Readouts directory OK");
            }
        }
        Log.d(TAG,"Readouts output directory: " + filepath);
    }

    public void saveFile() {
        // ukladame na SD kartu / saving to SD
        try {
            tvSetContent(registryVal, "Ukládám data" + filename);
            Log.d(TAG, "Timer pred ukladanim: " + timeLeft);

            responseTimeCounter.cancel(); // zastavime casovac odezvy elektromeru / cancel timer
            /* THIS CANCEL DID NOT WORKING - WHY???????????????????????? */

            Log.d(TAG, "Timer po jeho preruseni: " + timeLeft);
            filename = meterId.trim() + ".rd";
            myExternalFile = new File(filepath, filename);
            Log.d(TAG,"Output stream start " + myExternalFile.getAbsolutePath());
            FileOutputStream fos = new FileOutputStream(myExternalFile);
            Log.d(TAG, "Start writting data");
            fos.write(buffer.getBytes());
            Log.d(TAG, "Closing file - saved to " + myExternalFile.getAbsolutePath());
            fos.close();
            tvSetContent(registryVal, "Uloženo do " + filename);
            Log.d(TAG, "Closing serial communication");
            connection.close();
            bdSnd.start();

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


        // ukladame do interniho uloziste / saving in to internal storage
        /*
        try {
            filename = meterId.trim() + ".rd";
            BufferedWriter bufferedWriter = new BufferedWriter(new FileWriter(new
                    File(getFilesDir()+File.separator+filename)));
            bufferedWriter.write(buffer);
            bufferedWriter.close();
            tvSetContent(registryVal, "Uloženo do " + filename);
            //setDefaultValues();
            bdSnd.start();
        } catch (IOException e) {
            e.printStackTrace();
        }
        */

    }

    public void processData() throws InterruptedException {
        // prochazeni prichozich dat po znacich
        for (dataLength = 0; dataLength < data.length(); dataLength++) {
            //Process char
            char c = data.charAt(dataLength);
            String charToString = c + "";
            Log.d(TAG, "R <- " + charToString + " hex:" + toHex(charToString));
            Matcher incomingOneChar = STXpat.matcher(charToString);
            if (incomingOneChar.matches()) {
                Log.d(TAG, "\n\nSTX found - dropping it\n\n");
                charToString = ""; // drop STX char
            }

            Matcher nextIncomingOneChar = ETXpat.matcher(charToString);
            if (nextIncomingOneChar.matches()) {
                Log.d(TAG, "\n\nETX found - dropping it and next data\n\n");
                charToString = ""; // drop ETX char a vsechno za tim
                isETX = true;
                etxIndex += 1;
                dataLength = data.length(); // ukonci cyklus driv
                registryReceived = true;
                responseTimeCounter.cancel();
                saveFile();
            }

            // je prichozi znak CR?
            Matcher incomingCharCR = CRpat.matcher(charToString);
            if (incomingCharCR.matches()) {
                //tvAppend(textView, "CR");
                isCR = true;
                //Log.d(TAG, "CR");
            } else {
                // je prichozi znak LF?
                Matcher incomingCharLF = LFpat.matcher(charToString);
                if (incomingCharLF.matches()) {
                    //tvAppend(textView, "LF");
                    isLF = true;
                    //Log.d(TAG, "LF");
                }
            }

            tmpDataLine += charToString; // pridame nacteny znak do radku

            // reset casovace na vstupu
            responseTimeCounter.cancel();
            responseTimeCounter.start();
            /* THIS TIMER RESET WORKING */

            if (isCR && isLF) {
                // je konec radku?
                CRLF = true;
            }

            if (CRLF) {
                // mame cely radek, zpracujeme jej / processing completed data row
                //Log.d(TAG, "CRLF");

                if (firstLine) {
                    // v prvnim radku je identifikace s indexem rychlosti
                    Log.d(TAG, "First line completed - " + tmpDataLine);
                    if (tmpDataLine.equals(handshake)) {
                        // test na echo
                        meterEchoes = true;
                        tmpDataLine = "";   // drop radku s echem
                        Log.d(TAG, "Meter send echo");
                    } else {
                        getSpeed();
                        firstLine = false;
                        Log.d(TAG, "First line operated");
                    }
                }

                if (meterEchoes && tmpDataLine.equals(askData)) {
                    // pokud elektromer vraci echo, drop data
                    tmpDataLine = "";
                }

                buffer += tmpDataLine;
                Log.d(TAG, "tmpDataLine: " + tmpDataLine);

                if (!tmpDataLine.equals("")) {
                    // pokud nejaka data jsou, zobraz je
                    showValues();
                }

                dataLine = "";  // vymazeme obsah radku
                CRLF = false;
                isCR = false;
                isLF = false;
                tmpDataLine = "";
            }
        }

    }

    UsbSerialInterface.UsbReadCallback mCallback = new UsbSerialInterface.UsbReadCallback() {
        //Defining a Callback which triggers whenever data is read.
        //@TargetApi(Build.VERSION_CODES.KITKAT)
        //@RequiresApi(api = Build.VERSION_CODES.KITKAT)
        @Override
        public void onReceivedData(byte[] arg0) {
            //String data = null;
            try {
                data = new String(arg0);
                //Log.d(TAG, "R <- " + data);
                Log.d(TAG, "Timer pri vstupu dat na USB: " + timeLeft);
                processData();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

    };

    private void startSerialConnection(UsbDevice device) {
        Log.i(TAG, "Ready to open USB device connection");
        connection = usbManager.openDevice(device);
        serialDevice = UsbSerialDevice.createUsbSerialDevice(device, connection);
        if (serialDevice != null) {
            if (serialDevice.open()) {
                serialDevice.setBaudRate(speed);
                serialDevice.setDataBits(UsbSerialInterface.DATA_BITS_7);
                serialDevice.setStopBits(UsbSerialInterface.STOP_BITS_1);
                serialDevice.setParity(UsbSerialInterface.PARITY_EVEN);
                serialDevice.setFlowControl(UsbSerialInterface.FLOW_CONTROL_OFF);
                Log.d(TAG, "mCallback Called");
                serialDevice.read(mCallback);
                Log.d(TAG, "Serial connection opened at " + speed + " Bd");
            } else {
                Log.d(TAG, "Cannot open serial connection");
                tvSetContent(registryVal, "Nefunguje COM port");
                Toast.makeText(getApplicationContext(), "Nefunguje COM port", Toast.LENGTH_LONG).show();
                infoSnd.start();
            }
        } else {
            Log.d(TAG, "Could not create USB Serial Device");
        }
    }

    public class MyTimer extends CountDownTimer {
        public  MyTimer(long millisInFuture, long countDownInterval) {
            super(millisInFuture, countDownInterval);
        }

        @Override
        public void onTick(long millisUntilFinished) {
            tvSetContent(registryNam, String.valueOf(millisUntilFinished));
        }

        @Override
        public void onFinish() {
            errSnd.start();
            tvSetContent(registryVal, "Elektroměr neodpovídá"); // device did not respond
            connection.close();
        }
    }

    public void doReadout(View view) {
        setDefaultValues();
        chkDirectories();
        int radioButtonId = rg.getCheckedRadioButtonId();
        rb = findViewById(radioButtonId);
        Log.d(TAG, "Do Readout pressed");

        if (!isExternalStorageWritable()) {
            Toast.makeText(getApplicationContext(), "Externí úložiště není k dispozici.", Toast.LENGTH_LONG).show();
            errSnd.start();
        }

        // pro zapis na SD kartu
        if (!outputDirOk) {
            Toast.makeText(getApplicationContext(), "Adresář pro výstup není k dispozici.", Toast.LENGTH_LONG).show();
            errSnd.start();
        }

        if (checkUSBdevice() && isExternalStorageWritable() && outputDirOk) {
            startSerialConnection(device);
            if (device != null) {
                //tvSetContent(registryVal, "Sériová komunikace spuštěna");
                Log.d(TAG, "Serial communication opened");
            } else {
                Log.d(TAG, "Serial communication opening FAIL");
            }
            if (rb == null) {
                Toast.makeText(getApplicationContext(), "Není vybrán typ odečtu!", Toast.LENGTH_LONG).show();
                Log.d(TAG, "No readout type selected");
                errSnd.start();
            } else {
                getReadoutType();
                tvSetContent(registryVal, "Posílam handshake");
                serialDevice.write(handshake.getBytes());
                Log.d(TAG, "Handshake sent " + toHex(handshake));
                responseTimeCounter.start();
                Log.d(TAG, "Response timer started");
            }
        } else {
            // nepripojene USB nedelame nic
            errSnd.start();
            Toast.makeText(getApplicationContext(), "Není připojena USB optická sonda." ,Toast.LENGTH_LONG).show();
            Log.d(TAG, "No USB device connected");
        }
    }
}

Solution

  • I found the reason, why a timer did not want to be cancelled. There is .cancel() and .start() called in wrong place. They are called in cycle where is input processed char by char and there is too many cancels and starts in short time and that probably confused the timer. Moving timer restart out of this cycle solved my problem.