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.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.*;
|
||||
@@ -40,6 +43,8 @@ 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)
|
||||
@@ -60,6 +65,9 @@ public class BaseAndroidTest
|
||||
@Inject
|
||||
protected CommandRunner commandRunner;
|
||||
|
||||
@Inject
|
||||
protected HabitLogger logger;
|
||||
|
||||
protected AndroidTestComponent androidTestComponent;
|
||||
|
||||
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.*;
|
||||
|
||||
@RunWith(AndroidJUnit4.class)
|
||||
@SmallTest
|
||||
@MediumTest
|
||||
public class HabitsApplicationTest extends BaseAndroidTest
|
||||
{
|
||||
@Test
|
||||
|
||||
@@ -19,7 +19,10 @@
|
||||
|
||||
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.*;
|
||||
@@ -70,4 +73,21 @@ public class AndroidModule
|
||||
{
|
||||
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.show.*;
|
||||
import org.isoron.uhabits.ui.widgets.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
import org.isoron.uhabits.widgets.*;
|
||||
|
||||
/**
|
||||
@@ -106,4 +107,6 @@ public interface BaseComponent
|
||||
void inject(ReceiverActions receiverActions);
|
||||
|
||||
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
|
||||
private static Context context;
|
||||
|
||||
private static WidgetUpdater widgetManager;
|
||||
private static WidgetUpdater widgetUpdater;
|
||||
|
||||
public static BaseComponent getComponent()
|
||||
{
|
||||
@@ -63,9 +63,10 @@ public class HabitsApplication extends Application
|
||||
HabitsApplication.component = component;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@NonNull
|
||||
public static Context getContext()
|
||||
{
|
||||
if (context == null) throw new RuntimeException("context is null");
|
||||
return context;
|
||||
}
|
||||
|
||||
@@ -76,21 +77,23 @@ public class HabitsApplication extends Application
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static WidgetUpdater getWidgetManager()
|
||||
public static WidgetUpdater getWidgetUpdater()
|
||||
{
|
||||
if (widgetManager == null)
|
||||
throw new RuntimeException("widgetManager is null");
|
||||
if (widgetUpdater == null) throw new RuntimeException(
|
||||
"widgetUpdater is null");
|
||||
|
||||
return widgetManager;
|
||||
return widgetUpdater;
|
||||
}
|
||||
|
||||
public static boolean isTestMode()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (context != null) context
|
||||
.getClassLoader()
|
||||
.loadClass("org.isoron.uhabits.BaseAndroidTest");
|
||||
if (context != null)
|
||||
{
|
||||
String testClass = "org.isoron.uhabits.BaseAndroidTest";
|
||||
context.getClassLoader().loadClass(testClass);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
catch (final Exception e)
|
||||
@@ -114,8 +117,8 @@ public class HabitsApplication extends Application
|
||||
if (db.exists()) db.delete();
|
||||
}
|
||||
|
||||
widgetManager = new WidgetUpdater(this);
|
||||
widgetManager.startListening();
|
||||
widgetUpdater = new WidgetUpdater(this);
|
||||
widgetUpdater.startListening();
|
||||
|
||||
DatabaseUtils.initializeActiveAndroid();
|
||||
}
|
||||
@@ -125,7 +128,7 @@ public class HabitsApplication extends Application
|
||||
{
|
||||
HabitsApplication.context = null;
|
||||
ActiveAndroid.dispose();
|
||||
widgetManager.stopListening();
|
||||
widgetUpdater.stopListening();
|
||||
super.onTerminate();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
* 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.content.*;
|
||||
@@ -25,21 +25,23 @@ import android.net.*;
|
||||
import android.support.annotation.*;
|
||||
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
import org.isoron.uhabits.ui.habits.show.*;
|
||||
|
||||
public class PendingIntentFactory
|
||||
{
|
||||
public static final String BASE_URL = "content://org.isoron.uhabits/habit/";
|
||||
import static android.app.PendingIntent.*;
|
||||
|
||||
public class IntentFactory
|
||||
{
|
||||
@NonNull
|
||||
private final Context context;
|
||||
|
||||
public PendingIntentFactory(Context context)
|
||||
public IntentFactory(@NonNull 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();
|
||||
Intent checkIntent = new Intent(context, WidgetReceiver.class);
|
||||
@@ -47,7 +49,7 @@ public class PendingIntentFactory
|
||||
checkIntent.setAction(WidgetReceiver.ACTION_ADD_REPETITION);
|
||||
if (timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
||||
return PendingIntent.getBroadcast(context, 1, checkIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public PendingIntent buildDismissNotification()
|
||||
@@ -55,20 +57,37 @@ public class PendingIntentFactory
|
||||
Intent deleteIntent = new Intent(context, ReminderReceiver.class);
|
||||
deleteIntent.setAction(WidgetReceiver.ACTION_DISMISS_REMINDER);
|
||||
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();
|
||||
Intent snoozeIntent = new Intent(context, ReminderReceiver.class);
|
||||
snoozeIntent.setData(data);
|
||||
snoozeIntent.setAction(ReminderReceiver.ACTION_SNOOZE_REMINDER);
|
||||
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();
|
||||
Intent checkIntent = new Intent(context, WidgetReceiver.class);
|
||||
@@ -76,16 +95,18 @@ public class PendingIntentFactory
|
||||
checkIntent.setAction(WidgetReceiver.ACTION_TOGGLE_REPETITION);
|
||||
if (timestamp != null) checkIntent.putExtra("timestamp", timestamp);
|
||||
return PendingIntent.getBroadcast(context, 2, checkIntent,
|
||||
PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
|
||||
public PendingIntent buildViewHabit(Habit habit)
|
||||
{
|
||||
Uri uri = habit.getUri();
|
||||
|
||||
Intent intent = new Intent(context, ShowHabitActivity.class);
|
||||
intent.setData(Uri.parse(BASE_URL + habit.getId()));
|
||||
intent.setData(uri);
|
||||
return android.support.v4.app.TaskStackBuilder
|
||||
.create(context.getApplicationContext())
|
||||
.addNextIntentWithParentStack(intent)
|
||||
.getPendingIntent(0, PendingIntent.FLAG_UPDATE_CURRENT);
|
||||
.create(context)
|
||||
.addNextIntentWithParentStack(intent)
|
||||
.getPendingIntent(0, FLAG_UPDATE_CURRENT);
|
||||
}
|
||||
}
|
||||
@@ -17,7 +17,7 @@
|
||||
* 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.net.*;
|
||||
@@ -70,7 +70,7 @@ public class IntentParser
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
class CheckmarkIntentData
|
||||
public class CheckmarkIntentData
|
||||
{
|
||||
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
|
||||
* null.
|
||||
* Clears the reminder for a habit.
|
||||
*/
|
||||
public void clearReminder()
|
||||
{
|
||||
|
||||
@@ -38,8 +38,8 @@ public final class Reminder
|
||||
* Returns the days of the week the reminder should be shown.
|
||||
* <p>
|
||||
* This field can be converted to a list of booleans using the method
|
||||
* DateHelper.unpackWeekdayList and converted back to an integer by using
|
||||
* the method DateHelper.packWeekdayList.
|
||||
* DateUtils.unpackWeekdayList and converted back to an integer by using the
|
||||
* method DateUtils.packWeekdayList.
|
||||
*/
|
||||
public int getDays()
|
||||
{
|
||||
|
||||
@@ -29,6 +29,7 @@ import android.support.v4.app.*;
|
||||
import android.util.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.tasks.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
@@ -45,19 +46,22 @@ import javax.inject.*;
|
||||
public class ReminderReceiver extends BroadcastReceiver
|
||||
{
|
||||
public static final String ACTION_DISMISS_REMINDER =
|
||||
"org.isoron.uhabits.ACTION_DISMISS_REMINDER";
|
||||
"org.isoron.uhabits.ACTION_DISMISS_REMINDER";
|
||||
|
||||
public static final String ACTION_SHOW_REMINDER =
|
||||
"org.isoron.uhabits.ACTION_SHOW_REMINDER";
|
||||
"org.isoron.uhabits.ACTION_SHOW_REMINDER";
|
||||
|
||||
public static final String ACTION_SNOOZE_REMINDER =
|
||||
"org.isoron.uhabits.ACTION_SNOOZE_REMINDER";
|
||||
"org.isoron.uhabits.ACTION_SNOOZE_REMINDER";
|
||||
|
||||
private static final String TAG = "ReminderReceiver";
|
||||
|
||||
@Inject
|
||||
HabitList habits;
|
||||
|
||||
@Inject
|
||||
ReminderScheduler reminderScheduler;
|
||||
|
||||
public ReminderReceiver()
|
||||
{
|
||||
super();
|
||||
@@ -86,7 +90,7 @@ public class ReminderReceiver extends BroadcastReceiver
|
||||
break;
|
||||
|
||||
case Intent.ACTION_BOOT_COMPLETED:
|
||||
onActionBootCompleted(context);
|
||||
onActionBootCompleted();
|
||||
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)
|
||||
{
|
||||
createNotification(context, intent);
|
||||
createReminderAlarmsDelayed(context);
|
||||
createReminderAlarmsDelayed();
|
||||
}
|
||||
|
||||
private void createNotification(final Context context, final Intent intent)
|
||||
{
|
||||
final Uri data = intent.getData();
|
||||
final Habit habit = habits.getById(ContentUris.parseId(data));
|
||||
final Long timestamp =
|
||||
intent.getLongExtra("timestamp", DateUtils.getStartOfToday());
|
||||
final Long reminderTime =
|
||||
intent.getLongExtra("reminderTime", DateUtils.getStartOfToday());
|
||||
final Long timestamp = intent.getLongExtra("timestamp",
|
||||
DateUtils.getStartOfToday());
|
||||
final Long reminderTime = intent.getLongExtra("reminderTime",
|
||||
DateUtils.getStartOfToday());
|
||||
|
||||
if (habit == null) return;
|
||||
|
||||
@@ -137,40 +141,40 @@ public class ReminderReceiver extends BroadcastReceiver
|
||||
|
||||
Intent contentIntent = new Intent(context, MainActivity.class);
|
||||
contentIntent.setData(data);
|
||||
PendingIntent contentPendingIntent =
|
||||
PendingIntent.getActivity(context, 0, contentIntent,
|
||||
PendingIntent contentPendingIntent = PendingIntent.getActivity(
|
||||
context, 0, contentIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
PendingIntentFactory intentFactory =
|
||||
new PendingIntentFactory(context);
|
||||
IntentFactory intentFactory = new IntentFactory(context);
|
||||
|
||||
PendingIntent dismissPendingIntent;
|
||||
dismissPendingIntent = intentFactory.buildDismissNotification();
|
||||
PendingIntent checkIntentPending =
|
||||
intentFactory.buildAddCheckmark(habit, timestamp);
|
||||
intentFactory.buildAddCheckmark(habit, timestamp);
|
||||
PendingIntent snoozeIntentPending =
|
||||
intentFactory.buildSnoozeNotification(habit);
|
||||
intentFactory.buildSnoozeNotification(habit);
|
||||
|
||||
Uri ringtoneUri = ReminderUtils.getRingtoneUri(context);
|
||||
Uri ringtoneUri = RingtoneUtils.getRingtoneUri(context);
|
||||
|
||||
NotificationCompat.WearableExtender wearableExtender =
|
||||
new NotificationCompat.WearableExtender().setBackground(
|
||||
BitmapFactory.decodeResource(context.getResources(),
|
||||
R.drawable.stripe));
|
||||
new NotificationCompat.WearableExtender().setBackground(
|
||||
BitmapFactory.decodeResource(
|
||||
context.getResources(),
|
||||
R.drawable.stripe));
|
||||
|
||||
Notification notification =
|
||||
new NotificationCompat.Builder(context)
|
||||
Notification notification = new NotificationCompat.Builder(
|
||||
context)
|
||||
.setSmallIcon(R.drawable.ic_notification)
|
||||
.setContentTitle(habit.getName())
|
||||
.setContentText(habit.getDescription())
|
||||
.setContentIntent(contentPendingIntent)
|
||||
.setDeleteIntent(dismissPendingIntent)
|
||||
.addAction(R.drawable.ic_action_check,
|
||||
context.getString(R.string.check),
|
||||
checkIntentPending)
|
||||
context.getString(R.string.check),
|
||||
checkIntentPending)
|
||||
.addAction(R.drawable.ic_action_snooze,
|
||||
context.getString(R.string.snooze),
|
||||
snoozeIntentPending)
|
||||
context.getString(R.string.snooze),
|
||||
snoozeIntentPending)
|
||||
.setSound(ringtoneUri)
|
||||
.extend(wearableExtender)
|
||||
.setWhen(reminderTime)
|
||||
@@ -180,8 +184,8 @@ public class ReminderReceiver extends BroadcastReceiver
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(
|
||||
Activity.NOTIFICATION_SERVICE);
|
||||
(NotificationManager) context.getSystemService(
|
||||
Activity.NOTIFICATION_SERVICE);
|
||||
|
||||
int notificationId = (int) (habit.getId() % Integer.MAX_VALUE);
|
||||
notificationManager.notify(notificationId, notification);
|
||||
@@ -191,17 +195,18 @@ public class ReminderReceiver extends BroadcastReceiver
|
||||
}.execute();
|
||||
}
|
||||
|
||||
private void createReminderAlarmsDelayed(final Context context)
|
||||
private void createReminderAlarmsDelayed()
|
||||
{
|
||||
new Handler().postDelayed(
|
||||
() -> ReminderUtils.createReminderAlarms(context, habits), 5000);
|
||||
new Handler().postDelayed(() -> {
|
||||
reminderScheduler.schedule(habits);
|
||||
}, 5000);
|
||||
}
|
||||
|
||||
private void dismissNotification(Context context, Long habitId)
|
||||
{
|
||||
NotificationManager notificationManager =
|
||||
(NotificationManager) context.getSystemService(
|
||||
Activity.NOTIFICATION_SERVICE);
|
||||
(NotificationManager) context.getSystemService(
|
||||
Activity.NOTIFICATION_SERVICE);
|
||||
|
||||
int notificationId = (int) (habitId % Integer.MAX_VALUE);
|
||||
notificationManager.cancel(notificationId);
|
||||
@@ -210,15 +215,20 @@ public class ReminderReceiver extends BroadcastReceiver
|
||||
private void onActionSnoozeReminder(Context context, Intent intent)
|
||||
{
|
||||
Uri data = intent.getData();
|
||||
SharedPreferences prefs =
|
||||
PreferenceManager.getDefaultSharedPreferences(context);
|
||||
long delayMinutes =
|
||||
Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
||||
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(
|
||||
context);
|
||||
long delayMinutes = Long.parseLong(
|
||||
prefs.getString("pref_snooze_interval", "15"));
|
||||
|
||||
long habitId = ContentUris.parseId(data);
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -227,11 +237,11 @@ public class ReminderReceiver extends BroadcastReceiver
|
||||
if (!habit.hasReminder()) return false;
|
||||
Reminder reminder = habit.getReminder();
|
||||
|
||||
Long timestamp =
|
||||
intent.getLongExtra("timestamp", DateUtils.getStartOfToday());
|
||||
Long timestamp = intent.getLongExtra("timestamp",
|
||||
DateUtils.getStartOfToday());
|
||||
|
||||
boolean reminderDays[] =
|
||||
DateUtils.unpackWeekdayList(reminder.getDays());
|
||||
boolean reminderDays[] = DateUtils.unpackWeekdayList(
|
||||
reminder.getDays());
|
||||
int weekday = DateUtils.getWeekday(timestamp);
|
||||
|
||||
return reminderDays[weekday];
|
||||
|
||||
@@ -24,6 +24,7 @@ import android.support.annotation.*;
|
||||
import android.util.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
|
||||
import javax.inject.*;
|
||||
|
||||
@@ -50,6 +50,9 @@ public class BaseSystem
|
||||
@Inject
|
||||
HabitList habitList;
|
||||
|
||||
@Inject
|
||||
ReminderScheduler reminderScheduler;
|
||||
|
||||
public BaseSystem(Context context)
|
||||
{
|
||||
this.context = context;
|
||||
@@ -69,16 +72,16 @@ public class BaseSystem
|
||||
@NonNull
|
||||
public File dumpBugReportToFile() throws IOException
|
||||
{
|
||||
String date =
|
||||
DateUtils.getBackupDateFormat().format(DateUtils.getLocalTime());
|
||||
String date = DateUtils.getBackupDateFormat().format(
|
||||
DateUtils.getLocalTime());
|
||||
|
||||
if (context == null) throw new RuntimeException(
|
||||
"application context should not be null");
|
||||
"application context should not be null");
|
||||
File dir = FileUtils.getFilesDir("Logs");
|
||||
if (dir == null) throw new IOException("log dir should not be null");
|
||||
|
||||
File logFile =
|
||||
new File(String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||
File logFile = new File(
|
||||
String.format("%s/Log %s.txt", dir.getPath(), date));
|
||||
FileWriter output = new FileWriter(logFile);
|
||||
output.write(getBugReport());
|
||||
output.close();
|
||||
@@ -102,54 +105,12 @@ public class BaseSystem
|
||||
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
|
||||
{
|
||||
int maxLineCount = 250;
|
||||
StringBuilder builder = new StringBuilder();
|
||||
|
||||
String[] command = new String[]{"logcat", "-d"};
|
||||
String[] command = new String[]{ "logcat", "-d" };
|
||||
Process process = Runtime.getRuntime().exec(command);
|
||||
|
||||
InputStreamReader in = new InputStreamReader(process.getInputStream());
|
||||
@@ -172,4 +133,40 @@ public class BaseSystem
|
||||
|
||||
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(() -> {
|
||||
system.scheduleReminders();
|
||||
HabitsApplication.getWidgetManager().updateWidgets();
|
||||
HabitsApplication.getWidgetUpdater().updateWidgets();
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ public class ScoreCard extends HabitCard
|
||||
public void onItemSelected(int position)
|
||||
{
|
||||
setBucketSizeFromPosition(position);
|
||||
HabitsApplication.getWidgetManager().updateWidgets();
|
||||
HabitsApplication.getWidgetUpdater().updateWidgets();
|
||||
refreshData();
|
||||
}
|
||||
|
||||
|
||||
@@ -19,38 +19,46 @@
|
||||
|
||||
package org.isoron.uhabits.ui.settings;
|
||||
|
||||
import android.app.backup.BackupManager;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.preference.Preference;
|
||||
import android.support.v7.preference.PreferenceCategory;
|
||||
import android.support.v7.preference.PreferenceFragmentCompat;
|
||||
import android.app.backup.*;
|
||||
import android.content.*;
|
||||
import android.os.*;
|
||||
import android.support.v7.preference.*;
|
||||
|
||||
import org.isoron.uhabits.HabitsApplication;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.utils.InterfaceUtils;
|
||||
import org.isoron.uhabits.utils.ReminderUtils;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
import static org.isoron.uhabits.HabitsApplication.*;
|
||||
|
||||
public class SettingsFragment extends PreferenceFragmentCompat
|
||||
implements SharedPreferences.OnSharedPreferenceChangeListener
|
||||
{
|
||||
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
|
||||
public void onCreate(Bundle savedInstanceState)
|
||||
{
|
||||
super.onCreate(savedInstanceState);
|
||||
addPreferencesFromResource(R.xml.preferences);
|
||||
|
||||
setResultOnPreferenceClick("importData",
|
||||
HabitsApplication.RESULT_IMPORT_DATA);
|
||||
setResultOnPreferenceClick("exportCSV",
|
||||
HabitsApplication.RESULT_EXPORT_CSV);
|
||||
setResultOnPreferenceClick("exportDB",
|
||||
HabitsApplication.RESULT_EXPORT_DB);
|
||||
setResultOnPreferenceClick("bugReport",
|
||||
HabitsApplication.RESULT_BUG_REPORT);
|
||||
setResultOnPreferenceClick("importData", RESULT_IMPORT_DATA);
|
||||
setResultOnPreferenceClick("exportCSV", RESULT_EXPORT_CSV);
|
||||
setResultOnPreferenceClick("exportDB", RESULT_EXPORT_DB);
|
||||
setResultOnPreferenceClick("bugReport", RESULT_BUG_REPORT);
|
||||
|
||||
updateRingtoneDescription();
|
||||
|
||||
@@ -61,7 +69,45 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
||||
@Override
|
||||
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)
|
||||
@@ -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()
|
||||
{
|
||||
String ringtoneName = ReminderUtils.getRingtoneName(getContext());
|
||||
if(ringtoneName == null) return;
|
||||
String ringtoneName = RingtoneUtils.getRingtoneName(getContext());
|
||||
if (ringtoneName == null) return;
|
||||
Preference ringtonePreference = findPreference("reminderSound");
|
||||
ringtonePreference.setSummary(ringtoneName);
|
||||
}
|
||||
|
||||
@@ -24,8 +24,8 @@ import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
import org.isoron.uhabits.ui.widgets.views.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
|
||||
@@ -45,7 +45,7 @@ public class CheckmarkWidget extends BaseWidget
|
||||
@Override
|
||||
public PendingIntent getOnClickPendingIntent(Context context)
|
||||
{
|
||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
||||
IntentFactory factory = new IntentFactory(context);
|
||||
return factory.buildToggleCheckmark(habit, null);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
import org.isoron.uhabits.ui.common.views.*;
|
||||
import org.isoron.uhabits.ui.widgets.views.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
@@ -46,7 +46,7 @@ public class FrequencyWidget extends BaseWidget
|
||||
@Override
|
||||
public PendingIntent getOnClickPendingIntent(Context context)
|
||||
{
|
||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
||||
IntentFactory factory = new IntentFactory(context);
|
||||
return factory.buildViewHabit(habit);
|
||||
}
|
||||
|
||||
|
||||
@@ -56,7 +56,7 @@ public class HabitPickerDialog extends Activity
|
||||
{
|
||||
Long habitId = habitIds.get(position);
|
||||
preferences.addWidget(widgetId, habitId);
|
||||
HabitsApplication.getWidgetManager().updateWidgets();
|
||||
HabitsApplication.getWidgetUpdater().updateWidgets();
|
||||
|
||||
Intent resultValue = new Intent();
|
||||
resultValue.putExtra(EXTRA_APPWIDGET_ID, widgetId);
|
||||
|
||||
@@ -24,8 +24,8 @@ import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
import org.isoron.uhabits.ui.common.views.*;
|
||||
import org.isoron.uhabits.ui.widgets.views.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
@@ -44,7 +44,7 @@ public class HistoryWidget extends BaseWidget
|
||||
@Override
|
||||
public PendingIntent getOnClickPendingIntent(Context context)
|
||||
{
|
||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
||||
IntentFactory factory = new IntentFactory(context);
|
||||
return factory.buildViewHabit(habit);
|
||||
}
|
||||
|
||||
|
||||
@@ -24,8 +24,8 @@ import android.content.*;
|
||||
import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
import org.isoron.uhabits.ui.common.views.*;
|
||||
import org.isoron.uhabits.ui.habits.show.views.*;
|
||||
import org.isoron.uhabits.ui.widgets.views.*;
|
||||
@@ -47,7 +47,7 @@ public class ScoreWidget extends BaseWidget
|
||||
@Override
|
||||
public PendingIntent getOnClickPendingIntent(Context context)
|
||||
{
|
||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
||||
IntentFactory factory = new IntentFactory(context);
|
||||
return factory.buildViewHabit(habit);
|
||||
}
|
||||
|
||||
|
||||
@@ -25,8 +25,8 @@ import android.support.annotation.*;
|
||||
import android.view.*;
|
||||
import android.view.ViewGroup.*;
|
||||
|
||||
import org.isoron.uhabits.intents.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
import org.isoron.uhabits.ui.common.views.*;
|
||||
import org.isoron.uhabits.ui.widgets.views.*;
|
||||
import org.isoron.uhabits.utils.*;
|
||||
@@ -49,7 +49,7 @@ public class StreakWidget extends BaseWidget
|
||||
@Override
|
||||
public PendingIntent getOnClickPendingIntent(Context context)
|
||||
{
|
||||
PendingIntentFactory factory = new PendingIntentFactory(context);
|
||||
IntentFactory factory = new IntentFactory(context);
|
||||
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;
|
||||
|
||||
import android.app.*;
|
||||
import android.content.*;
|
||||
import android.media.*;
|
||||
import android.net.*;
|
||||
import android.os.*;
|
||||
import android.preference.*;
|
||||
import android.provider.*;
|
||||
import android.support.annotation.*;
|
||||
import android.support.v4.app.Fragment;
|
||||
import android.util.*;
|
||||
import android.support.v4.app.*;
|
||||
|
||||
import org.isoron.uhabits.*;
|
||||
import org.isoron.uhabits.models.*;
|
||||
import org.isoron.uhabits.receivers.*;
|
||||
|
||||
import java.text.*;
|
||||
import java.util.*;
|
||||
|
||||
public abstract class ReminderUtils
|
||||
public abstract class RingtoneUtils
|
||||
{
|
||||
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
|
||||
public static String getRingtoneName(Context context)
|
||||
{
|
||||
@@ -174,7 +105,7 @@ public abstract class ReminderUtils
|
||||
int requestCode)
|
||||
{
|
||||
Uri existingRingtoneUri =
|
||||
ReminderUtils.getRingtoneUri(fragment.getContext());
|
||||
getRingtoneUri(fragment.getContext());
|
||||
Uri defaultRingtoneUri = Settings.System.DEFAULT_NOTIFICATION_URI;
|
||||
|
||||
Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
|
||||
@@ -36,11 +36,14 @@ public class BaseUnitTest
|
||||
@Inject
|
||||
protected ModelFactory modelFactory;
|
||||
|
||||
protected TestComponent testComponent;
|
||||
|
||||
@Inject
|
||||
protected HabitList habitList;
|
||||
|
||||
@Inject
|
||||
protected HabitLogger logger;
|
||||
|
||||
protected TestComponent testComponent;
|
||||
|
||||
protected HabitFixtures fixtures;
|
||||
|
||||
@Before
|
||||
|
||||
@@ -73,4 +73,18 @@ public class TestModule
|
||||
{
|
||||
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