Перейти к содержимому
Все записи

Как я создавал Hydrame: умные напоминания без занудства

Приложение-напоминалка живёт или умирает благодаря своим уведомлениям. Рассказываю, как я спроектировал уведомления Hydrame, чтобы они мягко подталкивали вас — и подстраивались под ваш день, а не боролись с ним.

MFKAPPS 4 мин чтения

У большинства приложений-напоминалок один и тот же роковой изъян: сами напоминания. Они срабатывают по жёсткому расписанию, не замечают, чем вы заняты, и постепенно приучают вас смахивать их не читая. Как только уведомление становится шумом, шумом становится всё приложение.

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 приходит тихим: сдержанное число подсказок, ни одной ночью, и все их легко ещё убавить.

Если хотите более жёсткие цели или более частые проверки — они есть. Но вы включаете себе больше, а не выключаете меньше. Одна эта перестановка — спокойствие как пол, а не потолок — во многом и делает приложение уважительным.

Цель никогда не была в том, чтобы максимизировать уведомления. Она была в том, чтобы заслужить каждое из них.

Что я бы сказал каждому, кто делает приложение-напоминалку

  1. Привязывайте напоминания к моментам, когда можно действовать, а не к часам. Подсказка, по которой ничего нельзя сделать, — просто шум.
  2. Всегда оставляйте место, чтобы вздохнуть. Минимальный промежуток между сигналами не обсуждается.
  3. Пишите текст как человек, а не как метрика. Теплота ничего не стоит и покупает много доверия.
  4. Сделайте спокойный вариант выбором по умолчанию. Люди живут в ваших настройках по умолчанию.

Hydrame уже доступен в Google Play, и уведомления по-прежнему та часть, которую я не перестаю дорабатывать. Если попробуете, напишите мне, как ощущаются подсказки — эта обратная связь определяет, что будет дальше. Тихо, конечно же.

// По теме

Ещё из журнала

MFKAPPS 4 мин чтения

Привычка пить воду, которая не держится на силе воли

Большинство советов про питьевой режим сводятся к «пей больше воды» в надежде, что дальше сработает сила воли. Вот привычка, построенная на подсказках и мягких напоминаниях — вместо этого.

#mobile #ux #reminders
MFKAPPS 4 мин чтения

Разработка Granyn: трекер бюджета без входа в банк, всего на трёх таблицах

Как Granyn отслеживает расходы в разных валютах и распознаёт повторяющиеся счета, не подключая ни одного банковского аккаунта — схема Room под капотом и компромиссы, которые она навязывает.

#android #engineering #privacy
MFKAPPS 4 мин чтения

Правильные действия в уведомлениях Android: отметить приём дозы без открытия приложения

Практическое руководство по кнопкам действий в уведомлениях Android — PendingIntent, ограничение activity-трамплина, goAsync() и безопасное обновление состояния Room.

#android #engineering #notifications