/* * Copyright 2015 Ludwig Andersson * * This file is part of Thermospy-android. * * Thermospy-android is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * Thermospy-android is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with Thermospy-android. If not, see <http://www.gnu.org/licenses/>. */ package com.luan.thermospy.android.activities; import android.app.ProgressDialog; import android.content.DialogInterface; import android.content.Intent; import android.graphics.Color; import android.net.Uri; import android.os.Bundle; import android.support.v4.view.MenuItemCompat; import android.support.v7.app.ActionBarActivity; import android.support.v7.widget.ShareActionProvider; import android.view.Menu; import android.view.MenuItem; import com.android.volley.RequestQueue; import com.android.volley.toolbox.Volley; import com.github.mikephil.charting.charts.LineChart; import com.github.mikephil.charting.components.LimitLine; import com.github.mikephil.charting.components.YAxis; import com.github.mikephil.charting.data.Entry; import com.github.mikephil.charting.data.LineData; import com.github.mikephil.charting.data.LineDataSet; import com.luan.thermospy.android.R; import com.luan.thermospy.android.core.pojo.LogSession; import com.luan.thermospy.android.core.pojo.TemperatureEntry; import com.luan.thermospy.android.core.rest.GetTemperatureEntryListReq; import org.json.JSONException; import java.io.BufferedWriter; import java.io.File; import java.io.FileWriter; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.List; public class LineGraphActivity extends ActionBarActivity implements GetTemperatureEntryListReq.OnGetTemperatureEntryListener, DialogInterface.OnCancelListener { public static final String ARG_IP_ADDRESS = "ipaddress"; public static final String ARG_PORT = "port"; public static final String ARG_SESSION_ID = "sessionid"; public static final String ARG_DATEFORMAT = "dateformat"; private static final String LOG_TAG = LineGraphActivity.class.getSimpleName(); private LogSession mLogSession; private int mPort; private String mIpAddress; private ProgressDialog mProgressDialog; private RequestQueue mRequestQueue; private LineChart mChart; private GetTemperatureEntryListReq mGetTemperatureEntryListReq; private ShareActionProvider mShareActionProvider; private List<TemperatureEntry> mTemperatureList; private String mDateFormat; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_line_graph); if (savedInstanceState != null) { mLogSession = LogSession.fromJson(savedInstanceState.getString(ARG_SESSION_ID)); mPort = savedInstanceState.getInt(ARG_PORT, 0); mIpAddress = savedInstanceState.getString(ARG_IP_ADDRESS); mDateFormat = savedInstanceState.getString(ARG_DATEFORMAT); } else { mLogSession = LogSession.fromJson(getIntent().getStringExtra(ARG_SESSION_ID)); mPort = getIntent().getIntExtra(ARG_PORT, 0); mIpAddress = getIntent().getStringExtra(ARG_IP_ADDRESS); mDateFormat = getIntent().getStringExtra(ARG_DATEFORMAT); } mRequestQueue = Volley.newRequestQueue(this); mGetTemperatureEntryListReq = new GetTemperatureEntryListReq(mRequestQueue, this); setTitle(mLogSession.getName()); mChart = (LineChart) findViewById(R.id.chart); // no description text mChart.setDescription(""); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable value highlighting mChart.setHighlightEnabled(true); // enable touch gestures mChart.setTouchEnabled(true); // enable scaling and dragging mChart.setDragEnabled(true); mChart.setScaleEnabled(true); // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); // set an alternative background color // mChart.setBackgroundColor(Color.GRAY); // create a custom MarkerView (extend MarkerView) and specify the layout // to use for it //MyMarkerView mv = new MyMarkerView(this, R.layout.custom_marker_view); // set the marker to the chart //mChart.setMarkerView(mv); // enable/disable highlight indicators (the lines that indicate the // highlighted Entry) mChart.setHighlightIndicatorEnabled(false); } @Override public boolean onCreateOptionsMenu(Menu menu) { // Inflate the menu; this adds items to the action bar if it is present. getMenuInflater().inflate(R.menu.menu_line_graph, menu); MenuItem menuItem = menu.findItem(R.id.menu_item_share); mShareActionProvider = (ShareActionProvider)MenuItemCompat.getActionProvider(menuItem); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle action bar item clicks here. The action bar will // automatically handle clicks on the Home/Up button, so long // as you specify a parent activity in AndroidManifest.xml. int id = item.getItemId(); //noinspection SimplifiableIfStatement if (id == R.id.action_settings) { return true; } return super.onOptionsItemSelected(item); } private Intent createShareIntent() { File outputDir = getExternalCacheDir(); // context being the Activity pointer File outputFile = null; try { outputFile = File.createTempFile("export", ".csv", outputDir); BufferedWriter writer = null; try { writer = new BufferedWriter( new FileWriter( outputFile.getAbsolutePath())); SimpleDateFormat df = new SimpleDateFormat(mDateFormat); for (TemperatureEntry entry : mTemperatureList) { StringBuilder builder = new StringBuilder(); builder.append(entry.getId()).append(",") .append(df.format(entry.getTimestamp().getTime())) .append(",") .append(entry.getTemperature()) .append("\n"); writer.write(builder.toString()); } writer.flush(); } catch ( IOException e) { } finally { try { if ( writer != null) writer.close( ); } catch ( IOException e) { } } } catch (IOException | NullPointerException npe) { } if (outputFile != null) { Uri u1 = Uri.fromFile(outputFile); Intent sendIntent = new Intent(Intent.ACTION_SEND); sendIntent.putExtra(Intent.EXTRA_SUBJECT, "Temperature log from Thermospy"); sendIntent.putExtra(Intent.EXTRA_EMAIL, mLogSession.getName()); sendIntent.putExtra(Intent.EXTRA_STREAM, u1); sendIntent.setType("application/csv"); sendIntent.addFlags( Intent.FLAG_GRANT_READ_URI_PERMISSION); return sendIntent; } return null; } private void requestTemperatureEntries() { if (mProgressDialog == null) { mProgressDialog = new ProgressDialog(this); mProgressDialog.setOnCancelListener(this); } mProgressDialog.setCanceledOnTouchOutside(false); mProgressDialog.setTitle("Please wait"); mProgressDialog.setMessage("Initializing graph..."); mGetTemperatureEntryListReq.setTemperatureEntryId(mLogSession.getId()); mGetTemperatureEntryListReq.request(mIpAddress, mPort); mProgressDialog.show(); } @Override public void onTemperatureEntryRecv(List<TemperatureEntry> logSessionList) { if (logSessionList.size() > 0) { mTemperatureList =logSessionList; mShareActionProvider.setShareIntent(createShareIntent()); setupLineChart(); setData(logSessionList); showStats(); } else { //dmListener.onError(); } mProgressDialog.dismiss(); } private void showStats() { } private void setupLineChart() { // no description text mChart.setDescription(""); mChart.setNoDataTextDescription("You need to provide data for the chart."); // enable value highlighting mChart.setHighlightIndicatorEnabled(false); mChart.setDrawGridBackground(false); // enable touch gestures mChart.setTouchEnabled(true); // enable scaling and dragging mChart.setDragEnabled(true); mChart.setScaleEnabled(true); // if disabled, scaling can be done on x- and y-axis separately mChart.setPinchZoom(true); // set an alternative background color mChart.setBackgroundColor(Color.WHITE); // enable/disable highlight indicators (the lines that indicate the // highlighted Entry) mChart.setHighlightIndicatorEnabled(false); } private void setData(List<TemperatureEntry> logSessionList) { int min = Integer.MAX_VALUE; int max = Integer.MIN_VALUE; int ttt = Integer.MIN_VALUE; ArrayList<Entry> yVals = new ArrayList<>(); ArrayList<String> xVals = new ArrayList<>(); SimpleDateFormat df = new SimpleDateFormat(mDateFormat.split(" ")[1]); for (int i = 0; i < logSessionList.size(); i++) { TemperatureEntry temperatureEntry = logSessionList.get(i); xVals.add(df.format(temperatureEntry.getTimestamp())); Entry chartEntry = new Entry(logSessionList.get(i).getTemperature(), i, temperatureEntry); yVals.add(chartEntry); if (mLogSession.getTargetTemperature() != null && temperatureEntry.getTemperature() >= mLogSession.getTargetTemperature() && ttt == Integer.MIN_VALUE) { ttt = i; } if (temperatureEntry.getTemperature() < min) min = temperatureEntry.getTemperature(); if (temperatureEntry.getTemperature() > max) max = temperatureEntry.getTemperature(); } // create a dataset and give it a type LineDataSet set1 = new LineDataSet(yVals, getString(R.string.temperature)); // set1.setFillAlpha(110); // set1.setFillColor(Color.RED); // set the line to be drawn like this "- - - - - -" set1.enableDashedLine(10f, 5f, 0f); set1.setColor(Color.BLACK); set1.setCircleColor(Color.BLACK); set1.setLineWidth(2f); set1.setDrawValues(false); set1.setCircleSize(4f); set1.setFillAlpha(65); set1.setFillColor(Color.BLACK); // set1.setShader(new LinearGradient(0, 0, 0, mChart.getHeight(), // Color.BLACK, Color.WHITE, Shader.TileMode.MIRROR)); ArrayList<LineDataSet> dataSets = new ArrayList<LineDataSet>(); dataSets.add(set1); // add the datasets // create a data object with the datasets LineData data = new LineData(xVals, dataSets); YAxis leftAxis = mChart.getAxisLeft(); if (mLogSession.getTargetTemperature() != null) { String timeToTarget = "Target temperature"; if (ttt != Integer.MIN_VALUE) { long duration = (logSessionList.get(ttt).getTimestamp().getTime() - logSessionList.get(0).getTimestamp().getTime()) / 1000; timeToTarget = "Time to target temperature: " + String.format("%d:%02d:%02d", duration / 3600, (duration % 3600) / 60, (duration % 60)); } LimitLine ll1 = new LimitLine(mLogSession.getTargetTemperature(), timeToTarget); ll1.setLineWidth(4f); ll1.enableDashedLine(10f, 10f, 0f); ll1.setLabelPosition(LimitLine.LimitLabelPosition.POS_RIGHT); ll1.setTextSize(10f); leftAxis.addLimitLine(ll1); } leftAxis.setStartAtZero(true); leftAxis.setAxisMaxValue(max + 10); leftAxis.setAxisMinValue(min-10); mChart.getAxisRight().setEnabled(false); // set data mChart.setData(data); mChart.invalidate(); } @Override public void onTemperatureEntryError() { finish(); } @Override public void onDestroy() { super.onDestroy(); if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } mGetTemperatureEntryListReq.cancel(); mRequestQueue.cancelAll(this); } @Override public void onSaveInstanceState(Bundle outState) { outState.putString(ARG_IP_ADDRESS, mIpAddress); outState.putInt(ARG_PORT, mPort); outState.putString(ARG_DATEFORMAT, mDateFormat); try { outState.putString(ARG_SESSION_ID, LogSession.toJson(mLogSession).toString()); } catch (JSONException e) { } super.onSaveInstanceState(outState); } @Override public void onStop() { super.onStop(); if (mProgressDialog != null && mProgressDialog.isShowing()) { mProgressDialog.dismiss(); } mGetTemperatureEntryListReq.cancel(); mRequestQueue.cancelAll(this); } @Override public void onResume() { super.onResume(); requestTemperatureEntries(); } @Override public void onBackPressed() { setTitle(getString(R.string.temperature_log)); android.app.FragmentManager fm = getFragmentManager(); if (fm.getBackStackEntryCount() > 0) { fm.popBackStack(); } else { super.onBackPressed(); } } @Override public void onCancel(DialogInterface dialog) { mGetTemperatureEntryListReq.cancel(); finish(); } }