mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 09:08:52 -06:00
Refactor reminder scheduling, add tests
This commit is contained in:
@@ -24,12 +24,15 @@ import android.content.*;
|
|||||||
import android.os.*;
|
import android.os.*;
|
||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.support.test.*;
|
import android.support.test.*;
|
||||||
|
import android.support.test.runner.*;
|
||||||
|
import android.test.suitebuilder.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.commands.*;
|
import org.isoron.uhabits.commands.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.tasks.*;
|
import org.isoron.uhabits.tasks.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
import org.junit.*;
|
import org.junit.*;
|
||||||
|
import org.junit.runner.*;
|
||||||
|
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.concurrent.*;
|
import java.util.concurrent.*;
|
||||||
@@ -40,6 +43,8 @@ import static junit.framework.Assert.*;
|
|||||||
import static org.hamcrest.MatcherAssert.*;
|
import static org.hamcrest.MatcherAssert.*;
|
||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@MediumTest
|
||||||
public class BaseAndroidTest
|
public class BaseAndroidTest
|
||||||
{
|
{
|
||||||
// 8:00am, January 25th, 2015 (UTC)
|
// 8:00am, January 25th, 2015 (UTC)
|
||||||
@@ -60,6 +65,9 @@ public class BaseAndroidTest
|
|||||||
@Inject
|
@Inject
|
||||||
protected CommandRunner commandRunner;
|
protected CommandRunner commandRunner;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected HabitLogger logger;
|
||||||
|
|
||||||
protected AndroidTestComponent androidTestComponent;
|
protected AndroidTestComponent androidTestComponent;
|
||||||
|
|
||||||
protected HabitFixtures fixtures;
|
protected HabitFixtures fixtures;
|
||||||
|
|||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import android.os.*;
|
||||||
|
import android.support.test.runner.*;
|
||||||
|
import android.test.suitebuilder.annotation.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.ui.*;
|
||||||
|
import org.junit.*;
|
||||||
|
import org.junit.runner.*;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
import static org.hamcrest.MatcherAssert.*;
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
@MediumTest
|
||||||
|
public class HabitLoggerTest extends BaseAndroidTest
|
||||||
|
{
|
||||||
|
@Test
|
||||||
|
public void testLogReminderScheduled() throws IOException
|
||||||
|
{
|
||||||
|
if (!isLogcatAvailable()) return;
|
||||||
|
|
||||||
|
long time = 1422277200000L; // 13:00 jan 26, 2015 (UTC)
|
||||||
|
Habit habit = fixtures.createEmptyHabit();
|
||||||
|
habit.setName("Write journal");
|
||||||
|
|
||||||
|
logger.logReminderScheduled(habit, time);
|
||||||
|
|
||||||
|
String expectedMsg = "Setting alarm (2015-01-26 130000): Wri\n";
|
||||||
|
assertLogcatContains(expectedMsg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void assertLogcatContains(String expectedMsg) throws IOException
|
||||||
|
{
|
||||||
|
BaseSystem system = new BaseSystem(targetContext);
|
||||||
|
String logcat = system.getLogcat();
|
||||||
|
assertThat(logcat, containsString(expectedMsg));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected boolean isLogcatAvailable()
|
||||||
|
{
|
||||||
|
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -33,7 +33,7 @@ import static org.hamcrest.MatcherAssert.*;
|
|||||||
import static org.hamcrest.Matchers.*;
|
import static org.hamcrest.Matchers.*;
|
||||||
|
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
@SmallTest
|
@MediumTest
|
||||||
public class HabitsApplicationTest extends BaseAndroidTest
|
public class HabitsApplicationTest extends BaseAndroidTest
|
||||||
{
|
{
|
||||||
@Test
|
@Test
|
||||||
|
|||||||
@@ -19,7 +19,10 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits;
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import android.content.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.commands.*;
|
import org.isoron.uhabits.commands.*;
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.models.sqlite.*;
|
import org.isoron.uhabits.models.sqlite.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
@@ -70,4 +73,21 @@ public class AndroidModule
|
|||||||
{
|
{
|
||||||
return new WidgetPreferences();
|
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()
|
||||||
|
{
|
||||||
|
return new HabitLogger();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.isoron.uhabits.ui.habits.list.model.*;
|
|||||||
import org.isoron.uhabits.ui.habits.list.views.*;
|
import org.isoron.uhabits.ui.habits.list.views.*;
|
||||||
import org.isoron.uhabits.ui.habits.show.*;
|
import org.isoron.uhabits.ui.habits.show.*;
|
||||||
import org.isoron.uhabits.ui.widgets.*;
|
import org.isoron.uhabits.ui.widgets.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
import org.isoron.uhabits.widgets.*;
|
import org.isoron.uhabits.widgets.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -106,4 +107,6 @@ public interface BaseComponent
|
|||||||
void inject(ReceiverActions receiverActions);
|
void inject(ReceiverActions receiverActions);
|
||||||
|
|
||||||
void inject(ReminderReceiver reminderReceiver);
|
void inject(ReminderReceiver reminderReceiver);
|
||||||
|
|
||||||
|
void inject(ReminderScheduler reminderScheduler);
|
||||||
}
|
}
|
||||||
|
|||||||
45
app/src/main/java/org/isoron/uhabits/HabitLogger.java
Normal file
45
app/src/main/java/org/isoron/uhabits/HabitLogger.java
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits;
|
||||||
|
|
||||||
|
import android.support.annotation.*;
|
||||||
|
import android.util.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
|
import java.text.*;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public class HabitLogger
|
||||||
|
{
|
||||||
|
public void logReminderScheduled(@NonNull Habit habit,
|
||||||
|
@NonNull Long reminderTime)
|
||||||
|
{
|
||||||
|
int min = Math.min(3, habit.getName().length());
|
||||||
|
String name = habit.getName().substring(0, min);
|
||||||
|
|
||||||
|
DateFormat df = DateUtils.getBackupDateFormat();
|
||||||
|
String time = df.format(new Date(reminderTime));
|
||||||
|
|
||||||
|
Log.i("ReminderHelper",
|
||||||
|
String.format("Setting alarm (%s): %s", time, name));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -51,7 +51,7 @@ public class HabitsApplication extends Application
|
|||||||
@Nullable
|
@Nullable
|
||||||
private static Context context;
|
private static Context context;
|
||||||
|
|
||||||
private static WidgetUpdater widgetManager;
|
private static WidgetUpdater widgetUpdater;
|
||||||
|
|
||||||
public static BaseComponent getComponent()
|
public static BaseComponent getComponent()
|
||||||
{
|
{
|
||||||
@@ -63,9 +63,10 @@ public class HabitsApplication extends Application
|
|||||||
HabitsApplication.component = component;
|
HabitsApplication.component = component;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@NonNull
|
||||||
public static Context getContext()
|
public static Context getContext()
|
||||||
{
|
{
|
||||||
|
if (context == null) throw new RuntimeException("context is null");
|
||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,21 +77,23 @@ public class HabitsApplication extends Application
|
|||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
public static WidgetUpdater getWidgetManager()
|
public static WidgetUpdater getWidgetUpdater()
|
||||||
{
|
{
|
||||||
if (widgetManager == null)
|
if (widgetUpdater == null) throw new RuntimeException(
|
||||||
throw new RuntimeException("widgetManager is null");
|
"widgetUpdater is null");
|
||||||
|
|
||||||
return widgetManager;
|
return widgetUpdater;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isTestMode()
|
public static boolean isTestMode()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (context != null) context
|
if (context != null)
|
||||||
.getClassLoader()
|
{
|
||||||
.loadClass("org.isoron.uhabits.BaseAndroidTest");
|
String testClass = "org.isoron.uhabits.BaseAndroidTest";
|
||||||
|
context.getClassLoader().loadClass(testClass);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
catch (final Exception e)
|
catch (final Exception e)
|
||||||
@@ -114,8 +117,8 @@ public class HabitsApplication extends Application
|
|||||||
if (db.exists()) db.delete();
|
if (db.exists()) db.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
widgetManager = new WidgetUpdater(this);
|
widgetUpdater = new WidgetUpdater(this);
|
||||||
widgetManager.startListening();
|
widgetUpdater.startListening();
|
||||||
|
|
||||||
DatabaseUtils.initializeActiveAndroid();
|
DatabaseUtils.initializeActiveAndroid();
|
||||||
}
|
}
|
||||||
@@ -125,7 +128,7 @@ public class HabitsApplication extends Application
|
|||||||
{
|
{
|
||||||
HabitsApplication.context = null;
|
HabitsApplication.context = null;
|
||||||
ActiveAndroid.dispose();
|
ActiveAndroid.dispose();
|
||||||
widgetManager.stopListening();
|
widgetUpdater.stopListening();
|
||||||
super.onTerminate();
|
super.onTerminate();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.receivers;
|
package org.isoron.uhabits.intents;
|
||||||
|
|
||||||
import android.app.*;
|
import android.app.*;
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
@@ -25,21 +25,23 @@ import android.net.*;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
import org.isoron.uhabits.receivers.*;
|
||||||
import org.isoron.uhabits.ui.habits.show.*;
|
import org.isoron.uhabits.ui.habits.show.*;
|
||||||
|
|
||||||
public class PendingIntentFactory
|
import static android.app.PendingIntent.*;
|
||||||
{
|
|
||||||
public static final String BASE_URL = "content://org.isoron.uhabits/habit/";
|
|
||||||
|
|
||||||
|
public class IntentFactory
|
||||||
|
{
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Context context;
|
private final Context context;
|
||||||
|
|
||||||
public PendingIntentFactory(Context context)
|
public IntentFactory(@NonNull Context context)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PendingIntent buildAddCheckmark(Habit habit, Long timestamp)
|
public PendingIntent buildAddCheckmark(@NonNull Habit habit,
|
||||||
|
@Nullable Long timestamp)
|
||||||
{
|
{
|
||||||
Uri data = habit.getUri();
|
Uri data = habit.getUri();
|
||||||
Intent checkIntent = new Intent(context, WidgetReceiver.class);
|
Intent checkIntent = new Intent(context, WidgetReceiver.class);
|
||||||
@@ -47,7 +49,7 @@ public class PendingIntentFactory
|
|||||||
checkIntent.setAction(WidgetReceiver.ACTION_ADD_REPETITION);
|
checkIntent.setAction(WidgetReceiver.ACTION_ADD_REPETITION);
|
||||||
if (timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
if (timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
||||||
return PendingIntent.getBroadcast(context, 1, checkIntent,
|
return PendingIntent.getBroadcast(context, 1, checkIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PendingIntent buildDismissNotification()
|
public PendingIntent buildDismissNotification()
|
||||||
@@ -55,20 +57,37 @@ public class PendingIntentFactory
|
|||||||
Intent deleteIntent = new Intent(context, ReminderReceiver.class);
|
Intent deleteIntent = new Intent(context, ReminderReceiver.class);
|
||||||
deleteIntent.setAction(WidgetReceiver.ACTION_DISMISS_REMINDER);
|
deleteIntent.setAction(WidgetReceiver.ACTION_DISMISS_REMINDER);
|
||||||
return PendingIntent.getBroadcast(context, 0, deleteIntent,
|
return PendingIntent.getBroadcast(context, 0, deleteIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PendingIntent buildSnoozeNotification(Habit habit)
|
public PendingIntent buildShowReminder(@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 buildSnoozeNotification(@NonNull Habit habit)
|
||||||
{
|
{
|
||||||
Uri data = habit.getUri();
|
Uri data = habit.getUri();
|
||||||
Intent snoozeIntent = new Intent(context, ReminderReceiver.class);
|
Intent snoozeIntent = new Intent(context, ReminderReceiver.class);
|
||||||
snoozeIntent.setData(data);
|
snoozeIntent.setData(data);
|
||||||
snoozeIntent.setAction(ReminderReceiver.ACTION_SNOOZE_REMINDER);
|
snoozeIntent.setAction(ReminderReceiver.ACTION_SNOOZE_REMINDER);
|
||||||
return PendingIntent.getBroadcast(context, 0, snoozeIntent,
|
return PendingIntent.getBroadcast(context, 0, snoozeIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PendingIntent buildToggleCheckmark(Habit habit, Long timestamp)
|
public PendingIntent buildToggleCheckmark(@NonNull Habit habit,
|
||||||
|
@Nullable Long timestamp)
|
||||||
{
|
{
|
||||||
Uri data = habit.getUri();
|
Uri data = habit.getUri();
|
||||||
Intent checkIntent = new Intent(context, WidgetReceiver.class);
|
Intent checkIntent = new Intent(context, WidgetReceiver.class);
|
||||||
@@ -76,16 +95,18 @@ public class PendingIntentFactory
|
|||||||
checkIntent.setAction(WidgetReceiver.ACTION_TOGGLE_REPETITION);
|
checkIntent.setAction(WidgetReceiver.ACTION_TOGGLE_REPETITION);
|
||||||
if (timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
if (timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
||||||
return PendingIntent.getBroadcast(context, 2, checkIntent,
|
return PendingIntent.getBroadcast(context, 2, checkIntent,
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public PendingIntent buildViewHabit(Habit habit)
|
public PendingIntent buildViewHabit(Habit habit)
|
||||||
{
|
{
|
||||||
|
Uri uri = habit.getUri();
|
||||||
|
|
||||||
Intent intent = new Intent(context, ShowHabitActivity.class);
|
Intent intent = new Intent(context, ShowHabitActivity.class);
|
||||||
intent.setData(Uri.parse(BASE_URL + habit.getId()));
|
intent.setData(uri);
|
||||||
return android.support.v4.app.TaskStackBuilder
|
return android.support.v4.app.TaskStackBuilder
|
||||||
.create(context.getApplicationContext())
|
.create(context)
|
||||||
.addNextIntentWithParentStack(intent)
|
.addNextIntentWithParentStack(intent)
|
||||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
.getPendingIntent(0, FLAG_UPDATE_CURRENT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -17,7 +17,7 @@
|
|||||||
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
* with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
package org.isoron.uhabits.receivers;
|
package org.isoron.uhabits.intents;
|
||||||
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.net.*;
|
import android.net.*;
|
||||||
@@ -70,7 +70,7 @@ public class IntentParser
|
|||||||
return timestamp;
|
return timestamp;
|
||||||
}
|
}
|
||||||
|
|
||||||
class CheckmarkIntentData
|
public class CheckmarkIntentData
|
||||||
{
|
{
|
||||||
public Habit habit;
|
public Habit habit;
|
||||||
|
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.intents;
|
||||||
|
|
||||||
|
import android.app.*;
|
||||||
|
import android.content.*;
|
||||||
|
import android.os.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
|
import static android.app.AlarmManager.*;
|
||||||
|
import static android.content.Context.*;
|
||||||
|
|
||||||
|
public class IntentScheduler
|
||||||
|
{
|
||||||
|
private final AlarmManager manager;
|
||||||
|
|
||||||
|
public IntentScheduler(@NonNull Context context)
|
||||||
|
{
|
||||||
|
manager = (AlarmManager) context.getSystemService(ALARM_SERVICE);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void schedule(@NonNull Long timestamp, PendingIntent intent)
|
||||||
|
{
|
||||||
|
if (Build.VERSION.SDK_INT >= 23)
|
||||||
|
{
|
||||||
|
manager.setExactAndAllowWhileIdle(RTC_WAKEUP, timestamp, intent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Build.VERSION.SDK_INT >= 19)
|
||||||
|
{
|
||||||
|
manager.setExact(RTC_WAKEUP, timestamp, intent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
manager.set(RTC_WAKEUP, timestamp, intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -115,8 +115,7 @@ public class Habit
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clears the reminder for a habit. This sets all the related fields to
|
* Clears the reminder for a habit.
|
||||||
* null.
|
|
||||||
*/
|
*/
|
||||||
public void clearReminder()
|
public void clearReminder()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -38,8 +38,8 @@ public final class Reminder
|
|||||||
* Returns the days of the week the reminder should be shown.
|
* Returns the days of the week the reminder should be shown.
|
||||||
* <p>
|
* <p>
|
||||||
* This field can be converted to a list of booleans using the method
|
* This field can be converted to a list of booleans using the method
|
||||||
* DateHelper.unpackWeekdayList and converted back to an integer by using
|
* DateUtils.unpackWeekdayList and converted back to an integer by using the
|
||||||
* the method DateHelper.packWeekdayList.
|
* method DateUtils.packWeekdayList.
|
||||||
*/
|
*/
|
||||||
public int getDays()
|
public int getDays()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import android.support.v4.app.*;
|
|||||||
import android.util.*;
|
import android.util.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.tasks.*;
|
import org.isoron.uhabits.tasks.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
@@ -58,6 +59,9 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
@Inject
|
@Inject
|
||||||
HabitList habits;
|
HabitList habits;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ReminderScheduler reminderScheduler;
|
||||||
|
|
||||||
public ReminderReceiver()
|
public ReminderReceiver()
|
||||||
{
|
{
|
||||||
super();
|
super();
|
||||||
@@ -86,7 +90,7 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case Intent.ACTION_BOOT_COMPLETED:
|
case Intent.ACTION_BOOT_COMPLETED:
|
||||||
onActionBootCompleted(context);
|
onActionBootCompleted();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -96,25 +100,25 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onActionBootCompleted(Context context)
|
protected void onActionBootCompleted()
|
||||||
{
|
{
|
||||||
ReminderUtils.createReminderAlarms(context, habits);
|
reminderScheduler.schedule(habits);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void onActionShowReminder(Context context, Intent intent)
|
protected void onActionShowReminder(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
createNotification(context, intent);
|
createNotification(context, intent);
|
||||||
createReminderAlarmsDelayed(context);
|
createReminderAlarmsDelayed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createNotification(final Context context, final Intent intent)
|
private void createNotification(final Context context, final Intent intent)
|
||||||
{
|
{
|
||||||
final Uri data = intent.getData();
|
final Uri data = intent.getData();
|
||||||
final Habit habit = habits.getById(ContentUris.parseId(data));
|
final Habit habit = habits.getById(ContentUris.parseId(data));
|
||||||
final Long timestamp =
|
final Long timestamp = intent.getLongExtra("timestamp",
|
||||||
intent.getLongExtra("timestamp", DateUtils.getStartOfToday());
|
DateUtils.getStartOfToday());
|
||||||
final Long reminderTime =
|
final Long reminderTime = intent.getLongExtra("reminderTime",
|
||||||
intent.getLongExtra("reminderTime", DateUtils.getStartOfToday());
|
DateUtils.getStartOfToday());
|
||||||
|
|
||||||
if (habit == null) return;
|
if (habit == null) return;
|
||||||
|
|
||||||
@@ -137,12 +141,11 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
|
|
||||||
Intent contentIntent = new Intent(context, MainActivity.class);
|
Intent contentIntent = new Intent(context, MainActivity.class);
|
||||||
contentIntent.setData(data);
|
contentIntent.setData(data);
|
||||||
PendingIntent contentPendingIntent =
|
PendingIntent contentPendingIntent = PendingIntent.getActivity(
|
||||||
PendingIntent.getActivity(context, 0, contentIntent,
|
context, 0, contentIntent,
|
||||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||||
|
|
||||||
PendingIntentFactory intentFactory =
|
IntentFactory intentFactory = new IntentFactory(context);
|
||||||
new PendingIntentFactory(context);
|
|
||||||
|
|
||||||
PendingIntent dismissPendingIntent;
|
PendingIntent dismissPendingIntent;
|
||||||
dismissPendingIntent = intentFactory.buildDismissNotification();
|
dismissPendingIntent = intentFactory.buildDismissNotification();
|
||||||
@@ -151,15 +154,16 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
PendingIntent snoozeIntentPending =
|
PendingIntent snoozeIntentPending =
|
||||||
intentFactory.buildSnoozeNotification(habit);
|
intentFactory.buildSnoozeNotification(habit);
|
||||||
|
|
||||||
Uri ringtoneUri = ReminderUtils.getRingtoneUri(context);
|
Uri ringtoneUri = RingtoneUtils.getRingtoneUri(context);
|
||||||
|
|
||||||
NotificationCompat.WearableExtender wearableExtender =
|
NotificationCompat.WearableExtender wearableExtender =
|
||||||
new NotificationCompat.WearableExtender().setBackground(
|
new NotificationCompat.WearableExtender().setBackground(
|
||||||
BitmapFactory.decodeResource(context.getResources(),
|
BitmapFactory.decodeResource(
|
||||||
|
context.getResources(),
|
||||||
R.drawable.stripe));
|
R.drawable.stripe));
|
||||||
|
|
||||||
Notification notification =
|
Notification notification = new NotificationCompat.Builder(
|
||||||
new NotificationCompat.Builder(context)
|
context)
|
||||||
.setSmallIcon(R.drawable.ic_notification)
|
.setSmallIcon(R.drawable.ic_notification)
|
||||||
.setContentTitle(habit.getName())
|
.setContentTitle(habit.getName())
|
||||||
.setContentText(habit.getDescription())
|
.setContentText(habit.getDescription())
|
||||||
@@ -191,10 +195,11 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
}.execute();
|
}.execute();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void createReminderAlarmsDelayed(final Context context)
|
private void createReminderAlarmsDelayed()
|
||||||
{
|
{
|
||||||
new Handler().postDelayed(
|
new Handler().postDelayed(() -> {
|
||||||
() -> ReminderUtils.createReminderAlarms(context, habits), 5000);
|
reminderScheduler.schedule(habits);
|
||||||
|
}, 5000);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void dismissNotification(Context context, Long habitId)
|
private void dismissNotification(Context context, Long habitId)
|
||||||
@@ -210,15 +215,20 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
private void onActionSnoozeReminder(Context context, Intent intent)
|
private void onActionSnoozeReminder(Context context, Intent intent)
|
||||||
{
|
{
|
||||||
Uri data = intent.getData();
|
Uri data = intent.getData();
|
||||||
SharedPreferences prefs =
|
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
|
||||||
PreferenceManager.getDefaultSharedPreferences(context);
|
context);
|
||||||
long delayMinutes =
|
long delayMinutes = Long.parseLong(
|
||||||
Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
prefs.getString("pref_snooze_interval", "15"));
|
||||||
|
|
||||||
long habitId = ContentUris.parseId(data);
|
long habitId = ContentUris.parseId(data);
|
||||||
Habit habit = habits.getById(habitId);
|
Habit habit = habits.getById(habitId);
|
||||||
if (habit != null) ReminderUtils.createReminderAlarm(context, habit,
|
|
||||||
new Date().getTime() + delayMinutes * 60 * 1000);
|
if (habit != null)
|
||||||
|
{
|
||||||
|
long reminderTime = new Date().getTime() + delayMinutes * 60 * 1000;
|
||||||
|
reminderScheduler.schedule(habit, reminderTime);
|
||||||
|
}
|
||||||
|
|
||||||
dismissNotification(context, habitId);
|
dismissNotification(context, habitId);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -227,11 +237,11 @@ public class ReminderReceiver extends BroadcastReceiver
|
|||||||
if (!habit.hasReminder()) return false;
|
if (!habit.hasReminder()) return false;
|
||||||
Reminder reminder = habit.getReminder();
|
Reminder reminder = habit.getReminder();
|
||||||
|
|
||||||
Long timestamp =
|
Long timestamp = intent.getLongExtra("timestamp",
|
||||||
intent.getLongExtra("timestamp", DateUtils.getStartOfToday());
|
DateUtils.getStartOfToday());
|
||||||
|
|
||||||
boolean reminderDays[] =
|
boolean reminderDays[] = DateUtils.unpackWeekdayList(
|
||||||
DateUtils.unpackWeekdayList(reminder.getDays());
|
reminder.getDays());
|
||||||
int weekday = DateUtils.getWeekday(timestamp);
|
int weekday = DateUtils.getWeekday(timestamp);
|
||||||
|
|
||||||
return reminderDays[weekday];
|
return reminderDays[weekday];
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import android.support.annotation.*;
|
|||||||
import android.util.*;
|
import android.util.*;
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
|
|
||||||
import javax.inject.*;
|
import javax.inject.*;
|
||||||
|
|||||||
@@ -50,6 +50,9 @@ public class BaseSystem
|
|||||||
@Inject
|
@Inject
|
||||||
HabitList habitList;
|
HabitList habitList;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
ReminderScheduler reminderScheduler;
|
||||||
|
|
||||||
public BaseSystem(Context context)
|
public BaseSystem(Context context)
|
||||||
{
|
{
|
||||||
this.context = context;
|
this.context = context;
|
||||||
@@ -69,16 +72,16 @@ public class BaseSystem
|
|||||||
@NonNull
|
@NonNull
|
||||||
public File dumpBugReportToFile() throws IOException
|
public File dumpBugReportToFile() throws IOException
|
||||||
{
|
{
|
||||||
String date =
|
String date = DateUtils.getBackupDateFormat().format(
|
||||||
DateUtils.getBackupDateFormat().format(DateUtils.getLocalTime());
|
DateUtils.getLocalTime());
|
||||||
|
|
||||||
if (context == null) throw new RuntimeException(
|
if (context == null) throw new RuntimeException(
|
||||||
"application context should not be null");
|
"application context should not be null");
|
||||||
File dir = FileUtils.getFilesDir("Logs");
|
File dir = FileUtils.getFilesDir("Logs");
|
||||||
if (dir == null) throw new IOException("log dir should not be null");
|
if (dir == null) throw new IOException("log dir should not be null");
|
||||||
|
|
||||||
File logFile =
|
File logFile = new File(
|
||||||
new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||||
FileWriter output = new FileWriter(logFile);
|
FileWriter output = new FileWriter(logFile);
|
||||||
output.write(getBugReport());
|
output.write(getBugReport());
|
||||||
output.close();
|
output.close();
|
||||||
@@ -102,54 +105,12 @@ public class BaseSystem
|
|||||||
return deviceInfo + "\n" + logcat;
|
return deviceInfo + "\n" + logcat;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Recreates all application reminders.
|
|
||||||
*/
|
|
||||||
public void scheduleReminders()
|
|
||||||
{
|
|
||||||
new BaseTask()
|
|
||||||
{
|
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void doInBackground()
|
|
||||||
{
|
|
||||||
ReminderUtils.createReminderAlarms(context, habitList);
|
|
||||||
}
|
|
||||||
}.execute();
|
|
||||||
}
|
|
||||||
|
|
||||||
private String getDeviceInfo()
|
|
||||||
{
|
|
||||||
if (context == null) return "null context\n";
|
|
||||||
|
|
||||||
WindowManager wm =
|
|
||||||
(WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
|
||||||
|
|
||||||
return
|
|
||||||
String.format("App Version Name: %s\n", BuildConfig.VERSION_NAME) +
|
|
||||||
String.format("App Version Code: %s\n", BuildConfig.VERSION_CODE) +
|
|
||||||
String.format("OS Version: %s (%s)\n",
|
|
||||||
System.getProperty("os.version"), Build.VERSION.INCREMENTAL) +
|
|
||||||
String.format("OS API Level: %s\n", Build.VERSION.SDK) +
|
|
||||||
String.format("Device: %s\n", Build.DEVICE) +
|
|
||||||
String.format("Model (Product): %s (%s)\n", Build.MODEL,
|
|
||||||
Build.PRODUCT) +
|
|
||||||
String.format("Manufacturer: %s\n", Build.MANUFACTURER) +
|
|
||||||
String.format("Other tags: %s\n", Build.TAGS) +
|
|
||||||
String.format("Screen Width: %s\n",
|
|
||||||
wm.getDefaultDisplay().getWidth()) +
|
|
||||||
String.format("Screen Height: %s\n",
|
|
||||||
wm.getDefaultDisplay().getHeight()) +
|
|
||||||
String.format("External storage state: %s\n\n",
|
|
||||||
Environment.getExternalStorageState());
|
|
||||||
}
|
|
||||||
|
|
||||||
public String getLogcat() throws IOException
|
public String getLogcat() throws IOException
|
||||||
{
|
{
|
||||||
int maxLineCount = 250;
|
int maxLineCount = 250;
|
||||||
StringBuilder builder = new StringBuilder();
|
StringBuilder builder = new StringBuilder();
|
||||||
|
|
||||||
String[] command = new String[]{"logcat", "-d"};
|
String[] command = new String[]{ "logcat", "-d" };
|
||||||
Process process = Runtime.getRuntime().exec(command);
|
Process process = Runtime.getRuntime().exec(command);
|
||||||
|
|
||||||
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||||
@@ -172,4 +133,40 @@ public class BaseSystem
|
|||||||
|
|
||||||
return builder.toString();
|
return builder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Recreates all application reminders.
|
||||||
|
*/
|
||||||
|
public void scheduleReminders()
|
||||||
|
{
|
||||||
|
new SimpleTask(() -> reminderScheduler.schedule(habitList)).execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
private String getDeviceInfo()
|
||||||
|
{
|
||||||
|
if (context == null) return "null context\n";
|
||||||
|
|
||||||
|
WindowManager wm = (WindowManager) context.getSystemService(
|
||||||
|
Context.WINDOW_SERVICE);
|
||||||
|
|
||||||
|
return String.format("App Version Name: %s\n",
|
||||||
|
BuildConfig.VERSION_NAME) +
|
||||||
|
String.format("App Version Code: %s\n",
|
||||||
|
BuildConfig.VERSION_CODE) +
|
||||||
|
String.format("OS Version: %s (%s)\n",
|
||||||
|
System.getProperty("os.version"),
|
||||||
|
Build.VERSION.INCREMENTAL) +
|
||||||
|
String.format("OS API Level: %s\n", Build.VERSION.SDK) +
|
||||||
|
String.format("Device: %s\n", Build.DEVICE) +
|
||||||
|
String.format("Model (Product): %s (%s)\n", Build.MODEL,
|
||||||
|
Build.PRODUCT) +
|
||||||
|
String.format("Manufacturer: %s\n", Build.MANUFACTURER) +
|
||||||
|
String.format("Other tags: %s\n", Build.TAGS) +
|
||||||
|
String.format("Screen Width: %s\n",
|
||||||
|
wm.getDefaultDisplay().getWidth()) +
|
||||||
|
String.format("Screen Height: %s\n",
|
||||||
|
wm.getDefaultDisplay().getHeight()) +
|
||||||
|
String.format("External storage state: %s\n\n",
|
||||||
|
Environment.getExternalStorageState());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -176,7 +176,7 @@ public class ListHabitsController
|
|||||||
|
|
||||||
new Handler().postDelayed(() -> {
|
new Handler().postDelayed(() -> {
|
||||||
system.scheduleReminders();
|
system.scheduleReminders();
|
||||||
HabitsApplication.getWidgetManager().updateWidgets();
|
HabitsApplication.getWidgetUpdater().updateWidgets();
|
||||||
}, 1000);
|
}, 1000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ public class ScoreCard extends HabitCard
|
|||||||
public void onItemSelected(int position)
|
public void onItemSelected(int position)
|
||||||
{
|
{
|
||||||
setBucketSizeFromPosition(position);
|
setBucketSizeFromPosition(position);
|
||||||
HabitsApplication.getWidgetManager().updateWidgets();
|
HabitsApplication.getWidgetUpdater().updateWidgets();
|
||||||
refreshData();
|
refreshData();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,38 +19,46 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.ui.settings;
|
package org.isoron.uhabits.ui.settings;
|
||||||
|
|
||||||
import android.app.backup.BackupManager;
|
import android.app.backup.*;
|
||||||
import android.content.Intent;
|
import android.content.*;
|
||||||
import android.content.SharedPreferences;
|
import android.os.*;
|
||||||
import android.os.Bundle;
|
import android.support.v7.preference.*;
|
||||||
import android.support.v7.preference.Preference;
|
|
||||||
import android.support.v7.preference.PreferenceCategory;
|
|
||||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.HabitsApplication;
|
|
||||||
import org.isoron.uhabits.R;
|
import org.isoron.uhabits.R;
|
||||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
import org.isoron.uhabits.utils.*;
|
||||||
import org.isoron.uhabits.utils.ReminderUtils;
|
|
||||||
|
import static org.isoron.uhabits.HabitsApplication.*;
|
||||||
|
|
||||||
public class SettingsFragment extends PreferenceFragmentCompat
|
public class SettingsFragment extends PreferenceFragmentCompat
|
||||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||||
{
|
{
|
||||||
private static int RINGTONE_REQUEST_CODE = 1;
|
private static int RINGTONE_REQUEST_CODE = 1;
|
||||||
|
|
||||||
|
private SharedPreferences prefs;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
||||||
|
{
|
||||||
|
if (requestCode == RINGTONE_REQUEST_CODE)
|
||||||
|
{
|
||||||
|
RingtoneUtils.parseRingtoneData(getContext(), data);
|
||||||
|
updateRingtoneDescription();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle savedInstanceState)
|
public void onCreate(Bundle savedInstanceState)
|
||||||
{
|
{
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
addPreferencesFromResource(R.xml.preferences);
|
addPreferencesFromResource(R.xml.preferences);
|
||||||
|
|
||||||
setResultOnPreferenceClick("importData",
|
setResultOnPreferenceClick("importData", RESULT_IMPORT_DATA);
|
||||||
HabitsApplication.RESULT_IMPORT_DATA);
|
setResultOnPreferenceClick("exportCSV", RESULT_EXPORT_CSV);
|
||||||
setResultOnPreferenceClick("exportCSV",
|
setResultOnPreferenceClick("exportDB", RESULT_EXPORT_DB);
|
||||||
HabitsApplication.RESULT_EXPORT_CSV);
|
setResultOnPreferenceClick("bugReport", RESULT_BUG_REPORT);
|
||||||
setResultOnPreferenceClick("exportDB",
|
|
||||||
HabitsApplication.RESULT_EXPORT_DB);
|
|
||||||
setResultOnPreferenceClick("bugReport",
|
|
||||||
HabitsApplication.RESULT_BUG_REPORT);
|
|
||||||
|
|
||||||
updateRingtoneDescription();
|
updateRingtoneDescription();
|
||||||
|
|
||||||
@@ -61,7 +69,45 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
@Override
|
@Override
|
||||||
public void onCreatePreferences(Bundle bundle, String s)
|
public void onCreatePreferences(Bundle bundle, String s)
|
||||||
{
|
{
|
||||||
|
// NOP
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause()
|
||||||
|
{
|
||||||
|
prefs.unregisterOnSharedPreferenceChangeListener(this);
|
||||||
|
super.onPause();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onPreferenceTreeClick(Preference preference)
|
||||||
|
{
|
||||||
|
String key = preference.getKey();
|
||||||
|
if (key == null) return false;
|
||||||
|
|
||||||
|
if (key.equals("reminderSound"))
|
||||||
|
{
|
||||||
|
RingtoneUtils.startRingtonePickerActivity(this,
|
||||||
|
RINGTONE_REQUEST_CODE);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.onPreferenceTreeClick(preference);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onResume()
|
||||||
|
{
|
||||||
|
super.onResume();
|
||||||
|
prefs = getPreferenceManager().getSharedPreferences();
|
||||||
|
prefs.registerOnSharedPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
||||||
|
String key)
|
||||||
|
{
|
||||||
|
BackupManager.dataChanged("org.isoron.uhabits");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removePreference(String preferenceKey, String categoryKey)
|
private void removePreference(String preferenceKey, String categoryKey)
|
||||||
@@ -82,61 +128,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onResume()
|
|
||||||
{
|
|
||||||
super.onResume();
|
|
||||||
getPreferenceManager().getSharedPreferences().
|
|
||||||
registerOnSharedPreferenceChangeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onPause()
|
|
||||||
{
|
|
||||||
getPreferenceManager().getSharedPreferences().
|
|
||||||
unregisterOnSharedPreferenceChangeListener(this);
|
|
||||||
super.onPause();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
|
|
||||||
String key)
|
|
||||||
{
|
|
||||||
BackupManager.dataChanged("org.isoron.uhabits");
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onPreferenceTreeClick(Preference preference)
|
|
||||||
{
|
|
||||||
if (preference.getKey() == null) return false;
|
|
||||||
|
|
||||||
if (preference.getKey().equals("reminderSound"))
|
|
||||||
{
|
|
||||||
ReminderUtils.startRingtonePickerActivity(this,
|
|
||||||
RINGTONE_REQUEST_CODE);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.onPreferenceTreeClick(preference);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onActivityResult(int requestCode, int resultCode, Intent data)
|
|
||||||
{
|
|
||||||
if (requestCode == RINGTONE_REQUEST_CODE)
|
|
||||||
{
|
|
||||||
ReminderUtils.parseRingtoneData(getContext(), data);
|
|
||||||
updateRingtoneDescription();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void updateRingtoneDescription()
|
private void updateRingtoneDescription()
|
||||||
{
|
{
|
||||||
String ringtoneName = ReminderUtils.getRingtoneName(getContext());
|
String ringtoneName = RingtoneUtils.getRingtoneName(getContext());
|
||||||
if(ringtoneName == null) return;
|
if (ringtoneName == null) return;
|
||||||
Preference ringtonePreference = findPreference("reminderSound");
|
Preference ringtonePreference = findPreference("reminderSound");
|
||||||
ringtonePreference.setSummary(ringtoneName);
|
ringtonePreference.setSummary(ringtoneName);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import android.content.*;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.receivers.*;
|
|
||||||
import org.isoron.uhabits.ui.widgets.views.*;
|
import org.isoron.uhabits.ui.widgets.views.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
|
|
||||||
@@ -45,7 +45,7 @@ public class CheckmarkWidget extends BaseWidget
|
|||||||
@Override
|
@Override
|
||||||
public PendingIntent getOnClickPendingIntent(Context context)
|
public PendingIntent getOnClickPendingIntent(Context context)
|
||||||
{
|
{
|
||||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
IntentFactory factory = new IntentFactory(context);
|
||||||
return factory.buildToggleCheckmark(habit, null);
|
return factory.buildToggleCheckmark(habit, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import android.content.*;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.receivers.*;
|
|
||||||
import org.isoron.uhabits.ui.common.views.*;
|
import org.isoron.uhabits.ui.common.views.*;
|
||||||
import org.isoron.uhabits.ui.widgets.views.*;
|
import org.isoron.uhabits.ui.widgets.views.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
@@ -46,7 +46,7 @@ public class FrequencyWidget extends BaseWidget
|
|||||||
@Override
|
@Override
|
||||||
public PendingIntent getOnClickPendingIntent(Context context)
|
public PendingIntent getOnClickPendingIntent(Context context)
|
||||||
{
|
{
|
||||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
IntentFactory factory = new IntentFactory(context);
|
||||||
return factory.buildViewHabit(habit);
|
return factory.buildViewHabit(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ public class HabitPickerDialog extends Activity
|
|||||||
{
|
{
|
||||||
Long habitId = habitIds.get(position);
|
Long habitId = habitIds.get(position);
|
||||||
preferences.addWidget(widgetId, habitId);
|
preferences.addWidget(widgetId, habitId);
|
||||||
HabitsApplication.getWidgetManager().updateWidgets();
|
HabitsApplication.getWidgetUpdater().updateWidgets();
|
||||||
|
|
||||||
Intent resultValue = new Intent();
|
Intent resultValue = new Intent();
|
||||||
resultValue.putExtra(EXTRA_APPWIDGET_ID, widgetId);
|
resultValue.putExtra(EXTRA_APPWIDGET_ID, widgetId);
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import android.content.*;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.receivers.*;
|
|
||||||
import org.isoron.uhabits.ui.common.views.*;
|
import org.isoron.uhabits.ui.common.views.*;
|
||||||
import org.isoron.uhabits.ui.widgets.views.*;
|
import org.isoron.uhabits.ui.widgets.views.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
@@ -44,7 +44,7 @@ public class HistoryWidget extends BaseWidget
|
|||||||
@Override
|
@Override
|
||||||
public PendingIntent getOnClickPendingIntent(Context context)
|
public PendingIntent getOnClickPendingIntent(Context context)
|
||||||
{
|
{
|
||||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
IntentFactory factory = new IntentFactory(context);
|
||||||
return factory.buildViewHabit(habit);
|
return factory.buildViewHabit(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -24,8 +24,8 @@ import android.content.*;
|
|||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.view.*;
|
import android.view.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.receivers.*;
|
|
||||||
import org.isoron.uhabits.ui.common.views.*;
|
import org.isoron.uhabits.ui.common.views.*;
|
||||||
import org.isoron.uhabits.ui.habits.show.views.*;
|
import org.isoron.uhabits.ui.habits.show.views.*;
|
||||||
import org.isoron.uhabits.ui.widgets.views.*;
|
import org.isoron.uhabits.ui.widgets.views.*;
|
||||||
@@ -47,7 +47,7 @@ public class ScoreWidget extends BaseWidget
|
|||||||
@Override
|
@Override
|
||||||
public PendingIntent getOnClickPendingIntent(Context context)
|
public PendingIntent getOnClickPendingIntent(Context context)
|
||||||
{
|
{
|
||||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
IntentFactory factory = new IntentFactory(context);
|
||||||
return factory.buildViewHabit(habit);
|
return factory.buildViewHabit(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -25,8 +25,8 @@ import android.support.annotation.*;
|
|||||||
import android.view.*;
|
import android.view.*;
|
||||||
import android.view.ViewGroup.*;
|
import android.view.ViewGroup.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
import org.isoron.uhabits.models.*;
|
import org.isoron.uhabits.models.*;
|
||||||
import org.isoron.uhabits.receivers.*;
|
|
||||||
import org.isoron.uhabits.ui.common.views.*;
|
import org.isoron.uhabits.ui.common.views.*;
|
||||||
import org.isoron.uhabits.ui.widgets.views.*;
|
import org.isoron.uhabits.ui.widgets.views.*;
|
||||||
import org.isoron.uhabits.utils.*;
|
import org.isoron.uhabits.utils.*;
|
||||||
@@ -49,7 +49,7 @@ public class StreakWidget extends BaseWidget
|
|||||||
@Override
|
@Override
|
||||||
public PendingIntent getOnClickPendingIntent(Context context)
|
public PendingIntent getOnClickPendingIntent(Context context)
|
||||||
{
|
{
|
||||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
IntentFactory factory = new IntentFactory(context);
|
||||||
return factory.buildViewHabit(habit);
|
return factory.buildViewHabit(habit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,86 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package org.isoron.uhabits.utils;
|
||||||
|
|
||||||
|
import android.app.*;
|
||||||
|
import android.support.annotation.*;
|
||||||
|
|
||||||
|
import org.isoron.uhabits.*;
|
||||||
|
import org.isoron.uhabits.intents.*;
|
||||||
|
import org.isoron.uhabits.models.*;
|
||||||
|
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
import javax.inject.*;
|
||||||
|
|
||||||
|
import static org.isoron.uhabits.utils.DateUtils.*;
|
||||||
|
|
||||||
|
public class ReminderScheduler
|
||||||
|
{
|
||||||
|
private final IntentFactory intentFactory;
|
||||||
|
|
||||||
|
private final IntentScheduler intentScheduler;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
HabitLogger logger;
|
||||||
|
|
||||||
|
public ReminderScheduler(IntentFactory intentFactory,
|
||||||
|
IntentScheduler intentScheduler)
|
||||||
|
{
|
||||||
|
this.intentFactory = intentFactory;
|
||||||
|
this.intentScheduler = intentScheduler;
|
||||||
|
HabitsApplication.getComponent().inject(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void schedule(@NonNull Habit habit, @Nullable Long reminderTime)
|
||||||
|
{
|
||||||
|
if (!habit.hasReminder()) return;
|
||||||
|
Reminder reminder = habit.getReminder();
|
||||||
|
if (reminderTime == null) reminderTime = getReminderTime(reminder);
|
||||||
|
long timestamp = getStartOfDay(toLocalTime(reminderTime));
|
||||||
|
|
||||||
|
PendingIntent intent = intentFactory.buildShowReminder(habit,
|
||||||
|
reminderTime, timestamp);
|
||||||
|
intentScheduler.schedule(reminderTime, intent);
|
||||||
|
logger.logReminderScheduled(habit, reminderTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void schedule(@NonNull HabitList habits)
|
||||||
|
{
|
||||||
|
HabitList reminderHabits = habits.getFiltered(HabitMatcher.WITH_ALARM);
|
||||||
|
for (Habit habit : reminderHabits)
|
||||||
|
schedule(habit, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private Long getReminderTime(@NonNull Reminder reminder)
|
||||||
|
{
|
||||||
|
Calendar calendar = DateUtils.getStartOfTodayCalendar();
|
||||||
|
calendar.set(Calendar.HOUR_OF_DAY, reminder.getHour());
|
||||||
|
calendar.set(Calendar.MINUTE, reminder.getMinute());
|
||||||
|
calendar.set(Calendar.SECOND, 0);
|
||||||
|
Long time = calendar.getTimeInMillis();
|
||||||
|
|
||||||
|
if (DateUtils.getLocalTime() > time)
|
||||||
|
time += AlarmManager.INTERVAL_DAY;
|
||||||
|
|
||||||
|
return time;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,87 +19,18 @@
|
|||||||
|
|
||||||
package org.isoron.uhabits.utils;
|
package org.isoron.uhabits.utils;
|
||||||
|
|
||||||
import android.app.*;
|
|
||||||
import android.content.*;
|
import android.content.*;
|
||||||
import android.media.*;
|
import android.media.*;
|
||||||
import android.net.*;
|
import android.net.*;
|
||||||
import android.os.*;
|
|
||||||
import android.preference.*;
|
import android.preference.*;
|
||||||
import android.provider.*;
|
import android.provider.*;
|
||||||
import android.support.annotation.*;
|
import android.support.annotation.*;
|
||||||
import android.support.v4.app.Fragment;
|
import android.support.v4.app.*;
|
||||||
import android.util.*;
|
|
||||||
|
|
||||||
import org.isoron.uhabits.*;
|
import org.isoron.uhabits.*;
|
||||||
import org.isoron.uhabits.models.*;
|
|
||||||
import org.isoron.uhabits.receivers.*;
|
|
||||||
|
|
||||||
import java.text.*;
|
public abstract class RingtoneUtils
|
||||||
import java.util.*;
|
|
||||||
|
|
||||||
public abstract class ReminderUtils
|
|
||||||
{
|
{
|
||||||
public static void createReminderAlarm(Context context,
|
|
||||||
Habit habit,
|
|
||||||
@Nullable Long reminderTime)
|
|
||||||
{
|
|
||||||
if (!habit.hasReminder()) return;
|
|
||||||
Reminder reminder = habit.getReminder();
|
|
||||||
|
|
||||||
if (reminderTime == null)
|
|
||||||
{
|
|
||||||
Calendar calendar = Calendar.getInstance();
|
|
||||||
calendar.setTimeInMillis(System.currentTimeMillis());
|
|
||||||
calendar.set(Calendar.HOUR_OF_DAY, reminder.getHour());
|
|
||||||
calendar.set(Calendar.MINUTE, reminder.getMinute());
|
|
||||||
calendar.set(Calendar.SECOND, 0);
|
|
||||||
|
|
||||||
reminderTime = calendar.getTimeInMillis();
|
|
||||||
|
|
||||||
if (System.currentTimeMillis() > reminderTime)
|
|
||||||
reminderTime += AlarmManager.INTERVAL_DAY;
|
|
||||||
}
|
|
||||||
|
|
||||||
long timestamp =
|
|
||||||
DateUtils.getStartOfDay(DateUtils.toLocalTime(reminderTime));
|
|
||||||
|
|
||||||
Uri uri = habit.getUri();
|
|
||||||
|
|
||||||
Intent alarmIntent = new Intent(context, ReminderReceiver.class);
|
|
||||||
alarmIntent.setAction(ReminderReceiver.ACTION_SHOW_REMINDER);
|
|
||||||
alarmIntent.setData(uri);
|
|
||||||
alarmIntent.putExtra("timestamp", timestamp);
|
|
||||||
alarmIntent.putExtra("reminderTime", reminderTime);
|
|
||||||
|
|
||||||
PendingIntent pendingIntent = PendingIntent.getBroadcast(context,
|
|
||||||
((int) (habit.getId() % Integer.MAX_VALUE)) + 1, alarmIntent,
|
|
||||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
|
||||||
|
|
||||||
AlarmManager manager =
|
|
||||||
(AlarmManager) context.getSystemService(Context.ALARM_SERVICE);
|
|
||||||
|
|
||||||
if (Build.VERSION.SDK_INT >= 23)
|
|
||||||
manager.setExactAndAllowWhileIdle(AlarmManager.RTC_WAKEUP,
|
|
||||||
reminderTime, pendingIntent);
|
|
||||||
else if (Build.VERSION.SDK_INT >= 19)
|
|
||||||
manager.setExact(AlarmManager.RTC_WAKEUP, reminderTime,
|
|
||||||
pendingIntent);
|
|
||||||
else manager.set(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
|
|
||||||
|
|
||||||
String name =
|
|
||||||
habit.getName().substring(0, Math.min(3, habit.getName().length()));
|
|
||||||
Log.d("ReminderHelper", String.format("Setting alarm (%s): %s",
|
|
||||||
DateFormat.getDateTimeInstance().format(new Date(reminderTime)),
|
|
||||||
name));
|
|
||||||
}
|
|
||||||
|
|
||||||
public static void createReminderAlarms(Context context, HabitList habits)
|
|
||||||
{
|
|
||||||
HabitList reminderHabits = habits.getFiltered(HabitMatcher.WITH_ALARM);
|
|
||||||
for (Habit habit : reminderHabits)
|
|
||||||
createReminderAlarm(context, habit, null);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String getRingtoneName(Context context)
|
public static String getRingtoneName(Context context)
|
||||||
{
|
{
|
||||||
@@ -174,7 +105,7 @@ public abstract class ReminderUtils
|
|||||||
int requestCode)
|
int requestCode)
|
||||||
{
|
{
|
||||||
Uri existingRingtoneUri =
|
Uri existingRingtoneUri =
|
||||||
ReminderUtils.getRingtoneUri(fragment.getContext());
|
getRingtoneUri(fragment.getContext());
|
||||||
Uri defaultRingtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI;
|
Uri defaultRingtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI;
|
||||||
|
|
||||||
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
|
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
|
||||||
@@ -36,11 +36,14 @@ public class BaseUnitTest
|
|||||||
@Inject
|
@Inject
|
||||||
protected ModelFactory modelFactory;
|
protected ModelFactory modelFactory;
|
||||||
|
|
||||||
protected TestComponent testComponent;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected HabitList habitList;
|
protected HabitList habitList;
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected HabitLogger logger;
|
||||||
|
|
||||||
|
protected TestComponent testComponent;
|
||||||
|
|
||||||
protected HabitFixtures fixtures;
|
protected HabitFixtures fixtures;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
|
|||||||
@@ -73,4 +73,18 @@ public class TestModule
|
|||||||
{
|
{
|
||||||
return mock(WidgetPreferences.class);
|
return mock(WidgetPreferences.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
ReminderScheduler provideReminderScheduler()
|
||||||
|
{
|
||||||
|
return mock(ReminderScheduler.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
HabitLogger provideLogger()
|
||||||
|
{
|
||||||
|
return mock(HabitLogger.class);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,139 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2016 Álinson Santos Xavier <isoron@gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
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.*;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@Before
|
||||||
|
@Override
|
||||||
|
public void setUp()
|
||||||
|
{
|
||||||
|
super.setUp();
|
||||||
|
intentFactory = mock(IntentFactory.class);
|
||||||
|
intentScheduler = mock(IntentScheduler.class);
|
||||||
|
intent = mock(PendingIntent.class);
|
||||||
|
|
||||||
|
scheduler = new ReminderScheduler(intentFactory, intentScheduler);
|
||||||
|
habit = fixtures.createEmptyHabit();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedule_atSpecificTime()
|
||||||
|
{
|
||||||
|
long atTime = 1422617400000L; // 11:30 jan 30, 2015 (UTC)
|
||||||
|
long expectedCheckmarkTime = 1422576000000L; // 00:00 jan 27, 2015 (UTC)
|
||||||
|
|
||||||
|
habit.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS));
|
||||||
|
scheduleAndVerify(atTime, expectedCheckmarkTime, atTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedule_laterToday()
|
||||||
|
{
|
||||||
|
long now = 1422253800000L; // 06:30 jan 26, 2015 (UTC)
|
||||||
|
DateUtils.setFixedLocalTime(now);
|
||||||
|
|
||||||
|
long expectedCheckmarkTime = 1422230400000L; // 00:00 jan 26, 2015 (UTC)
|
||||||
|
long expectedReminderTime = 1422261000000L; // 08:30 jan 26, 2015 (UTC)
|
||||||
|
|
||||||
|
habit.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS));
|
||||||
|
|
||||||
|
scheduleAndVerify(null, expectedCheckmarkTime, expectedReminderTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedule_list()
|
||||||
|
{
|
||||||
|
long now = 1422277200000L; // 13:00 jan 26, 2015 (UTC)
|
||||||
|
DateUtils.setFixedLocalTime(now);
|
||||||
|
|
||||||
|
fixtures.purgeHabits();
|
||||||
|
|
||||||
|
Habit h1 = fixtures.createEmptyHabit();
|
||||||
|
h1.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS));
|
||||||
|
|
||||||
|
Habit h2 = fixtures.createEmptyHabit();
|
||||||
|
h2.setReminder(new Reminder(18, 30, DateUtils.ALL_WEEK_DAYS));
|
||||||
|
|
||||||
|
fixtures.createEmptyHabit();
|
||||||
|
|
||||||
|
scheduler.schedule(habitList);
|
||||||
|
|
||||||
|
verify(intentScheduler).schedule(1422347400000L, null);
|
||||||
|
verify(intentScheduler).schedule(1422297000000L, null);
|
||||||
|
verifyNoMoreInteractions(intentScheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedule_tomorrow()
|
||||||
|
{
|
||||||
|
long now = 1453813200000L; // 13:00 jan 26, 2016 (UTC)
|
||||||
|
DateUtils.setFixedLocalTime(now);
|
||||||
|
|
||||||
|
long expectedCheckmarkTime = 1453852800000L; // 00:00 jan 27, 2016 (UTC)
|
||||||
|
long expectedReminderTime = 1453883400000L; // 08:30 jan 27, 2016 (UTC)
|
||||||
|
|
||||||
|
habit.setReminder(new Reminder(8, 30, DateUtils.ALL_WEEK_DAYS));
|
||||||
|
scheduleAndVerify(null, expectedCheckmarkTime, expectedReminderTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testSchedule_withoutReminder()
|
||||||
|
{
|
||||||
|
scheduler.schedule(habit, null);
|
||||||
|
verifyZeroInteractions(intentScheduler);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void scheduleAndVerify(Long atTime,
|
||||||
|
long expectedCheckmarkTime,
|
||||||
|
long expectedReminderTime)
|
||||||
|
{
|
||||||
|
when(intentFactory.buildShowReminder(habit, expectedReminderTime,
|
||||||
|
expectedCheckmarkTime)).thenReturn(intent);
|
||||||
|
|
||||||
|
scheduler.schedule(habit, atTime);
|
||||||
|
|
||||||
|
verify(logger).logReminderScheduled(habit, expectedReminderTime);
|
||||||
|
|
||||||
|
verify(intentFactory).buildShowReminder(habit, expectedReminderTime,
|
||||||
|
expectedCheckmarkTime);
|
||||||
|
verify(intentScheduler).schedule(expectedReminderTime, intent);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user