diff --git a/app/src/main/java/uk/org/openseizuredetector/EventLogManager/EventLogListAdapter.java b/app/src/main/java/uk/org/openseizuredetector/EventLogManager/EventLogListAdapter.java deleted file mode 100644 index 91db4ef..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/EventLogManager/EventLogListAdapter.java +++ /dev/null @@ -1,98 +0,0 @@ -package uk.org.openseizuredetector.EventLogManager; - -import java.util.ArrayList; - -import android.content.Context; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; -import android.widget.BaseAdapter; -import android.widget.ImageView; -import android.widget.TextView; - -import uk.org.openseizuredetector.R; - -public class EventLogListAdapter extends BaseAdapter { - EventLogManager dm; - ArrayList logEntryModelList; - LayoutInflater inflater; - Context _context; - - public EventLogListAdapter(Context context) { - - logEntryModelList = new ArrayList(); - _context = context; - inflater = (LayoutInflater) context - .getSystemService(Context.LAYOUT_INFLATER_SERVICE); - dm = new EventLogManager(_context); - logEntryModelList = dm.getAllData(); - - } - - @Override - public void notifyDataSetChanged() { - super.notifyDataSetChanged(); - //refetching the new data from database - logEntryModelList = dm.getAllData(); - - } - - public void delRow(int delPosition) { - - dm.deleteRow(logEntryModelList.get(delPosition).getId()); - logEntryModelList.remove(delPosition); - - } - - @Override - public int getCount() { - return logEntryModelList.size(); - } - - @Override - public Object getItem(int position) { - return logEntryModelList.get(position); - } - - @Override - public long getItemId(int position) { - return 0; - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - ViewHolder vHolder; - if (convertView == null) { - convertView = inflater.inflate(R.layout.log_entry_layout, null); - vHolder = new ViewHolder(); - - vHolder.date = (TextView) convertView - .findViewById(R.id.event_date); - vHolder.alarmState = (TextView) convertView - .findViewById(R.id.event_alarmState); - vHolder.note = (TextView) convertView - .findViewById(R.id.event_note); - vHolder.dataJSON = (TextView) convertView - .findViewById(R.id.event_dataJSON); - convertView.setTag(vHolder); - } else { - vHolder = (ViewHolder) convertView.getTag(); - } - - LogEntryModel eventObj = logEntryModelList.get(position); - - //vHolder.date.setText(eventObj.getDate().toString()); - vHolder.alarmState.setText(eventObj.getAlarmState()); - vHolder.note.setText(eventObj.getNote()); - vHolder.dataJSON.setText(eventObj.getDataJSON()); - - return convertView; - } - - class ViewHolder { - TextView date,alarmState,note,dataJSON; - } - -} diff --git a/app/src/main/java/uk/org/openseizuredetector/EventLogManager/EventLogManager.java b/app/src/main/java/uk/org/openseizuredetector/EventLogManager/EventLogManager.java deleted file mode 100644 index daebfca..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/EventLogManager/EventLogManager.java +++ /dev/null @@ -1,210 +0,0 @@ -/** - * Database manager for logging events and associated seizure detector data. - */ -package uk.org.openseizuredetector.EventLogManager; - -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Date; -import java.util.Locale; - -import android.content.ContentValues; -import android.content.Context; -import android.database.Cursor; -import android.database.SQLException; -import android.database.sqlite.SQLiteDatabase; -import android.database.sqlite.SQLiteOpenHelper; -import android.util.Log; - -public class EventLogManager { - final static String TAG = "EventLogManager"; - private SQLiteDatabase db; // a reference to the database manager class. - private static final String DB_NAME = "eventlog"; // the name of our database - private static final int DB_VERSION = 1; // the version of the database - - private static final String TABLE_NAME = "events";// table name - - // the names for our database columns - private static final String TABLE_ROW_ID = "_id"; - private static final String TABLE_ROW_DATE = "event_date"; - private static final String TABLE_ROW_ALARM_STATE = "alarm_state"; - private static final String TABLE_ROW_DATA_JSON = "data_json"; - private static final String TABLE_ROW_NOTE = "note"; - private Context context; - - public EventLogManager(Context context) { - this.context = context; - - // create or open the database - CustomSQLiteOpenHelper helper = new CustomSQLiteOpenHelper(context); - this.db = helper.getWritableDatabase(); - - helper.onCreate(this.db); - } - - // the beginnings our SQLiteOpenHelper class - private class CustomSQLiteOpenHelper extends SQLiteOpenHelper { - - public CustomSQLiteOpenHelper(Context context) { - super(context, DB_NAME, null, DB_VERSION); - } - - @Override - public void onCreate(SQLiteDatabase db) { - // the SQLite query string that will create our column database - // table. - String newTableQueryString = "create table " + TABLE_NAME + " (" - + TABLE_ROW_ID - + " integer primary key autoincrement not null," - + TABLE_ROW_DATE + " timestamp not null," + TABLE_ROW_ALARM_STATE - + " integer not null," + TABLE_ROW_NOTE + " text not null," - + TABLE_ROW_DATA_JSON + " text not null" + ");"; - - // execute the query string to the database. - db.execSQL(newTableQueryString); - } - - @Override - public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { - - // LATER, WE WOULD SPECIFIY HOW TO UPGRADE THE DATABASE - // FROM OLDER VERSIONS. - String DROP_TABLE = "DROP TABLE IF EXISTS " + TABLE_NAME; - db.execSQL(DROP_TABLE); - onCreate(db); - - } - - } - - public void addRow(LogEntryModel eventObj) { - ContentValues values = prepareData(eventObj); - // ask the database object to insert the new data - try { - db.insert(TABLE_NAME, null, values); - } catch (Exception e) { - Log.e("DB ERROR", e.toString()); // prints the error message to - // the log - e.printStackTrace(); // prints the stack trace to the log - } - } - - private String getDateTime(Date date) { - SimpleDateFormat dateFormat = new SimpleDateFormat( - "yyyy-MM-dd HH:mm:ss", Locale.getDefault()); - if (date==null) - return ""; - else - return dateFormat.format(date); - } - - private ContentValues prepareData(LogEntryModel eventObj) { - - ContentValues values = new ContentValues(); - values.put(TABLE_ROW_ALARM_STATE, eventObj.getAlarmState()); - values.put(TABLE_ROW_DATE, getDateTime(eventObj.getDate())); - values.put(TABLE_ROW_NOTE, eventObj.getNote()); - values.put(TABLE_ROW_DATA_JSON, eventObj.getDataJSON()); - return values; - } - - // Returns row data in form of LogEntryModel object - public LogEntryModel getRowAsObject(int rowID) { - - LogEntryModel rowContactObj = new LogEntryModel(); - Cursor cursor; - - try { - - cursor = db.query(TABLE_NAME, new String[] { TABLE_ROW_ID, - TABLE_ROW_ALARM_STATE, TABLE_ROW_DATE, TABLE_ROW_NOTE, - TABLE_ROW_DATA_JSON }, TABLE_ROW_ID + "=" + rowID, null, - null, null, null, null); - - cursor.moveToFirst(); - prepareSendObject(rowContactObj, cursor); - - } catch (SQLException e) { - Log.e("DB ERROR", e.toString()); - e.printStackTrace(); - } - - return rowContactObj; - } - - // Returns all the rows data in form of LogEntryModel object list - - public ArrayList getAllData() { - - ArrayList allRowsObj = new ArrayList(); - Cursor cursor; - LogEntryModel rowContactObj; - - String[] columns = new String[] { TABLE_ROW_ID, TABLE_ROW_ALARM_STATE, - TABLE_ROW_DATE, TABLE_ROW_NOTE, TABLE_ROW_DATA_JSON }; - - try { - - cursor = db - .query(TABLE_NAME, columns, null, null, null, null, null); - cursor.moveToFirst(); - - if (!cursor.isAfterLast()) { - do { - rowContactObj = new LogEntryModel(); - rowContactObj.setId(cursor.getInt(0)); - prepareSendObject(rowContactObj, cursor); - allRowsObj.add(rowContactObj); - - } while (cursor.moveToNext()); // try to move the cursor's - // pointer forward one position. - } - } catch (SQLException e) { - Log.e("DB ERROR", e.toString()); - e.printStackTrace(); - } - - return allRowsObj; - - } - - private void prepareSendObject(LogEntryModel rowObj, Cursor cursor) { - rowObj.setId(cursor.getInt(cursor.getColumnIndexOrThrow(TABLE_ROW_ID))); - rowObj.setAlarmState(cursor.getInt(cursor - .getColumnIndexOrThrow(TABLE_ROW_ALARM_STATE))); - String dateTimeStr = cursor.getString(cursor - .getColumnIndexOrThrow(TABLE_ROW_DATE)); - Log.v(TAG,"dateTimeStr = "+dateTimeStr); - Date dateVal; - try { dateVal = new Date(dateTimeStr); } - catch (IllegalArgumentException e) { dateVal = null; } - rowObj.setDate(dateVal); - rowObj.setNote(cursor.getString(cursor - .getColumnIndexOrThrow(TABLE_ROW_NOTE))); - rowObj.setDataJSON(cursor.getString(cursor - .getColumnIndexOrThrow(TABLE_ROW_DATA_JSON))); - } - - public void deleteRow(int rowID) { - // ask the database manager to delete the row of given id - try { - db.delete(TABLE_NAME, TABLE_ROW_ID + "=" + rowID, null); - } catch (Exception e) { - Log.e("DB ERROR", e.toString()); - e.printStackTrace(); - } - } - - public void updateRow(int rowId, LogEntryModel contactObj) { - - ContentValues values = prepareData(contactObj); - - String whereClause = TABLE_ROW_ID + "=?"; - String whereArgs[] = new String[] { String.valueOf(rowId) }; - - db.update(TABLE_NAME, values, whereClause, whereArgs); - - } - - -} diff --git a/app/src/main/java/uk/org/openseizuredetector/EventLogManager/LogEntryModel.java b/app/src/main/java/uk/org/openseizuredetector/EventLogManager/LogEntryModel.java deleted file mode 100644 index 9502239..0000000 --- a/app/src/main/java/uk/org/openseizuredetector/EventLogManager/LogEntryModel.java +++ /dev/null @@ -1,53 +0,0 @@ -package uk.org.openseizuredetector.EventLogManager; - -import java.util.Date; - -/** - * Our LogEntryModel class which will have fields like id, name, contact number - * and email and corresponding getter and setter methods. - * **/ -public class LogEntryModel { - - private int id; - private Date date; - private int alarmState; - private String dataJSON; - private String note; - - - public int getId() { - return id; - } - - public void setId(int id) { - this.id = id; - } - - public int getAlarmState() { - return alarmState; - } - - public void setAlarmState(int alarmState) { - this.alarmState = alarmState; - } - - public String getNote() { - return note; - } - - public void setNote(String note) { - this.note = note; - } - - public Date getDate() { - return date; - } - - public void setDate(Date date) { - this.date = date; - } - - public String getDataJSON() { return dataJSON; } - - public void setDataJSON(String dataJSON) { this.dataJSON = dataJSON; } -} diff --git a/app/src/main/java/uk/org/openseizuredetector/LogManager.java b/app/src/main/java/uk/org/openseizuredetector/LogManager.java index 7c799e1..56c47cb 100644 --- a/app/src/main/java/uk/org/openseizuredetector/LogManager.java +++ b/app/src/main/java/uk/org/openseizuredetector/LogManager.java @@ -45,6 +45,7 @@ import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; +import java.util.HashMap; import static android.database.sqlite.SQLiteDatabase.openOrCreateDatabase; @@ -253,6 +254,25 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface } + /** + * setDatapointStatus() - Update the status of data point id. + * @param id + * @param statusVal + * @return true on success or false on failure + */ + public boolean setDatapointStatus(int id, int statusVal) { + Log.d(TAG,"setDatapointStatus() - id="+id+", statusVal="+statusVal); + Cursor c = null; + ContentValues cv = new ContentValues(); + cv.put("status",statusVal); + int nRowsUpdated = mOSDDb.getWritableDatabase().update(mDbTableName, cv, "id = ?", + new String[]{String.format("%d",id)}); + + return(nRowsUpdated == 1); + + } + + /** * getDatapointsJSON() Returns a JSON Object of all of the datapoints in the local database * between endDateStr-duration and endDateStr @@ -284,6 +304,54 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface } + // Return an array list of objects representing the events in the database. + // Based on https://www.tutlane.com/tutorial/android/android-sqlite-listview-with-examples + public ArrayList> getEventsList(boolean includeWarnings) { + Log.v(TAG,"getEventsList()"); + SQLiteDatabase db = mOSDDb.getWritableDatabase(); + ArrayList> eventsList = new ArrayList<>(); + String statusListStr, sqlStr; + if (includeWarnings) { + statusListStr ="1,2,3,5"; // Warning, Alarm, Fall, Manual Alarm + } else { + statusListStr = "2,3,5"; // Alarm, Fall, Manual Alarm + } + + sqlStr = "SELECT * from "+ mDbTableName + " where Status in ("+statusListStr+") order by dataTime desc;"; + + Cursor cursor = db.rawQuery(sqlStr, null); + Log.v(TAG,"getEventsList - returned "+cursor.getCount()+" records"); + while (cursor.moveToNext()) { + HashMap event = new HashMap<>(); + //event.put("id", cursor.getString(cursor.getColumnIndex("id"))); + event.put("dataTime", cursor.getString(cursor.getColumnIndex("dataTime"))); + int status = cursor.getInt(cursor.getColumnIndex("Status")); + String statusStr = "Unknown"; + switch (status) { + case 1: + statusStr = "WARNING"; + break; + case 2: + statusStr = "ALARM"; + break; + case 3: + statusStr = "FALL"; + break; + case 5: + statusStr = "MANUAL ALARM"; + break; + default: + statusStr = "Unknown"; + } + event.put("status",statusStr); + //event.put("uploaded", cursor.getString(cursor.getColumnIndex("uploaded"))); + //event.put("dataJSON", cursor.getString(cursor.getColumnIndex("dataJSON"))); + eventsList.add(event); + } + Log.v(TAG,"getEventsList() - returning "+eventsList); + return eventsList; + } + /** * pruneLocalDb() removes data that is older than mLocalDbMaxAgeDays days * */ @@ -360,12 +428,12 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface public int getNearestDatapointToDate(String dateStr) { Log.v(TAG, "getNearestDatapointToDate()"); String SQLStr = "SQLStr"; - String recordStr; + //String recordStr; int recordId; try { - SQLStr = "SELECT *, (dataTime-date('"+dateStr+"')) as ddiff from "+ mDbTableName + " order by ABS(ddiff) desc;"; - SQLStr = "SELECT * from "+ mDbTableName + " ;"; + SQLStr = "SELECT *, (julianday(dataTime)-julianday(datetime('"+dateStr+"'))) as ddiff from "+ mDbTableName + " order by ABS(ddiff) asc;"; + //SQLStr = "SELECT * from "+ mDbTableName + " ;"; Cursor resultSet = mOSDDb.getWritableDatabase().rawQuery(SQLStr,null); resultSet.moveToFirst(); if (resultSet.getCount() == 0) { @@ -373,14 +441,14 @@ public class LogManager implements AuthCallbackInterface, EventCallbackInterface recordId = -1; } else { recordId = resultSet.getInt(0); - resultSet.moveToFirst(); - recordStr = cursor2Json(resultSet); //getDatapointById(recordId); - Log.d(TAG, "getNearestDatapointToDate(): id=" + recordId + ", count="+resultSet.getCount()+", recordStr=" + recordStr); + //resultSet.moveToFirst(); + //recordStr = cursor2Json(resultSet); //getDatapointById(recordId); + Log.d(TAG, "getNearestDatapointToDate(): id=" + recordId + ", count="+resultSet.getCount()); } } catch (SQLException e) { Log.e(TAG,"getNearestDatapointToDate(): Error selecting Data: " + e.toString()); Log.e(TAG,"SQLStr was "+SQLStr); - recordStr = "ERROR"; + //recordStr = "ERROR"; recordId = -1; } return (recordId); diff --git a/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java b/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java index d7cb546..8492700 100644 --- a/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/LogManagerControlActivity.java @@ -3,20 +3,29 @@ package uk.org.openseizuredetector; //import androidx.appcompat.app.AppCompatActivity; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.os.Bundle; import android.os.CountDownTimer; +import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; import android.util.Log; import android.view.View; import android.widget.Button; +import android.widget.ListAdapter; +import android.widget.ListView; +import android.widget.SimpleAdapter; import android.widget.TextView; +import java.util.ArrayList; +import java.util.HashMap; + public class LogManagerControlActivity extends AppCompatActivity { private String TAG = "LogManagerControlActivity"; private LogManager mLm; private Context mContext; private UiTimer mUiTimer; + private ArrayList> mEventsList; @Override @@ -32,12 +41,16 @@ public class LogManagerControlActivity extends AppCompatActivity { Button pruneBtn = (Button) findViewById(R.id.pruneDatabaseBtn); pruneBtn.setOnClickListener(onPruneBtn); + Button reportSeizureBtn = + (Button) findViewById(R.id.reportSeizureBtn); + reportSeizureBtn.setOnClickListener(onReportSeizureBtn); Button remoteDbBtn = (Button) findViewById(R.id.view_remote_db_button); remoteDbBtn.setOnClickListener(onRemoteDbBtn); mLm = new LogManager(this); + updateUi(); } @@ -56,7 +69,7 @@ public class LogManagerControlActivity extends AppCompatActivity { private void updateUi() { - Log.v(TAG,"updateUi()"); + //Log.v(TAG,"updateUi()"); TextView tv; Button btn; // Local Database Information @@ -67,6 +80,16 @@ public class LogManagerControlActivity extends AppCompatActivity { int datapointsCount = mLm.getLocalDatapointsCount(); tv.setText(String.format("%d",datapointsCount)); + // Populate events list - we only do it once when the activity is created because the query might slow down the UI. + // We could try this code in updateUI() and see though. + // Based on https://www.tutlane.com/tutorial/android/android-sqlite-listview-with-examples + mEventsList = mLm.getEventsList(true); + ListView lv = (ListView) findViewById(R.id.eventLogListView); + ListAdapter adapter = new SimpleAdapter(LogManagerControlActivity.this, mEventsList, R.layout.log_entry_layout, + new String[]{"dataTime","status"}, + new int[]{R.id.event_date, R.id.event_alarmState}); + lv.setAdapter(adapter); + //Log.v(TAG,"eventsList="+mEventsList); // Remote Database Information @@ -96,9 +119,42 @@ public class LogManagerControlActivity extends AppCompatActivity { @Override public void onClick(View view) { Log.v(TAG, "onPruneBtn"); - mLm.pruneLocalDb(); + AlertDialog.Builder builder = new AlertDialog.Builder(mContext); + + builder.setTitle("Prune Database"); + builder.setMessage("This will remove all data from the database that is more than xxx days old.\nAre you sure?"); + + builder.setPositiveButton("YES", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + mLm.pruneLocalDb(); + dialog.dismiss(); + } + }); + + builder.setNegativeButton("NO", new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialog, int which) { + dialog.dismiss(); + } + }); + + AlertDialog alert = builder.create(); + alert.show(); } }; + + View.OnClickListener onReportSeizureBtn = + new View.OnClickListener() { + @Override + public void onClick(View view) { + Log.v(TAG, "onReportSeizureBtn"); + Intent i; + i =new Intent(mContext, ReportSeizureActivity.class); + startActivity(i); + } + }; + View.OnClickListener onRemoteDbBtn = new View.OnClickListener() { @Override @@ -153,7 +209,7 @@ public class LogManagerControlActivity extends AppCompatActivity { @Override public void onFinish() { - Log.v(TAG, "UiTimer - onFinish - Updating UI"); + //Log.v(TAG, "UiTimer - onFinish - Updating UI"); updateUi(); // Restart this timer. start(); diff --git a/app/src/main/java/uk/org/openseizuredetector/ReportSeizureActivity.java b/app/src/main/java/uk/org/openseizuredetector/ReportSeizureActivity.java index 1ca9d93..a98d3ac 100644 --- a/app/src/main/java/uk/org/openseizuredetector/ReportSeizureActivity.java +++ b/app/src/main/java/uk/org/openseizuredetector/ReportSeizureActivity.java @@ -8,7 +8,9 @@ import android.content.Context; import android.content.Intent; import android.os.Bundle; import android.os.CountDownTimer; +import android.os.Handler; import android.support.v7.app.AppCompatActivity; +import android.system.Os; import android.util.Log; import android.view.View; import android.webkit.WebSettings; @@ -33,12 +35,16 @@ public class ReportSeizureActivity extends AppCompatActivity { private UiTimer mUiTimer; private LogManager mLm; private int mYear, mMonth, mDay, mHour, mMinute; + private String mMsg = "Messages"; + private OsdUtil osdUtil; @Override protected void onCreate(Bundle savedInstanceState) { Log.v(TAG, "onCreate()"); super.onCreate(savedInstanceState); mContext = this; + Handler h = new Handler(); + osdUtil = new OsdUtil(mContext, h); setContentView(R.layout.activity_report_seizure); mLm= new LogManager(mContext); @@ -89,7 +95,7 @@ public class ReportSeizureActivity extends AppCompatActivity { } private void updateUi() { - Log.v(TAG,"updateUi()"); + //Log.v(TAG,"updateUi()"); TextView tv; Button btn; @@ -103,6 +109,8 @@ public class ReportSeizureActivity extends AppCompatActivity { tv.setText(String.format("%02d",mHour)); tv = (TextView)findViewById(R.id.time_mm_tv); tv.setText(String.format("%02d",mMinute)); + tv = (TextView)findViewById(R.id.msg_tv); + tv.setText(mMsg); } View.OnClickListener onOk = @@ -112,8 +120,19 @@ public class ReportSeizureActivity extends AppCompatActivity { Log.v(TAG, "onOk"); String dateStr=String.format("%4d-%02d-%02d %02d:%02d:30",mYear,mMonth+1,mDay, mHour, mMinute); Log.v(TAG, "onOk() - dateSTr="+dateStr); + mMsg = "Finding Nearest Datapoint to Date/Time "+dateStr+"..."; int id = mLm.getNearestDatapointToDate(dateStr); + mMsg = mMsg + "\nNearest Datapoint is "+id; Log.v(TAG, "onOK() - nearest datapoint is "+id); + if (id!=-1) { + mLm.setDatapointStatus(id,5); + mMsg = mMsg + "\nSet Datapoint to Manual Alarm Status"; + osdUtil.showToast(getString(R.string.createdNewEvent)); + finish(); + } else { + mMsg = mMsg + "\n*** Datapoint not found - not doing anything ***"; + osdUtil.showToast(getString(R.string.DatapointNotFound)); + } } }; View.OnClickListener onCancel = @@ -205,7 +224,7 @@ public class ReportSeizureActivity extends AppCompatActivity { @Override public void onFinish() { - Log.v(TAG, "UiTimer - onFinish - Updating UI"); + //Log.v(TAG, "UiTimer - onFinish - Updating UI"); updateUi(); // Restart this timer. start(); diff --git a/app/src/main/res/layout/activity_log_manager_control.xml b/app/src/main/res/layout/activity_log_manager_control.xml index 0068922..c662c16 100644 --- a/app/src/main/res/layout/activity_log_manager_control.xml +++ b/app/src/main/res/layout/activity_log_manager_control.xml @@ -1,103 +1,116 @@ + android:textAppearance="?android:attr/textAppearanceLarge" /> + + + android:text="@string/num_local_events" /> + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="000" /> + + + android:text="@string/num_local_datapoints" /> + + android:layout_width="wrap_content" + android:layout_height="wrap_content" + android:text="000" /> + +