Added support for watch info settins in SdDataSourceGarmin. Started converting to sqlite database data logging rather than text file.
This commit is contained in:
@@ -1,13 +1,114 @@
|
||||
package uk.org.openseizuredetector;
|
||||
|
||||
import android.app.DatePickerDialog;
|
||||
import android.app.TimePickerDialog;
|
||||
import android.content.Context;
|
||||
import android.os.Handler;
|
||||
import android.support.v7.app.AppCompatActivity;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.Button;
|
||||
import android.widget.DatePicker;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TimePicker;
|
||||
|
||||
import java.util.Calendar;
|
||||
|
||||
public class DBQueryActivity extends AppCompatActivity
|
||||
implements View.OnClickListener {
|
||||
String TAG = "DBQueryActivity";
|
||||
Button mDateBtn;
|
||||
Button mTimeBtn;
|
||||
Button mExportBtn;
|
||||
EditText mDateTxt;
|
||||
EditText mTimeTxt;
|
||||
EditText mDurationTxt;
|
||||
|
||||
int mYear;
|
||||
int mMonth;
|
||||
int mDay;
|
||||
int mHour;
|
||||
int mMinute;
|
||||
double mDuration;
|
||||
|
||||
OsdUtil mUtil;
|
||||
Handler mHandler;
|
||||
|
||||
public class DBQueryActivity extends AppCompatActivity {
|
||||
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
setContentView(R.layout.activity_dbquery);
|
||||
|
||||
mHandler = new Handler();
|
||||
mUtil = new OsdUtil(this, mHandler);
|
||||
|
||||
mDateBtn = (Button)findViewById(R.id.dateBtn);
|
||||
mDateBtn.setOnClickListener(this);
|
||||
mTimeBtn = (Button)findViewById(R.id.timeBtn);
|
||||
mTimeBtn.setOnClickListener(this);
|
||||
mExportBtn = (Button)findViewById(R.id.exportBtn);
|
||||
mExportBtn.setOnClickListener(this);
|
||||
mDateTxt = (EditText)findViewById(R.id.endDateText);
|
||||
mTimeTxt = (EditText)findViewById(R.id.endTimeText);
|
||||
mDurationTxt = (EditText)findViewById(R.id.durationText);
|
||||
|
||||
// Get Current Date
|
||||
final Calendar c = Calendar.getInstance();
|
||||
mYear = c.get(Calendar.YEAR);
|
||||
mMonth = c.get(Calendar.MONTH);
|
||||
mDay = c.get(Calendar.DAY_OF_MONTH);
|
||||
mHour = c.get(Calendar.HOUR_OF_DAY);
|
||||
mMinute = c.get(Calendar.MINUTE);
|
||||
|
||||
mDateTxt.setText(String.format("%02d-%02d-%04d",mDay, mMonth+1, mYear));
|
||||
mTimeTxt.setText(String.format("%02d:%02d:%02d", mHour, mMinute, 00));
|
||||
mDuration = 2.0;
|
||||
mDurationTxt.setText(String.format("%03.1f", mDuration));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
Log.v(TAG, "onClick()");
|
||||
if (view == mDateBtn) {
|
||||
DatePickerDialog datePickerDialog = new DatePickerDialog(this,
|
||||
new DatePickerDialog.OnDateSetListener() {
|
||||
@Override
|
||||
public void onDateSet(DatePicker view, int year,
|
||||
int monthOfYear, int dayOfMonth) {
|
||||
mDay = dayOfMonth;
|
||||
mMonth = monthOfYear;
|
||||
mYear = year;
|
||||
mDateTxt.setText(String.format("%02d-%02d-%04d",mDay, mMonth+1, mYear));
|
||||
}
|
||||
}, mYear, mMonth, mDay);
|
||||
datePickerDialog.show();
|
||||
}
|
||||
if (view == mTimeBtn) {
|
||||
// Launch Time Picker Dialog
|
||||
TimePickerDialog timePickerDialog = new TimePickerDialog(this,
|
||||
new TimePickerDialog.OnTimeSetListener() {
|
||||
@Override
|
||||
public void onTimeSet(TimePicker view, int hourOfDay,
|
||||
int minute) {
|
||||
mHour = hourOfDay;
|
||||
mMinute = minute;
|
||||
mTimeTxt.setText(String.format("%02d:%02d:%02d", mHour, mMinute, 00));
|
||||
}
|
||||
}, mHour, mMinute, false);
|
||||
timePickerDialog.show();
|
||||
}
|
||||
if (view == mExportBtn) {
|
||||
mDateTxt.setText(String.format("%02d-%02d-%04d",mDay, mMonth+1, mYear));
|
||||
mTimeTxt.setText(String.format("%02d:%02d:%02d", mHour, mMinute, 00));
|
||||
mDuration = Double.parseDouble(mDurationTxt.getText().toString());
|
||||
|
||||
|
||||
mUtil.showToast(String.format("EndDate=%s %s, Duration=%3.1f hrs",
|
||||
mDateTxt.getText().toString(), mTimeTxt.getText().toString() ,mDuration));
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,9 +28,25 @@ import android.database.DatabaseUtils;
|
||||
import android.database.SQLException;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
import android.os.AsyncTask;
|
||||
import android.os.CountDownTimer;
|
||||
import android.os.Handler;
|
||||
import android.text.format.Time;
|
||||
import android.util.Log;
|
||||
|
||||
import org.apache.commons.codec.binary.Base64;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Reader;
|
||||
import java.io.UnsupportedEncodingException;
|
||||
import java.net.HttpURLConnection;
|
||||
import java.net.URL;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
|
||||
import static android.database.sqlite.SQLiteDatabase.openOrCreateDatabase;
|
||||
|
||||
/**
|
||||
@@ -47,7 +63,10 @@ public class LogManager {
|
||||
private int mOSDWearerId;
|
||||
private String mOSDUrl;
|
||||
private OsdDbHelper mOSDDb;
|
||||
private RemoteLogTimer mRemoteLogTimer;
|
||||
private Context mContext;
|
||||
private OsdUtil mUtil;
|
||||
|
||||
|
||||
public LogManager(boolean logRemote,
|
||||
boolean logRemoteMobile,
|
||||
@@ -64,6 +83,9 @@ public class LogManager {
|
||||
mOSDUrl = OSDUrl;
|
||||
mContext = context;
|
||||
|
||||
Handler handler = new Handler();
|
||||
mUtil = new OsdUtil(mContext, handler);
|
||||
|
||||
try {
|
||||
mOSDDb = new OsdDbHelper(mDbTableName, mContext);
|
||||
if (!checkTableExists(mOSDDb, mDbTableName)) {
|
||||
@@ -72,9 +94,12 @@ public class LogManager {
|
||||
} catch (SQLException e) {
|
||||
Log.e(TAG, "Failed to open Database: " + e.toString());
|
||||
}
|
||||
|
||||
startRemoteLogTimer();
|
||||
}
|
||||
|
||||
|
||||
|
||||
private boolean checkTableExists(OsdDbHelper osdDb, String osdTableName) {
|
||||
Cursor c = null;
|
||||
boolean tableExists = false;
|
||||
@@ -126,8 +151,142 @@ public class LogManager {
|
||||
|
||||
}
|
||||
|
||||
public void writeToRemoteServer() {
|
||||
Log.v(TAG,"writeToRemoteServer()");
|
||||
if (!mLogRemote) {
|
||||
Log.v(TAG,"mLogRemote not set, not doing anything");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!mLogRemoteMobile) {
|
||||
// Check network state - are we using mobile data?
|
||||
if (mUtil.isMobileDataActive()) {
|
||||
Log.v(TAG,"Using mobile data, so not doing anything");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!mUtil.isNetworkConnected()) {
|
||||
Log.v(TAG,"No network connection - doing nothing");
|
||||
return;
|
||||
}
|
||||
|
||||
Log.v(TAG,"Requirements for remote logging met!");
|
||||
uploadSdData();
|
||||
}
|
||||
|
||||
/**
|
||||
* Upload a batch of seizure detector data records to the server..
|
||||
* Uses the UploadSdDataTask class to upload the data in the
|
||||
* background. DownloadSdDataTask.onPostExecute() is called on completion.
|
||||
*/
|
||||
public void uploadSdData() {
|
||||
Log.v(TAG, "uploadSdData()");
|
||||
String dataStr = "data string to upload";
|
||||
//new PostDataTask().execute("http://" + mOSDUrl + ":8080/data", dataStr, mOSDUname, mOSDPasswd);
|
||||
new PostDataTask().execute("http://192.168.43.175:8765/datapoints/add", dataStr, mOSDUname, mOSDPasswd);
|
||||
}
|
||||
|
||||
private class PostDataTask extends AsyncTask<String, Void, String> {
|
||||
@Override
|
||||
protected String doInBackground(String... params) {
|
||||
// params comes from the execute() call:
|
||||
// params[0] is the url,
|
||||
// params[1] is the data to send.
|
||||
// params[2] is the user name
|
||||
// params[3] is the password
|
||||
int MAXLEN = 500; // Maximum length of response that we will accept (bytes)
|
||||
InputStream is = null;
|
||||
String urlStr = params[0];
|
||||
String dataStr = params[1];
|
||||
String uname = params[2];
|
||||
String passwd = params[3];
|
||||
String resultStr = "Not Initialised";
|
||||
Log.v(TAG,"doInBackgound(): url="+urlStr+" data="+dataStr+" uname="+uname+" passwd="+passwd);
|
||||
try {
|
||||
URL url = new URL(urlStr);
|
||||
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
|
||||
conn.setReadTimeout(2000 /* milliseconds */);
|
||||
conn.setConnectTimeout(5000 /* milliseconds */);
|
||||
conn.setRequestMethod("POST");
|
||||
conn.setRequestProperty("Content-Type", "application/json; utf-8");
|
||||
conn.setRequestProperty("Accept", "application/json");
|
||||
String auth = uname + ":" + passwd;
|
||||
byte[] encodedAuth = Base64.encodeBase64(auth.getBytes("utf-8"));
|
||||
String authHeaderValue = "Basic " + new String(encodedAuth);
|
||||
conn.setRequestProperty("Authorization", authHeaderValue);
|
||||
conn.setDoInput(true);
|
||||
|
||||
// Put our data into the outputstream associated with the connection.
|
||||
OutputStream os = conn.getOutputStream();
|
||||
byte[] input = dataStr.getBytes("utf-8");
|
||||
os.write(input, 0, input.length);
|
||||
|
||||
// Starts the query
|
||||
conn.connect();
|
||||
int response = conn.getResponseCode();
|
||||
Log.d(TAG, "The response code is: " + response);
|
||||
is = conn.getInputStream();
|
||||
|
||||
// Convert the InputStream into a string
|
||||
Reader reader = new InputStreamReader(is, "UTF-8");
|
||||
char[] buffer = new char[MAXLEN];
|
||||
reader.read(buffer);
|
||||
resultStr = new String(buffer);
|
||||
|
||||
} catch (IOException e) {
|
||||
Log.v(TAG,"doInBackground(): IOException - "+e.toString());
|
||||
resultStr = "Error"+e.toString();
|
||||
|
||||
// Makes sure that the InputStream is closed after the app is
|
||||
// finished using it.
|
||||
} finally {
|
||||
if (is != null) {
|
||||
try {
|
||||
is.close();
|
||||
} catch (IOException e) {
|
||||
Log.v(TAG,"doInBackground(): IOException - "+e.toString());
|
||||
resultStr = "Error"+e.toString();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (resultStr.startsWith("Unable to retrieve web page")) {
|
||||
Log.v(TAG,"doInBackground() - Unable to retrieve data");
|
||||
} else {
|
||||
Log.v(TAG,"doInBackground(): result = "+resultStr);
|
||||
}
|
||||
return (resultStr);
|
||||
|
||||
}
|
||||
// onPostExecute displays the results of the AsyncTask.
|
||||
@Override
|
||||
protected void onPostExecute(String result) {
|
||||
Log.v(TAG,"onPostExecute() - result = "+result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
public void close() {
|
||||
mOSDDb.close();
|
||||
stopRemoteLogTimer();
|
||||
}
|
||||
|
||||
|
||||
public JSONObject queryDatapoints(String endDateStr, Double duration) {
|
||||
Log.d(TAG,"queryDatapoints() - endDateStr="+endDateStr);
|
||||
Cursor c = null;
|
||||
try {
|
||||
c = mOSDDb.getWritableDatabase().query(mDbTableName, null,
|
||||
null, null, null, null, null);
|
||||
//c.query("Select * from ? where DataTime < ?", mDbTableName, endDateStr);
|
||||
}
|
||||
catch (Exception e) {
|
||||
Log.d(TAG, mDbTableName+" doesn't exist :(((");
|
||||
}
|
||||
return(null);
|
||||
}
|
||||
|
||||
public class OsdDbHelper extends SQLiteOpenHelper {
|
||||
@@ -170,4 +329,54 @@ public class LogManager {
|
||||
onUpgrade(db, oldVersion, newVersion);
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Start the timer that will send and SMS alert after a given period.
|
||||
*/
|
||||
private void startRemoteLogTimer() {
|
||||
if (mRemoteLogTimer != null) {
|
||||
Log.v(TAG, "startRemoteLogTimer -timer already running - cancelling it");
|
||||
mRemoteLogTimer.cancel();
|
||||
mRemoteLogTimer = null;
|
||||
}
|
||||
Log.v(TAG, "startRemoteLogTimer() - starting RemoteLogTimer");
|
||||
mRemoteLogTimer =
|
||||
new RemoteLogTimer(10 * 1000, 1000);
|
||||
mRemoteLogTimer.start();
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* Cancel the SMS timer to prevent the SMS message being sent..
|
||||
*/
|
||||
public void stopRemoteLogTimer() {
|
||||
if (mRemoteLogTimer != null) {
|
||||
Log.v(TAG, "stopRemoteLogTimer(): cancelling Remote Log timer");
|
||||
mRemoteLogTimer.cancel();
|
||||
mRemoteLogTimer = null;
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Inhibit fault alarm initiation for a period to avoid spurious warning
|
||||
* beeps caused by short term network interruptions.
|
||||
*/
|
||||
private class RemoteLogTimer extends CountDownTimer {
|
||||
public RemoteLogTimer(long startTime, long interval) {
|
||||
super(startTime, interval);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTick(long l) {
|
||||
// Do Nothing
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFinish() {
|
||||
Log.v(TAG, "mRemoteLogTimer - onFinish");
|
||||
writeToRemoteServer();
|
||||
start();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -238,6 +238,17 @@ public class MainActivity extends AppCompatActivity {
|
||||
mConnection.mSdServer.sendSMSAlarm();
|
||||
}
|
||||
return true;
|
||||
case R.id.action_export:
|
||||
Log.i(TAG, "action_export");
|
||||
try {
|
||||
Intent i = new Intent(
|
||||
MainActivity.this,
|
||||
DBQueryActivity.class);
|
||||
this.startActivity(i);
|
||||
} catch (Exception ex) {
|
||||
Log.i(TAG, "exception starting export activity " + ex.toString());
|
||||
}
|
||||
return true;
|
||||
case R.id.action_logs:
|
||||
Log.i(TAG, "action_logs");
|
||||
try {
|
||||
|
||||
@@ -49,6 +49,8 @@ import android.content.res.Resources;
|
||||
import android.content.res.XmlResourceParser;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.NetworkInfo;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Environment;
|
||||
@@ -295,6 +297,24 @@ public class OsdUtil implements ActivityCompat.OnRequestPermissionsResultCallbac
|
||||
return null;
|
||||
}
|
||||
|
||||
public boolean isMobileDataActive() {
|
||||
// return true if we are using mobile data, otherwise return false
|
||||
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
if (activeNetwork.getType() == ConnectivityManager.TYPE_MOBILE) {
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isNetworkConnected() {
|
||||
// return true if we have a network connection, otherwise false.
|
||||
ConnectivityManager cm = (ConnectivityManager)mContext.getSystemService(Context.CONNECTIVITY_SERVICE);
|
||||
NetworkInfo activeNetwork = cm.getActiveNetworkInfo();
|
||||
return (activeNetwork.isConnected());
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a Toast message on screen.
|
||||
*
|
||||
|
||||
@@ -354,6 +354,10 @@ public class SdDataSourceGarmin extends SdDataSource {
|
||||
// Returns a message string that is passed back to the watch.
|
||||
public String updateFromJSON(String jsonStr) {
|
||||
String retVal = "undefined";
|
||||
String watchPartNo;
|
||||
String watchFwVersion;
|
||||
String sdVersion;
|
||||
String sdName;
|
||||
Log.v(TAG,"updateFromJSON - "+jsonStr);
|
||||
|
||||
try {
|
||||
@@ -397,6 +401,23 @@ public class SdDataSourceGarmin extends SdDataSource {
|
||||
mSampleFreq = (short)dataObject.getInt("sampleFreq");
|
||||
mSdData.batteryPc = (short)dataObject.getInt("battery");
|
||||
Log.v(TAG,"updateFromJSON - mSamplePeriod="+mSamplePeriod+" mSampleFreq="+mSampleFreq);
|
||||
mUtil.writeToSysLogFile("SdDataSourceGarmin.updateFromJSON - Settings Received");
|
||||
mUtil.writeToSysLogFile(" * mSamplePeriod="+mSamplePeriod+" mSampleFreq="+mSampleFreq);
|
||||
mUtil.writeToSysLogFile(" * batteryPc = "+mSdData.batteryPc);
|
||||
|
||||
try {
|
||||
watchPartNo = dataObject.getString("watchPartNo");
|
||||
watchFwVersion = dataObject.getString("watchFwVersion");
|
||||
sdVersion = dataObject.getString("sdVersion");
|
||||
sdName = dataObject.getString("sdName");
|
||||
mUtil.writeToSysLogFile(" * sdName = "+sdName+" version "+sdVersion);
|
||||
mUtil.writeToSysLogFile(" * watchPartNo = "+watchPartNo+" fwVersion "+watchFwVersion);
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG,"updateFromJSON - Error Parsing V3.2 JSON String - "+e.toString());
|
||||
mUtil.writeToSysLogFile("updateFromJSON - Error Parsing V3.2 JSON String - "+e.toString());
|
||||
mUtil.writeToSysLogFile(" This is probably because of an out of date watch app - please upgrade!");
|
||||
e.printStackTrace();
|
||||
}
|
||||
mSdData.haveSettings = true;
|
||||
mSdData.mSampleFreq = mSampleFreq;
|
||||
mWatchAppRunningCheck = true;
|
||||
@@ -407,6 +428,7 @@ public class SdDataSourceGarmin extends SdDataSource {
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.e(TAG,"updateFromJSON - Error Parsing JSON String - "+e.toString());
|
||||
mUtil.writeToSysLogFile("updateFromJSON - Error Parsing JSON String - "+e.toString());
|
||||
e.printStackTrace();
|
||||
retVal = "ERROR";
|
||||
}
|
||||
|
||||
@@ -371,6 +371,9 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
}
|
||||
*/
|
||||
|
||||
// Stop the log Manager
|
||||
mLm.close();
|
||||
|
||||
|
||||
try {
|
||||
// Cancel the notification.
|
||||
@@ -386,9 +389,6 @@ public class SdServer extends Service implements SdDataReceiver {
|
||||
mToneGenerator.release();
|
||||
mToneGenerator = null;
|
||||
|
||||
// Stop the log Manager
|
||||
mLm.close();
|
||||
|
||||
// stop this service.
|
||||
Log.v(TAG, "onDestroy(): calling stopSelf()");
|
||||
mUtil.writeToSysLogFile("SdServer.onDestroy() - stopping self");
|
||||
|
||||
@@ -1,9 +1,71 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".DBQueryActivity">
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="End Date/Time (dd-mm-yyyy hh:mm)"
|
||||
/>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
</android.support.constraint.ConstraintLayout>
|
||||
<EditText
|
||||
android:id="@+id/endDateText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:text="(end date)" />
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Select Date"
|
||||
android:id="@+id/dateBtn"
|
||||
/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/endTimeText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:ems="10"
|
||||
android:text="(end time)" />
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Select Time"
|
||||
android:id="@+id/timeBtn"
|
||||
/>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Duration (hrs)"
|
||||
/>
|
||||
<EditText
|
||||
android:id="@+id/durationText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:inputType="numberDecimal"
|
||||
android:ems="10"
|
||||
android:text="1.0" />
|
||||
|
||||
<Button
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Export Data"
|
||||
android:id="@+id/exportBtn"
|
||||
/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -55,6 +55,14 @@
|
||||
android:enabled="true"
|
||||
/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_export"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
android:showAsAction="never|withText"
|
||||
android:title="Export Data"
|
||||
android:enabled="true"
|
||||
/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_settings"
|
||||
android:icon="@drawable/ic_action_settings"
|
||||
|
||||
Reference in New Issue
Block a user