From a984467516124178c5ce99c0db89d24f54ea5036 Mon Sep 17 00:00:00 2001 From: Alinson Xavier Date: Thu, 21 Jul 2016 22:48:37 -0400 Subject: [PATCH] Some refactoring; add tests for ListHabitsScreen --- .../org/isoron/uhabits/BaseAndroidTest.java | 5 - .../org/isoron/uhabits/AndroidModule.java | 42 +--- .../org/isoron/uhabits/BaseComponent.java | 14 +- .../java/org/isoron/uhabits/HabitLogger.java | 9 + .../uhabits/commands/CommandRunner.java | 4 + .../isoron/uhabits/intents/IntentFactory.java | 85 ++----- .../uhabits/intents/IntentScheduler.java | 4 + .../uhabits/intents/PendingIntentFactory.java | 118 ++++++++++ .../java/org/isoron/uhabits/io/DirFinder.java | 106 +++++++++ .../models/sqlite/SQLModelFactory.java | 9 + .../uhabits/receivers/ReminderReceiver.java | 9 +- .../org/isoron/uhabits/ui/BaseActivity.java | 27 +++ .../org/isoron/uhabits/ui/BaseScreen.java | 31 +-- .../ui/common/dialogs/ColorPickerDialog.java | 58 +++++ .../common/dialogs/ConfirmDeleteDialog.java | 60 +++++ .../ui/common/dialogs/DialogFactory.java | 71 ++++++ .../dialogs}/FilePickerDialog.java | 107 ++++----- .../dialogs}/HistoryEditorDialog.java | 2 +- .../dialogs}/WeekdayPickerDialog.java | 59 +++-- ...aseDialogFragment.java => BaseDialog.java} | 43 ++-- ...ogFragment.java => CreateHabitDialog.java} | 2 +- ...alogFragment.java => EditHabitDialog.java} | 11 +- .../ui/habits/list/ListHabitsController.java | 4 +- .../ui/habits/list/ListHabitsScreen.java | 109 ++++----- .../ui/habits/show/ShowHabitController.java | 2 +- .../ui/habits/show/ShowHabitScreen.java | 17 +- .../uhabits/ui/intro/IntroActivity.java | 8 +- .../uhabits/ui/settings/SettingsActivity.java | 3 - .../uhabits/ui/widgets/CheckmarkWidget.java | 4 +- .../uhabits/ui/widgets/FrequencyWidget.java | 4 +- .../uhabits/ui/widgets/HistoryWidget.java | 4 +- .../uhabits/ui/widgets/ScoreWidget.java | 4 +- .../uhabits/ui/widgets/StreakWidget.java | 4 +- .../org/isoron/uhabits/utils/FileUtils.java | 94 ++++---- .../org/isoron/uhabits/utils/Preferences.java | 4 + .../uhabits/utils/ReminderScheduler.java | 15 +- .../uhabits/utils/WidgetPreferences.java | 8 +- app/src/main/res/layout/edit_habit.xml | 2 +- .../java/org/isoron/uhabits/BaseUnitTest.java | 30 ++- .../java/org/isoron/uhabits/TestModule.java | 52 ++++- .../ui/habits/list/ListHabitsScreenTest.java | 213 ++++++++++++++++++ .../uhabits/utils/ReminderSchedulerTest.java | 28 +-- 42 files changed, 1057 insertions(+), 428 deletions(-) create mode 100644 app/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.java create mode 100644 app/src/main/java/org/isoron/uhabits/io/DirFinder.java create mode 100644 app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ColorPickerDialog.java create mode 100644 app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ConfirmDeleteDialog.java create mode 100644 app/src/main/java/org/isoron/uhabits/ui/common/dialogs/DialogFactory.java rename app/src/main/java/org/isoron/uhabits/ui/{settings => common/dialogs}/FilePickerDialog.java (84%) rename app/src/main/java/org/isoron/uhabits/ui/{habits/edit => common/dialogs}/HistoryEditorDialog.java (98%) rename app/src/main/java/org/isoron/uhabits/ui/{habits/edit => common/dialogs}/WeekdayPickerDialog.java (79%) rename app/src/main/java/org/isoron/uhabits/ui/habits/edit/{BaseDialogFragment.java => BaseDialog.java} (85%) rename app/src/main/java/org/isoron/uhabits/ui/habits/edit/{CreateHabitDialogFragment.java => CreateHabitDialog.java} (95%) rename app/src/main/java/org/isoron/uhabits/ui/habits/edit/{EditHabitDialogFragment.java => EditHabitDialog.java} (84%) create mode 100644 app/src/test/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreenTest.java diff --git a/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java b/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java index 0bb843323..e4e3715b6 100644 --- a/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java +++ b/app/src/androidTest/java/org/isoron/uhabits/BaseAndroidTest.java @@ -24,15 +24,12 @@ import android.content.*; import android.os.*; import android.support.annotation.*; import android.support.test.*; -import android.support.test.runner.*; -import android.test.suitebuilder.annotation.*; import org.isoron.uhabits.commands.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.utils.*; import org.junit.*; -import org.junit.runner.*; import java.util.*; import java.util.concurrent.*; @@ -43,8 +40,6 @@ import static junit.framework.Assert.*; import static org.hamcrest.MatcherAssert.*; import static org.hamcrest.Matchers.*; -@RunWith(AndroidJUnit4.class) -@MediumTest public class BaseAndroidTest { // 8:00am, January 25th, 2015 (UTC) diff --git a/app/src/main/java/org/isoron/uhabits/AndroidModule.java b/app/src/main/java/org/isoron/uhabits/AndroidModule.java index 06deb46d4..e2d7902e8 100644 --- a/app/src/main/java/org/isoron/uhabits/AndroidModule.java +++ b/app/src/main/java/org/isoron/uhabits/AndroidModule.java @@ -21,11 +21,8 @@ package org.isoron.uhabits; import android.content.*; -import org.isoron.uhabits.commands.*; -import org.isoron.uhabits.intents.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.models.sqlite.*; -import org.isoron.uhabits.utils.*; import javax.inject.*; @@ -42,52 +39,21 @@ public class AndroidModule { @Provides @Singleton - CommandRunner provideCommandRunner() - { - return new CommandRunner(); - } - - @Provides - @Singleton - HabitList provideHabitList() + static HabitList provideHabitList() { return SQLiteHabitList.getInstance(); } @Provides - ModelFactory provideModelFactory() + static ModelFactory provideModelFactory() { return new SQLModelFactory(); } @Provides @Singleton - Preferences providePreferences() - { - return new Preferences(); - } - - @Provides - @Singleton - WidgetPreferences provideWidgetPreferences() - { - return new WidgetPreferences(); - } - - @Provides - @Singleton - ReminderScheduler provideReminderScheduler() - { - Context context = HabitsApplication.getContext(); - IntentScheduler intentScheduler = new IntentScheduler(context); - IntentFactory intentFactory = new IntentFactory(context); - return new ReminderScheduler(intentFactory, intentScheduler); - } - - @Provides - @Singleton - HabitLogger provideLogger() + static Context provideApplicationContext() { - return new HabitLogger(); + return HabitsApplication.getContext(); } } diff --git a/app/src/main/java/org/isoron/uhabits/BaseComponent.java b/app/src/main/java/org/isoron/uhabits/BaseComponent.java index d5cb21b29..b4d13fe8a 100644 --- a/app/src/main/java/org/isoron/uhabits/BaseComponent.java +++ b/app/src/main/java/org/isoron/uhabits/BaseComponent.java @@ -20,11 +20,13 @@ package org.isoron.uhabits; import org.isoron.uhabits.commands.*; +import org.isoron.uhabits.intents.*; import org.isoron.uhabits.io.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.receivers.*; import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.ui.*; +import org.isoron.uhabits.ui.common.dialogs.*; import org.isoron.uhabits.ui.habits.edit.*; import org.isoron.uhabits.ui.habits.list.*; import org.isoron.uhabits.ui.habits.list.controllers.*; @@ -40,6 +42,8 @@ import org.isoron.uhabits.widgets.*; */ public interface BaseComponent { + IntentFactory getIntentFactory(); + void inject(CheckmarkButtonController checkmarkButtonController); void inject(ListHabitsController listHabitsController); @@ -90,7 +94,7 @@ public interface BaseComponent void inject(HabitsCSVExporter habitsCSVExporter); - void inject(BaseDialogFragment baseDialogFragment); + void inject(BaseDialog baseDialog); void inject(ShowHabitController showHabitController); @@ -109,4 +113,12 @@ public interface BaseComponent void inject(ReminderReceiver reminderReceiver); void inject(ReminderScheduler reminderScheduler); + + void inject(ListHabitsScreen listHabitsScreen); + + void inject(ShowHabitScreen showHabitScreen); + + void inject(ConfirmDeleteDialog confirmDeleteDialog); + + void inject(PendingIntentFactory pendingIntentFactory); } diff --git a/app/src/main/java/org/isoron/uhabits/HabitLogger.java b/app/src/main/java/org/isoron/uhabits/HabitLogger.java index 752a33401..6a0f83e3d 100644 --- a/app/src/main/java/org/isoron/uhabits/HabitLogger.java +++ b/app/src/main/java/org/isoron/uhabits/HabitLogger.java @@ -28,8 +28,17 @@ import org.isoron.uhabits.utils.*; import java.text.*; import java.util.*; +import javax.inject.*; + +@Singleton public class HabitLogger { + @Inject + public HabitLogger() + { + + } + public void logReminderScheduled(@NonNull Habit habit, @NonNull Long reminderTime) { diff --git a/app/src/main/java/org/isoron/uhabits/commands/CommandRunner.java b/app/src/main/java/org/isoron/uhabits/commands/CommandRunner.java index d1051e744..299aa1fa6 100644 --- a/app/src/main/java/org/isoron/uhabits/commands/CommandRunner.java +++ b/app/src/main/java/org/isoron/uhabits/commands/CommandRunner.java @@ -26,16 +26,20 @@ import org.isoron.uhabits.tasks.BaseTask; import java.util.LinkedList; +import javax.inject.*; + /** * A CommandRunner executes and undoes commands. *

* CommandRunners also allows objects to subscribe to it, and receive events * whenever a command is performed. */ +@Singleton public class CommandRunner { private LinkedList listeners; + @Inject public CommandRunner() { listeners = new LinkedList<>(); diff --git a/app/src/main/java/org/isoron/uhabits/intents/IntentFactory.java b/app/src/main/java/org/isoron/uhabits/intents/IntentFactory.java index 090e4e414..19a222bbe 100644 --- a/app/src/main/java/org/isoron/uhabits/intents/IntentFactory.java +++ b/app/src/main/java/org/isoron/uhabits/intents/IntentFactory.java @@ -19,94 +19,53 @@ package org.isoron.uhabits.intents; -import android.app.*; import android.content.*; import android.net.*; -import android.support.annotation.*; +import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; -import org.isoron.uhabits.receivers.*; +import org.isoron.uhabits.ui.about.*; import org.isoron.uhabits.ui.habits.show.*; +import org.isoron.uhabits.ui.intro.*; +import org.isoron.uhabits.ui.settings.*; -import static android.app.PendingIntent.*; +import javax.inject.*; public class IntentFactory { - @NonNull - private final Context context; - - public IntentFactory(@NonNull Context context) + @Inject + public IntentFactory() { - this.context = context; - } - public PendingIntent buildAddCheckmark(@NonNull Habit habit, - @Nullable Long timestamp) - { - Uri data = habit.getUri(); - Intent checkIntent = new Intent(context, WidgetReceiver.class); - checkIntent.setData(data); - checkIntent.setAction(WidgetReceiver.ACTION_ADD_REPETITION); - if (timestamp != null) checkIntent.putExtra("timestamp", timestamp); - return PendingIntent.getBroadcast(context, 1, checkIntent, - FLAG_UPDATE_CURRENT); } - public PendingIntent buildDismissNotification() + public Intent startAboutActivity(Context context) { - Intent deleteIntent = new Intent(context, ReminderReceiver.class); - deleteIntent.setAction(WidgetReceiver.ACTION_DISMISS_REMINDER); - return PendingIntent.getBroadcast(context, 0, deleteIntent, - FLAG_UPDATE_CURRENT); + return new Intent(context, AboutActivity.class); } - public PendingIntent buildShowReminder(@NonNull Habit habit, - @Nullable Long reminderTime, - long timestamp) + public Intent startIntroActivity(Context context) { - Uri uri = habit.getUri(); - - Intent intent = new Intent(context, ReminderReceiver.class); - intent.setAction(ReminderReceiver.ACTION_SHOW_REMINDER); - intent.setData(uri); - intent.putExtra("timestamp", timestamp); - intent.putExtra("reminderTime", reminderTime); - int reqCode = ((int) (habit.getId() % Integer.MAX_VALUE)) + 1; - return PendingIntent.getBroadcast(context, reqCode, intent, - FLAG_UPDATE_CURRENT); + return new Intent(context, IntroActivity.class); } - public PendingIntent buildSnoozeNotification(@NonNull Habit habit) + public Intent startSettingsActivity(Context context) { - Uri data = habit.getUri(); - Intent snoozeIntent = new Intent(context, ReminderReceiver.class); - snoozeIntent.setData(data); - snoozeIntent.setAction(ReminderReceiver.ACTION_SNOOZE_REMINDER); - return PendingIntent.getBroadcast(context, 0, snoozeIntent, - FLAG_UPDATE_CURRENT); + return new Intent(context, SettingsActivity.class); } - public PendingIntent buildToggleCheckmark(@NonNull Habit habit, - @Nullable Long timestamp) + public Intent startShowHabitActivity(Context context, Habit habit) { - Uri data = habit.getUri(); - Intent checkIntent = new Intent(context, WidgetReceiver.class); - checkIntent.setData(data); - checkIntent.setAction(WidgetReceiver.ACTION_TOGGLE_REPETITION); - if (timestamp != null) checkIntent.putExtra("timestamp", timestamp); - return PendingIntent.getBroadcast(context, 2, checkIntent, - FLAG_UPDATE_CURRENT); + Intent intent = new Intent(context, ShowHabitActivity.class); + intent.setData(habit.getUri()); + return intent; } - public PendingIntent buildViewHabit(Habit habit) + public Intent viewFAQ(Context context) { - Uri uri = habit.getUri(); - - Intent intent = new Intent(context, ShowHabitActivity.class); - intent.setData(uri); - return android.support.v4.app.TaskStackBuilder - .create(context) - .addNextIntentWithParentStack(intent) - .getPendingIntent(0, FLAG_UPDATE_CURRENT); + Intent intent = new Intent(); + intent.setAction(Intent.ACTION_VIEW); + intent.setData(Uri.parse(context.getString(R.string.helpURL))); + return intent; } } diff --git a/app/src/main/java/org/isoron/uhabits/intents/IntentScheduler.java b/app/src/main/java/org/isoron/uhabits/intents/IntentScheduler.java index 5bcca76e7..76bf70b89 100644 --- a/app/src/main/java/org/isoron/uhabits/intents/IntentScheduler.java +++ b/app/src/main/java/org/isoron/uhabits/intents/IntentScheduler.java @@ -24,13 +24,17 @@ import android.content.*; import android.os.*; import android.support.annotation.*; +import javax.inject.*; + import static android.app.AlarmManager.*; import static android.content.Context.*; +@Singleton public class IntentScheduler { private final AlarmManager manager; + @Inject public IntentScheduler(@NonNull Context context) { manager = (AlarmManager) context.getSystemService(ALARM_SERVICE); diff --git a/app/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.java b/app/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.java new file mode 100644 index 000000000..b7d687dc9 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/intents/PendingIntentFactory.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.intents; + +import android.app.*; +import android.content.*; +import android.net.*; +import android.support.annotation.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.receivers.*; + +import javax.inject.*; + +import static android.app.PendingIntent.*; + +@Singleton +public class PendingIntentFactory +{ + @NonNull + private final Context context; + + @Inject + protected IntentFactory intentFactory; + + @Inject + public PendingIntentFactory(@NonNull Context context) + { + this.context = context; + HabitsApplication.getComponent().inject(this); + } + + public PendingIntent addCheckmark(@NonNull Habit habit, + @Nullable Long timestamp) + { + Uri data = habit.getUri(); + Intent checkIntent = new Intent(context, WidgetReceiver.class); + checkIntent.setData(data); + checkIntent.setAction(WidgetReceiver.ACTION_ADD_REPETITION); + if (timestamp != null) checkIntent.putExtra("timestamp", timestamp); + return PendingIntent.getBroadcast(context, 1, checkIntent, + FLAG_UPDATE_CURRENT); + } + + public PendingIntent dismissNotification() + { + Intent deleteIntent = new Intent(context, ReminderReceiver.class); + deleteIntent.setAction(WidgetReceiver.ACTION_DISMISS_REMINDER); + return PendingIntent.getBroadcast(context, 0, deleteIntent, + FLAG_UPDATE_CURRENT); + } + + public PendingIntent showReminder(@NonNull Habit habit, + @Nullable Long reminderTime, + long timestamp) + { + Uri uri = habit.getUri(); + + Intent intent = new Intent(context, ReminderReceiver.class); + intent.setAction(ReminderReceiver.ACTION_SHOW_REMINDER); + intent.setData(uri); + intent.putExtra("timestamp", timestamp); + intent.putExtra("reminderTime", reminderTime); + int reqCode = ((int) (habit.getId() % Integer.MAX_VALUE)) + 1; + return PendingIntent.getBroadcast(context, reqCode, intent, + FLAG_UPDATE_CURRENT); + } + + public PendingIntent snoozeNotification(@NonNull Habit habit) + { + Uri data = habit.getUri(); + Intent snoozeIntent = new Intent(context, ReminderReceiver.class); + snoozeIntent.setData(data); + snoozeIntent.setAction(ReminderReceiver.ACTION_SNOOZE_REMINDER); + return PendingIntent.getBroadcast(context, 0, snoozeIntent, + FLAG_UPDATE_CURRENT); + } + + public PendingIntent toggleCheckmark(@NonNull Habit habit, + @Nullable Long timestamp) + { + Uri data = habit.getUri(); + Intent checkIntent = new Intent(context, WidgetReceiver.class); + checkIntent.setData(data); + checkIntent.setAction(WidgetReceiver.ACTION_TOGGLE_REPETITION); + if (timestamp != null) checkIntent.putExtra("timestamp", timestamp); + return PendingIntent.getBroadcast(context, 2, checkIntent, + FLAG_UPDATE_CURRENT); + } + + public PendingIntent showHabit(Habit habit) + { + Intent intent = intentFactory.startShowHabitActivity(context, habit); + + return android.support.v4.app.TaskStackBuilder + .create(context) + .addNextIntentWithParentStack(intent) + .getPendingIntent(0, FLAG_UPDATE_CURRENT); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/io/DirFinder.java b/app/src/main/java/org/isoron/uhabits/io/DirFinder.java new file mode 100644 index 000000000..08754b4af --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/io/DirFinder.java @@ -0,0 +1,106 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.io; + +import android.content.*; +import android.os.*; +import android.support.annotation.*; +import android.util.*; + +import org.isoron.uhabits.*; + +import java.io.*; + +import javax.inject.*; + +import static android.support.v4.content.ContextCompat.*; + +/** + * A DirFinder locates suitable directories for storing user files. + */ +public class DirFinder +{ + private static final String TAG = "DirFinder"; + + private final Context context; + + @Inject + public DirFinder() + { + context = HabitsApplication.getContext(); + } + + @Nullable + public File findSDCardDir(@Nullable String subpath) + { + File parents[] = new File[]{ + Environment.getExternalStorageDirectory() + }; + + return findDir(parents, subpath); + } + + @Nullable + public File findStorageDir(@Nullable String relativePath) + { + File potentialParents[] = getExternalFilesDirs(context, null); + + if (potentialParents == null) + { + Log.e(TAG, "getFilesDir: getExternalFilesDirs returned null"); + return null; + } + + return findDir(potentialParents, relativePath); + } + + @Nullable + private File findDir(@NonNull File potentialParents[], + @Nullable String relativePath) + { + if (relativePath == null) relativePath = ""; + + File chosenDir = null; + for (File dir : potentialParents) + { + if (dir == null || !dir.canWrite()) continue; + chosenDir = dir; + break; + } + + if (chosenDir == null) + { + Log.e(TAG, + "getDir: all potential parents are null or non-writable"); + return null; + } + + File dir = new File( + String.format("%s/%s/", chosenDir.getAbsolutePath(), relativePath)); + if (!dir.exists() && !dir.mkdirs()) + { + Log.e(TAG, + "getDir: chosen dir does not exist and cannot be created"); + return null; + } + + return dir; + } +} diff --git a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java index 904391e36..70b1e9e70 100644 --- a/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java +++ b/app/src/main/java/org/isoron/uhabits/models/sqlite/SQLModelFactory.java @@ -21,11 +21,20 @@ package org.isoron.uhabits.models.sqlite; import org.isoron.uhabits.models.*; +import javax.inject.*; + /** * Factory that provides models backed by an SQLite database. */ +@Singleton public class SQLModelFactory implements ModelFactory { + @Inject + public SQLModelFactory() + { + + } + @Override public RepetitionList buildRepetitionList(Habit habit) { diff --git a/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java b/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java index 1808fe376..18e2f604c 100644 --- a/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java +++ b/app/src/main/java/org/isoron/uhabits/receivers/ReminderReceiver.java @@ -145,14 +145,15 @@ public class ReminderReceiver extends BroadcastReceiver context, 0, contentIntent, PendingIntent.FLAG_CANCEL_CURRENT); - IntentFactory intentFactory = new IntentFactory(context); + PendingIntentFactory + pendingIntentFactory = new PendingIntentFactory(context); PendingIntent dismissPendingIntent; - dismissPendingIntent = intentFactory.buildDismissNotification(); + dismissPendingIntent = pendingIntentFactory.dismissNotification(); PendingIntent checkIntentPending = - intentFactory.buildAddCheckmark(habit, timestamp); + pendingIntentFactory.addCheckmark(habit, timestamp); PendingIntent snoozeIntentPending = - intentFactory.buildSnoozeNotification(habit); + pendingIntentFactory.snoozeNotification(habit); Uri ringtoneUri = RingtoneUtils.getRingtoneUri(context); diff --git a/app/src/main/java/org/isoron/uhabits/ui/BaseActivity.java b/app/src/main/java/org/isoron/uhabits/ui/BaseActivity.java index d286e2fe0..dabaa86fb 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/BaseActivity.java +++ b/app/src/main/java/org/isoron/uhabits/ui/BaseActivity.java @@ -24,6 +24,7 @@ import android.os.*; import android.support.annotation.*; import android.support.v7.app.*; import android.view.*; +import android.widget.*; import org.isoron.uhabits.utils.*; @@ -51,6 +52,8 @@ abstract public class BaseActivity extends AppCompatActivity @Nullable private BaseScreen screen; + private Toast toast; + @Override public boolean onCreateOptionsMenu(@Nullable Menu menu) { @@ -78,6 +81,30 @@ abstract public class BaseActivity extends AppCompatActivity this.screen = screen; } + public void showDialog(AppCompatDialogFragment dialog, String tag) + { + dialog.show(getSupportFragmentManager(), tag); + } + + public void showDialog(AppCompatDialog dialog) + { + dialog.show(); + } + + /** + * Shows a message on the screen. + * + * @param stringId the string resource id for this message. + */ + public void showMessage(@StringRes Integer stringId) + { + if (stringId == null) return; + if (toast == null) + toast = Toast.makeText(this, stringId, Toast.LENGTH_SHORT); + else toast.setText(stringId); + toast.show(); + } + @Override public void uncaughtException(@Nullable Thread thread, @Nullable Throwable ex) diff --git a/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java b/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java index 08b30526b..5b034a2e0 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java +++ b/app/src/main/java/org/isoron/uhabits/ui/BaseScreen.java @@ -27,12 +27,11 @@ import android.os.*; import android.support.annotation.*; import android.support.v7.app.*; import android.support.v7.view.ActionMode; -import android.support.v7.widget.Toolbar; +import android.support.v7.widget.*; import android.view.*; -import android.widget.*; import org.isoron.uhabits.*; -import org.isoron.uhabits.tasks.ProgressBar; +import org.isoron.uhabits.tasks.*; import org.isoron.uhabits.utils.*; import java.io.*; @@ -48,8 +47,6 @@ public abstract class BaseScreen { protected BaseActivity activity; - private Toast toast; - @Nullable private BaseRootView rootView; @@ -155,6 +152,11 @@ public abstract class BaseScreen activity.setBaseMenu(menu); } + public void showMessage(@StringRes int stringId) + { + activity.showMessage(stringId); + } + /** * Sets the root view for this screen. * @@ -179,20 +181,6 @@ public abstract class BaseScreen this.selectionMenu = menu; } - /** - * Shows a message on the screen. - * - * @param stringId the string resource id for this message. - */ - public void showMessage(@Nullable Integer stringId) - { - if (stringId == null) return; - if (toast == null) - toast = Toast.makeText(activity, stringId, Toast.LENGTH_SHORT); - else toast.setText(stringId); - toast.show(); - } - public void showSendEmailScreen(String to, String subject, String content) { Intent intent = new Intent(); @@ -225,11 +213,6 @@ public abstract class BaseScreen activity.startSupportActionMode(new ActionModeWrapper()); } - protected void showDialog(AppCompatDialogFragment dialog, String tag) - { - dialog.show(activity.getSupportFragmentManager(), tag); - } - public void invalidateToolbar() { if (rootView == null) return; diff --git a/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ColorPickerDialog.java b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ColorPickerDialog.java new file mode 100644 index 000000000..c296c0713 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ColorPickerDialog.java @@ -0,0 +1,58 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.ui.common.dialogs; + +import android.content.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.utils.*; + +/** + * Dialog that allows the user to choose a color. + */ +public class ColorPickerDialog extends com.android.colorpicker.ColorPickerDialog +{ + public static ColorPickerDialog newInstance(int paletteColor) + { + ColorPickerDialog dialog = new ColorPickerDialog(); + Context context = dialog.getContext(); + + int color = ColorUtils.getColor(context, paletteColor); + + dialog.initialize(R.string.color_picker_default_title, + ColorUtils.getPalette(context), color, 4, + com.android.colorpicker.ColorPickerDialog.SIZE_SMALL); + + return dialog; + } + + public void setListener(OnColorSelectedListener listener) + { + super.setOnColorSelectedListener(c -> { + c = ColorUtils.colorToPaletteIndex(getContext(), c); + listener.onColorSelected(c); + }); + } + + public interface OnColorSelectedListener + { + void onColorSelected(int color); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ConfirmDeleteDialog.java b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ConfirmDeleteDialog.java new file mode 100644 index 000000000..556b67393 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/ConfirmDeleteDialog.java @@ -0,0 +1,60 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.ui.common.dialogs; + +import android.content.*; +import android.support.annotation.*; +import android.support.v7.app.*; + +import org.isoron.uhabits.*; + +import butterknife.*; + +/** + * Dialog that asks the user confirmation before executing a delete operation. + */ +public class ConfirmDeleteDialog extends AlertDialog +{ + @BindString(R.string.delete_habits_message) + protected String question; + + @BindString(android.R.string.yes) + protected String yes; + + @BindString(android.R.string.no) + protected String no; + + protected ConfirmDeleteDialog(@NonNull Context context, + @NonNull Callback callback) + { + super(context); + ButterKnife.bind(this); + + setTitle(R.string.delete_habits); + setMessage(question); + setButton(BUTTON_POSITIVE, yes, (dialog, which) -> callback.run()); + setButton(BUTTON_NEGATIVE, no, (dialog, which) -> {}); + } + + public interface Callback + { + void run(); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/DialogFactory.java b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/DialogFactory.java new file mode 100644 index 000000000..a684d7704 --- /dev/null +++ b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/DialogFactory.java @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2016 Álinson Santos Xavier + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.ui.common.dialogs; + +import android.content.*; +import android.support.annotation.*; + +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.habits.edit.*; + +import java.io.*; + +import javax.inject.*; + +public class DialogFactory +{ + @Inject + public DialogFactory() + { + + } + + @NonNull + public ColorPickerDialog buildColorPicker(int paletteColor) + { + return ColorPickerDialog.newInstance(paletteColor); + } + + @NonNull + public ConfirmDeleteDialog buildConfirmDeleteDialog( + @NonNull Context context, + @NonNull ConfirmDeleteDialog.Callback callback) + { + return new ConfirmDeleteDialog(context, callback); + } + + @NonNull + public CreateHabitDialog buildCreateHabitDialog() + { + return new CreateHabitDialog(); + } + + @NonNull + public EditHabitDialog buildEditHabitDialog(Habit habit) + { + return EditHabitDialog.newInstance(habit); + } + + @NonNull + public FilePickerDialog buildFilePicker(Context context, File dir) + { + return new FilePickerDialog(context, dir); + } +} diff --git a/app/src/main/java/org/isoron/uhabits/ui/settings/FilePickerDialog.java b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/FilePickerDialog.java similarity index 84% rename from app/src/main/java/org/isoron/uhabits/ui/settings/FilePickerDialog.java rename to app/src/main/java/org/isoron/uhabits/ui/common/dialogs/FilePickerDialog.java index 8d7faa1c7..c6a97f0fb 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/settings/FilePickerDialog.java +++ b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/FilePickerDialog.java @@ -17,50 +17,43 @@ * with this program. If not, see . */ -package org.isoron.uhabits.ui.settings; - -import android.app.Activity; -import android.app.Dialog; -import android.support.annotation.NonNull; -import android.view.View; -import android.view.ViewGroup; -import android.view.WindowManager.LayoutParams; -import android.widget.AdapterView; -import android.widget.ArrayAdapter; -import android.widget.ListView; -import android.widget.TextView; - -import java.io.File; -import java.io.FileFilter; -import java.util.Arrays; +package org.isoron.uhabits.ui.common.dialogs; +import android.content.*; +import android.support.annotation.*; +import android.support.v7.app.*; +import android.view.*; +import android.view.WindowManager.*; +import android.widget.*; + +import java.io.*; +import java.util.*; + +/** + * Dialog that allows the user to pick a file. + */ public class FilePickerDialog implements AdapterView.OnItemClickListener { private static final String PARENT_DIR = ".."; - private final Activity activity; + private final Context context; private ListView list; - private Dialog dialog; + private AppCompatDialog dialog; private File currentPath; - public interface OnFileSelectedListener - { - void onFileSelected(File file); - } - private OnFileSelectedListener listener; - public FilePickerDialog(Activity activity, File initialDirectory) + public FilePickerDialog(Context context, File initialDirectory) { - this.activity = activity; + this.context = context; - list = new ListView(activity); + list = new ListView(context); list.setOnItemClickListener(this); - dialog = new Dialog(activity); + dialog = new AppCompatDialog(context); dialog.setContentView(list); dialog .getWindow() @@ -69,6 +62,11 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener navigateTo(initialDirectory); } + public AppCompatDialog getDialog() + { + return dialog; + } + @Override public void onItemClick(AdapterView parent, View view, @@ -92,27 +90,14 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener } } - public void show() - { - dialog.show(); - } - public void setListener(OnFileSelectedListener listener) { this.listener = listener; } - private void navigateTo(File path) + public void show() { - if (!path.exists()) return; - - File[] dirs = path.listFiles(new ReadableDirFilter()); - File[] files = path.listFiles(new RegularReadableFileFilter()); - if (dirs == null || files == null) return; - - this.currentPath = path; - dialog.setTitle(currentPath.getPath()); - list.setAdapter(new FilePickerAdapter(getFileList(path, dirs, files))); + dialog.show(); } @NonNull @@ -144,11 +129,38 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener return fileList; } + private void navigateTo(File path) + { + if (!path.exists()) return; + + File[] dirs = path.listFiles(new ReadableDirFilter()); + File[] files = path.listFiles(new RegularReadableFileFilter()); + if (dirs == null || files == null) return; + + this.currentPath = path; + dialog.setTitle(currentPath.getPath()); + list.setAdapter(new FilePickerAdapter(getFileList(path, dirs, files))); + } + + public interface OnFileSelectedListener + { + void onFileSelected(File file); + } + + private static class ReadableDirFilter implements FileFilter + { + @Override + public boolean accept(File file) + { + return (file.isDirectory() && file.canRead()); + } + } + private class FilePickerAdapter extends ArrayAdapter { public FilePickerAdapter(@NonNull String[] fileList) { - super(FilePickerDialog.this.activity, + super(FilePickerDialog.this.context, android.R.layout.simple_list_item_1, fileList); } @@ -162,15 +174,6 @@ public class FilePickerDialog implements AdapterView.OnItemClickListener } } - private static class ReadableDirFilter implements FileFilter - { - @Override - public boolean accept(File file) - { - return (file.isDirectory() && file.canRead()); - } - } - private class RegularReadableFileFilter implements FileFilter { @Override diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/HistoryEditorDialog.java b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/HistoryEditorDialog.java similarity index 98% rename from app/src/main/java/org/isoron/uhabits/ui/habits/edit/HistoryEditorDialog.java rename to app/src/main/java/org/isoron/uhabits/ui/common/dialogs/HistoryEditorDialog.java index 3aa60ef76..d625e8a70 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/HistoryEditorDialog.java +++ b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/HistoryEditorDialog.java @@ -17,7 +17,7 @@ * with this program. If not, see . */ -package org.isoron.uhabits.ui.habits.edit; +package org.isoron.uhabits.ui.common.dialogs; import android.app.*; import android.content.*; diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/WeekdayPickerDialog.java b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/WeekdayPickerDialog.java similarity index 79% rename from app/src/main/java/org/isoron/uhabits/ui/habits/edit/WeekdayPickerDialog.java rename to app/src/main/java/org/isoron/uhabits/ui/common/dialogs/WeekdayPickerDialog.java index 477c736f0..ca0fee0f3 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/WeekdayPickerDialog.java +++ b/app/src/main/java/org/isoron/uhabits/ui/common/dialogs/WeekdayPickerDialog.java @@ -17,39 +17,39 @@ * with this program. If not, see . */ -package org.isoron.uhabits.ui.habits.edit; +package org.isoron.uhabits.ui.common.dialogs; -import android.app.Dialog; -import android.content.DialogInterface; -import android.os.Bundle; +import android.app.*; +import android.content.*; +import android.os.*; import android.support.v7.app.AlertDialog; -import android.support.v7.app.AppCompatDialogFragment; +import android.support.v7.app.*; -import org.isoron.uhabits.R; -import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.*; +import org.isoron.uhabits.utils.*; +/** + * Dialog that allows the user to pick one or more days of the week. + */ public class WeekdayPickerDialog extends AppCompatDialogFragment implements DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener { - public interface OnWeekdaysPickedListener - { - void onWeekdaysPicked(boolean[] selectedDays); - } - private boolean[] selectedDays; private OnWeekdaysPickedListener listener; - public void setListener(OnWeekdaysPickedListener listener) + @Override + public void onClick(DialogInterface dialog, int which, boolean isChecked) { - this.listener = listener; + selectedDays[which] = isChecked; } - public void setSelectedDays(boolean[] selectedDays) + @Override + public void onClick(DialogInterface dialog, int which) { - this.selectedDays = selectedDays; + if (listener != null) listener.onWeekdaysPicked(selectedDays); } @Override @@ -61,28 +61,25 @@ public class WeekdayPickerDialog extends AppCompatDialogFragment implements .setMultiChoiceItems(DateUtils.getLongDayNames(), selectedDays, this) .setPositiveButton(android.R.string.yes, this) - .setNegativeButton(android.R.string.cancel, - new DialogInterface.OnClickListener() - { - @Override - public void onClick(DialogInterface dialog, int which) - { - dismiss(); - } - }); + .setNegativeButton(android.R.string.cancel, (dialog, which) -> { + dismiss(); + }); return builder.create(); } - @Override - public void onClick(DialogInterface dialog, int which, boolean isChecked) + public void setListener(OnWeekdaysPickedListener listener) { - selectedDays[which] = isChecked; + this.listener = listener; } - @Override - public void onClick(DialogInterface dialog, int which) + public void setSelectedDays(boolean[] selectedDays) { - if (listener != null) listener.onWeekdaysPicked(selectedDays); + this.selectedDays = selectedDays; + } + + public interface OnWeekdaysPickedListener + { + void onWeekdaysPicked(boolean[] selectedDays); } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogFragment.java b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialog.java similarity index 85% rename from app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogFragment.java rename to app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialog.java index 8b597694f..656c3d163 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialogFragment.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/BaseDialog.java @@ -25,14 +25,14 @@ import android.support.v7.app.*; import android.text.format.*; import android.view.*; -import com.android.colorpicker.*; import com.android.datetimepicker.time.*; import org.isoron.uhabits.*; import org.isoron.uhabits.commands.*; import org.isoron.uhabits.models.*; -import org.isoron.uhabits.utils.*; +import org.isoron.uhabits.ui.common.dialogs.*; import org.isoron.uhabits.utils.DateUtils; +import org.isoron.uhabits.utils.*; import java.util.*; @@ -40,7 +40,7 @@ import javax.inject.*; import butterknife.*; -public abstract class BaseDialogFragment extends AppCompatDialogFragment +public abstract class BaseDialog extends AppCompatDialogFragment { @Nullable protected Habit originalHabit; @@ -60,6 +60,9 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment @Inject protected HabitList habitList; + @Inject + protected DialogFactory dialogFactory; + @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -83,7 +86,8 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment if (position < 0 || position > 4) throw new IllegalArgumentException(); int freqNums[] = { 1, 1, 2, 5, 3 }; int freqDens[] = { 1, 7, 7, 7, 7 }; - modifiedHabit.setFrequency(new Frequency(freqNums[position], freqDens[position])); + modifiedHabit.setFrequency( + new Frequency(freqNums[position], freqDens[position])); helper.populateFrequencyFields(modifiedHabit); } @@ -169,23 +173,22 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment WeekdayPickerDialog dialog = new WeekdayPickerDialog(); dialog.setListener(new OnWeekdaysPickedListener()); - dialog.setSelectedDays( - DateUtils.unpackWeekdayList(reminder.getDays())); + dialog.setSelectedDays(DateUtils.unpackWeekdayList(reminder.getDays())); dialog.show(getFragmentManager(), "weekdayPicker"); } @OnClick(R.id.buttonPickColor) void showColorPicker() { - int androidColor = - ColorUtils.getColor(getContext(), modifiedHabit.getColor()); + int color = modifiedHabit.getColor(); + ColorPickerDialog picker = dialogFactory.buildColorPicker(color); - ColorPickerDialog picker = - ColorPickerDialog.newInstance(R.string.color_picker_default_title, - ColorUtils.getPalette(getContext()), androidColor, 4, - ColorPickerDialog.SIZE_SMALL); + picker.setListener(c -> { + prefs.setDefaultHabitColor(c); + modifiedHabit.setColor(c); + helper.populateColor(c); + }); - picker.setOnColorSelectedListener(new OnColorSelectedListener()); picker.show(getFragmentManager(), "picker"); } @@ -198,20 +201,6 @@ public abstract class BaseDialogFragment extends AppCompatDialogFragment timePicker.show(getFragmentManager(), "timePicker"); } - private class OnColorSelectedListener - implements ColorPickerSwatch.OnColorSelectedListener - { - @Override - public void onColorSelected(int androidColor) - { - int paletteColor = - ColorUtils.colorToPaletteIndex(getActivity(), androidColor); - prefs.setDefaultHabitColor(paletteColor); - modifiedHabit.setColor(paletteColor); - helper.populateColor(paletteColor); - } - } - private class OnTimeSetListener implements TimePickerDialog.OnTimeSetListener { diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialogFragment.java b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialog.java similarity index 95% rename from app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialogFragment.java rename to app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialog.java index 6479dd000..2eeae776d 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialogFragment.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/CreateHabitDialog.java @@ -23,7 +23,7 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.commands.*; import org.isoron.uhabits.models.*; -public class CreateHabitDialogFragment extends BaseDialogFragment +public class CreateHabitDialog extends BaseDialog { @Override protected int getTitle() diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/EditHabitDialogFragment.java b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/EditHabitDialog.java similarity index 84% rename from app/src/main/java/org/isoron/uhabits/ui/habits/edit/EditHabitDialogFragment.java rename to app/src/main/java/org/isoron/uhabits/ui/habits/edit/EditHabitDialog.java index 478d7d092..19b068c5b 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/edit/EditHabitDialogFragment.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/edit/EditHabitDialog.java @@ -25,13 +25,16 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.commands.*; import org.isoron.uhabits.models.*; -public class EditHabitDialogFragment extends BaseDialogFragment +public class EditHabitDialog extends BaseDialog { - public static EditHabitDialogFragment newInstance(long habitId) + public static EditHabitDialog newInstance(Habit habit) { - EditHabitDialogFragment frag = new EditHabitDialogFragment(); + if(habit.getId() == null) + throw new IllegalArgumentException("habit not saved"); + + EditHabitDialog frag = new EditHabitDialog(); Bundle args = new Bundle(); - args.putLong("habitId", habitId); + args.putLong("habitId", habit.getId()); frag.setArguments(args); return frag; } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java index ded3f4b8d..4d3e0ff8b 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsController.java @@ -48,7 +48,8 @@ public class ListHabitsController @NonNull private final HabitList habitList; - private HabitCardListAdapter adapter; + @NonNull + private final HabitCardListAdapter adapter; @Inject Preferences prefs; @@ -81,6 +82,7 @@ public class ListHabitsController if (filename != null) screen.showSendFileScreen(filename); else screen.showMessage(R.string.could_not_export); }); + task.execute(); } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java index 2440126c9..e3c176308 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/list/ListHabitsScreen.java @@ -20,35 +20,43 @@ package org.isoron.uhabits.ui.habits.list; import android.content.*; -import android.net.*; import android.os.*; import android.support.annotation.*; -import android.support.v7.app.*; - -import com.android.colorpicker.*; import org.isoron.uhabits.*; +import org.isoron.uhabits.intents.*; +import org.isoron.uhabits.io.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.ui.*; -import org.isoron.uhabits.ui.about.*; +import org.isoron.uhabits.ui.common.dialogs.*; +import org.isoron.uhabits.ui.common.dialogs.ColorPickerDialog.*; import org.isoron.uhabits.ui.habits.edit.*; -import org.isoron.uhabits.ui.habits.show.*; -import org.isoron.uhabits.ui.intro.*; -import org.isoron.uhabits.ui.settings.*; import org.isoron.uhabits.utils.*; import java.io.*; +import javax.inject.*; + public class ListHabitsScreen extends BaseScreen { @Nullable ListHabitsController controller; + @Inject + protected DialogFactory dialogFactory; + + @Inject + protected IntentFactory intentFactory; + + @Inject + protected DirFinder dirFinder; + public ListHabitsScreen(@NonNull BaseActivity activity, - ListHabitsRootView rootView) + @NonNull ListHabitsRootView rootView) { super(activity); setRootView(rootView); + HabitsApplication.getComponent().inject(this); } @Override @@ -83,90 +91,83 @@ public class ListHabitsScreen extends BaseScreen public void showAboutScreen() { - Intent intent = new Intent(activity, AboutActivity.class); + Intent intent = intentFactory.startAboutActivity(activity); activity.startActivity(intent); } - public void showColorPicker(Habit habit, OnColorSelectedListener callback) + /** + * Displays a {@link ColorPickerDialog} to the user. + *

+ * The selected color on the dialog is the color of the given habit. + * + * @param habit the habit + * @param callback + */ + public void showColorPicker(@NonNull Habit habit, + @NonNull OnColorSelectedListener callback) { - int color = ColorUtils.getColor(activity, habit.getColor()); - ColorPickerDialog picker = - ColorPickerDialog.newInstance(R.string.color_picker_default_title, - ColorUtils.getPalette(activity), color, 4, - ColorPickerDialog.SIZE_SMALL); - - picker.setOnColorSelectedListener(c -> { - c = ColorUtils.colorToPaletteIndex(activity, c); - callback.onColorSelected(c); - }); - picker.show(activity.getSupportFragmentManager(), "picker"); + dialogFactory.buildColorPicker(habit.getColor()); + picker.setListener(callback); + activity.showDialog(picker, "picker"); } public void showCreateHabitScreen() { - showDialog(new CreateHabitDialogFragment(), "editHabit"); + CreateHabitDialog dialog = dialogFactory.buildCreateHabitDialog(); + activity.showDialog(dialog, "editHabit"); } - public void showDeleteConfirmationScreen(Callback callback) + public void showDeleteConfirmationScreen(ConfirmDeleteDialog.Callback callback) { - new AlertDialog.Builder(activity) - .setTitle(R.string.delete_habits) - .setMessage(R.string.delete_habits_message) - .setPositiveButton(android.R.string.yes, - (dialog, which) -> callback.run()) - .setNegativeButton(android.R.string.no, null) - .show(); + ConfirmDeleteDialog dialog = + dialogFactory.buildConfirmDeleteDialog(activity, callback); + activity.showDialog(dialog); } public void showEditHabitScreen(Habit habit) { - BaseDialogFragment frag = - EditHabitDialogFragment.newInstance(habit.getId()); - frag.show(activity.getSupportFragmentManager(), "editHabit"); + EditHabitDialog dialog = dialogFactory.buildEditHabitDialog(habit); + activity.showDialog(dialog, "editHabit"); } public void showFAQScreen() { - Intent intent = new Intent(); - intent.setAction(Intent.ACTION_VIEW); - intent.setData(Uri.parse(activity.getString(R.string.helpURL))); + Intent intent = intentFactory.viewFAQ(activity); activity.startActivity(intent); } public void showHabitScreen(@NonNull Habit habit) { - Intent intent = new Intent(activity, ShowHabitActivity.class); - intent.setData( - Uri.parse("content://org.isoron.uhabits/habit/" + habit.getId())); + Intent intent = intentFactory.startShowHabitActivity(activity, habit); activity.startActivity(intent); } public void showImportScreen() { - if (controller == null) return; + File dir = dirFinder.findStorageDir(null); - File dir = FileUtils.getFilesDir(null); if (dir == null) { - showMessage(R.string.could_not_import); + activity.showMessage(R.string.could_not_import); return; } - FilePickerDialog picker = new FilePickerDialog(activity, dir); - picker.setListener(file -> controller.onImportData(file)); - picker.show(); + FilePickerDialog picker = dialogFactory.buildFilePicker(activity, dir); + if (controller != null) + picker.setListener(file -> controller.onImportData(file)); + activity.showDialog(picker.getDialog()); } public void showIntroScreen() { - Intent intent = new Intent(activity, IntroActivity.class); + Intent intent = intentFactory.startIntroActivity(activity); activity.startActivity(intent); } public void showSettingsScreen() { - Intent intent = new Intent(activity, SettingsActivity.class); + Intent intent = intentFactory.startSettingsActivity(activity); activity.startActivityForResult(intent, 0); } @@ -182,7 +183,7 @@ public class ListHabitsScreen extends BaseScreen private void refreshTheme() { new Handler().postDelayed(() -> { - Intent intent = new Intent(activity, MainActivity.class); + Intent intent = new Intent(activity, ListHabitsScreen.class); activity.finish(); activity.overridePendingTransition(android.R.anim.fade_in, @@ -191,14 +192,4 @@ public class ListHabitsScreen extends BaseScreen }, 500); // HACK: Let the menu disappear first } - - interface Callback - { - void run(); - } - - public interface OnColorSelectedListener - { - void onColorSelected(int color); - } } diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitController.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitController.java index d05909f67..b42fa9c48 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitController.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitController.java @@ -25,7 +25,7 @@ import org.isoron.uhabits.*; import org.isoron.uhabits.commands.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.tasks.*; -import org.isoron.uhabits.ui.habits.edit.*; +import org.isoron.uhabits.ui.common.dialogs.*; import javax.inject.*; diff --git a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java index d5f7cf21b..c84288ef9 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java +++ b/app/src/main/java/org/isoron/uhabits/ui/habits/show/ShowHabitScreen.java @@ -20,33 +20,38 @@ package org.isoron.uhabits.ui.habits.show; import android.support.annotation.*; -import android.support.v4.app.*; +import org.isoron.uhabits.*; import org.isoron.uhabits.models.*; import org.isoron.uhabits.ui.*; +import org.isoron.uhabits.ui.common.dialogs.*; import org.isoron.uhabits.ui.habits.edit.*; +import javax.inject.*; + public class ShowHabitScreen extends BaseScreen { @NonNull private final Habit habit; + @Inject + protected DialogFactory dialogFactory; + public ShowHabitScreen(@NonNull BaseActivity activity, @NonNull Habit habit, ShowHabitRootView view) { super(activity); + HabitsApplication.getComponent().inject(this); + this.habit = habit; setRootView(view); } public void showEditHabitDialog() { - Long id = habit.getId(); - if (id == null) throw new RuntimeException("habit not saved"); - - FragmentManager manager = activity.getSupportFragmentManager(); - EditHabitDialogFragment.newInstance(id).show(manager, "editHabit"); + EditHabitDialog dialog = dialogFactory.buildEditHabitDialog(habit); + activity.showDialog(dialog, "editHabit"); } public void showEditHistoryDialog( diff --git a/app/src/main/java/org/isoron/uhabits/ui/intro/IntroActivity.java b/app/src/main/java/org/isoron/uhabits/ui/intro/IntroActivity.java index 0e6bcdf45..19ce9d321 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/intro/IntroActivity.java +++ b/app/src/main/java/org/isoron/uhabits/ui/intro/IntroActivity.java @@ -19,11 +19,11 @@ package org.isoron.uhabits.ui.intro; -import android.graphics.Color; -import android.os.Bundle; +import android.content.*; +import android.graphics.*; +import android.os.*; -import com.github.paolorotolo.appintro.AppIntro2; -import com.github.paolorotolo.appintro.AppIntroFragment; +import com.github.paolorotolo.appintro.*; import org.isoron.uhabits.R; diff --git a/app/src/main/java/org/isoron/uhabits/ui/settings/SettingsActivity.java b/app/src/main/java/org/isoron/uhabits/ui/settings/SettingsActivity.java index 24198dc7b..c281f23d8 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/settings/SettingsActivity.java +++ b/app/src/main/java/org/isoron/uhabits/ui/settings/SettingsActivity.java @@ -20,9 +20,6 @@ package org.isoron.uhabits.ui.settings; import android.os.*; -import android.support.annotation.*; -import android.support.v4.app.*; -import android.view.*; import org.isoron.uhabits.*; import org.isoron.uhabits.ui.*; diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/CheckmarkWidget.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/CheckmarkWidget.java index 6fcfb1133..14047809c 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/widgets/CheckmarkWidget.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/CheckmarkWidget.java @@ -45,8 +45,8 @@ public class CheckmarkWidget extends BaseWidget @Override public PendingIntent getOnClickPendingIntent(Context context) { - IntentFactory factory = new IntentFactory(context); - return factory.buildToggleCheckmark(habit, null); + PendingIntentFactory factory = new PendingIntentFactory(context); + return factory.toggleCheckmark(habit, null); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/FrequencyWidget.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/FrequencyWidget.java index 113b01d95..0fe977538 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/widgets/FrequencyWidget.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/FrequencyWidget.java @@ -46,8 +46,8 @@ public class FrequencyWidget extends BaseWidget @Override public PendingIntent getOnClickPendingIntent(Context context) { - IntentFactory factory = new IntentFactory(context); - return factory.buildViewHabit(habit); + PendingIntentFactory factory = new PendingIntentFactory(context); + return factory.showHabit(habit); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/HistoryWidget.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/HistoryWidget.java index e34586cca..d4ac726b2 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/widgets/HistoryWidget.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/HistoryWidget.java @@ -44,8 +44,8 @@ public class HistoryWidget extends BaseWidget @Override public PendingIntent getOnClickPendingIntent(Context context) { - IntentFactory factory = new IntentFactory(context); - return factory.buildViewHabit(habit); + PendingIntentFactory factory = new PendingIntentFactory(context); + return factory.showHabit(habit); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/ScoreWidget.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/ScoreWidget.java index fe11f3658..4def1ba49 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/widgets/ScoreWidget.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/ScoreWidget.java @@ -47,8 +47,8 @@ public class ScoreWidget extends BaseWidget @Override public PendingIntent getOnClickPendingIntent(Context context) { - IntentFactory factory = new IntentFactory(context); - return factory.buildViewHabit(habit); + PendingIntentFactory factory = new PendingIntentFactory(context); + return factory.showHabit(habit); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/ui/widgets/StreakWidget.java b/app/src/main/java/org/isoron/uhabits/ui/widgets/StreakWidget.java index 4957867e6..afc536a97 100644 --- a/app/src/main/java/org/isoron/uhabits/ui/widgets/StreakWidget.java +++ b/app/src/main/java/org/isoron/uhabits/ui/widgets/StreakWidget.java @@ -49,8 +49,8 @@ public class StreakWidget extends BaseWidget @Override public PendingIntent getOnClickPendingIntent(Context context) { - IntentFactory factory = new IntentFactory(context); - return factory.buildViewHabit(habit); + PendingIntentFactory factory = new PendingIntentFactory(context); + return factory.showHabit(habit); } @Override diff --git a/app/src/main/java/org/isoron/uhabits/utils/FileUtils.java b/app/src/main/java/org/isoron/uhabits/utils/FileUtils.java index 867dbbcf0..4bc4a5d76 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/FileUtils.java +++ b/app/src/main/java/org/isoron/uhabits/utils/FileUtils.java @@ -19,21 +19,15 @@ package org.isoron.uhabits.utils; -import android.content.Context; -import android.os.Environment; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v4.content.ContextCompat; -import android.util.Log; - -import org.isoron.uhabits.HabitsApplication; - -import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; +import android.content.*; +import android.os.*; +import android.support.annotation.*; +import android.support.v4.content.*; +import android.util.*; + +import org.isoron.uhabits.*; + +import java.io.*; public abstract class FileUtils { @@ -60,60 +54,60 @@ public abstract class FileUtils } @Nullable - public static File getSDCardDir(@Nullable String relativePath) + private static File getDir(@NonNull File potentialParentDirs[], + @Nullable String relativePath) { - File parents[] = new File[]{ Environment.getExternalStorageDirectory() }; - return getDir(parents, relativePath); - } - - @Nullable - public static File getFilesDir(@Nullable String relativePath) - { - Context context = HabitsApplication.getContext(); - if(context == null) - { - Log.e("DatabaseHelper", "getFilesDir: no application context available"); - return null; - } - - File externalFilesDirs[] = ContextCompat.getExternalFilesDirs(context, null); - - if(externalFilesDirs == null) - { - Log.e("DatabaseHelper", "getFilesDir: getExternalFilesDirs returned null"); - return null; - } - - return getDir(externalFilesDirs, relativePath); - } - - @Nullable - private static File getDir(@NonNull File potentialParentDirs[], @Nullable String relativePath) - { - if(relativePath == null) relativePath = ""; + if (relativePath == null) relativePath = ""; File chosenDir = null; - for(File dir : potentialParentDirs) + for (File dir : potentialParentDirs) { if (dir == null || !dir.canWrite()) continue; chosenDir = dir; break; } - if(chosenDir == null) + if (chosenDir == null) { - Log.e("DatabaseHelper", "getDir: all potential parents are null or non-writable"); + Log.e("DatabaseHelper", + "getDir: all potential parents are null or non-writable"); return null; } - File dir = new File(String.format("%s/%s/", chosenDir.getAbsolutePath(), relativePath)); + File dir = new File( + String.format("%s/%s/", chosenDir.getAbsolutePath(), relativePath)); if (!dir.exists() && !dir.mkdirs()) { - Log.e("DatabaseHelper", "getDir: chosen dir does not exist and cannot be created"); + Log.e("DatabaseHelper", + "getDir: chosen dir does not exist and cannot be created"); return null; } return dir; } + @Nullable + public static File getFilesDir(@Nullable String relativePath) + { + Context context = HabitsApplication.getContext(); + File externalFilesDirs[] = + ContextCompat.getExternalFilesDirs(context, null); + + if (externalFilesDirs == null) + { + Log.e("DatabaseHelper", + "getFilesDir: getExternalFilesDirs returned null"); + return null; + } + + return getDir(externalFilesDirs, relativePath); + } + + @Nullable + public static File getSDCardDir(@Nullable String relativePath) + { + File parents[] = + new File[]{ Environment.getExternalStorageDirectory() }; + return getDir(parents, relativePath); + } } diff --git a/app/src/main/java/org/isoron/uhabits/utils/Preferences.java b/app/src/main/java/org/isoron/uhabits/utils/Preferences.java index 22c631df6..5941a3541 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/Preferences.java +++ b/app/src/main/java/org/isoron/uhabits/utils/Preferences.java @@ -24,12 +24,16 @@ import android.preference.*; import org.isoron.uhabits.*; +import javax.inject.*; + +@Singleton public class Preferences { private Context context; private SharedPreferences prefs; + @Inject public Preferences() { this.context = HabitsApplication.getContext(); diff --git a/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java b/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java index 19a6bbc82..48bbddacf 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java +++ b/app/src/main/java/org/isoron/uhabits/utils/ReminderScheduler.java @@ -32,20 +32,21 @@ import javax.inject.*; import static org.isoron.uhabits.utils.DateUtils.*; +@Singleton public class ReminderScheduler { - private final IntentFactory intentFactory; + @Inject + protected PendingIntentFactory pendingIntentFactory; - private final IntentScheduler intentScheduler; + @Inject + protected IntentScheduler intentScheduler; @Inject HabitLogger logger; - public ReminderScheduler(IntentFactory intentFactory, - IntentScheduler intentScheduler) + @Inject + public ReminderScheduler() { - this.intentFactory = intentFactory; - this.intentScheduler = intentScheduler; HabitsApplication.getComponent().inject(this); } @@ -56,7 +57,7 @@ public class ReminderScheduler if (reminderTime == null) reminderTime = getReminderTime(reminder); long timestamp = getStartOfDay(toLocalTime(reminderTime)); - PendingIntent intent = intentFactory.buildShowReminder(habit, + PendingIntent intent = pendingIntentFactory.showReminder(habit, reminderTime, timestamp); intentScheduler.schedule(reminderTime, intent); logger.logReminderScheduled(habit, reminderTime); diff --git a/app/src/main/java/org/isoron/uhabits/utils/WidgetPreferences.java b/app/src/main/java/org/isoron/uhabits/utils/WidgetPreferences.java index 6fac955b5..e0b3e4f5c 100644 --- a/app/src/main/java/org/isoron/uhabits/utils/WidgetPreferences.java +++ b/app/src/main/java/org/isoron/uhabits/utils/WidgetPreferences.java @@ -22,17 +22,17 @@ package org.isoron.uhabits.utils; import android.content.*; import android.preference.*; -import org.isoron.uhabits.*; +import javax.inject.*; +@Singleton public class WidgetPreferences { - private Context context; private SharedPreferences prefs; - public WidgetPreferences() + @Inject + public WidgetPreferences(Context context) { - this.context = HabitsApplication.getContext(); prefs = PreferenceManager.getDefaultSharedPreferences(context); } diff --git a/app/src/main/res/layout/edit_habit.xml b/app/src/main/res/layout/edit_habit.xml index e744f58e5..cf76d7f43 100644 --- a/app/src/main/res/layout/edit_habit.xml +++ b/app/src/main/res/layout/edit_habit.xml @@ -22,7 +22,7 @@ style="@style/dialogForm" xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" - tools:context=".ui.habits.edit.BaseDialogFragment" + tools:context=".ui.habits.edit.BaseDialog" tools:ignore="MergeRootFrame"> + * + * This file is part of Loop Habit Tracker. + * + * Loop Habit Tracker is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by the + * Free Software Foundation, either version 3 of the License, or (at your + * option) any later version. + * + * Loop Habit Tracker is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +package org.isoron.uhabits.ui.habits.list; + + +import android.content.*; +import android.support.v7.app.*; + +import org.isoron.uhabits.*; +import org.isoron.uhabits.models.*; +import org.isoron.uhabits.ui.*; +import org.isoron.uhabits.ui.common.dialogs.*; +import org.isoron.uhabits.ui.common.dialogs.ColorPickerDialog.*; +import org.isoron.uhabits.ui.habits.edit.*; +import org.junit.*; +import org.junit.runner.*; +import org.junit.runners.*; + +import java.io.*; + +import static org.mockito.Mockito.*; + +@RunWith(JUnit4.class) +public class ListHabitsScreenTest extends BaseUnitTest +{ + private BaseActivity activity; + + private ListHabitsRootView rootView; + + private ListHabitsScreen screen; + + private ListHabitsController controller; + + private Habit habit; + + private Intent intent; + + @Before + @Override + public void setUp() + { + super.setUp(); + + activity = mock(BaseActivity.class); + rootView = mock(ListHabitsRootView.class); + controller = mock(ListHabitsController.class); + intent = mock(Intent.class); + + habit = new Habit(); + screen = new ListHabitsScreen(activity, rootView); + screen.setController(controller); + } + + @Test + public void testCreateHabitScreen() + { + CreateHabitDialog dialog = mock(CreateHabitDialog.class); + when(dialogFactory.buildCreateHabitDialog()).thenReturn(dialog); + + screen.showCreateHabitScreen(); + + verify(activity).showDialog(eq(dialog), any()); + } + + @Test + public void testOnResult_bugReport() + { + screen.onResult(0, HabitsApplication.RESULT_BUG_REPORT, null); + verify(controller).onSendBugReport(); + } + + @Test + public void testOnResult_exportCSV() + { + screen.onResult(0, HabitsApplication.RESULT_EXPORT_CSV, null); + verify(controller).onExportCSV(); + } + + @Test + public void testOnResult_exportDB() + { + screen.onResult(0, HabitsApplication.RESULT_EXPORT_DB, null); + verify(controller).onExportDB(); + } + + @Test + public void testOnResult_importData() + { + screen.onResult(0, HabitsApplication.RESULT_IMPORT_DATA, null); + testShowImportScreen(); + } + + @Test + public void testShowAboutScreen() throws Exception + { + when(intentFactory.startAboutActivity(activity)).thenReturn(intent); + screen.showAboutScreen(); + verify(activity).startActivity(eq(intent)); + } + + @Test + public void testShowColorPicker() + { + habit.setColor(999); + ColorPickerDialog picker = mock(ColorPickerDialog.class); + when(dialogFactory.buildColorPicker(999)).thenReturn(picker); + OnColorSelectedListener callback = mock(OnColorSelectedListener.class); + + screen.showColorPicker(habit, callback); + + verify(activity).showDialog(eq(picker), any()); + verify(picker).setListener(callback); + } + + @Test + public void testShowDeleteConfirmationScreen() + { + ConfirmDeleteDialog.Callback callback; + callback = mock(ConfirmDeleteDialog.Callback.class); + + ConfirmDeleteDialog dialog = mock(ConfirmDeleteDialog.class); + when(dialogFactory.buildConfirmDeleteDialog(activity, + callback)).thenReturn(dialog); + + screen.showDeleteConfirmationScreen(callback); + + verify(activity).showDialog(dialog); + } + + @Test + public void testShowEditHabitScreen() + { + EditHabitDialog dialog = mock(EditHabitDialog.class); + when(dialogFactory.buildEditHabitDialog(habit)).thenReturn(dialog); + + screen.showEditHabitScreen(habit); + verify(activity).showDialog(eq(dialog), any()); + } + + @Test + public void testShowFAQScreen() + { + when(intentFactory.viewFAQ(activity)).thenReturn(intent); + screen.showFAQScreen(); + verify(activity).startActivity(intent); + } + + @Test + public void testShowHabitScreen() + { + when(intentFactory.startShowHabitActivity(activity, habit)).thenReturn( + intent); + screen.showHabitScreen(habit); + verify(activity).startActivity(intent); + } + + @Test + public void testShowImportScreen() + { + File dir = mock(File.class); + when(dirFinder.findStorageDir(any())).thenReturn(dir); + + FilePickerDialog picker = mock(FilePickerDialog.class); + AppCompatDialog dialog = mock(AppCompatDialog.class); + when(picker.getDialog()).thenReturn(dialog); + when(dialogFactory.buildFilePicker(activity, dir)).thenReturn(picker); + + screen.showImportScreen(); + + verify(activity).showDialog(dialog); + } + + @Test + public void testShowImportScreen_withInvalidPath() + { + when(dirFinder.findStorageDir(any())).thenReturn(null); + screen.showImportScreen(); + verify(activity).showMessage(R.string.could_not_import); + } + + @Test + public void testShowIntroScreen() + { + when(intentFactory.startIntroActivity(activity)).thenReturn(intent); + screen.showIntroScreen(); + verify(activity).startActivity(intent); + } + + @Test + public void testShowSettingsScreen() + { + when(intentFactory.startSettingsActivity(activity)).thenReturn(intent); + screen.showSettingsScreen(); + verify(activity).startActivityForResult(eq(intent), anyInt()); + } +} \ No newline at end of file diff --git a/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java b/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java index 07cc76311..d6171c7b2 100644 --- a/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java +++ b/app/src/test/java/org/isoron/uhabits/utils/ReminderSchedulerTest.java @@ -22,7 +22,6 @@ package org.isoron.uhabits.utils; import android.app.*; import org.isoron.uhabits.*; -import org.isoron.uhabits.intents.*; import org.isoron.uhabits.models.*; import org.junit.*; @@ -31,26 +30,19 @@ import static org.mockito.Mockito.*; @SuppressWarnings("JavaDoc") public class ReminderSchedulerTest extends BaseUnitTest { - private IntentFactory intentFactory; - - private IntentScheduler intentScheduler; - - private ReminderScheduler scheduler; - private Habit habit; private PendingIntent intent; + private ReminderScheduler reminderScheduler; + @Before @Override public void setUp() { super.setUp(); - intentFactory = mock(IntentFactory.class); - intentScheduler = mock(IntentScheduler.class); intent = mock(PendingIntent.class); - - scheduler = new ReminderScheduler(intentFactory, intentScheduler); + reminderScheduler = new ReminderScheduler(); habit = fixtures.createEmptyHabit(); } @@ -94,7 +86,7 @@ public class ReminderSchedulerTest extends BaseUnitTest fixtures.createEmptyHabit(); - scheduler.schedule(habitList); + reminderScheduler.schedule(habitList); verify(intentScheduler).schedule(1422347400000L, null); verify(intentScheduler).schedule(1422297000000L, null); @@ -117,7 +109,7 @@ public class ReminderSchedulerTest extends BaseUnitTest @Test public void testSchedule_withoutReminder() { - scheduler.schedule(habit, null); + reminderScheduler.schedule(habit, null); verifyZeroInteractions(intentScheduler); } @@ -125,15 +117,15 @@ public class ReminderSchedulerTest extends BaseUnitTest long expectedCheckmarkTime, long expectedReminderTime) { - when(intentFactory.buildShowReminder(habit, expectedReminderTime, - expectedCheckmarkTime)).thenReturn(intent); + when(pendingIntentFactory.showReminder(habit, expectedReminderTime, + expectedCheckmarkTime)).thenReturn(intent); - scheduler.schedule(habit, atTime); + reminderScheduler.schedule(habit, atTime); verify(logger).logReminderScheduled(habit, expectedReminderTime); - verify(intentFactory).buildShowReminder(habit, expectedReminderTime, - expectedCheckmarkTime); + verify(pendingIntentFactory).showReminder(habit, expectedReminderTime, + expectedCheckmarkTime); verify(intentScheduler).schedule(expectedReminderTime, intent); } }