Show alarm only on certain days of the week

pull/30/head
Alinson S. Xavier 10 years ago
parent c68176ad09
commit 1a4dbd9cba

@ -21,7 +21,7 @@
<meta-data <meta-data
android:name="AA_DB_VERSION" android:name="AA_DB_VERSION"
android:value="10"/> android:value="11"/>
<meta-data <meta-data
android:name="com.google.android.backup.api_key" android:name="com.google.android.backup.api_key"

@ -0,0 +1 @@
alter table habits add column reminder_days integer not null default 127;

@ -19,6 +19,8 @@ package org.isoron.helpers;
import android.content.Context; import android.content.Context;
import android.text.format.DateFormat; import android.text.format.DateFormat;
import org.isoron.uhabits.R;
import java.util.Date; import java.util.Date;
import java.util.GregorianCalendar; import java.util.GregorianCalendar;
import java.util.Locale; import java.util.Locale;
@ -51,10 +53,22 @@ public class DateHelper
{ {
GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT")); GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
day.setTimeInMillis(DateHelper.getStartOfDay(DateHelper.getLocalTime())); day.setTimeInMillis(DateHelper.getStartOfDay(DateHelper.getLocalTime()));
return day;
}
public static GregorianCalendar getCalendar(long timestamp)
{
GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
day.setTimeInMillis(timestamp);
return day; return day;
} }
public static int getWeekday(long timestamp)
{
GregorianCalendar day = getCalendar(timestamp);
return day.get(GregorianCalendar.DAY_OF_WEEK) % 7;
}
public static long getStartOfToday() public static long getStartOfToday()
{ {
return getStartOfDay(DateHelper.getLocalTime()); return getStartOfDay(DateHelper.getLocalTime());
@ -87,6 +101,17 @@ public class DateHelper
} }
public static String[] getShortDayNames() public static String[] getShortDayNames()
{
return getDayNames(GregorianCalendar.SHORT);
}
public static String[] getLongDayNames()
{
return getDayNames(GregorianCalendar.LONG);
}
public static String[] getDayNames(int format)
{ {
String[] wdays = new String[7]; String[] wdays = new String[7];
@ -95,7 +120,7 @@ public class DateHelper
for (int i = 0; i < 7; i++) for (int i = 0; i < 7; i++)
{ {
wdays[i] = day.getDisplayName(GregorianCalendar.DAY_OF_WEEK, GregorianCalendar.SHORT, wdays[i] = day.getDisplayName(GregorianCalendar.DAY_OF_WEEK, format,
Locale.getDefault()); Locale.getDefault());
day.add(GregorianCalendar.DAY_OF_MONTH, 1); day.add(GregorianCalendar.DAY_OF_MONTH, 1);
} }
@ -103,4 +128,61 @@ public class DateHelper
return wdays; return wdays;
} }
public static String formatWeekdayList(Context context, boolean weekday[])
{
String shortDayNames[] = getShortDayNames();
String longDayNames[] = getLongDayNames();
StringBuilder buffer = new StringBuilder();
int count = 0;
int first = 0;
boolean isFirst = true;
for(int i = 0; i < 7; i++)
{
if(weekday[i])
{
if(isFirst) first = i;
else buffer.append(", ");
buffer.append(shortDayNames[i]);
isFirst = false;
count++;
}
}
if(count == 1) return longDayNames[first];
if(count == 2 && weekday[0] && weekday[1]) return context.getString(R.string.weekends);
if(count == 5 && !weekday[0] && !weekday[1]) return context.getString(R.string.any_weekday);
if(count == 7) return context.getString(R.string.any_day);
return buffer.toString();
}
public static Integer packWeekdayList(boolean weekday[])
{
int list = 0;
int current = 1;
for(int i = 0; i < 7; i++)
{
if(weekday[i]) list |= current;
current = current << 1;
}
return list;
}
public static boolean[] unpackWeekdayList(int list)
{
boolean[] weekday = new boolean[7];
int current = 1;
for(int i = 0; i < 7; i++)
{
if((list & current) != 0) weekday[i] = true;
current = current << 1;
}
return weekday;
}
} }

@ -28,6 +28,7 @@ import android.content.SharedPreferences;
import android.graphics.BitmapFactory; import android.graphics.BitmapFactory;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Handler;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat; import android.support.v4.app.NotificationCompat;
@ -46,34 +47,44 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
public static final String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE"; public static final String ACTION_SNOOZE = "org.isoron.uhabits.ACTION_SNOOZE";
@Override @Override
public void onReceive(Context context, Intent intent) public void onReceive(final Context context, Intent intent)
{ {
switch (intent.getAction()) switch (intent.getAction())
{ {
case ACTION_REMIND: case ACTION_REMIND:
createNotification(context, intent.getData()); createNotification(context, intent);
createReminderAlarms(context);
break; break;
case ACTION_DISMISS: case ACTION_DISMISS:
dismissAllHabits(); dismissAllHabits();
ReminderHelper.createReminderAlarms(context);
break; break;
case ACTION_CHECK: case ACTION_CHECK:
checkHabit(context, intent.getData()); checkHabit(context, intent);
ReminderHelper.createReminderAlarms(context);
break; break;
case ACTION_SNOOZE: case ACTION_SNOOZE:
snoozeHabit(context, intent.getData()); snoozeHabit(context, intent);
ReminderHelper.createReminderAlarms(context);
break; break;
} }
} }
private void snoozeHabit(Context context, Uri data) private void createReminderAlarms(final Context context)
{ {
new Handler().postDelayed(new Runnable()
{
@Override
public void run()
{
ReminderHelper.createReminderAlarms(context);
}
}, 5000);
}
private void snoozeHabit(Context context, Intent intent)
{
Uri data = intent.getData();
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
long delayMinutes = Long.parseLong(prefs.getString("pref_snooze_interval", "15")); long delayMinutes = Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
@ -83,8 +94,9 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
dismissNotification(context, habit); dismissNotification(context, habit);
} }
private void checkHabit(Context context, Uri data) private void checkHabit(Context context, Intent intent)
{ {
Uri data = intent.getData();
Long timestamp = DateHelper.getStartOfToday(); Long timestamp = DateHelper.getStartOfToday();
String paramTimestamp = data.getQueryParameter("timestamp"); String paramTimestamp = data.getQueryParameter("timestamp");
@ -115,9 +127,9 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
} }
private void createNotification(Context context, Uri data) private void createNotification(Context context, Intent intent)
{ {
Uri data = intent.getData();
Habit habit = Habit.get(ContentUris.parseId(data)); Habit habit = Habit.get(ContentUris.parseId(data));
if (habit.hasImplicitRepToday()) return; if (habit.hasImplicitRepToday()) return;
@ -125,6 +137,8 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
habit.highlight = 1; habit.highlight = 1;
habit.save(); habit.save();
if (!checkWeekday(intent, habit)) return;
// Check if reminder has been turned off after alarm was scheduled // Check if reminder has been turned off after alarm was scheduled
if (habit.reminderHour == null) return; if (habit.reminderHour == null) return;
@ -149,6 +163,8 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION); Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
NotificationCompat.WearableExtender wearableExtender = NotificationCompat.WearableExtender wearableExtender =
new NotificationCompat.WearableExtender().setBackground( new NotificationCompat.WearableExtender().setBackground(
BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe)); BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
@ -165,6 +181,8 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
context.getString(R.string.snooze), snoozeIntentPending) context.getString(R.string.snooze), snoozeIntentPending)
.setSound(soundUri) .setSound(soundUri)
.extend(wearableExtender) .extend(wearableExtender)
.setWhen(reminderTime)
.setShowWhen(true)
.build(); .build();
notification.flags |= Notification.FLAG_AUTO_CANCEL; notification.flags |= Notification.FLAG_AUTO_CANCEL;
@ -176,4 +194,14 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
notificationManager.notify(notificationId, notification); notificationManager.notify(notificationId, notification);
} }
private boolean checkWeekday(Intent intent, Habit habit)
{
Long timestamp = intent.getLongExtra("timestamp", DateHelper.getStartOfToday());
boolean reminderDays[] = DateHelper.unpackWeekdayList(habit.reminderDays);
int weekday = DateHelper.getWeekday(timestamp);
return reminderDays[weekday];
}
} }

@ -0,0 +1,63 @@
package org.isoron.uhabits.dialogs;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.content.DialogInterface;
import android.os.Bundle;
import org.isoron.helpers.DateHelper;
public class WeekdayPickerDialog extends DialogFragment
implements DialogInterface.OnMultiChoiceClickListener, DialogInterface.OnClickListener
{
public interface OnWeekdaysPickedListener
{
void onWeekdaysPicked(boolean[] selectedDays);
}
private boolean[] selectedDays;
private OnWeekdaysPickedListener listener;
public void setListener(OnWeekdaysPickedListener listener)
{
this.listener = listener;
}
public void setSelectedDays(boolean[] selectedDays)
{
this.selectedDays = selectedDays;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState)
{
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle("Select weekdays")
.setMultiChoiceItems(DateHelper.getLongDayNames(), selectedDays, this)
.setPositiveButton(android.R.string.yes, this)
.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener()
{
@Override
public void onClick(DialogInterface dialog, int which)
{
dismiss();
}
});
return builder.create();
}
@Override
public void onClick(DialogInterface dialog, int which, boolean isChecked)
{
selectedDays[which] = isChecked;
}
@Override
public void onClick(DialogInterface dialog, int which)
{
if(listener != null) listener.onWeekdaysPicked(selectedDays);
}
}

@ -34,16 +34,18 @@ import com.android.colorpicker.ColorPickerDialog;
import com.android.colorpicker.ColorPickerSwatch; import com.android.colorpicker.ColorPickerSwatch;
import com.android.datetimepicker.time.RadialPickerLayout; import com.android.datetimepicker.time.RadialPickerLayout;
import com.android.datetimepicker.time.TimePickerDialog; import com.android.datetimepicker.time.TimePickerDialog;
import com.android.datetimepicker.time.TimePickerDialog.OnTimeSetListener;
import org.isoron.helpers.ColorHelper; import org.isoron.helpers.ColorHelper;
import org.isoron.helpers.Command; import org.isoron.helpers.Command;
import org.isoron.helpers.DateHelper; import org.isoron.helpers.DateHelper;
import org.isoron.helpers.DialogHelper.OnSavedListener; import org.isoron.helpers.DialogHelper.OnSavedListener;
import org.isoron.uhabits.R; import org.isoron.uhabits.R;
import org.isoron.uhabits.dialogs.WeekdayPickerDialog;
import org.isoron.uhabits.models.Habit; import org.isoron.uhabits.models.Habit;
public class EditHabitFragment extends DialogFragment implements OnClickListener public class EditHabitFragment extends DialogFragment
implements OnClickListener, WeekdayPickerDialog.OnWeekdaysPickedListener,
TimePickerDialog.OnTimeSetListener
{ {
private Integer mode; private Integer mode;
static final int EDIT_MODE = 0; static final int EDIT_MODE = 0;
@ -52,7 +54,7 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
private OnSavedListener onSavedListener; private OnSavedListener onSavedListener;
private Habit originalHabit, modifiedHabit; private Habit originalHabit, modifiedHabit;
private TextView tvName, tvDescription, tvFreqNum, tvFreqDen, tvInputReminder; private TextView tvName, tvDescription, tvFreqNum, tvFreqDen, tvReminderTime, tvReminderDays;
private SharedPreferences prefs; private SharedPreferences prefs;
private boolean is24HourMode; private boolean is24HourMode;
@ -85,14 +87,16 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
tvDescription = (TextView) view.findViewById(R.id.input_description); tvDescription = (TextView) view.findViewById(R.id.input_description);
tvFreqNum = (TextView) view.findViewById(R.id.input_freq_num); tvFreqNum = (TextView) view.findViewById(R.id.input_freq_num);
tvFreqDen = (TextView) view.findViewById(R.id.input_freq_den); tvFreqDen = (TextView) view.findViewById(R.id.input_freq_den);
tvInputReminder = (TextView) view.findViewById(R.id.inputReminderTime); tvReminderTime = (TextView) view.findViewById(R.id.inputReminderTime);
tvReminderDays = (TextView) view.findViewById(R.id.inputReminderDays);
Button buttonSave = (Button) view.findViewById(R.id.buttonSave); Button buttonSave = (Button) view.findViewById(R.id.buttonSave);
Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard); Button buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard);
buttonSave.setOnClickListener(this); buttonSave.setOnClickListener(this);
buttonDiscard.setOnClickListener(this); buttonDiscard.setOnClickListener(this);
tvInputReminder.setOnClickListener(this); tvReminderTime.setOnClickListener(this);
tvReminderDays.setOnClickListener(this);
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor); ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor);
@ -151,15 +155,20 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
{ {
if (modifiedHabit.reminderHour != null) if (modifiedHabit.reminderHour != null)
{ {
tvInputReminder.setTextColor(Color.BLACK); tvReminderTime.setTextColor(Color.BLACK);
tvInputReminder.setText(DateHelper.formatTime(getActivity(), tvReminderTime.setText(DateHelper.formatTime(getActivity(), modifiedHabit.reminderHour,
modifiedHabit.reminderHour, modifiedHabit.reminderMin)); modifiedHabit.reminderMin));
tvReminderDays.setVisibility(View.VISIBLE);
} }
else else
{ {
tvInputReminder.setTextColor(Color.GRAY); tvReminderTime.setTextColor(Color.GRAY);
tvInputReminder.setText(R.string.reminder_off); tvReminderTime.setText(R.string.reminder_off);
tvReminderDays.setVisibility(View.GONE);
} }
boolean weekdays[] = DateHelper.unpackWeekdayList(modifiedHabit.reminderDays);
tvReminderDays.setText(DateHelper.formatWeekdayList(getActivity(), weekdays));
} }
public void setOnSavedListener(OnSavedListener onSavedListener) public void setOnSavedListener(OnSavedListener onSavedListener)
@ -176,6 +185,10 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
onDateSpinnerClick(); onDateSpinnerClick();
break; break;
case R.id.inputReminderDays:
onWeekdayClick();
break;
case R.id.buttonSave: case R.id.buttonSave:
onSaveButtonClick(); onSaveButtonClick();
break; break;
@ -273,25 +286,39 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
defaultMin = modifiedHabit.reminderMin; defaultMin = modifiedHabit.reminderMin;
} }
TimePickerDialog timePicker = TimePickerDialog.newInstance(new OnTimeSetListener() TimePickerDialog timePicker =
{ TimePickerDialog.newInstance(this, defaultHour, defaultMin, is24HourMode);
@Override timePicker.show(getFragmentManager(), "timePicker");
public void onTimeSet(RadialPickerLayout view, int hour, int minute) }
{
modifiedHabit.reminderHour = hour;
modifiedHabit.reminderMin = minute;
updateReminder();
}
@Override private void onWeekdayClick()
public void onTimeCleared(RadialPickerLayout view) {
{ WeekdayPickerDialog dialog = new WeekdayPickerDialog();
modifiedHabit.reminderHour = null; dialog.setListener(this);
modifiedHabit.reminderMin = null; dialog.setSelectedDays(DateHelper.unpackWeekdayList(modifiedHabit.reminderDays));
updateReminder(); dialog.show(getFragmentManager(), "weekdayPicker");
} }
}, defaultHour, defaultMin, is24HourMode);
timePicker.show(getFragmentManager(), "timePicker"); @Override
public void onTimeSet(RadialPickerLayout view, int hour, int minute)
{
modifiedHabit.reminderHour = hour;
modifiedHabit.reminderMin = minute;
updateReminder();
}
@Override
public void onTimeCleared(RadialPickerLayout view)
{
modifiedHabit.reminderHour = null;
modifiedHabit.reminderMin = null;
updateReminder();
}
@Override
public void onWeekdaysPicked(boolean[] selectedDays)
{
modifiedHabit.reminderDays = DateHelper.packWeekdayList(selectedDays);
updateReminder();
} }
} }

@ -58,14 +58,13 @@ public class ReminderHelper
long timestamp = DateHelper.getStartOfDay(DateHelper.toLocalTime(reminderTime)); long timestamp = DateHelper.getStartOfDay(DateHelper.toLocalTime(reminderTime));
Uri uri = Uri.parse(String.format("content://org.isoron.uhabits/habit/%d?timestamp=%d", Uri uri = Uri.parse(String.format("content://org.isoron.uhabits/habit/%d", habit.getId()));
habit.getId(), timestamp));
Log.d("Alarm", uri.toString());
Intent alarmIntent = new Intent(context, ReminderAlarmReceiver.class); Intent alarmIntent = new Intent(context, ReminderAlarmReceiver.class);
alarmIntent.setAction(ReminderAlarmReceiver.ACTION_REMIND); alarmIntent.setAction(ReminderAlarmReceiver.ACTION_REMIND);
alarmIntent.setData(uri); alarmIntent.setData(uri);
alarmIntent.putExtra("timestamp", timestamp);
alarmIntent.putExtra("reminderTime", reminderTime);
PendingIntent pendingIntent = PendingIntent pendingIntent =
PendingIntent.getBroadcast(context, ((int) (habit.getId() % Integer.MAX_VALUE)) + 1, PendingIntent.getBroadcast(context, ((int) (habit.getId() % Integer.MAX_VALUE)) + 1,
@ -77,7 +76,7 @@ public class ReminderHelper
else else
manager.set(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent); manager.set(AlarmManager.RTC_WAKEUP, reminderTime, pendingIntent);
Log.d("Alarm", String.format("Setting alarm (%s): %s", Log.d("ReminderHelper", String.format("Setting alarm (%s): %s",
DateFormat.getDateTimeInstance().format(new Date(reminderTime)), habit.name)); DateFormat.getDateTimeInstance().format(new Date(reminderTime)), habit.name));
} }
} }

@ -71,6 +71,9 @@ public class Habit extends Model
@Column(name = "reminder_min") @Column(name = "reminder_min")
public Integer reminderMin; public Integer reminderMin;
@Column(name = "reminder_days")
public Integer reminderDays;
@Column(name = "highlight") @Column(name = "highlight")
public Integer highlight; public Integer highlight;
@ -90,6 +93,7 @@ public class Habit extends Model
this.archived = 0; this.archived = 0;
this.freqDen = 7; this.freqDen = 7;
this.freqNum = 3; this.freqNum = 3;
this.reminderDays = 127;
} }
public static Habit get(Long id) public static Habit get(Long id)
@ -189,6 +193,7 @@ public class Habit extends Model
this.position = model.position; this.position = model.position;
this.reminderHour = model.reminderHour; this.reminderHour = model.reminderHour;
this.reminderMin = model.reminderMin; this.reminderMin = model.reminderMin;
this.reminderDays = model.reminderDays;
this.highlight = model.highlight; this.highlight = model.highlight;
this.archived = model.archived; this.archived = model.archived;
} }

@ -75,6 +75,11 @@
android:id="@+id/inputReminderTime" android:id="@+id/inputReminderTime"
style="@style/dialogFormTimePicker" /> style="@style/dialogFormTimePicker" />
<TextView
android:id="@+id/inputReminderDays"
android:text="Any weekday"
style="@style/dialogFormTimePicker" />
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>

@ -107,6 +107,9 @@
<string name="hint_landscape">You can see more days by putting your phone in landscape mode.</string> <string name="hint_landscape">You can see more days by putting your phone in landscape mode.</string>
<string name="delete_habits">Delete Habits</string> <string name="delete_habits">Delete Habits</string>
<string name="delete_habits_message">The habits will be permanently deleted. This action cannot be undone.</string> <string name="delete_habits_message">The habits will be permanently deleted. This action cannot be undone.</string>
<string name="weekends">Weekends</string>
<string name="any_weekday">Any weekday</string>
<string name="any_day">Any day</string>
<string-array name="hints"> <string-array name="hints">
<item>@string/hint_drag</item> <item>@string/hint_drag</item>

Loading…
Cancel
Save