-
Notifications
You must be signed in to change notification settings - Fork 162
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix #285 #295
Fix #285 #295
Changes from 7 commits
697e1ff
e47e12c
06984ab
749ea74
89e6543
c5b4218
2a20536
99b510b
ae0f1cc
826e4ca
1514998
afb6d1a
545af91
7a305cc
c79c10e
7556aa9
05e12ad
8b51d72
b5eb81d
10d731d
6c1895b
acacf1d
976f522
76c6073
5f7725a
c1a098d
4b07fff
6653e10
59db67f
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,13 +30,13 @@ | |
import android.support.v4.content.FileProvider; | ||
import android.util.Log; | ||
|
||
import org.glucosio.android.activity.MainActivity; | ||
import org.glucosio.android.GlucosioApplication; | ||
import org.glucosio.android.db.DatabaseHandler; | ||
import org.glucosio.android.db.GlucoseReading; | ||
import org.glucosio.android.tools.ReadingToCSV; | ||
import org.glucosio.android.view.ExportView; | ||
|
||
import java.io.File; | ||
import java.util.ArrayList; | ||
import java.util.Calendar; | ||
import java.util.List; | ||
|
||
|
@@ -56,84 +56,78 @@ public class ExportPresenter { | |
private int toDay; | ||
private int toMonth; | ||
private int toYear; | ||
private DatabaseHandler dB; | ||
private MainActivity activity; | ||
|
||
public ExportPresenter(MainActivity exportActivity, DatabaseHandler dbHandler) { | ||
this.activity = exportActivity; | ||
dB = dbHandler; | ||
} | ||
|
||
public void onExportClicked(final Boolean all) { | ||
final List<GlucoseReading> readings; | ||
private Activity mActivity; | ||
private ExportView mExportView; | ||
private DatabaseHandler dB; | ||
|
||
if (all) { | ||
readings = dB.getGlucoseReadings(); | ||
public ExportPresenter(Activity activity) { | ||
mActivity = activity; | ||
if (activity instanceof ExportView) { | ||
mExportView = (ExportView) activity; | ||
} else { | ||
Calendar fromDate = Calendar.getInstance(); | ||
fromDate.set(Calendar.YEAR, fromYear); | ||
fromDate.set(Calendar.MONTH, fromMonth); | ||
fromDate.set(Calendar.DAY_OF_MONTH, fromDay); | ||
|
||
Calendar toDate = Calendar.getInstance(); | ||
toDate.set(Calendar.YEAR, toYear); | ||
toDate.set(Calendar.MONTH, toMonth); | ||
toDate.set(Calendar.DAY_OF_MONTH, toDay); | ||
readings = dB.getGlucoseReadings(fromDate.getTime(), toDate.getTime()); | ||
throw new RuntimeException("ExportPresenter Activity must implement ExportView interface"); | ||
} | ||
dB = GlucosioApplication.getInstance().getDBHandler(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why singleton preferred instead of parameter? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This way you don't have to pass the databaseHandler as a parameter since you are always using the one residing in the application context anyway. Using a singleton you ensure you are accessing the same reference. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is actually breaking testability. If you have parameter then you can easy mock it in unit tests. You have to make static setters or some hooks with the singleton pattern. It is probably a good thing if you want to keep the single reference to the DB (I'm not sure if required). But all scope logic should be done outside of consumer class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Once again I've gone along the lines of what was already present. I think the constructor should remain with the ExportView as the only param, and have a setter for the dbh. Once again I've gone along the lines of what was present.
You're right. |
||
} | ||
|
||
public void onExportClicked(final boolean isExportAll) { | ||
|
||
if (hasStoragePermissions(mActivity)) { | ||
// final ReadingToCSV csv = new ReadingToCSV(GlucosioApplication.getInstance()); | ||
final String preferredUnit = dB.getUser(1).getPreferred_unit(); | ||
final boolean[] isEmpty = {false}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be better to create simple structure There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So you can handle all three cases: When export process was successful, export process was successful but the database is empty or export process was unsuccessful. |
||
|
||
new AsyncTask<Void, Void, String>() { | ||
@Override | ||
protected String doInBackground(Void... params) { | ||
|
||
Realm realm = dB.getNewRealmInstance(); | ||
final List<GlucoseReading> readings; | ||
if (isExportAll) { | ||
readings = dB.getGlucoseReadings(realm); | ||
} else { | ||
Calendar fromDate = Calendar.getInstance(); | ||
fromDate.set(Calendar.YEAR, fromYear); | ||
fromDate.set(Calendar.MONTH, fromMonth); | ||
fromDate.set(Calendar.DAY_OF_MONTH, fromDay); | ||
|
||
Calendar toDate = Calendar.getInstance(); | ||
toDate.set(Calendar.YEAR, toYear); | ||
toDate.set(Calendar.MONTH, toMonth); | ||
toDate.set(Calendar.DAY_OF_MONTH, toDay); | ||
readings = dB.getGlucoseReadings(realm, fromDate.getTime(), toDate.getTime()); | ||
} | ||
|
||
if (readings.size() != 0) { | ||
if (hasStoragePermissions(activity)) { | ||
activity.showExportedSnackBar(readings.size()); | ||
final ReadingToCSV csv = new ReadingToCSV(activity.getApplicationContext()); | ||
final String preferredUnit = dB.getUser(1).getPreferred_unit(); | ||
|
||
new AsyncTask<Void, Void, String>() { | ||
@Override | ||
protected String doInBackground(Void... params) { | ||
final ArrayList<GlucoseReading> readingsToExport; | ||
Realm realm = dB.getNewRealmInstance(); | ||
|
||
if (all) { | ||
readingsToExport = dB.getGlucoseReadings(realm); | ||
} else { | ||
Calendar fromDate = Calendar.getInstance(); | ||
fromDate.set(Calendar.YEAR, fromYear); | ||
fromDate.set(Calendar.MONTH, fromMonth); | ||
fromDate.set(Calendar.DAY_OF_MONTH, fromDay); | ||
|
||
Calendar toDate = Calendar.getInstance(); | ||
toDate.set(Calendar.YEAR, toYear); | ||
toDate.set(Calendar.MONTH, toMonth); | ||
toDate.set(Calendar.DAY_OF_MONTH, toDay); | ||
readingsToExport = dB.getGlucoseReadings(realm, fromDate.getTime(), toDate.getTime()); | ||
} | ||
|
||
if (dirExists()) { | ||
Log.i("glucosio", "Dir exists"); | ||
return csv.createCSVFile(realm, readingsToExport, preferredUnit); | ||
} else { | ||
Log.i("glucosio", "Dir NOT exists"); | ||
return null; | ||
} | ||
mExportView.onExportStarted(readings.size()); | ||
if (readings.isEmpty()) { | ||
isEmpty[0] = true; | ||
return null; | ||
} | ||
|
||
@Override | ||
protected void onPostExecute(String filename) { | ||
super.onPostExecute(filename); | ||
if (filename != null) { | ||
Uri uri = FileProvider.getUriForFile(activity.getApplicationContext(), | ||
activity.getApplicationContext().getPackageName() + ".provider.fileprovider", new File(filename)); | ||
activity.showShareDialog(uri); | ||
} else { | ||
//TODO: Show error SnackBar | ||
activity.showExportError(); | ||
} | ||
if (dirExists()) { | ||
Log.i("glucosio", "Dir exists"); | ||
return ReadingToCSV.createCSVFile(mActivity, realm, readings, preferredUnit); | ||
} else { | ||
Log.i("glucosio", "Dir NOT exists"); | ||
return null; | ||
} | ||
}.execute(); | ||
} | ||
} else { | ||
activity.showNoReadingsSnackBar(); | ||
} | ||
|
||
@Override | ||
protected void onPostExecute(String filename) { | ||
super.onPostExecute(filename); | ||
if (filename != null) { | ||
Uri uri = FileProvider.getUriForFile(mActivity.getApplicationContext(), | ||
mActivity.getApplicationContext().getPackageName() + ".provider.fileprovider", new File(filename)); | ||
mExportView.onExportFinish(uri); | ||
} else if (isEmpty[0]) { | ||
mExportView.onNoItemsToExport(); | ||
} else { | ||
mExportView.onExportError(); | ||
} | ||
} | ||
}.execute(); | ||
} | ||
} | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,22 +30,15 @@ | |
import java.io.File; | ||
import java.io.FileOutputStream; | ||
import java.io.OutputStreamWriter; | ||
import java.util.ArrayList; | ||
import java.util.List; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! |
||
|
||
import io.realm.Realm; | ||
|
||
public class ReadingToCSV { | ||
|
||
private Context context; | ||
|
||
public ReadingToCSV(Context mContext) { | ||
this.context = mContext; | ||
} | ||
|
||
|
||
public String createCSVFile(Realm realm, final ArrayList<GlucoseReading> readings, String um) { | ||
public static String createCSVFile(Context context, Realm realm, final List<GlucoseReading> readings, String um) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why static method instead of class? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its not required to instantiate the class/object for the current usage, you only wish to "consume" code residing in it, which really makes it more of a helper class. http://stackoverflow.com/a/218761/3374428 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Clear! I would not bother about performance unless you show me it is the bottle neck or some problem here. Because this again breaks testability. With this solution, I can not mock this behaviour in unit tests. So if I test any class that uses this functionality, I will also test real exporting as well. An article about "love" to statics http://www.endran.nl/blog/death-to-statics/ There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I always worry about performance, this can of course be tested if we change the
That being said, static elements are awesome when used right, they go both ways, if we ignore performance we'll have the same problems ;). |
||
try { | ||
final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/glucosio", "glucosio_export_ " + System.currentTimeMillis()/1000 + ".csv"); | ||
final File file = new File(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS) + "/glucosio", "glucosio_export_ " + System.currentTimeMillis() / 1000 + ".csv"); | ||
final File sd = Environment.getExternalStorageDirectory(); | ||
if (sd.canWrite()) { | ||
FileOutputStream fileOutputStream = new FileOutputStream(file); | ||
|
@@ -84,13 +77,13 @@ public String createCSVFile(Realm realm, final ArrayList<GlucoseReading> reading | |
osw.append(dateTool.convertRawTime(reading.getCreated() + "")); | ||
osw.append(','); | ||
|
||
osw.append(reading.getReading() + ""); | ||
osw.append(String.valueOf(reading.getReading())); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice! |
||
osw.append(','); | ||
|
||
osw.append("mg/dL"); | ||
osw.append(','); | ||
|
||
osw.append(reading.getReading_type() + ""); | ||
osw.append(String.valueOf(reading.getReading_type())); | ||
osw.append(','); | ||
|
||
osw.append(reading.getNotes()); | ||
|
@@ -122,7 +115,6 @@ public String createCSVFile(Realm realm, final ArrayList<GlucoseReading> reading | |
|
||
} | ||
} | ||
|
||
osw.flush(); | ||
osw.close(); | ||
Log.i("Glucosio", "Done exporting readings"); | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package org.glucosio.android.view; | ||
|
||
import android.net.Uri; | ||
|
||
/** | ||
* Created by joaquimley on 08/09/16. | ||
*/ | ||
public interface ExportView { | ||
|
||
void onExportStarted(int numberOfItemsToExport); | ||
|
||
void onNoItemsToExport(); | ||
|
||
void onExportFinish(Uri fileUri); | ||
|
||
void onExportError(); | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not to have parameter for constructor only
ExportView
type? Why do we need to cast it?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Because you need the Activity since you are handling the permissions in the presenter (it wouldn't be really my choice but see line 140) which requires to have an activity, the cast is to ensure said activity is implementing the required View interface.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Clear! It was there already. As for me, a presenter should not have any dependency to Android API, but this is the discussion for the android team.
As the quick solution I would have two parameters - one view and one activity. And remove casting!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes it would be easy to abstract the Android apis from the Presenter if there was no Permission handling there (and it shouldn't).
With the current setup you'll be instantiating something like this:
new ExportPresenter(this, this);
not pretty, but refactoring this wouldn't make the PR with to many differences.