package ie.cathalcoffey.android.projecteuler; import ie.cathalcoffey.android.projecteuler.ProjectEulerClient.EulerProblem; import java.io.BufferedInputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Hashtable; import java.util.Set; import org.jsoup.Jsoup; import org.jsoup.nodes.Document; import org.jsoup.nodes.Element; import android.app.Notification; import android.app.NotificationManager; import android.app.PendingIntent; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.database.Cursor; import android.database.SQLException; import android.database.sqlite.SQLiteDatabase; import android.database.sqlite.SQLiteOpenHelper; import android.support.v4.app.NotificationCompat; import android.util.Log; public class MyDataBaseHelper extends SQLiteOpenHelper { private static String DB_PATH = "/data/data/ie.cathalcoffey.android.projecteuler/databases/"; private static String DB_NAME = "euler.db"; private SQLiteDatabase myDataBase; private final Context context; private boolean cancel = false; public MyDataBaseHelper(Context context) { super(context, DB_NAME, null, 5); this.context = context; } public int[] getSolvedCount() { String query = "SELECT solved, COUNT(_id) FROM data GROUP BY solved"; Cursor c = myDataBase.rawQuery(query, new String[]{}); int solved = 0; int unsolved = 0; while(c.moveToNext()) { switch(c.getInt(0)) { case 0: unsolved = c.getInt(1); break; case 1: solved = c.getInt(1); break; } } c.close(); return new int[]{solved, solved+unsolved}; } public Cursor getData(String constraint) { if (constraint == null || constraint == "") return getData(); String query; if (MyApplication.display_text != null && MyApplication.display_text.equals(Label.All.toString())) query = "SELECT _id, title, published, updated, solvedby, solved, html, answer FROM data WHERE ('PROBLEM ' || _id ) LIKE ? OR title LIKE ?"; else if (MyApplication.display_text != null && MyApplication.display_text.equals(Label.Solved.toString())) query = "SELECT _id, title, published, updated, solvedby, solved, html, answer FROM data WHERE solved = 1 AND (('PROBLEM ' || _id ) LIKE ? OR title LIKE ?)"; else if (MyApplication.display_text != null && MyApplication.display_text.equals(Label.Starred.toString())) query = "SELECT _id, title, published, updated, solvedby, solved, html, answer FROM data WHERE _id IN (" + toCommaList(MyApplication.stars) + ") AND (('PROBLEM ' || _id ) LIKE ? OR title LIKE ?)"; else query = "SELECT _id, title, published, updated, solvedby, solved, html, answer FROM data WHERE solved = 0 AND (('PROBLEM ' || _id ) LIKE ? OR title LIKE ?)"; return myDataBase.rawQuery(query, new String[]{"%" + constraint + "%", "%" + constraint + "%"}); } public Cursor getData() { String query = "SELECT _id, title, published, updated, solvedby, solved, html, answer FROM data"; if(MyApplication.display_text != null) { if (MyApplication.display_text.equals(Label.Solved.toString())) query += " WHERE solved = 1"; else if (MyApplication.display_text.equals(Label.Unsolved.toString())) query += " WHERE solved = 0"; else if (MyApplication.display_text.equals(Label.Starred.toString())) query += " WHERE _id IN (" + toCommaList(MyApplication.stars) + ")"; } return myDataBase.rawQuery(query, new String[]{}); } private String toCommaList(Hashtable<String, Boolean> stars) { if(stars.size() == 0) return ""; Set<String> keys = stars.keySet(); String[] sorted_keys = new String[keys.size()]; keys.toArray(sorted_keys); Arrays.sort(sorted_keys); StringBuilder sb = new StringBuilder(); for(String id : sorted_keys) { sb.append(","); sb.append(id); } return sb.substring(1); } private void copyDataBase() throws IOException { Decompress.unzip(context.getAssets().open("assets.zip"), "/data/data/ie.cathalcoffey.android.projecteuler/"); } public boolean checkDataBase() { File file = context.getDatabasePath(DB_NAME); return file.exists(); } public void openDataBase(int mode) throws SQLException { String myPath = DB_PATH + DB_NAME; myDataBase = SQLiteDatabase.openDatabase(myPath, null, SQLiteDatabase.NO_LOCALIZED_COLLATORS | mode); } @Override public synchronized void close() { super.close(); } @Override public void onCreate(SQLiteDatabase db) { } public ArrayList<Long> getLastUpdated() { ArrayList<Long> last_updated = new ArrayList<Long>(); Cursor cursor = myDataBase.rawQuery("select updated from data", null); while(cursor.moveToNext()) { last_updated.add(cursor.getLong(0)); } cursor.close(); return last_updated; } public void updateProblems(ProjectEulerClient pec, ArrayList<EulerProblem> problems, boolean install, boolean userStarted) { NotificationManager notificationManager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE); NotificationCompat.Builder builder = new NotificationCompat.Builder(context); Intent intent = new Intent(context, PreferencesActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION); PendingIntent contentIntent = PendingIntent.getActivity(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT); builder.setContentIntent(contentIntent) .setSmallIcon(R.drawable.ic_notification) .setWhen(System.currentTimeMillis()) .setAutoCancel(true) .setContentTitle("Updating problem set") .setContentText("Authenticating..."); Notification notification = builder.build(); ContentValues args = new ContentValues(); long start, end; start = System.currentTimeMillis(); // Step 1, update solved and solved_by for(EulerProblem ep : problems) { if(MyApplication.cancelUpdater) return; if(userStarted) { builder.setContentText("Updating: Problem " + ep.id + " of " + problems.size()); notification = builder.build(); notification.flags |= Notification.FLAG_AUTO_CANCEL; notificationManager.notify(1, notification); } args.put("solvedby", ep.solved_by); args.put("solved", ep.solved_flag); myDataBase.beginTransaction(); myDataBase.update("data", args, "_id = ?", new String[]{"" + ep.id}); myDataBase.setTransactionSuccessful(); myDataBase.endTransaction(); } end = System.currentTimeMillis(); Log.w("Euler upate: solved and solved_by", "" + (end-start) / 1000 + " seconds"); if(install) { start = System.currentTimeMillis(); // Step 2, figure out which problems have changed. ArrayList<Long> last_updated = getLastUpdated(); int i; for(i = 0; i < Math.min(problems.size(), last_updated.size()); i++) { if(MyApplication.cancelUpdater) return; if(last_updated.get(i) < problems.get(i).date_last_update) { builder.setContentText("Modifying: Problem " + problems.get(i).id + " of " + problems.size()); notification = builder.build(); notification.flags |= Notification.FLAG_AUTO_CANCEL; notificationManager.notify(1, notification); installOrUpdateProblem(pec, problems.get(i), false); } } end = System.currentTimeMillis(); Log.w("Euler upate: Problem which have changed", "" + (end-start) / 1000 + " seconds"); start = System.currentTimeMillis(); // Step 3, add new problems. for(int j = i; j < problems.size(); j++) { if(MyApplication.cancelUpdater) return; builder.setContentText("Installing: Problem " + problems.get(j).id + " of " + problems.size()); notification = builder.build(); notification.flags |= Notification.FLAG_AUTO_CANCEL; notificationManager.notify(1, notification); installOrUpdateProblem(pec, problems.get(j), true); } end = System.currentTimeMillis(); Log.w("Euler upate: Install new problems", (end-start) / 1000 + " seconds"); } notificationManager.cancel(1); } public void installOrUpdateProblem(ProjectEulerClient pec, EulerProblem ep, boolean install) { ContentValues args = new ContentValues(); args.put("_id", ep.id); args.put("title", ep.description); args.put("published", ep.date_published); args.put("updated", ep.date_last_update); args.put("solvedby", ep.solved_by); args.put("solved", ep.solved_flag); try { String html = pec.getProblem(ep.id).html(); Document soup = Jsoup.parse(html); for(Element img : soup.select("img")) { if(img.hasAttr("src")) { String src = img.attr("src"); if (src.startsWith("http://projecteuler.net/")) src = src.substring("http://projecteuler.net/".length()); File f = new File("/data/data/ie.cathalcoffey.android.projecteuler/" + src); if(!f.exists()) { f.getParentFile().mkdirs(); InputStream input = new BufferedInputStream(new URL("http://projecteuler.net/" + src).openStream()); OutputStream output = new FileOutputStream("/data/data/ie.cathalcoffey.android.projecteuler/" + src); byte data[] = new byte[1024]; int count; while ((count = input.read(data)) != -1) output.write(data, 0, count); output.flush(); output.close(); input.close(); } } } String body = soup.body().html(); args.put("html", body); args.put("answer", ep.answer); } catch (Exception e) { Log.w("Error", e.getMessage()); return; } myDataBase.beginTransaction(); if(install) myDataBase.insert("data", null, args); else myDataBase.update("data", args, "_id = ?", new String[]{"" + ep.id}); myDataBase.setTransactionSuccessful(); myDataBase.endTransaction(); } public long getLastModified(long _id) { Cursor cursor = myDataBase.rawQuery("select updated from data where _id=?", new String[] { "" + _id }); long lastModified = -1; if(cursor.getCount() > 0) { cursor.moveToFirst(); lastModified = cursor.getLong(0); } cursor.close(); return lastModified; } public synchronized void solve(String id) { myDataBase.beginTransaction(); ContentValues args = new ContentValues(); args.put("solved", 1); myDataBase.update("data", args, "_id = ?", new String[]{id}); myDataBase.setTransactionSuccessful(); myDataBase.endTransaction(); } public synchronized void updateSolved() { myDataBase.beginTransaction(); ContentValues args = new ContentValues(); args.put("solved", 0); myDataBase.update("data", args, null, null); myDataBase.setTransactionSuccessful(); myDataBase.endTransaction(); } public void kill() { cancel = true; } @Override public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) { /*if(newVersion > oldVersion) { File file = context.getDatabasePath(DB_NAME); if(file.exists()) file.delete(); try { copyDataBase(); } catch (IOException e) { throw new Error("Error copying database"); } } */ } public void createDataBase() throws IOException { //this.getReadableDatabase(); File file = context.getDatabasePath(DB_NAME); if(!file.exists()) { try { copyDataBase(); } catch (IOException e) { throw new Error("Error copying database"); } } } }