diff --git a/app/build.gradle b/app/build.gradle index 695bd53a3..d98191ed1 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -9,7 +9,7 @@ android { minSdkVersion 15 targetSdkVersion 23 - buildConfigField "Integer", "databaseVersion", "12" + buildConfigField "Integer", "databaseVersion", "13" buildConfigField "String", "databaseFilename", "\"uhabits.db\"" testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" diff --git a/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java b/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java index 1eafd98c7..a73f5cd9d 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/BaseTest.java @@ -20,6 +20,7 @@ package org.isoron.uhabits; import android.content.Context; +import android.os.Build; import android.os.Looper; import android.support.test.InstrumentationRegistry; @@ -27,7 +28,6 @@ import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.tasks.BaseTask; import org.junit.Before; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; public class BaseTest @@ -55,6 +55,12 @@ public class BaseTest protected void waitForAsyncTasks() throws InterruptedException, TimeoutException { - BaseTask.waitForTasks(30, TimeUnit.SECONDS); + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) + { + Thread.sleep(1000); + return; + } + + BaseTask.waitForTasks(10000); } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java index c6401cab4..22a425266 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitFixtures.java @@ -19,9 +19,23 @@ package org.isoron.uhabits.unit; +import android.content.Context; +import android.support.annotation.Nullable; +import android.util.Log; + import org.isoron.uhabits.helpers.ColorHelper; +import org.isoron.uhabits.helpers.DatabaseHelper; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.tasks.BaseTask; +import org.isoron.uhabits.tasks.ExportDBTask; +import org.isoron.uhabits.tasks.ImportDataTask; + +import java.io.File; +import java.io.InputStream; +import java.util.Random; + +import static org.junit.Assert.fail; public class HabitFixtures { @@ -79,6 +93,74 @@ public class HabitFixtures return habit; } + public static void generateHugeDataSet() throws Throwable + { + final int nHabits = 30; + final int nYears = 5; + + DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command() + { + @Override + public void execute() + { + Random rand = new Random(); + + for(int i = 0; i < nHabits; i++) + { + Log.i("HabitFixture", String.format("Creating habit %d / %d", i, nHabits)); + + Habit habit = new Habit(); + habit.name = String.format("Habit %d", i); + habit.save(); + + long today = DateHelper.getStartOfToday(); + long day = DateHelper.millisecondsInOneDay; + + + for(int j = 0; j < 365 * nYears; j++) + { + if(rand.nextBoolean()) + habit.repetitions.toggle(today - j * day); + } + + habit.scores.getTodayValue(); + habit.streaks.getAll(1); + } + } + }); + + ExportDBTask task = new ExportDBTask(null); + task.setListener(new ExportDBTask.Listener() + { + @Override + public void onExportDBFinished(@Nullable String filename) + { + if(filename != null) + Log.i("HabitFixture", String.format("Huge data set exported to %s", filename)); + else + Log.i("HabitFixture", "Failed to save database"); + } + }); + task.execute(); + + BaseTask.waitForTasks(30000); + } + + public static void loadHugeDataSet(Context testContext) throws Throwable + { + File baseDir = DatabaseHelper.getFilesDir("Backups"); + if(baseDir == null) fail("baseDir should not be null"); + + File dst = new File(String.format("%s/%s", baseDir.getPath(), "loopHuge.db")); + InputStream in = testContext.getAssets().open("fixtures/loopHuge.db"); + DatabaseHelper.copy(in, dst); + + ImportDataTask task = new ImportDataTask(dst, null); + task.execute(); + + BaseTask.waitForTasks(30000); + } + public static void purgeHabits() { for(Habit h : Habit.getAll(true)) diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/HabitsApplicationTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitsApplicationTest.java index 8bd20370f..6c1e669fb 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/HabitsApplicationTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/HabitsApplicationTest.java @@ -19,6 +19,7 @@ package org.isoron.uhabits.unit; +import android.os.Build; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; @@ -38,6 +39,9 @@ public class HabitsApplicationTest @Test public void getLogcat() throws IOException { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) + return; + String msg = "LOGCAT TEST"; new RuntimeException(msg).printStackTrace(); diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java index bb3c0887b..a15841faa 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/models/RepetitionListTest.java @@ -25,6 +25,7 @@ import android.test.suitebuilder.annotation.SmallTest; import org.isoron.uhabits.BaseTest; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.models.Repetition; import org.isoron.uhabits.unit.HabitFixtures; import org.junit.After; import org.junit.Before; @@ -37,6 +38,7 @@ import java.util.GregorianCalendar; import java.util.HashMap; import java.util.Random; +import static junit.framework.Assert.assertFalse; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; @@ -174,4 +176,16 @@ public class RepetitionListTest extends BaseTest from = to - 5 * DateHelper.millisecondsInOneDay; assertThat(habit.repetitions.count(from, to), equalTo(3)); } + + @Test + public void getOldest() + { + long expectedOldestTimestamp = DateHelper.getStartOfToday() - 9 * DateHelper.millisecondsInOneDay; + + assertThat(habit.repetitions.getOldestTimestamp(), equalTo(expectedOldestTimestamp)); + + Repetition oldest = habit.repetitions.getOldest(); + assertFalse(oldest == null); + assertThat(oldest.timestamp, equalTo(expectedOldestTimestamp)); + } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java index 43546ab60..f134f6267 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/tasks/ImportDataTaskTest.java @@ -34,8 +34,6 @@ import org.junit.runner.RunWith; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import static org.hamcrest.MatcherAssert.assertThat; import static org.hamcrest.Matchers.equalTo; diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java index 257ff92cc..03b05e7c3 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/CheckmarkViewTest.java @@ -47,6 +47,7 @@ public class CheckmarkViewTest extends ViewTest habit = HabitFixtures.createShortHabit(); view = new CheckmarkView(targetContext); view.setHabit(habit); + refreshData(view); measureView(dpToPixels(100), dpToPixels(200), view); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java index 4eba3bebb..ce1651dc4 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitFrequencyViewTest.java @@ -45,6 +45,7 @@ public class HabitFrequencyViewTest extends ViewTest view = new HabitFrequencyView(targetContext); view.setHabit(habit); + refreshData(view); measureView(dpToPixels(300), dpToPixels(100), view); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java index e624148e2..5957f4a7a 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitHistoryViewTest.java @@ -21,7 +21,6 @@ package org.isoron.uhabits.unit.views; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; @@ -54,12 +53,12 @@ public class HabitHistoryViewTest extends ViewTest view = new HabitHistoryView(targetContext); view.setHabit(habit); measureView(dpToPixels(300), dpToPixels(100), view); + refreshData(view); } @Test public void render() throws Throwable { - Log.d("HabitHistoryViewTest", String.format("height=%d", dpToPixels(100))); assertRenders(view, "HabitHistoryView/render.png"); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java index a832733c4..8324e4929 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitScoreViewTest.java @@ -48,6 +48,7 @@ public class HabitScoreViewTest extends ViewTest view = new HabitScoreView(targetContext); view.setHabit(habit); view.setBucketSize(7); + refreshData(view); measureView(dpToPixels(300), dpToPixels(100), view); } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java index 4dfb24206..b192d757b 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/HabitStreakViewTest.java @@ -21,7 +21,6 @@ package org.isoron.uhabits.unit.views; import android.support.test.runner.AndroidJUnit4; import android.test.suitebuilder.annotation.SmallTest; -import android.util.Log; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.unit.HabitFixtures; @@ -45,8 +44,10 @@ public class HabitStreakViewTest extends ViewTest Habit habit = HabitFixtures.createLongHabit(); view = new HabitStreakView(targetContext); - view.setHabit(habit); measureView(dpToPixels(300), dpToPixels(100), view); + + view.setHabit(habit); + refreshData(view); } @Test @@ -66,6 +67,8 @@ public class HabitStreakViewTest extends ViewTest public void render_withSmallSize() throws Throwable { measureView(dpToPixels(100), dpToPixels(100), view); + refreshData(view); + assertRenders(view, "HabitStreakView/renderSmallSize.png"); } } diff --git a/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java b/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java index cb54fc396..d1953ff58 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/unit/views/ViewTest.java @@ -27,7 +27,9 @@ import android.view.MotionEvent; import android.view.View; import org.isoron.uhabits.BaseTest; -import org.isoron.uhabits.helpers.DialogHelper; +import org.isoron.uhabits.helpers.UIHelper; +import org.isoron.uhabits.tasks.BaseTask; +import org.isoron.uhabits.views.HabitDataView; import java.io.File; import java.io.FileOutputStream; @@ -38,7 +40,7 @@ import static junit.framework.Assert.fail; public class ViewTest extends BaseTest { - protected static final double SIMILARITY_CUTOFF = 0.08; + protected static final double SIMILARITY_CUTOFF = 0.09; public static final int HISTOGRAM_BIN_SIZE = 8; protected void measureView(int width, int height, View view) @@ -179,7 +181,7 @@ public class ViewTest extends BaseTest protected int dpToPixels(int dp) { - return (int) DialogHelper.dpToPixels(targetContext, dp); + return (int) UIHelper.dpToPixels(targetContext, dp); } protected void tap(GestureDetector.OnGestureListener view, int x, int y) throws InterruptedException @@ -190,4 +192,25 @@ public class ViewTest extends BaseTest view.onSingleTapUp(e); e.recycle(); } + + protected void refreshData(final HabitDataView view) + { + new BaseTask() + { + @Override + protected void doInBackground() + { + view.refreshData(); + } + }.execute(); + + try + { + waitForAsyncTasks(); + } + catch (Exception e) + { + throw new RuntimeException("Time out"); + } + } } diff --git a/app/src/main/assets/migrations/13.sql b/app/src/main/assets/migrations/13.sql new file mode 100644 index 000000000..1d7eeafcf --- /dev/null +++ b/app/src/main/assets/migrations/13.sql @@ -0,0 +1,4 @@ +create index idx_score_habit_timestamp on score(habit, timestamp); +create index idx_checkmark_habit_timestamp on checkmarks(habit, timestamp); +create index idx_repetitions_habit_timestamp on repetitions(habit, timestamp); +create index idx_streak_habit_end on streak(habit, end); \ No newline at end of file diff --git a/app/src/main/java/org/isoron/uhabits/MainActivity.java b/app/src/main/java/org/isoron/uhabits/MainActivity.java index 7bd02995f..d3bb4dc88 100644 --- a/app/src/main/java/org/isoron/uhabits/MainActivity.java +++ b/app/src/main/java/org/isoron/uhabits/MainActivity.java @@ -37,7 +37,7 @@ import android.view.Menu; import android.view.MenuItem; import org.isoron.uhabits.helpers.DateHelper; -import org.isoron.uhabits.helpers.DialogHelper; +import org.isoron.uhabits.helpers.UIHelper; import org.isoron.uhabits.fragments.ListHabitsFragment; import org.isoron.uhabits.helpers.ReminderHelper; import org.isoron.uhabits.models.Habit; @@ -85,8 +85,8 @@ public class MainActivity extends BaseActivity private void onStartup() { PreferenceManager.setDefaultValues(this, R.xml.preferences, false); - DialogHelper.incrementLaunchCount(this); - DialogHelper.updateLastAppVersion(this); + UIHelper.incrementLaunchCount(this); + UIHelper.updateLastAppVersion(this); showTutorial(); new AsyncTask() { diff --git a/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java b/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java index f802419ea..4fa707bd9 100644 --- a/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java +++ b/app/src/main/java/org/isoron/uhabits/ShowHabitActivity.java @@ -20,27 +20,16 @@ package org.isoron.uhabits; import android.app.ActionBar; -import android.content.BroadcastReceiver; import android.content.ContentUris; -import android.content.Context; -import android.content.Intent; -import android.content.IntentFilter; import android.graphics.drawable.ColorDrawable; import android.net.Uri; import android.os.Bundle; -import android.support.v4.content.LocalBroadcastManager; -import org.isoron.uhabits.fragments.ShowHabitFragment; import org.isoron.uhabits.models.Habit; public class ShowHabitActivity extends BaseActivity { - - public Habit habit; - private Receiver receiver; - private LocalBroadcastManager localBroadcastManager; - - private ShowHabitFragment fragment; + private Habit habit; @Override protected void onCreate(Bundle savedInstanceState) @@ -51,37 +40,18 @@ public class ShowHabitActivity extends BaseActivity habit = Habit.get(ContentUris.parseId(data)); ActionBar actionBar = getActionBar(); - if(actionBar != null) + if(actionBar != null && getHabit() != null) { - actionBar.setTitle(habit.name); - + actionBar.setTitle(getHabit().name); if (android.os.Build.VERSION.SDK_INT >= 21) - actionBar.setBackgroundDrawable(new ColorDrawable(habit.color)); + actionBar.setBackgroundDrawable(new ColorDrawable(getHabit().color)); } setContentView(R.layout.show_habit_activity); - - fragment = (ShowHabitFragment) getFragmentManager().findFragmentById(R.id.fragment2); - - receiver = new Receiver(); - localBroadcastManager = LocalBroadcastManager.getInstance(this); - localBroadcastManager.registerReceiver(receiver, - new IntentFilter(MainActivity.ACTION_REFRESH)); } - class Receiver extends BroadcastReceiver - { - @Override - public void onReceive(Context context, Intent intent) - { - fragment.refreshData(); - } - } - - @Override - protected void onDestroy() + public Habit getHabit() { - localBroadcastManager.unregisterReceiver(receiver); - super.onDestroy(); + return habit; } } diff --git a/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java b/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java index 59d4c1645..7654fb0ed 100644 --- a/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java +++ b/app/src/main/java/org/isoron/uhabits/dialogs/HistoryEditorDialog.java @@ -30,6 +30,7 @@ import android.util.Log; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.tasks.BaseTask; import org.isoron.uhabits.views.HabitHistoryView; public class HistoryEditorDialog extends DialogFragment @@ -44,7 +45,6 @@ public class HistoryEditorDialog extends DialogFragment { Context context = getActivity(); historyView = new HabitHistoryView(context, null); - int p = (int) getResources().getDimension(R.dimen.history_editor_padding); if(savedInstanceState != null) { @@ -52,7 +52,8 @@ public class HistoryEditorDialog extends DialogFragment if(id > 0) this.habit = Habit.get(id); } - historyView.setPadding(p, 0, p, 0); + int padding = (int) getResources().getDimension(R.dimen.history_editor_padding); + historyView.setPadding(padding, 0, padding, 0); historyView.setHabit(habit); historyView.setIsEditable(true); @@ -61,9 +62,23 @@ public class HistoryEditorDialog extends DialogFragment .setView(historyView) .setPositiveButton(android.R.string.ok, this); + refreshData(); + return builder.create(); } + private void refreshData() + { + new BaseTask() + { + @Override + protected void doInBackground() + { + historyView.refreshData(); + } + }.execute(); + } + @Override public void onResume() { diff --git a/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java index 628f85148..3f1a117c9 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/EditHabitFragment.java @@ -42,7 +42,7 @@ import com.android.datetimepicker.time.TimePickerDialog; import org.isoron.uhabits.helpers.ColorHelper; import org.isoron.uhabits.helpers.DateHelper; -import org.isoron.uhabits.helpers.DialogHelper.OnSavedListener; +import org.isoron.uhabits.helpers.UIHelper.OnSavedListener; import org.isoron.uhabits.R; import org.isoron.uhabits.commands.Command; import org.isoron.uhabits.commands.CreateHabitCommand; diff --git a/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java b/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java index cd5031757..9d124a691 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/HabitSelectionCallback.java @@ -36,7 +36,7 @@ import org.isoron.uhabits.commands.ChangeHabitColorCommand; import org.isoron.uhabits.commands.DeleteHabitsCommand; import org.isoron.uhabits.commands.UnarchiveHabitsCommand; import org.isoron.uhabits.helpers.ColorHelper; -import org.isoron.uhabits.helpers.DialogHelper; +import org.isoron.uhabits.helpers.UIHelper; import org.isoron.uhabits.loaders.HabitListLoader; import org.isoron.uhabits.models.Habit; @@ -49,7 +49,7 @@ public class HabitSelectionCallback implements ActionMode.Callback private List selectedPositions; private BaseActivity activity; private Listener listener; - private DialogHelper.OnSavedListener onSavedListener; + private UIHelper.OnSavedListener onSavedListener; private ProgressBar progressBar; public interface Listener @@ -74,7 +74,7 @@ public class HabitSelectionCallback implements ActionMode.Callback this.progressBar = progressBar; } - public void setOnSavedListener(DialogHelper.OnSavedListener onSavedListener) + public void setOnSavedListener(UIHelper.OnSavedListener onSavedListener) { this.onSavedListener = onSavedListener; } diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java index 6f419f802..4c8bc4190 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/ListHabitsFragment.java @@ -56,8 +56,7 @@ import org.isoron.uhabits.commands.Command; import org.isoron.uhabits.commands.ToggleRepetitionCommand; import org.isoron.uhabits.dialogs.FilePickerDialog; import org.isoron.uhabits.helpers.DateHelper; -import org.isoron.uhabits.helpers.DialogHelper; -import org.isoron.uhabits.helpers.DialogHelper.OnSavedListener; +import org.isoron.uhabits.helpers.UIHelper.OnSavedListener; import org.isoron.uhabits.helpers.HintManager; import org.isoron.uhabits.helpers.ListHabitsHelper; import org.isoron.uhabits.helpers.ReminderHelper; @@ -107,7 +106,9 @@ public class ListHabitsFragment extends Fragment listView = (DragSortListView) view.findViewById(R.id.listView); llButtonsHeader = (LinearLayout) view.findViewById(R.id.llButtonsHeader); llEmpty = view.findViewById(R.id.llEmpty); + progressBar = (ProgressBar) view.findViewById(R.id.progressBar); + progressBar.setVisibility(View.GONE); selectedPositions = new LinkedList<>(); loader = new HabitListLoader(); @@ -116,7 +117,6 @@ public class ListHabitsFragment extends Fragment loader.setListener(this); loader.setCheckmarkCount(helper.getButtonCount()); - loader.setProgressBar(progressBar); llHint.setOnClickListener(this); tvStarEmpty.setTypeface(helper.getFontawesome()); diff --git a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java index fa3afb932..8ebbfb40a 100644 --- a/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java +++ b/app/src/main/java/org/isoron/uhabits/fragments/ShowHabitFragment.java @@ -33,33 +33,32 @@ import android.view.View; import android.view.ViewGroup; import android.widget.AdapterView; import android.widget.Button; -import android.widget.LinearLayout; import android.widget.Spinner; import android.widget.TextView; -import org.isoron.uhabits.helpers.ColorHelper; -import org.isoron.uhabits.helpers.DialogHelper; import org.isoron.uhabits.HabitBroadcastReceiver; import org.isoron.uhabits.R; import org.isoron.uhabits.ShowHabitActivity; import org.isoron.uhabits.commands.Command; import org.isoron.uhabits.dialogs.HistoryEditorDialog; +import org.isoron.uhabits.helpers.ColorHelper; import org.isoron.uhabits.helpers.ReminderHelper; +import org.isoron.uhabits.helpers.UIHelper; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Score; +import org.isoron.uhabits.tasks.BaseTask; import org.isoron.uhabits.views.HabitDataView; import org.isoron.uhabits.views.HabitFrequencyView; import org.isoron.uhabits.views.HabitHistoryView; import org.isoron.uhabits.views.HabitScoreView; import org.isoron.uhabits.views.HabitStreakView; -import org.isoron.uhabits.views.RepetitionCountView; import org.isoron.uhabits.views.RingView; import java.util.LinkedList; import java.util.List; public class ShowHabitFragment extends Fragment - implements DialogHelper.OnSavedListener, HistoryEditorDialog.Listener, + implements UIHelper.OnSavedListener, HistoryEditorDialog.Listener, Spinner.OnItemSelectedListener { @Nullable @@ -77,6 +76,8 @@ public class ShowHabitFragment extends Fragment @Nullable private SharedPreferences prefs; + private int previousScoreInterval; + @Override public void onStart() { @@ -89,7 +90,7 @@ public class ShowHabitFragment extends Fragment { View view = inflater.inflate(R.layout.show_habit, container, false); activity = (ShowHabitActivity) getActivity(); - habit = activity.habit; + habit = activity.getHabit(); dataViews = new LinkedList<>(); @@ -101,22 +102,19 @@ public class ShowHabitFragment extends Fragment prefs = PreferenceManager.getDefaultSharedPreferences(getActivity()); int defaultScoreInterval = prefs.getInt("pref_score_view_interval", 1); if(defaultScoreInterval > 5 || defaultScoreInterval < 0) defaultScoreInterval = 1; + previousScoreInterval = defaultScoreInterval; setScoreBucketSize(defaultScoreInterval); + + sStrengthInterval.setSelection(defaultScoreInterval); sStrengthInterval.setOnItemSelectedListener(this); - dataViews.add((HabitStreakView) view.findViewById(R.id.streakView)); - dataViews.add((HabitStreakView) view.findViewById(R.id.smallStreakView)); dataViews.add((HabitScoreView) view.findViewById(R.id.scoreView)); dataViews.add((HabitHistoryView) view.findViewById(R.id.historyView)); dataViews.add((HabitFrequencyView) view.findViewById(R.id.punchcardView)); - - LinearLayout llRepetition = (LinearLayout) view.findViewById(R.id.llRepetition); - for(int i = 0; i < llRepetition.getChildCount(); i++) - dataViews.add((RepetitionCountView) llRepetition.getChildAt(i)); + dataViews.add((HabitStreakView) view.findViewById(R.id.streakView)); updateHeaders(view); - updateScoreRing(view); for(HabitDataView dataView : dataViews) dataView.setHabit(habit); @@ -145,12 +143,21 @@ public class ShowHabitFragment extends Fragment } setHasOptionsMenu(true); + return view; } + @Override + public void onResume() + { + super.onResume(); + refreshData(); + } + private void updateScoreRing(View view) { if(habit == null) return; + if(view == null) return; RingView scoreRing = (RingView) view.findViewById(R.id.scoreRing); scoreRing.setColor(habit.color); @@ -172,7 +179,6 @@ public class ShowHabitFragment extends Fragment updateColor(view, R.id.tvStrength); updateColor(view, R.id.tvStreaks); updateColor(view, R.id.tvWeekdayFreq); - updateColor(view, R.id.tvCount); } private void updateColor(View view, int viewId) @@ -230,11 +236,30 @@ public class ShowHabitFragment extends Fragment public void refreshData() { - if(dataViews == null) return; - updateScoreRing(getView()); + new BaseTask() + { + @Override + protected void doInBackground() + { + if(dataViews == null) return; + updateScoreRing(getView()); + + int count = 0; + for(HabitDataView view : dataViews) + { + view.refreshData(); + onProgressUpdate(count++); + } + } + + @Override + protected void onProgressUpdate(Integer... values) + { + if(dataViews == null) return; + dataViews.get(values[0]).postInvalidate(); + } + }.execute(); - for(HabitDataView view : dataViews) - view.refreshData(); } @Override @@ -246,17 +271,18 @@ public class ShowHabitFragment extends Fragment private void setScoreBucketSize(int position) { + if(scoreView == null) return; + int sizes[] = { 1, 7, 31, 92, 365 }; int size = sizes[position]; - if(scoreView != null) - { - scoreView.setBucketSize(size); - scoreView.refreshData(); - } + scoreView.setBucketSize(size); + if(position != previousScoreInterval) refreshData(); if(prefs != null) prefs.edit().putInt("pref_score_view_interval", position).apply(); + + previousScoreInterval = position; } @Override diff --git a/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java index 03f66ec5a..fa8c509b6 100644 --- a/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java +++ b/app/src/main/java/org/isoron/uhabits/helpers/DatabaseHelper.java @@ -20,11 +20,13 @@ package org.isoron.uhabits.helpers; import android.content.Context; +import android.database.Cursor; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.content.ContextCompat; import com.activeandroid.ActiveAndroid; +import com.activeandroid.Cache; import com.activeandroid.Configuration; import org.isoron.uhabits.BuildConfig; @@ -163,4 +165,20 @@ public class DatabaseHelper ActiveAndroid.initialize(dbConfig); } + + public static long longQuery(String query, String args[]) + { + Cursor c = null; + + try + { + c = Cache.openDatabase().rawQuery(query, args); + if (!c.moveToFirst()) return 0; + return c.getLong(0); + } + finally + { + if(c != null) c.close(); + } + } } diff --git a/app/src/main/java/org/isoron/uhabits/helpers/DialogHelper.java b/app/src/main/java/org/isoron/uhabits/helpers/UIHelper.java similarity index 79% rename from app/src/main/java/org/isoron/uhabits/helpers/DialogHelper.java rename to app/src/main/java/org/isoron/uhabits/helpers/UIHelper.java index a3b660b7b..00c012b1c 100644 --- a/app/src/main/java/org/isoron/uhabits/helpers/DialogHelper.java +++ b/app/src/main/java/org/isoron/uhabits/helpers/UIHelper.java @@ -23,6 +23,9 @@ import android.content.Context; import android.content.SharedPreferences; import android.content.res.Resources; import android.graphics.Typeface; +import android.os.Build; +import android.os.Debug; +import android.os.Looper; import android.preference.PreferenceManager; import android.util.AttributeSet; import android.util.DisplayMetrics; @@ -33,7 +36,7 @@ import android.view.inputmethod.InputMethodManager; import org.isoron.uhabits.BuildConfig; import org.isoron.uhabits.commands.Command; -public abstract class DialogHelper +public abstract class UIHelper { public static final String ISORON_NAMESPACE = "http://isoron.org/android"; @@ -110,4 +113,37 @@ public abstract class DialogHelper DisplayMetrics metrics = resources.getDisplayMetrics(); return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, sp, metrics); } + + /** + * Throws a runtime exception if called from the main thread. Useful to make sure that + * slow methods never accidentally slow the application down. + * + * @throws RuntimeException when run from main thread + */ + public static void throwIfMainThread() throws RuntimeException + { + Looper looper = Looper.myLooper(); + if(looper == null) return; + + if(looper == Looper.getMainLooper()) + throw new RuntimeException("This method should never be called from the main thread"); + } + + public static void startTracing() + { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) + { + throw new UnsupportedOperationException(); + } + else + { + Debug.startMethodTracingSampling("Android/data/org.isoron.uhabits/perf", + 32 * 1024 * 1024, 100); + } + } + + public static void stopTracing() + { + Debug.stopMethodTracing(); + } } diff --git a/app/src/main/java/org/isoron/uhabits/loaders/HabitListLoader.java b/app/src/main/java/org/isoron/uhabits/loaders/HabitListLoader.java index 48a736455..12a8e9e9f 100644 --- a/app/src/main/java/org/isoron/uhabits/loaders/HabitListLoader.java +++ b/app/src/main/java/org/isoron/uhabits/loaders/HabitListLoader.java @@ -19,13 +19,9 @@ package org.isoron.uhabits.loaders; -import android.os.AsyncTask; -import android.os.Handler; -import android.view.View; -import android.widget.ProgressBar; - import org.isoron.uhabits.helpers.DateHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.tasks.BaseTask; import java.util.HashMap; import java.util.List; @@ -37,9 +33,8 @@ public class HabitListLoader void onLoadFinished(); } - private AsyncTask currentFetchTask; + private BaseTask currentFetchTask; private int checkmarkCount; - private ProgressBar progressBar; private Listener listener; private Long lastLoadTimestamp; @@ -56,11 +51,6 @@ public class HabitListLoader this.includeArchived = includeArchived; } - public void setProgressBar(ProgressBar progressBar) - { - this.progressBar = progressBar; - } - public void setCheckmarkCount(int checkmarkCount) { this.checkmarkCount = checkmarkCount; @@ -98,7 +88,7 @@ public class HabitListLoader { if (currentFetchTask != null) currentFetchTask.cancel(true); - currentFetchTask = new AsyncTask() + currentFetchTask = new BaseTask() { public HashMap newHabits; public HashMap newCheckmarks; @@ -106,7 +96,7 @@ public class HabitListLoader public List newHabitList; @Override - protected Void doInBackground(Void... params) + protected void doInBackground() { newHabits = new HashMap<>(); newCheckmarks = new HashMap<>(); @@ -136,12 +126,12 @@ public class HabitListLoader commit(); - if(!updateScoresAndCheckmarks) return null; + if(!updateScoresAndCheckmarks) return; int current = 0; for (Habit h : newHabitList) { - if (isCancelled()) return null; + if (isCancelled()) return; Long id = h.getId(); newScores.put(id, h.scores.getTodayValue()); @@ -149,8 +139,6 @@ public class HabitListLoader publishProgress(current++, newHabits.size()); } - - return null; } private void commit() @@ -161,26 +149,9 @@ public class HabitListLoader habitsList = newHabitList; } - @Override - protected void onPreExecute() - { - if(progressBar != null) - { - progressBar.setIndeterminate(false); - progressBar.setProgress(0); - progressBar.setVisibility(View.VISIBLE); - } - } - @Override protected void onProgressUpdate(Integer... values) { - if(progressBar != null) - { - progressBar.setMax(values[1]); - progressBar.setProgress(values[0]); - } - if(listener != null) listener.onLoadFinished(); } @@ -189,11 +160,12 @@ public class HabitListLoader { if (isCancelled()) return; - if(progressBar != null) progressBar.setVisibility(View.INVISIBLE); lastLoadTimestamp = DateHelper.getStartOfToday(); currentFetchTask = null; if(listener != null) listener.onLoadFinished(); + + super.onPostExecute(null); } }; @@ -203,50 +175,29 @@ public class HabitListLoader public void updateHabit(final Long id) { - new AsyncTask() + new BaseTask() { @Override - protected Void doInBackground(Void... params) + protected void doInBackground() { long dateTo = DateHelper.getStartOfDay(DateHelper.getLocalTime()); long dateFrom = dateTo - (checkmarkCount - 1) * DateHelper.millisecondsInOneDay; Habit h = Habit.get(id); + if(h == null) return; + habits.put(id, h); scores.put(id, h.scores.getTodayValue()); checkmarks.put(id, h.checkmarks.getValues(dateFrom, dateTo)); - - return null; - } - - @Override - protected void onPreExecute() - { - new Handler().postDelayed(new Runnable() - { - @Override - public void run() - { - if (getStatus() == Status.RUNNING) - { - if(progressBar != null) - { - progressBar.setIndeterminate(true); - progressBar.setVisibility(View.VISIBLE); - } - } - } - }, 500); } @Override protected void onPostExecute(Void aVoid) { - if(progressBar != null) progressBar.setVisibility(View.GONE); - if(listener != null) listener.onLoadFinished(); + super.onPostExecute(null); } }.execute(); } diff --git a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java index 8c5f5f577..80b565c9c 100644 --- a/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java +++ b/app/src/main/java/org/isoron/uhabits/models/CheckmarkList.java @@ -21,15 +21,16 @@ package org.isoron.uhabits.models; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteStatement; import android.support.annotation.NonNull; import android.support.annotation.Nullable; -import com.activeandroid.ActiveAndroid; import com.activeandroid.Cache; import com.activeandroid.query.Delete; import com.activeandroid.query.Select; import org.isoron.uhabits.helpers.DateHelper; +import org.isoron.uhabits.helpers.UIHelper; import java.io.IOException; import java.io.Writer; @@ -134,10 +135,9 @@ public class CheckmarkList */ protected void computeAll() { - Repetition oldestRep = habit.repetitions.getOldest(); - if(oldestRep == null) return; + long fromTimestamp = habit.repetitions.getOldestTimestamp(); + if(fromTimestamp == 0) return; - Long fromTimestamp = oldestRep.timestamp; Long toTimestamp = DateHelper.getStartOfToday(); compute(fromTimestamp, toTimestamp); @@ -150,9 +150,11 @@ public class CheckmarkList * @param from timestamp for the beginning of the interval * @param to timestamp for the end of the interval */ - protected void compute(long from, long to) + protected void compute(long from, final long to) { - long day = DateHelper.millisecondsInOneDay; + UIHelper.throwIfMainThread(); + + final long day = DateHelper.millisecondsInOneDay; Checkmark newestCheckmark = findNewest(); if(newestCheckmark != null) @@ -165,9 +167,9 @@ public class CheckmarkList .selectFromTo(fromExtended, to) .execute(); - int nDays = (int) ((to - from) / day) + 1; + final int nDays = (int) ((to - from) / day) + 1; int nDaysExtended = (int) ((to - fromExtended) / day) + 1; - int checks[] = new int[nDaysExtended]; + final int checks[] = new int[nDaysExtended]; for (Repetition rep : reps) { @@ -187,24 +189,38 @@ public class CheckmarkList checks[i] = Checkmark.CHECKED_IMPLICITLY; } - ActiveAndroid.beginTransaction(); + + long timestamps[] = new long[nDays]; + for (int i = 0; i < nDays; i++) + timestamps[i] = to - i * day; + + insert(timestamps, checks); + } + + private void insert(long timestamps[], int values[]) + { + String query = "insert into Checkmarks(habit, timestamp, value) values (?,?,?)"; + + SQLiteDatabase db = Cache.openDatabase(); + db.beginTransaction(); try { - for (int i = 0; i < nDays; i++) + SQLiteStatement statement = db.compileStatement(query); + + for (int i = 0; i < timestamps.length; i++) { - Checkmark c = new Checkmark(); - c.habit = habit; - c.timestamp = to - i * day; - c.value = checks[i]; - c.save(); + statement.bindLong(1, habit.getId()); + statement.bindLong(2, timestamps[i]); + statement.bindLong(3, values[i]); + statement.execute(); } - ActiveAndroid.setTransactionSuccessful(); + db.setTransactionSuccessful(); } finally { - ActiveAndroid.endTransaction(); + db.endTransaction(); } } diff --git a/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java b/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java index 6e6d720ed..5bfe22fba 100644 --- a/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java +++ b/app/src/main/java/org/isoron/uhabits/models/RepetitionList.java @@ -28,7 +28,9 @@ import com.activeandroid.Cache; import com.activeandroid.query.Delete; import com.activeandroid.query.From; import com.activeandroid.query.Select; +import com.activeandroid.util.SQLiteUtils; +import org.isoron.uhabits.helpers.DatabaseHelper; import org.isoron.uhabits.helpers.DateHelper; import java.util.Arrays; @@ -96,22 +98,21 @@ public class RepetitionList timestamp = DateHelper.getStartOfDay(timestamp); if (contains(timestamp)) - { delete(timestamp); - } else - { - Repetition rep = new Repetition(); - rep.habit = habit; - rep.timestamp = timestamp; - rep.save(); - } + insert(timestamp); habit.scores.invalidateNewerThan(timestamp); habit.checkmarks.deleteNewerThan(timestamp); habit.streaks.deleteNewerThan(timestamp); } + private void insert(long timestamp) + { + String[] args = { habit.getId().toString(), Long.toString(timestamp) }; + SQLiteUtils.execSql("insert into Repetitions(habit, timestamp) values (?,?)", args); + } + /** * Returns the oldest repetition for the habit. If there is no repetition, returns null. * Repetitions in the future are discarded. @@ -124,6 +125,21 @@ public class RepetitionList return (Repetition) select().limit(1).executeSingle(); } + /** + * Returns the timestamp of the oldest repetition. If there are no repetitions, returns zero. + * Repetitions in the future are discarded. + * + * @return timestamp of the oldest repetition + */ + public long getOldestTimestamp() + { + String[] args = { habit.getId().toString(), Long.toString(DateHelper.getStartOfToday()) }; + String query = "select timestamp from Repetitions where habit = ? and timestamp <= ? " + + "order by timestamp limit 1"; + + return DatabaseHelper.longQuery(query, args); + } + /** * Returns the total number of repetitions for each month, from the first repetition until * today, grouped by day of week. The repetitions are returned in a HashMap. The key is the diff --git a/app/src/main/java/org/isoron/uhabits/models/ScoreList.java b/app/src/main/java/org/isoron/uhabits/models/ScoreList.java index 1972dc411..eca89b9e0 100644 --- a/app/src/main/java/org/isoron/uhabits/models/ScoreList.java +++ b/app/src/main/java/org/isoron/uhabits/models/ScoreList.java @@ -21,6 +21,7 @@ package org.isoron.uhabits.models; import android.database.Cursor; import android.database.sqlite.SQLiteDatabase; +import android.database.sqlite.SQLiteStatement; import android.support.annotation.NonNull; import android.support.annotation.Nullable; @@ -28,9 +29,11 @@ import com.activeandroid.Cache; import com.activeandroid.query.Delete; import com.activeandroid.query.From; import com.activeandroid.query.Select; +import com.activeandroid.util.SQLiteUtils; import org.isoron.uhabits.helpers.DatabaseHelper; import org.isoron.uhabits.helpers.DateHelper; +import org.isoron.uhabits.helpers.UIHelper; import java.io.IOException; import java.io.Writer; @@ -60,31 +63,6 @@ public class ScoreList .orderBy("timestamp desc"); } - /** - * Returns the most recent score already computed. If no score has been computed yet, returns - * null. - * - * @return newest score, or null if none exist - */ - @Nullable - protected Score findNewest() - { - return select().limit(1).executeSingle(); - } - - /** - * Returns the value of the most recent score that was already computed. If no score has been - * computed yet, returns zero. - * - * @return value of newest score, or zero if none exist - */ - protected int findNewestValue() - { - Score newest = findNewest(); - if(newest == null) return 0; - else return newest.score; - } - /** * Marks all scores that have timestamp equal to or newer than the given timestamp as invalid. * Any following getValue calls will trigger the scores to be recomputed. @@ -104,10 +82,9 @@ public class ScoreList */ private void computeAll() { - Repetition oldestRep = habit.repetitions.getOldest(); - if(oldestRep == null) return; + long fromTimestamp = habit.repetitions.getOldestTimestamp(); + if(fromTimestamp == 0) return; - long fromTimestamp = oldestRep.timestamp; long toTimestamp = DateHelper.getStartOfToday(); compute(fromTimestamp, toTimestamp); } @@ -119,45 +96,89 @@ public class ScoreList * included. * * This function assumes that there are no gaps on the scores. That is, if the newest score has - * timestamp t, then every score with timestamp lower than t has already been computed. + * timestamp t, then every score with timestamp lower than t has already been computed. * * @param from timestamp of the beginning of the interval * @param to timestamp of the end of the time interval */ protected void compute(long from, long to) { + UIHelper.throwIfMainThread(); + final long day = DateHelper.millisecondsInOneDay; final double freq = ((double) habit.freqNum) / habit.freqDen; int newestScoreValue = findNewestValue(); - Score newestScore = findNewest(); + long newestTimestamp = findNewestTimestamp(); - if(newestScore != null) - from = newestScore.timestamp + day; + if(newestTimestamp > 0) + from = newestTimestamp + day; final int checkmarkValues[] = habit.checkmarks.getValues(from, to); - final int firstScore = newestScoreValue; final long beginning = from; - DatabaseHelper.executeAsTransaction(new DatabaseHelper.Command() + int lastScore = newestScoreValue; + int size = checkmarkValues.length; + + long timestamps[] = new long[size]; + long values[] = new long[size]; + + for (int i = 0; i < checkmarkValues.length; i++) + { + int checkmarkValue = checkmarkValues[checkmarkValues.length - i - 1]; + lastScore = Score.compute(freq, lastScore, checkmarkValue); + timestamps[i] = beginning + day * i; + values[i] = lastScore; + } + + insert(timestamps, values); + } + + /** + * Returns the value of the most recent score that was already computed. If no score has been + * computed yet, returns zero. + * + * @return value of newest score, or zero if none exist + */ + protected int findNewestValue() + { + String args[] = { habit.getId().toString() }; + String query = "select score from Score where habit = ? order by timestamp desc limit 1"; + return SQLiteUtils.intQuery(query, args); + } + + private long findNewestTimestamp() + { + String args[] = { habit.getId().toString() }; + String query = "select timestamp from Score where habit = ? order by timestamp desc limit 1"; + return DatabaseHelper.longQuery(query, args); + } + + private void insert(long timestamps[], long values[]) + { + String query = "insert into Score(habit, timestamp, score) values (?,?,?)"; + + SQLiteDatabase db = Cache.openDatabase(); + db.beginTransaction(); + + try { - @Override - public void execute() + SQLiteStatement statement = db.compileStatement(query); + + for (int i = 0; i < timestamps.length; i++) { - int lastScore = firstScore; - - for (int i = 0; i < checkmarkValues.length; i++) - { - int checkmarkValue = checkmarkValues[checkmarkValues.length - i - 1]; - - Score s = new Score(); - s.habit = habit; - s.timestamp = beginning + day * i; - s.score = lastScore = Score.compute(freq, lastScore, checkmarkValue); - s.save(); - } + statement.bindLong(1, habit.getId()); + statement.bindLong(2, timestamps[i]); + statement.bindLong(3, values[i]); + statement.execute(); } - }); + + db.setTransactionSuccessful(); + } + finally + { + db.endTransaction(); + } } /** @@ -185,9 +206,9 @@ public class ScoreList */ public int getValue(long timestamp) { - Score s = get(timestamp); - if(s == null) return 0; - else return s.score; + computeAll(); + String[] args = { habit.getId().toString(), Long.toString(timestamp) }; + return SQLiteUtils.intQuery("select score from Score where habit = ? and timestamp = ?", args); } /** diff --git a/app/src/main/java/org/isoron/uhabits/models/StreakList.java b/app/src/main/java/org/isoron/uhabits/models/StreakList.java index f8a296ae2..691403d25 100644 --- a/app/src/main/java/org/isoron/uhabits/models/StreakList.java +++ b/app/src/main/java/org/isoron/uhabits/models/StreakList.java @@ -28,6 +28,7 @@ import com.activeandroid.query.Delete; import com.activeandroid.query.Select; import org.isoron.uhabits.helpers.DateHelper; +import org.isoron.uhabits.helpers.UIHelper; import java.util.ArrayList; import java.util.LinkedList; @@ -86,6 +87,8 @@ public class StreakList public void rebuild() { + UIHelper.throwIfMainThread(); + long beginning; long today = DateHelper.getStartOfToday(); long day = DateHelper.millisecondsInOneDay; diff --git a/app/src/main/java/org/isoron/uhabits/tasks/BaseTask.java b/app/src/main/java/org/isoron/uhabits/tasks/BaseTask.java index e97896be9..d9542c84b 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/BaseTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/BaseTask.java @@ -20,42 +20,53 @@ package org.isoron.uhabits.tasks; import android.os.AsyncTask; +import android.os.Build; -import java.util.concurrent.CountDownLatch; -import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -public class BaseTask extends AsyncTask +public abstract class BaseTask extends AsyncTask { - private static CountDownLatch latch; private static int activeTaskCount; - @Override - protected Void doInBackground(Void... params) - { - return null; - } - @Override protected void onPreExecute() { + super.onPreExecute(); activeTaskCount++; - latch = new CountDownLatch(activeTaskCount); } @Override protected void onPostExecute(Void aVoid) { activeTaskCount--; - latch.countDown(); + super.onPostExecute(null); + } + + @Override + protected final Void doInBackground(Void... params) + { + doInBackground(); + return null; } - public static void waitForTasks(long timeout, TimeUnit unit) + protected abstract void doInBackground(); + + public static void waitForTasks(long timeout) throws TimeoutException, InterruptedException { - if(activeTaskCount == 0) return; + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) + throw new UnsupportedOperationException("waitForTasks requires API 16+"); + + int poolInterval = 100; + + while(timeout > 0) + { + if(activeTaskCount == 0) return; + + timeout -= poolInterval; + Thread.sleep(poolInterval); + } - boolean successful = latch.await(timeout, unit); - if(!successful) throw new TimeoutException(); + throw new TimeoutException(); } } diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java index dc3a53305..5c85bc89e 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ExportCSVTask.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.tasks; -import android.os.AsyncTask; import android.support.annotation.Nullable; import android.view.View; import android.widget.ProgressBar; @@ -80,12 +79,12 @@ public class ExportCSVTask extends BaseTask } @Override - protected Void doInBackground(Void... params) + protected void doInBackground() { try { File dir = DatabaseHelper.getFilesDir("CSV"); - if(dir == null) return null; + if(dir == null) return; HabitsCSVExporter exporter = new HabitsCSVExporter(selectedHabits, dir); archiveFilename = exporter.writeArchive(); @@ -94,7 +93,5 @@ public class ExportCSVTask extends BaseTask { e.printStackTrace(); } - - return null; } } diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java index ebdb213a9..4e184335f 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ExportDBTask.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.tasks; -import android.os.AsyncTask; import android.support.annotation.Nullable; import android.view.View; import android.widget.ProgressBar; @@ -75,14 +74,14 @@ public class ExportDBTask extends BaseTask } @Override - protected Void doInBackground(Void... params) + protected void doInBackground() { filename = null; try { File dir = DatabaseHelper.getFilesDir("Backups"); - if(dir == null) return null; + if(dir == null) return; filename = DatabaseHelper.saveDatabaseCopy(dir); } @@ -90,7 +89,5 @@ public class ExportDBTask extends BaseTask { e.printStackTrace(); } - - return null; } } diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java index fe7f03528..477f1f51b 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ImportDataTask.java @@ -19,7 +19,6 @@ package org.isoron.uhabits.tasks; -import android.os.AsyncTask; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.view.View; @@ -86,7 +85,7 @@ public class ImportDataTask extends BaseTask } @Override - protected Void doInBackground(Void... params) + protected void doInBackground() { try { @@ -106,7 +105,5 @@ public class ImportDataTask extends BaseTask result = FAILED; e.printStackTrace(); } - - return null; } } \ No newline at end of file diff --git a/app/src/main/java/org/isoron/uhabits/tasks/ToggleRepetitionTask.java b/app/src/main/java/org/isoron/uhabits/tasks/ToggleRepetitionTask.java index bece42414..3b46ec95c 100644 --- a/app/src/main/java/org/isoron/uhabits/tasks/ToggleRepetitionTask.java +++ b/app/src/main/java/org/isoron/uhabits/tasks/ToggleRepetitionTask.java @@ -38,10 +38,9 @@ public class ToggleRepetitionTask extends BaseTask } @Override - protected Void doInBackground(Void... params) + protected void doInBackground() { habit.repetitions.toggle(timestamp); - return null; } @Override diff --git a/app/src/main/java/org/isoron/uhabits/views/CheckmarkView.java b/app/src/main/java/org/isoron/uhabits/views/CheckmarkView.java index 5455e974d..8819e8e37 100644 --- a/app/src/main/java/org/isoron/uhabits/views/CheckmarkView.java +++ b/app/src/main/java/org/isoron/uhabits/views/CheckmarkView.java @@ -30,20 +30,18 @@ import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import org.isoron.uhabits.R; import org.isoron.uhabits.helpers.ColorHelper; import org.isoron.uhabits.models.Habit; -public class CheckmarkView extends View +public class CheckmarkView extends View implements HabitDataView { private Paint pCard; private Paint pIcon; private int primaryColor; - private int backgroundColor; private int timesColor; private int darkGrey; @@ -56,12 +54,8 @@ public class CheckmarkView extends View private String fa_check; private String fa_times; - private String fa_full_star; - private String fa_half_star; - private String fa_empty_star; private int check_status; - private int star_status; private Rect rect; private TextPaint textPaint; @@ -99,25 +93,19 @@ public class CheckmarkView extends View fa_check = context.getString(R.string.fa_check); fa_times = context.getString(R.string.fa_times); - fa_empty_star = context.getString(R.string.fa_star_o); - fa_half_star = context.getString(R.string.fa_star_half_o); - fa_full_star = context.getString(R.string.fa_star); primaryColor = ColorHelper.palette[10]; - backgroundColor = Color.argb(255, 255, 255, 255); timesColor = Color.argb(128, 255, 255, 255); darkGrey = Color.argb(64, 0, 0, 0); rect = new Rect(); - check_status = 2; - star_status = 0; - label = "Wake up early"; + check_status = 0; + label = "Habit"; } public void setHabit(Habit habit) { this.habit = habit; - refreshData(); } @Override @@ -190,21 +178,25 @@ public class CheckmarkView extends View padding = 8 * leftMargin; textPaint.setTextSize(0.15f * width); - refreshData(); + updateLabel(); } public void refreshData() { this.check_status = habit.checkmarks.getTodayValue(); - this.star_status = habit.scores.getTodayStarStatus(); this.primaryColor = Color.argb(230, Color.red(habit.color), Color.green(habit.color), Color.blue(habit.color)); this.label = habit.name; + updateLabel(); + postInvalidate(); + } + + private void updateLabel() + { textPaint.setColor(Color.WHITE); labelLayout = new StaticLayout(label, textPaint, (int) (width - 2 * leftMargin - 2 * padding), Layout.Alignment.ALIGN_CENTER, 1.0f, 0.0f, false); } - } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitDataView.java b/app/src/main/java/org/isoron/uhabits/views/HabitDataView.java index f6d3c712a..b1e239d5e 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitDataView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitDataView.java @@ -26,4 +26,6 @@ public interface HabitDataView void setHabit(Habit habit); void refreshData(); + + void postInvalidate(); } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java b/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java index 94b34af32..ceee57489 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitFrequencyView.java @@ -84,13 +84,10 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV { this.habit = habit; createColors(); - refreshData(); - postInvalidate(); } private void init() { - refreshData(); createPaints(); createColors(); @@ -181,7 +178,7 @@ public class HabitFrequencyView extends ScrollableDataView implements HabitDataV else if(habit != null) frequency = habit.repetitions.getWeekdayFrequency(); - invalidate(); + postInvalidate(); } private void generateRandomData() diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java index a15059295..67297101e 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitHistoryView.java @@ -26,15 +26,15 @@ import android.graphics.Paint; import android.graphics.Paint.Align; import android.graphics.RectF; import android.util.AttributeSet; -import android.util.Log; import android.view.HapticFeedbackConstants; import android.view.MotionEvent; import org.isoron.uhabits.R; import org.isoron.uhabits.helpers.ColorHelper; import org.isoron.uhabits.helpers.DateHelper; -import org.isoron.uhabits.helpers.DialogHelper; +import org.isoron.uhabits.helpers.UIHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.tasks.BaseTask; import org.isoron.uhabits.tasks.ToggleRepetitionTask; import java.text.SimpleDateFormat; @@ -89,13 +89,10 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie { this.habit = habit; createColors(); - refreshData(); - postInvalidate(); } private void init() { - refreshData(); createPaints(); createColors(); @@ -136,7 +133,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie float baseSize = height / 8.0f; setScrollerBucketSize((int) baseSize); - squareSpacing = DialogHelper.dpToPixels(getContext(), 1.0f); + squareSpacing = UIHelper.dpToPixels(getContext(), 1.0f); float maxTextSize = getResources().getDimensionPixelSize(R.dimen.regularTextSize); float textSize = Math.min(baseSize * 0.5f, maxTextSize); @@ -221,7 +218,7 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie } updateDate(); - invalidate(); + postInvalidate(); } private void generateRandomData() @@ -388,11 +385,23 @@ public class HabitHistoryView extends ScrollableDataView implements HabitDataVie this.isEditable = isEditable; } - @Override public void onToggleRepetitionFinished() { - refreshData(); - invalidate(); + new BaseTask() + { + @Override + protected void doInBackground() + { + refreshData(); + } + + @Override + protected void onPostExecute(Void aVoid) + { + invalidate(); + super.onPostExecute(null); + } + }.execute(); } } diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java index 02cd33947..d92b4cd03 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitScoreView.java @@ -93,15 +93,12 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView { this.habit = habit; createColors(); - refreshData(); - postInvalidate(); } private void init() { createPaints(); createColors(); - if(isInEditMode()) refreshData(); dfYear = new SimpleDateFormat("yyyy", Locale.getDefault()); dfMonth = new SimpleDateFormat("MMM", Locale.getDefault()); @@ -186,7 +183,7 @@ public class HabitScoreView extends ScrollableDataView implements HabitDataView scores = habit.scores.getAllValues(bucketSize); } - invalidate(); + postInvalidate(); } public void setBucketSize(int bucketSize) diff --git a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java index e3f1692e1..37d2e16c9 100644 --- a/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java +++ b/app/src/main/java/org/isoron/uhabits/views/HabitStreakView.java @@ -78,9 +78,7 @@ public class HabitStreakView extends View implements HabitDataView public void setHabit(Habit habit) { this.habit = habit; - createColors(); - postInvalidate(); } private void init() @@ -93,7 +91,7 @@ public class HabitStreakView extends View implements HabitDataView dateFormat = DateFormat.getDateInstance(DateFormat.MEDIUM); dateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); rect = new RectF(); - + maxStreakCount = 10; baseSize = getResources().getDimensionPixelSize(R.dimen.baseSize); } @@ -118,7 +116,6 @@ public class HabitStreakView extends View implements HabitDataView em = paint.getFontSpacing(); textMargin = 0.5f * em; - refreshData(); updateMaxMin(); } diff --git a/app/src/main/java/org/isoron/uhabits/views/NumberView.java b/app/src/main/java/org/isoron/uhabits/views/NumberView.java index f8f8695c4..2d5ab4402 100644 --- a/app/src/main/java/org/isoron/uhabits/views/NumberView.java +++ b/app/src/main/java/org/isoron/uhabits/views/NumberView.java @@ -33,7 +33,7 @@ import android.view.View; import org.isoron.uhabits.R; import org.isoron.uhabits.helpers.ColorHelper; -import org.isoron.uhabits.helpers.DialogHelper; +import org.isoron.uhabits.helpers.UIHelper; public class NumberView extends View { @@ -66,9 +66,9 @@ public class NumberView extends View this.textSize = getResources().getDimension(R.dimen.regularTextSize); - this.label = DialogHelper.getAttribute(context, attrs, "label", "Number"); - this.number = DialogHelper.getIntAttribute(context, attrs, "number", 0); - this.textSize = DialogHelper.getFloatAttribute(context, attrs, "textSize", + this.label = UIHelper.getAttribute(context, attrs, "label", "Number"); + this.number = UIHelper.getIntAttribute(context, attrs, "number", 0); + this.textSize = UIHelper.getFloatAttribute(context, attrs, "textSize", getResources().getDimension(R.dimen.regularTextSize)); this.color = ColorHelper.palette[7]; @@ -92,7 +92,6 @@ public class NumberView extends View public void setNumber(int number) { this.number = number; - requestLayout(); postInvalidate(); } diff --git a/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java b/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java index 4797cea1b..e0f80aa90 100644 --- a/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java +++ b/app/src/main/java/org/isoron/uhabits/views/RepetitionCountView.java @@ -24,7 +24,7 @@ import android.util.AttributeSet; import org.isoron.uhabits.R; import org.isoron.uhabits.helpers.DateHelper; -import org.isoron.uhabits.helpers.DialogHelper; +import org.isoron.uhabits.helpers.UIHelper; import org.isoron.uhabits.models.Habit; import java.util.Calendar; @@ -38,14 +38,12 @@ public class RepetitionCountView extends NumberView implements HabitDataView public RepetitionCountView(Context context, AttributeSet attrs) { super(context, attrs); - this.interval = DialogHelper.getIntAttribute(context, attrs, "interval", 7); - int labelValue = DialogHelper.getIntAttribute(context, attrs, "labelValue", 7); - String labelFormat = DialogHelper.getAttribute(context, attrs, "labelFormat", + this.interval = UIHelper.getIntAttribute(context, attrs, "interval", 7); + int labelValue = UIHelper.getIntAttribute(context, attrs, "labelValue", 7); + String labelFormat = UIHelper.getAttribute(context, attrs, "labelFormat", getResources().getString(R.string.last_x_days)); setLabel(String.format(labelFormat, labelValue)); - - refreshData(); } @Override @@ -73,6 +71,8 @@ public class RepetitionCountView extends NumberView implements HabitDataView if(habit != null) setNumber(habit.repetitions.count(from, to)); + + postInvalidate(); } @Override @@ -80,6 +80,5 @@ public class RepetitionCountView extends NumberView implements HabitDataView { this.habit = habit; setColor(habit.color); - refreshData(); } } diff --git a/app/src/main/java/org/isoron/uhabits/views/RingView.java b/app/src/main/java/org/isoron/uhabits/views/RingView.java index fad31fa3b..7007d9924 100644 --- a/app/src/main/java/org/isoron/uhabits/views/RingView.java +++ b/app/src/main/java/org/isoron/uhabits/views/RingView.java @@ -29,11 +29,10 @@ import android.text.Layout; import android.text.StaticLayout; import android.text.TextPaint; import android.util.AttributeSet; -import android.util.Log; import android.view.View; import org.isoron.uhabits.R; -import org.isoron.uhabits.helpers.DialogHelper; +import org.isoron.uhabits.helpers.UIHelper; public class RingView extends View { @@ -63,9 +62,9 @@ public class RingView extends View { super(context, attrs); - this.label = DialogHelper.getAttribute(context, attrs, "label", "Label"); - this.maxDiameter = DialogHelper.getFloatAttribute(context, attrs, "maxDiameter", 100); - this.maxDiameter = DialogHelper.dpToPixels(context, maxDiameter); + this.label = UIHelper.getAttribute(context, attrs, "label", "Label"); + this.maxDiameter = UIHelper.getFloatAttribute(context, attrs, "maxDiameter", 100); + this.maxDiameter = UIHelper.dpToPixels(context, maxDiameter); init(); } @@ -98,7 +97,6 @@ public class RingView extends View fadedTextColor = getResources().getColor(R.color.fadedTextColor); textSize = getResources().getDimension(R.dimen.smallTextSize); - Log.d("RingView", String.format("textSize=%f", textSize)); rect = new RectF(); } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java index 4dc96e4a1..b75493a76 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/BaseWidgetProvider.java @@ -34,9 +34,10 @@ import android.view.View; import android.widget.ImageView; import android.widget.RemoteViews; -import org.isoron.uhabits.helpers.DialogHelper; import org.isoron.uhabits.R; +import org.isoron.uhabits.helpers.UIHelper; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.tasks.BaseTask; import java.io.FileOutputStream; import java.io.IOException; @@ -92,12 +93,12 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider } } - private void updateWidget(Context context, AppWidgetManager manager, int widgetId, Bundle options) + private void updateWidget(Context context, AppWidgetManager manager, + int widgetId, Bundle options) { updateWidgetSize(context, options); Context appContext = context.getApplicationContext(); - RemoteViews remoteViews = new RemoteViews(context.getPackageName(), getLayoutId()); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(appContext); Long habitId = prefs.getLong(getHabitIdKey(widgetId), -1L); @@ -112,24 +113,11 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider return; } - View widgetView = buildCustomView(context, habit); - measureCustomView(context, width, height, widgetView); - - widgetView.setDrawingCacheEnabled(true); - widgetView.buildDrawingCache(true); - Bitmap drawingCache = widgetView.getDrawingCache(); - - remoteViews.setTextViewText(R.id.label, habit.name); - remoteViews.setImageViewBitmap(R.id.imageView, drawingCache); - - //savePreview(context, widgetId, drawingCache); - - PendingIntent onClickIntent = getOnClickPendingIntent(context, habit); - if(onClickIntent != null) remoteViews.setOnClickPendingIntent(R.id.imageView, onClickIntent); - - manager.updateAppWidget(widgetId, remoteViews); + new RenderWidgetTask(widgetId, context, habit, manager).execute(); } + protected abstract void refreshCustomViewData(View widgetView); + private void savePreview(Context context, int widgetId, Bitmap widgetCache) { try @@ -170,13 +158,13 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider if (options != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { - maxWidth = (int) DialogHelper.dpToPixels(context, + maxWidth = (int) UIHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH)); - maxHeight = (int) DialogHelper.dpToPixels(context, + maxHeight = (int) UIHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT)); - minWidth = (int) DialogHelper.dpToPixels(context, + minWidth = (int) UIHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH)); - minHeight = (int) DialogHelper.dpToPixels(context, + minHeight = (int) UIHelper.dpToPixels(context, options.getInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT)); } @@ -204,4 +192,60 @@ public abstract class BaseWidgetProvider extends AppWidgetProvider customView.measure(specWidth, specHeight); customView.layout(0, 0, customView.getMeasuredWidth(), customView.getMeasuredHeight()); } + + private class RenderWidgetTask extends BaseTask + { + private final int widgetId; + private final Context context; + private final Habit habit; + private final AppWidgetManager manager; + public RemoteViews remoteViews; + public View widgetView; + + public RenderWidgetTask(int widgetId, Context context, Habit habit, + AppWidgetManager manager) + { + this.widgetId = widgetId; + this.context = context; + this.habit = habit; + this.manager = manager; + } + + @Override + protected void onPreExecute() + { + super.onPreExecute(); + + remoteViews = new RemoteViews(context.getPackageName(), getLayoutId()); + widgetView = buildCustomView(context, habit); + measureCustomView(context, width, height, widgetView); + manager.updateAppWidget(widgetId, remoteViews); + } + + @Override + protected void doInBackground() + { + refreshCustomViewData(widgetView); + } + + @Override + protected void onPostExecute(Void aVoid) + { + widgetView.invalidate(); + widgetView.setDrawingCacheEnabled(true); + widgetView.buildDrawingCache(true); + Bitmap drawingCache = widgetView.getDrawingCache(); + remoteViews.setTextViewText(R.id.label, habit.name); + remoteViews.setImageViewBitmap(R.id.imageView, drawingCache); + + //savePreview(context, widgetId, drawingCache); + + PendingIntent onClickIntent = getOnClickPendingIntent(context, habit); + if(onClickIntent != null) remoteViews.setOnClickPendingIntent(R.id.imageView, onClickIntent); + + manager.updateAppWidget(widgetId, remoteViews); + + super.onPostExecute(aVoid); + } + } } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java index 849fdf52e..bd04e8eb0 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/CheckmarkWidgetProvider.java @@ -26,8 +26,9 @@ import org.isoron.uhabits.HabitBroadcastReceiver; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.views.CheckmarkView; +import org.isoron.uhabits.views.HabitDataView; -public class CheckmarkWidgetProvider extends BaseWidgetProvider +public class CheckmarkWidgetProvider extends BaseWidgetProvider { @Override protected View buildCustomView(Context context, Habit habit) @@ -37,6 +38,12 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider return view; } + @Override + protected void refreshCustomViewData(View view) + { + ((HabitDataView) view).refreshData(); + } + @Override protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) { @@ -60,4 +67,6 @@ public class CheckmarkWidgetProvider extends BaseWidgetProvider { return R.layout.widget_checkmark; } + + } diff --git a/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java index 82af6dcb3..2cac07c06 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/FrequencyWidgetProvider.java @@ -26,6 +26,7 @@ import android.view.View; import org.isoron.uhabits.HabitBroadcastReceiver; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.views.HabitDataView; import org.isoron.uhabits.views.HabitFrequencyView; public class FrequencyWidgetProvider extends BaseWidgetProvider @@ -39,6 +40,12 @@ public class FrequencyWidgetProvider extends BaseWidgetProvider return view; } + @Override + protected void refreshCustomViewData(View view) + { + ((HabitDataView) view).refreshData(); + } + @Override protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) { diff --git a/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java index 957e1b63c..e02cb608c 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/HistoryWidgetProvider.java @@ -25,6 +25,7 @@ import android.view.View; import org.isoron.uhabits.HabitBroadcastReceiver; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.views.HabitDataView; import org.isoron.uhabits.views.HabitHistoryView; public class HistoryWidgetProvider extends BaseWidgetProvider @@ -38,6 +39,12 @@ public class HistoryWidgetProvider extends BaseWidgetProvider return view; } + @Override + protected void refreshCustomViewData(View view) + { + ((HabitDataView) view).refreshData(); + } + @Override protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) { diff --git a/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java index f1d64439b..456491efe 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/ScoreWidgetProvider.java @@ -25,6 +25,7 @@ import android.view.View; import org.isoron.uhabits.HabitBroadcastReceiver; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.views.HabitDataView; import org.isoron.uhabits.views.HabitScoreView; public class ScoreWidgetProvider extends BaseWidgetProvider @@ -38,6 +39,12 @@ public class ScoreWidgetProvider extends BaseWidgetProvider return view; } + @Override + protected void refreshCustomViewData(View view) + { + ((HabitDataView) view).refreshData(); + } + @Override protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) { diff --git a/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java b/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java index 05d4af462..f5d81d4f6 100644 --- a/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java +++ b/app/src/main/java/org/isoron/uhabits/widgets/StreakWidgetProvider.java @@ -25,6 +25,7 @@ import android.view.View; import org.isoron.uhabits.HabitBroadcastReceiver; import org.isoron.uhabits.R; import org.isoron.uhabits.models.Habit; +import org.isoron.uhabits.views.HabitDataView; import org.isoron.uhabits.views.HabitStreakView; public class StreakWidgetProvider extends BaseWidgetProvider @@ -38,6 +39,12 @@ public class StreakWidgetProvider extends BaseWidgetProvider return view; } + @Override + protected void refreshCustomViewData(View view) + { + ((HabitDataView) view).refreshData(); + } + @Override protected PendingIntent getOnClickPendingIntent(Context context, Habit habit) { diff --git a/app/src/main/res/drawable/widget_preview_streaks.png b/app/src/main/res/drawable/widget_preview_streaks.png index 9977ca6f0..b7771c2b8 100644 Binary files a/app/src/main/res/drawable/widget_preview_streaks.png and b/app/src/main/res/drawable/widget_preview_streaks.png differ diff --git a/app/src/main/res/layout/show_habit.xml b/app/src/main/res/layout/show_habit.xml index 6505299c6..fd5da21b5 100644 --- a/app/src/main/res/layout/show_habit.xml +++ b/app/src/main/res/layout/show_habit.xml @@ -53,75 +53,6 @@ app:maxDiameter="80" app:textSize="@dimen/smallTextSize"/> - - - - - - - - - - - - - - - - - - - - - - - - - -