package com.lcneves.cookme;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteStatement;
import android.net.ConnectivityManager;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.PowerManager;
import android.util.JsonReader;
import android.util.Log;
import android.widget.Toast;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.text.DecimalFormat;
import java.util.zip.GZIPInputStream;
public class JSONHelper extends Activity {
final String recName = DatabaseHelper.REC_NAME;
final String recIngredients = DatabaseHelper.REC_INGREDIENTS;
final String recURL = DatabaseHelper.REC_URL;
final String recipesTable = DatabaseHelper.RECIPES_TABLE;
final String FILE_NAME_OLD = "recipeitems-latest.json";
final String FILE_NAME_NEW = "recipeitems-edited.json";
final String FILE_NAME_GZ = "recipeitems-latest.json.gz";
File fileDir;
File fileOld;
File fileNew;
File fileGz;
final String JSON_URL = "http://openrecipes.s3.amazonaws.com/recipeitems-latest.json.gz";
private int lineCount = 0;
Context context = JSONHelper.this;
ProgressDialog mProgressDialog;
DatabaseHelper database = new DatabaseHelper(this);
InputStream input = null;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_jsonhelper);
final int MIN_FREE_SPACE = 314572800; // During tests, this class used up to 250+ MB.
if(Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
if(context.getExternalFilesDir(null).getFreeSpace() > MIN_FREE_SPACE) {
fileDir = context.getExternalFilesDir(null);
} else {
if(context.getFilesDir().getFreeSpace() > MIN_FREE_SPACE) {
fileDir = context.getFilesDir();
} else {
FreeSpaceDialogFragment dialogFreeSpace = new FreeSpaceDialogFragment();
dialogFreeSpace.show(getFragmentManager(), "tag");
}
}
} else {
if(context.getFilesDir().getFreeSpace() > MIN_FREE_SPACE) {
fileDir = context.getFilesDir();
} else {
FreeSpaceDialogFragment dialogFreeSpace = new FreeSpaceDialogFragment();
dialogFreeSpace.show(getFragmentManager(), "tag");
}
}
if(fileDir != null) {
fileOld = new File(fileDir, FILE_NAME_OLD);
fileNew = new File(fileDir, FILE_NAME_NEW);
fileGz = new File(fileDir, FILE_NAME_GZ);
ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
if(cm.getActiveNetworkInfo() != null) {
if(cm.getActiveNetworkInfo().isConnected()) {
downloadJSON(JSON_URL);
}
} else {
ConnectivityDialogFragment dialogConnectivity = new ConnectivityDialogFragment();
dialogConnectivity.show(getFragmentManager(), "tag");
}
}
}
private void downloadJSON(String url) {
mProgressDialog = new ProgressDialog(JSONHelper.this);
mProgressDialog.setMessage("Cleaning old database...");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
final DownloadTask downloadTask = new DownloadTask(JSONHelper.this);
mProgressDialog.setButton(DialogInterface.BUTTON_NEGATIVE, "Cancel", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
downloadTask.cancel(true);
}
});
downloadTask.execute(url);
}
private void unzipJSON(File fileIn, File fileOut) {
mProgressDialog = new ProgressDialog(JSONHelper.this);
mProgressDialog.setMessage("Decompressing...");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setCancelable(false);
final UnzipTask unzipTask = new UnzipTask(JSONHelper.this);
unzipTask.execute(fileIn, fileOut);
}
private void rebuildJSON(File fileIn, File fileOut) {
mProgressDialog = new ProgressDialog(JSONHelper.this);
mProgressDialog.setMessage("Reformatting database file...");
mProgressDialog.setCancelable(false);
final RebuildTask rebuildTask = new RebuildTask(JSONHelper.this);
rebuildTask.execute(fileIn, fileOut);
}
private void parseJSON() {
mProgressDialog = new ProgressDialog(JSONHelper.this);
mProgressDialog.setMessage("Parsing "+lineCount+" recipes...");
mProgressDialog.setIndeterminate(true);
mProgressDialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL);
mProgressDialog.setCancelable(false);
final ParseTask parseTask = new ParseTask(JSONHelper.this);
parseTask.execute();
}
private class DownloadTask extends AsyncTask<String, Integer, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
double fileLength;
int fileLengthInt;
public DownloadTask(Context context) {
this.context = context;
}
@Override
protected String doInBackground(String... sUrl) {
OutputStream output = null;
HttpURLConnection connection = null;
database.recreateDatabase();
if(fileGz.exists())
fileGz.delete();
if(fileOld.exists())
fileOld.delete();
if(fileNew.exists())
fileNew.delete();
try {
URL url = new URL(sUrl[0]);
connection = (HttpURLConnection) url.openConnection();
connection.connect();
if (connection.getResponseCode() != HttpURLConnection.HTTP_OK) {
return "Server returned HTTP " + connection.getResponseCode()
+ " " + connection.getResponseMessage();
}
fileLengthInt = connection.getContentLength();
fileLength = fileLengthInt;
input = connection.getInputStream();
output = new FileOutputStream(fileGz);
byte data[] = new byte[4096];
long total = 0;
int count;
while ((count = input.read(data)) != -1) {
if (isCancelled()) {
input.close();
if(fileGz.exists())
fileGz.delete();
database.dropRecipes();
Intent intent = new Intent(JSONHelper.this, MainActivity.class);
startActivity(intent);
return null;
}
total += count;
if (fileLength > 0)
publishProgress((int) (total/1024));
output.write(data, 0, count);
}
} catch (Exception e) {
return e.toString();
} finally {
try {
if (output != null)
output.close();
if (input != null)
input.close();
} catch (IOException ignored) {
}
if (connection != null)
connection.disconnect();
}
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
mProgressDialog.show();
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMessage("Downloading "+(new DecimalFormat("#.##").format(fileLength/1048576))+" MB...");
mProgressDialog.setMax(fileLengthInt/1024);
mProgressDialog.setProgress(progress[0]);
}
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
mProgressDialog.dismiss();
if (result != null) {
Toast.makeText(context,"Download error: "+result, Toast.LENGTH_LONG).show();
if(fileGz.exists())
fileGz.delete();
database.dropRecipes();
Intent intent = new Intent(JSONHelper.this, MainActivity.class);
startActivity(intent);
} else {
unzipJSON(fileGz, fileOld);
}
}
}
private class UnzipTask extends AsyncTask<File, Integer, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
public UnzipTask(Context context) {
this.context = context;
}
@Override
protected String doInBackground(File... fileInOut) {
int sChunk = 8192;
int length;
byte[] buffer = new byte[sChunk];
InputStream is = null;
GZIPInputStream zis = null;
BufferedOutputStream bos = null;
try {
is = new FileInputStream(fileInOut[0]);
zis = new GZIPInputStream(new BufferedInputStream(is));
bos = new BufferedOutputStream(new FileOutputStream(fileInOut[1]));
while ((length = zis.read(buffer, 0, sChunk)) != -1) bos.write(buffer, 0, length);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
bos.close();
zis.close();
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
mProgressDialog.show();
}
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
mProgressDialog.dismiss();
if (result == null)
if(fileGz.exists())
fileGz.delete();
rebuildJSON(fileOld, fileNew);
}
}
private class RebuildTask extends AsyncTask<File, Void, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
public RebuildTask(Context context) {
this.context = context;
}
@Override
protected String doInBackground(File... fileInOut) {
if(fileInOut[1].exists())
fileInOut[1].delete();
FileInputStream fileStream = null;
try {
fileStream = new FileInputStream(fileInOut[0]);
BufferedReader br = new BufferedReader(new InputStreamReader(fileStream));
BufferedWriter bw = new BufferedWriter(new FileWriter(fileInOut[1]));
String strLine;
bw.write("[");
while ((strLine = br.readLine()) != null) {
bw.write(strLine + ",");
bw.newLine();
lineCount++;
}
bw.write("{}]");
bw.close();
br.close();
} catch (FileNotFoundException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
try {
fileStream.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
mProgressDialog.show();
}
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
mProgressDialog.dismiss();
if (result == null) {
if(fileOld.exists())
fileOld.delete();
parseJSON();
}
}
}
private class ParseTask extends AsyncTask<String, Integer, String> {
private Context context;
private PowerManager.WakeLock mWakeLock;
public ParseTask(Context context) {
this.context = context;
}
@Override
protected String doInBackground(final String... args) {
SQLiteDatabase db = database.getWritableDatabase();
db.beginTransaction();
JsonReader jsonReader = null;
String jsonName;
String jsonIngredients;
String jsonUrl;
final String NAME = "name";
final String INGREDIENTS = "ingredients";
final String URL = "url";
int lineProgress = 0;
long oldTime = System.nanoTime();
final String comma = ",";
final short MIN_INGREDIENTS_LENGTH = 5;
SQLiteStatement st = db.compileStatement("INSERT INTO "+recipesTable+" ("+recName+comma+recIngredients+comma+recURL+") VALUES (?,?,?);");
try {
jsonReader = new JsonReader(new BufferedReader(new FileReader(fileNew)));
jsonReader.beginArray();
while (jsonReader.hasNext()) {
jsonName = null;
jsonIngredients = null;
jsonUrl = null;
jsonReader.beginObject();
while (jsonReader.hasNext()) {
String name = jsonReader.nextName();
if (name.equals(NAME)) {
jsonName = jsonReader.nextString().replace("&", "&");
} else if (name.equals(INGREDIENTS)) {
jsonIngredients = jsonReader.nextString().replace("&", "&").trim();
if(jsonIngredients.length() < MIN_INGREDIENTS_LENGTH) jsonIngredients = null;
else {
int lineBreak = jsonIngredients.indexOf("\n");
if(lineBreak == -1) lineBreak = jsonIngredients.length();
if(jsonIngredients.substring(0,(lineBreak/2)).equals
(jsonIngredients.substring((lineBreak/2)+1, lineBreak)))
jsonIngredients = null;
}
} else if (name.equals(URL)) {
jsonUrl = jsonReader.nextString();
} else {
jsonReader.skipValue();
}
}
lineProgress++;
if(System.nanoTime() - oldTime > 1e9) { // update every second
oldTime = System.nanoTime();
publishProgress((int) (lineProgress));
}
jsonReader.endObject();
if(jsonName != null && jsonIngredients != null && !jsonIngredients.isEmpty() && jsonUrl != null) {
st.bindString(1, jsonName);
st.bindString(2, jsonIngredients);
st.bindString(3, jsonUrl);
st.executeInsert();
st.clearBindings();
}
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} finally {
db.setTransactionSuccessful();
db.endTransaction();
db.close();
try {
jsonReader.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
return null;
}
@Override
protected void onProgressUpdate(Integer... progress) {
super.onProgressUpdate(progress);
// if we get here, length is known, now set indeterminate to false
mProgressDialog.setIndeterminate(false);
mProgressDialog.setMax(lineCount);
mProgressDialog.setProgress(progress[0]);
}
@Override
protected void onPreExecute() {
super.onPreExecute();
// take CPU lock to prevent CPU from going off if the user
// presses the power button during download
PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
getClass().getName());
mWakeLock.acquire();
mProgressDialog.show();
}
@Override
protected void onPostExecute(String result) {
mWakeLock.release();
mProgressDialog.dismiss();
if (result == null) {
if(fileGz.exists())
fileGz.delete();
if(fileOld.exists())
fileOld.delete();
if(fileNew.exists())
fileNew.delete();
Intent intent = new Intent(JSONHelper.this, MainActivity.class);
startActivity(intent);
}
}
}
public static class FreeSpaceDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("This device does not have enough free space to import the database. Please free at least 300 MB and try again.")
.setPositiveButton("OK", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
}
});
return builder.create();
}
}
public static class ConnectivityDialogFragment extends DialogFragment {
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setMessage("Please ensure that this device is connected to the internet.")
.setPositiveButton("Try again", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
new Handler().post(new Runnable() {
@Override
public void run() {
Intent intent = getActivity().getIntent();
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP | Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_NO_ANIMATION);
getActivity().overridePendingTransition(0, 0);
getActivity().finish();
getActivity().overridePendingTransition(0, 0);
startActivity(intent);
}
});
}
})
.setNegativeButton("Later", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int id) {
Intent intent = new Intent(getActivity(), MainActivity.class);
startActivity(intent);
}
});
return builder.create();
}
}
}