Как я создавал Hydrame: умные напоминания без занудства
Приложение-напоминалка живёт или умирает благодаря своим уведомлениям. Рассказываю, как я спроектировал уведомления Hydrame, чтобы они мягко подталкивали вас — и подстраивались под ваш день, а не боролись с ним.
У большинства приложений-напоминалок один и тот же роковой изъян: сами напоминания. Они срабатывают по жёсткому расписанию, не замечают, чем вы заняты, и постепенно приучают вас смахивать их не читая. Как только уведомление становится шумом, шумом становится всё приложение.
Hydrame — это приложение для учёта воды, а учёт воды — целиком задача о напоминаниях. Если подсказки хороши, приложение работает. Если они занудствуют, вы удалите его к четвергу. Поэтому большую часть времени на проектирование я потратил ровно на один вопрос: как напомнить человеку выпить воды, не превратившись в то, что ему противно?
Жёсткое расписание — враг
Наивный подход — будильник каждые два часа. Его легко сделать и мучительно с ним жить. Он жужжит во время совещаний, пока вы задремали, и через десять минут после того, как вы только что выпили стакан. Каждое несвоевременное напоминание отнимает у вас капельку доверия, и это доверие не возвращается.
Идея, определившая Hydrame: напоминание хорошо только тогда, когда приходит в момент, когда вы действительно можете что-то сделать. Всё остальное — шум в костюме помощника.
Подстраиваться под день
Вместо жёсткого таймера Hydrame распределяет дневную цель по часам вашего бодрствования и подталкивает лишь тогда, когда вы по-настоящему отстали и с последней подсказки прошло достаточно времени. Отметка выпитого отодвигает следующее напоминание. Пейте регулярно — и, возможно, вы вовсе не услышите приложение, а в этом и весь смысл.
Основная логика планирования намеренно небольшая:
interface ReminderContext {
now: Date;
wakeHour: number; // e.g. 8
sleepHour: number; // e.g. 23
goalMl: number; // daily target
consumedMl: number; // logged so far today
lastNudge: Date | null;
minGapMinutes: number; // breathing room between nudges
}
function shouldNudge(ctx: ReminderContext): boolean {
const hour = ctx.now.getHours();
// Never during sleeping hours.
if (hour < ctx.wakeHour || hour >= ctx.sleepHour) return false;
// Respect a minimum gap so we never feel pushy.
if (ctx.lastNudge) {
const gapMin = (ctx.now.getTime() - ctx.lastNudge.getTime()) / 60000;
if (gapMin < ctx.minGapMinutes) return false;
}
// How much *should* you have had by now, given the day's progress?
const dayLength = ctx.sleepHour - ctx.wakeHour;
const elapsed = Math.max(0, hour - ctx.wakeHour);
const expectedMl = ctx.goalMl * (elapsed / dayLength);
// Only nudge if you're meaningfully behind the curve.
return ctx.consumedMl < expectedMl * 0.85;
}
В этой математике нет ничего хитроумного, и это намеренно. Порог 0.85 означает «отстаёшь, но не настолько, чтобы наказывать». Ограничитель minGapMinutes — это и есть разница между спутником и занудой. Бо́льшая часть работы состояла в том, чтобы убирать поводы сработать, а не добавлять их.
Текст уведомления важен не меньше времени
Даже идеально подгаданное напоминание может оставить неприятный осадок, если слова неверны. «Вы проваливаете свою цель!» — формально это подсказка. А ещё это маленький акт враждебности, который вы сами пригласили на чей-то экран блокировки.
Напоминания Hydrame короткие, тёплые и никогда не давят на чувство вины:
- «Хороший момент для стакана воды.»
- «Середина дня — немного наверстаем?»
Никаких восклицательных знаков, дерущихся за внимание. Никакой серии, готовой «прерваться». Тон — как друг, который об этом упомянул, а не тренер, который об этом орёт.
Спокойствие по умолчанию, настройка по желанию
Значения по умолчанию — самое важное проектное решение в любом приложении, ведь большинство людей их никогда не меняет. Поэтому Hydrame приходит тихим: сдержанное число подсказок, ни одной ночью, и все их легко ещё убавить.
Если хотите более жёсткие цели или более частые проверки — они есть. Но вы включаете себе больше, а не выключаете меньше. Одна эта перестановка — спокойствие как пол, а не потолок — во многом и делает приложение уважительным.
Цель никогда не была в том, чтобы максимизировать уведомления. Она была в том, чтобы заслужить каждое из них.
Что я бы сказал каждому, кто делает приложение-напоминалку
- Привязывайте напоминания к моментам, когда можно действовать, а не к часам. Подсказка, по которой ничего нельзя сделать, — просто шум.
- Всегда оставляйте место, чтобы вздохнуть. Минимальный промежуток между сигналами не обсуждается.
- Пишите текст как человек, а не как метрика. Теплота ничего не стоит и покупает много доверия.
- Сделайте спокойный вариант выбором по умолчанию. Люди живут в ваших настройках по умолчанию.
Hydrame уже доступен в Google Play, и уведомления по-прежнему та часть, которую я не перестаю дорабатывать. Если попробуете, напишите мне, как ощущаются подсказки — эта обратная связь определяет, что будет дальше. Тихо, конечно же.
// По теме
Ещё из журнала
Привычка пить воду, которая не держится на силе воли
Большинство советов про питьевой режим сводятся к «пей больше воды» в надежде, что дальше сработает сила воли. Вот привычка, построенная на подсказках и мягких напоминаниях — вместо этого.
Разработка Granyn: трекер бюджета без входа в банк, всего на трёх таблицах
Как Granyn отслеживает расходы в разных валютах и распознаёт повторяющиеся счета, не подключая ни одного банковского аккаунта — схема Room под капотом и компромиссы, которые она навязывает.
Правильные действия в уведомлениях Android: отметить приём дозы без открытия приложения
Практическое руководство по кнопкам действий в уведомлениях Android — PendingIntent, ограничение activity-трамплина, goAsync() и безопасное обновление состояния Room.