mirror of
https://github.com/iSoron/uhabits.git
synced 2025-12-06 01:08:50 -06:00
Show alarm only on certain days of the week
This commit is contained in:
@@ -21,7 +21,7 @@
|
||||
|
||||
<meta-data
|
||||
android:name="AA_DB_VERSION"
|
||||
android:value="10"/>
|
||||
android:value="11"/>
|
||||
|
||||
<meta-data
|
||||
android:name="com.google.android.backup.api_key"
|
||||
|
||||
1
app/src/main/assets/migrations/11.sql
Normal file
1
app/src/main/assets/migrations/11.sql
Normal file
@@ -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.text.format.DateFormat;
|
||||
|
||||
import org.isoron.uhabits.R;
|
||||
|
||||
import java.util.Date;
|
||||
import java.util.GregorianCalendar;
|
||||
import java.util.Locale;
|
||||
@@ -51,10 +53,22 @@ public class DateHelper
|
||||
{
|
||||
GregorianCalendar day = new GregorianCalendar(TimeZone.getTimeZone("GMT"));
|
||||
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;
|
||||
}
|
||||
|
||||
public static int getWeekday(long timestamp)
|
||||
{
|
||||
GregorianCalendar day = getCalendar(timestamp);
|
||||
return day.get(GregorianCalendar.DAY_OF_WEEK) % 7;
|
||||
}
|
||||
|
||||
public static long getStartOfToday()
|
||||
{
|
||||
return getStartOfDay(DateHelper.getLocalTime());
|
||||
@@ -87,6 +101,17 @@ public class DateHelper
|
||||
}
|
||||
|
||||
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];
|
||||
|
||||
@@ -95,7 +120,7 @@ public class DateHelper
|
||||
|
||||
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());
|
||||
day.add(GregorianCalendar.DAY_OF_MONTH, 1);
|
||||
}
|
||||
@@ -103,4 +128,61 @@ public class DateHelper
|
||||
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.media.RingtoneManager;
|
||||
import android.net.Uri;
|
||||
import android.os.Handler;
|
||||
import android.preference.PreferenceManager;
|
||||
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";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent)
|
||||
public void onReceive(final Context context, Intent intent)
|
||||
{
|
||||
switch (intent.getAction())
|
||||
{
|
||||
case ACTION_REMIND:
|
||||
createNotification(context, intent.getData());
|
||||
createNotification(context, intent);
|
||||
createReminderAlarms(context);
|
||||
break;
|
||||
|
||||
case ACTION_DISMISS:
|
||||
dismissAllHabits();
|
||||
ReminderHelper.createReminderAlarms(context);
|
||||
break;
|
||||
|
||||
case ACTION_CHECK:
|
||||
checkHabit(context, intent.getData());
|
||||
ReminderHelper.createReminderAlarms(context);
|
||||
checkHabit(context, intent);
|
||||
break;
|
||||
|
||||
case ACTION_SNOOZE:
|
||||
snoozeHabit(context, intent.getData());
|
||||
ReminderHelper.createReminderAlarms(context);
|
||||
snoozeHabit(context, intent);
|
||||
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);
|
||||
long delayMinutes = Long.parseLong(prefs.getString("pref_snooze_interval", "15"));
|
||||
|
||||
@@ -83,8 +94,9 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
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();
|
||||
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));
|
||||
|
||||
if (habit.hasImplicitRepToday()) return;
|
||||
@@ -125,6 +137,8 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
habit.highlight = 1;
|
||||
habit.save();
|
||||
|
||||
if (!checkWeekday(intent, habit)) return;
|
||||
|
||||
// Check if reminder has been turned off after alarm was scheduled
|
||||
if (habit.reminderHour == null) return;
|
||||
|
||||
@@ -149,6 +163,8 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
|
||||
Uri soundUri = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
|
||||
|
||||
Long reminderTime = intent.getLongExtra("reminderTime", DateHelper.getStartOfToday());
|
||||
|
||||
NotificationCompat.WearableExtender wearableExtender =
|
||||
new NotificationCompat.WearableExtender().setBackground(
|
||||
BitmapFactory.decodeResource(context.getResources(), R.drawable.stripe));
|
||||
@@ -165,6 +181,8 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
context.getString(R.string.snooze), snoozeIntentPending)
|
||||
.setSound(soundUri)
|
||||
.extend(wearableExtender)
|
||||
.setWhen(reminderTime)
|
||||
.setShowWhen(true)
|
||||
.build();
|
||||
|
||||
notification.flags |= Notification.FLAG_AUTO_CANCEL;
|
||||
@@ -176,4 +194,14 @@ public class ReminderAlarmReceiver extends BroadcastReceiver
|
||||
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.datetimepicker.time.RadialPickerLayout;
|
||||
import com.android.datetimepicker.time.TimePickerDialog;
|
||||
import com.android.datetimepicker.time.TimePickerDialog.OnTimeSetListener;
|
||||
|
||||
import org.isoron.helpers.ColorHelper;
|
||||
import org.isoron.helpers.Command;
|
||||
import org.isoron.helpers.DateHelper;
|
||||
import org.isoron.helpers.DialogHelper.OnSavedListener;
|
||||
import org.isoron.uhabits.R;
|
||||
import org.isoron.uhabits.dialogs.WeekdayPickerDialog;
|
||||
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;
|
||||
static final int EDIT_MODE = 0;
|
||||
@@ -52,7 +54,7 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
|
||||
private OnSavedListener onSavedListener;
|
||||
|
||||
private Habit originalHabit, modifiedHabit;
|
||||
private TextView tvName, tvDescription, tvFreqNum, tvFreqDen, tvInputReminder;
|
||||
private TextView tvName, tvDescription, tvFreqNum, tvFreqDen, tvReminderTime, tvReminderDays;
|
||||
|
||||
private SharedPreferences prefs;
|
||||
private boolean is24HourMode;
|
||||
@@ -85,14 +87,16 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
|
||||
tvDescription = (TextView) view.findViewById(R.id.input_description);
|
||||
tvFreqNum = (TextView) view.findViewById(R.id.input_freq_num);
|
||||
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 buttonDiscard = (Button) view.findViewById(R.id.buttonDiscard);
|
||||
|
||||
buttonSave.setOnClickListener(this);
|
||||
buttonDiscard.setOnClickListener(this);
|
||||
tvInputReminder.setOnClickListener(this);
|
||||
tvReminderTime.setOnClickListener(this);
|
||||
tvReminderDays.setOnClickListener(this);
|
||||
|
||||
ImageButton buttonPickColor = (ImageButton) view.findViewById(R.id.buttonPickColor);
|
||||
|
||||
@@ -151,15 +155,20 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
|
||||
{
|
||||
if (modifiedHabit.reminderHour != null)
|
||||
{
|
||||
tvInputReminder.setTextColor(Color.BLACK);
|
||||
tvInputReminder.setText(DateHelper.formatTime(getActivity(),
|
||||
modifiedHabit.reminderHour, modifiedHabit.reminderMin));
|
||||
tvReminderTime.setTextColor(Color.BLACK);
|
||||
tvReminderTime.setText(DateHelper.formatTime(getActivity(), modifiedHabit.reminderHour,
|
||||
modifiedHabit.reminderMin));
|
||||
tvReminderDays.setVisibility(View.VISIBLE);
|
||||
}
|
||||
else
|
||||
{
|
||||
tvInputReminder.setTextColor(Color.GRAY);
|
||||
tvInputReminder.setText(R.string.reminder_off);
|
||||
tvReminderTime.setTextColor(Color.GRAY);
|
||||
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)
|
||||
@@ -176,6 +185,10 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
|
||||
onDateSpinnerClick();
|
||||
break;
|
||||
|
||||
case R.id.inputReminderDays:
|
||||
onWeekdayClick();
|
||||
break;
|
||||
|
||||
case R.id.buttonSave:
|
||||
onSaveButtonClick();
|
||||
break;
|
||||
@@ -273,25 +286,39 @@ public class EditHabitFragment extends DialogFragment implements OnClickListener
|
||||
defaultMin = modifiedHabit.reminderMin;
|
||||
}
|
||||
|
||||
TimePickerDialog timePicker = TimePickerDialog.newInstance(new OnTimeSetListener()
|
||||
{
|
||||
@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();
|
||||
}
|
||||
}, defaultHour, defaultMin, is24HourMode);
|
||||
|
||||
TimePickerDialog timePicker =
|
||||
TimePickerDialog.newInstance(this, defaultHour, defaultMin, is24HourMode);
|
||||
timePicker.show(getFragmentManager(), "timePicker");
|
||||
}
|
||||
|
||||
private void onWeekdayClick()
|
||||
{
|
||||
WeekdayPickerDialog dialog = new WeekdayPickerDialog();
|
||||
dialog.setListener(this);
|
||||
dialog.setSelectedDays(DateHelper.unpackWeekdayList(modifiedHabit.reminderDays));
|
||||
dialog.show(getFragmentManager(), "weekdayPicker");
|
||||
}
|
||||
|
||||
@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));
|
||||
|
||||
Uri uri = Uri.parse(String.format("content://org.isoron.uhabits/habit/%d?timestamp=%d",
|
||||
habit.getId(), timestamp));
|
||||
|
||||
Log.d("Alarm", uri.toString());
|
||||
Uri uri = Uri.parse(String.format("content://org.isoron.uhabits/habit/%d", habit.getId()));
|
||||
|
||||
Intent alarmIntent = new Intent(context, ReminderAlarmReceiver.class);
|
||||
alarmIntent.setAction(ReminderAlarmReceiver.ACTION_REMIND);
|
||||
alarmIntent.setData(uri);
|
||||
alarmIntent.putExtra("timestamp", timestamp);
|
||||
alarmIntent.putExtra("reminderTime", reminderTime);
|
||||
|
||||
PendingIntent pendingIntent =
|
||||
PendingIntent.getBroadcast(context, ((int) (habit.getId() % Integer.MAX_VALUE)) + 1,
|
||||
@@ -77,7 +76,7 @@ public class ReminderHelper
|
||||
else
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +71,9 @@ public class Habit extends Model
|
||||
@Column(name = "reminder_min")
|
||||
public Integer reminderMin;
|
||||
|
||||
@Column(name = "reminder_days")
|
||||
public Integer reminderDays;
|
||||
|
||||
@Column(name = "highlight")
|
||||
public Integer highlight;
|
||||
|
||||
@@ -90,6 +93,7 @@ public class Habit extends Model
|
||||
this.archived = 0;
|
||||
this.freqDen = 7;
|
||||
this.freqNum = 3;
|
||||
this.reminderDays = 127;
|
||||
}
|
||||
|
||||
public static Habit get(Long id)
|
||||
@@ -189,6 +193,7 @@ public class Habit extends Model
|
||||
this.position = model.position;
|
||||
this.reminderHour = model.reminderHour;
|
||||
this.reminderMin = model.reminderMin;
|
||||
this.reminderDays = model.reminderDays;
|
||||
this.highlight = model.highlight;
|
||||
this.archived = model.archived;
|
||||
}
|
||||
|
||||
@@ -75,6 +75,11 @@
|
||||
android:id="@+id/inputReminderTime"
|
||||
style="@style/dialogFormTimePicker" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/inputReminderDays"
|
||||
android:text="Any weekday"
|
||||
style="@style/dialogFormTimePicker" />
|
||||
|
||||
</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="delete_habits">Delete Habits</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">
|
||||
<item>@string/hint_drag</item>
|
||||
|
||||
Reference in New Issue
Block a user