public final class AIMSICDDbAdapter extends SQLiteOpenHelper {

    public static final int DATABASE_VERSION = 1; // Is this "pragma user_version;" ?

    public static final String TAG = "AICDL";
    public static final String mTAG = "DB: ";

    // TODO: This should be implemented as a SharedPreference...
    private static final Boolean MONO_DB_DUMP = true; // Also back-up DB with one monolithic dump file?
    private static final String DB_NAME = "aimsicd.db";

    private String[] mTables;
    private SQLiteDatabase mDb;
    private Context mContext;
    private SharedPreferences mPreferences;
    public static String mDatabasePath;
    public static String mExternalFilesDirPath;

    public AIMSICDDbAdapter(Context context) {
        super(context, DB_NAME, null, 1);
        mContext = context;
        mPreferences = PreferenceManager.getDefaultSharedPreferences(mContext);
        mDatabasePath = mContext.getDatabasePath(DB_NAME).getAbsolutePath();
        mExternalFilesDirPath = mContext.getExternalFilesDir(null) + File.separator;
        //e.g. /storage/emulated/0/Android/data/zz.aimsicd.lite/

        // Create a new blank DB then write pre-compiled DB in assets folder to blank DB.
        // This will throw error on first create because there is no DB to open and this is normal.
        createDataBase();

        //return writable database
        mDb = SQLiteDatabase.openDatabase(mDatabasePath, null, SQLiteDatabase.OPEN_READWRITE);

        // This will return the database as open so we don't need to use .open . Then when app
        // is exiting we use new AIMSICDDbAdapter(getApplicationContext()).close(); to close it
        this.getWritableDatabase();
        mTables = new String[]{

                // I am trying to keep in same order and aimsicd.sql script
                // Only backing up useful tables, uncomment if you want to backup
                DBTableColumnIds.DEFAULT_LOCATION_TABLE_NAME,   // defaultLocation:     Default MCC for each country
                //DBTableColumnIds.API_KEYS_TABLE_NAME,         // API_keys:            API keys for OpenCellID, MLS etc.
                //DBTableColumnIds.COUNTER_MEASURES_TABLE_NAME, // CounterMeasures:     Counter Measures thresholds and description
                //DBTableColumnIds.DBE_CAPABILITIES_TABLE_NAME, // DBe_capabilities:    External: MNO & BTS network capabilities
                DBTableColumnIds.DBE_IMPORT_TABLE_NAME,         // DBe_import:          External: BTS import table
                DBTableColumnIds.DBI_BTS_TABLE_NAME,            // DBi_bts:             Internal: (physical) BTS data
                DBTableColumnIds.DBI_MEASURE_TABLE_NAME,        // DBi_measure:         Internal: (volatile) network measurements
                //DBTableColumnIds.DETECTION_FLAGS_TABLE_NAME,  // DetectionFlags:      Detection Flag description, settings and scoring table
                DBTableColumnIds.EVENTLOG_TABLE_NAME,           //                      Detection and general EventLog (persistent)
                //DBTableColumnIds.SECTOR_TYPE_TABLE_NAME,      // SectorType:          BTS tower sector configuration (Many CID, same BTS)
                DBTableColumnIds.DETECTION_STRINGS_TABLE_NAME,  //                      Detection strings to will be picked up in logcat
                DBTableColumnIds.SMS_DATA_TABLE_NAME,           //                      Silent SMS details
        };
    }

    /**
     * Description:     Creates an empty SQLite Database file on the system and rewrites it with
     *                  our own pre-fabricated AIMSICD.db.
     *
     * NOTES:           This is a modified version to suit of needs of this guys great guide on
     *                  how to build a pre compiled db for android. Cheers Juan-Manuel Fluxà
     *                  See:
     *                  http://www.reigndesign.com/blog/using-your-own-sqlite-database-in-android-applications/
     *
     **/
    public boolean createDataBase() {
        if (!checkDataBase()) {
            // By calling this method, an empty database will be created into the default system path
            // of your application so that we can overwrite that database with our own database.
            this.getReadableDatabase();
            try {
                copyDataBase();
                Log.i(TAG, mTAG + "Database created");
                return true;
            } catch (IOException e) {
                Log.i(TAG, mTAG + "Error creating database: " + e);
                throw new Error("Error copying database", e);
            }

        }
        return false;
    }

    /**
     * Check if the database already exist to avoid re-copying the file each time you open the application.
     *
     * ToDo: NOTE:  This is a dumb check, as it currently only tries to open it.
     *              It may be other reasons why it can't be opened even if it already exists.
     *
     * @return true if it exists, false if it doesn't
     */
    private boolean checkDataBase() {

        SQLiteDatabase checkDB = null;
        try {
            Log.i(TAG, mTAG + "Checking if DB exists...");
            checkDB = SQLiteDatabase.openDatabase(mDatabasePath, null, SQLiteDatabase.OPEN_READONLY);
        } catch (SQLiteException e) {
            Log.i(TAG, mTAG + "SQL Exception! Database can\'t be opened: " + e);
            //Log.i(TAG, mTAG + "Database not yet created: " + e);
        }

        if (checkDB != null) {
            checkDB.close();
            Log.i(TAG, mTAG + "OK (found)");
            return true;
        }
        Log.i(TAG, mTAG + "Database probably not yet created.");
        return false;
    }

    /**
     * Description:     Copies your database from your local assets-folder to the just created
     *                  empty database in the system folder, from where it can be accessed and handled.
     *                  This is done by transferring bytestream.
     */
    private void copyDataBase() throws IOException {
        // Open your local DB as the input stream
        InputStream myInput = mContext.getAssets().open(DB_NAME);
        // Open the empty DB as the output stream
        OutputStream myOutput = new FileOutputStream(mDatabasePath);

        // Transfer bytes from the input file to the output file
        byte[] buffer = new byte[1024];
        int length;
        while ((length = myInput.read(buffer)) > 0) {
            myOutput.write(buffer, 0, length);
        }
        // Close the streams
        myOutput.flush();
        myOutput.close();
        myInput.close();
    }

    public AIMSICDDbAdapter open() throws SQLException {
        mDb = this.getWritableDatabase();
        return this;
    }

    public void close() {
        mDb.close();
    }

    @Override
    public void onCreate(SQLiteDatabase sqLiteDatabase) {
        // To use foreign keys in SQLite we use:
        //db.execSQL("PRAGMA foreign_keys = ON;");
        // But this is already created inside SQL script!!
    }

    @Override
    public void onUpgrade(SQLiteDatabase sqLiteDatabase, int i, int i1) {
    }


    // ====================================================================
    //      Populate the DB tables  (INSERT)
    // ====================================================================

    /**
     * Description:     This is used in the AIMSICD framework Tests to delete cells.
     *                  see: ../src/androidTest/java/com.SecUpwN.test/.
     *
     * Issues:          TODO: See comments below!
     *
     * @param cellId    This method deletes a cell with CID from CELL_TABLE
     * @return result   of deleting that CID
     */
    public int deleteCell(int cellId) {
        Log.i(TAG, mTAG + "Deleted CID: " + cellId);
        // TODO Instead we need to delete this cell from DBi_measure, since:
        // we are using foreign_key enforced DB, that doesn't allow you to
        // remove Dbi_bts without corresponding DBi_measures that uses them.
        // Rewrite this query!
        return mDb.delete("DBi_bts", "CID=" + cellId, null);
    }

    /* ====================================================================
     *      mDb.query statements (get)        SELECT
     * ====================================================================
     *  IMPORTANT!  The SQL queries as presented here are not in the same order
     *              as in the DB tables themselves, therefore it may be hard
     *              to easily match the various items when using the Cursors
     *              as shown below.
     *
     *              For example, in the opencellid (DBe_import) table, the items are ordered as:
     *                 Lat,Lng,Mcc,Mnc,CellID,...
     *              whereas in the getOpenCellIDData() cursor, they are arranged as:
     *                 CellID,Lac,Mcc,Mnc,Lat,Lng,AvgSigStr,Samples
     *
     *              Thus when used in MapFragment.java at loadEntries() and
     *              loadOpenCellIDMarkers(), the index used there is completely different
     *              than what could be expected.
     *
     *  ISSUES:     [ ] To avoid un-necessary future code obscurity, we should rearrange
     *                  all Cursor queries to reflect the actual DB table order.
     *                  Todo: This is a tedious job...
     *                  TODO: Also AVOID using this type of Query!!
     *
     * ====================================================================
     */


    /**
     * Returns Cell Information (DBi_bts) database contents this returns BTSs that we logged and is
     * called from MapFragment.java to display cells on map<
     *
     * Because RAT is stored in measurement table. JOIN the two table together for a complete
     * picture.
     *
     * TODO: 2016-02-28 Consider if this is the best way to get the info. Database schema refactor?</p>
     *
     * @see #returnDBiBtsWithRAT()
     */
    public Cursor getCellData() {
        return returnDBiBtsWithRAT();
    }

    /**
     * Returns Cell Information for contribution to the OpenCellID project
     * by listing all rows from the DBi_measure table where isSubmitted is not 1.
     */
    public Cursor getOCIDSubmitData() {

        // This is used (prepareOpenCellUploadData) when uploading data to OCID
        // TODO: Use something like this instead... VVV Need testing may need ...,RAT FROM DBi_measure ..
        // @EVA created a new function getRatFromDBimeasure(CellId) to get RAT with CID
        //      this can be used when creating the upload data to add the RAT to the CSV file

        // IMPORTANT: Note the order of the items (to match CSV)!
        //TODO @EVA this is working but its returning 2 of each rows any ideas??
        String query = "SELECT DISTINCT MCC,MNC,LAC,CID,gpsd_lon,gpsd_lat,rx_signal,time,gpsd_accu FROM DBi_measure, DBi_bts WHERE isSubmitted <> 1 ORDER BY time;";
        //String query = "SELECT MCC,MNC,LAC,CID,gpsd_lon,gpsd_lat,rx_signal,time,gpsd_accu FROM DBi_bts WHERE isSubmitted <> 1"; // OLD query for reference
        return mDb.rawQuery(query, null);
    }


    // ====================================================================
    //      Various DB operations
    // ====================================================================

    /**
     *  Description:    This take a "Cell" bundle (from API) as input and uses its CID to check
     *                  in the DBi_measure (?) if there is already an associated LAC. It then
     *                  compares the API LAC to that of the DBi_Measure LAC.
     *
     *  Issues:     [ ] We should make all detections outside of AIMSICDDbAdapter.java in a
     *                  separate module as described in the diagram in GH issue #215.
     *                  https://github.com/SecUpwN/Android-IMSI-Catcher-Detector/issues/215
     *                  where it is referred to as "Detection Module" (DET)...
     *
     *              [ ] Seem we're querying too much, when we only need items: 1,3,4,8,11
     *                  (Try to avoid over query to improve performance.)
     *
     *              [ ]  V V V V
     *
     *              This is using the LAC found by API and comparing to LAC found from a previous
     *              measurement in the "DBi_measure". This is NOT depending on "DBe_import".
     *              This works for now...but we probably should consider populating "DBi_measure"
     *              as soon as the API gets a new LAC. Then the detection can be done by SQL,
     *              and by just comparing last 2 LAC entries for same CID.
     *
     * @return false if LAC is not OK (Cell's LAC differs from Cell's LAC previously stored value in DB)
     */
    public boolean checkLAC(Cell cell) {
        String query = String.format(Locale.US, "SELECT * FROM DBi_bts WHERE CID = %d", cell.getCID());  //CID

        Cursor bts_cursor = mDb.rawQuery(query, null);

        while (bts_cursor.moveToNext()) {
            // 1=LAC, 8=Accuracy, 11=Time
            if (cell.getLAC() != bts_cursor.getInt(bts_cursor.getColumnIndex("LAC"))) {
                Log.i(TAG, mTAG + "ALERT: Changing LAC on CID: " + cell.getCID()
                        + " LAC(API): " + cell.getLAC()
                        + " LAC(DBi): " + bts_cursor.getInt(bts_cursor.getColumnIndex("LAC")));

                bts_cursor.close();
                return false;
            } else {
                Log.i(TAG, mTAG + "LAC checked - no change on CID:" + cell.getCID()
                        + " LAC(API): " + cell.getLAC()
                        + " LAC(DBi): " + bts_cursor.getInt(bts_cursor.getColumnIndex("LAC")));
            }
        }
        bts_cursor.close();
        return true;
    }


    /**
     * UPDATE DBi_measure to indicate if OpenCellID DB contribution has been made
     */
    public void ocidProcessed() {
        ContentValues ocidValues = new ContentValues();
        ocidValues.put("isSubmitted", 1); // isSubmitted
        mDb.update("DBi_measure", ocidValues, "isSubmitted<>?", new String[]{"1"}); // isSubmitted
    }


    /**
     * This returns all BTS in the DBe_import by current sim card network rather
     * than returning other bts from different networks and slowing down map view
     */
    public Cursor returnOcidBtsByNetwork(int mcc, int mnc) {
        String query = String.format(Locale.US,
                "SELECT * FROM DBe_import WHERE MCC = %d AND MNC = %d", mcc, mnc);
        return mDb.rawQuery(query, null);
    }

    public double[] getDefaultLocation(int mcc) {
        String query = String.format(Locale.US,
                "SELECT lat, lon FROM defaultlocation WHERE MCC = %d", mcc);

        double[] loc = new double[2];
        Cursor cursor = mDb.rawQuery(query, null);

        if (cursor != null && cursor.moveToFirst()) {
            loc[0] = Double.parseDouble(cursor.getString(cursor.getColumnIndex("lat")));
            loc[1] = Double.parseDouble(cursor.getString(cursor.getColumnIndex("lon")));
        } else {
            loc[0] = 0.0;
            loc[1] = 0.0;
        }
        if (cursor != null) {
            cursor.close();
        }
        return loc;
    }

    /**
     * Description:     Remove all but the last row, unless its CID is invalid...
     *
     * Dependencies:    CellTracker.java:  ( dbHelper.cleanseCellTable(); )
     *
     * Issues:          [ ] This will not work if: PRAGMA foreign_key=ON, then we need to delete
     *                      the corresponding DBi_measure entries before / as well.
     *
     *                  [ ] TODO: It is UNCLEAR why this is needed!! It's probably an artifact of old DB tables??
     *                      TODO: Consider changing or removing!
     *
     * Notes:           Do we need to clean LAC as well? (Test with airplane-mode or roaming)
     *                  - probably not since a APM would give both LAC and CID as "-1".
     *
     */
    public void cleanseCellTable() {
        // This removes all but the last row in the "DBi_bts" table
        //"DELETE FROM DBi_bts WHERE _id NOT IN (SELECT MAX(_id) FROM DBi_bts) GROUP BY CID"
        mDb.execSQL("DELETE FROM DBi_bts WHERE _id NOT IN (SELECT MAX(_id) FROM DBi_bts)");

        // TODO: MOVE this, as this is only executed once!!
        // This removes erroneous BTS entries due to API giving you CID/LAC of "-1" or MAX_INT,
        // when either roaming, in airplane mode or during crappy hand-overs.
        String query2 = String.format(Locale.US, "DELETE FROM DBi_bts WHERE CID = %d OR CID = -1",
                Integer.MAX_VALUE);
        mDb.execSQL(query2);
    }

    /**
     * Description:     Prepares the CSV file used to upload new data to the OCID server.
     *
     * Issues:          TODO:
     *                  [ ] Add "act" in upload data for the DBi_measure:RAT
     *                  [ ] function getOCIDSubmitData() is not fully working ==> DB join not yet implemented
     *                  [ ] skip (or change) progress bar, since CSV write is too fast to be seen.
     *
     * Note:            Q: Where is this file?
     *                  A: It is wherever your device has mounted its SDCard.
     *                     For example, in:  /data/media/0/AIMSICD/OpenCellID
     *
     *                 OCID CSV upload format:
     *                  "cellid"        = CID (in UMTS long format)
     *                  "measured_at"   = time
     *                  "rating"        = gpsd_accu
     *                  "act"           = RAT (TEXT):
     *                                     1xRTT, CDMA, eHRPD, IS95A, IS95B, EVDO_0, EVDO_A, EVDO_B,
     *                                     UMTS, HSPA+, HSDPA, HSUPA, HSPA, LTE, EDGE, GPRS, GSM
     *
     */
    public boolean prepareOpenCellUploadData() {
        boolean result;

        File dir = new File(mExternalFilesDirPath + "OpenCellID/");
        if (!dir.exists()) {
            result = dir.mkdirs();
            if (!result) {
                return false;
            }
        }
        File file = new File(dir, "aimsicd-ocid-data.csv");

        try {
            // Get data not yet submitted:
            Cursor c = getOCIDSubmitData();
            // Check if we have something to upload:
            if (c.getCount() > 0) {
                if (!file.exists()) {
                    result = file.createNewFile();
                    if (!result) {
                        c.close();
                        return false;
                    }

                    // OCID CSV upload format and items
                    // mcc,mnc,lac,cellid,lon,lat,signal,measured_at,rating,speed,direction,act,ta,psc,tac,pci,sid,nid,bid
                    CSVWriter csvWrite = new CSVWriter(new FileWriter(file));
                    // TODO: Add "act"
                    csvWrite.writeNext("mcc,mnc,lac,cellid,lon,lat,signal,measured_at,rating");

                    int size = c.getCount();
                   Log.d(TAG, mTAG + "OCID UPLOAD: row count = " + size);

                    while (c.moveToNext()) {
                        csvWrite.writeNext(
                                String.valueOf(c.getInt(c.getColumnIndex("MCC"))),
                                String.valueOf(c.getInt(c.getColumnIndex("MNC"))),
                                String.valueOf(c.getInt(c.getColumnIndex("LAC"))),
                                String.valueOf(c.getInt(c.getColumnIndex("CID"))),
                                c.getString(c.getColumnIndex("gpsd_lon")),
                                c.getString(c.getColumnIndex("gpsd_lat")),
                                c.getString(c.getColumnIndex("rx_signal")),
                                c.getString(c.getColumnIndex("time")),
                                //c.getString(c.getColumnIndex("RAT")),                     // OCID: "act" TODO
                                String.valueOf(c.getInt(c.getColumnIndex("gpsd_accu"))));
                    }
                    csvWrite.close();
                    c.close();
                }
                return true;
            }
            c.close();
            return false;
        } catch (Exception e) {
           Log.e(TAG, mTAG + "prepareOpenCellUploadData(): Error creating OpenCellID Upload Data: ", e);
            return false;
        }
    }


    /**
     *  Description:    Parses the downloaded CSV from OpenCellID and uses it to populate
     *                  "DBe_import" table.
     *
     *
     *  Dependency:     RequestTask.java :: onPostExecute()
     *                  insertDBeImport()
     *  Issues:
     *
     *          [ ]     Progress bar is not shown or is the operation too quick to be seen?
     *          [ ]     Why are we only populating 8 items out of 19?
     *                  From downloaded OCID CSV file:  (19 items)
     *
     *  NOTES:
     *
     *          a)  We do not include "rej_cause" in backups. set to 0 as default
     *          b)  Unfortunately there are 2 important missing items in the OCID CSV file:
     *                  - "time_first"
     *                  - "time_last"
     *          c)  In addition the OCID data often contain unexplained negative values for one or both of:
     *                  - "samples"
     *                  - "range"
     *
     *          d) The difference between "Cellid" and "cid", is that "cellid" is the "Long CID",
     *             consisting of RNC and a multiplier:
     *                      Long CID = 65536 * RNC + CID
     *             See FAQ.
     *
     * ========================================================================
     * For details on available OpenCellID API DB values, see:
     * http://wiki.opencellid.org/wiki/API
     * http://wiki.opencellid.org/wiki/FAQ#Long_CellID_vs._short_Cell_ID
     * ========================================================================
     *   # head -2 opencellid.csv
     *   lat,lon,mcc,mnc,lac,cellid,averageSignalStrength,range,samples,changeable,radio,rnc,cid,psc,tac,pci,sid,nid,bid
     *
     *   0 lat                      TEXT
     *   1 lon                      TEXT
     *   2 mcc                      INTEGER
     *   3 mnc                      INTEGER
     *   4 lac                      INTEGER
     *   5 cellid                   INTEGER     (Long CID) = 65536 * RNC + CID
     *   6 averageSignalStrength    INTEGER     (rx_power)
     *   7 range                    INTEGER     (accu)
     *   8 samples                  INTEGER
     *   9 changeable               INTEGER     (isGPSexact)
     *   10 radio                   TEXT        (RAT)
     *   11 rnc                     INTEGER
     *   12 cid                     INTEGER     CID (Short)= "Long CID" mod 65536
     *   13 psc                     INTEGER
     *   --------- vvv  See OCID API  vvv ---------
     *   14 tac                     -
     *   15 pci                     -
     *   16 sid                     -
     *   17 nid                     -
     *   18 bid                     -
     *
     *   54.63376,25.160243,246,3,20,1294,0,-1,1,1,GSM,,,,,,,,
     * ========================================================================
     */
    public boolean populateDBeImport() {
        // This was not finding the file on a Samsung S5
        // String fileName = Environment.getExternalStorageDirectory()+ "/AIMSICD/OpenCellID/opencellid.csv";
        String fileName = mContext.getExternalFilesDir(null) + File.separator + "OpenCellID/opencellid.csv";
        File file = new File(fileName);

        try {
            if (file.exists()) {

                CSVReader csvReader = new CSVReader(new FileReader(file));
                List<String[]> csvCellID = new ArrayList<>();
                String next[];

                while ((next = csvReader.readNext()) != null) {
                    csvCellID.add(next);
                }

                if (!csvCellID.isEmpty()) {
                    int lines = csvCellID.size();
                    Log.i(TAG, mTAG + "UpdateOpenCellID: OCID CSV size (lines): " + lines);

                    // TODO: WHAT IS THIS DOING?? (Why is it needed?)
                    // This counts how many CIDs we have in DBe_import
                    Cursor lCursor = mDb.rawQuery("SELECT CID, COUNT(CID) FROM DBe_import GROUP BY CID", null);
                    SparseArray<Boolean> lPresentCellID = new SparseArray<>();
                    if (lCursor.getCount() > 0) {
                        while (lCursor.moveToNext()) {
                            lPresentCellID.put(lCursor.getInt(0), true);
                        }
                    }
                    lCursor.close();


                    int rowCounter;
                    for (rowCounter = 1; rowCounter < lines; rowCounter++) {
                        // TODO: IS this needed!???
                        // Inserted into the table only unique values CID
                        // without opening additional redundant cursor before each insert.
                        if (lPresentCellID.get(Integer.parseInt(csvCellID.get(rowCounter)[5]), false)) {
                            continue;
                        }
                        // Insert details into OpenCellID Database using:  insertDBeImport()
                        // Beware of negative values of "range" and "samples"!!
                        String lat = csvCellID.get(rowCounter)[0],          //TEXT
                                lon = csvCellID.get(rowCounter)[1],          //TEXT
                                mcc = csvCellID.get(rowCounter)[2],          //int
                                mnc = csvCellID.get(rowCounter)[3],          //int
                                lac = csvCellID.get(rowCounter)[4],          //int
                                cellid = csvCellID.get(rowCounter)[5],       //int   long CID [>65535]
                                range = csvCellID.get(rowCounter)[6],        //int
                                avg_sig = csvCellID.get(rowCounter)[7],      //int
                                samples = csvCellID.get(rowCounter)[8],      //int
                                change = csvCellID.get(rowCounter)[9],       //int
                                radio = csvCellID.get(rowCounter)[10],       //TEXT
//                                rnc = csvCellID.get(rowCounter)[11],         //int
//                                cid = csvCellID.get(rowCounter)[12],         //int   short CID [<65536]
                                psc = csvCellID.get(rowCounter)[13];         //int

                        // Some OCID data may not contain PSC so we indicate this with an out-of-range PSC value (666) > 512?.
                        // Should be -1 but hey people already imported so we're stuck with this...
                        int iPsc = 666;
                        if (psc != null && !psc.isEmpty()) {
                            iPsc = Integer.parseInt(psc);
                        }

                        //Reverse order 1 = 0 & 0 = 1
                        // what if ichange is 4? ~ agilob
                        int ichange = Integer.parseInt(change);
                        ichange = (ichange == 0 ? 1 : 0);

                        insertDBeImport(
                                "OCID",                     // DBsource
                                radio,                      // RAT
                                Integer.parseInt(mcc),      // MCC
                                Integer.parseInt(mnc),      // MNC
                                Integer.parseInt(lac),      // LAC
                                Integer.parseInt(cellid),   // CID (cellid) ?
                                iPsc,                       // psc
                                lat,                        // gps_lat
                                lon,                        // gps_lon
                                ichange,                    // isGPSexact
                                Integer.parseInt(avg_sig),  // avg_signal [dBm]
                                Integer.parseInt(range),    // avg_range [m]
                                Integer.parseInt(samples),  // samples
                                "n/a",                      // time_first  (not in OCID)
                                "n/a",                      // time_last   (not in OCID)
                                0                           // TODO: rej_cause , set default 0
                        );
                    }
                   Log.d(TAG, mTAG + "PopulateDBeImport(): inserted " + rowCounter + " cells.");
                }
            } else {
               Log.e(TAG, mTAG + "Opencellid.csv file does not exist!");
            }
            return true;
        } catch (Exception e) {
           Log.e(TAG, mTAG + "Error parsing OpenCellID data: " + e.getMessage());
            return false;
        } finally {
            try {
                Thread.sleep(1000); // wait 1 second to allow user to see progress bar.
            } catch (InterruptedException ex) {
                Thread.currentThread().interrupt();
            }
        }
    }

    //=============================================================================================
    // Database Backup and Restore
    //=============================================================================================

    /**
     *  Description:    Restores the database tables from a previously Exported CSV files.
     *                  One CSV file per table with the name:  "aimsicd-<table_name>.csv"
     *
     *  Issues:         [ ]
     *
     *  Notes:      1) Restoring the DB can be done from a monolithic SQLite3 DB by (check!):
     *                  # sqlite3 aimsicd.db <aimsicd.dump
     */
    public boolean restoreDB() {
        try {

            for (String table : mTables) {

                File file = new File(mExternalFilesDirPath + "aimsicd-" + table + ".csv");
                if (file.exists()) {
                    List<String[]> records = new ArrayList<>();
                    String next[];
                    CSVReader csvReader = new CSVReader(new FileReader(file));
                    while ((next = csvReader.readNext()) != null) {
                        records.add(next);
                    }

                    if (!records.isEmpty()) {
                        int lines = records.size();
                        for (int i = 1; i < lines; i++) {

                            switch (table) {
                                case "defaultlocation":
                                    try {
                                        insertDefaultLocation(
                                                records.get(i)[1],       // country
                                                Integer.parseInt(records.get(i)[2]), // MCC
                                                records.get(i)[3],       // lat
                                                records.get(i)[4]        // lon
                                        );
                                    } catch (Exception ee) {
                                       Log.e(TAG, mTAG + "RestoreDB: Error in insertDefaultLocation()", ee);
                                    }
                                    break;

                                case "API_keys":
                                    insertApiKeys(
                                            records.get(i)[1],           //name
                                            records.get(i)[2],           //type
                                            records.get(i)[3],           //key
                                            records.get(i)[4],           //time_add
                                            records.get(i)[5]            //time_exp
                                    );
                                    break;

                                case "CounterMeasures":
                                    insertCounterMeasures(
                                            records.get(i)[1],                      //name
                                            records.get(i)[2],                      //description
                                            Integer.parseInt(records.get(i)[3]),    //thresh
                                            Double.parseDouble(records.get(i)[4])   //thfine
                                    );
                                    break;

                                case "DBe_capabilities":
                                    insertDBeCapabilities(
                                            records.get(i)[1],           //MCC
                                            records.get(i)[2],           //MNC
                                            records.get(i)[3],           //LAC
                                            records.get(i)[4],           //op_name
                                            records.get(i)[5],           //band_plan
                                            records.get(i)[6]            //__EXPAND___
                                    );

                                    break;

                                case "DBe_import":
                                    try {
                                        insertDBeImport(
                                                records.get(i)[1],                      // DBsource
                                                records.get(i)[2],                      // RAT
                                                Integer.parseInt(records.get(i)[3]),    // MCC
                                                Integer.parseInt(records.get(i)[4]),    // MNC
                                                Integer.parseInt(records.get(i)[5]),    // LAC
                                                Integer.parseInt(records.get(i)[6]),    // CID
                                                Integer.parseInt(records.get(i)[7]),    // PSC ??
                                                records.get(i)[8],                      // gps_lat
                                                records.get(i)[9],                      // gps_lon
                                                Integer.parseInt(records.get(i)[10]),   // isGPSexact
                                                Integer.parseInt(records.get(i)[11]),   // avg_range
                                                Integer.parseInt(records.get(i)[12]),   // avg_signal
                                                Integer.parseInt(records.get(i)[13]),   // samples
                                                records.get(i)[14],                     // time_first
                                                records.get(i)[15],                     // time_last
                                                0 //Integer.parseInt(records.get(i)[16])  // TODO: rej_cause
                                        );
                                    } catch (Exception ee) {
                                       Log.e(TAG, mTAG + "RestoreDB: Error in insertDBeImport()", ee);
                                    }
                                    break;

                                case "DBi_bts":
                                    try {
                                        insertBTS(
                                                Integer.parseInt(records.get(i)[1]),    // MCC
                                                Integer.parseInt(records.get(i)[2]),    // MNC
                                                Integer.parseInt(records.get(i)[3]),    // LAC
                                                Integer.parseInt(records.get(i)[4]),    // CID
                                                Integer.parseInt(records.get(i)[5]),    // PSC
                                                Integer.parseInt(records.get(i)[6]),    // T3212
                                                Integer.parseInt(records.get(i)[7]),    // A5x
                                                Integer.parseInt(records.get(i)[8]),    // ST_id
                                                records.get(i)[9],                      // time_first
                                                records.get(i)[10],                     // time_last
                                                Double.parseDouble(records.get(i)[11]), // lat
                                                Double.parseDouble(records.get(i)[12])  // lon
                                        );
                                    } catch (Exception ee) {
                                       Log.e(TAG, mTAG + "RestoreDB: Error in insertBTS()", ee);
                                    }
                                    break;

                                case "DBi_measure":
                                    try {
                                        insertDbiMeasure(
                                                Integer.parseInt(records.get(i)[1]),    // bts_id
                                                records.get(i)[2],                      // nc_list
                                                records.get(i)[3],                      // time
                                                records.get(i)[4],                      // gpsd_lat
                                                records.get(i)[5],                      // gpsd_lon
                                                Integer.parseInt(records.get(i)[6]),    // gpsd_accu
                                                records.get(i)[7],                      // gpse_lat TODO: remove!
                                                records.get(i)[8],                      // gpse_lon TODO: remove!
                                                records.get(i)[9],                      // bb_power
                                                records.get(i)[10],                     // bb_rf_temp
                                                records.get(i)[11],                     // tx_power
                                                records.get(i)[12],                     // rx_signal
                                                records.get(i)[13],                     // rx_stype
                                                records.get(i)[14],                     // RAT
                                                records.get(i)[15],                     // BCCH
                                                records.get(i)[16],                     // TMSI
                                                Integer.parseInt(records.get(i)[17]),   // TA
                                                Integer.parseInt(records.get(i)[18]),   // PD
                                                Integer.parseInt(records.get(i)[19]),   // BER
                                                records.get(i)[20],                     // AvgEcNo
                                                Integer.parseInt(records.get(i)[21]),   // isSubmitted
                                                Integer.parseInt(records.get(i)[22])    // isNeighbour
                                                //records.get(i)[23].toString()         // TODO: con_state
                                        );
                                    } catch (Exception ee) {
                                       Log.e(TAG, mTAG + "RestoreDB: Error in insertDbiMeasure()", ee);
                                    }
                                    break;

                                case "DetectionFlags":
                                    insertDetectionFlags(
                                            Integer.parseInt(records.get(i)[1]),    //code
                                            records.get(i)[2],                      //name
                                            records.get(i)[3],                      //description
                                            Integer.parseInt(records.get(i)[4]),    //p1
                                            Integer.parseInt(records.get(i)[5]),    //p2
                                            Integer.parseInt(records.get(i)[6]),    //p3
                                            Double.parseDouble(records.get(i)[7]),  //p1_fine
                                            Double.parseDouble(records.get(i)[8]),  //p2_fine
                                            Double.parseDouble(records.get(i)[9]),  //p3_fine
                                            records.get(i)[10],                     //app_text
                                            records.get(i)[11],                     //func_use
                                            Integer.parseInt(records.get(i)[12]),   //istatus
                                            Integer.parseInt(records.get(i)[13])    //CM_id

                                    );
                                    break;

                                case "EventLog":
                                    insertEventLog(
                                            records.get(i)[1],                      //time
                                            Integer.parseInt(records.get(i)[2]),    //LAC
                                            Integer.parseInt(records.get(i)[3]),    //CID
                                            Integer.parseInt(records.get(i)[4]),    //PSC
                                            records.get(i)[5],                      //gpsd_lat
                                            records.get(i)[6],                      //gpsd_lon
                                            Integer.parseInt(records.get(i)[7]),    //gpsd_accu
                                            Integer.parseInt(records.get(i)[8]),    //DF_id
                                            records.get(i)[9]                       //DF_desc
                                    );
                                    break;

                                case "SectorType":
                                    insertSectorType(records.get(i)[1]);
                                    break;

                                case "DetectionStrings":
                                    insertDetectionStrings(
                                            records.get(i)[1],
                                            records.get(i)[2]);
                                    break;

                                case "SmsData":
                                    insertSmsData(
                                            records.get(i)[1],                      //time
                                            records.get(i)[2],                      //number
                                            records.get(i)[3],                      //smsc
                                            records.get(i)[4],                      //message
                                            records.get(i)[5],                      //type
                                            records.get(i)[6],                      //class
                                            Integer.parseInt(records.get(i)[7]),    //lac
                                            Integer.parseInt(records.get(i)[8]),    //cid
                                            records.get(i)[9],                      //rat
                                            Double.parseDouble(records.get(i)[10]), //gps_lat
                                            Double.parseDouble(records.get(i)[11]), //gps_lon
                                            Integer.parseInt(records.get(i)[12])    //isRoaming
                                    );
                                    break;
                            }
                        }
                    }
                }
            }
            Log.i(TAG, mTAG + "RestoreDB() Finished");
            return true;
        } catch (Exception e) {
           Log.e(TAG, mTAG + "RestoreDB() Error", e);
            return false;
        }

    }

    /**
     *  Description:    Dumps the internal aimsicd.db to a file called "aimsicd_dump.db".
     *
     *  Requires:       root + SQLite3 binary
     *
     *  Where?          Used in backupDB() and depend on the  MONO_DB_DUMP  boolean.
     *
     *  Notes:  1) We probably also need to test if we have the sqlite3 binary. (See Busybox checking code.)
     *
     *          2) Apparently pipes doesn't work from Java... No idea why, as they appear to work
     *              in the AtCommandFragment.java... for checking for /dev/ files.
     *
     *          3) We can use either ".dump" or ".backup", but "dump" makes an SQL file,
     *             whereas "backup" make a binary SQLite DB.
     *
     *            a) # sqlite3 aimsicd.db '.dump' | gzip -c >aimsicd.dump.gz
     *            b) # sqlite3 aimsicd.db '.dump' >aimsicd.dump
     *            c) # sqlite3 aimsicd.db '.backup aimsicd.back'
     *
     *    execString = "/system/xbin/sqlite3 " + dir + "aimsicd.db '.dump' | gzip -c >" + file;
     *    execString = "/system/xbin/sqlite3 " + aimdir + "aimsicd.db '.dump' >" + file;
     *    execString = "/system/xbin/sqlite3 " + aimdir + "aimsicd.db '.backup " +file + "'";
     *
     *          4) To re-import use:
     *             # zcat aimsicd.dump.gz | sqlite3 aimsicd.db
     *
     */
    private void dumpDB() {

        File dumpdir = new File(mExternalFilesDirPath);
        //if (!dir.exists()) { dir.mkdirs(); }
        File file = new File(dumpdir, "aimsicd_dump.db");
        //Bad coding? (What is AOS standard?)
        //Context.getFilesDir().getPath("com.SecUpwN.AIMSICD/databases"); ????
        String aimdir = new File(mDatabasePath).getParent();
        String execString = "/system/xbin/sqlite3 " + aimdir + "aimsicd.db '.backup " + file + "'";

        try {
            Log.i(TAG, mTAG + "DumpDB() Attempting to dump DB to: " + file + "\nUsing: \"" + execString + "\"\n");
            CMDProcessor.runSuCommand(execString); // We need SU for this...
        } catch (Exception e) {
           Log.e(TAG, mTAG + "DumpDB() Failed to export DB dump file: ", e);
        }
        Log.i(TAG, mTAG + "DumpDB() Dumped internal database to: " + aimdir + file);
    }


    /**
     *  Description:    Backup the database tables to CSV files (or monolithic dump file)
     *
     *  Depends:        On the Boolean MONO_DB_DUMP to indicate if we want to try to
     *                  dump a monolithic DB using the rooted shell + sqlite3 binary
     *                  method above.
     *
     * @return boolean indicating backup outcome
     */
    public boolean backupDB() {
        try {
            for (String table : mTables) {
                backup(table);
            }
            if (MONO_DB_DUMP) {
                dumpDB();
            }
            return true;
        } catch (Exception ioe) {
           Log.e(TAG, mTAG + "BackupDB() Error: ", ioe);
            return false;
        }
    }

    /**
     * Exports the database tables to CSV files
     *
     * @param tableName String representing table name to export
     */
    private void backup(String tableName) {
        Log.i(TAG, mTAG + "Database Backup: " + DB_NAME);

        File dir = new File(mExternalFilesDirPath);
        if (!dir.exists()) {
            if (!dir.mkdirs()) {
               Log.e(TAG, mTAG + "Backup(): Cannot create directory structure to " + dir.getAbsolutePath());
            }
        }  // We should probably add some more error handling here.
        File file = new File(dir, "aimsicd-" + tableName + ".csv");

        try {
            Log.i(TAG, mTAG + "Backup(): Backup file was created? " + file.createNewFile());
            CSVWriter csvWrite = new CSVWriter(new FileWriter(file));
           Log.d(TAG, mTAG + "DB backup() tableName: " + tableName);

            Cursor c = mDb.rawQuery("SELECT * FROM " + tableName, new String[0]);

            csvWrite.writeNext(c.getColumnNames());
            String[] rowData = new String[c.getColumnCount()];
            int size = c.getColumnCount();

            while (c.moveToNext()) {
                for (int i = 0; i < size; i++) {
                    rowData[i] = c.getString(i);
                }
                csvWrite.writeNext(rowData);
            }
            csvWrite.close();
            c.close();

        } catch (Exception e) {
           Log.e(TAG, mTAG + "Error exporting table: " + tableName, e);
        }
        Log.i(TAG, mTAG + "Backup(): Successfully exported DB table to: " + file);
    }


    // ====================================================================
    //      Cleanup and filtering of DB tables
    // ====================================================================

    /**
     *  What:           This is the DBe_import data consistency check
     *
     *  Description:    This method checks each imported BTS data for consistency
     *                  and correctness according to general 3GPP LAC/CID/RAT rules
     *                  and according to the app settings:
     *
     *                  tf_settings         (currently hard-coded)
     *                  min_gps_precision   (currently hard-coded)
     *
     *                  So there are really two steps in this procedure:
     *                  a) Remove bad BTSs from DBe_import
     *                  b) Mark unsafe BTSs in the DBe_import with "rej_cause" value.
     *
     *                  See:    #253    http://tinyurl.com/lybrfxb
     *                          #203    http://tinyurl.com/mzgjdcz
     *
     *                  We filter:
     *
     *  Used:
     *                  RequestTask.java :: onPostExecute()
     *
     *  Issues:
     *
     *          [ ] Look into "long CID" and "Short CID" for UMTS/LTE...
     *              http://wiki.opencellid.org/wiki/FAQ
     *
     *              The formula for the long cell ID is as follows:
     *                  Long CID = 65536 * RNC + CID
     *
     *              If you have the Long CID, you can get RNC and CID in the following way:
     *                  RNC = Long CID / 65536 (integer division)
     *                  CID = Long CID mod 65536 (modulo operation)
     *
     *
     *  2017-04-21  E:V:A   These VVVV are probably outdated. Trying to repair this file!!
     *
     *  TODO:   (1) Implement some kind of counter, to count how many cells was removed.
     *  TODO:   (2) Better description of what was removed.
     *  TODO:   (3) Give a return value for success/failure
     *  TODO:   (4) Implement the "rej_cause" check and UPDATE table.
     *
     * Notes:   (a) By using rawQuery, we could count the number of items affected.
     *                  mDb.rawQuery(sqlq, null);
     *              But rawQuery() is not executed until there is an associated Cursor operation!
     *
     *          (b)
     *
     *
     *  ChangeLog:
     *          2015-08-01  E:V:A           Updated Queries to reflect new DB structure
     *
     */
    public void checkDBe() {
        // We hard-code these for now, but should be in the settings eventually
//        int tf_settings = 30;         // [days] Minimum acceptable number of days since "time_first" seen.
        int min_gps_precision = 50;   // [m]    Minimum acceptable GPS accuracy in meters.

        String sqlQuery;                // SQL Query string

        //=============================================================
        //===  DELETE bad cells from BTS data
        //=============================================================

       Log.d(TAG, mTAG + "CheckDBe() Attempting to delete bad import data from DBe_import table...");

        // =========== samples ===========
        sqlQuery = "DELETE FROM DBe_import WHERE samples < 1";
        mDb.execSQL(sqlQuery);

        // =========== avg_range ===========
        // TODO: OCID data marks many good BTS with a negative range so we can't use this yet.
        // TODO: Also delete cells where the avg_range is way too large, say > 2000 meter
        //sqlQuery = "DELETE FROM DBe_import WHERE avg_range < 1 OR avg_range > 2000";
        //mDb.rawQuery(sqlQuery, null);

        // =========== LAC ===========
        sqlQuery = "DELETE FROM DBe_import WHERE LAC < 1";
        mDb.execSQL(sqlQuery);

        // We should delete cells with CDMA (4) LAC not in [1,65534] but we can simplify this to:
        // Delete ANY cells with a LAC not in [1,65534]
        sqlQuery = "DELETE FROM DBe_import WHERE LAC > 65534";
        mDb.execSQL(sqlQuery);

        // Delete cells with GSM/UMTS/LTE (1/2/3/13 ??) (or all others?) LAC not in [1,65533]
        //sqlQuery = "DELETE FROM DBe_import WHERE LAC > 65533 AND RAT != 'CDMA'";
        //mDb.rawQuery(sqlQuery, null);

        // =========== CID ===========
        sqlQuery = "DELETE FROM DBe_import WHERE CID < 1";
        mDb.execSQL(sqlQuery);

        // We should delete cells with UMTS/LTE (3,13) CID not in [1,268435455] (0xFFF FFFF) but
        // we can simplify this to:
        // Delete ANY cells with a CID not in [1,268435455]
        sqlQuery = "DELETE FROM DBe_import WHERE CID > 268435455";
        mDb.execSQL(sqlQuery);

        // Delete cells with GSM/CDMA (1-3,4) CID not in [1,65534]
        sqlQuery = "DELETE FROM DBe_import WHERE CID > 65534 AND (RAT='GSM' OR RAT='CDMA')";
        mDb.execSQL(sqlQuery);

        // SELECT count(*) from DBe_import;
        Log.i(TAG, mTAG + "CheckDBe() Deleted BTS entries from DBe_import table with bad LAC/CID...");

        //=============================================================
        //===  UPDATE "rej_cause" in BTS data (DBe_import)
        //=============================================================

        // =========== isGPSexact ===========
        // Increase rej_cause, when:  the GPS position of the BTS is not exact:
        // NOTE:  In OCID: "changeable"=1 ==> isGPSexact=0
        sqlQuery = "UPDATE DBe_import SET rej_cause = rej_cause + 3 WHERE isGPSexact=0";
        mDb.execSQL(sqlQuery);

        // =========== avg_range ===========
        // Increase rej_cause, when:  the average range is < a minimum GPS precision
        sqlQuery = "UPDATE DBe_import SET rej_cause = rej_cause + 3 WHERE avg_range < " + min_gps_precision;
        mDb.execSQL(sqlQuery);

        // =========== time_first ===========
        // Increase rej_cause, when:  the time first seen is less than a number of days.
        // TODO: We need to convert tf_settings to seconds since epoch/unix time...
        //      int tf_settings = current_time[s] - (3600 * 24 * tf_settings) ???
        //sqlQuery = "UPDATE DBe_import SET rej_cause = rej_cause + 1 WHERE time_first < " + tf_settings;
        //mDb.execSQL(sqlQuery);

    }


    // =======================================================================================
    //      Signal Strengths Table
    // =======================================================================================

    /**
     * Description:     Remove too old signal strengths entries from DBi_measure table,
     *                  given a particular LAC,CID,PSC,RAT (or all?).
     *
     *                  TODO: Why do we need this at all?
     *
     * Note:            WARNING!    Do not remove based upon time only, as that would remove
     *                              all other measurement entries as well.
     *
     * Issues:          TODO:   timestamp in DBi_measure is a String,
     *                          but the one from SignalStrengthTracker is a long
     */

    public void cleanseCellStrengthTables(long maxTime) {
       Log.d(TAG, mTAG + "CleanseCellStrengthTables(): Cleaning DBi_measure WHERE time < " + maxTime);

        //TODO Change "time" to INTEGER in DB   -- currently not working
        String query = String.format(Locale.US,
                "DELETE FROM DBi_measure WHERE time < %d",
                maxTime);
        mDb.execSQL(query);
    }

    // TODO: Where is this used?
    public int getAverageSignalStrength(int cellID) {
        String query = String.format(Locale.US,
                // todo: "SELECT AVG(rx_signal) FROM DBi_measure,DBi_bts WHERE CID= %d",  // E:V:A
                "SELECT avg(rx_signal) FROM DBi_measure WHERE bts_id= %d",
                cellID);
        Cursor c = mDb.rawQuery(query, null);
        c.moveToFirst();
        int lAnswer = c.getInt(0);
        c.close();
        return lAnswer;
    }

    //=============================================================================================
    // ********************** ALL NEW FUNCTIONS ADDED AFTER THIS LINE *****************************
    //=============================================================================================

    // ====================================================================
    //      Get all detection strings
    // ====================================================================
    public List<AdvanceUserItems> getDetectionStrings() {

        Cursor cursor = mDb.rawQuery("SELECT * FROM DetectionStrings", null);

        List<AdvanceUserItems> detectionStrings = new ArrayList<>();

        if (cursor.getCount() > 0) {
            while (cursor.moveToNext()) {
                AdvanceUserItems setItems = new AdvanceUserItems();
                setItems.setDetection_string(cursor.getString(cursor.getColumnIndex("det_str")));
                setItems.setDetection_type(cursor.getString(cursor.getColumnIndex("sms_type")));
                detectionStrings.add(setItems);

            }
        } else {
            AdvanceUserItems setItems = new AdvanceUserItems();
            setItems.setDetection_string("No data");
            setItems.setDetection_type("No data");
            detectionStrings.add(setItems);
        }
        cursor.close();
        return detectionStrings;
    }

    public boolean deleteDetectedSms(long deleteme) {
        String mTAG = "AIMSICDDbAdapter";

        try {
            mDb.delete("SmsData", "_id=" + deleteme, null);
            return true;
        } catch (Exception ee) {
            Log.i(TAG, mTAG + mTAG + ": Deleting SMS data failed", ee);
        }
        return false;
    }

    public boolean deleteDetectionString(String deleteme) {

        try {
            mDb.delete("DetectionStrings", "det_str='" + deleteme + "'", null);
            return true;
        } catch (Exception ee) {
            Log.i(TAG, mTAG + "Deleting detection string failed", ee);
        }
        return false;

    }

    //====================================================================
    //      Insert new detection strings into database
    //====================================================================

    /**
     * Description:     When inserting strings it has to be in the format:
     *                  "i am a type 0 string". These strings can be found in main logcat.
     *
     * Issues:          [ ] Need to change time data type to INTEGER in DB
     *
     * NOTES:
     *
     *      TYPE0 SILENTVOICE FLASH <--- These have to be in CAPS
     *      ContentValues newconvalues = new ContentValues();
     *      newconvalues.put(DETECTION_STRINGS_LOGCAT_STRING, "your string goes here");
     *      newconvalues.put(DETECTION_STRINGS_SMS_TYPE, "TYPE0");
     *      database.insert(DETECTION_STRINGS_TABLE_NAME,,null,newconvalues);
     *
     * ChangeLog:
     *
     */
    public boolean insertNewDetectionString(ContentValues newString) {

        // First check that string not in DB
        String check4String = String.format(Locale.US,
                "SELECT * FROM DetectionStrings WHERE det_str = \"%s\"",
                newString.get("det_str").toString());

        Cursor cursor = mDb.rawQuery(check4String, null);
        final boolean exists = cursor.getCount() > 0;
        cursor.close();

        if (exists) {
            Log.i(TAG, mTAG + "Detection String already in Database");
        } else {
            try {
                mDb.insert("DetectionStrings", null, newString);
                Log.i(TAG, mTAG + "New detection string added.");
                return true;
            } catch (Exception ee) {
                Log.i(TAG, mTAG + "Adding detection string Failed! ", ee);
            }
        }
        return false;
    }

    public CapturedSmsData storeCapturedSms(CapturedSmsData smsdata) {

        ContentValues values = new ContentValues();

        values.put("number", smsdata.getSenderNumber());
        values.put("message", smsdata.getSenderMsg());
        values.put("time", smsdata.getSmsTimestamp());
        values.put("type", smsdata.getSmsType());
        values.put("lac", smsdata.getCurrent_lac());
        values.put("cid", smsdata.getCurrent_cid());
        values.put("rat", smsdata.getCurrent_nettype());
        values.put("isRoaming", smsdata.getCurrent_roam_status());
        values.put("gps_lat", smsdata.getCurrent_gps_lat());
        values.put("gps_lon", smsdata.getCurrent_gps_lon());

        long insertId = mDb.insert("SmsData", null, values);
        smsdata.setId(insertId);
        return smsdata;
    }

    // Boolean Check if a give timestamp already exists in SmsData table
    public boolean isTimeStampInDB(String timestamp) {
        String check4timestamp = String.format(Locale.US,
                "SELECT time FROM SmsData WHERE time = \"%s\"", timestamp);
        Cursor cursor = mDb.rawQuery(check4timestamp, null);
        final boolean exists = cursor.getCount() > 0;
        cursor.close();
        return exists;
    }

    //====================================================================
    //      RETURN DATABASE CURSORS START HERE
    //
    // @banjaxbanjo:    (1) What do you mean with this actually?
    //                  (2) Where are these used?
    //                  (3) Are you making cursors?
    //
    // TODO:  Because of many changes to DB design, please keep these
    // TODO:  "Returned Columns" comments updated.
    //====================================================================

    /*
        Returned Columns:
        "_id"       INTEGER PRIMARY KEY,
        "country"   TEXT,
        "MCC"       INTEGER,
        "lat"       TEXT,
        "lon"       TEXT
        returns Default Mcc Locations
     */
    public Cursor returnDefaultLocation() {
        return mDb.rawQuery("SELECT * FROM defaultlocation", null);
    }

    /**
     * Returns DBe_import contents
     *
     * Used in:
     * DbViewerFragment.java
     * MapFragment.java
     *
     * Returned Columns:
     * "_id"        INTEGER PRIMARY KEY AUTOINCREMENT,
     * "DBsource"   TEXT NOT NULL,
     * "RAT"        TEXT,
     * "MCC"        INTEGER,
     * "MNC"        INTEGER,
     * "LAC"        INTEGER,
     * "CID"        INTEGER,
     * "PSC"        INTEGER,
     * "gps_lat"    REAL,
     * "gps_lon"    REAL,
     * "isGPSexact" INTEGER,
     * "avg_range"  INTEGER,
     * "avg_signal" INTEGER,
     * "samples"    INTEGER,
     * "time_first" INTEGER,
     * "time_last"  INTEGER,
     * "rej_cause"  INTEGER
     */
    public Cursor returnDBeImport() {
        return mDb.rawQuery("SELECT * FROM DBe_import", null);
    }

    // TODO: THESE ARE OUTDATED!! Please see design and update
    /*
    Returned Columns:
        "_id"           INTEGER PRIMARY KEY AUTOINCREMENT,
        "MCC"           INTEGER NOT NULL,
        "MNC"           INTEGER NOT NULL,
        "LAC"           INTEGER NOT NULL,
        "CID"           INTEGER NOT NULL,
        "PSC"           INTEGER,
        "T3212"         INTEGER,
        "A5x"           INTEGER,
        "ST_id"         INTEGER,
        "time_first"    INTEGER,
        "time_last"     INTEGER,
        "LAT"           REAL NOT NULL,  -- NULL should be okay, as GPS is not always available
        "LON"           REAL NOT NULL
    */
    public Cursor returnDBiBts() {
        return mDb.rawQuery("SELECT * FROM DBi_bts", null);
    }
    public Cursor returnDBiBtsWithRAT() {
        // Basically the same as above, but with RAT from a joined, related table
        return mDb.rawQuery("SELECT DBi_bts.*, DBi_measure.RAT FROM DBi_bts JOIN DBi_measure ON DBi_measure.bts_id = DBi_bts.CID", null);
    }

    // TODO: THESE ARE OUTDATED!! Please see design and update
    /*
    Returned Columns:
        "_id"           INTEGER PRIMARY KEY AUTOINCREMENT,
        "bts_id"        INTEGER NOT NULL,
        "nc_list"       TEXT,
        "time"          TEXT NOT NULL,
        "gpsd_lat"      TEXT NOT NULL,
        "gpsd_lon"      TEXT NOT NULL,
        "gpsd_accuracy" INTEGER,
        "gpse_lat"      TEXT,
        "gpse_lon"      TEXT,
        "bb_power"      TEXT,
        "bb_rf_temp"    TEXT,
        "tx_power"      TEXT,
        "rx_signal"     TEXT,
        "rx_stype"      TEXT,
        "RAT"           TEXT,
        "BCCH"          TEXT,
        "TMSI"          TEXT,
        "TA"            INTEGER,
        "PD"            INTEGER,
        "BER"           INTEGER,
        "AvgEcNo"       TEXT,
        "isSubmitted"   INTEGER DEFAULT 0,
        "isNeighbour"   INTEGER DEFAULT 0,
*/
    public Cursor returnDBiMeasure() {
        // This is special since this table is linked to DBi_bts by a foreign key,
        // so if you're not able to get LAC/CID, it's probably due to the FOREIGN KEY Pragma being OFF,
        // then try with:
        // "SELECT * FROM DBi_measure, DBi_bts WHERE DBi_measure.bts_id=DBi_bts.CID;"
        // NOTE:  TODO: bts_id should not be populated, then replace with "DBi_measure.bts_id=DBi_bts._id;"
        return mDb.rawQuery("SELECT * FROM DBi_measure", null);
    }

    /*
    Returned Columns
        "_id"           INTEGER PRIMARY KEY,
        "code"          INTEGER,
        "name"          TEXT,
        "description"   TEXT,
        "p1"            INTEGER,
        "p2"            INTEGER,
        "p3"            INTEGER,
        "p1_fine"       REAL,
        "p2_fine"       REAL,
        "p3_fine"       REAL,
        "app_text"      TEXT,
        "func_use"      TEXT,
        "istatus"       INTEGER,
        "CM_id"         INTEGER
     */
    public Cursor returnDetectionFlags() {
        return mDb.rawQuery("SELECT * FROM DetectionFlags", null);
    }

    // TODO: THESE ARE OUTDATED!! Please see design and update

    /**
     * Returned Columns:
     * "_id"            INTEGER PRIMARY KEY AUTOINCREMENT,
     * "time"           TEXT NOT NULL,
     * "LAC"            INTEGER NOT NULL,
     * "CID"            INTEGER NOT NULL,
     * "PSC"            INTEGER,
     * "gpsd_lat"       TEXT,--Should this be double?
     * "gpsd_lon"       TEXT,--Should this be double?
     * "gpsd_accu"      INTEGER,
     * "DF_id"          INTEGER,
     * "DF_description" TEXT,
     */
    public Cursor returnEventLogData() {
        return mDb.rawQuery("SELECT * FROM EventLog", null);
    }

    /**
     * Returned Columns:
     * "_id"            INTEGER PRIMARY KEY AUTOINCREMENT,
     * "detection_str"  TEXT,
     * "sms_type"       TEXT--(WapPush MWI TYPE0 etc..)
     */
    public Cursor returnDetectionStrings() {
        return mDb.rawQuery("SELECT * FROM DetectionStrings", null);
    }

    // TODO: THESE ARE OUTDATED!! Please see design and update
    /*
    Returned Columns:
    "_id"       INTEGER PRIMARY KEY AUTOINCREMENT,
    "time"      TEXT,
    "number"    TEXT,
    "smsc"      TEXT,
    "message"   TEXT,
    "type"      TEXT,   -- WapPush MWI TYPE0 etc..)
    "class"     TEXT,   --(CLASS 0 etc...)
    "lac"       INTEGER,
    "cid"       INTEGER,
    "rat"       TEXT,
    "gps_lat"   REAL,
    "gps_lon"   REAL,
    "isRoaming" INTEGER
 */
    public Cursor returnSmsData() {
        return mDb.rawQuery("SELECT * FROM SmsData", null);
    }

    //----END OF RETURN DATABASE CURSORS------//

    //====================================================================
    //      START OF INSERT FUNCTIONS
    //====================================================================

    // TODO: Remove this, it's confusing and doesn't seem necessary!
    public void insertDefaultLocation(String country, int mcc, String lat, String lon) {

        ContentValues def_location = new ContentValues();
        def_location.put("country", country);
        def_location.put("MCC", mcc);
        def_location.put("lat", lat);
        def_location.put("lon", lon);

        // Check that the country and MCC not known in the DefaultLocation DB to avoid duplicates
        // TODO: @banjaxbanjo:  Please clarify its use:
        //                      Why would there be duplicates?
        //                      All countries are pre-loaded??
        String query = String.format(Locale.US,
                "SELECT * FROM defaultlocation WHERE country = \"%s\" AND MCC = %d ",
                country, mcc);

        Cursor cursor = mDb.rawQuery(query, null);
        if (cursor.getCount() <= 0) {
            // <= 0 means country is not in database yet
            mDb.insert("defaultlocation", null, def_location);
        }
        cursor.close();
    }

    public boolean insertApiKeys(String name,
                                 String type,
                                 String key,
                                 String time_add,
                                 String time_exp) {

        ContentValues ApiKeys = new ContentValues();
        ApiKeys.put("name", name);
        ApiKeys.put("type", type);
        ApiKeys.put("key", key);
        ApiKeys.put("time_add", time_add);
        ApiKeys.put("time_exp", time_exp);

        String query = String.format(Locale.US, "SELECT * FROM API_keys WHERE key = \"%s\"", key);
        Cursor cursor = mDb.rawQuery(query, null);

        // Only insert a new key if the key not already in DB
        if (cursor.getCount() <= 0) {
            mDb.insert("API_keys", null, ApiKeys);
            cursor.close();
            return true;
        }
        cursor.close();
        return false;
    }

    public void insertCounterMeasures(String name, String description,
                                      int thresh, double thfine) {

        ContentValues counterMeasures = new ContentValues();
        counterMeasures.put("name", name);
        counterMeasures.put("description", description);
        counterMeasures.put("thresh", thresh);
        counterMeasures.put("thfine", thfine);

        mDb.insert("CounterMeasures", null, counterMeasures);
    }

    /**
     * "MCC"        TEXT,
     * "MNC"        TEXT,
     * "LAC"        TEXT,
     * "op_name"    TEXT,
     * "band_plan"  TEXT,
     * "__EXPAND__" TEXT
     */
    public void insertDBeCapabilities(String mcc, String mnc, String lac, String op_name,
                                      String bandPlan, String __EXPAND__) {

        ContentValues dbeCapabilities = new ContentValues();

        dbeCapabilities.put("MCC", mcc);
        dbeCapabilities.put("MNC", mnc);
        dbeCapabilities.put("LAC", lac);
        dbeCapabilities.put("op_name", op_name);
        dbeCapabilities.put("band_plan", bandPlan);
        dbeCapabilities.put("__EXPAND__", __EXPAND__);

        mDb.insert("DBe_capabilities", null, dbeCapabilities);
    }

    /**
     * This method is used to insert and populate the downloaded or previously
     * backed up OCID details into the DBe_import database table.
     *
     * It also prevents adding multiple entries of the same cell-id, when OCID
     * downloads are repeated.
     */
    public void insertDBeImport(String db_src, String rat, int mcc, int mnc, int lac,
                                int cid, int psc, String lat, String lon, int isGpsExact,
                                int avg_range, int avg_signal, int samples, String time_first,
                                String time_last, int rej_cause) {


        ContentValues dbeImport = new ContentValues();

        dbeImport.put("DBsource", db_src);
        dbeImport.put("RAT", rat);
        dbeImport.put("MCC", mcc);
        dbeImport.put("MNC", mnc);
        dbeImport.put("LAC", lac);
        dbeImport.put("CID", cid);
        dbeImport.put("PSC", psc);
        dbeImport.put("gps_lat", lat);
        dbeImport.put("gps_lon", lon);
        dbeImport.put("isGPSexact", isGpsExact);
        dbeImport.put("avg_range", avg_range);
        dbeImport.put("avg_signal", avg_signal);
        dbeImport.put("samples", samples);
        dbeImport.put("time_first", time_first);
        dbeImport.put("time_last", time_last);
        dbeImport.put("rej_cause", rej_cause);

        // Check that the LAC/CID is not already in the DBe_import (to avoid adding duplicate cells)
        String query = String.format(Locale.US,
                "SELECT LAC,CID FROM DBe_import WHERE LAC = %d AND CID = %d ",
                lac, cid);
        Cursor cursor = mDb.rawQuery(query, null);
        if (cursor.getCount() <= 0) { // <= 0 means cell is not in database yet
            mDb.insert("DBe_import", null, dbeImport);
        }
        cursor.close();
    }

    /**
     * Created this because we don't need to insert all the data in this table
     * since we don't yet have items like TMSI etc.
     */
    public void insertBTS(Cell cell) {

        // If LAC and CID are not already in DBi_bts, then add them.
        if (!cellInDbiBts(cell.getLAC(), cell.getCID())) {
            ContentValues values = new ContentValues();

            values.put("MCC", cell.getMCC());
            values.put("MNC", cell.getMNC());
            values.put("LAC", cell.getLAC());
            values.put("CID", cell.getCID());
            values.put("PSC", cell.getPSC());

            // TODO: setting these to 0 because if empty causing error in DB Restore
            values.put("T3212", 0);  // TODO
            values.put("A5x", 0);    // TODO
            values.put("ST_id", 0);  // TODO

            values.put("time_first", MiscUtils.getCurrentTimeStamp());
            values.put("time_last", MiscUtils.getCurrentTimeStamp());

            values.put("gps_lat", cell.getLat());  // TODO NO! These should be exact GPS from DBe_import or by manual addition!
            values.put("gps_lon", cell.getLon());  // TODO NO! These should be exact GPS from DBe_import or by manual addition!

            mDb.insert("DBi_bts", null, values);

            Log.i(TAG, mTAG + "DBi_bts was populated.");

        } else {
            // If cell is already in the DB, update it to last time seen and
            // update its GPS coordinates, if not 0.0
            ContentValues values = new ContentValues();
            values.put("time_last", MiscUtils.getCurrentTimeStamp());

            // TODO NO! These should be exact GPS from DBe_import or by manual addition!
            // Only update if GPS coordinates are good
            if (Double.doubleToRawLongBits(cell.getLat()) != 0
                    && Double.doubleToRawLongBits(cell.getLat()) != 0
                    && Double.doubleToRawLongBits(cell.getLon()) != 0
                    && Double.doubleToRawLongBits(cell.getLon()) != 0) {
                values.put("gps_lat", cell.getLat());
                values.put("gps_lon", cell.getLon());
            }

            // This is the native update equivalent to:
            // "UPDATE Dbi_bts time_last=...,gps_lat=..., gps_lon=... WHERE CID=..."
            // update (String table, ContentValues values, String whereClause, String[] whereArgs)
            mDb.update("DBi_bts", values, "CID=?", new String[]{Integer.toString(cell.getCID())});

            Log.i(TAG, mTAG + "DBi_bts updated: CID=" + cell.getCID() + " LAC=" + cell.getLAC());
        }

        // TODO: This doesn't make sense, if it's in DBi_bts it IS part of DBi_measure!
        // Checking to see if CID (now bts_id) is already in DBi_measure, if not add it.
        if (!cellInDbiMeasure(cell.getCID())) {
            ContentValues dbiMeasure = new ContentValues();

            dbiMeasure.put("bts_id", cell.getCID());                    // TODO: No!! Comment this out!
            dbiMeasure.put("nc_list", "no_data");                       // TODO: Better with "n/a" or "0", where are we getting this?
            dbiMeasure.put("time", MiscUtils.getCurrentTimeStamp());

            String slat = String.valueOf(cell.getLat());
            String slon = String.valueOf(cell.getLon());
            if (slat.isEmpty()) {
                slat = "0.0";
            }
            if (slon.isEmpty()) {
                slat = "0.0";
            }
            dbiMeasure.put("gpsd_lat", slat);
            dbiMeasure.put("gpsd_lon", slon);
            dbiMeasure.put("gpsd_accu", cell.getAccuracy());

            dbiMeasure.put("bb_power", "0");                            //TODO: This is not yet available, setting to "0"
            dbiMeasure.put("tx_power", "0");                             //TODO putting 0 here as we don't have this value yet
            dbiMeasure.put("rx_signal", String.valueOf(cell.getDBM())); //TODO putting cell.getDBM() here so we have some signal for OCID upload.
            dbiMeasure.put("RAT", String.valueOf(cell.getNetType()));
            dbiMeasure.put("TA", cell.getTimingAdvance());              //TODO does this actually get timing advance?
            dbiMeasure.put("BER", 0);                                    //TODO setting 0 because we don't have data yet.
            dbiMeasure.put("isSubmitted", 0);
            dbiMeasure.put("isNeighbour", 0);

            mDb.insert("DBi_measure", null, dbiMeasure);
            Log.i(TAG, mTAG + "DBi_measure inserted bts_id=" + cell.getCID());  // TODO: NO!!

        } else {
            // Updating DBi_measure tables if already exists.
            //TODO commented out items are because we don't have this data yet or it doesn't need updating
            ContentValues dbiMeasure = new ContentValues();
            //dbiMeasure.put("nc_list", nc_list);
            //dbiMeasure.put("time", MiscUtils.getCurrentTimeStamp());

            if (Double.doubleToRawLongBits(cell.getLat()) != 0
                    && Double.doubleToRawLongBits(cell.getLon()) != 0) {
                dbiMeasure.put("gpsd_lat", cell.getLat());
                dbiMeasure.put("gpsd_lon", cell.getLon());
            }
            if (Double.doubleToRawLongBits(cell.getAccuracy()) != 0
                    && cell.getAccuracy() > 0) {
                dbiMeasure.put("gpsd_accu", cell.getAccuracy());
            }

            //if(cell.getDBM() > 0) { //TODO: Wrong, bb_power is not DBM!
            //    dbiMeasure.put("bb_power", String.valueOf(cell.getDBM()));
            //}

            //dbiMeasure.put("bb_rf_temp",bb_rf_temp);

            //TODO set correct value for tx_power and rx_signal and un comment when working again
            //  if(cell.getRssi() >0) {
            //    dbiMeasure.put("tx_power", String.valueOf(cell.getRssi()));
            //    dbiMeasure.put("rx_signal",String.valueOf(cell.getRssi()));
            //}
            if (cell.getDBM() > 0) {
                dbiMeasure.put("rx_signal", String.valueOf(cell.getDBM())); // [dBm]
            }

            //dbiMeasure.put("rx_stype",rx_stype);
            //dbiMeasure.put("RAT", String.valueOf(cell.getNetType()));
            //dbiMeasure.put("BCCH",BCCH);
            //dbiMeasure.put("TMSI",TMSI);
            if (cell.getTimingAdvance() > 0) {
                dbiMeasure.put("TA", cell.getTimingAdvance());  // Only available on API >16 on LTE
            }

            //dbiMeasure.put("PD",PD);                          // TODO: LTE: I need to check this...
            //dbiMeasure.put("BER",0);                          // getGsmBitErrorRate() for API 17+
            // See: http://developer.android.com/reference/android/telephony/SignalStrength.html

            //dbiMeasure.put("AvgEcNo",AvgEcNo);                // TODO: I need to check this...

            mDb.update("DBi_measure", dbiMeasure, "bts_id=?", new String[]{Integer.toString(cell.getCID())});
            Log.i(TAG, mTAG + "DBi_measure updated bts_id=" + cell.getCID());

        }

    }


    /**
     * Inserts (API?) Cell Details into Database (DBi_bts)
     * Used be restoreDB()
     */
    // TODO: THESE ARE OUTDATED!! Please see design and update
    public void insertBTS(int mcc, int mnc, int lac, int cid, int psc, int t3212, int a5x,
                          int st_id, String time_first, String time_last, double lat, double lon) {

        if (cid != -1) {
            // Populate Content Values for INSERT or UPDATE
            ContentValues btsValues = new ContentValues();
            btsValues.put("MCC", mcc);
            btsValues.put("MNC", mnc);
            btsValues.put("LAC", lac);
            btsValues.put("CID", cid);
            btsValues.put("PSC", psc);
            btsValues.put("T3212", t3212);
            btsValues.put("A5x", a5x);
            btsValues.put("ST_id", st_id);
            btsValues.put("time_first", time_first);
            btsValues.put("time_last", time_last);
            btsValues.put("gps_lat", lat);
            btsValues.put("gps_lon", lon);

            // Only insert new cell if its not in DBi_bts
            if (!cellInDbiBts(lac, cid)) {
                mDb.insert("DBi_bts", null, btsValues);
            } else {
                // =======================================================================
                // TODO: EVA do I need to update an already known cell?
                // Good question! The DBi_bts is meant to be physical (non-volatile) and
                // stay the same, but I now see that PSC, T3212, A5x etc., might change,
                // under certain and special circumstances.
                // However, if they do, we should log this as an (suspicious?) event in EventLog
                // TODO: Addendum:
                // We could consider adding already known cells, if ANY of their parameters (not time)
                // has changed. At the moment this is only PSC, since we don't have the others...
                // =======================================================================
                mDb.update("DBi_bts", btsValues, "CID=?", new String[]{Integer.toString(cid)});
                Log.i(TAG, mTAG + "Warning: Physical cell data in DBi_bts has changed! CID=" + cid);
            }
        }

    }

    /**
     * Inserts a measurement into the DBi_measure and DBi_bts tables
     */
    public void insertDbiMeasure(int bts_id,
                                 String nc_list,
                                 String time,
                                 String gpsd_lat,
                                 String gpsd_lon,
                                 int gpsd_accuracy,
                                 String gpse_lat,
                                 String gpse_lon,
                                 String bb_power,
                                 String bb_rf_temp,
                                 String tx_power,
                                 String rx_signal,
                                 String rx_stype,
                                 String rat,
                                 String BCCH,
                                 String TMSI,
                                 int TA,
                                 int PD,
                                 int BER,
                                 String AvgEcNo,
                                 int isSubmitted,
                                 int isNeighbour) {

        // Check if a bts_id is already stored in DBi_measure. Only adds new cell if false
        // @banjaxbanjo:  No this is wrong... We should always add new measurements
        //
        if (cellInDbiMeasure(bts_id)) {
            ContentValues dbiMeasure = new ContentValues();

            dbiMeasure.put("bts_id", bts_id);    // TODO: comment this out!

            dbiMeasure.put("nc_list", nc_list);
            dbiMeasure.put("time", time);
            dbiMeasure.put("gpsd_lat", gpsd_lat);
            dbiMeasure.put("gpsd_lon", gpsd_lon);
            dbiMeasure.put("gpsd_accu", gpsd_accuracy);
            dbiMeasure.put("gpse_lat", gpse_lat);
            dbiMeasure.put("gpse_lon", gpse_lon);
            dbiMeasure.put("bb_power", bb_power);
            dbiMeasure.put("bb_rf_temp", bb_rf_temp);
            dbiMeasure.put("tx_power", tx_power);
            dbiMeasure.put("rx_signal", rx_signal);
            dbiMeasure.put("rx_stype", rx_stype);
            dbiMeasure.put("RAT", rat);
            dbiMeasure.put("BCCH", BCCH);
            dbiMeasure.put("TMSI", TMSI);
            dbiMeasure.put("TA", TA);
            dbiMeasure.put("PD", PD);
            dbiMeasure.put("BER", BER);
            dbiMeasure.put("AvgEcNo", AvgEcNo);
            dbiMeasure.put("isSubmitted", isSubmitted);
            dbiMeasure.put("isNeighbour", isNeighbour);

            mDb.insert("DBi_measure", null, dbiMeasure);
            // TODO: try with:
            // mDb.insert("DBi_measure, DBi_bts", null, dbiMeasure); // but you'll need the DBi_bts data then as well...
        }

    }

    /**
     * This inserts Detection Flag data that is used to fine tune the various
     * available detection mechanisms. (See Detection List in issue #230)
     *
     * There's also a CounterMeasure ID to link to possible future counter measures.
     */
    public void insertDetectionFlags(int code,
                                     String name,
                                     String description,
                                     int p1, int p2, int p3,
                                     double p1_fine, double p2_fine, double p3_fine,
                                     String app_text,
                                     String func_use,
                                     int istatus,
                                     int CM_id
    ) {

        ContentValues detectionFlags = new ContentValues();
        detectionFlags.put("code", code);
        detectionFlags.put("name", name);
        detectionFlags.put("description", description);
        detectionFlags.put("p1", p1);
        detectionFlags.put("p2", p2);
        detectionFlags.put("p3", p3);
        detectionFlags.put("p1_fine", p1_fine);
        detectionFlags.put("p2_fine", p2_fine);
        detectionFlags.put("p3_fine", p3_fine);
        detectionFlags.put("app_text", app_text);
        detectionFlags.put("func_use", func_use);
        detectionFlags.put("istatus", istatus);
        detectionFlags.put("CM_id", CM_id);

        mDb.insert("DetectionFlags", null, detectionFlags);
    }

    /**
     * Inserts log data into the EventLog table, using data provided by
     * the TelephonyManager (TM) or an already backed up EvenLog database...
     *
     * If you just need to add an event with currently connected TM data such
     * as CID,LAC,PSC,GPS, then use the simple version called toEventLog(),
     * defined below.
     *
     * DF_id   DF_desc
     * ---------------
     * 1       changing lac
     * 2       cell no in OCID
     * 3       "Detected Type-0 SMS"
     * 4       "Detected MWI SMS"
     * 5       "Detected WAP PUSH SMS"
     * 6       "Detected WAP PUSH (2) SMS"
     * 7 ...
     *
     * --------------------------------------------------------------------------
     * Old description
     * --------------------------------------------------------------------------
     * Description:     Inserts log data into the EventLog table
     *
     * Issues:          [ ] ALL events should be logged!!
     *                  [ ] To avoid repeated copies, only check last DB entries
     *                  [ ] Before inserting event, check that LAC/CID are not "-1".
     *                  [ ] Any related notifications are better put here as well, right?
     *                  [ ]
     *
     * Notes:           a)  Table item order:
     *
     *                      time,LAC,CID,PSC,gpsd_lat,gpsd_lon,gpsd_accu,DF_id,DF_desc
     *
     *                  b)  We need to check if cell is not in OCID  Events are not continuously
     *                      logged to the database as it currently stands. If the same cell shows
     *                      up it will again be dumped to the event log and will fill up pretty
     *                      quickly.
     *
     *                      @banjaxobanjo    What do you mean here? ALL events should be logged!
     *
     *                  c) To select last lines use:
     *
     *      SELECT * FROM EventLog WHERE LAC=30114 AND CID=779149 AND DF_id=1 ORDER BY _id DESC LIMIT 1;
     *      SELECT * FROM EventLog WHERE _id=(SELECT max(_id) FROM EventLog) AND LAC=30114 AND CID=779149 AND DF_id=1;
     *
     *
     *      Query examples for future devs:
     *
     *       SELECT * FROM EventLog WHERE CID = 1234 AND LAC = 4321 AND DF_id BETWEEN 1 AND 4
     *       SELECT * FROM EventLog WHERE CID = 1234 AND LAC = 4321 AND DF_id = 1" Changing LAC
     *       SELECT * FROM EventLog WHERE CID = 1234 AND LAC = 4321 AND DF_id = 2" Cell not in OCID
     *       SELECT * FROM EventLog WHERE CID = 1234 AND LAC = 4321 AND DF_id = 3" Detected SMS
     *       SELECT * FROM EventLog WHERE CID = 1234 AND LAC = 4321 AND DF_id = 4" Unknown T.B.A...     *
     */
    public void insertEventLog(String time,
                               int lac,
                               int cid,
                               int psc,
                               String gpsd_lat,
                               String gpsd_lon,
                               int gpsd_accu,
                               int DF_id,
                               String DF_description) {

        if (cid != -1) { // skip CID of "-1" (due to crappy API or roaming or Air-Plane Mode)
            // ONLY check if LAST entry is the same!
            String query = String.format(Locale.US,
                    // "SELECT * FROM EventLog WHERE LAC=%d AND CID=%d AND DF_id=%d ORDER BY _id DESC LIMIT 1",
                    "SELECT * from EventLog WHERE _id=(SELECT max(_id) from EventLog) AND CID=%d AND LAC=%d AND DF_id=%d",
                    // was: "SELECT * FROM EventLog WHERE CID = %d AND LAC = %d AND DF_id = %d",
                    cid, lac, DF_id);
            Cursor cursor = mDb.rawQuery(query, null);

            boolean insertData = true;
            if (cursor.getCount() > 0) {
                insertData = false;
            }
            cursor.close();

            if (insertData) {
                ContentValues eventLog = new ContentValues();

                eventLog.put("time", time);
                eventLog.put("LAC", lac);
                eventLog.put("CID", cid);
                eventLog.put("PSC", psc);
                eventLog.put("gpsd_lat", gpsd_lat);
                eventLog.put("gpsd_lon", gpsd_lon);
                eventLog.put("gpsd_accu", gpsd_accu);
                eventLog.put("DF_id", DF_id);
                eventLog.put("DF_description", DF_description);

                mDb.insert("EventLog", null, eventLog);
                Log.i(TAG, mTAG + "InsertEventLog(): Insert detection event into EventLog table with CID=" + cid);
            } else {
                // TODO This may need to be removed as it may spam the logcat buffer...
                Log.i(TAG, mTAG + "InsertEventLog(): Skipped inserting duplicate event into EventLog table with CID=" + cid);
            }
        }
    }


    // Defining a new simpler version of insertEventLog for use in CellTracker.
    // Please note, that in AMSICDDbAdapter (here) it is also used to backup DB,
    // in which case we can not use this simpler version!
    public void toEventLog(int DF_id, String DF_desc) {

        String time = MiscUtils.getCurrentTimeStamp();                    // time
        int lac = CellTracker.mMonitorCell.getLAC();                // LAC
        int cid = CellTracker.mMonitorCell.getCID();                // CID
        int psc = CellTracker.mMonitorCell.getPSC();                // PSC [UMTS,LTE]
        String gpsd_lat = String.valueOf(CellTracker.mMonitorCell.getLat()); // gpsd_lat
        String gpsd_lon = String.valueOf(CellTracker.mMonitorCell.getLon()); // gpsd_lon
        int gpsd_accu = (int) CellTracker.mMonitorCell.getAccuracy();        // gpsd_accu

        // skip CID/LAC of "-1" (due to crappy API, Roaming or Air-Plane Mode)
        if (cid != -1 || lac != -1) {
            // Check if LAST entry is the same!
            String query = String.format(Locale.US,
                    "SELECT * from EventLog WHERE _id=(SELECT max(_id) from EventLog) AND CID=%d AND LAC=%d AND PSC=%d AND DF_id=%d",
                    cid, lac, psc, DF_id);
            Cursor cursor = mDb.rawQuery(query, null);

            // WARNING: By skipping duplicate events, we might be missing counts of Type-0 SMS etc.
            boolean insertData = true;
            if (cursor.getCount() > 0) {
                insertData = false;
            }
            cursor.close();

            if (insertData) {
                ContentValues eventLog = new ContentValues();

                eventLog.put("time", time);                 // time
                eventLog.put("LAC", lac);                   // LAC
                eventLog.put("CID", cid);                   // CID
                eventLog.put("PSC", psc);                   // PSC [UMTS,LTE]
                eventLog.put("gpsd_lat", gpsd_lat);         // gpsd_lat
                eventLog.put("gpsd_lon", gpsd_lon);         // gpsd_lon
                eventLog.put("gpsd_accu", gpsd_accu);       // gpsd_accu
                eventLog.put("DF_id", DF_id);               // DF_id
                eventLog.put("DF_description", DF_desc);    // DF_desc

                mDb.insert("EventLog", null, eventLog);
                Log.i(TAG, mTAG + "ToEventLog(): Added new event: id=" + DF_id + " time=" + time + " cid=" + cid);

                // Short 100 ms Vibration
                // TODO not elegant solution, vibrator invocation should be moved somewhere else imho
                boolean vibrationEnabled = mPreferences.getBoolean(mContext.getString(zz.aimsicd.lite.R.string.pref_notification_vibrate_enable), true);
                int thresholdLevel = Integer.valueOf(mPreferences.getString(mContext.getString(zz.aimsicd.lite.R.string.pref_notification_vibrate_min_level), String.valueOf(Status.MEDIUM.ordinal())));
                boolean higherLevelThanThreshold = Status.MEDIUM.ordinal() <= thresholdLevel;

                if (vibrationEnabled && higherLevelThanThreshold) {
                    Vibrator v = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
                    v.vibrate(100);
                }

                // Short sound:
                // TODO see issue #15

            }
//            else {
            // TODO This may need to be removed as it may spam the logcat buffer...
            //Log.i(TAG, mTAG + mTAG + ":toEventLog(): Skipped inserting duplicate event");
//            }
        }
        // TODO This may need to be removed as it may spam the logcat buffer...
        //Log.i(TAG, mTAG + mTAG + ":insertEventLog(): Skipped inserting bad CID/LAC data");
    }


    /**
     * Inserts BTS Sector Type data into the SectorType table
     */
    public void insertSectorType(String description) {
        ContentValues sectorType = new ContentValues();
        sectorType.put("description", description);
        mDb.insert("SectorType", null, sectorType);
    }

    /**
     * Inserts SMS Detection Strings data into the "DetectionStrings" table
     */
    public void insertDetectionStrings(String det_str, String sms_type) {

        ContentValues detectionStrings = new ContentValues();
        detectionStrings.put("det_str", det_str);
        detectionStrings.put("sms_type", sms_type);

        String query = String.format(Locale.US,
                "SELECT * FROM DetectionStrings WHERE det_str = \"%s\" AND sms_type = \"%s\"",
                det_str, sms_type);

        // Check that the new string is not already in DB then insert
        Cursor cursor = mDb.rawQuery(query, null);
        if (cursor.getCount() <= 0) {
            mDb.insert("DetectionStrings", null, detectionStrings);
            cursor.close();
        } else {
            cursor.close();
        }
    }

    /**
     * Inserts detected silent SMS data into "SmsData" table
     */
    public void insertSmsData(String time,
                              String number,
                              String smsc,
                              String message,
                              String type,
                              String CLASS, //<-- had to put in uppercase class is used be api
                              int lac,
                              int cid,
                              String rat,
                              double gps_lat,
                              double gps_lon,
                              int isRoaming) {

        ContentValues smsData = new ContentValues();

        smsData.put("time", time);
        smsData.put("number", number);
        smsData.put("smsc", smsc);
        smsData.put("message", message);
        smsData.put("type", type);
        smsData.put("class", CLASS);
        smsData.put("lac", lac);
        smsData.put("cid", cid);
        smsData.put("rat", rat);
        smsData.put("gps_lat", gps_lat);
        smsData.put("gps_lon", gps_lon);
        smsData.put("isRoaming", isRoaming);

        // Check that SMS timestamp is not already in DB, then INSERT
        if (!isTimeStampInDB(time)) {
            mDb.insert("SmsData", null, smsData);
        }
    }

    /**
     * This checks if a cell with a given CID already exists in the (DBe_import) database.
     */
    public boolean openCellExists(int cellID) {
        String qry = String.format(Locale.US, "SELECT CID FROM DBe_import WHERE CID = %d", cellID);
        Cursor cursor = mDb.rawQuery(qry, null);
        boolean exists = cursor.getCount() > 0;
        cursor.close();
        return exists;
    }

    /**
     * Check if CID and LAC is already in DBi_bts
     * WARNING: This is only for checking, if used to get info, replace "CID,LAC" with "*"
     */
    public boolean cellInDbiBts(int lac, int cellID) {

        String query = String.format(Locale.US, "SELECT CID,LAC FROM DBi_bts WHERE LAC = %d AND CID = %d",
                lac, cellID);

        Cursor cursor = mDb.rawQuery(query, null);
        boolean exists = cursor.getCount() > 0;
        cursor.close();
        return exists;
    }

    /**
     * Check if CID (currently bts_id) is already in DBi_measure
     */
    public boolean cellInDbiMeasure(int cellID) {
        String query = String.format(Locale.US,
                // "SELECT CID FROM DBi_bts, DBi_measure WHERE bts_id = %d", // todo E:V:A
                "SELECT bts_id FROM DBi_measure WHERE bts_id = %d",
                cellID);

        Cursor cursor = mDb.rawQuery(query, null);
        boolean exists = cursor.getCount() > 0;
        cursor.close();
        return exists;
    }
} public class BaseInflaterAdapter<T> extends BaseAdapter {

    private final List<T> m_items = new ArrayList<>();

    private IAdapterViewInflater<T> m_viewInflater;

    public BaseInflaterAdapter(IAdapterViewInflater<T> viewInflater) {
        m_viewInflater = viewInflater;
    }

    public BaseInflaterAdapter(List<T> items, IAdapterViewInflater<T> viewInflater) {
        m_items.addAll(items);
        m_viewInflater = viewInflater;
    }

    public void setViewInflater(IAdapterViewInflater<T> viewInflater, boolean notifyChange) {
        m_viewInflater = viewInflater;

        if (notifyChange) {
            notifyDataSetChanged();
        }
    }

    public void addItem(T item, boolean notifyChange) {
        m_items.add(item);

        if (notifyChange) {
            notifyDataSetChanged();
        }
    }

    public void addItems(List<T> items, boolean notifyChange) {
        m_items.addAll(items);

        if (notifyChange) {
            notifyDataSetChanged();
        }
    }

    public void clear(boolean notifyChange) {
        m_items.clear();

        if (notifyChange) {
            notifyDataSetChanged();
        }
    }

    @Override
    public int getCount() {
        return m_items.size();
    }

    @Override
    public Object getItem(int pos) {
        return getTItem(pos);
    }

    public T getTItem(int pos) {
        return m_items.get(pos);
    }

    @Override
    public long getItemId(int pos) {
        return pos;
    }

    @Override
    public View getView(int pos, View convertView, ViewGroup parent) {
        return m_viewInflater != null ? m_viewInflater.inflate(this, pos, convertView, parent)
                : null;
    }
} public class BtsMeasureCardInflater implements IAdapterViewInflater<BtsMeasureItemData> {

    @Override
    public View inflate(final BaseInflaterAdapter<BtsMeasureItemData> adapter,
                        final int pos, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.bts_measure_data, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final BtsMeasureItemData item = adapter.getTItem(pos);
        holder.updateDisplay(item);

        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;

        private TextView bts_id;
        private TextView nc_list;
        private TextView time;
        private TextView gpsd_lat;
        private TextView gpsd_lon;
        private TextView gpsd_accu;
        private TextView gpse_lat;
        private TextView gpse_lon;
        private TextView bb_power;
        private TextView bb_rf_temp;
        private TextView tx_power;
        private TextView rx_signal;
        private TextView rx_stype;
        private TextView rat;
        private TextView BCCH;
        private TextView TMSI;
        private TextView TA;
        private TextView PD;
        private TextView BER;
        private TextView AvgEcNo;
        private TextView isSubmitted;
        private TextView isNeighbour;

        private TextView mRecordId;     //EVA

        ViewHolder(View rootView) {
            mRootView = rootView;

            bts_id = (TextView) mRootView.findViewById(R.id.tv_bts_measure_bts_id);
            nc_list = (TextView) mRootView.findViewById(R.id.tv_bts_measure_nc_list);
            time = (TextView) mRootView.findViewById(R.id.tv_bts_measure_time);
            gpsd_lat = (TextView) mRootView.findViewById(R.id.tv_bts_measure_gpsd_lat);
            gpsd_lon = (TextView) mRootView.findViewById(R.id.tv_bts_measure_gpsd_lon);
            gpsd_accu = (TextView) mRootView.findViewById(R.id.tv_bts_measure_gpsd_accu);
            //gpse_lat = (TextView) mRootView.findViewById(R.id.tv_bts_measure_gpse_lat);
            //gpse_lon = (TextView) mRootView.findViewById(R.id.tv_bts_measure_gpse_lon);
            //bb_power = (TextView) mRootView.findViewById(R.id.tv_bts_measure_bb_power);
            //bb_rf_temp = (TextView) mRootView.findViewById(R.id.tv_bts_measure_bb_rf_temp);
            //tx_power = (TextView) mRootView.findViewById(R.id.tv_bts_measure_tx_power);
            rx_signal = (TextView) mRootView.findViewById(R.id.tv_bts_measure_rx_signal);
            //rx_stype = (TextView) mRootView.findViewById(R.id.tv_bts_measure_rx_stype);
            rat = (TextView) mRootView.findViewById(R.id.tv_bts_measure_rat);
            //BCCH = (TextView) mRootView.findViewById(R.id.tv_bts_measure_BCCH);
            //TMSI = (TextView) mRootView.findViewById(R.id.tv_bts_measure_TMSI);
            //TA = (TextView) mRootView.findViewById(R.id.tv_bts_measure_TA);
            //PD = (TextView) mRootView.findViewById(R.id.tv_bts_measure_PD);
            //BER = (TextView) mRootView.findViewById(R.id.tv_bts_measure_BER);
            //AvgEcNo = (TextView) mRootView.findViewById(R.id.tv_bts_measure_AvgEcNo);
            isSubmitted = (TextView) mRootView.findViewById(zz.aimsicd.lite.R.id.tv_bts_measure_isSubmitted);
            isNeighbour = (TextView) mRootView.findViewById(R.id.tv_bts_measure_isNeighbour);

            mRecordId = (TextView) mRootView.findViewById(R.id.record_id);      // EVA
            rootView.setTag(this);
        }

        public void updateDisplay(BtsMeasureItemData item) {

           bts_id.setText(item.getBts_id());
            nc_list.setText(item.getNc_list());
            time.setText(item.getTime());
            gpsd_lat.setText(item.getGpsd_lat());
            gpsd_lon.setText(item.getGpsd_lon());
            gpsd_accu.setText(item.getGpsd_accu());
//            gpse_lat.setText(item.getGpse_lat());
//            gpse_lon.setText(item.getGpse_lon());
//            bb_power.setText(item.getBb_power());
//            bb_rf_temp.setText(item.getBb_rf_temp());
//            tx_power.setText(item.getTx_power());
            rx_signal.setText(item.getRx_signal());
//            rx_stype.setText(item.getRx_stype());
            // TODO: 2016-02-27 Someone has got to unify RAT as either an int or human-string
            // This converts a string of a number to an integer and then gets the human-string
            // From the number.
            rat.setText(Cell.getRatFromInt(Integer.parseInt(item.getRat())));
//            BCCH.setText(item.getBCCH());
//            TMSI.setText(item.getTMSI());
//            TA.setText(item.getTA());
//            PD.setText(item.getPD());
//            BER.setText(item.getBER());
//            AvgEcNo.setText(item.getAvgEcNo());
            isSubmitted.setText(item.getIsSubmitted());
            isNeighbour.setText(item.getIsNeighbour());

            mRecordId.setText(item.getRecordId());      // EVA
        }
    }
} public class BtsMeasureItemData  {
    public String getBts_id() {
        return bts_id;
    }

    public void setBts_id(String bts_id) {
        this.bts_id = bts_id;
    }

    public String getNc_list() {
        return nc_list;
    }

    public void setNc_list(String nc_list) {
        this.nc_list = nc_list;
    }

    public String getTime() {
        return time;
    }

    public void setTime(String time) {
        this.time = time;
    }

    public String getGpsd_lat() {
        return gpsd_lat;
    }

    public void setGpsd_lat(String gpsd_lat) {
        this.gpsd_lat = gpsd_lat;
    }

    public String getGpsd_lon() {
        return gpsd_lon;
    }

    public void setGpsd_lon(String gpsd_lon) {
        this.gpsd_lon = gpsd_lon;
    }

    public String getGpsd_accu() {
        return gpsd_accu;
    }

    public void setGpsd_accu(String gpsd_accu) {
        this.gpsd_accu = gpsd_accu;
    }

    public String getGpse_lat() {
        return gpse_lat;
    }

    public void setGpse_lat(String gpse_lat) {
        this.gpse_lat = gpse_lat;
    }

    public String getGpse_lon() {
        return gpse_lon;
    }

    public void setGpse_lon(String gpse_lon) {
        this.gpse_lon = gpse_lon;
    }

    public String getBb_power() {
        return bb_power;
    }

    public void setBb_power(String bb_power) {
        this.bb_power = bb_power;
    }

    public String getBb_rf_temp() {
        return bb_rf_temp;
    }

    public void setBb_rf_temp(String bb_rf_temp) {
        this.bb_rf_temp = bb_rf_temp;
    }

    public String getTx_power() {
        return tx_power;
    }

    public void setTx_power(String tx_power) {
        this.tx_power = tx_power;
    }

    public String getRx_signal() {
        return rx_signal;
    }

    public void setRx_signal(String rx_signal) {
        this.rx_signal = rx_signal;
    }

    public String getRx_stype() {
        return rx_stype;
    }

    public void setRx_stype(String rx_stype) {
        this.rx_stype = rx_stype;
    }

    public String getRat() {
        return rat;
    }

    public void setRat(String rat) {
        this.rat = rat;
    }

    public String getBCCH() {
        return BCCH;
    }

    public void setBCCH(String BCCH) {
        this.BCCH = BCCH;
    }

    public String getTMSI() {
        return TMSI;
    }

    public void setTMSI(String TMSI) {
        this.TMSI = TMSI;
    }

    public String getTA() {
        return TA;
    }

    public void setTA(String TA) {
        this.TA = TA;
    }

    public String getPD() {
        return PD;
    }

    public void setPD(String PD) {
        this.PD = PD;
    }

    public String getBER() {
        return BER;
    }

    public void setBER(String BER) {
        this.BER = BER;
    }

    public String getAvgEcNo() {
        return AvgEcNo;
    }

    public void setAvgEcNo(String avgEcNo) {
        AvgEcNo = avgEcNo;
    }

    public String getIsSubmitted() {
        return isSubmitted;
    }

    public void setIsSubmitted(String isSubmitted) {
        this.isSubmitted = isSubmitted;
    }

    public String getIsNeighbour() {
        return isNeighbour;
    }

    public void setIsNeighbour(String isNeighbour) {
        this.isNeighbour = isNeighbour;
    }

    public String getRecordId() {
        return mRecordId;
    }

    private String bts_id;
    private String nc_list;
    private String time;
    private String gpsd_lat;
    private String gpsd_lon;
    private String gpsd_accu;
    private String gpse_lat;
    private String gpse_lon;
    private String bb_power;
    private String bb_rf_temp;
    private String tx_power;
    private String rx_signal;
    private String rx_stype;
    private String rat;
    private String BCCH;
    private String TMSI;
    private String TA;
    private String PD;
    private String BER;
    private String AvgEcNo;
    private String isSubmitted;
    private String isNeighbour;

    private String mRecordId;

    public BtsMeasureItemData(
            String _bts_id,
            String _nc_list,
            String _time,
            String _gpsd_lat,
            String _gpsd_lon,
            String _gpsd_accu,
//            String _gpse_lat,
//            String _gpse_lon,
//            String _bb_power,
//            String _bb_rf_temp,
//            String _tx_power,
            String _rx_signal,
//            String _rx_stype,
            String _rat,
//            String _BCCH,
//            String _TMSI,
//            String _TA,
//            String _PD,
//            String _BER,
//            String _AvgEcNo,
            String _isSubmitted,
            String _isNeighbour,
            String _mRecordId) {

        this.bts_id = _bts_id;
        this.nc_list = _nc_list;
        this.time = _time;
        this.gpsd_lat = _gpsd_lat;
        this.gpsd_lon = _gpsd_lon;
        this.gpsd_accu = _gpsd_accu;
//        this.gpse_lat = _gpse_lat;
//        this.gpse_lon = _gpse_lon;
//        this.bb_power = _bb_power;
//        this.bb_rf_temp = _bb_rf_temp;
//        this.tx_power = _tx_power;
        this.rx_signal = _rx_signal;
//        this.rx_stype = _rx_stype;
        this.rat = _rat;
//        this.BCCH = _BCCH;
//        this.TMSI = _TMSI;
//        this.TA = _TA;
//        this.PD = _PD;
//        this.BER = _BER;
//        this.AvgEcNo = _AvgEcNo;
        this.isSubmitted = _isSubmitted;
        this.isNeighbour = _isNeighbour;

        this.mRecordId = _mRecordId;
    }

    public BtsMeasureItemData(String... args) {
        this(
                args[0],    // bts_id
                args[1],    // nc_list
                args[2],    // time
                args[3],    // gpsd_lat
                args[4],    // gpsd_lon
                args[5],    // gpsd_accu
                args[6],    // rx_signal
                args[7],    // rat
                args[8],    // isSubmitted
                args[9],    // isNeighbour
                args[10]    // mRecordId        // EVA
                //,
        );
    }
} public class CardItemData {
    // OLD (in old DB tables)
    private final String mCellID;
    private final String mLac;
    private final String mMcc;
    private final String mMnc;
    private final String mNet;
    private final String mSignal;
    private final String mAvgSigStr;
    private final String mSamples;
    private final String mLat;
    private final String mLng;
    private final String mCountry;
    private final String mPsc;
    private final String mTimestamp;
    private final String mRecordId;

    // NEW (in new DB tables)
/*
    private final String mtime;
    private final String mLAC;
    private final String mCID;
    private final String mPSC;
    private final String mgpsd_lat;
    private final String mgpsd_lon;
    private final String mgpsd_accu;
    private final String mDF_id;
    private final String mDF_description;
*/


    // OLD items in old DB table structure

    public CardItemData(Cell cell, String recordId) {

        if (cell.getCID() != Integer.MAX_VALUE && cell.getCID() != -1) {
            mCellID = "CID: " + cell.getCID() + "  (0x" + Integer.toHexString(cell.getCID()) + ")";
        } else {
            mCellID = "N/A";
        }

        if (cell.getLAC() != Integer.MAX_VALUE && cell.getLAC() != -1) {
            mLac = "LAC: " + cell.getLAC();
        } else {
            mLac = "N/A";
        }

        if (cell.getMCC() != Integer.MAX_VALUE && cell.getMCC() != 0) {
            mMcc = "MCC: " + cell.getMCC();
        } else {
            mMcc = "N/A";
        }

        if (cell.getMNC() != Integer.MAX_VALUE && cell.getMNC() != 0) {
            mMnc = "MNC: " + cell.getMNC();
        } else {
            mMnc = "N/A";
        }

        if (cell.getNetType() != Integer.MAX_VALUE && cell.getNetType() != -1) {
            mNet = "Type: " + cell.getNetType() + " - " + cell.getRAT();
        } else {
            mNet = "N/A";
        }

        if (cell.getPSC() != Integer.MAX_VALUE && cell.getPSC() != -1) {
            mPsc = "PSC: " + cell.getPSC();
        } else {
            mPsc = "N/A";
        }

        if (cell.getRssi() != Integer.MAX_VALUE && cell.getRssi() != -1) {
            mSignal = "RSSI: " + cell.getRssi();
        } else if (cell.getDBM() != Integer.MAX_VALUE && cell.getDBM() != -1) {
            mSignal = "dBm: " + cell.getDBM();
        } else {
            mSignal = "N/A";
        }
        // NEW (in new DB tables)


        // end New

        mLat = "N/A";
        mLng = "N/A";
        mAvgSigStr = "N/A";
        mSamples = "N/A";
        mCountry = "N/A";
        mTimestamp = "N/A";
        mRecordId = recordId;

        // NEW (in new DB tables)

        // end New

    }

    public CardItemData(String cellID, String lac, String mcc, String mnc, String lat, String lng,
            String avgSigStr, String samples, String recordId) {
        mCellID = cellID;
        mLac = lac;
        mMcc = mcc;
        mMnc = mnc;
        mNet = "Network Type: N/A";
        mLat = lat;
        mLng = lng;
        mSignal = "Signal: N/A";
        mAvgSigStr = avgSigStr;
        mSamples = samples;
        mPsc = "PSC: N/A";
        mCountry = "Country: N/A";
        mTimestamp = "Timestamp: N/A";
        mRecordId = recordId;
    }

    public CardItemData(String cellID, String psc, String mcc, String mnc, String signal,
            String recordId) {
        mCellID = cellID;
        mLac = "LAC: N/A";
        mMcc = mcc;
        mMnc = mnc;
        mLat = "Latitude: N/A";
        mLng = "Longitude: N/A";
        mNet = "Network Type: N/A";
        mAvgSigStr = "Avg Signal: N/A";
        mSamples = "Samples: N/A";
        mSignal = signal;
        mPsc = psc;
        mCountry = "Country: N/A";
        mTimestamp = "Timestamp: N/A";
        mRecordId = recordId;
    }

    public CardItemData(String cellID, String lac, String mcc, String mnc, String signal,
            String psc, String timestamp, String recordId) {
        mCellID = cellID;
        mLac = lac;
        mMcc = mcc;
        mMnc = mnc;
        mLat = "Latitude: N/A";
        mLng = "Longitude: N/A";
        mNet = "Network Type: N/A";
        mSignal = signal;
        mPsc = psc;
        mAvgSigStr = "Avg Signal: N/A";
        mSamples = "Samples: N/A";
        mTimestamp = timestamp;
        mCountry = "Country: N/A";
        mRecordId = recordId;
    }

    public CardItemData(int type, String cellID, String lac, String mcc, String mnc, String signal,
            String timestamp, String recordId) {
        mCellID = cellID;
        mLac = lac;
        mMcc = mcc;
        mMnc = mnc;
        mLat = "Latitude: N/A";
        mLng = "Longitude: N/A";
        mNet = "Network Type: N/A";
        mSignal = signal;
        mAvgSigStr = "Avg Signal: N/A";
        mSamples = "Samples: N/A";
        mTimestamp = timestamp;
        mPsc = "PSC: N/A";
        mCountry = "Country: N/A";
        mRecordId = recordId;
    }

    public CardItemData(String cellID, String lac, String net, String lat, String lng,
            String signal, String recordId) {
        mCellID = cellID;
        mLac = lac;
        mNet = net;
        mMcc = "MCC: N/A";
        mMnc = "MNC: N/A";
        mLat = lat;
        mLng = lng;
        mSignal = signal;
        mAvgSigStr = "Avg Signal: N/A";
        mSamples = "Samples: N/A";
        mPsc = "PSC: N/A";
        mCountry = "Country: N/A";
        mTimestamp = "Timestamp: N/A";
        mRecordId = recordId;
    }

    public CardItemData(String country, String mcc, String lat, String lng, String recordId) {
        mCellID = "CellID: N/A";
        mLac = "LAC: N/A";
        mCountry = country;
        mMcc = mcc;
        mMnc = "MNC: N/A";
        mNet = "Network Type: N/A";
        mSignal = "Signal: N/A";
        mLat = lat;
        mLng = lng;
        mAvgSigStr = "Avg Signal: N/A";
        mSamples = "Samples: N/A";
        mPsc = "PSC: N/A";
        mTimestamp = "Timestamp: N/A";
        mRecordId = recordId;
    }

    public String getCellID() {
        return mCellID;
    }

    public String getLac() {
        return mLac;
    }

    public String getMcc() {
        return mMcc;
    }

    public String getMnc() {
        return mMnc;
    }

    public String getNet() {
        return mNet;
    }

    public String getSignal() {
        return mSignal;
    }

    public String getAvgSigStr() {
        return mAvgSigStr;
    }

    public String getSamples() {
        return mSamples;
    }

    public String getLat() {
        return mLat;
    }

    public String getLng() {
        return mLng;
    }

    public String getCountry() {
        return mCountry;
    }

    public String getRecordId() {
        return mRecordId;
    }

    public String getPsc() {
        return mPsc;
    }

    public String getTimestamp() {
        return mTimestamp;
    }

    // NEW (in new DB tables)
    // EventLog

    //public String getAccu() {
    //    return mAccu;
    //}

} public class DbViewerSpinnerAdapter extends ArrayAdapter<StatesDbViewer> {

    public DbViewerSpinnerAdapter(Context pContext, int pResource) {
        super(pContext, pResource);
        mDataList = StatesDbViewer.getStates();
    }

    List<StatesDbViewer> mDataList;


    @Override
    public View getView(int pPosition, View pConvertView, ViewGroup pParent) {

        View lView = pConvertView;
        ViewTableNameSpinnerHolder lHolder;
        if (lView == null
                || (lView.getId() != R.id.item_root_layout && !(lView.getTag() instanceof ViewTableNameSpinnerHolder))) {

            lView = ((LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(
                    R.layout.item_spinner_db_viewer, pParent, false);
            lHolder = setViewHolderToView(lView);

        } else {
            lHolder = (ViewTableNameSpinnerHolder) lView.getTag();

        }

        StatesDbViewer lEntry = mDataList.get(pPosition);
        lHolder.name.setText(lEntry.getDisplayName(getContext()));

        return lView;
    }


    @Override
    public View getDropDownView(int pPosition, View pConvertView, ViewGroup pParent) {
        return getView(pPosition, pConvertView, pParent);
    }


    @Override
    public StatesDbViewer getItem(int position) {
        return mDataList.get(position);
    }


    private ViewTableNameSpinnerHolder setViewHolderToView(View pView) {
        ViewTableNameSpinnerHolder lHolder;
        lHolder = new ViewTableNameSpinnerHolder();

        lHolder.name = (TextView) pView.findViewById(R.id.item_name);

        pView.setTag(lHolder);
        return lHolder;
    }


    @Override
    public int getCount() {
        return mDataList.size();
    }

} public class DbeImportCardInflater implements IAdapterViewInflater<DbeImportItemData> {

    @Override
    public View inflate(final BaseInflaterAdapter<DbeImportItemData> adapter,
                        final int pos, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.dbe_import_items, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final DbeImportItemData item = adapter.getTItem(pos);
        holder.updateDisplay(item);
        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;

        private final TextView DB_SOURCE;
        private final TextView RAT;
        private final TextView MCC;
        private final TextView MNC;
        private final TextView LAC;
        private final TextView CID;
        private final TextView PSC;
        private final TextView GPS_LAT;
        private final TextView GPS_LON;
        private final TextView IS_GPS_EXACT;
        private final TextView AVG_RANGE;
        private final TextView AVG_SIGNAL;
        private final TextView SAMPLES;
        private final TextView TIME_FIRST;
        private final TextView TIME_LAST;
        private final TextView REJ_CAUSE;

        private final TextView mRecordId;

        ViewHolder(View rootView) {
            mRootView = rootView;
            // TODO: explain these and try to adhere to a naming convention
            // These are the id names as used in the "dbe_import_items.xml" stylesheet
            DB_SOURCE =     (TextView) mRootView.findViewById(R.id.dbsource);
            RAT =           (TextView) mRootView.findViewById(R.id.RAT);
            MCC =           (TextView) mRootView.findViewById(R.id.nMCC);
            MNC =           (TextView) mRootView.findViewById(R.id.nMNC);
            LAC =           (TextView) mRootView.findViewById(R.id.nLAC);
            CID =           (TextView) mRootView.findViewById(R.id.nCID);
            PSC =           (TextView) mRootView.findViewById(R.id.nPSC);
            GPS_LAT =       (TextView) mRootView.findViewById(R.id.ngpsd_lat);
            GPS_LON =       (TextView) mRootView.findViewById(R.id.ngpsd_lon);
            IS_GPS_EXACT =  (TextView) mRootView.findViewById(R.id.is_exact);
            AVG_RANGE =     (TextView) mRootView.findViewById(R.id.navg_range);
            AVG_SIGNAL =    (TextView) mRootView.findViewById(R.id.navg_signal);
            SAMPLES =       (TextView) mRootView.findViewById(R.id.nSAMPLES);
            TIME_FIRST =    (TextView) mRootView.findViewById(R.id.nTIME_FIRST);
            TIME_LAST =     (TextView) mRootView.findViewById(R.id.nTIME_LAST);
            REJ_CAUSE =     (TextView) mRootView.findViewById(R.id.nREJ_CAUSE);

            mRecordId =     (TextView) mRootView.findViewById(R.id.record_id);
            rootView.setTag(this);
        }

        public void updateDisplay(DbeImportItemData item) {
            DB_SOURCE.setText(item.getDB_SOURCE());
            RAT.setText(item.getRAT());
            MCC.setText(item.getMCC());
            MNC.setText(item.getMNC());
            LAC.setText(item.getLAC());
            CID.setText(item.getCID());
            PSC.setText(item.getPSC());
            GPS_LAT.setText(item.getGPS_LAT());
            GPS_LON.setText(item.getGPS_LON());
            IS_GPS_EXACT.setText(item.getIS_GPS_EXACT());
            AVG_RANGE.setText(item.getAVG_RANGE());
            AVG_SIGNAL.setText(item.getAVG_SIGNAL());
            SAMPLES.setText(item.getSAMPLES());
            TIME_FIRST.setText(item.getTIME_FIRST());
            TIME_LAST.setText(item.getTIME_LAST());
            REJ_CAUSE.setText(item.getREJ_CAUSE());

            mRecordId.setText(item.getRecordId());
        }
    }
} public class DbeImportItemData {

    private final String DB_SOURCE;
    private final String RAT;
    private final String MCC;
    private final String MNC;
    private final String LAC;
    private final String CID;
    private final String PSC;
    private final String GPS_LAT;
    private final String GPS_LON;
    private final String IS_GPS_EXACT;
    private final String AVG_RANGE;
    private final String AVG_SIGNAL;
    private final String SAMPLES;
    private final String TIME_FIRST;
    private final String TIME_LAST;
    private final String REJ_CAUSE;

    //private boolean mIsFakeData;
    private final String mRecordId;

    public DbeImportItemData(
            String db_source,
             String rat,
             String mcc,
             String mnc,
             String lac,
             String cid,
             String psc,
             String gps_lat,
             String gps_lon,
             String is_gps_exact,
             String avg_range,
             String avg_signal,
             String samples,
             String time_first,
             String time_last,
             String rej_cause,
             String _mRecordId) {

            DB_SOURCE = db_source;
            RAT = rat;
            MCC = mcc;
            MNC = mnc;
            LAC = lac;
            CID = cid;
            PSC = psc;
            GPS_LAT = gps_lat;
            GPS_LON = gps_lon;
            IS_GPS_EXACT = is_gps_exact;
            AVG_RANGE = avg_range;
            AVG_SIGNAL = avg_signal;
            SAMPLES = samples;
            TIME_FIRST = time_first;
            TIME_LAST = time_last;
            REJ_CAUSE = rej_cause;

            mRecordId = _mRecordId;
    }

    public String getDB_SOURCE() {
        return DB_SOURCE;
    }


    public String getRAT() {
        return RAT;
    }

    public String getMCC() {
        return MCC;
    }

    public String getMNC() {
        return MNC;
    }

    public String getLAC() {
        return LAC;
    }

    public String getCID() {
        return CID;
    }

    public String getPSC() {
        return PSC;
    }

    public String getGPS_LAT() {
        return GPS_LAT;
    }

    public String getGPS_LON() {
        return GPS_LON;
    }

    public String getIS_GPS_EXACT() {
        return IS_GPS_EXACT;
    }

    public String getAVG_RANGE() {
        return AVG_RANGE;
    }

    public String getAVG_SIGNAL() {
        return AVG_SIGNAL;
    }

    public String getSAMPLES() {
        return SAMPLES;
    }

    public String getTIME_FIRST() {
        return TIME_FIRST;
    }

    public String getTIME_LAST() {
        return TIME_LAST;
    }

    public String getREJ_CAUSE() {
        return REJ_CAUSE;
    }

    public String getRecordId() {
        return mRecordId;
    }

} public class DefaultLocationCardInflater implements IAdapterViewInflater<CardItemData> {

    @Override
    public View inflate(final BaseInflaterAdapter<CardItemData> adapter, final int pos,
            View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.default_location_items, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final CardItemData item = adapter.getTItem(pos);
        holder.updateDisplay(item);

        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;
        private final TextView mCountry;
        private final TextView mMcc;
        private final TextView mLat;
        private final TextView mLng;
        private final TextView mRecordId;

        ViewHolder(View rootView) {
            mRootView = rootView;

            mCountry =  (TextView) mRootView.findViewById(R.id.country);
            mMcc =      (TextView) mRootView.findViewById(R.id.mcc);
            mLat =      (TextView) mRootView.findViewById(R.id.lat);
            mLng =      (TextView) mRootView.findViewById(R.id.lng);
            mRecordId = (TextView) mRootView.findViewById(R.id.record_id);

            rootView.setTag(this);
        }

        public void updateDisplay(CardItemData item) {
            mCountry.setText(item.getCountry());
            mMcc.setText(item.getMcc());
            mLat.setText(item.getLat());
            mLng.setText(item.getLng());
            mRecordId.setText(item.getRecordId());
        }
    }
} public class EventLogCardInflater implements IAdapterViewInflater<EventLogItemData> {

    @Override
    public View inflate(final BaseInflaterAdapter<EventLogItemData> adapter,
                        final int pos, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.eventlog_items, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final EventLogItemData item = adapter.getTItem(pos);
        holder.updateDisplay(item);

        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;
        private final TextView mtime;
        private final TextView mLAC;
        private final TextView mCID;
        private final TextView mPSC;
        private final TextView mgpsd_lat;
        private final TextView mgpsd_lon;
        private final TextView mgpsd_accu;
        private final TextView mDF_id;
        private final TextView mDF_desc;

        private final TextView mRecordId;
        private final TextView mExample;

        ViewHolder(View rootView) {
            mRootView = rootView;

            mtime =         (TextView) mRootView.findViewById(R.id.time);
            mLAC =          (TextView) mRootView.findViewById(R.id.LAC);
            mCID =          (TextView) mRootView.findViewById(R.id.CID);
            mPSC =          (TextView) mRootView.findViewById(R.id.PSC);
            mgpsd_lat =     (TextView) mRootView.findViewById(R.id.gpsd_lat);
            mgpsd_lon =     (TextView) mRootView.findViewById(R.id.gpsd_lon);
            mgpsd_accu =    (TextView) mRootView.findViewById(R.id.gpsd_accu);
            mDF_id =        (TextView) mRootView.findViewById(R.id.DF_id);
            mDF_desc =      (TextView) mRootView.findViewById(R.id.DF_desc);

            mRecordId =     (TextView) mRootView.findViewById(R.id.record_id);
            mExample =      (TextView) mRootView.findViewById(R.id.example);

            rootView.setTag(this);
        }

        public void updateDisplay(EventLogItemData item) {
            mtime.setText(item.getTimestamp());          // need fix ?
            mLAC.setText(item.getLac());
            mCID.setText(item.getCellID());
            mPSC.setText(item.getPsc());
            mgpsd_lat.setText(item.getLat());
            mgpsd_lon.setText(item.getLng());
            mgpsd_accu.setText(item.getgpsd_accu());
            mDF_id.setText(item.getDF_id());
            mDF_desc.setText(item.getDF_desc());

            mRecordId.setText(item.getRecordId());
            if (item.isFakeData()) {
                mExample.setText(mRootView.getContext().getString(R.string.example));
                mExample.setVisibility(View.VISIBLE);
            } else {
                mExample.setVisibility(View.GONE);
            }
        }
    }
} public class EventLogItemData {
    // OLD (in old DB tables)
    private final String mTimestamp;
    private final String mCellID;
    private final String mLac;
    private final String mPsc;
    private final String mLat;
    private final String mLng;
    private final String mgpsd_accu;
    private final String mDF_id;
    private final String mDF_desc;

    private final String mRecordId;
    private boolean mIsFakeData;


    public EventLogItemData(
                String time,
                String LAC,
                String CID,
                String PSC,
                String gpsd_lat,
                String gpsd_lon,
                String gpsd_accu,
                String DF_id,
                String DF_desc,

                String recordId) {
        this(
                time,
                LAC,
                CID,
                PSC,
                gpsd_lat,
                gpsd_lon,
                gpsd_accu,
                DF_id,
                DF_desc,

                recordId,
                false
        );
    }

    public EventLogItemData(String pTime,
                            String pLAC,
                            String pCID,
                            String pPSC,
                            String pGpsd_lat,
                            String pGpsd_lon,
                            String pGpsd_accu,
                            String pDF_id,
                            String pDF_desc,

                            String pRecordId,
                            boolean pIsFakeData) {
        mTimestamp = pTime;
        mLac = pLAC;
        mCellID = pCID;
        mPsc = pPSC;
        mLat = pGpsd_lat;
        mLng = pGpsd_lon;
        mgpsd_accu = pGpsd_accu;
        mDF_id = pDF_id;
        mDF_desc = pDF_desc;

        mRecordId = pRecordId;
        mIsFakeData = pIsFakeData;
    }

    public String getTimestamp() {
        return mTimestamp;
    }

    public String getCellID() {
        return mCellID;
    }

    public String getLac() {
        return mLac;
    }

    public String getPsc() {
        return mPsc;
    }

    public String getLat() {
        return mLat;
    }

    public String getLng() {
        return mLng;
    }

    public String getgpsd_accu() {
        return mgpsd_accu;
    }

    public String getDF_id() {
        return mDF_id;
    }

    public String getDF_desc() {
        return mDF_desc;
    }


    public String getRecordId() {
        return mRecordId;
    }

    public boolean isFakeData() {
        return mIsFakeData;
    }

    public void setIsFakeData(boolean pIsFakeData) {
        mIsFakeData = pIsFakeData;
    }
} public class MeasuredCellStrengthCardData {

    private final String timestamp;
    private final int signal;
    private final int cellID;

    public MeasuredCellStrengthCardData(int cellID, int signal, String timestamp) {
        this.cellID = cellID;
        this.signal = signal;
        this.timestamp = timestamp;
    }


    // Let's try to get it in HEX as well
    public String getCellID() {
        return "CID: " + cellID + "  (0x" + Integer.toHexString(cellID) + ")";
    }

    //original
    // public String getCellID() {
    //    return "CID: "+cellID;
    //}

    // This is probably not dBm but ASU ?
    // Please see: http://wiki.opencellid.org/wiki/API#Filtering_of_data
    public String getSignal() {
        return "RSS: " + signal + " dBm";
    }

    public String getTimestamp() {
        return "Time: " + timestamp;
    }
} public class MeasuredCellStrengthCardInflater implements IAdapterViewInflater<MeasuredCellStrengthCardData> {

    @Override
    public View inflate(final BaseInflaterAdapter<MeasuredCellStrengthCardData> adapter, final int pos,
                        View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.measured_signal_str, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final MeasuredCellStrengthCardData item = adapter.getTItem(pos);
        holder.updateDisplay(item);

        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;

        private final TextView cid;
        private final TextView rss;
        private final TextView time;

        ViewHolder(View rootView) {
            mRootView = rootView;

            cid =   (TextView) mRootView.findViewById(R.id.tv_measure_cid);
            rss =   (TextView) mRootView.findViewById(R.id.tv_measure_rss);
            time =  (TextView) mRootView.findViewById(R.id.tv_measure_time);

            rootView.setTag(this);
        }

        public void updateDisplay(MeasuredCellStrengthCardData item) {
            cid.setText(valueOf(item.getCellID()));
            rss.setText(valueOf(item.getSignal()));
            time.setText(item.getTimestamp());
        }
    }
} public class SilentSmsCardData {

    private final String mAddress;
    private final String mDisplayAddress;
    private final String mMessageClass;
    private final String mServiceCentre;
    private final String mMessage;
    private final long mTimestamp;
    private boolean mIsFakeData;

    public SilentSmsCardData(String address, String displayAddress, String messageClass,
            String serviceCentre, String messageBody, long timestamp) {
        mAddress = address;
        mDisplayAddress = displayAddress;
        mMessageClass = messageClass;
        mServiceCentre = serviceCentre;
        mMessage = messageBody;
        mTimestamp = timestamp;
    }

    public String getAddress() {
        String address = "Address: ";
        if (mAddress.isEmpty()) {
            return address + "Unavailable";
        } else {
            return address + mAddress;
        }
    }

    public String getDisplayAddress() {
        String display = "Display Address: ";
        if (mDisplayAddress.isEmpty()) {
            return display + "Unavailable";
        } else {
            return display + mDisplayAddress;
        }
    }

    public String getMessageClass() {
        String messageClass = "Message Class: ";
        if (messageClass.isEmpty()) {
            return messageClass + "Unavailable";
        } else {
            return messageClass + mMessageClass;
        }
    }

    public String getServiceCentre() {
        String serviceCentre = "Service Centre: ";
        if (mServiceCentre.isEmpty()) {
            return serviceCentre + "Unavailable";
        } else {
            return serviceCentre + mServiceCentre;
        }
    }

    public String getMessage() {
        String message = "Message: ";
        if (message.isEmpty()) {
            return message + "Unavailable";
        } else {
            return message + mMessage;
        }
    }

    public String getTimestamp() {
        SimpleDateFormat formatter = new SimpleDateFormat("dd-MMM-yyyy HH:mm:ss", Locale.getDefault());
        String dateString = formatter.format(mTimestamp);
        return "Timestamp: " + dateString;
    }

    public boolean isFakeData() {
        return mIsFakeData;
    }

    public void setIsFakeData(boolean pIsFakeData) {
        mIsFakeData = pIsFakeData;
    }
} public class UniqueBtsCardInflater implements IAdapterViewInflater<UniqueBtsItemData> {

    @Override
    public View inflate(final BaseInflaterAdapter<UniqueBtsItemData> adapter,
                        final int pos, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.unique_bts_data, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final UniqueBtsItemData item = adapter.getTItem(pos);
        holder.updateDisplay(item);

        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;

        // TODO: Order these and the rest, as in DB table (shown above)
        private final TextView LAC;
        private final TextView CID;
        private final TextView MCC;
        private final TextView MNC;
        private final TextView PSC;

        private final TextView T3212;
        private final TextView A5X;
        private final TextView ST_ID;

        private final TextView TIME_FIRST;
        private final TextView TIME_LAST;
        private final TextView LAT; // These are exact GPS coordinates if found in DBe_import, else "-"
        private final TextView LON; // These are exact GPS coordinates if found in DBe_import, else "-"

        private final TextView RecordId;

        // These are the names of the "@+id/xxxx" items in the XML layout file
        ViewHolder(View rootView) {
            mRootView = rootView;

            LAC =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_lac);
            CID =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_cid);
            MCC =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_mcc);
            MNC =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_mnc);
            PSC =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_psc);
            T3212 =     (TextView) mRootView.findViewById(R.id.tv_uniquebts_t3212);
            A5X =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_a5x);
            ST_ID =     (TextView) mRootView.findViewById(R.id.tv_uniquebts_st_id);
            TIME_FIRST = (TextView) mRootView.findViewById(R.id.tv_uniquebts_time_first);
            TIME_LAST = (TextView) mRootView.findViewById(R.id.tv_uniquebts_time_last);
            LAT =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_lat);
            LON =       (TextView) mRootView.findViewById(R.id.tv_uniquebts_lon);

            RecordId =  (TextView) mRootView.findViewById(R.id.record_id);
            rootView.setTag(this);
        }

        public void updateDisplay(UniqueBtsItemData item) {

            LAC.setText(item.getLac());
            CID.setText(item.getCid());

            MCC.setText(item.getMcc());
            MNC.setText(item.getMnc());
            PSC.setText(item.getPsc());

            // TODO: Get values from DB when available
            T3212.setText("n/a");   //T3212.setText(item.getT3212());
            A5X.setText("n/a");     //A5X.setText(item.getA5x());
            ST_ID.setText("n/a");   //ST_ID.setText(item.getStId());

            TIME_FIRST.setText(item.getTime_first());
            TIME_LAST.setText(item.getTime_last());

            LAT.setText(String.valueOf(item.getLat())); // DBe_import
            LON.setText(String.valueOf(item.getLon())); // DBe_import

            RecordId.setText(item.getRecordId());
        }
    }
} public class UniqueBtsItemData {

    public String getCid() {
        return cid;
    }

    public void setCid(String cid) {
        this.cid = cid;
    }

    public String getLac() {
        return lac;
    }

    public void setLac(String lac) {
        this.lac = lac;
    }

    public String getMcc() {
        return mcc;
    }

    public void setMcc(String mcc) {
        this.mcc = mcc;
    }

    public String getMnc() {
        return mnc;
    }

    public void setMnc(String mnc) {
        this.mnc = mnc;
    }

    public String getTime_first() {
        return time_first;
    }

    public void setTime_first(String time_first) {
        this.time_first = time_first;
    }

    public String getTime_last() {
        return time_last;
    }

    public void setTime_last(String time_last) {
        this.time_last = time_last;
    }

    public String getPsc() {
        return psc;
    }

    public void setPsc(String psc) {
        this.psc = psc;
    }

    // START new ==============================================================
    public String getT3212() {
        return t3212;
    }

    public void setT3212(String t3212) {
        this.t3212 = t3212;
    }

    public String getA5x() {
        return a5x;
    }

    public void setA5x(String a5x) {
        this.a5x = a5x;
    }

    public String getStId() {
        return st_id;
    }

    public void setStId(String st_id) {
        this.st_id = st_id;
    }


    // END new ==============================================================

    public String getLat() {
        return lat;
    }

    public void setLat(String lat) {
        this.lat = lat;
    }

    public String getLon() {
        return lon;
    }

    public void setLon(String lon) {
        this.lon = lon;
    }

    public String getRecordId() {
        return mRecordId;
    }

    String mcc;
    String mnc;
    String lac;
    String cid;
    String psc;
    String t3212;
    String a5x;
    String st_id;
    String time_first;
    String time_last;
    String lat;
    String lon;

    String mRecordId;

    public UniqueBtsItemData(
            String imcc,
            String imnc,
            String ilac,
            String icid,
            String ipsc,
            String itime_first,
            String itime_last,
            String ilat,
            String ilon,
            //String it3212,
            //String ia5x,
            //String ist_id,
            String iRecordId
            ) {

            mcc = imcc;
            mnc = imnc;
            lac = ilac;
            cid = icid;
            psc = ipsc;
            time_first = itime_first;
            time_last = itime_last;
            lat = ilat;
            lon = ilon;
            //t3212 = it3212;
            //a5x = ia5x;
            //st_id = ist_id;
            mRecordId = iRecordId;
    }

} public class DBTableColumnIds {
    //defaultlocation
    public static final String DEFAULT_LOCATION_TABLE_NAME = "defaultlocation";
    public static final String DEFAULT_LOCATION_ID = "_id";
    public static final String DEFAULT_LOCATION_COUNTRY = "country";
    public static final String DEFAULT_LOCATION_MCC = "MCC";
    public static final String DEFAULT_LOCATION_LAT = "lat";
    public static final String DEFAULT_LOCATION_LON = "lon";

    //API_keys
    public static final String API_KEYS_TABLE_NAME = "API_keys";
    public static final String API_KEYS_ID = "_id";
    public static final String API_KEYS_NAME = "name";
    public static final String API_KEYS_TYPE = "type";
    public static final String API_KEYS_KEY = "key";
    public static final String API_KEYS_TIME_ADD = "time_add";
    public static final String API_KEYS_TIME_EXP = "time_exp";

    //CounterMeasures
    public static final String COUNTER_MEASURES_TABLE_NAME = "CounterMeasures";
    public static final String COUNTER_MEASURES_ID = "_id";
    public static final String COUNTER_MEASURES_NAME = "name";
    public static final String COUNTER_MEASURES_DESCRIPTION = "description";
    public static final String COUNTER_MEASURES_THRESH = "thresh";
    public static final String COUNTER_MEASURES_THFINE = "thfine";

    //DBe_capabilities
    public static final String DBE_CAPABILITIES_TABLE_NAME = "DBe_capabilities";
    public static final String DBE_CAPABILITIES_ID = "_id";
    public static final String DBE_CAPABILITIES_MCC = "MCC";
    public static final String DBE_CAPABILITIES_MNC = "MNC";
    public static final String DBE_CAPABILITIES_LAC = "LAC";
    public static final String DBE_CAPABILITIES_OP_NAME = "op_name";
    public static final String DBE_CAPABILITIES_BAND_PLAN = "band_plan";
    public static final String DBE_CAPABILITIES_EXPAND = "__EXPAND__";

    //DBe_import
    public static final String DBE_IMPORT_TABLE_NAME = "DBe_import";
    public static final String DBE_IMPORT_ID = "_id";
    public static final String DBE_IMPORT_DBSOURCE = "DBsource";
    public static final String DBE_IMPORT_RAT = "RAT";
    public static final String DBE_IMPORT_MCC = "MCC";
    public static final String DBE_IMPORT_MNC = "MNC";
    public static final String DBE_IMPORT_LAC = "LAC";
    public static final String DBE_IMPORT_CID = "CID";
    public static final String DBE_IMPORT_PSC = "PSC";
    public static final String DBE_IMPORT_GPS_LAT = "gps_lat";
    public static final String DBE_IMPORT_GPS_LON = "gps_lon";
    public static final String DBE_IMPORT_IS_GPS_EXACT = "isGPSexact";
    public static final String DBE_IMPORT_AVG_RANGE = "avg_range";
    public static final String DBE_IMPORT_AVG_SIGNAL = "avg_signal";
    public static final String DBE_IMPORT_SAMPLES = "samples";
    public static final String DBE_IMPORT_TIME_FIRST = "time_first";
    public static final String DBE_IMPORT_TIME_LAST = "time_last";
    public static final String DBE_IMPORT_REJ_CAUSE = "rej_cause";

    //DBi_bts
    public static final String DBI_BTS_TABLE_NAME = "DBi_bts";
    public static final String DBI_BTS_ID = "_id";
    public static final String DBI_BTS_MCC = "MCC";
    public static final String DBI_BTS_MNC = "MNC";
    public static final String DBI_BTS_LAC = "LAC";
    public static final String DBI_BTS_CID = "CID";
    public static final String DBI_BTS_PSC = "PSC";
    public static final String DBI_BTS_T3212 = "T3212";
    public static final String DBI_BTS_A5X = "A5x";
    public static final String DBI_BTS_ST_ID = "ST_id";
    public static final String DBI_BTS_TIME_FIRST = "time_first";
    public static final String DBI_BTS_TIME_LAST = "time_last";
    public static final String DBI_BTS_LAT = "gps_lat";
    public static final String DBI_BTS_LON = "gps_lon";
    public static final String DBI_BTS_JOINED_RAT = "RAT";

    //DBi_measure
    public static final String DBI_MEASURE_TABLE_NAME = "DBi_measure";
    public static final String DBI_MEASURE_ID = "_id";
    public static final String DBI_MEASURE_BTS_ID = "bts_id";
    public static final String DBI_MEASURE_NC_LIST = "nc_list";
    public static final String DBI_MEASURE_TIME = "time";
    public static final String DBI_MEASURE_GPSD_LAT = "gpsd_lat";
    public static final String DBI_MEASURE_GPSD_LON = "gpsd_lon";
    public static final String DBI_MEASURE_GPSD_ACCURACY = "gpsd_accu";
    public static final String DBI_MEASURE_GPSE_LAT = "gpse_lat";
    public static final String DBI_MEASURE_GPSE_LON = "gpse_lon";
    public static final String DBI_MEASURE_BB_POWER = "bb_power";
    public static final String DBI_MEASURE_BB_RF_TEMP = "bb_rf_temp";
    public static final String DBI_MEASURE_TX_POWER = "tx_power";
    public static final String DBI_MEASURE_RX_SIGNAL = "rx_signal";
    public static final String DBI_MEASURE_RX_STYPE = "rx_stype";
    public static final String DBI_MEASURE_RAT = "RAT";
    public static final String DBI_MEASURE_BCCH = "BCCH";
    public static final String DBI_MEASURE_TMSI = "TMSI";
    public static final String DBI_MEASURE_TA = "TA";
    public static final String DBI_MEASURE_PD = "PD";
    public static final String DBI_MEASURE_BER = "BER";
    public static final String DBI_MEASURE_AVG_EC_NO = "AvgEcNo";
    public static final String DBI_MEASURE_IS_SUBMITTED = "isSubmitted";
    public static final String DBI_MEASURE_IS_NEIGHBOUR = "isNeighbour";
    public static final String DBI_MEASURE_FOREIGN_KEY = "bts_id"; // TODO: Remove?

    //DetectionFlags
    public static final String DETECTION_FLAGS_TABLE_NAME = "DetectionFlags";
    public static final String DETECTION_FLAGS_ID = "_id";
    public static final String DETECTION_FLAGS_CODE = "code";
    public static final String DETECTION_FLAGS_NAME = "name";
    public static final String DETECTION_FLAGS_DESCRIPTION = "description";
    public static final String DETECTION_FLAGS_P1 = "p1";
    public static final String DETECTION_FLAGS_P2 = "p2";
    public static final String DETECTION_FLAGS_P3 = "p3";
    public static final String DETECTION_FLAGS_P1_FINE = "p1_fine";
    public static final String DETECTION_FLAGS_P2_FINE = "p2_fine";
    public static final String DETECTION_FLAGS_P3_FINE = "p3_fine";
    public static final String DETECTION_FLAGS_APP_TEXT = "app_text";
    public static final String DETECTION_FLAGS_FUNC_USE = "func_use";
    public static final String DETECTION_FLAGS_IS_STATUS = "istatus";
    public static final String DETECTION_FLAGS_CM_ID = "CM_id";

    //EventLog
    public static final String EVENTLOG_TABLE_NAME = "EventLog";
    public static final String EVENTLOG_TIME = "time";
    public static final String EVENTLOG_LAC = "LAC";
    public static final String EVENTLOG_CID = "CID";
    public static final String EVENTLOG_PSC = "PSC";
    public static final String EVENTLOG_LAT = "gpsd_lat";
    public static final String EVENTLOG_LON = "gpsd_lon";
    public static final String EVENTLOG_ACCU = "gpsd_accu";
    public static final String EVENTLOG_DF_ID = "DF_id";
    public static final String EVENTLOG_DF_DESC = "DF_description"; // TODO: should be "DF_desc"

    //SectorType
    public static final String SECTOR_TYPE_TABLE_NAME = "SectorType";
    public static final String SECTOR_TYPE_ID = "_id";
    public static final String SECTOR_TYPE_DESCRIPTION = "description";

    //DetectionStrings  (For SMS)
    public static final String DETECTION_STRINGS_TABLE_NAME = "DetectionStrings";
    public static final String DETECTION_STRINGS_ID = "_id";
    public static final String DETECTION_STRINGS_LOGCAT_STRING = "det_str";
    public static final String DETECTION_STRINGS_SMS_TYPE = "sms_type";

    //SmsData  (For SMS)
    public static final String SMS_DATA_TABLE_NAME = "SmsData";
    public static final String SMS_DATA_ID = "_id";
    public static final String SMS_DATA_TIMESTAMP = "time";
    public static final String SMS_DATA_SENDER_NUMBER = "number";
    public static final String SMS_DATA_SENDER_SMSC = "smsc";
    public static final String SMS_DATA_SENDER_MSG = "message";
    public static final String SMS_DATA_SMS_TYPE = "type";
    public static final String SMS_DATA_SMS_CLASS = "class";
    public static final String SMS_DATA_LAC = "lac";
    public static final String SMS_DATA_CID = "cid";
    public static final String SMS_DATA_RAT = "rat";
    public static final String SMS_DATA_GPS_LAT = "gps_lat";
    public static final String SMS_DATA_GPS_LON = "gps_lon";
    public static final String SMS_DATA_ROAM_STATE = "isRoaming";


} public class Examples {

    /**
     *  Constants of examples for EventLogItemData
     */
    public static class EVENT_LOG_DATA {
        public static final String LAC = "12345";
        public static final String CID = "543210";
        public static final String PSC = "111";
        public static final String GPSD_LAT = "54.6";
        public static final String GPSD_LON = "25.2";
        public static final String GPSD_ACCU = "100";
        public static final String DF_ID = "2";
    }

    /**
     * Constants of examples for SilentSmsCardData
     */
    public static class SILENT_SMS_CARD_DATA {
        public static final String ADDRESS = "ADREZZ";
        public static final String DISPLAY = "DizzPlay";
    }


} public enum StatesDbViewer {

    UNIQUE_BTS_DATA(R.string.unique_bts_data),
    BTS_MEASUREMENTS(R.string.bts_measurements),
    IMPORTED_OCID_DATA(R.string.imported_ocid_data),
    DEFAULT_MCC_LOCATIONS(R.string.default_mmc_locations),
    SILENT_SMS(R.string.silent_sms),
    MEASURED_SIGNAL_STRENGTHS(R.string.measured_signal_strengths),
    EVENT_LOG(R.string.eventlog),
    DETECTION_STRINGS(R.string.detection_strings);
    //TODO DetectionFlags
    // DETECTION_FLAGS(R.string.detection_flags)

    private final int mStatementValue;

    StatesDbViewer(int pStatementValue) {
        mStatementValue = pStatementValue;
    }

    public int getStatementValue() {
        return mStatementValue;
    }


    public static List<StatesDbViewer> getStates() {
        return new ArrayList<>(Arrays.asList(values()));
    }

    public static StatesDbViewer getValueByOrdinal(int pOrdinal) {
        StatesDbViewer lResult = null;
        for (StatesDbViewer item : values()) {
            if (item.ordinal() == pOrdinal) {
                lResult = item;
                break;
            }
        }
        return lResult;
    }

    public String getDisplayName(Context pContext) {
        if (pContext == null) {
            return null;
        }
        return pContext.getString(getStatementValue());
    }

} public class CapturedSmsCardInflater implements IAdapterViewInflater<CapturedSmsData> {

    @Override
    public View inflate(final BaseInflaterAdapter<CapturedSmsData> adapter,
                        final int pos, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.detection_sms_db_listview, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final CapturedSmsData item = adapter.getTItem(pos);
        holder.updateDisplay(item);

        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;
        private final TextView smsd_timestamp, smsd_smstype, smsd_number, smsd_data,
                smsd_lac, smsd_cid, smsd_rat, smsd_roam, smsd_lat, smsd_lon;



        ViewHolder(View rootView) {
            mRootView = rootView;
            //
            smsd_timestamp = (TextView) mRootView.findViewById(R.id.tv_smsdata_timestamp);
            smsd_smstype = (TextView) mRootView.findViewById(R.id.tv_smsdata_smstype);
            smsd_number = (TextView) mRootView.findViewById(R.id.tv_smsdata_number);
            smsd_data = (TextView) mRootView.findViewById(R.id.tv_smsdata_msg);
            smsd_lac = (TextView) mRootView.findViewById(zz.aimsicd.lite.R.id.tv_smsdata_lac);
            smsd_cid = (TextView) mRootView.findViewById(R.id.tv_smsdata_cid);
            smsd_rat = (TextView) mRootView.findViewById(R.id.tv_smsdata_nettype);
            smsd_roam = (TextView) mRootView.findViewById(R.id.tv_smsdata_roaming);
            smsd_lat = (TextView) mRootView.findViewById(R.id.tv_smsdata_lat);
            smsd_lon = (TextView) mRootView.findViewById(R.id.tv_smsdata_lon);


            rootView.setTag(this);
        }

        public void updateDisplay(CapturedSmsData item) {
            smsd_timestamp.setText(item.getSmsTimestamp());
            smsd_smstype.setText(item.getSmsType());
            smsd_number.setText(item.getSenderNumber());
            smsd_data.setText(item.getSenderMsg());
            smsd_lac.setText(SV(item.getCurrent_lac()));
            smsd_cid.setText(SV(item.getCurrent_cid()));
            smsd_rat.setText(item.getCurrent_nettype());
            String isRoaming = "false";
            if (item.getCurrent_roam_status() == 1) {
                isRoaming = "true";
            }
            smsd_roam.setText(isRoaming);
            smsd_lat.setText(String.valueOf(item.getCurrent_gps_lat()));
            smsd_lon.setText(String.valueOf(item.getCurrent_gps_lon()));


        }
    }

    public String SV(int value) {
        return String.valueOf(value);
    }
} public class CapturedSmsData {

    private long id;
    private String senderNumber;
    private String senderMsg;
    private String smsTimestamp;
    private String smsType;
    private int current_lac;
    private int current_cid;
    private String current_nettype;
    private int current_roam_status;
    private double current_gps_lat;
    private double current_gps_lon;

    public String getCurrent_nettype() {
        return current_nettype;
    }

    public void setCurrent_nettype(String current_nettype) {
        this.current_nettype = current_nettype;
    }

    public int getCurrent_roam_status() {
        return current_roam_status;
    }

    public void setCurrent_roam_status(int current_roam_status) {
        this.current_roam_status = current_roam_status;
    }


    public double getCurrent_gps_lat() {
        return current_gps_lat;
    }

    public void setCurrent_gps_lat(double current_gps_lat) {
        this.current_gps_lat = current_gps_lat;
    }

    public double getCurrent_gps_lon() {
        return current_gps_lon;
    }

    public void setCurrent_gps_lon(double current_gps_lon) {
        this.current_gps_lon = current_gps_lon;
    }


    public CapturedSmsData() {

    }

    public int getCurrent_lac() {
        return current_lac;
    }

    public void setCurrent_lac(int current_lac) {
        this.current_lac = current_lac;
    }

    public int getCurrent_cid() {
        return current_cid;
    }

    public void setCurrent_cid(int current_cid) {
        this.current_cid = current_cid;
    }

    public String getSenderNumber() {
        return senderNumber;
    }

    public void setSenderNumber(String senderNumber) {
        this.senderNumber = senderNumber;
    }

    public String getSenderMsg() {
        return senderMsg;
    }

    public void setSenderMsg(String senderMsg) {
        this.senderMsg = senderMsg;
    }

    public String getSmsTimestamp() {
        return smsTimestamp;
    }

    public void setSmsTimestamp(String smsTimestamp) {
        this.smsTimestamp = smsTimestamp;
    }

    public String getSmsType() {
        return smsType;
    }

    public void setSmsType(String smsType) {
        this.smsType = smsType;
    }


    public long getId() {
        return id;
    }

    public void setId(long id) {
        this.id = id;
    }
} public class DetectionStringsCardInflater implements IAdapterViewInflater<DetectionStringsData> {

    @Override
    public View inflate(final BaseInflaterAdapter<DetectionStringsData> adapter,
                        final int pos, View convertView, ViewGroup parent) {
        ViewHolder holder;

        if (convertView == null) {
            LayoutInflater inflater = LayoutInflater.from(parent.getContext());
            convertView = inflater.inflate(R.layout.detection_strings_items, parent, false);
            holder = new ViewHolder(convertView);
        } else {
            holder = (ViewHolder) convertView.getTag();
        }

        final DetectionStringsData item = adapter.getTItem(pos);
        holder.updateDisplay(item);

        return convertView;
    }

    private class ViewHolder {

        private final View mRootView;
        private final TextView d_string;
        private final TextView d_type;


        ViewHolder(View rootView) {
            mRootView = rootView;
            //
            d_string =         (TextView) mRootView.findViewById(zz.aimsicd.lite.R.id.tv_det_str_info);
            d_type =          (TextView) mRootView.findViewById(R.id.tv_det_type_info);

            rootView.setTag(this);
        }

        public void updateDisplay(DetectionStringsData item) {
            d_string.setText(item.getDetectionString());
            d_type.setText(item.getDetectionType());
        }
    }
} public class DetectionStringsData {

    private final String mDetection_string;
    private final String mDetection_type;
    private boolean mIsFakeData;




    public DetectionStringsData(String detection_string,
                                String detection_type) {
        this(detection_string,
                detection_type,
                false);
    }

    public DetectionStringsData(String pString,
                                String pType,
                                boolean pIsFakeData) {
        mDetection_string = pString;
        mDetection_type = pType;

        mIsFakeData = pIsFakeData;
    }

    public String getDetectionString() {
        return mDetection_string;
    }

    public String getDetectionType() {
        return mDetection_type;
    }

    public boolean isFakeData() {
        return mIsFakeData;
    }

    public void setIsFakeData(boolean pIsFakeData) {
        mIsFakeData = pIsFakeData;
    }
} public class Cell implements Parcelable {

    public static final String TAG = "AICDL";
    public static final String mTAG = "XXX";

    public static final String INVALID_PSC = "invalid";

    // Cell Specific Variables
    private int cid;                // Cell Identification code
    private int lac;                // Location Area Code
    private int mcc;                // Mobile Country Code
    private int mnc;                // Mobile Network Code
    private int dbm;                // [dBm] RX signal "power"
    private int psc;                // Primary Scrambling Code
    private int rssi;               // Relative Signal Strength Indicator [dBm, asu etc.]
    private int timingAdvance;      // Timing Advance [LTE,GSM]
    private int sid;                // Cell-ID for [CDMA]
    private long timestamp;         // time

    // Tracked Cell Specific Variables
    private int netType;
    private double speed;       //
    private double accuracy;    //
    private double bearing;     //
    private double lon;
    private double lat;

    {
        cid = Integer.MAX_VALUE;
        lac = Integer.MAX_VALUE;
        mcc = Integer.MAX_VALUE;
        mnc = Integer.MAX_VALUE;
        dbm = Integer.MAX_VALUE;
        psc = Integer.MAX_VALUE;
        rssi = Integer.MAX_VALUE;
        timingAdvance = Integer.MAX_VALUE;
        sid = Integer.MAX_VALUE;
        netType = Integer.MAX_VALUE;
        lon = 0.0;
        lat = 0.0;
        speed = 0.0;
        accuracy = 0.0;
        bearing = 0.0;
    }

    public Cell() {
    }

    public Cell(int cid, int lac, int mcc, int mnc, int dbm, long timestamp) {
        super();
        this.cid = cid;
        this.lac = lac;
        this.mcc = mcc;
        this.mnc = mnc;
        this.dbm = dbm;
        this.rssi = Integer.MAX_VALUE;
        this.psc = Integer.MAX_VALUE;
        this.timestamp = timestamp;
        this.timingAdvance = Integer.MAX_VALUE;
        this.sid = Integer.MAX_VALUE;
        this.netType = Integer.MAX_VALUE;
        this.lon = 0.0;
        this.lat = 0.0;
        this.speed = 0.0;
        this.accuracy = 0.0;
        this.bearing = 0.0;
    }

    public Cell(int cid, int lac, int signal, int psc, int netType, boolean dbm) {
        this.cid = cid;
        this.lac = lac;
        this.mcc = Integer.MAX_VALUE;
        this.mnc = Integer.MAX_VALUE;

        if (dbm) {
            this.dbm = signal;
        } else {
            this.rssi = signal;
        }
        this.psc = psc;

        this.netType = netType;
        this.timingAdvance = Integer.MAX_VALUE;
        this.sid = Integer.MAX_VALUE;
        this.lon = 0.0;
        this.lat = 0.0;
        this.speed = 0.0;
        this.accuracy = 0.0;
        this.bearing = 0.0;
        this.timestamp = SystemClock.currentThreadTimeMillis();
    }

    public Cell(int cid, int lac, int mcc, int mnc, int dbm, double accuracy, double speed,
                double bearing, int netType, long timestamp) {
        this.cid = cid;
        this.lac = lac;
        this.mcc = mcc;
        this.mnc = mnc;
        this.dbm = dbm;
        this.rssi = Integer.MAX_VALUE;
        this.timingAdvance = Integer.MAX_VALUE;
        this.sid = Integer.MAX_VALUE;
        this.accuracy = accuracy;
        this.speed = speed;
        this.bearing = bearing;
        this.netType = netType;
        this.timestamp = timestamp;
    }

    /**
     * Current Cell ID
     *
     * @return int representing the Cell ID from GSM or CDMA devices
     */
    public int getCID() {
        return this.cid;
    }

    /**
     * Sets current Cell ID
     *
     * @param cid Cell ID
     */
    public void setCID(int cid) {
        this.cid = cid;
    }

    /**
     * Location Area Code (LAC) of current Cell
     *
     * @return int Current cells Location Area Code
     */
    public int getLAC() {
        return this.lac;
    }

    /**
     * Set Location Area Code (LAC) of current Cell
     *
     * @param lac Location Area Code
     */
    public void setLAC(int lac) {
        this.lac = lac;
    }

    /**
     * Mobile Country Code (Mcc) of current Cell
     *
     * @return int Current cells Mobile Country Code
     */
    public int getMCC() {
        return this.mcc;
    }

    /**
     * Set Mobile Country Code (Mcc) of current Cell
     *
     * @param mcc Mobile Country Code
     */
    public void setMCC(int mcc) {
        this.mcc = mcc;
    }

    /**
     * Mobile Network Code (Mnc) of current Cell
     *
     * @return int Current cells Mobile Network Code
     */
    public int getMNC() {
        return this.mnc;
    }

    /**
     * Set Mobile Network Code (Mnc) of current Cell
     *
     * @param mnc Mobile Network Code
     */
    public void setMNC(int mnc) {
        this.mnc = mnc;
    }

    /**
     * Primary Scrambling Code (PSC) of current Cell
     *
     * @return int Current cells Primary Scrambling Code
     */
    public int getPSC() {
        return this.psc;
    }

    /**
     * Set Primary Scrambling Code (PSC) of current Cell
     *
     * @param psc Primary Scrambling Code
     */
    public void setPSC(int psc) {
        if (psc == -1) {
            this.psc = Integer.MAX_VALUE;
        } else {
            this.psc = psc;
        }
    }

    /**
     * Radio Access Technology (RAT)
     *
     * Some places in the app refers to this as the Network Type.
     *
     * For our purposes, network types displayed to the user is referred to as RAT.
     *
     * @return Current cell's Radio Access Technology (e.g. UMTS, GSM) or null if not known
     */
    public String getRAT() {
        return getRatFromInt(this.netType);
    }

    /**
     * CDMA System ID
     *
     * @return System ID or Integer.MAX_VALUE if not supported
     */
    public int getSID() {
        return this.sid;
    }

    /**
     * Set CDMA System ID (SID) of current Cell
     *
     * @param sid CDMA System ID
     */
    public void setSID(int sid) {
        this.sid = sid;
    }

    /**
     * Signal Strength Measurement (dBm)
     *
     * @return Signal Strength Measurement (dBm)
     */
    public int getDBM() {
        return dbm;
    }

    /**
     * Set Signal Strength (dBm) of current Cell
     *
     * @param dbm Signal Strength (dBm)
     */
    public void setDBM(int dbm) {
        this.dbm = dbm;
    }

    /**
     * Longitude Geolocation of current Cell
     *
     * @return Longitude
     */
    public double getLon() {
        return this.lon;
    }

    /**
     * Set Longitude Geolocation of current Cell
     *
     * @param lon Longitude
     */
    public void setLon(double lon) {
        this.lon = lon;
    }

    /**
     * Latitude Geolocation of current Cell
     *
     * @return Latitude
     */
    public double getLat() {
        return this.lat;
    }

    /**
     * Set Latitude Geolocation of current Cell
     *
     * @param lat Latitude
     */
    public void setLat(double lat) {
        this.lat = lat;
    }

    /**
     * Ground speed in metres/second
     *
     * @return Ground speed or 0.0 if unavailable
     */
    public double getSpeed() {
        return this.speed;
    }

    /**
     * Set current ground speed in metres/second
     *
     * @param speed Ground Speed
     */
    public void setSpeed(double speed) {
        this.speed = speed;
    }

    /**
     * Accuracy of location in metres
     *
     * @return Location accuracy in metres or 0.0 if unavailable
     */
    public double getAccuracy() {
        return this.accuracy;
    }

    /**
     * Set current location accuracy in metres
     *
     * @param accuracy Location accuracy
     */
    public void setAccuracy(double accuracy) {
        this.accuracy = accuracy;
    }

    /**
     * Set current Bearing
     *
     * @param bearing Current bearing
     */
    public void setBearing(double bearing) {
        this.bearing = bearing;
    }

    /**
     * LTE Timing Advance
     *
     * @return LTE Timing Advance or Integer.MAX_VALUE if unavailable
     */
    public int getTimingAdvance() {
        return this.timingAdvance;
    }

    /**
     * Set current LTE Timing Advance
     *
     * @param ta Current LTE Timing Advance
     */
    public void setTimingAdvance(int ta) {
        this.timingAdvance = ta;
    }

    /**
     * Network Type
     *
     * @return string representing device Network Type
     */
    public int getNetType() {
        return this.netType;
    }

    /**
     * Set current Network Type
     *
     * @param netType Current Network Type
     */
    public void setNetType(int netType) {
        this.netType = netType;
    }

    /**
     * Timestamp of current cell information
     *
     * @return Timestamp
     */
    public long getTimestamp() {
        return this.timestamp;
    }

    /**
     * Set current cell information timestamp
     *
     * @param timestamp Current cell information timestamp
     */
    public void setTimestamp(long timestamp) {
        this.timestamp = timestamp;
    }

    /**
     * Received Signal Strength
     *
     * @return Received Signal Strength Indicator (RSSI)
     */
    public int getRssi() {
        return this.rssi;
    }

    @Override
    public int hashCode() {
        final int prime = 31;
        int result = 1;
        result = prime * result + cid;
        result = prime * result + lac;
        result = prime * result + mcc;
        result = prime * result + mnc;
        if (psc != -1) {
            result = prime * result + psc;
        }
        return result;
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj) {
            return true;
        }
        if (obj == null) {
            return false;
        }
        if (((Object) this).getClass() != obj.getClass()) {
            return false;
        }
        Cell other = (Cell) obj;
        if (this.psc != Integer.MAX_VALUE) {
            return this.cid == other.getCID() && this.lac == other.getLAC() && this.mcc == other
                    .getMCC() && this.mnc == other.getMNC() && this.psc == other.getPSC();
        } else {
            return this.cid == other.getCID() && this.lac == other.getLAC() && this.mcc == other
                    .getMCC() && this.mnc == other.getMNC();
        }
    }

    public String toString() {
        StringBuilder result = new StringBuilder();

        result.append("CID - ").append(cid).append("\n");
        result.append("LAC - ").append(lac).append("\n");
        result.append("MCC - ").append(mcc).append("\n");
        result.append("MNC - ").append(mnc).append("\n");
        result.append("DBm - ").append(dbm).append("\n");
        result.append("PSC - ").append(validatePscValue(psc)).append("\n");
        result.append("Type - ").append(netType).append("\n");
        result.append("Lon - ").append(lon).append("\n");
        result.append("Lat - ").append(lat).append("\n");

        return result.toString();
    }

    public boolean isValid() {
        return this.getCID() != Integer.MAX_VALUE && this.getLAC() != Integer.MAX_VALUE;
    }

    /**
     * Get a human-readable string of RAT/Network Type
     *
     * Frustratingly it looks like the app uses RAT & Network Type interchangably with both either
     * being an integer representation (TelephonyManager's constants) or a human-readable string.
     *
     * @param netType The integer representation of the network type, via TelephonyManager
     * @return Human-readable representation of network type (e.g. "EDGE", "LTE")
     */
    public static String getRatFromInt(int netType) {
        switch (netType) {
            case TelephonyManager.NETWORK_TYPE_1xRTT:
                return "1xRTT";
            case TelephonyManager.NETWORK_TYPE_CDMA:
                return "CDMA";
            case TelephonyManager.NETWORK_TYPE_EDGE:
                return "EDGE";
            case TelephonyManager.NETWORK_TYPE_EHRPD:
                return "eHRPD";
            case TelephonyManager.NETWORK_TYPE_EVDO_0:
                return "EVDO rev. 0";
            case TelephonyManager.NETWORK_TYPE_EVDO_A:
                return "EVDO rev. A";
            case TelephonyManager.NETWORK_TYPE_EVDO_B:
                return "EVDO rev. B";
            case TelephonyManager.NETWORK_TYPE_GPRS:
                return "GPRS";
            case TelephonyManager.NETWORK_TYPE_HSDPA:
                return "HSDPA";
            case TelephonyManager.NETWORK_TYPE_HSPA:
                return "HSPA";
            case TelephonyManager.NETWORK_TYPE_HSPAP:
                return "HSPA+";
            case TelephonyManager.NETWORK_TYPE_HSUPA:
                return "HSUPA";
            case TelephonyManager.NETWORK_TYPE_IDEN:
                return "iDen";
            case TelephonyManager.NETWORK_TYPE_LTE:
                return "LTE";
            case TelephonyManager.NETWORK_TYPE_UMTS:
                return "UMTS";
            case TelephonyManager.NETWORK_TYPE_UNKNOWN:
                return "Unknown";
            default:
                return String.valueOf(netType);
        }
    }

    public static String validatePscValue(Context c, String psc) {
        return validatePscValue(c, Integer.parseInt(psc));
    }

    /**
     * Validate PSC is in bounds, return i18n'd "Unknown" if invalid
     *
     * @see #validatePscValue(int)
     *
     * @param c Used for getString translations
     * @param psc
     * @return PSC or "Unknown "if invalid
     */
    public static String validatePscValue(Context c, int psc) {
        String s = validatePscValue(psc);
        if (s.equals(INVALID_PSC)) {
            return c.getString(R.string.unknown);
        }
        return s;
    }

    public static String validatePscValue(String psc) {
        return validatePscValue(Integer.parseInt(psc));
    }

    /**
     * Validate PSC is in bounds
     *
     * Database import stores cell's PSC as "666" if its absent in OCID. This method will return
     * "invalid" instead.
     *
     * Use this method to translate/i18n a cell's missing PSC value.
     *
     * @param psc
     * @return PSC or "invalid" untranslated string if invalid
     */
    public static String validatePscValue(int psc) {
        if (psc < 0 || psc > 511) {
            return INVALID_PSC;
        }
        return String.valueOf(psc);
    }

    // Parcelling
    public Cell(Parcel in) {
        String[] data = new String[15];

        in.readStringArray(data);
        cid = Integer.valueOf(data[0]);
        lac = Integer.valueOf(data[1]);
        mcc = Integer.valueOf(data[2]);
        mnc = Integer.valueOf(data[3]);
        dbm = Integer.valueOf(data[4]);
        psc = Integer.valueOf(data[5]);
        rssi = Integer.valueOf(data[6]);
        timingAdvance = Integer.valueOf(data[7]);
        sid = Integer.valueOf(data[8]);
        netType = Integer.valueOf(data[9]);
        lon = Double.valueOf(data[10]);
        lat = Double.valueOf(data[11]);
        speed = Double.valueOf(data[12]);
        accuracy = Double.valueOf(data[13]);
        bearing = Double.valueOf(data[14]);
    }

    public int describeContents() {
        return 0;
    }

    @Override
    public void writeToParcel(Parcel dest, int flags) {
        dest.writeStringArray(new String[]{
                String.valueOf(this.cid),
                String.valueOf(this.lac),
                String.valueOf(this.mcc),
                String.valueOf(this.mnc),
                String.valueOf(this.dbm),
                String.valueOf(this.psc),
                String.valueOf(this.rssi),
                String.valueOf(this.timingAdvance),
                String.valueOf(this.sid),
                String.valueOf(this.netType),
                String.valueOf(this.lon),
                String.valueOf(this.lat),
                String.valueOf(this.speed),
                String.valueOf(this.accuracy),
                String.valueOf(this.bearing)});
    }

    public static final Parcelable.Creator CREATOR = new Parcelable.Creator() {
        public Cell createFromParcel(Parcel in) {
            return new Cell(in);
        }

        public Cell[] newArray(int size) {
            return new Cell[size];
        }
    };
}
import android.app.Activity;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ListView;
import android.widget.Spinner;
import android.support.v4.app.ActivityCompat;
import zz.aimsicd.lite.R;
import zz.aimsicd.lite.adapters.AIMSICDDbAdapter;
import zz.aimsicd.lite.adapters.BaseInflaterAdapter;
import zz.aimsicd.lite.adapters.BtsMeasureCardInflater;
import zz.aimsicd.lite.adapters.BtsMeasureItemData;
import zz.aimsicd.lite.adapters.CardItemData;
import zz.aimsicd.lite.adapters.DbViewerSpinnerAdapter;
import zz.aimsicd.lite.adapters.DbeImportCardInflater;
import zz.aimsicd.lite.adapters.DbeImportItemData;
import zz.aimsicd.lite.adapters.DefaultLocationCardInflater;
import zz.aimsicd.lite.adapters.EventLogCardInflater;
import zz.aimsicd.lite.adapters.EventLogItemData;
import zz.aimsicd.lite.adapters.MeasuredCellStrengthCardData;
import zz.aimsicd.lite.adapters.MeasuredCellStrengthCardInflater;
import zz.aimsicd.lite.adapters.SilentSmsCardData;
import zz.aimsicd.lite.adapters.UniqueBtsCardInflater;
import zz.aimsicd.lite.adapters.UniqueBtsItemData;
import zz.aimsicd.lite.constants.DBTableColumnIds;
import zz.aimsicd.lite.constants.Examples;
import zz.aimsicd.lite.enums.StatesDbViewer;
import zz.aimsicd.lite.smsdetection.CapturedSmsCardInflater;
import zz.aimsicd.lite.smsdetection.CapturedSmsData;
import zz.aimsicd.lite.smsdetection.DetectionStringsCardInflater;
import zz.aimsicd.lite.smsdetection.DetectionStringsData;
import zz.aimsicd.lite.utils.Cell;
import io.freefair.android.injection.annotation.InjectView;
import io.freefair.android.injection.annotation.XmlLayout;
import io.freefair.android.injection.app.InjectionFragment;
package zz.aimsicd.lite.ui.fragments;




// TODO: remove this crap

/**
 * Description:    Class that handles the display of the items in the 'Database Viewer' (DBV)
 *
 * Issues:
 *
 * Notes:          See issue #234 for details on how to format the UI
 */
@XmlLayout(zz.aimsicd.lite.R.layout.fragment_db_viewer)
public final class DbViewerFragment extends InjectionFragment {

    private AIMSICDDbAdapter mDb;
    private StatesDbViewer mTableSelected;

    @InjectView(zz.aimsicd.lite.R.id.table_spinner)
    private Spinner tblSpinner;

    @InjectView(R.id.list_view)
    private ListView lv;

    @InjectView(zz.aimsicd.lite.R.id.db_list_empty)
    private View emptyView;

    public DbViewerFragment() {
    }

    // Todo: Let me try to fix this deprecation...
    @Override
    public void onAttach(Context activity) {
        super.onAttach(activity);
        mDb = new AIMSICDDbAdapter(activity);

    //public void onAttach(Activity activity) {
    //    super.onAttach(activity);
    //    mDb = new AIMSICDDbAdapter(activity.getBaseContext());
    }


    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        DbViewerSpinnerAdapter mSpinnerAdapter = new DbViewerSpinnerAdapter(getActivity(), zz.aimsicd.lite.R.layout.item_spinner_db_viewer);
        tblSpinner.setAdapter(mSpinnerAdapter);
        tblSpinner.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
            @Override
            public void onItemSelected(AdapterView<?> parentView, View selectedItemView, final int position, long id) {
                Object selectedItem = tblSpinner.getSelectedItem();
                if (!(selectedItem instanceof StatesDbViewer)) {
                    return;
                }
                mTableSelected = (StatesDbViewer) selectedItem;
                new AsyncTask<Void, Void, BaseInflaterAdapter>() {

                    @Override
                    protected BaseInflaterAdapter doInBackground(Void... params) {
                        //# mDb.open();
                        Cursor result;

                        switch (position) {
                            case 0: // UNIQUE_BTS_DATA          ("DBi_bts")
                                // The unique BTSs we have been connected to in the past
                                result = mDb.returnDBiBts();
                                break;

                            case 1: // BTS_MEASUREMENTS:        ("DBi_measure")
                                // All BTS measurements we have done since start
                                result = mDb.returnDBiMeasure();
                                break;

                            case 2: // IMPORTED_OCID_DATA:      ("DBe_import")
                                // All Externally imported BTS data (from OCID, MLS, etc)
                                result = mDb.returnDBeImport();
                                break;

                            case 3: // DEFAULT_MCC_LOCATIONS:   ("defaultlocation")
                                // Default MCC location codes and approx corresponding GPS locations
                                result = mDb.returnDefaultLocation();
                                break;

                            case 4: // SILENT_SMS:              ("SmsData")
                                // SMS log data, such as SMSC, type, etc
                                result = mDb.returnSmsData();
                                break;

                            case 5: // MEASURED_SIGNAL_STRENGTHS: ("DBi_measure")
                                // TODO:     ToBe merged into "DBi_measure:rx_signal"
                                result = mDb.returnDBiMeasure();
                                break;

                            case 6: // EVENT_LOG:               ("EventLog")
                                // Table: "EventLog"
                                result = mDb.returnEventLogData();
                                break;
                            case 7:// SMS DETECTION STRINGS     ("DetectionStrings")
                                result = mDb.returnDetectionStrings();
                                break;

                            // TODO: Not yet implemented...leave as for time being
                            //case 8: // DETECTION_FLAGS:       ("DetectionFlags")
                            //    result = mDb.getDetectionFlagsData();
                            //    break;

                            default:
                                throw new IllegalArgumentException("Unknown type of table");
                        }

                        BaseInflaterAdapter adapter = null;
                        if (result != null) {
                            adapter = BuildTable(result);
                            result.close();
                        }

                        return adapter;
                    }

                    @Override
                    protected void onPostExecute(BaseInflaterAdapter adapter) {
                        if (getActivity() == null) {
                            return; // fragment detached
                        }

                        lv.setEmptyView(emptyView);
                        if (adapter != null) {
                            lv.setAdapter(adapter);
                            lv.setVisibility(View.VISIBLE);
                        } else {
                            lv.setVisibility(View.GONE);
                            emptyView.setVisibility(View.VISIBLE);
                        }

                        // ToDo: 2017-05-04
                        getActivity().setProgressBarIndeterminateVisibility(false);
                        /*getActivity().setSupportProgressBarIndeterminateVisibility(false);
                        getSupportActionBar().setDisplayShowCustomEnabled(true);
                        getSupportActionBar().setCustomView(R.layout.toolbar);*/
                    }
                }.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
            }

            @Override
            public void onNothingSelected(AdapterView<?> parentView) {
            }
        });
    }

    /**
     * Description:     Content layout and presentation of the Database Viewer
     *
     * This is where the text labels are created for each column in
     * the Database Viewer (DBV). For details of how this should be presented, see:
     * https://github.com/SecUpwN/Android-IMSI-Catcher-Detector/issues/234
     *
     * Lat/Lng:     Latitude / Longitude (We should use "Lon" instead of "Lng".)
     * AvgSignal:   Average Signal Strength
     * RSSI:        Received Signal Strength Indicator (previously "Signal Strength")
     * Can have different meanings on different RAN's, e.g. RSCP in UMTS.
     * RAN:         Radio Access Network (GSM, UMTS, LTE etc.)
     *
     * Notes:
     *
     * 1. Although "RAN" is more correct here, we'll use "RAT" (Radio Access Technology),
     * which is the more common terminology. Thus reverting.
     *
     * 2. Since Signal is not an "indicator" we should just call it "RSS" or "RXS"
     */
    private BaseInflaterAdapter BuildTable(Cursor tableData) {
        if (tableData != null && tableData.getCount() > 0) {
            switch (mTableSelected) {

                case UNIQUE_BTS_DATA: {     // DBi_bts

                    BaseInflaterAdapter<UniqueBtsItemData> adapter
                            = new BaseInflaterAdapter<>(new UniqueBtsCardInflater());
                    int count = tableData.getCount();
                    while (tableData.moveToNext()) {
                        UniqueBtsItemData data = new UniqueBtsItemData(
                                String.valueOf(tableData.getInt(tableData.getColumnIndex(DBTableColumnIds.DBI_BTS_MCC))),   // MCC
                                String.valueOf(tableData.getInt(tableData.getColumnIndex(DBTableColumnIds.DBI_BTS_MNC))),   // MNC
                                String.valueOf(tableData.getInt(tableData.getColumnIndex(DBTableColumnIds.DBI_BTS_LAC))),   // LAC
                                String.valueOf(tableData.getInt(tableData.getColumnIndex(DBTableColumnIds.DBI_BTS_CID))),   // CID
                                Cell.validatePscValue(