diff --git a/Jpandroid/AndroidManifest.xml b/Jpandroid/AndroidManifest.xml
new file mode 100644
index 0000000..d286041
--- /dev/null
+++ b/Jpandroid/AndroidManifest.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/Jpandroid/res/drawable/icon.png b/Jpandroid/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
Binary files /dev/null and b/Jpandroid/res/drawable/icon.png differ
diff --git a/Jpandroid/res/layout/main.xml b/Jpandroid/res/layout/main.xml
new file mode 100644
index 0000000..3a5f117
--- /dev/null
+++ b/Jpandroid/res/layout/main.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/Jpandroid/res/values/strings.xml b/Jpandroid/res/values/strings.xml
new file mode 100644
index 0000000..f9ddbb8
--- /dev/null
+++ b/Jpandroid/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ Hello World!
+
+
diff --git a/Jpandroid/src/com/meditationtracker/persistence/helpers/ReflectionHelper.java b/Jpandroid/src/com/meditationtracker/persistence/helpers/ReflectionHelper.java
new file mode 100644
index 0000000..61dbeaa
--- /dev/null
+++ b/Jpandroid/src/com/meditationtracker/persistence/helpers/ReflectionHelper.java
@@ -0,0 +1,43 @@
+package com.meditationtracker.persistence.helpers;
+
+import java.lang.reflect.Field;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.persistence.Column;
+import javax.persistence.Id;
+
+import android.provider.BaseColumns;
+
+public class ReflectionHelper {
+
+ public static Map getPersistenceColumnNamesForClass(Class> clazz) {
+ Map result = new HashMap();
+ for (Field f : getPersistenceColumnsForClass(clazz))
+ {
+ Column columnAnn = f.getAnnotation(Column.class);
+ if (columnAnn != null)
+ result.put(columnAnn.name(), f);
+ else
+ if (f.getAnnotation(Id.class) != null)
+ result.put(BaseColumns._ID, f);
+ }
+
+ return result;
+ }
+
+ protected static List getPersistenceColumnsForClass(Class> clazz) {
+ List result = new ArrayList();
+ for (Field f : clazz.getFields()) {
+ if (f.getAnnotation(Column.class) != null || f.getAnnotation(Id.class) != null)
+ {
+ result.add(f);
+ }
+ }
+
+ return result;
+ }
+
+}
diff --git a/Jpandroid/src/com/meditationtracker/persistence/jpa/Entity.java b/Jpandroid/src/com/meditationtracker/persistence/jpa/Entity.java
new file mode 100644
index 0000000..a10dabd
--- /dev/null
+++ b/Jpandroid/src/com/meditationtracker/persistence/jpa/Entity.java
@@ -0,0 +1,80 @@
+package com.meditationtracker.persistence.jpa;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.sql.ResultSet;
+import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Map;
+
+import com.meditationtracker.persistence.helpers.ReflectionHelper;
+import doo.util.functional.Collection;
+import doo.util.functional.Folder;
+
+public class Entity {
+ private String hash;
+ private Class> clazz = null;
+ private Map columns;
+
+ public Entity(Class> clazz) {
+ this.clazz = clazz;
+ hash = buildHash(clazz);
+ }
+
+ @SuppressWarnings("unchecked")
+ public T createInstance(ResultSet row) throws IllegalArgumentException, SecurityException, InstantiationException, IllegalAccessException, InvocationTargetException, NoSuchMethodException, SQLException {
+ if (clazz == null)
+ throw new IllegalArgumentException("Entity was initialized without a class, cannot create one.");
+
+ return fillObject((T)clazz.getConstructor((Class[])null).newInstance(), row);
+ }
+
+ private T fillObject(T obj, ResultSet row) throws SQLException, IllegalArgumentException, IllegalAccessException {
+ ResultSetMetaData metaData = row.getMetaData();
+ for (int x=0; x columns = new ArrayList();
+
+ ResultSetMetaData rowMetaData = row.getMetaData();
+ for(int x = 0; x clazz) {
+ columns = ReflectionHelper.getPersistenceColumnNamesForClass(clazz);
+ List colNames = new ArrayList(columns.keySet());
+
+ return sortAndJoinForHash(colNames);
+ }
+
+ private static String sortAndJoinForHash(List list) {
+ Collections.sort(list);
+ return Collection.fold(list, "|", new Folder() {
+ public String fold(String accumulator, String current) {
+ return accumulator += current + "|";
+ }
+
+ });
+ }
+
+}
diff --git a/Jpandroid/src/com/meditationtracker/persistence/jpa/EntityManager.java b/Jpandroid/src/com/meditationtracker/persistence/jpa/EntityManager.java
new file mode 100644
index 0000000..5e92e0b
--- /dev/null
+++ b/Jpandroid/src/com/meditationtracker/persistence/jpa/EntityManager.java
@@ -0,0 +1,22 @@
+package com.meditationtracker.persistence.jpa;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.HashMap;
+
+
+public class EntityManager {
+ private static HashMap entities;
+
+ public static void registerEntity(Class> clazz) {
+ Entity entity = new Entity(clazz);
+ entities.put(entity.hashCode(), entity);
+ }
+
+ public static Entity getEntityInfo(ResultSet row) throws SQLException {
+ return entities.get(Entity.buildHash(row));
+ }
+
+
+
+}
diff --git a/Jpandroid/src/com/meditationtracker/persistence/jpa/JpaHandler.java b/Jpandroid/src/com/meditationtracker/persistence/jpa/JpaHandler.java
new file mode 100644
index 0000000..1b64a38
--- /dev/null
+++ b/Jpandroid/src/com/meditationtracker/persistence/jpa/JpaHandler.java
@@ -0,0 +1,28 @@
+package com.meditationtracker.persistence.jpa;
+
+import java.sql.ResultSet;
+import java.sql.SQLException;
+
+import org.apache.commons.dbutils.ResultSetHandler;
+
+
+public class JpaHandler implements ResultSetHandler{
+
+ public T handle(ResultSet row) throws SQLException {
+ Entity info = EntityManager.getEntityInfo(row);
+
+ T result;
+ try {
+ result = info.createInstance(row);
+
+ return result;
+ } catch (Exception e) {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+
+ return null;
+ }
+
+ }
+
+}
diff --git a/MeditationTracker/AndroidManifest.xml b/MeditationTracker/AndroidManifest.xml
new file mode 100644
index 0000000..c082756
--- /dev/null
+++ b/MeditationTracker/AndroidManifest.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MeditationTracker/misc/189917.eps b/MeditationTracker/misc/189917.eps
new file mode 100644
index 0000000..46ab7e5
Binary files /dev/null and b/MeditationTracker/misc/189917.eps differ
diff --git a/MeditationTracker/misc/AlmazniyUm_print.pdf b/MeditationTracker/misc/AlmazniyUm_print.pdf
new file mode 100644
index 0000000..980acfd
Binary files /dev/null and b/MeditationTracker/misc/AlmazniyUm_print.pdf differ
diff --git a/MeditationTracker/misc/Ngoendro_imgs.zip b/MeditationTracker/misc/Ngoendro_imgs.zip
new file mode 100644
index 0000000..ea14d23
Binary files /dev/null and b/MeditationTracker/misc/Ngoendro_imgs.zip differ
diff --git a/MeditationTracker/misc/Ngoendro_imgs/1.jpg b/MeditationTracker/misc/Ngoendro_imgs/1.jpg
new file mode 100644
index 0000000..376577b
Binary files /dev/null and b/MeditationTracker/misc/Ngoendro_imgs/1.jpg differ
diff --git a/MeditationTracker/misc/Ngoendro_imgs/2.jpg b/MeditationTracker/misc/Ngoendro_imgs/2.jpg
new file mode 100644
index 0000000..ec1fe45
Binary files /dev/null and b/MeditationTracker/misc/Ngoendro_imgs/2.jpg differ
diff --git a/MeditationTracker/misc/Ngoendro_imgs/3.jpg b/MeditationTracker/misc/Ngoendro_imgs/3.jpg
new file mode 100644
index 0000000..778139e
Binary files /dev/null and b/MeditationTracker/misc/Ngoendro_imgs/3.jpg differ
diff --git a/MeditationTracker/misc/Ngoendro_imgs/4.jpg b/MeditationTracker/misc/Ngoendro_imgs/4.jpg
new file mode 100644
index 0000000..46698d8
Binary files /dev/null and b/MeditationTracker/misc/Ngoendro_imgs/4.jpg differ
diff --git a/MeditationTracker/misc/Ngoendro_imgs/AlmazniyUm_print.tif b/MeditationTracker/misc/Ngoendro_imgs/AlmazniyUm_print.tif
new file mode 100644
index 0000000..f215c5c
Binary files /dev/null and b/MeditationTracker/misc/Ngoendro_imgs/AlmazniyUm_print.tif differ
diff --git a/MeditationTracker/misc/Ngoendro_imgs/Prostiraniya_print.tif b/MeditationTracker/misc/Ngoendro_imgs/Prostiraniya_print.tif
new file mode 100644
index 0000000..5e91f64
Binary files /dev/null and b/MeditationTracker/misc/Ngoendro_imgs/Prostiraniya_print.tif differ
diff --git "a/MeditationTracker/misc/Ngoendro_imgs/\320\224\320\265\321\200\320\266\320\260\321\202\320\265\320\273\321\214 \320\220\320\273\320\274\320\260\320\267\320\260.jpg" "b/MeditationTracker/misc/Ngoendro_imgs/\320\224\320\265\321\200\320\266\320\260\321\202\320\265\320\273\321\214 \320\220\320\273\320\274\320\260\320\267\320\260.jpg"
new file mode 100644
index 0000000..3054b0c
Binary files /dev/null and "b/MeditationTracker/misc/Ngoendro_imgs/\320\224\320\265\321\200\320\266\320\260\321\202\320\265\320\273\321\214 \320\220\320\273\320\274\320\260\320\267\320\260.jpg" differ
diff --git a/MeditationTracker/misc/Prostiraniya_2010_print.pdf b/MeditationTracker/misc/Prostiraniya_2010_print.pdf
new file mode 100644
index 0000000..372ffb7
Binary files /dev/null and b/MeditationTracker/misc/Prostiraniya_2010_print.pdf differ
diff --git a/MeditationTracker/misc/clean logo.png b/MeditationTracker/misc/clean logo.png
new file mode 100644
index 0000000..a56dab0
Binary files /dev/null and b/MeditationTracker/misc/clean logo.png differ
diff --git a/MeditationTracker/misc/clean logo.svg b/MeditationTracker/misc/clean logo.svg
new file mode 100644
index 0000000..6881fe1
--- /dev/null
+++ b/MeditationTracker/misc/clean logo.svg
@@ -0,0 +1,201 @@
+
+
+
+
diff --git a/MeditationTracker/misc/icon.jpg b/MeditationTracker/misc/icon.jpg
new file mode 100644
index 0000000..cc04938
Binary files /dev/null and b/MeditationTracker/misc/icon.jpg differ
diff --git a/MeditationTracker/misc/icons.png b/MeditationTracker/misc/icons.png
new file mode 100644
index 0000000..0e82ccf
Binary files /dev/null and b/MeditationTracker/misc/icons.png differ
diff --git a/MeditationTracker/misc/logo.svg b/MeditationTracker/misc/logo.svg
new file mode 100644
index 0000000..367c2d4
--- /dev/null
+++ b/MeditationTracker/misc/logo.svg
@@ -0,0 +1,337 @@
+
+
+
+
diff --git a/MeditationTracker/misc/logo.xcf b/MeditationTracker/misc/logo.xcf
new file mode 100644
index 0000000..86028cf
Binary files /dev/null and b/MeditationTracker/misc/logo.xcf differ
diff --git a/MeditationTracker/misc/logo_48.xcf b/MeditationTracker/misc/logo_48.xcf
new file mode 100644
index 0000000..a50f2fa
Binary files /dev/null and b/MeditationTracker/misc/logo_48.xcf differ
diff --git a/MeditationTracker/misc/logo_512.png b/MeditationTracker/misc/logo_512.png
new file mode 100644
index 0000000..6cdfdf5
Binary files /dev/null and b/MeditationTracker/misc/logo_512.png differ
diff --git a/MeditationTracker/misc/logo_512.xcf b/MeditationTracker/misc/logo_512.xcf
new file mode 100644
index 0000000..6fb66d7
Binary files /dev/null and b/MeditationTracker/misc/logo_512.xcf differ
diff --git a/MeditationTracker/misc/logo_72.xcf b/MeditationTracker/misc/logo_72.xcf
new file mode 100644
index 0000000..f57d7a0
Binary files /dev/null and b/MeditationTracker/misc/logo_72.xcf differ
diff --git a/MeditationTracker/misc/orig/diamond_mind_big.jpg b/MeditationTracker/misc/orig/diamond_mind_big.jpg
new file mode 100644
index 0000000..139a60d
Binary files /dev/null and b/MeditationTracker/misc/orig/diamond_mind_big.jpg differ
diff --git a/MeditationTracker/misc/orig/guru_yoga_big.png b/MeditationTracker/misc/orig/guru_yoga_big.png
new file mode 100644
index 0000000..aef4530
Binary files /dev/null and b/MeditationTracker/misc/orig/guru_yoga_big.png differ
diff --git a/MeditationTracker/misc/orig/icon.png b/MeditationTracker/misc/orig/icon.png
new file mode 100644
index 0000000..a07c69f
Binary files /dev/null and b/MeditationTracker/misc/orig/icon.png differ
diff --git a/MeditationTracker/misc/orig/icon_diamond_mind.png b/MeditationTracker/misc/orig/icon_diamond_mind.png
new file mode 100644
index 0000000..ad82e0b
Binary files /dev/null and b/MeditationTracker/misc/orig/icon_diamond_mind.png differ
diff --git a/MeditationTracker/misc/orig/icon_guru_yoga.png b/MeditationTracker/misc/orig/icon_guru_yoga.png
new file mode 100644
index 0000000..c54f653
Binary files /dev/null and b/MeditationTracker/misc/orig/icon_guru_yoga.png differ
diff --git a/MeditationTracker/misc/orig/icon_karmapa.png b/MeditationTracker/misc/orig/icon_karmapa.png
new file mode 100644
index 0000000..ae0bc65
Binary files /dev/null and b/MeditationTracker/misc/orig/icon_karmapa.png differ
diff --git a/MeditationTracker/misc/orig/icon_mandala_offering.png b/MeditationTracker/misc/orig/icon_mandala_offering.png
new file mode 100644
index 0000000..ab7ba6b
Binary files /dev/null and b/MeditationTracker/misc/orig/icon_mandala_offering.png differ
diff --git a/MeditationTracker/misc/orig/icon_refuge.png b/MeditationTracker/misc/orig/icon_refuge.png
new file mode 100644
index 0000000..b1b17d1
Binary files /dev/null and b/MeditationTracker/misc/orig/icon_refuge.png differ
diff --git a/MeditationTracker/misc/orig/karmapa.png b/MeditationTracker/misc/orig/karmapa.png
new file mode 100644
index 0000000..97a5ba3
Binary files /dev/null and b/MeditationTracker/misc/orig/karmapa.png differ
diff --git a/MeditationTracker/misc/orig/launcher_48.png b/MeditationTracker/misc/orig/launcher_48.png
new file mode 100644
index 0000000..403a6c1
Binary files /dev/null and b/MeditationTracker/misc/orig/launcher_48.png differ
diff --git a/MeditationTracker/misc/orig/launcher_72.png b/MeditationTracker/misc/orig/launcher_72.png
new file mode 100644
index 0000000..810895d
Binary files /dev/null and b/MeditationTracker/misc/orig/launcher_72.png differ
diff --git a/MeditationTracker/misc/orig/mandala_offering_big.png b/MeditationTracker/misc/orig/mandala_offering_big.png
new file mode 100644
index 0000000..b7baebb
Binary files /dev/null and b/MeditationTracker/misc/orig/mandala_offering_big.png differ
diff --git a/MeditationTracker/misc/orig/more.png b/MeditationTracker/misc/orig/more.png
new file mode 100644
index 0000000..1c6f7b0
Binary files /dev/null and b/MeditationTracker/misc/orig/more.png differ
diff --git a/MeditationTracker/misc/orig/refuge.png b/MeditationTracker/misc/orig/refuge.png
new file mode 100644
index 0000000..89b03a7
Binary files /dev/null and b/MeditationTracker/misc/orig/refuge.png differ
diff --git a/MeditationTracker/misc/orig/refuge_big.jpg b/MeditationTracker/misc/orig/refuge_big.jpg
new file mode 100644
index 0000000..694b472
Binary files /dev/null and b/MeditationTracker/misc/orig/refuge_big.jpg differ
diff --git a/MeditationTracker/misc/orig/top_logo.png b/MeditationTracker/misc/orig/top_logo.png
new file mode 100644
index 0000000..9505ed6
Binary files /dev/null and b/MeditationTracker/misc/orig/top_logo.png differ
diff --git a/MeditationTracker/misc/orig/top_logo_bg.png b/MeditationTracker/misc/orig/top_logo_bg.png
new file mode 100644
index 0000000..e2b8146
Binary files /dev/null and b/MeditationTracker/misc/orig/top_logo_bg.png differ
diff --git a/MeditationTracker/misc/ormlite/ClickCounter.zip b/MeditationTracker/misc/ormlite/ClickCounter.zip
new file mode 100644
index 0000000..04de103
Binary files /dev/null and b/MeditationTracker/misc/ormlite/ClickCounter.zip differ
diff --git a/MeditationTracker/misc/ormlite/DatabaseHelper.java b/MeditationTracker/misc/ormlite/DatabaseHelper.java
new file mode 100644
index 0000000..9cb2e18
--- /dev/null
+++ b/MeditationTracker/misc/ormlite/DatabaseHelper.java
@@ -0,0 +1,84 @@
+package com.example.helloandroid;
+
+import java.sql.SQLException;
+
+import android.content.Context;
+import android.database.sqlite.SQLiteDatabase;
+import android.util.Log;
+
+import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
+import com.j256.ormlite.dao.BaseDaoImpl;
+import com.j256.ormlite.dao.Dao;
+import com.j256.ormlite.support.ConnectionSource;
+import com.j256.ormlite.table.TableUtils;
+
+/**
+ * Database helper class used to manage the creation and upgrading of your database. This class also usually provides
+ * the DAOs used by the other classes.
+ */
+public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
+
+ // name of the database file for your application -- change to something appropriate for your app
+ private static final String DATABASE_NAME = "helloAndroid.db";
+ // any time you make changes to your database objects, you may have to increase the database version
+ private static final int DATABASE_VERSION = 2;
+
+ // the DAO object we use to access the SimpleData table
+ private Dao simpleDao = null;
+
+ public DatabaseHelper(Context context) {
+ super(context, DATABASE_NAME, null, DATABASE_VERSION);
+ }
+
+ /**
+ * This is called when the database is first created. Usually you should call createTable statements here to create
+ * the tables that will store your data.
+ */
+ @Override
+ public void onCreate(SQLiteDatabase db, ConnectionSource connectionSource) {
+ try {
+ Log.i(DatabaseHelper.class.getName(), "onCreate");
+ TableUtils.createTable(connectionSource, SimpleData.class);
+ } catch (SQLException e) {
+ Log.e(DatabaseHelper.class.getName(), "Can't create database", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * This is called when your application is upgraded and it has a higher version number. This allows you to adjust
+ * the various data to match the new version number.
+ */
+ @Override
+ public void onUpgrade(SQLiteDatabase db, ConnectionSource connectionSource, int oldVersion, int newVersion) {
+ try {
+ Log.i(DatabaseHelper.class.getName(), "onUpgrade");
+ TableUtils.dropTable(connectionSource, SimpleData.class, true);
+ // after we drop the old databases, we create the new ones
+ onCreate(db);
+ } catch (SQLException e) {
+ Log.e(DatabaseHelper.class.getName(), "Can't drop databases", e);
+ throw new RuntimeException(e);
+ }
+ }
+
+ /**
+ * Returns the Database Access Object (DAO) for our SimpleData class. It will create it or just give the cached
+ * value.
+ */
+ public Dao getSimpleDataDao() throws SQLException {
+ if (simpleDao == null) {
+ simpleDao = BaseDaoImpl.createDao(getConnectionSource(), SimpleData.class);
+ }
+ return simpleDao;
+ }
+
+ /**
+ * Close the database connections and clear any cached DAOs.
+ */
+ @Override
+ public void close() {
+ super.close();
+ simpleDao = null;
+ }
+}
diff --git a/MeditationTracker/misc/ormlite/HelloAndroid.java b/MeditationTracker/misc/ormlite/HelloAndroid.java
new file mode 100644
index 0000000..fe0713a
--- /dev/null
+++ b/MeditationTracker/misc/ormlite/HelloAndroid.java
@@ -0,0 +1,88 @@
+package com.example.helloandroid;
+
+import java.sql.SQLException;
+import java.util.List;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.TextView;
+
+import com.j256.ormlite.android.apptools.OpenHelperManager;
+import com.j256.ormlite.android.apptools.OrmLiteBaseActivity;
+import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
+import com.j256.ormlite.android.apptools.OpenHelperManager.SqliteOpenHelperFactory;
+import com.j256.ormlite.dao.Dao;
+
+/**
+ * Our Android UI activity which displays a text window when it is run.
+ */
+public class HelloAndroid extends OrmLiteBaseActivity {
+
+ private final String LOG_TAG = getClass().getSimpleName();
+
+ static {
+ OpenHelperManager.setOpenHelperFactory(new SqliteOpenHelperFactory() {
+ public OrmLiteSqliteOpenHelper getHelper(Context context) {
+ return new DatabaseHelper(context);
+ }
+ });
+ }
+
+ /**
+ * Called when the activity is first created.
+ */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Log.i(LOG_TAG, "creating " + getClass());
+ TextView tv = new TextView(this);
+ doSampleDatabaseStuff("onCreate", tv);
+ setContentView(tv);
+ }
+
+ /**
+ * Do our sample database stuff.
+ */
+ private void doSampleDatabaseStuff(String action, TextView tv) {
+ try {
+ // get our dao
+ Dao simpleDao = getHelper().getSimpleDataDao();
+ // query for all of the data objects in the database
+ List list = simpleDao.queryForAll();
+ // our string builder for building the content-view
+ StringBuilder sb = new StringBuilder();
+ sb.append("got ").append(list.size()).append(" entries in ").append(action).append("\n");
+
+ // if we already have items in the database
+ if (list.size() > 0) {
+ // output the first one
+ SimpleData simple = list.get(0);
+ sb.append("--------------------------------\n");
+ sb.append("[0] = ").append(simple).append("\n");
+ sb.append("--------------------------------\n");
+ // delete it
+ int ret = simpleDao.delete(simple);
+ sb.append("deleted entry = ").append(ret).append("\n");
+ Log.i(LOG_TAG, "deleting simple(" + simple.millis + ") returned " + ret);
+ }
+
+ // create a new simple object
+ long millis = System.currentTimeMillis();
+ SimpleData simple = new SimpleData(millis);
+ // store it in the database
+ int ret = simpleDao.create(simple);
+ Log.i(LOG_TAG, "creating simple(" + millis + ") returned " + ret);
+
+ // output it
+ sb.append("created new entry = ").append(ret).append("\n");
+ sb.append("--------------------------------\n");
+ sb.append("new entry = ").append(simple).append("\n");
+ sb.append("--------------------------------\n");
+ tv.setText(sb.toString());
+ } catch (SQLException e) {
+ Log.e(LOG_TAG, "Database exception", e);
+ return;
+ }
+ }
+}
\ No newline at end of file
diff --git a/MeditationTracker/misc/ormlite/HelloAndroid.zip b/MeditationTracker/misc/ormlite/HelloAndroid.zip
new file mode 100644
index 0000000..2a698ea
Binary files /dev/null and b/MeditationTracker/misc/ormlite/HelloAndroid.zip differ
diff --git a/MeditationTracker/misc/ormlite/NotifyService.zip b/MeditationTracker/misc/ormlite/NotifyService.zip
new file mode 100644
index 0000000..6a0a029
Binary files /dev/null and b/MeditationTracker/misc/ormlite/NotifyService.zip differ
diff --git a/MeditationTracker/misc/ormlite/SimpleData.java b/MeditationTracker/misc/ormlite/SimpleData.java
new file mode 100644
index 0000000..8d51ec4
--- /dev/null
+++ b/MeditationTracker/misc/ormlite/SimpleData.java
@@ -0,0 +1,45 @@
+package com.example.helloandroid;
+
+import java.util.Date;
+
+import com.j256.ormlite.field.DatabaseField;
+
+/**
+ * A simple demonstration object we are creating and persisting to the database.
+ */
+public class SimpleData {
+
+ // id is generated by the database and set on the object automagically
+ @DatabaseField(generatedId = true)
+ int id;
+ @DatabaseField
+ String string;
+ @DatabaseField
+ long millis;
+ @DatabaseField
+ Date date;
+ @DatabaseField
+ boolean even;
+
+ SimpleData() {
+ // needed by ormlite
+ }
+
+ public SimpleData(long millis) {
+ this.date = new Date(millis);
+ this.string = "millis = " + millis;
+ this.millis = millis;
+ this.even = ((millis % 2) == 0);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("id=").append(id);
+ sb.append(", ").append("string=").append(string);
+ sb.append(", ").append("millis=").append(millis);
+ sb.append(", ").append("date=").append(date);
+ sb.append(", ").append("even=").append(even);
+ return sb.toString();
+ }
+}
diff --git a/MeditationTracker/misc/ormlite/ormlite-android-4.2.jar b/MeditationTracker/misc/ormlite/ormlite-android-4.2.jar
new file mode 100644
index 0000000..afbbc6b
Binary files /dev/null and b/MeditationTracker/misc/ormlite/ormlite-android-4.2.jar differ
diff --git a/MeditationTracker/misc/ormlite/ormlite.pdf b/MeditationTracker/misc/ormlite/ormlite.pdf
new file mode 100644
index 0000000..395a01e
Binary files /dev/null and b/MeditationTracker/misc/ormlite/ormlite.pdf differ
diff --git a/MeditationTracker/misc/qrcode.png b/MeditationTracker/misc/qrcode.png
new file mode 100644
index 0000000..0c2dd54
Binary files /dev/null and b/MeditationTracker/misc/qrcode.png differ
diff --git a/MeditationTracker/misc/sqlite/-column b/MeditationTracker/misc/sqlite/-column
new file mode 100644
index 0000000..e69de29
diff --git a/MeditationTracker/misc/sqlite/MediTracker b/MeditationTracker/misc/sqlite/MediTracker
new file mode 100644
index 0000000..a533716
Binary files /dev/null and b/MeditationTracker/misc/sqlite/MediTracker differ
diff --git a/MeditationTracker/misc/sqlite/MediTracker15 b/MeditationTracker/misc/sqlite/MediTracker15
new file mode 100644
index 0000000..41c54c1
Binary files /dev/null and b/MeditationTracker/misc/sqlite/MediTracker15 differ
diff --git a/MeditationTracker/misc/sqlite/MediTracker22 b/MeditationTracker/misc/sqlite/MediTracker22
new file mode 100644
index 0000000..dcec665
Binary files /dev/null and b/MeditationTracker/misc/sqlite/MediTracker22 differ
diff --git a/MeditationTracker/misc/sqlite/create_tables.sql b/MeditationTracker/misc/sqlite/create_tables.sql
new file mode 100644
index 0000000..62cbd55
--- /dev/null
+++ b/MeditationTracker/misc/sqlite/create_tables.sql
@@ -0,0 +1,3 @@
+CREATE TABLE Practices (_ID INTEGER PRIMARY KEY, ISNGONDRO BOOLEAN, SORT_ORDER INTEGER, RESOURCEIDICON INTEGER, RESOURCEIDTITLE INTEGER, TITLE TEXT, ICONURL TEXT, TOTALCOUNT INTEGER, SCHEDULEDCOMPLETION INTEGER);
+
+CREATE TABLE PracticeHistory (_ID INTEGER PRIMARY KEY, PRACTICE_ID INTEGER, PRACTICE_DATE DATETIME default CURRENT_TIMESTAMP, DONE_COUNT INTEGER);
diff --git a/MeditationTracker/misc/sqlite/default_data.sql b/MeditationTracker/misc/sqlite/default_data.sql
new file mode 100644
index 0000000..3245761
--- /dev/null
+++ b/MeditationTracker/misc/sqlite/default_data.sql
@@ -0,0 +1,26 @@
+INSERT INTO Practices (
+ ISNGONDRO,
+ SORT_ORDER,
+ RESOURCEIDICON,
+ RESOURCEIDTITLE,
+ TOTALCOUNT,
+ SCHEDULEDCOMPLETION) values (
+ 1, 0, 100, 500, 111111, 0
+ );
+
+
+INSERT INTO Practices (
+ ISNGONDRO,
+ SORT_ORDER,
+ ICONURL,
+ TITLE,
+ TOTALCOUNT,
+ SCHEDULEDCOMPLETION) values (
+ 1, 1, 'file://icon.png', 'Dorje sempa', 111111, 0
+ );
+
+INSERT INTO PracticeHistory (PRACTICE_ID, DONE_COUNT) values (1, 108);
+INSERT INTO PracticeHistory (PRACTICE_ID, DONE_COUNT) values (1, 114);
+INSERT INTO PracticeHistory (PRACTICE_ID, DONE_COUNT) values (2, 124);
+INSERT INTO PracticeHistory (PRACTICE_ID, DONE_COUNT) values (1, 120);
+
diff --git a/MeditationTracker/misc/sqlite/mediTrack.db b/MeditationTracker/misc/sqlite/mediTrack.db
new file mode 100644
index 0000000..981b5ec
Binary files /dev/null and b/MeditationTracker/misc/sqlite/mediTrack.db differ
diff --git a/MeditationTracker/misc/sqlite/practices_status.sql b/MeditationTracker/misc/sqlite/practices_status.sql
new file mode 100644
index 0000000..1c6d256
--- /dev/null
+++ b/MeditationTracker/misc/sqlite/practices_status.sql
@@ -0,0 +1,8 @@
+select ICONURL, THUMBURL, TITLE, TOTALCOUNT, SCHEDULEDCOMPLETION,
+SUM(DONE_COUNT) as DONE, 100.0*SUM(DONE_COUNT)/TOTALCOUNT as DONE_PERC ,
+MAX(PRACTICE_DATE) AS LAST_PRACTICE
+
+from practices p join practiceHistory ph on p._ID = ph.PRACTICE_ID
+WHERE ISNGONDRO = 1
+GROUP BY p._ID
+ORDER BY SORT_ORDER;
\ No newline at end of file
diff --git a/MeditationTracker/misc/ss/1.png b/MeditationTracker/misc/ss/1.png
new file mode 100644
index 0000000..4910efd
Binary files /dev/null and b/MeditationTracker/misc/ss/1.png differ
diff --git a/MeditationTracker/misc/ss/2.png b/MeditationTracker/misc/ss/2.png
new file mode 100644
index 0000000..c939560
Binary files /dev/null and b/MeditationTracker/misc/ss/2.png differ
diff --git a/MeditationTracker/misc/ss/3.png b/MeditationTracker/misc/ss/3.png
new file mode 100644
index 0000000..cfdcfc0
Binary files /dev/null and b/MeditationTracker/misc/ss/3.png differ
diff --git a/MeditationTracker/misc/ss/4.png b/MeditationTracker/misc/ss/4.png
new file mode 100644
index 0000000..2ab1221
Binary files /dev/null and b/MeditationTracker/misc/ss/4.png differ
diff --git a/MeditationTracker/misc/ss/5.png b/MeditationTracker/misc/ss/5.png
new file mode 100644
index 0000000..cf391a2
Binary files /dev/null and b/MeditationTracker/misc/ss/5.png differ
diff --git a/MeditationTracker/misc/ss/6.png b/MeditationTracker/misc/ss/6.png
new file mode 100644
index 0000000..7b57da9
Binary files /dev/null and b/MeditationTracker/misc/ss/6.png differ
diff --git a/MeditationTracker/misc/ss/all.png b/MeditationTracker/misc/ss/all.png
new file mode 100644
index 0000000..d2571d0
Binary files /dev/null and b/MeditationTracker/misc/ss/all.png differ
diff --git a/MeditationTracker/misc/ss/main.png b/MeditationTracker/misc/ss/main.png
new file mode 100644
index 0000000..4910efd
Binary files /dev/null and b/MeditationTracker/misc/ss/main.png differ
diff --git a/MeditationTracker/misc/ss/refuge.png b/MeditationTracker/misc/ss/refuge.png
new file mode 100644
index 0000000..2efae2d
Binary files /dev/null and b/MeditationTracker/misc/ss/refuge.png differ
diff --git a/MeditationTracker/misc/top_logo.xcf b/MeditationTracker/misc/top_logo.xcf
new file mode 100644
index 0000000..c377cce
Binary files /dev/null and b/MeditationTracker/misc/top_logo.xcf differ
diff --git a/MeditationTracker/misc/tracker_all5.jpg b/MeditationTracker/misc/tracker_all5.jpg
new file mode 100644
index 0000000..e57357d
Binary files /dev/null and b/MeditationTracker/misc/tracker_all5.jpg differ
diff --git a/MeditationTracker/release/1.0.0/MeditationTracker.apk b/MeditationTracker/release/1.0.0/MeditationTracker.apk
new file mode 100644
index 0000000..3ab6213
Binary files /dev/null and b/MeditationTracker/release/1.0.0/MeditationTracker.apk differ
diff --git a/MeditationTracker/release/1.0.1/MeditationTracker.apk b/MeditationTracker/release/1.0.1/MeditationTracker.apk
new file mode 100644
index 0000000..0f1e7a9
Binary files /dev/null and b/MeditationTracker/release/1.0.1/MeditationTracker.apk differ
diff --git a/MeditationTracker/res/drawable/diamond_mind_big.jpg b/MeditationTracker/res/drawable/diamond_mind_big.jpg
new file mode 100644
index 0000000..139a60d
Binary files /dev/null and b/MeditationTracker/res/drawable/diamond_mind_big.jpg differ
diff --git a/MeditationTracker/res/drawable/guru_yoga_big.png b/MeditationTracker/res/drawable/guru_yoga_big.png
new file mode 100644
index 0000000..db4d44b
Binary files /dev/null and b/MeditationTracker/res/drawable/guru_yoga_big.png differ
diff --git a/MeditationTracker/res/drawable/icon.png b/MeditationTracker/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
Binary files /dev/null and b/MeditationTracker/res/drawable/icon.png differ
diff --git a/MeditationTracker/res/drawable/icon_diamond_mind.png b/MeditationTracker/res/drawable/icon_diamond_mind.png
new file mode 100644
index 0000000..0c2e97b
Binary files /dev/null and b/MeditationTracker/res/drawable/icon_diamond_mind.png differ
diff --git a/MeditationTracker/res/drawable/icon_guru_yoga.png b/MeditationTracker/res/drawable/icon_guru_yoga.png
new file mode 100644
index 0000000..83d8ada
Binary files /dev/null and b/MeditationTracker/res/drawable/icon_guru_yoga.png differ
diff --git a/MeditationTracker/res/drawable/icon_karmapa.png b/MeditationTracker/res/drawable/icon_karmapa.png
new file mode 100644
index 0000000..ae0bc65
Binary files /dev/null and b/MeditationTracker/res/drawable/icon_karmapa.png differ
diff --git a/MeditationTracker/res/drawable/icon_mandala_offering.png b/MeditationTracker/res/drawable/icon_mandala_offering.png
new file mode 100644
index 0000000..e03d0d2
Binary files /dev/null and b/MeditationTracker/res/drawable/icon_mandala_offering.png differ
diff --git a/MeditationTracker/res/drawable/icon_refuge.png b/MeditationTracker/res/drawable/icon_refuge.png
new file mode 100644
index 0000000..cb7120c
Binary files /dev/null and b/MeditationTracker/res/drawable/icon_refuge.png differ
diff --git a/MeditationTracker/res/drawable/karmapa.png b/MeditationTracker/res/drawable/karmapa.png
new file mode 100644
index 0000000..97a5ba3
Binary files /dev/null and b/MeditationTracker/res/drawable/karmapa.png differ
diff --git a/MeditationTracker/res/drawable/launcher_48.png b/MeditationTracker/res/drawable/launcher_48.png
new file mode 100644
index 0000000..403a6c1
Binary files /dev/null and b/MeditationTracker/res/drawable/launcher_48.png differ
diff --git a/MeditationTracker/res/drawable/launcher_72.png b/MeditationTracker/res/drawable/launcher_72.png
new file mode 100644
index 0000000..810895d
Binary files /dev/null and b/MeditationTracker/res/drawable/launcher_72.png differ
diff --git a/MeditationTracker/res/drawable/mandala_offering_big.png b/MeditationTracker/res/drawable/mandala_offering_big.png
new file mode 100644
index 0000000..b7baebb
Binary files /dev/null and b/MeditationTracker/res/drawable/mandala_offering_big.png differ
diff --git a/MeditationTracker/res/drawable/more.png b/MeditationTracker/res/drawable/more.png
new file mode 100644
index 0000000..1c6f7b0
Binary files /dev/null and b/MeditationTracker/res/drawable/more.png differ
diff --git a/MeditationTracker/res/drawable/refuge.png b/MeditationTracker/res/drawable/refuge.png
new file mode 100644
index 0000000..89b03a7
Binary files /dev/null and b/MeditationTracker/res/drawable/refuge.png differ
diff --git a/MeditationTracker/res/drawable/refuge_big.jpg b/MeditationTracker/res/drawable/refuge_big.jpg
new file mode 100644
index 0000000..694b472
Binary files /dev/null and b/MeditationTracker/res/drawable/refuge_big.jpg differ
diff --git a/MeditationTracker/res/drawable/top_logo.png b/MeditationTracker/res/drawable/top_logo.png
new file mode 100644
index 0000000..9505ed6
Binary files /dev/null and b/MeditationTracker/res/drawable/top_logo.png differ
diff --git a/MeditationTracker/res/drawable/top_logo_bg.png b/MeditationTracker/res/drawable/top_logo_bg.png
new file mode 100644
index 0000000..e2b8146
Binary files /dev/null and b/MeditationTracker/res/drawable/top_logo_bg.png differ
diff --git a/MeditationTracker/res/drawable/top_logo_no_text.png b/MeditationTracker/res/drawable/top_logo_no_text.png
new file mode 100644
index 0000000..f24ea69
Binary files /dev/null and b/MeditationTracker/res/drawable/top_logo_no_text.png differ
diff --git a/MeditationTracker/res/layout-land/session.xml b/MeditationTracker/res/layout-land/session.xml
new file mode 100644
index 0000000..62a2dc5
--- /dev/null
+++ b/MeditationTracker/res/layout-land/session.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/res/layout/about.xml b/MeditationTracker/res/layout/about.xml
new file mode 100644
index 0000000..d1e697c
--- /dev/null
+++ b/MeditationTracker/res/layout/about.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/res/layout/main.xml b/MeditationTracker/res/layout/main.xml
new file mode 100644
index 0000000..28b9bed
--- /dev/null
+++ b/MeditationTracker/res/layout/main.xml
@@ -0,0 +1,50 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/res/layout/menubar.xml b/MeditationTracker/res/layout/menubar.xml
new file mode 100644
index 0000000..84c5036
--- /dev/null
+++ b/MeditationTracker/res/layout/menubar.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
+
diff --git a/MeditationTracker/res/layout/new_meditation.xml b/MeditationTracker/res/layout/new_meditation.xml
new file mode 100644
index 0000000..51136e9
--- /dev/null
+++ b/MeditationTracker/res/layout/new_meditation.xml
@@ -0,0 +1,85 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MeditationTracker/res/layout/practice.xml b/MeditationTracker/res/layout/practice.xml
new file mode 100644
index 0000000..4443ad2
--- /dev/null
+++ b/MeditationTracker/res/layout/practice.xml
@@ -0,0 +1,128 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/MeditationTracker/res/layout/practice_list_item.xml b/MeditationTracker/res/layout/practice_list_item.xml
new file mode 100644
index 0000000..087bdcd
--- /dev/null
+++ b/MeditationTracker/res/layout/practice_list_item.xml
@@ -0,0 +1,76 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/res/layout/schedule.xml b/MeditationTracker/res/layout/schedule.xml
new file mode 100644
index 0000000..0dedb21
--- /dev/null
+++ b/MeditationTracker/res/layout/schedule.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/res/layout/session.xml b/MeditationTracker/res/layout/session.xml
new file mode 100644
index 0000000..5fe9668
--- /dev/null
+++ b/MeditationTracker/res/layout/session.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/res/menu/image_source.xml b/MeditationTracker/res/menu/image_source.xml
new file mode 100644
index 0000000..0d610ef
--- /dev/null
+++ b/MeditationTracker/res/menu/image_source.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/MeditationTracker/res/menu/main_menu.xml b/MeditationTracker/res/menu/main_menu.xml
new file mode 100644
index 0000000..abb2147
--- /dev/null
+++ b/MeditationTracker/res/menu/main_menu.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/MeditationTracker/res/menu/practice_context_menu.xml b/MeditationTracker/res/menu/practice_context_menu.xml
new file mode 100644
index 0000000..3598218
--- /dev/null
+++ b/MeditationTracker/res/menu/practice_context_menu.xml
@@ -0,0 +1,7 @@
+
+
diff --git a/MeditationTracker/res/menu/timer_menu.xml b/MeditationTracker/res/menu/timer_menu.xml
new file mode 100644
index 0000000..98f7e42
--- /dev/null
+++ b/MeditationTracker/res/menu/timer_menu.xml
@@ -0,0 +1,6 @@
+
+
diff --git a/MeditationTracker/res/values-de/strings.xml b/MeditationTracker/res/values-de/strings.xml
new file mode 100644
index 0000000..1f7ee03
--- /dev/null
+++ b/MeditationTracker/res/values-de/strings.xml
@@ -0,0 +1,57 @@
+
+
+ Löschen
+ Öffnen
+ Fotografieren
+ Bild wählen
+ Klingelton
+ Vibrieren
+ Meditation hinzufügen
+ Countdown
+ Stopuhr
+ Stopuhr oder countdown?
+
+ Ton
+ Session länge min.
+ Mala grösse
+ Mala grösse (leer für standarte)
+ Ngondro zeigen
+
+ Einstellungen
+
+ Meditation Tracker
+ Ngondro
+ Meine Meditationen
+ Guru Yoga
+ Zuflücht
+ Diamond Geist
+ #Mandala Offering
+ Übungs Name
+ Benötigte Menge
+ Abgeschlossene Menge
+ Speichern
+ Bild tippen für ändern
+ Bild Quelle
+ Starten
+ Zeitplan
+ Editiern
+ Geplannt für heute
+ Heute gemacht
+ Tag letztes Übung
+ Erwartete Abschlussdatum
+ Tägliche repetitionen
+ Abschlussdatum
+ Aktion wählen
+ Sie könenn den Ngondro nicht löschen. Wenn nicht gebraucht - kann man es im einstellungen ausschalten.
+ Fehler
+ Information
+ Möchten Sie wirklich dieses Übung löschen?
+ Bestätigen
+ Geplannt:
+ , fertig:
+ Wie viel zugeben?
+ Uhr stoppen
+ Uhr starten
+ Mala zugeben
+ Neues Zahl
+
diff --git a/MeditationTracker/res/values-ru/strings.xml b/MeditationTracker/res/values-ru/strings.xml
new file mode 100644
index 0000000..1423c4d
--- /dev/null
+++ b/MeditationTracker/res/values-ru/strings.xml
@@ -0,0 +1,57 @@
+
+
+ Удалить
+ Открыть
+ Сфотографировать
+ Выбрать фото
+ Звук
+ Вибрация
+ Добавить медитацию
+ Обратный отсчёт
+ Секундомер
+ Секундомер или обратный отсчёт?
+
+ Мелодия
+ Длинна сессии (мин).
+ Размер чёток
+ Размер чёток (оставьте пустым для стандартного)
+ Показывать нёндро
+
+ Настройки
+
+ Meditation Tracker
+ Нёндро
+ Мои медитации
+ Гуру йога
+ Прибежище
+ Алмазный ум
+ Дарение мандал
+ Название практики
+ Необходимое количество
+ Выполненое количество
+ Сохранить
+ Нажмите изображение, чтоб изменить
+ Источник изображения
+ Начать практику
+ Расписание
+
+ Запланировано на сегодня
+ Выполнено сегодня
+ День прошлой практики
+ Ожидаемый день окончания
+ Ежедневных повторов
+ День окончания
+ Выберите действие
+ Нельзя удалить нёндро. Если оно не нужно - его можно скрыть в настройках.
+ Ошибка
+ Информация
+ Точно удалить практику?
+ Подтвердите
+ Запланировано:
+ , выполнено:
+ Сколько добавим?
+ Остановить таймер
+ Запустить таймер
+ Добавить
+ Задать число
+
diff --git a/MeditationTracker/res/values/attrs.xml b/MeditationTracker/res/values/attrs.xml
new file mode 100644
index 0000000..9cb957f
--- /dev/null
+++ b/MeditationTracker/res/values/attrs.xml
@@ -0,0 +1,15 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/res/values/default_theme.xml b/MeditationTracker/res/values/default_theme.xml
new file mode 100644
index 0000000..9f8c877
--- /dev/null
+++ b/MeditationTracker/res/values/default_theme.xml
@@ -0,0 +1,6 @@
+
+
+
+
diff --git a/MeditationTracker/res/values/dummy.xml b/MeditationTracker/res/values/dummy.xml
new file mode 100644
index 0000000..583ca0e
--- /dev/null
+++ b/MeditationTracker/res/values/dummy.xml
@@ -0,0 +1,8 @@
+
+
+- 1
+- 2
+- 3
+- 4
+
+
diff --git a/MeditationTracker/res/values/preference_keys.xml b/MeditationTracker/res/values/preference_keys.xml
new file mode 100644
index 0000000..224e38c
--- /dev/null
+++ b/MeditationTracker/res/values/preference_keys.xml
@@ -0,0 +1,13 @@
+
+
+ prefTimerBuzz
+ Session end notification
+ prefTimerSound
+ prefUseStopWatch
+ prefBellSound
+
+ prefSessionLength
+ prefMalaSize
+ prefShowNgondro
+prefFirstRun
+
diff --git a/MeditationTracker/res/values/strings.xml b/MeditationTracker/res/values/strings.xml
new file mode 100644
index 0000000..6025683
--- /dev/null
+++ b/MeditationTracker/res/values/strings.xml
@@ -0,0 +1,62 @@
+
+
+ Delete
+ Open
+ Take photo
+ Pick picture
+ Sound
+ Buzz
+ Add meditation
+ Edit meditation
+ Countdown
+ Stopwatch
+ Stop watch or countdown?
+
+ Play sound
+ Session length min.
+ Mala size
+ Mala size (leave empty for default)
+ Show Ngondro
+
+ Settings
+
+ Meditation Tracker
+ Ngondro
+ My meditations
+ Guru Yoga
+ Refuge
+ Diamond Mind
+ Mandala Offering
+ Practice name
+ Required repetitions
+ Completed repetitions
+ Save
+ Tap image to choose
+ Choose image source
+ Start practice
+ Schedule
+ Edit
+ Scheduled for today
+ Completed today
+ Last practice date
+ Scheduled completion date
+ Daily repetitions
+ Completion date
+ Choose action
+ You cannot delete Ngondro entries. If you\'re not interested in them - you can hide them in settings.
+ Error
+ Information
+ Are you sure you want to delete this practice?
+ Confirm deletion
+ Scheduled:
+ , completed:
+ How much shall we add this time?
+ Stop timer
+ Start timer
+ Add mala
+ Set count
+
+ About...
+ Support/discussion
+ Go to app\'s blog
+
diff --git a/MeditationTracker/res/xml/preferences.xml b/MeditationTracker/res/xml/preferences.xml
new file mode 100644
index 0000000..87e665a
--- /dev/null
+++ b/MeditationTracker/res/xml/preferences.xml
@@ -0,0 +1,41 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/MeditationTracker/src/com/meditationtracker/ActivityHistory.java b/MeditationTracker/src/com/meditationtracker/ActivityHistory.java
new file mode 100644
index 0000000..7021236
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/ActivityHistory.java
@@ -0,0 +1,43 @@
+package com.meditationtracker;
+
+import java.util.ArrayList;
+import java.util.Calendar;
+import java.util.List;
+
+import android.content.Intent;
+import android.os.Bundle;
+import android.text.format.DateFormat;
+
+public class ActivityHistory
+{
+ private static final int MAX_SIZE = 50;
+ private List history = new ArrayList();
+
+
+
+ public void logOnCreate(Bundle savedInstanceState, Intent intent) {
+ addEntry(String.format("OnCreate {%s %s}", savedInstanceState!=null ? savedInstanceState.toString() : "", intent != null ? intent.toString() : ""));
+
+ }
+
+ public void logOnActivityResult(int requestCode, int resultCode, Intent data) {
+ addEntry(String.format("OnActivityResult {%d %d %s}", requestCode, resultCode, data!=null ? data.toString() : ""));
+ }
+
+ public String getLog() {
+ StringBuilder sb = new StringBuilder();
+ for (String s : history) {
+ sb.append(s);
+ sb.append("\n");
+ }
+
+ return sb.toString();
+ }
+
+ private void addEntry(String entry) {
+ history.add(DateFormat.format("hh:mm:ss", Calendar.getInstance().getTime()) + ": " + entry);
+ if (history.size() > MAX_SIZE){
+ history.remove(0);
+ }
+ }
+}
diff --git a/MeditationTracker/src/com/meditationtracker/DBActivity.java b/MeditationTracker/src/com/meditationtracker/DBActivity.java
new file mode 100644
index 0000000..bcfc03a
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/DBActivity.java
@@ -0,0 +1,22 @@
+package com.meditationtracker;
+
+import android.os.Bundle;
+
+public class DBActivity extends VerboseActivity
+{
+ protected static final int DONE_EDITING = 0;
+ protected PracticeDatabase db;
+ protected PracticeEntry practice;
+ protected long id;
+
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+ id = getIntent().getLongExtra(ExtraKeys.ID, -1);
+
+ db = new PracticeDatabase(this);
+ if (id != -1){
+ practice = db.getPractice(id);
+ }
+ }
+}
diff --git a/MeditationTracker/src/com/meditationtracker/ExtraKeys.java b/MeditationTracker/src/com/meditationtracker/ExtraKeys.java
new file mode 100644
index 0000000..293da0b
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/ExtraKeys.java
@@ -0,0 +1,14 @@
+package com.meditationtracker;
+
+public final class ExtraKeys
+{
+ public static final String ID = "id";
+ public static final String CurrentCount = "currentCount";
+ public static final String TotalCount = "totalCount";
+ public static final String ImgURL = "imgUrl";
+ public static final String ThumbURL = "thumbUrl";
+ public static final String Title = "title";
+ public static final String ScheduledCount = "scheduledCount";
+ public static final String MalaCount = "malaCount";
+ public static final String MalaSize = "malaSize";
+}
diff --git a/MeditationTracker/src/com/meditationtracker/ImagePicker.java b/MeditationTracker/src/com/meditationtracker/ImagePicker.java
new file mode 100644
index 0000000..8b212dc
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/ImagePicker.java
@@ -0,0 +1,139 @@
+package com.meditationtracker;
+
+import java.io.File;
+
+import doo.util.Util;
+
+import android.app.Activity;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.DialogInterface.OnClickListener;
+import android.net.Uri;
+import android.os.Bundle;
+
+public class ImagePicker extends VerboseActivity
+{
+ private final String SDCARD_PATH = "/sdcard/"; //TODO: make it sdcard independent
+ private final String TEMP_PATH = SDCARD_PATH + "temp_picture.jpeg";
+ public static final String TAKE_PICTURE = "take-picture";
+
+ private String cropFileName;
+
+ private static final int SELECT_IMAGE = 0;
+ private static final int TAKE_PHOTO = 1;
+ private static final int REQUEST_CROP_PHOTO = 2;
+
+ //private Uri imgCaptureUri;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().getBooleanExtra(TAKE_PICTURE, false))
+ {
+ File tempFile = getTempFile();
+ if (tempFile.exists()){
+ tempFile.delete();
+ }
+ startActivityForResult(new Intent(android.provider.MediaStore.ACTION_IMAGE_CAPTURE).putExtra(android.provider.MediaStore.EXTRA_OUTPUT, getTempUri()/*android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI*/), TAKE_PHOTO);
+ }
+ else
+ {
+ startActivityForResult(new Intent(Intent.ACTION_PICK, android.provider.MediaStore.Images.Media.INTERNAL_CONTENT_URI), SELECT_IMAGE);
+ }
+ }
+
+ private Uri getTempUri()
+ {
+ return Uri.fromFile(getTempFile());
+ }
+
+ private File getTempFile()
+ {
+ return new File(TEMP_PATH);
+ }
+
+ private void generateCropFileName(){
+ cropFileName = Math.random() + ".jpg";
+ }
+
+ private File getCropFile() {
+ return new File(SDCARD_PATH + cropFileName);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ File cropFile = getCropFile();
+ if (cropFile.exists())
+ {
+ cropFile.delete();
+ }
+
+ if (resultCode != RESULT_OK)
+ {
+ setResult(Activity.RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ Uri uri = null;
+ switch (requestCode) {
+ case TAKE_PHOTO:
+ if ((data == null || (uri = data.getData()) == null) && getTempFile().exists()) {
+ uri = getTempUri();
+ }
+ //cropImage(uri);
+ //break;
+ case SELECT_IMAGE:
+ generateCropFileName();
+ cropImage(uri);
+ break;
+ case REQUEST_CROP_PHOTO:
+ setResult(Activity.RESULT_OK, new Intent(data));
+ finish();
+ break;
+ }
+
+ }
+
+ private void cropImage(Uri imgUri)
+ {
+ if (imgUri == null) {
+ setResult(Activity.RESULT_CANCELED);
+ Util.showWhateverError(this, "The image disappeared. Impermanence.", new OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int which)
+ {
+ ImagePicker.this.finish();
+ }
+ });
+ return;
+ }
+
+ File cropFile = getCropFile();
+ getTempFile().renameTo(cropFile);
+
+ imgUri = Uri.fromFile(cropFile);
+
+ Intent intent = new Intent("com.android.camera.action.CROP", imgUri);
+ intent.setDataAndType(imgUri, "image/*");
+/* if (myIntent.getStringExtra("mimeType") != null) {
+ intent.setDataAndType(myIntent.getData(), myIntent.getStringExtra("mimeType"));
+ }*/
+ intent.putExtra("crop", true);
+ intent.putExtra("scale", true);
+ intent.putExtra("noFaceDetection", true);
+ intent.putExtra("return-data", true);
+
+ intent.putExtra("aspectX", 150);
+ intent.putExtra("aspectY", 196);
+ intent.putExtra("outputX", 150);
+ intent.putExtra("outputY", 196);
+ startActivityForResult(intent, REQUEST_CROP_PHOTO);
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/MainActivity.java b/MeditationTracker/src/com/meditationtracker/MainActivity.java
new file mode 100644
index 0000000..1fa522b
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/MainActivity.java
@@ -0,0 +1,267 @@
+package com.meditationtracker;
+
+import android.app.AlertDialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.DialogInterface.OnClickListener;
+import android.os.Bundle;
+import android.os.Environment;
+import android.preference.PreferenceManager;
+import android.util.Log;
+import android.view.ContextMenu;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewParent;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.widget.AdapterView;
+import android.widget.ListView;
+import android.widget.SimpleCursorAdapter;
+import android.widget.Toast;
+import android.widget.AdapterView.AdapterContextMenuInfo;
+import android.widget.AdapterView.OnItemClickListener;
+
+public class MainActivity extends VerboseActivity
+{
+ protected static final int SETTINGS_DONE = 0;
+ protected static final int NEW_OR_EDIT_PRACTICE_DONE = 1;
+ protected static final int PRACTICE_DONE = 2;
+ private PracticeDatabase db;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.main);
+
+ db = new PracticeDatabase(this);
+ ensureNgondroDefaultsOnFirstRun();
+
+ UpdateUI();
+
+ String absolutePath = Environment.getExternalStorageDirectory().getAbsolutePath();
+ Toast.makeText(this, absolutePath, Toast.LENGTH_LONG);
+
+ }
+
+ private void UpdateUI()
+ {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ ListView lv = (ListView) findViewById(R.id.ngondroList);
+
+ int ngondroVisible = preferences.getBoolean(getString(R.string.prefShowNgondro), true) ? View.VISIBLE
+ : View.GONE;
+
+ lv.setVisibility(ngondroVisible);
+ findViewById(R.id.ngondroTitle).setVisibility(ngondroVisible);
+ findViewById(R.id.customPracticesTitle).setVisibility(ngondroVisible);
+
+ SimpleCursorAdapter viewAdapter;
+ if (ngondroVisible != View.GONE)
+ {
+ viewAdapter = new SimpleCursorAdapter(this, R.layout.practice_list_item, db.getPracticesStatuses(true),
+ new String[] { PracticeDatabase.KEY_THUMBURL, PracticeDatabase.KEY_TITLE, PracticeDatabase.KEY_SCHEDULEDCOUNT, PracticeDatabase.KEY_DONE }, new int[] {
+ R.id.practiceImg, R.id.practiceTitle, R.id.scheduledText, R.id.completedText });
+ viewAdapter.setViewBinder(new SmartViewBinder());
+
+ lv.setAdapter(viewAdapter);
+ registerForContextMenu(lv);
+ lv.setOnItemClickListener(practiceClick);
+ }
+
+ lv = (ListView) findViewById(R.id.customList);
+
+ viewAdapter = new SimpleCursorAdapter(this, R.layout.practice_list_item, db.getPracticesStatuses(false),
+ new String[] { PracticeDatabase.KEY_THUMBURL, PracticeDatabase.KEY_TITLE, PracticeDatabase.KEY_SCHEDULEDCOUNT, PracticeDatabase.KEY_DONE }, new int[] {
+ R.id.practiceImg, R.id.practiceTitle, R.id.scheduledText, R.id.completedText });
+ viewAdapter.setViewBinder(new SmartViewBinder());
+
+ lv.setAdapter(viewAdapter);
+ registerForContextMenu(lv);
+ lv.setOnItemClickListener(practiceClick);
+
+ //db.dumpNgondroStatus();
+ }
+
+
+
+ @Override
+ protected void onPostCreate(Bundle savedInstanceState) {
+ // TODO Auto-generated method stub
+ super.onPostCreate(savedInstanceState);
+ }
+
+ private void ensureNgondroDefaultsOnFirstRun()
+ {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ if (preferences.getBoolean(getString(R.string.prefFirstRun), true) && !db.hasNgondroEntries(this))
+ {
+ db.insertPractice(true, 0, getResources().getString(R.string.refuge), R.drawable.refuge, R.drawable.icon_refuge, 111111);
+ db.insertPractice(true, 1, getResources().getString(R.string.diamondMind), R.drawable.diamond_mind_big, R.drawable.icon_diamond_mind,
+ 111111);
+ db.insertPractice(true, 2, getResources().getString(R.string.mandalaOffering),
+ R.drawable.mandala_offering_big, R.drawable.icon_mandala_offering, 111111);
+ db.insertPractice(true, 3, getResources().getString(R.string.guruYoga), R.drawable.guru_yoga_big, R.drawable.icon_guru_yoga, 111111);
+
+ SharedPreferences.Editor editor = preferences.edit();
+ editor.putBoolean(getString(R.string.prefFirstRun), false);
+ editor.commit();
+ }
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.main_menu, menu);
+ return true;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ case R.id.settingsMenuItem:
+ startActivityForResult(new Intent(this, SettingsActivity.class), SETTINGS_DONE);
+
+ return true;
+ case R.id.addPracticeMenuItem:
+ editPractice(-1);
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ switch (requestCode)
+ {
+ case SETTINGS_DONE:
+ break;
+ case NEW_OR_EDIT_PRACTICE_DONE:
+ if (resultCode == RESULT_OK)
+ {
+
+ }
+ break;
+ case PRACTICE_DONE:
+ break;
+ default:
+ break;
+ }
+
+ UpdateUI();
+ }
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
+ {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.practice_context_menu, menu);
+ menu.setHeaderTitle(R.string.chooseAction);
+ }
+
+ private void openPractice(long id)
+ {
+ startActivityForResult(new Intent(this, PracticeActivity.class).putExtra("id", id), PRACTICE_DONE);
+ }
+
+ private void editPractice(long id)
+ {
+ startActivityForResult(new Intent(this, NewOrEditPracticeDBActivity.class).putExtra("id", id),
+ NEW_OR_EDIT_PRACTICE_DONE);
+ }
+
+ private void deletePractice(long id)
+ {
+ db.deletePractice(id);
+ }
+
+ private OnItemClickListener practiceClick = new OnItemClickListener()
+ {
+
+ public void onItemClick(AdapterView> parent, View view, int position, long id)
+ {
+ Log.d("MTRK", "selected: " + id);
+ openPractice(id);
+ }
+ };
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item)
+ {
+ final AdapterContextMenuInfo menuInfo = (AdapterContextMenuInfo) item.getMenuInfo();
+
+ switch (item.getItemId())
+ {
+ case R.id.openPractice:
+ openPractice(menuInfo.id);
+ break;
+ case R.id.editPractice:
+ // if (!isNgondroEditAction(menuInfo))
+ editPractice(menuInfo.id);
+ break;
+ case R.id.deletePractice:
+ if (!isNgondroEditAction(menuInfo))
+ {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage(R.string.confirmDeletionMsg).setTitle(R.string.confirmDeletionTitle).setIcon(
+ android.R.drawable.ic_dialog_alert).setPositiveButton(android.R.string.yes, new OnClickListener()
+ {
+ public void onClick(DialogInterface dialog, int which)
+ {
+ deletePractice(menuInfo.id);
+ UpdateUI();
+ }
+ }).setNegativeButton(android.R.string.no, null).show();
+
+ }
+ break;
+ }
+ return super.onContextItemSelected(item);
+ }
+
+ private boolean isNgondroEditAction(AdapterContextMenuInfo info)
+ {
+ if (isChildOf(info.targetView, R.id.ngondroList))
+ {
+ ShowNoEditNgondroMessage();
+ return true;
+ } else
+ return false;
+
+ }
+
+ private void ShowNoEditNgondroMessage()
+ {
+ AlertDialog.Builder builder = new AlertDialog.Builder(this);
+ builder.setMessage(R.string.msgNoEditNgondro).setTitle(R.string.info).setIcon(
+ android.R.drawable.ic_dialog_info).setPositiveButton(android.R.string.ok, null).show();
+
+ }
+
+ private boolean isChildOf(View child, int parentId)
+ {
+ ViewParent cur = (ViewParent) child;
+ while (cur != null)
+ {
+ if (cur instanceof View)
+ if (((View) cur).getId() == parentId)
+ return true;
+
+ cur = cur.getParent();
+ }
+
+ return false;
+ }
+}
\ No newline at end of file
diff --git a/MeditationTracker/src/com/meditationtracker/NewOrEditPracticeDBActivity.java b/MeditationTracker/src/com/meditationtracker/NewOrEditPracticeDBActivity.java
new file mode 100644
index 0000000..0b4996d
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/NewOrEditPracticeDBActivity.java
@@ -0,0 +1,66 @@
+package com.meditationtracker;
+
+import doo.util.Pair;
+import doo.util.Util;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+public class NewOrEditPracticeDBActivity extends DBActivity
+{
+ private boolean isEdit;
+
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ Intent screenIntent = new Intent(this, NewOrEditPracticeScreenActivity.class).putExtra(ExtraKeys.ID, id);
+ if (isEdit = (id != -1)){
+ screenIntent.putExtra(ExtraKeys.Title, practice.getValues().getAsString(PracticeDatabase.KEY_TITLE));
+ screenIntent.putExtra(ExtraKeys.ImgURL, practice.getValues().getAsString(PracticeDatabase.KEY_ICONURL));
+ screenIntent.putExtra(ExtraKeys.ThumbURL, practice.getValues().getAsString(PracticeDatabase.KEY_THUMBURL));
+ screenIntent.putExtra(ExtraKeys.TotalCount, practice.getValues().getAsInteger(PracticeDatabase.KEY_TOTALCOUNT));
+ screenIntent.putExtra(ExtraKeys.CurrentCount, practice.getValues().getAsInteger(PracticeDatabase.KEY_DONE));
+ screenIntent.putExtra(ExtraKeys.MalaSize, practice.getValues().getAsInteger(PracticeDatabase.KEY_MALASIZE));
+ }
+
+ startActivityForResult(screenIntent, DONE_EDITING);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode != Activity.RESULT_OK){
+ setResult(Activity.RESULT_CANCELED);
+ }
+ else {
+ Bundle extras = data.getExtras();
+
+ String title = extras.getString(ExtraKeys.Title);
+ String imgUrl = extras.getString(ExtraKeys.ImgURL);
+ String thumbUrl = extras.getString(ExtraKeys.ThumbURL);
+
+ Pair parsed = Util.tryParse(extras.getString(ExtraKeys.TotalCount));
+ int totalCount = parsed._1 ? parsed._2.intValue() : 0;
+
+ parsed = Util.tryParse(extras.getString(ExtraKeys.CurrentCount));
+ int currentCount = parsed._1 ? parsed._2.intValue() : 0;
+
+ parsed = Util.tryParse(extras.getString(ExtraKeys.MalaSize));
+ int malaSize = parsed._1 ? parsed._2.intValue() : 0;
+
+ if (isEdit)
+ db.updatePractice(new PracticeEntry((int)id, title, imgUrl, thumbUrl, totalCount, currentCount, malaSize));
+ else
+ db.insertPractice(new PracticeEntry(title, imgUrl, thumbUrl, totalCount, currentCount, malaSize));
+
+ setResult(RESULT_OK);
+ }
+
+ finish();
+ }
+
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/NewOrEditPracticeScreenActivity.java b/MeditationTracker/src/com/meditationtracker/NewOrEditPracticeScreenActivity.java
new file mode 100644
index 0000000..2062c34
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/NewOrEditPracticeScreenActivity.java
@@ -0,0 +1,196 @@
+package com.meditationtracker;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import com.meditationtracker.controls.MenuBar;
+
+import doo.util.Pair;
+import doo.util.Util;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Bitmap;
+import android.net.Uri;
+import android.os.Bundle;
+import android.view.ContextMenu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnClickListener;
+import android.view.View.OnTouchListener;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.Toast;
+
+public class NewOrEditPracticeScreenActivity extends VerboseActivity
+{
+ private static final String THUMBNAIL_PREFIX = "th_";
+
+ private static final int SELECT_IMAGE = 0;
+
+ private String imgUrl;
+ private String thumbUrl;
+
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ setResult(RESULT_CANCELED);
+
+ setContentView(R.layout.new_meditation);
+
+ Toast.makeText(this, getString(R.string.tapImgToChoose), Toast.LENGTH_SHORT).show();
+
+ View v = findViewById(R.id.practiceImage);
+ v.setOnTouchListener(imgTouchListener);
+ registerForContextMenu(v);
+
+ String title = this.getString(R.string.newPractice);
+
+ Bundle extras = getIntent().getExtras();
+ if (extras.containsKey(ExtraKeys.ID) && extras.getLong(ExtraKeys.ID) != -1){
+
+ title = this.getString(R.string.editPractice);
+
+ thumbUrl = extras.getString(ExtraKeys.ThumbURL);
+ imgUrl = extras.getString(ExtraKeys.ImgURL);
+
+ Pair parsed = Util.tryParse(imgUrl);
+ if (parsed._1)
+ ((ImageView)v).setImageResource(parsed._2.intValue());
+ else
+ if (imgUrl != null)
+ ((ImageView)v).setImageURI(Uri.parse(imgUrl));
+
+ ((TextView)findViewById(R.id.textPracticeName)).setText(extras.getString(ExtraKeys.Title));
+ ((TextView)findViewById(R.id.textRepetitionCount)).setText(String.valueOf(extras.getInt(ExtraKeys.TotalCount)));
+ ((TextView)findViewById(R.id.textCompletedRepetitions)).setText(String.valueOf(extras.getInt(ExtraKeys.CurrentCount)));
+
+ int malaSize = extras.getInt(ExtraKeys.MalaSize);
+ if (malaSize != 0)
+ {
+ ((TextView)findViewById(R.id.textMalaSize)).setText(String.valueOf(malaSize));
+ }
+ }
+
+ findViewById(R.id.saveButton).setOnClickListener(saveClicked);
+ ((MenuBar)findViewById(R.id.menuBar)).setText(title);
+ }
+
+ private OnTouchListener imgTouchListener = new OnTouchListener()
+ {
+ public boolean onTouch(View v, MotionEvent event)
+ {
+ if (event.getAction() == MotionEvent.ACTION_UP){
+
+ /*startActivityForResult(Intent.createChooser(
+ new Intent(Intent.ACTION_GET_CONTENT).
+ addCategory(Intent.CATEGORY_OPENABLE).
+ setType("image/*"), getString(R.string.imageSourceTitle)), SELECT_IMAGE);*/
+ openContextMenu(v);
+ }
+ return true;
+ }
+ };
+
+ @Override
+ public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo)
+ {
+ super.onCreateContextMenu(menu, v, menuInfo);
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.image_source, menu);
+ menu.setHeaderTitle(R.string.imageSourceTitle);
+ }
+
+ @Override
+ public boolean onContextItemSelected(MenuItem item)
+ {
+ switch (item.getItemId()) {
+ case R.id.browseImage:
+ startActivityForResult(new Intent(this, ImagePicker.class).putExtra(ImagePicker.TAKE_PICTURE, false), SELECT_IMAGE);
+ return true;
+ case R.id.takePhoto:
+ startActivityForResult(new Intent(this, ImagePicker.class).putExtra(ImagePicker.TAKE_PICTURE, true), SELECT_IMAGE);
+ return true;
+ }
+
+ return super.onContextItemSelected(item);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode != RESULT_OK)
+ return;
+
+ ImageView v = (ImageView)findViewById(R.id.practiceImage);
+ if (requestCode == SELECT_IMAGE) {
+ final Bundle extras = data.getExtras();
+ if (extras != null) {
+ Bitmap photo = extras.getParcelable("data");
+ if (photo != null) {
+ FileOutputStream fos;
+ try
+ {
+ File mainImageFile = File.createTempFile("img", ".png", getFilesDir());
+ String practiceImageName = mainImageFile.getName();
+
+ fos = openFileOutput(practiceImageName, Context.MODE_PRIVATE);
+ photo.compress(Bitmap.CompressFormat.PNG, 95, fos);
+ fos.close();
+
+ fos = openFileOutput(THUMBNAIL_PREFIX + practiceImageName, Context.MODE_PRIVATE);
+ Bitmap.createScaledBitmap(photo, 35, 46, false).compress(Bitmap.CompressFormat.JPEG, 95, fos);
+ fos.close();
+
+ imgUrl = PracticeImageProvider.URI_PREFIX + File.separator + practiceImageName;
+ thumbUrl = PracticeImageProvider.URI_PREFIX + File.separator + THUMBNAIL_PREFIX + practiceImageName;
+ v.setImageURI(Uri.parse(imgUrl));
+
+ updateResult();
+ } catch (FileNotFoundException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IOException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ }
+ }
+
+ private OnClickListener saveClicked = new OnClickListener()
+ {
+ public void onClick(View v)
+ {
+ updateResult();
+ finish();
+ }
+ };
+
+
+ private void updateResult()
+ {
+ setResult(Activity.RESULT_OK,
+ new Intent().
+ putExtra(ExtraKeys.ImgURL, imgUrl == null ? String.valueOf(R.drawable.karmapa) : imgUrl).
+ putExtra(ExtraKeys.ThumbURL, thumbUrl == null ? String.valueOf(R.drawable.icon_karmapa) : thumbUrl).
+ putExtra(ExtraKeys.Title, ((TextView)findViewById(R.id.textPracticeName)).getText().toString()).
+ putExtra(ExtraKeys.TotalCount, ((TextView)findViewById(R.id.textRepetitionCount)).getText().toString()).
+ putExtra(ExtraKeys.CurrentCount, ((TextView)findViewById(R.id.textCompletedRepetitions)).getText().toString()).
+ putExtra(ExtraKeys.MalaSize, ((TextView)findViewById(R.id.textMalaSize)).getText().toString()));
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/NoScrollListView.java b/MeditationTracker/src/com/meditationtracker/NoScrollListView.java
new file mode 100644
index 0000000..2d7dc5e
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/NoScrollListView.java
@@ -0,0 +1,49 @@
+package com.meditationtracker;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.View;
+import android.widget.ListView;
+
+public class NoScrollListView extends ListView
+{
+
+ public NoScrollListView(Context context)
+ {
+ super(context, null);
+ }
+
+ public NoScrollListView(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
+ {
+ super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.UNSPECIFIED, 0) /*heightMeasureSpec*/);
+
+ int childHeight = getMeasuredHeight() - (getListPaddingTop() + getListPaddingBottom() + getVerticalFadingEdgeLength() * 2);
+
+ // on a first run let's have a space for at least one child so it'll trigger remeasurement
+ int fullHeight = getListPaddingTop() + getListPaddingBottom() + /*getVerticalFadingEdgeLength() * 2*/ + childHeight*(getCount());
+
+ int newChildHeight = 0;
+ for (int x = 0; x parsed = Util.tryParse(imgUrl);
+ if (parsed._1)
+ ((ImageView) findViewById(R.id.imgPractice)).setImageResource(parsed._2.intValue());
+ else if (imgUrl != null)
+ ((ImageView) findViewById(R.id.imgPractice)).setImageURI(Uri.parse(imgUrl));
+
+ } else
+ {
+ //TODO: do an error here, we cannot go into non-existent practice
+ }
+ }
+
+ private void calculateScheduledEnd()
+ {
+ Pair scheduledCountParsed = Util.tryParse(scheduledCount);
+
+ if (scheduledCountParsed._1 && scheduledCountParsed._2 != 0) {
+ Pair totalCountParsed = Util.tryParse(totalCount);
+ Pair currentCountParsed = Util.tryParse(currentCount);
+
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DAY_OF_YEAR, (int) ((totalCountParsed._2-currentCountParsed._2)/scheduledCountParsed._2) + 1);
+
+ ((TextView) findViewById(R.id.textScheduledEndDate)).setText(Util.formatCalendar(cal, this));
+ }
+ else
+ ((TextView) findViewById(R.id.textScheduledEndDate)).setText("-");
+ }
+
+ private String setText(int fieldId, String dbKey, PracticeEntry practice)
+ {
+ String result;
+ ((TextView) findViewById(fieldId)).setText(result = practice.getValues().getAsString(dbKey));
+
+ return result;
+ }
+
+ private OnClickListener scheduleClicked = new OnClickListener()
+ {
+ public void onClick(View v)
+ {
+ startActivityForResult(new Intent(PracticeActivity.this, ScheduleActivity.class).putExtra(
+ ExtraKeys.ScheduledCount, scheduledCount).putExtra(ExtraKeys.TotalCount, totalCount).putExtra(
+ ExtraKeys.CurrentCount, currentCount), SCHEDULE_CHANGED);
+ }
+ };
+
+ private OnClickListener editClicked = new OnClickListener()
+ {
+ public void onClick(View v)
+ {
+ startActivityForResult(new Intent(PracticeActivity.this, NewOrEditPracticeDBActivity.class).putExtra(
+ ExtraKeys.ID, id), PRACTICE_CHANGED);
+ }
+ };
+
+ private OnClickListener startClicked = new OnClickListener()
+ {
+ public void onClick(View v)
+ {
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(PracticeActivity.this);
+
+ long malaSize = 108;
+
+ if (customMalaSize > 0)
+ {
+ malaSize = customMalaSize;
+ }
+ else
+ {
+ Pair parsed = Util.tryParse(preferences.getString(getString(R.string.prefMalaSize), "108"));
+ if (parsed._1){
+ malaSize = parsed._2.intValue();
+ }
+ }
+
+ startActivityForResult(
+ new Intent(PracticeActivity.this, SessionActivity.class).
+ putExtra(ExtraKeys.ImgURL, imgUrl).
+ putExtra(ExtraKeys.MalaSize, malaSize).
+ putExtra(ExtraKeys.Title, title),
+ SESSION_DONE);
+ }
+
+
+ };
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ super.onActivityResult(requestCode, resultCode, data);
+
+ if (resultCode != RESULT_OK)
+ return;
+
+ switch (requestCode)
+ {
+ case SCHEDULE_CHANGED:
+ if (data.hasExtra(ExtraKeys.ScheduledCount))
+ {
+ practice.getValues().put(PracticeDatabase.KEY_SCHEDULEDCOUNT,
+ data.getLongExtra(ExtraKeys.ScheduledCount, 0));
+ db.updatePractice(practice);
+ }
+ break;
+ case PRACTICE_CHANGED:
+ break;
+ case SESSION_DONE:
+ /*SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+ int malaSize = 108;
+ Pair parsed = Util.tryParse(preferences.getString(getString(R.string.prefMalaSize), "108"));
+ if (parsed._1){
+ malaSize = parsed._2.intValue();
+ }*/
+
+ int addCount = data.getExtras().getInt(ExtraKeys.MalaCount);
+ if (addCount != 0) {
+ db.insertSession((int) id, addCount);
+ }
+ }
+
+ updateView();
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/PracticeDatabase.java b/MeditationTracker/src/com/meditationtracker/PracticeDatabase.java
new file mode 100644
index 0000000..406799f
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/PracticeDatabase.java
@@ -0,0 +1,395 @@
+package com.meditationtracker;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.Map.Entry;
+
+import android.content.ContentValues;
+import android.content.Context;
+import android.database.Cursor;
+import android.database.SQLException;
+import android.database.sqlite.SQLiteDatabase;
+import android.database.sqlite.SQLiteOpenHelper;
+import android.database.sqlite.SQLiteStatement;
+import android.provider.BaseColumns;
+import android.util.Log;
+
+public class PracticeDatabase
+{
+ private static final String MTRK_LOG_KEY = "MTRK";
+ private static final String DBNAME = "MediTracker";
+ private static final int DBVERSION = 1;
+
+ private static final String PRACTICE_TABLE_NAME = "Practices";
+ private static final String PRACTICE_HISTORY_TABLE_NAME = "PracticeHistory";
+ private static final String DONE_TODAY_VIEW_NAME = "DONE_TODAY";
+
+ public static final String KEY_ISNGONDRO = "ISNGONDRO";
+ public static final String KEY_ORDER = "SORT_ORDER";
+ public static final String KEY_TITLE = "TITLE";
+ public static final String KEY_TOTALCOUNT = "TOTALCOUNT";
+ public static final String KEY_SCHEDULEDCOMPLETION = "SCHEDULEDCOMPLETION";
+ public static final String KEY_SCHEDULEDCOUNT = "SCHEDULEDCOUNT";
+ public static final String KEY_ICONURL = "ICONURL";
+ public static final String KEY_THUMBURL = "THUMBURL";
+ public static final String KEY_MALASIZE = "MALASIZE";
+
+ public static final String KEY_DONE = "DONE";
+ public static final String KEY_DONE_PERC = "DONE_PERC";
+ public static final String KEY_DONE_TODAY = "DONE_TODAY";
+ public static final String KEY_LAST_PRACTICE = "LAST_PRACTICE";
+
+ public static final String KEY_PRACTICE_ID = "PRACTICE_ID";
+ public static final String KEY_DATE = "PRACTICE_DATE";
+ public static final String KEY_COUNT = "DONE_COUNT";
+
+ private static final String PRACTICE_TABLE_CREATE = "CREATE TABLE " + PRACTICE_TABLE_NAME + " (" +
+ BaseColumns._ID + " INTEGER PRIMARY KEY, " +
+ KEY_ISNGONDRO + " BOOLEAN NOT NULL DEFAULT 0, " +
+ KEY_ORDER + " INTEGER NOT NULL, " +
+ KEY_TITLE + " TEXT NOT NULL, " +
+ KEY_ICONURL + " TEXT NOT NULL, " +
+ KEY_THUMBURL + " TEXT NOT NULL, " +
+ KEY_TOTALCOUNT + " INTEGER NOT NULL DEFAULT 111111, " +
+ KEY_SCHEDULEDCOUNT + " INTEGER NOT NULL DEFAULT 0, " +
+ KEY_MALASIZE + " INTEGER NOT NULL DEFAULT 0) ";
+ //KEY_SCHEDULEDCOMPLETION + " TEXT NOT NULL DEFAULT 0)";
+
+ private static final String PRACTICE_HISTORY_TABLE_CREATE = "CREATE TABLE " + PRACTICE_HISTORY_TABLE_NAME + " (" +
+ BaseColumns._ID + " INTEGER PRIMARY KEY, " +
+ KEY_PRACTICE_ID + " INTEGER NOT NULL, " +
+ KEY_DATE + " TEXT default CURRENT_DATE, " +
+ KEY_COUNT + " INTEGER NOT NULL) ";
+
+ private static final String DONE_TODAY_VIEW_CREATE = String.format(
+ "CREATE VIEW '%s' AS SELECT %s, SUM(%s) AS %s " +
+ "FROM %s WHERE %s = CURRENT_DATE GROUP BY %s",
+ DONE_TODAY_VIEW_NAME, KEY_PRACTICE_ID, KEY_COUNT, KEY_DONE_TODAY,
+ PRACTICE_HISTORY_TABLE_NAME, KEY_DATE, KEY_PRACTICE_ID);
+
+
+
+ private static final String INSERT_PRACTICE_QUERY = "INSERT INTO " + PRACTICE_TABLE_NAME + " (" +
+ KEY_ISNGONDRO + ", " +
+ KEY_ORDER + ", " +
+ KEY_TITLE + ", " +
+ KEY_ICONURL + ", " +
+ KEY_THUMBURL + ", " +
+ KEY_TOTALCOUNT + ") " +
+ "VALUES (?, ?, ?, ?, ?, ?)";
+
+ private static final String INSERT_SESSION_QUERY = "INSERT INTO " + PRACTICE_HISTORY_TABLE_NAME + " (" +
+ KEY_PRACTICE_ID + ", " + KEY_DATE + ", " + KEY_COUNT + ") " + " VALUES (?, ?, ?)";
+
+ private static final String INSERT_TODAY_SESSION_QUERY = "INSERT INTO " + PRACTICE_HISTORY_TABLE_NAME + " (" +
+ KEY_PRACTICE_ID + ", " + KEY_COUNT + ") " + " VALUES (?, ?)";
+
+ private static final String NGONDRO_CHEK_QUERY = "SELECT COUNT(" + BaseColumns._ID + ") FROM " + PRACTICE_TABLE_NAME
+ + " WHERE " + KEY_ISNGONDRO + " = 1";
+
+ private static final String PRACTICES_STATUS_PREFIX = String.format("SELECT p.%s AS %s, %s, %s, %s, %s, %s, %s, " +
+ "IFNULL(SUM(%s), 0) as %s, IFNULL(100.0*SUM(%s)/%s, 0) as %s, IFNULL(MAX(%s), 0) AS %s, " +
+ "IFNULL (dt.%s, 0) AS %s " +
+ "FROM %s p LEFT JOIN %s ph ON p.%s = ph.%s " +
+ "LEFT JOIN %s dt ON dt.%s = p.%s ",
+
+ BaseColumns._ID, BaseColumns._ID, KEY_ICONURL, KEY_THUMBURL, KEY_TITLE, KEY_TOTALCOUNT, KEY_SCHEDULEDCOUNT, KEY_MALASIZE,
+ KEY_COUNT, KEY_DONE, KEY_COUNT, KEY_TOTALCOUNT, KEY_DONE_PERC, KEY_DATE, KEY_LAST_PRACTICE,
+ KEY_DONE_TODAY, KEY_DONE_TODAY,
+ PRACTICE_TABLE_NAME, PRACTICE_HISTORY_TABLE_NAME, BaseColumns._ID, KEY_PRACTICE_ID,
+ DONE_TODAY_VIEW_NAME, KEY_PRACTICE_ID, BaseColumns._ID);
+
+ private static final String PRACTICES_STATUS_SUFFIX = String.format("GROUP BY p.%s ORDER BY %s", BaseColumns._ID, KEY_ORDER);
+
+ private static final String PRACTICES_STATUS = String.format(
+ PRACTICES_STATUS_PREFIX + "WHERE %s = ? " + PRACTICES_STATUS_SUFFIX, KEY_ISNGONDRO);
+
+ private static final String SINGLE_PRACTICE_STATUS = String.format(
+ PRACTICES_STATUS_PREFIX + "WHERE p.%s = ? " + PRACTICES_STATUS_SUFFIX, BaseColumns._ID);
+
+
+ /*private static final String TODAY_DONE = String.format(
+ "SELECT %s, SUM(%s) as %s " +
+ "FROM %s WHERE %s=CURRENT_DATE GROUP BY %s",
+ KEY_PRACTICE_ID, KEY_DONE, KEY_DONE_TODAY,
+ PRACTICE_HISTORY_TABLE_NAME, KEY_DATE, KEY_PRACTICE_ID);*/
+
+
+
+ private static final String PRACTICE_DONE_COUNT = "SELECT SUM(" + KEY_COUNT + ") FROM " + PRACTICE_HISTORY_TABLE_NAME + " WHERE " + KEY_PRACTICE_ID + " = ?";
+ private static final String NEXT_SORT_ORDER = "SELECT MAX(" + KEY_ORDER + ")+1 as " + KEY_ORDER + " FROM " + PRACTICE_TABLE_NAME;
+
+ private static SQLiteDatabase db;
+
+ private SQLiteStatement insertQueryPractice;
+ private SQLiteStatement insertQuerySession;
+ private SQLiteStatement insertTodayQuerySession;
+ private SQLiteStatement queryNgondroPresence;
+ private SQLiteStatement queryNextSortOrder;
+
+ private static HashMap> tableColumns;
+
+ public PracticeDatabase(Context ctx)
+ {
+ if (db == null)
+ {
+ MySQLiteOpenHelper helper = new MySQLiteOpenHelper(ctx);
+ db = helper.getWritableDatabase();
+ }
+
+ insertQueryPractice = db.compileStatement(INSERT_PRACTICE_QUERY);
+ queryNgondroPresence = db.compileStatement(NGONDRO_CHEK_QUERY);
+ insertQuerySession = db.compileStatement(INSERT_SESSION_QUERY);
+ insertTodayQuerySession = db.compileStatement(INSERT_TODAY_SESSION_QUERY);
+ queryNextSortOrder = db.compileStatement(NEXT_SORT_ORDER);
+
+ if (tableColumns == null){
+ tableColumns = new HashMap>();
+
+ getTableColumnsNames(PRACTICE_TABLE_NAME);
+ getTableColumnsNames(PRACTICE_HISTORY_TABLE_NAME);
+ }
+ }
+
+ private void getTableColumnsNames(String tableName)
+ {
+ Cursor c = db.rawQuery("SELECT * FROM " + tableName, null);
+ HashSet columns = new HashSet();
+ for (String col : c.getColumnNames()){
+ columns.add(col);
+ }
+ tableColumns.put(tableName, columns);
+ c.close();
+ }
+
+ public void insertPractice(boolean isNgondro, int order, String title, int iconId, int thumbId, int totalCount)
+ {
+ insertPractice(isNgondro, order, title, String.valueOf(iconId), String.valueOf(thumbId), totalCount);
+ }
+
+ public void insertPractice(boolean isNgondro, int order, String title, String iconUrl, String thumbUrl, int totalCount)
+ {
+ insertQueryPractice.clearBindings();
+ insertQueryPractice.bindLong(1, isNgondro ? 1l : 0l);
+ insertQueryPractice.bindLong(2, order);
+ insertQueryPractice.bindString(3, title);
+ insertQueryPractice.bindString(4, iconUrl);
+ insertQueryPractice.bindString(5, thumbUrl);
+ insertQueryPractice.bindLong(6, totalCount);
+
+ insertQueryPractice.executeInsert();
+ }
+
+ public boolean hasNgondroEntries(Context ctx)
+ {
+ return queryNgondroPresence.simpleQueryForLong() != 0;
+ }
+
+ /*
+ * public List getPractices(boolean ngondro){ Cursor q =
+ * db.rawQuery(PRACTICES_STATUS, new String [] {ngondro ? "1" : "0"});
+ * List result = new ArrayList();
+ *
+ * if (q.moveToFirst()) do{ result.add(new PracticeEntry(q)); } while
+ * (q.moveToNext());
+ *
+ * return result; }
+ */
+
+ public PracticeEntry getPractice(long id)
+ {
+ /*Cursor c = db.query(PRACTICE_TABLE_NAME, new String[] { "*" }, BaseColumns._ID + " = ?", new String[] { String
+ .valueOf(id) }, null, null, null);*/
+
+ Cursor c = db.rawQuery(SINGLE_PRACTICE_STATUS.replaceFirst("\\?", String.valueOf(id)), /* new String[] { String.valueOf(id) }*/null);
+
+ PracticeEntry entry = new PracticeEntry(c);
+ c.close();
+ return entry;
+ }
+
+ public void updatePractice(PracticeEntry entry)
+ {
+ int affected;
+
+ // sanitize input so we insert only valid data
+ ContentValues values = sanitizeColumns(entry);
+
+ if ((affected = db.update(PRACTICE_TABLE_NAME, values, BaseColumns._ID + " = ?", new String[] { String
+ .valueOf(entry.getId()) })) != 1)
+ {
+ throw new SQLException(String.format("Affected %d rows instead just one during update.", affected));
+ }
+
+
+ long currentCount = getDoneCount(entry);
+ if (currentCount != entry.getCurrentCount()) {
+ db.delete(PRACTICE_HISTORY_TABLE_NAME, KEY_PRACTICE_ID + " = ? AND " + KEY_DATE + "=0", new String[] { String.valueOf(entry.getId()) });
+
+ currentCount = getDoneCount(entry);
+
+ insertSession(entry.getId(), "0", (int) (entry.getCurrentCount() - currentCount));
+ }
+ }
+
+ private ContentValues sanitizeColumns(PracticeEntry entry)
+ {
+ ContentValues originals = entry.getValues();
+ ContentValues values = new ContentValues(originals);
+ for (Entry e : originals.valueSet())
+ {
+ if (!tableColumns.get(PRACTICE_TABLE_NAME).contains(e.getKey())){
+ values.remove(e.getKey());
+ }
+
+ }
+ return values;
+ }
+
+ private long getDoneCount(PracticeEntry entry)
+ {
+ long result;
+ Cursor c = db.rawQuery(PRACTICE_DONE_COUNT, new String[] { String.valueOf(entry.getId()) });
+ c.moveToFirst();
+ result = c.getLong(0);
+ c.close();
+
+ return result;
+ }
+
+ public void insertSession(int practiceId, int count) {
+ insertTodayQuerySession.clearBindings();
+ insertTodayQuerySession.bindLong(1, practiceId);
+ insertTodayQuerySession.bindLong(2, count);
+
+ insertTodayQuerySession.executeInsert();
+ }
+
+ public void insertSession(int practiceId, String date, int count) {
+ insertQuerySession.clearBindings();
+ insertQuerySession.bindLong(1, practiceId);
+ insertQuerySession.bindString(2, date);
+ insertQuerySession.bindLong(3, count);
+
+ insertQuerySession.executeInsert();
+ }
+
+ public void insertPractice(PracticeEntry entry)
+ {
+ //ContentValues entryValues = entry.getValues();
+ patchMissingDefaults(entry);
+ ContentValues entryValues = sanitizeColumns(entry);
+ long columnId;
+ if ((columnId = db.insert(PRACTICE_TABLE_NAME, null, entryValues)) == -1)
+ {
+ throw new SQLException("There was an error inserting new row.");
+ }
+
+ entry.getValues().put(BaseColumns._ID, columnId);
+
+ updatePractice(entry);
+ }
+
+ private void patchMissingDefaults(PracticeEntry entry)
+ {
+ if (!entry.getValues().containsKey(KEY_ISNGONDRO)){
+ entry.getValues().put(KEY_ISNGONDRO, false);
+ }
+
+ if (!entry.getValues().containsKey(KEY_ORDER)){
+ entry.getValues().put(KEY_ORDER, queryNextSortOrder.simpleQueryForLong());
+ }
+
+ /*if (!values.containsKey(KEY_SCHEDULEDCOMPLETION)){
+
+ values.put(KEY_SCHEDULEDCOMPLETION, 0);
+ }*/
+ }
+
+ public void deletePractice(long id)
+ {
+ int affected;
+
+ db.delete(PRACTICE_HISTORY_TABLE_NAME, BaseColumns._ID + " = ?", new String[] { String.valueOf(id) });
+ if ((affected = db.delete(PRACTICE_TABLE_NAME, BaseColumns._ID + " = ?", new String[] { String.valueOf(id) })) != 1)
+ {
+ throw new SQLException(String.format("Affected %d rows instead just one during deletion.", affected));
+ }
+ }
+
+ public Cursor getPracticesStatuses(boolean ngondroGroup)
+ {
+ return db.rawQuery(PRACTICES_STATUS.replaceFirst("\\?", ngondroGroup ? "1" : "0"), /*
+ * new
+ * String
+ * [
+ * ]
+ * {
+ * "1"
+ * }
+ */null);
+ }
+
+ //TODO: remove me
+ public Cursor dumpNgondroStatus()
+ {
+ Cursor q = db.rawQuery(PRACTICES_STATUS.replaceFirst("\\?", "1"), /*
+ * new
+ * String
+ * []
+ * {"1"}
+ */null);
+
+ if (q != null && q.moveToFirst())
+ {
+ String row = "";
+
+ for (String s : q.getColumnNames())
+ row += s + " |\t";
+
+ Log.d(MTRK_LOG_KEY, row);
+ do
+ {
+ // result.add(new PracticeEntry(q));
+
+ row = "";
+ for (int x = 0; x < q.getColumnCount(); x++)
+ {
+ row += q.getString(x) + " |\t";
+ }
+
+ Log.d(MTRK_LOG_KEY, row);
+
+ } while (q.moveToNext());
+ } else
+ Log.d(MTRK_LOG_KEY, "no data returned from query");
+
+ q.requery();
+
+ return q;
+ }
+
+ private static class MySQLiteOpenHelper extends SQLiteOpenHelper
+ {
+ public MySQLiteOpenHelper(Context context)
+ {
+ super(context, DBNAME, null, DBVERSION);
+ }
+
+ @Override
+ public void onCreate(SQLiteDatabase db)
+ {
+ db.execSQL(PRACTICE_TABLE_CREATE);
+ db.execSQL(PRACTICE_HISTORY_TABLE_CREATE);
+ db.execSQL(DONE_TODAY_VIEW_CREATE);
+ }
+
+ @Override
+ public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion)
+ {
+ // TODO Auto-generated method stub
+
+ }
+ }
+}
\ No newline at end of file
diff --git a/MeditationTracker/src/com/meditationtracker/PracticeEntry.java b/MeditationTracker/src/com/meditationtracker/PracticeEntry.java
new file mode 100644
index 0000000..1fbcdf6
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/PracticeEntry.java
@@ -0,0 +1,68 @@
+package com.meditationtracker;
+
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.provider.BaseColumns;
+
+public class PracticeEntry
+{
+ /*
+ * private int resourceIdIcon; private int resourceIdTitle; private int
+ * totalCount;
+ *
+ * private long scheduledCompletion; private long lastPracticed;
+ */
+
+ private ContentValues values;
+
+ public PracticeEntry(Cursor q)
+ {
+ int columnCount = q.getColumnCount();
+ values = new ContentValues(columnCount);
+ if (q.moveToFirst())
+ {
+ for (int x = 0; x parsed = Util.tryParse(val);
+ if (parsed._1){
+ totalCount = parsed._2;
+ }
+ else
+ {
+ //TODO: error and exit
+ }
+
+ ((TextView)findViewById(R.id.textCompletedRepetitions)).setText(val = extras.getString(ExtraKeys.CurrentCount));
+ parsed = Util.tryParse(val);
+ if (parsed._1){
+ currentCount = parsed._2;
+ }
+ else
+ {
+ //TODO: error and exit
+ }
+
+
+ scheduleCountText = (EditText)findViewById(R.id.textScheduledForToday);
+ scheduleCountText.setText(extras.getString(ExtraKeys.ScheduledCount));
+ scheduleCountText.addTextChangedListener(textChangedListener);
+ //scheduleCountText.setOnKeyListener(keyPressListener);
+ }
+
+ DatePicker dp = (DatePicker)findViewById(R.id.pickerScheduledEndDate);
+ dp.init(2010, 01, 01, onDateChangedListener);
+
+ updatePicker();
+ }
+
+ private boolean softUpdate;
+
+ protected void updatePicker()
+ {
+ if (softUpdate)
+ return;
+
+ softUpdate = true;
+
+ try {
+ DatePicker dp = (DatePicker)findViewById(R.id.pickerScheduledEndDate);
+
+ String scheduledString = ((TextView)findViewById(R.id.textScheduledForToday)).getText().toString();
+ Pair parsed = Util.tryParse(scheduledString);
+ if (parsed._1 && parsed._2 != 0){
+ long remainingDays = (totalCount - currentCount) / parsed._2 + 1;
+ Calendar cal = Calendar.getInstance();
+ cal.add(Calendar.DAY_OF_YEAR, (int)remainingDays);
+
+ dp.updateDate(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.get(Calendar.DAY_OF_MONTH));
+
+ setResult(RESULT_OK, new Intent().putExtra(ExtraKeys.ScheduledCount, parsed._2));
+ }
+ }
+ catch(Exception ignore) {} // depending on theme min/max values for date picker vary and thus can throw
+
+ softUpdate = false;
+
+ }
+
+ protected void updateCount(int year, int monthOfYear, int dayOfMonth)
+ {
+ if (softUpdate)
+ return;
+
+ softUpdate = true;
+
+ try {
+ Calendar now = Calendar.getInstance();
+ now.add(Calendar.DAY_OF_YEAR, 1);
+
+ Calendar then = Calendar.getInstance();
+ then.set(year, monthOfYear, dayOfMonth);
+
+ if (then.after(now)) {
+ long days = (then.getTimeInMillis() - now.getTimeInMillis()) / (1000*60*60*24) + 1;
+
+ long newCount = Math.round(((double)(totalCount - currentCount % totalCount)) / days);
+ scheduleCountText.setText(String.valueOf(newCount < 1 ? 1 : newCount));
+
+ setResult(RESULT_OK, new Intent().putExtra(ExtraKeys.ScheduledCount, newCount));
+ }
+ }
+ catch(Exception ignore) {}
+
+ softUpdate = false;
+ }
+
+
+ private TextWatcher textChangedListener = new TextWatcher()
+ {
+ public void onTextChanged(CharSequence s, int start, int before, int count)
+ {
+ updatePicker();
+ }
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after)
+ {
+ // TODO Auto-generated method stub
+
+ }
+
+ public void afterTextChanged(Editable s)
+ {
+ // TODO Auto-generated method stub
+
+ }
+ };
+
+ private OnDateChangedListener onDateChangedListener = new OnDateChangedListener()
+ {
+ public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth)
+ {
+ updateCount(year, monthOfYear, dayOfMonth);
+ }
+ };
+}
diff --git a/MeditationTracker/src/com/meditationtracker/SessionActivity.java b/MeditationTracker/src/com/meditationtracker/SessionActivity.java
new file mode 100644
index 0000000..341a2b9
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/SessionActivity.java
@@ -0,0 +1,298 @@
+package com.meditationtracker;
+
+import com.meditationtracker.controls.MenuBar;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.CountDownTimer;
+import android.os.Vibrator;
+import android.preference.PreferenceManager;
+import android.text.InputType;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.TextView.SavedState;
+import doo.util.Pair;
+import doo.util.Util;
+
+public class SessionActivity extends VerboseActivity
+{
+ private static final String CURRENT_COUNT = "CURRENT_COUNT";
+
+ protected static final int DIALOG_CHANGE_MALA_COUNT = 0;
+
+ protected int malaCount;
+
+ protected int sessionLength;
+ protected long malaSize;
+ protected boolean doStopwatch;
+ protected boolean doSessionEndSound;
+ protected String sessionEndSoundUrl;
+ protected boolean doSessionEndBuzz;
+
+ protected static CountDownTimer timer;
+ protected static TextView timerView;
+
+
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.session);
+
+ getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
+ updateUI();
+ }
+
+ private void updateUI()
+ {
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ String imgUrl = extras.getString(ExtraKeys.ImgURL);
+ Pair parsed = Util.tryParse(imgUrl);
+ if (parsed._1)
+ ((ImageView)findViewById(R.id.imgPractice)).setImageResource(parsed._2.intValue());
+ else
+ ((ImageView)findViewById(R.id.imgPractice)).setImageURI(Uri.parse(imgUrl));
+
+ malaSize = extras.getLong(ExtraKeys.MalaSize);
+
+ String practiceTitle = extras.getString(ExtraKeys.Title);
+ ((MenuBar)findViewById(R.id.menuBar)).setText(practiceTitle);
+ //((TextView) findViewById(R.id.textPracticeName)).setText(practiceTitle);
+ }
+
+ Button btnAdd = (Button)findViewById(R.id.addMalaButton);
+ btnAdd.setOnClickListener(addMalaClick);
+ btnAdd.setText(String.format("%s (%d)", getString(R.string.addMala), malaSize));
+
+ //findViewById(R.id.textMalaCount).setOnKeyListener(malaCountChanged);
+
+ ((Button)findViewById(R.id.editMalaButton)).setOnClickListener(editMalaClick);
+
+ SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
+
+ doStopwatch = preferences.getBoolean(getString(R.string.prefUseStopWatch), true);
+
+ Pair parsed = Util.tryParse(preferences.getString(getString(R.string.prefSessionLength), "10"));
+ sessionLength = 10 * 60;
+ if (parsed._1)
+ sessionLength = parsed._2.intValue()*60;
+
+ doSessionEndSound = preferences.getBoolean(getString(R.string.prefTimerSound), false);
+ sessionEndSoundUrl = preferences.getString(getString(R.string.prefBellSound), "");
+ doSessionEndBuzz = preferences.getBoolean(getString(R.string.prefTimerBuzz), false);
+
+ timerView = (TextView) findViewById(R.id.textTimer);
+
+ updateTimer(sessionLength*1000);
+ }
+
+
+
+ @Override
+ protected void onRestoreInstanceState(Bundle savedInstanceState) {
+ super.onRestoreInstanceState(savedInstanceState);
+
+ malaCount = savedInstanceState.getInt(CURRENT_COUNT);
+ updateResult();
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(CURRENT_COUNT, malaCount);
+ }
+
+ private void updateTimer(long time)
+ {
+ long h = time / 3600000;
+ long m = (time - h * 3600000) / 60000;
+ long s = (time / 1000) % 60;
+ if (timer != null)
+ {
+ timerView.setVisibility(View.VISIBLE);
+ timerView.setText(String.format("%02d:%02d:%02d", h, m, s));
+ }
+ }
+
+ private void updateResult()
+ {
+ setResult(RESULT_OK, new Intent().putExtra(ExtraKeys.MalaCount, malaCount));
+ ((TextView)findViewById(R.id.textViewMalaCount)).setText(String.valueOf(malaCount));
+ }
+
+ private void startTimer() {
+ stopTimer();
+
+ timer = new CountDownTimer(sessionLength * 1000, 1000)
+ {
+
+ @Override
+ public void onFinish()
+ {
+ updateTimer(0);
+
+ if (doSessionEndBuzz)
+ {
+ vibrate(1000);
+ }
+
+ if (doSessionEndSound)
+ {
+ MediaPlayer mp = new MediaPlayer();
+ try
+ {
+ mp.setDataSource(sessionEndSoundUrl);
+ mp.prepare();
+ mp.start();
+ } catch (Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ }
+
+ @Override
+ public void onTick(long millisUntilFinished)
+ {
+ long time = millisUntilFinished;
+ if (doStopwatch)
+ time = sessionLength * 1000 - millisUntilFinished;
+
+ updateTimer(time);
+
+ }
+
+ }.start();
+ }
+
+ private void stopTimer() {
+ if (timer != null) {
+ timer.cancel();
+ timer = null;
+ }
+ updateTimer(sessionLength*1000);
+ timerView.setVisibility(View.GONE);
+ }
+
+ public boolean onCreateOptionsMenu(Menu menu)
+ {
+ MenuInflater inflater = getMenuInflater();
+ inflater.inflate(R.menu.timer_menu, menu);
+ return true;
+ }
+
+ public boolean onOptionsItemSelected(MenuItem item)
+ {
+ switch (item.getItemId())
+ {
+ case R.id.startTimerMenuItem:
+ startTimer();
+ return true;
+ case R.id.stopTimerMenuItem:
+ stopTimer();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+
+
+ private OnClickListener addMalaClick = new OnClickListener()
+ {
+ public void onClick(View v)
+ {
+ malaCount+=malaSize;
+ updateResult();
+
+ vibrate(50);
+ }
+ };
+
+ private OnClickListener editMalaClick = new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ SessionActivity.this.showDialog(DIALOG_CHANGE_MALA_COUNT);
+ }
+ };
+
+ private EditText editTextMalaCount;
+
+ @Override
+ protected Dialog onCreateDialog(int id) {
+ editTextMalaCount = new EditText(this);
+ editTextMalaCount.setInputType(InputType.TYPE_CLASS_NUMBER);
+ editTextMalaCount.setText(String.valueOf(malaCount));
+
+ return new AlertDialog.Builder(SessionActivity.this)
+ .setPositiveButton(android.R.string.ok, onEditMalaOkClick)
+ .setNegativeButton(android.R.string.cancel, null)
+ .setView(editTextMalaCount)
+ .setTitle(R.string.setMalaCount).create();
+ }
+
+ private android.content.DialogInterface.OnClickListener onEditMalaOkClick = new android.content.DialogInterface.OnClickListener() {
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ updateMalaCount(editTextMalaCount.getText().toString());
+ }
+ };
+
+ private void updateMalaCount(CharSequence s) {
+ Pair parsed = Util.tryParse(s.toString());
+ if (parsed._1){
+ malaCount = parsed._2.intValue();
+ }
+ else {
+ malaCount = 0;
+ }
+
+ updateResult();
+ }
+
+ /*private TextWatcher textChangedWatcher = new TextWatcher()
+ {
+
+ public void onTextChanged(CharSequence s, int start, int before, int count)
+ {
+ if (s == null)
+ return;
+
+ updateMalaCount(s);
+ }
+
+
+ public void beforeTextChanged(CharSequence s, int start, int count, int after)
+ {
+ }
+
+ public void afterTextChanged(Editable s)
+ {
+ }
+ };*/
+
+ protected void vibrate(int duration)
+ {
+ Vibrator vibrator = (Vibrator) getSystemService(VIBRATOR_SERVICE);
+ vibrator.vibrate(duration);
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/SettingsActivity.java b/MeditationTracker/src/com/meditationtracker/SettingsActivity.java
new file mode 100644
index 0000000..56df427
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/SettingsActivity.java
@@ -0,0 +1,17 @@
+package com.meditationtracker;
+
+import android.preference.PreferenceActivity;
+import android.os.Bundle;
+
+public class SettingsActivity extends PreferenceActivity
+{
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ addPreferencesFromResource(R.xml.preferences);
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/SmartViewBinder.java b/MeditationTracker/src/com/meditationtracker/SmartViewBinder.java
new file mode 100644
index 0000000..bf96c52
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/SmartViewBinder.java
@@ -0,0 +1,44 @@
+package com.meditationtracker;
+
+import doo.util.Pair;
+import doo.util.Util;
+
+import android.database.Cursor;
+import android.net.Uri;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+import android.widget.SimpleCursorAdapter.ViewBinder;
+
+public class SmartViewBinder implements ViewBinder
+{
+
+ public boolean setViewValue(View view, Cursor cursor, int columnIndex)
+ {
+ String textToBind = cursor.getString(columnIndex);
+ if (textToBind == null)
+ return false;
+
+ if (view instanceof ImageView) {
+ ImageView imgView = (ImageView)view;
+
+ Pair parsed = Util.tryParse(textToBind);
+ if (parsed._1){
+ imgView.setImageResource(parsed._2.intValue());
+ }
+ else {
+ // it's an url
+ imgView.setImageURI(Uri.parse(textToBind));
+ }
+ }
+ else if (view instanceof TextView) {
+ ((TextView)view).setText(textToBind);
+ }
+ else
+ return false;
+
+
+ return true;
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/SupportActor.java b/MeditationTracker/src/com/meditationtracker/SupportActor.java
new file mode 100644
index 0000000..9d8a8d0
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/SupportActor.java
@@ -0,0 +1,16 @@
+package com.meditationtracker;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.preference.Preference;
+
+import com.meditationtracker.preferences.ActionPreference;
+
+public class SupportActor implements ActionPreference.IActor {
+
+ @Override
+ public void act(Preference preference, String param) {
+ preference.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(param)));
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/VerboseActivity.java b/MeditationTracker/src/com/meditationtracker/VerboseActivity.java
new file mode 100644
index 0000000..85dd5b7
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/VerboseActivity.java
@@ -0,0 +1,66 @@
+package com.meditationtracker;
+
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.io.Writer;
+import java.lang.Thread.UncaughtExceptionHandler;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Environment;
+
+public class VerboseActivity extends Activity
+{
+ private static UncaughtExceptionHandler defaultUncaughtExceptionHandler;
+ private static ActivityHistory activityHistory;
+
+ /** Called when the activity is first created. */
+ @Override
+ public void onCreate(Bundle savedInstanceState)
+ {
+ if (defaultUncaughtExceptionHandler == null) {
+ defaultUncaughtExceptionHandler = Thread.getDefaultUncaughtExceptionHandler();
+ Thread.setDefaultUncaughtExceptionHandler(loggingExceptionHandler);
+
+ activityHistory = new ActivityHistory();
+ }
+
+ activityHistory.logOnCreate(savedInstanceState, getIntent());
+
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data)
+ {
+ activityHistory.logOnActivityResult(requestCode, resultCode, data);
+ super.onActivityResult(requestCode, resultCode, data);
+ }
+
+ private UncaughtExceptionHandler loggingExceptionHandler = new UncaughtExceptionHandler()
+ {
+ public void uncaughtException(Thread thread, Throwable ex)
+ {
+ try {
+ final Writer result = new StringWriter();
+ final PrintWriter printWriter = new PrintWriter(result);
+ ex.printStackTrace(printWriter);
+ String stacktrace = result.toString();
+ printWriter.close();
+
+ BufferedWriter bos = new BufferedWriter(new FileWriter(Environment.getExternalStorageDirectory().getAbsolutePath() + "/meditracker.crash.log"));
+ bos.write(ex.toString() + "\n\n" + stacktrace + "\n\n" + activityHistory.getLog());
+ bos.flush();
+ bos.close();
+
+ } catch(Exception e) {
+
+ }
+
+ defaultUncaughtExceptionHandler.uncaughtException(thread, ex);
+ }
+ };
+}
diff --git a/MeditationTracker/src/com/meditationtracker/controls/MenuBar.java b/MeditationTracker/src/com/meditationtracker/controls/MenuBar.java
new file mode 100644
index 0000000..3a92fc6
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/controls/MenuBar.java
@@ -0,0 +1,34 @@
+package com.meditationtracker.controls;
+
+import com.meditationtracker.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+public class MenuBar extends LinearLayout {
+ private View view;
+
+ public MenuBar(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ view = View.inflate(context, R.layout.menubar, null);
+
+ // XXX: WTF?!
+ view.setLayoutParams(new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.WRAP_CONTENT));
+
+ addView(view);
+
+ TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.MenuBar);
+ setText(a.getString(R.styleable.MenuBar_text));
+ }
+
+ public void setText(String text) {
+ ((TextView)view.findViewById(R.id.textWindowTitle)).setText(text);
+ }
+
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/persistence/Practice.java b/MeditationTracker/src/com/meditationtracker/persistence/Practice.java
new file mode 100644
index 0000000..1a6af9c
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/persistence/Practice.java
@@ -0,0 +1,121 @@
+package com.meditationtracker.persistence;
+
+import java.util.Collection;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+import javax.persistence.JoinColumn;
+import javax.persistence.OneToMany;
+
+@Entity(name = "Practices")
+public class Practice {
+ @Id
+ @GeneratedValue
+ private int id;
+
+ @Column(name="ISNGONDRO", nullable = false, columnDefinition = "default false")
+ private Boolean isNgondro;
+
+ @Column(name="SORT_ORDER", nullable = false)
+ private int sortOrder;
+
+ @Column(name="TITLE", nullable = false)
+ private String title;
+
+ @Column(name="ICONURL", nullable = false)
+ private String iconURL;
+
+ @Column(name="THUMBURL", nullable = false)
+ private String thumbURL;
+
+ @Column(name="TOTALCOUNT", nullable = false, columnDefinition = "default 111111")
+ private int totalCount;
+
+// @Column(name="SCHEDULEDCOMPLETION")
+ @Column(name="SCHEDULEDCOUNT", nullable = false)
+ private int scheduleCount;
+
+ @Column(name="MALASIZE", nullable = false, columnDefinition = "default 0")
+ private int malaSize;
+
+ @OneToMany
+ @JoinColumn(name="PRACTICE_ID")
+ private Collection history;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public Boolean getIsNgondro() {
+ return isNgondro;
+ }
+
+ public void setIsNgondro(Boolean isNgondro) {
+ this.isNgondro = isNgondro;
+ }
+
+ public int getSortOrder() {
+ return sortOrder;
+ }
+
+ public void setSortOrder(int sortOrder) {
+ this.sortOrder = sortOrder;
+ }
+
+ public String getTitle() {
+ return title;
+ }
+
+ public void setTitle(String title) {
+ this.title = title;
+ }
+
+ public String getIconURL() {
+ return iconURL;
+ }
+
+ public void setIconURL(String iconURL) {
+ this.iconURL = iconURL;
+ }
+
+ public String getThumbURL() {
+ return thumbURL;
+ }
+
+ public void setThumbURL(String thumbURL) {
+ this.thumbURL = thumbURL;
+ }
+
+ public int getTotalCount() {
+ return totalCount;
+ }
+
+ public void setTotalCount(int totalCount) {
+ this.totalCount = totalCount;
+ }
+
+ public int getScheduleCount() {
+ return scheduleCount;
+ }
+
+ public void setScheduleCount(int scheduleCount) {
+ this.scheduleCount = scheduleCount;
+ }
+
+ public int getMalaSize() {
+ return malaSize;
+ }
+
+ public void setMalaSize(int malaSize) {
+ this.malaSize = malaSize;
+ }
+
+
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/persistence/PracticeHistory.java b/MeditationTracker/src/com/meditationtracker/persistence/PracticeHistory.java
new file mode 100644
index 0000000..3a42c0b
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/persistence/PracticeHistory.java
@@ -0,0 +1,56 @@
+package com.meditationtracker.persistence;
+
+import java.util.Date;
+
+import javax.persistence.Column;
+import javax.persistence.Entity;
+import javax.persistence.GeneratedValue;
+import javax.persistence.Id;
+
+@Entity(name = "PracticeHistory")
+public class PracticeHistory {
+ @Id
+ @GeneratedValue
+ int id;
+
+ @Column(name="PRACTICE_ID", nullable = false)
+ private int practiceId;
+
+ @Column(name="PRACTICE_DATE", nullable = false, columnDefinition = "default CURRENT_DATE")
+ private Date practiceDate;
+
+ @Column(name="DONE_COUNT", nullable = false)
+ private int doneCount;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getPracticeId() {
+ return practiceId;
+ }
+
+ public void setPracticeId(int practiceId) {
+ this.practiceId = practiceId;
+ }
+
+ public Date getPracticeDate() {
+ return practiceDate;
+ }
+
+ public void setPracticeDate(Date practiceDate) {
+ this.practiceDate = practiceDate;
+ }
+
+ public int getDoneCount() {
+ return doneCount;
+ }
+
+ public void setDoneCount(int doneCount) {
+ this.doneCount = doneCount;
+ }
+}
diff --git a/MeditationTracker/src/com/meditationtracker/preferences/ActionPreference.java b/MeditationTracker/src/com/meditationtracker/preferences/ActionPreference.java
new file mode 100644
index 0000000..a542d7e
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/preferences/ActionPreference.java
@@ -0,0 +1,39 @@
+package com.meditationtracker.preferences;
+
+import com.meditationtracker.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.widget.Toast;
+
+public class ActionPreference extends Preference {
+ public interface IActor {
+ void act(Preference preference, String param);
+ }
+
+ private String actorClass;
+ private String actionParam;
+
+ public ActionPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.ActionPreference);
+ actorClass = a.getString(R.styleable.ActionPreference_actor);
+ actionParam = a.getString(R.styleable.ActionPreference_param);
+ }
+
+ @Override
+ protected void onClick() {
+ try {
+ Class> actor = Class.forName(actorClass);
+ Object newInstance = actor.getConstructor((Class[])null).newInstance((Object[])null);
+
+ ((IActor)newInstance).act(this, actionParam);
+ } catch (Exception e) {
+ Toast.makeText(getContext(), "Failed acting with class " + actorClass + "\n"+e, Toast.LENGTH_LONG).show();
+ } }
+
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/preferences/ChattyEditTextPreference.java b/MeditationTracker/src/com/meditationtracker/preferences/ChattyEditTextPreference.java
new file mode 100644
index 0000000..c9fde14
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/preferences/ChattyEditTextPreference.java
@@ -0,0 +1,44 @@
+package com.meditationtracker.preferences;
+
+import android.content.Context;
+import android.preference.EditTextPreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+public abstract class ChattyEditTextPreference extends EditTextPreference {
+
+ public ChattyEditTextPreference(Context context) {
+ this(context, null);
+ }
+
+ public ChattyEditTextPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ /*@Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+
+ updateChattySummary();
+ }*/
+
+ @Override
+ protected View onCreateView(ViewGroup parent)
+ {
+ updateChattySummary();
+ return super.onCreateView(parent);
+ }
+
+ @Override
+ protected void onDialogClosed(boolean positiveResult) {
+ super.onDialogClosed(positiveResult);
+ updateChattySummary();
+ }
+
+ protected void updateChattySummary() {
+ setSummary(getChattySummary());
+ }
+
+ protected abstract CharSequence getChattySummary();
+}
diff --git a/MeditationTracker/src/com/meditationtracker/preferences/ChattyIntPreference.java b/MeditationTracker/src/com/meditationtracker/preferences/ChattyIntPreference.java
new file mode 100644
index 0000000..d7685fc
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/preferences/ChattyIntPreference.java
@@ -0,0 +1,24 @@
+package com.meditationtracker.preferences;
+
+import android.content.Context;
+import android.text.InputType;
+import android.util.AttributeSet;
+
+public class ChattyIntPreference extends ChattyEditTextPreference {
+ public ChattyIntPreference(Context context) {
+ this(context, null);
+ }
+
+ public ChattyIntPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ getEditText().setInputType(InputType.TYPE_CLASS_NUMBER);
+ }
+
+ @Override
+ protected CharSequence getChattySummary() {
+ String text = getText();
+ return text;
+ }
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/preferences/ChattyRingtonePreference.java b/MeditationTracker/src/com/meditationtracker/preferences/ChattyRingtonePreference.java
new file mode 100644
index 0000000..1967851
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/preferences/ChattyRingtonePreference.java
@@ -0,0 +1,43 @@
+package com.meditationtracker.preferences;
+
+import android.content.Context;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.preference.RingtonePreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ChattyRingtonePreference extends RingtonePreference {
+ public ChattyRingtonePreference(Context context) {
+ this(context, null);
+ }
+
+ public ChattyRingtonePreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ @Override
+ protected View onCreateView(ViewGroup parent)
+ {
+ updateChattySummary(onRestoreRingtone());
+ return super.onCreateView(parent);
+ }
+
+ @Override
+ protected void onSaveRingtone(Uri ringtoneUri)
+ {
+ super.onSaveRingtone(ringtoneUri);
+ updateChattySummary(ringtoneUri);
+
+ }
+
+ protected void updateChattySummary(Uri ringtoneUri) {
+ Ringtone ringtone = RingtoneManager.getRingtone(this.getContext(), ringtoneUri);
+ if (ringtone != null)
+ {
+ setSummary(ringtone.getTitle(this.getContext()));
+ }
+ }
+}
diff --git a/MeditationTracker/src/com/meditationtracker/preferences/ShowDialogPreference.java b/MeditationTracker/src/com/meditationtracker/preferences/ShowDialogPreference.java
new file mode 100644
index 0000000..b100d75
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/preferences/ShowDialogPreference.java
@@ -0,0 +1,37 @@
+package com.meditationtracker.preferences;
+
+import com.meditationtracker.R;
+
+import android.app.Dialog;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.preference.DialogPreference;
+import android.util.AttributeSet;
+import android.view.View;
+
+public class ShowDialogPreference extends DialogPreference {
+
+ private int layoutId;
+
+ public ShowDialogPreference(Context context, AttributeSet attrs) {
+ super(context, attrs);
+
+ TypedArray a=getContext().obtainStyledAttributes(attrs,R.styleable.ShowDialogPreferece);
+ layoutId = a.getResourceId(R.styleable.ShowDialogPreferece_layout, -1);
+
+ this.setDialogTitle(getTitle());
+ }
+
+ @Override
+ protected void onClick() {
+ this.showDialog(null);
+ }
+
+ @Override
+ protected View onCreateDialogView() {
+ new View(getContext());
+ return View.inflate(getContext(), layoutId, null);
+ }
+
+
+}
diff --git a/MeditationTracker/src/com/meditationtracker/util/Pair.java b/MeditationTracker/src/com/meditationtracker/util/Pair.java
new file mode 100644
index 0000000..450af65
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/util/Pair.java
@@ -0,0 +1,15 @@
+package com.meditationtracker.util;
+
+public class Pair
+{
+ public T _1;
+ public U _2;
+
+ public Pair() {}
+
+ public Pair(T _1, U _2)
+ {
+ this._1 = _1;
+ this._2 = _2;
+ }
+}
diff --git a/MeditationTracker/src/com/meditationtracker/util/Util.java b/MeditationTracker/src/com/meditationtracker/util/Util.java
new file mode 100644
index 0000000..27b65fc
--- /dev/null
+++ b/MeditationTracker/src/com/meditationtracker/util/Util.java
@@ -0,0 +1,120 @@
+package com.meditationtracker.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Calendar;
+import java.util.Date;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface.OnClickListener;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+public final class Util
+{
+ public static void showWhateverError(Context ctx, String message, OnClickListener okClick){
+ AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
+ builder.setMessage(message).setTitle(android.R.string.dialog_alert_title).setIcon(
+ android.R.drawable.ic_dialog_alert).setPositiveButton(android.R.string.ok, okClick).show();
+
+ }
+
+ public static Calendar parseSqliteDate(String date) {
+ Calendar cal = Calendar.getInstance();
+
+ String[] parts = date.split("-");
+ if (parts.length != 3)
+ return cal;
+
+ //int[] partPositions = new int[] {Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH};
+ int[] ymd = new int[3];
+
+ for (int x=0; x<3; x++) {
+ Pair parsed = Util.tryParse(parts[x]);
+ //cal.set(partPositions[x], parsed._2.intValue());
+ ymd[x] = parsed._2.intValue();
+ }
+
+ cal.set(ymd[0], ymd[1]-1, ymd[2]);
+
+
+ return cal;
+ }
+
+ public static String formatCalendar(Calendar cal, Context ctx) {
+ Date time = cal.getTime();
+
+ return DateFormat.getDateFormat(ctx).format(time);
+ }
+
+ public static Pair tryParse(String value) {
+ Pair result = new Pair();
+ try {
+ result._2 = Long.parseLong(value);
+ result._1 = true;
+ } catch(Exception e) {
+ result._1 = false;
+ }
+
+ return result;
+ }
+
+ public static class Reflection
+ {
+
+ public static void dumpDeclareds(Object on)
+ {
+ dumpDeclareds(on.getClass());
+ }
+
+ public static void dumpDeclareds(Class> onClass)
+ {
+ if (onClass == null)
+ return;
+
+ Log.d("MTRK", "Methods for class: " + onClass.getSimpleName());
+ for (Method m : onClass.getDeclaredMethods())
+ {
+ Log.d("MTRK", m.getName());
+ }
+
+ dumpDeclareds(onClass.getSuperclass());
+ }
+
+ public static Object invokePrivateMethod(Class> declaringClass, Object on, String methodName,
+ Class>[] paramTypes, Object... args)
+ {
+ try
+ {
+ Method m = declaringClass.getDeclaredMethod(methodName, (Class>[]) paramTypes);
+ m.setAccessible(true);
+ return m.invoke(on, args);
+ } catch (SecurityException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchMethodException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ Util.Reflection.dumpDeclareds(on);
+ } catch (IllegalArgumentException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+ }
+
+}
diff --git a/dootil/AndroidManifest.xml b/dootil/AndroidManifest.xml
new file mode 100644
index 0000000..f7fa0d7
--- /dev/null
+++ b/dootil/AndroidManifest.xml
@@ -0,0 +1,11 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dootil/res/drawable/icon.png b/dootil/res/drawable/icon.png
new file mode 100644
index 0000000..a07c69f
Binary files /dev/null and b/dootil/res/drawable/icon.png differ
diff --git a/dootil/res/layout/main.xml b/dootil/res/layout/main.xml
new file mode 100644
index 0000000..3a5f117
--- /dev/null
+++ b/dootil/res/layout/main.xml
@@ -0,0 +1,12 @@
+
+
+
+
diff --git a/dootil/res/layout/timepicker.xml b/dootil/res/layout/timepicker.xml
new file mode 100644
index 0000000..b5eb6d6
--- /dev/null
+++ b/dootil/res/layout/timepicker.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
diff --git a/dootil/res/values/strings.xml b/dootil/res/values/strings.xml
new file mode 100644
index 0000000..4c77b6e
--- /dev/null
+++ b/dootil/res/values/strings.xml
@@ -0,0 +1,5 @@
+
+
+ Hello World!
+ dootil
+
diff --git a/dootil/src/doo/alarm/AlarmHelper.java b/dootil/src/doo/alarm/AlarmHelper.java
new file mode 100644
index 0000000..87c6c21
--- /dev/null
+++ b/dootil/src/doo/alarm/AlarmHelper.java
@@ -0,0 +1,89 @@
+package doo.alarm;
+
+import java.util.Calendar;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.widget.Toast;
+
+public class AlarmHelper
+{
+
+ private static final Intent ACTION_ALARM_CHANGED = new Intent("android.intent.action.ALARM_CHANGED");
+
+
+ public static void updateAlarmState(Context ctx, Boolean enabled,
+ int alarmTime, int alarmId, Class extends BroadcastReceiver> alarmReceiverClass, Class> notificaionActionClass)
+ {
+ updateAlarm(ctx, enabled, alarmTime, alarmId, alarmReceiverClass);
+ //updateNotification(ctx, enabled, alarmTime, alarmId, notificaionActionClass);
+ }
+
+ protected static void updateAlarm(Context ctx, Boolean enabled,
+ int alarmTime, int alarmId, Class extends BroadcastReceiver> alarmReceiverClass)
+ {
+ AlarmOperator operator = new AlarmOperator(ctx);
+
+ operator.removeAlarm(alarmReceiverClass, Integer.toString(alarmId));
+
+ if (enabled)
+ {
+ Calendar calNow = Calendar.getInstance();
+ calNow.setTimeInMillis(System.currentTimeMillis());
+ Calendar futureTime = Calendar.getInstance();
+
+ futureTime.set(1, 1, 1, alarmTime / 60, alarmTime % 60);
+
+ long millisecondsAfter = AlarmOperator.GetMillisecondsAfter(calNow,
+ futureTime);
+ operator.setAlarm(alarmReceiverClass, millisecondsAfter, Integer
+ .toString(alarmId));
+
+ int dueInHours = (int) (millisecondsAfter / (60 * 60 * 1000));
+ long dueInMinutes = (millisecondsAfter / (60 * 1000)) % 60;
+ Toast.makeText(
+ ctx,
+ "Alarm is set due in about "
+ + (dueInHours != 0 ? dueInHours + " hours and "
+ : "") + dueInMinutes + " minute"
+ + (dueInMinutes != 1 ? "s" : ""),
+ Toast.LENGTH_SHORT).show();
+
+ }
+ updateStatusBarIcon(ctx, enabled);
+ }
+
+ protected static void updateStatusBarIcon(Context context, boolean enabled) {
+ Intent alarmChanged = new Intent(ACTION_ALARM_CHANGED);
+ alarmChanged.putExtra("alarmSet", enabled);
+ context.sendBroadcast(alarmChanged);
+ }
+
+
+ protected static void updateNotification(Context ctx, Boolean enabled, int alarmTime, int alarmId, Class> notificaionActionClass)
+ {
+ NotificationManager nm = (NotificationManager)ctx.getSystemService(Context.NOTIFICATION_SERVICE);
+
+ if (!enabled || notificaionActionClass == null)
+ {
+ nm.cancelAll();
+ }
+ else
+ {
+ Notification notification = new Notification(android.R.drawable.ic_lock_idle_alarm, null, System.currentTimeMillis());
+ Intent dummyIntent = new Intent(ctx, notificaionActionClass);
+ PendingIntent dummyPending = PendingIntent.getActivity(ctx, 0, dummyIntent, 0/*PendingIntent.FLAG_NO_CREATE*/);
+ int hours = alarmTime / 60;
+ int minutes = alarmTime % 60;
+ notification.setLatestEventInfo(ctx, "Alarm is on", "It will fire at " + hours + ":" + (minutes<10 ? "0"+minutes : minutes), dummyPending);
+
+ notification.flags |= Notification.FLAG_NO_CLEAR | Notification.FLAG_ONGOING_EVENT;
+ nm.notify(alarmId, notification);
+ }
+ }
+
+}
diff --git a/dootil/src/doo/alarm/AlarmOperator.java b/dootil/src/doo/alarm/AlarmOperator.java
new file mode 100644
index 0000000..22856f3
--- /dev/null
+++ b/dootil/src/doo/alarm/AlarmOperator.java
@@ -0,0 +1,72 @@
+package doo.alarm;
+
+import java.util.Calendar;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public class AlarmOperator
+{
+ private static final int REQUEST_CODE = 0;
+ private Context context;
+
+ public AlarmOperator(Context ctx)
+ {
+ this.context = ctx;
+ }
+
+ public void setAlarm(Class extends BroadcastReceiver> alarmClass, long millisecondsAfterNow, String alarmMessage)
+ {
+ setAlarm(alarmClass, millisecondsAfterNow, 24 * 60 * 60 * 1000, alarmMessage);
+ }
+
+ public void setAlarm(Class extends BroadcastReceiver> alarmClass, long millisecondsAfterNow, long millisecondsInterval, String alarmMessage)
+ {
+ PendingIntent pendingIntent = generateIntent(alarmClass, alarmMessage);
+
+ AlarmManager manager = getNotificationManager();
+
+ manager.setRepeating(AlarmManager.RTC_WAKEUP, System.currentTimeMillis() + millisecondsAfterNow, millisecondsInterval, pendingIntent);
+ }
+
+ private PendingIntent generateIntent(Class extends BroadcastReceiver> alarmClass, String alarmMessage)
+ {
+ Intent intent = new Intent(context, alarmClass);
+ intent.setAction(alarmMessage);
+
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(context, REQUEST_CODE, intent, 0);
+ return pendingIntent;
+ }
+
+ public void removeAlarm(Class extends BroadcastReceiver> alarmClass, String alarmMessage)
+ {
+ PendingIntent pendingIntent = generateIntent(alarmClass, alarmMessage);
+
+ AlarmManager manager = getNotificationManager();
+ manager.cancel(pendingIntent);
+ }
+
+ /**
+ * @param futureTime, only time as HH:MM:00 will be taken in account
+ * @param pastTime, start point for calculation, normally "now"
+ * @return milliseconds between pastTime and nearest future at futureTime
+ */
+ public static long GetMillisecondsAfter(Calendar pastTime, Calendar futureTime)
+ {
+ Calendar future = Calendar.getInstance();
+ future.set(pastTime.get(Calendar.YEAR), pastTime.get(Calendar.MONTH), pastTime.get(Calendar.DAY_OF_MONTH), futureTime.get(Calendar.HOUR_OF_DAY), futureTime.get(Calendar.MINUTE), 0);
+ future.set(Calendar.MILLISECOND, pastTime.get(Calendar.MILLISECOND));
+
+ long result = future.getTimeInMillis() - pastTime.getTimeInMillis();
+
+ return result <= 0 ? result + (24 * 60 * 60 * 1000) : result;
+ }
+
+ private AlarmManager getNotificationManager()
+ {
+ return (AlarmManager)context.getSystemService(Context.ALARM_SERVICE);
+ }
+}
diff --git a/dootil/src/doo/android/BroadcastActivityStarter.java b/dootil/src/doo/android/BroadcastActivityStarter.java
new file mode 100644
index 0000000..53b615a
--- /dev/null
+++ b/dootil/src/doo/android/BroadcastActivityStarter.java
@@ -0,0 +1,33 @@
+package doo.android;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+
+public abstract class BroadcastActivityStarter extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent activityIntent = new Intent();
+ activityIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ activityIntent.setClass(context, getActivityClass());
+ activityIntent.putExtras(intent.getExtras());
+
+ try
+ {
+ Integer.parseInt(intent.getAction());
+
+ activityIntent.setAction(intent.getAction());
+ } catch (NumberFormatException e)
+ {
+ e.printStackTrace();
+
+ activityIntent.setAction("WEIRD|" + intent.getAction());
+ }
+
+ context.startActivity(activityIntent);
+ }
+
+ protected abstract Class> getActivityClass();
+
+}
diff --git a/dootil/src/doo/android/Locker.java b/dootil/src/doo/android/Locker.java
new file mode 100644
index 0000000..960fc46
--- /dev/null
+++ b/dootil/src/doo/android/Locker.java
@@ -0,0 +1,44 @@
+package doo.android;
+
+import android.app.KeyguardManager;
+import android.app.KeyguardManager.KeyguardLock;
+import android.content.Context;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+
+public class Locker {
+
+ public static WakeLock wakeThePhone(Context ctx)
+ {
+ return wakeThePhone(ctx, ctx.getClass().getName());
+ }
+
+ public static WakeLock wakeThePhone(Context ctx, String tag)
+ {
+ return getWakeLock(ctx, tag, PowerManager.FULL_WAKE_LOCK | PowerManager.ACQUIRE_CAUSES_WAKEUP);
+ }
+
+ public static WakeLock getWakeLock(Context ctx, String tag, int flags)
+ {
+ final PowerManager powerManager = (PowerManager) ctx.getSystemService(Context.POWER_SERVICE);
+ WakeLock wakeLock = powerManager.newWakeLock(flags, tag);
+ wakeLock.acquire();
+
+ return wakeLock;
+ }
+
+ public static KeyguardLock disableKeyGuard(Context ctx)
+ {
+ return disableKeyGuard(ctx, ctx.getClass().getName());
+ }
+
+ public static KeyguardLock disableKeyGuard(Context ctx, String tag)
+ {
+ KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(Context.KEYGUARD_SERVICE);
+ KeyguardLock keyLock = keyguardManager.newKeyguardLock(tag);
+ keyLock.disableKeyguard();
+
+ return keyLock;
+ }
+
+}
diff --git a/dootil/src/doo/file/FileHelper.java b/dootil/src/doo/file/FileHelper.java
new file mode 100644
index 0000000..e949e65
--- /dev/null
+++ b/dootil/src/doo/file/FileHelper.java
@@ -0,0 +1,39 @@
+package doo.file;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import android.os.Environment;
+
+public class FileHelper {
+ public static void copy(File src, File dst) throws IOException {
+ InputStream in = new FileInputStream(src);
+ OutputStream out = new FileOutputStream(dst);
+ // Transfer bytes from in to out
+ byte[] buf = new byte[1024];
+ int len;
+ while ((len = in.read(buf)) > 0) {
+ out.write(buf, 0, len);
+ }
+ in.close();
+ out.flush();
+ out.close();
+ }
+
+ public static void quickWriteToFile(File name, String text) throws IOException{
+ FileWriter out = new FileWriter(name);
+ out.write(text);
+
+ out.flush();
+ out.close();
+ }
+
+ public static void quickWriteToSDRoot(String fileName, String text) throws IOException {
+ quickWriteToFile(new File(Environment.getExternalStorageDirectory() + File.separator + fileName), text);
+ }
+}
diff --git a/dootil/src/doo/persistence/XmlPersistor.java b/dootil/src/doo/persistence/XmlPersistor.java
new file mode 100644
index 0000000..f2e1bc4
--- /dev/null
+++ b/dootil/src/doo/persistence/XmlPersistor.java
@@ -0,0 +1,30 @@
+package doo.persistence;
+
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.simpleframework.xml.core.Persister;
+
+public class XmlPersistor {
+ public static T loadFrom(Class extends T> clazz, String fileName) throws Exception {
+ InputStream is = new FileInputStream(fileName);
+ return loadFrom(clazz, is);
+ }
+
+ public static T loadFrom(Class extends T> clazz, InputStream openFileInput) throws Exception {
+ Persister ser = new Persister();
+ return ser.read(clazz, openFileInput);
+ }
+
+ public static void saveTo(T instance, String fileName) throws Exception {
+ OutputStream os = new FileOutputStream(fileName);
+ saveTo(instance, os);
+ }
+
+ public static void saveTo(T instance, OutputStream os) throws Exception {
+ Persister ser = new Persister();
+ ser.write(instance, os);
+ }
+}
diff --git a/dootil/src/doo/settings/ButtonPreference.java b/dootil/src/doo/settings/ButtonPreference.java
new file mode 100644
index 0000000..1dbb67e
--- /dev/null
+++ b/dootil/src/doo/settings/ButtonPreference.java
@@ -0,0 +1,23 @@
+package doo.settings;
+
+import android.content.Context;
+import android.preference.Preference;
+import android.util.AttributeSet;
+
+public class ButtonPreference extends Preference
+{
+ public ButtonPreference(Context context, AttributeSet attrs, int defStyle)
+ {
+ super(context, attrs, defStyle);
+ }
+
+ public ButtonPreference(Context context, AttributeSet attrs)
+ {
+ super(context, attrs, 0);
+ }
+
+ public ButtonPreference(Context context)
+ {
+ super(context, null);
+ }
+}
diff --git a/dootil/src/doo/settings/TimePreference.java b/dootil/src/doo/settings/TimePreference.java
new file mode 100644
index 0000000..c7c6235
--- /dev/null
+++ b/dootil/src/doo/settings/TimePreference.java
@@ -0,0 +1,119 @@
+package doo.settings;
+
+import android.content.Context;
+import android.os.Parcelable;
+import android.preference.Preference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TimePicker;
+import android.widget.TimePicker.OnTimeChangedListener;
+import doo.util.root.R;
+
+public class TimePreference extends Preference implements OnTimeChangedListener
+{
+ private TimePicker timePicker;
+
+ public TimePreference(Context context, AttributeSet attrs, int defStyle)
+ {
+ super(context, attrs, defStyle);
+ }
+
+ public TimePreference(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ }
+
+ public TimePreference(Context context)
+ {
+ super(context);
+ }
+
+ public int getTimeInMinutes()
+ {
+ return getPersistedInt(0);
+ }
+
+ @Override
+ protected View onCreateView(ViewGroup parent)
+ {
+ if (timePicker == null)
+ {
+ this.setLayoutResource(R.layout.timepicker);
+
+ View view = super.onCreateView(parent);
+ TimePicker timePicker = (TimePicker) view
+ .findViewById(R.id.prefTimePicker);
+ timePicker.setIs24HourView(true); //TODO: for some reason it's not automatically read from system settings, so do it manually here
+ timePicker.setOnTimeChangedListener(this);
+ return view;
+ }
+
+ return (View) timePicker.getParent();
+ }
+
+ @Override
+ protected void onBindView(View view)
+ {
+ super.onBindView(view);
+
+ TimePicker tp = (TimePicker) view.findViewById(R.id.prefTimePicker);
+
+ Integer allTime = getPersistedInt(tp.getCurrentHour() * 60 + tp.getCurrentMinute());
+
+ tp.setCurrentHour(allTime / 60);
+ tp.setCurrentMinute(allTime % 60);
+ }
+
+ @Override
+ protected void onPrepareForRemoval()
+ {
+ // TODO Auto-generated method stub
+ super.onPrepareForRemoval();
+ }
+
+ @Override
+ protected void onRestoreInstanceState(Parcelable state)
+ {
+ // TODO Auto-generated method stub
+ super.onRestoreInstanceState(state);
+ }
+
+ @Override
+ protected Parcelable onSaveInstanceState()
+ {
+ // TODO Auto-generated method stub
+ return super.onSaveInstanceState();
+ }
+
+ public void onTimeChanged(TimePicker view, final int hourOfDay,
+ final int minute)
+ {
+ setNewTime(hourOfDay, minute);
+ }
+
+ private void setNewTime(final int hourOfDay, final int minute)
+ {
+ setNewTime(timeToInt(hourOfDay, minute));
+ }
+
+ private void setNewTime(final int compressedTime)
+ {
+ if (!callChangeListener(compressedTime)) {
+ return;
+ }
+
+ this.persistInt(compressedTime);
+
+ notifyDependencyChange(shouldDisableDependents());
+
+ notifyChanged();
+
+ }
+
+ private int timeToInt(final int hourOfDay, final int minute)
+ {
+ return hourOfDay * 60 + minute;
+ }
+
+}
diff --git a/dootil/src/doo/settings/VerboseRingtonePreference.java b/dootil/src/doo/settings/VerboseRingtonePreference.java
new file mode 100644
index 0000000..9d7cab4
--- /dev/null
+++ b/dootil/src/doo/settings/VerboseRingtonePreference.java
@@ -0,0 +1,55 @@
+package doo.settings;
+
+import android.content.Context;
+import android.media.Ringtone;
+import android.media.RingtoneManager;
+import android.net.Uri;
+import android.preference.RingtonePreference;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class VerboseRingtonePreference extends RingtonePreference
+{
+
+ public VerboseRingtonePreference(Context context, AttributeSet attrs,
+ int defStyle)
+ {
+ super(context, attrs, defStyle);
+ }
+
+ public VerboseRingtonePreference(Context context, AttributeSet attrs)
+ {
+ super(context, attrs);
+ }
+
+ public VerboseRingtonePreference(Context context)
+ {
+ super(context);
+ }
+
+ @Override
+ protected View onCreateView(ViewGroup parent)
+ {
+ updateSummary(onRestoreRingtone());
+ return super.onCreateView(parent);
+ }
+
+ @Override
+ protected void onSaveRingtone(Uri ringtoneUri)
+ {
+ super.onSaveRingtone(ringtoneUri);
+
+ updateSummary(ringtoneUri);
+
+ }
+
+ private void updateSummary(Uri ringtoneUri)
+ {
+ Ringtone ringtone = RingtoneManager.getRingtone(this.getContext(), ringtoneUri);
+ if (ringtone != null)
+ {
+ setSummary(ringtone.getTitle(this.getContext()));
+ }
+ }
+}
diff --git a/dootil/src/doo/util/Pair.java b/dootil/src/doo/util/Pair.java
new file mode 100644
index 0000000..52ff99f
--- /dev/null
+++ b/dootil/src/doo/util/Pair.java
@@ -0,0 +1,15 @@
+package doo.util;
+
+public class Pair
+{
+ public T _1;
+ public U _2;
+
+ public Pair() {}
+
+ public Pair(T _1, U _2)
+ {
+ this._1 = _1;
+ this._2 = _2;
+ }
+}
diff --git a/dootil/src/doo/util/Util.java b/dootil/src/doo/util/Util.java
new file mode 100644
index 0000000..ab4fd0b
--- /dev/null
+++ b/dootil/src/doo/util/Util.java
@@ -0,0 +1,120 @@
+package doo.util;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.util.Calendar;
+import java.util.Date;
+
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface.OnClickListener;
+import android.text.format.DateFormat;
+import android.util.Log;
+
+public final class Util
+{
+ public static void showWhateverError(Context ctx, String message, OnClickListener okClick){
+ AlertDialog.Builder builder = new AlertDialog.Builder(ctx);
+ builder.setMessage(message).setTitle(android.R.string.dialog_alert_title).setIcon(
+ android.R.drawable.ic_dialog_alert).setPositiveButton(android.R.string.ok, okClick).show();
+
+ }
+
+ public static Calendar parseSqliteDate(String date) {
+ Calendar cal = Calendar.getInstance();
+
+ String[] parts = date.split("-");
+ if (parts.length != 3)
+ return cal;
+
+ //int[] partPositions = new int[] {Calendar.YEAR, Calendar.MONTH, Calendar.DAY_OF_MONTH};
+ int[] ymd = new int[3];
+
+ for (int x=0; x<3; x++) {
+ Pair parsed = Util.tryParse(parts[x]);
+ //cal.set(partPositions[x], parsed._2.intValue());
+ ymd[x] = parsed._2.intValue();
+ }
+
+ cal.set(ymd[0], ymd[1]-1, ymd[2]);
+
+
+ return cal;
+ }
+
+ public static String formatCalendar(Calendar cal, Context ctx) {
+ Date time = cal.getTime();
+
+ return DateFormat.getDateFormat(ctx).format(time);
+ }
+
+ public static Pair tryParse(String value) {
+ Pair result = new Pair();
+ try {
+ result._2 = Long.parseLong(value);
+ result._1 = true;
+ } catch(Exception e) {
+ result._1 = false;
+ }
+
+ return result;
+ }
+
+ public static class Reflection
+ {
+
+ public static void dumpDeclareds(Object on)
+ {
+ dumpDeclareds(on.getClass());
+ }
+
+ public static void dumpDeclareds(Class> onClass)
+ {
+ if (onClass == null)
+ return;
+
+ Log.d("MTRK", "Methods for class: " + onClass.getSimpleName());
+ for (Method m : onClass.getDeclaredMethods())
+ {
+ Log.d("MTRK", m.getName());
+ }
+
+ dumpDeclareds(onClass.getSuperclass());
+ }
+
+ public static Object invokePrivateMethod(Class> declaringClass, Object on, String methodName,
+ Class>[] paramTypes, Object... args)
+ {
+ try
+ {
+ Method m = declaringClass.getDeclaredMethod(methodName, (Class>[]) paramTypes);
+ m.setAccessible(true);
+ return m.invoke(on, args);
+ } catch (SecurityException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (NoSuchMethodException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ Util.Reflection.dumpDeclareds(on);
+ } catch (IllegalArgumentException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (IllegalAccessException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ } catch (InvocationTargetException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+
+ return null;
+ }
+ }
+}
diff --git a/dootil/src/doo/util/functional/Collection.java b/dootil/src/doo/util/functional/Collection.java
new file mode 100644
index 0000000..97845a2
--- /dev/null
+++ b/dootil/src/doo/util/functional/Collection.java
@@ -0,0 +1,14 @@
+package doo.util.functional;
+
+import java.util.List;
+
+public class Collection {
+ public static TResult fold(List list, TResult initial, Folder folderOp) {
+ for (TSource val : list){
+ initial = folderOp.fold(initial, val);
+ }
+
+ return initial;
+ }
+
+}
diff --git a/dootil/src/doo/util/functional/Folder.java b/dootil/src/doo/util/functional/Folder.java
new file mode 100644
index 0000000..3eb16b2
--- /dev/null
+++ b/dootil/src/doo/util/functional/Folder.java
@@ -0,0 +1,5 @@
+package doo.util.functional;
+
+public interface Folder {
+ TResult fold(TResult accumulator, TSource current);
+}
\ No newline at end of file
diff --git a/dootil/src/doo/wifi/WifiScanner.java b/dootil/src/doo/wifi/WifiScanner.java
new file mode 100644
index 0000000..35127eb
--- /dev/null
+++ b/dootil/src/doo/wifi/WifiScanner.java
@@ -0,0 +1,121 @@
+package doo.wifi;
+
+import java.util.List;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.net.wifi.ScanResult;
+import android.net.wifi.WifiManager;
+import android.util.Log;
+
+public class WifiScanner
+{
+ public interface OnScanResults
+ {
+ public void onResults(List results);
+ }
+
+ private WifiManager wifiMan;
+ private OnScanResults resultReceiver;
+ private boolean running;
+ private boolean preScanState;
+
+ public WifiScanner()
+ {
+ }
+
+ public Boolean initiateScan(Context ctx)
+ {
+ if (wifiMan == null)
+ {
+ acquireWifiManager(ctx);
+ ctx.registerReceiver(receiverWifi, new IntentFilter(WifiManager.SCAN_RESULTS_AVAILABLE_ACTION));
+
+ Log.d("cmw", "wifistate is: " + wifiMan.getWifiState());
+ preScanState = isWifiEnabled();
+ if (!preScanState) //TODO: move this to separate thread, ja?
+ {
+ try
+ {
+ int cnt = 0;
+ while (++cnt < 5 && !(running = isWifiEnabled()))
+ {
+ wifiMan.setWifiEnabled(true);
+ Log.d("cmw", "wifistate is: " + wifiMan.getWifiState());
+ Log.d("cmw", "waiting for wifi ... " + cnt);
+ Thread.sleep(400); //TODO: make sure 4*400ms is enough for slow devices.
+ }
+ } catch (InterruptedException e)
+ {
+ // TODO Auto-generated catch block
+ e.printStackTrace();
+ }
+ }
+ }
+
+ return running = wifiMan.startScan();
+ }
+
+ private void acquireWifiManager(Context ctx) {
+ wifiMan = (WifiManager) ctx.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ // wifi manager's isWifiEnabled is not trustworthy, doing its job
+ private boolean isWifiEnabled() {
+ return wifiMan.getWifiState() >= WifiManager.WIFI_STATE_ENABLED;
+ };
+
+ public void setResultReceiver(OnScanResults receiver)
+ {
+ resultReceiver = receiver;
+ }
+
+ public OnScanResults getResultReceiver()
+ {
+ return resultReceiver;
+ }
+
+ public void releaseScanner(Context ctx)
+ {
+ if (wifiMan != null)
+ {
+ wifiMan.setWifiEnabled(preScanState);
+ // ctx.unregisterReceiver(receiverWifi);
+
+ running = false;
+ // keep the instance so there would no be concurrent later
+ // "oh i've got a message and the receiver even though unregistered is gone"
+ wifiMan = null;
+ try
+ {
+ ctx.unregisterReceiver(receiverWifi);
+ } catch (IllegalArgumentException e)
+ {
+
+ // TODO: fook it, we don't care if it's not registered, even
+ // though it's a bug since we deregister it twice...
+ }
+ }
+
+ }
+
+ public boolean isRunning()
+ {
+ return running;
+ }
+
+ private BroadcastReceiver receiverWifi = new BroadcastReceiver()
+ {
+
+ @Override
+ public void onReceive(Context context, Intent intent)
+ {
+ if (running && resultReceiver != null)
+ {
+ resultReceiver.onResults(wifiMan.getScanResults());
+ }
+ }
+ };
+}