From af0ef90e4d7a3ad76473e2365cbad95b8f011a2c Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Thu, 25 Feb 2016 07:29:45 -0500 Subject: [PATCH] Export to CSV --- .../uhabits/fragments/ListHabitsFragment.java | 53 ++++- .../org/isoron/uhabits/io/CSVExporter.java | 184 ++++++++++++++++++ .../main/res/menu-v21/list_habits_context.xml | 5 + app/src/main/res/menu/list_habits_context.xml | 5 + app/src/main/res/values/strings.xml | 1 + 5 files changed, 247 insertions(+), 1 deletion(-) create mode 100644 app/src/main/java/org/isoron/uhabits/io/CSVExporter.java diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java index 9a9fcfc73..699d3320b 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java @@ -23,9 +23,12 @@ import android.app.AlertDialog; import android.app.Fragment; import android.content.Context; import android.content.DialogInterface; +import android.content.Intent; import android.content.SharedPreferences; import android.graphics.Color; import android.graphics.Typeface; +import android.net.Uri; +import android.os.AsyncTask; import android.os.Bundle; import android.preference.PreferenceManager; import android.util.DisplayMetrics; @@ -68,9 +71,11 @@ import org.isoron.uhabits.commands.ChangeHabitColorCommand; import org.isoron.uhabits.commands.DeleteHabitsCommand; import org.isoron.uhabits.commands.UnarchiveHabitsCommand; import org.isoron.uhabits.helpers.ReminderHelper; +import org.isoron.uhabits.io.CSVExporter; import org.isoron.uhabits.loaders.HabitListLoader; import org.isoron.uhabits.models.Habit; +import java.io.File; import java.util.Date; import java.util.GregorianCalendar; import java.util.LinkedList; @@ -199,6 +204,12 @@ public class ListHabitsFragment extends Fragment return true; } + + case R.id.action_export_csv: + { + onExportHabitsClick(selectedHabits); + return true; + } } return false; @@ -248,6 +259,7 @@ public class ListHabitsFragment extends Fragment private ActionMode actionMode; private List selectedPositions; private DragSortController dragSortController; + private ProgressBar progressBar; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -265,7 +277,7 @@ public class ListHabitsFragment extends Fragment View view = inflater.inflate(R.layout.list_habits_fragment, container, false); tvNameHeader = (TextView) view.findViewById(R.id.tvNameHeader); - ProgressBar progressBar = (ProgressBar) view.findViewById(R.id.progressBar); + progressBar = (ProgressBar) view.findViewById(R.id.progressBar); loader.setProgressBar(progressBar); adapter = new ListHabitsAdapter(getActivity()); @@ -782,4 +794,43 @@ public class ListHabitsFragment extends Fragment if (refreshKey == null) loader.updateAllHabits(true); else loader.updateHabit(refreshKey); } + + private void onExportHabitsClick(final LinkedList selectedHabits) + { + new AsyncTask() + { + String filename; + + @Override + protected void onPreExecute() + { + progressBar.setIndeterminate(true); + progressBar.setVisibility(View.VISIBLE); + } + + @Override + protected void onPostExecute(Void aVoid) + { + if(filename != null) + { + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_SEND); + intent.setType("application/zip"); + intent.putExtra(Intent.EXTRA_STREAM, Uri.fromFile(new File(filename))); + + startActivity(intent); + } + + progressBar.setVisibility(View.GONE); + } + + @Override + protected Void doInBackground(Void... params) + { + CSVExporter exporter = new CSVExporter(activity, selectedHabits); + filename = exporter.writeArchive(); + return null; + } + }.execute(); + } } diff --git a/app/src/main/java/org/isoron/uhabits/io/CSVExporter.java b/app/src/main/java/org/isoron/uhabits/io/CSVExporter.java new file mode 100644 index 000000000..40eaf7d6d --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/io/CSVExporter.java @@ -0,0 +1,184 @@ +package org.isoron.uhabits.io; + +import android.content.Context; +import android.database.Cursor; +import android.database.sqlite.SQLiteDatabase; + +import com.activeandroid.Cache; + +import org.isoron.helpers.DateHelper; +import org.isoron.uhabits.models.Habit; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.FileWriter; +import java.io.IOException; +import java.text.SimpleDateFormat; +import java.util.Date; +import java.util.LinkedList; +import java.util.List; +import java.util.TimeZone; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +public class CSVExporter +{ + private List habits; + private Context context; + private java.text.DateFormat dateFormat; + + private List generateDirs; + private List generateFilenames; + + private String basePath; + + public CSVExporter(Context context, List habits) + { + this.habits = habits; + this.context = context; + generateDirs = new LinkedList<>(); + generateFilenames = new LinkedList<>(); + + basePath = String.format("%s/export/", context.getFilesDir()); + + dateFormat = new SimpleDateFormat("yyyy-MM-dd"); + dateFormat.setTimeZone(TimeZone.getTimeZone("UTC")); + } + + public String formatDate(long timestamp) + { + return dateFormat.format(new Date(timestamp)); + } + + public String formatScore(int score) + { + return String.format("%.2f", ((float) score) / Habit.MAX_SCORE); + } + + private void writeScores(String dirPath, Habit habit) throws IOException + { + String path = dirPath + "scores.csv"; + FileWriter out = new FileWriter(basePath + path); + generateFilenames.add(path); + + String query = "select timestamp, score from score where habit = ? order by timestamp"; + String params[] = { habit.getId().toString() }; + + SQLiteDatabase db = Cache.openDatabase(); + Cursor cursor = db.rawQuery(query, params); + + if(!cursor.moveToFirst()) return; + + do + { + String timestamp = formatDate(cursor.getLong(0)); + String score = formatScore(cursor.getInt(1)); + out.write(String.format("%s,%s\n", timestamp, score)); + + } while(cursor.moveToNext()); + + out.close(); + cursor.close(); + } + + private void writeCheckmarks(String dirPath, Habit habit) throws IOException + { + String path = dirPath + "checkmarks.csv"; + FileWriter out = new FileWriter(basePath + path); + generateFilenames.add(path); + + String query = "select timestamp, value from checkmarks where habit = ? order by timestamp"; + String params[] = { habit.getId().toString() }; + + SQLiteDatabase db = Cache.openDatabase(); + Cursor cursor = db.rawQuery(query, params); + + if(!cursor.moveToFirst()) return; + + do + { + String timestamp = formatDate(cursor.getLong(0)); + Integer value = cursor.getInt(1); + out.write(String.format("%s,%d\n", timestamp, value)); + + } while(cursor.moveToNext()); + + out.close(); + cursor.close(); + } + + private void writeFiles(Habit habit) throws IOException + { + boolean success; + + String path = String.format("%s/", habit.name); + new File(basePath + path).mkdirs(); + generateDirs.add(path); + + writeScores(path, habit); + writeCheckmarks(path, habit); + } + + private void writeZipFile(String zipFilename) throws IOException + { + FileOutputStream fos = new FileOutputStream(zipFilename); + ZipOutputStream zos = new ZipOutputStream(fos); + + for(String filename : generateFilenames) + addFileToZip(zos, filename); + + zos.close(); + fos.close(); + } + + private void addFileToZip(ZipOutputStream zos, String filename) throws IOException + { + FileInputStream fis = new FileInputStream(new File(basePath + filename)); + ZipEntry ze = new ZipEntry(filename); + zos.putNextEntry(ze); + + int length; + byte bytes[] = new byte[1024]; + while((length = fis.read(bytes)) >= 0) + zos.write(bytes, 0, length); + + zos.closeEntry(); + fis.close(); + } + + private void cleanup() + { + for(String filename : generateFilenames) + new File(basePath + filename).delete(); + + for(String filename : generateDirs) + new File(basePath + filename).delete(); + + new File(basePath).delete(); + } + + public String writeArchive() + { + String date = formatDate(DateHelper.getStartOfToday()); + String zipFilename = String.format("%s/habits-%s.zip", context.getExternalCacheDir(), date); + + try + { + for (Habit h : habits) + writeFiles(h); + + writeZipFile(zipFilename); + cleanup(); + } + catch (IOException e) + { + e.printStackTrace(); + return null; + } + + return zipFilename; + } + + +} diff --git a/app/src/main/res/menu-v21/list_habits_context.xml b/app/src/main/res/menu-v21/list_habits_context.xml index 1427d4ab5..1f1872977 100644 --- a/app/src/main/res/menu-v21/list_habits_context.xml +++ b/app/src/main/res/menu-v21/list_habits_context.xml @@ -21,6 +21,11 @@ android:title="@string/unarchive" android:icon="@drawable/ic_action_unarchive_dark"/> + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8795e7a86..21e79bef6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -111,6 +111,7 @@ Weekdays Any day Select days + Export to CSV @string/hint_drag