Блог Андрея

 
 

Как я писал своё первое «умное» приложение

Это интересно, но впервые я задумался о том, что было бы неплохо, чтобы мой смартфон сам убавлял яркость на ночь пришла мне в голову примерно через 7 лет использования смартфонов. Скорее всего, поводом послужила информация о том, что продвинутые модели оснащены сенсорами и устанавливают яркость в зависимости от освещения. Мне как раз хотелось написать что-то небольшое, но законченное (долгосрочные проекты, которым конца-края не видно нередко вызывают такое желание) и у меня было время на осуществление этого желания.

Я не сомневался, что подобных приложений в Google Play хоть пруд пруди, но мне хотелось сделать самому.

Поначалу я хотел написать обычный планировщик, в который можно было бы вносить время, когда убавить яркость и насколько, а когда прибавить. Чуть позже пришла идея, что неплохо бы заставить аппарат запоминать изменения яркости. Я буду просто изменять системную настройку яркости, как делал всё это время, а смартфон будет собирать статистику. Мне придется реже делать это вручную, а после года использования - ещё реже, так как приложение сможет использовать статистику за год.

Идея показалась похожей на хорошую и заслуживающей немедленной реализации. Но как это сделать?

Для начала был придуман сервис в фоне. Прежде всего он делает простую вещь: ежеминутно записывает текущее значение яркости в память.

Но, перед записью сервис проверяет, не изменилось ли предыдущее значение по сравнению с текущим. Если изменилось, факт изменения фиксируется в памяти устройства.

Если же значение не изменилось, сервис запрашивает сохраненное ранее в это время суток значение яркости и если оно найдено, устанавливает. Но только в том случае, если пользователь только что не изменил яркость по своему усмотрению.

План похож на хороший, осталось подумать, каким именно образом фиксировать факт изменения записи и каким образом определить, что надо изменять яркость.

Сначала рассмотрим тривиальный случай, как будто бы у нас всегда день и ночь неизменяемой продолжительности. Другими словами, допустим, что пользователь всегда убавляет яркость в одно и то же время и всегда прибавляет яркость в одно и то же время. Так как сервис совершает свой анализ раз в минуту (чаще, пожалуй излишне, да и батарея не резиновая), время буду сохранять как количество минут, прошедших с начала года и как количество минут, прошедших с начала текущих суток.

При фиксации изменения яркости достаточно записать в файл яркость в текущее время T и яркость минуту назад во время T0.

T будет соответствовать текущая яркость

T0 будет соответствовать яркость минуту назад

Тогда будет достаточно для умной установки яркости взять текущее время C и сравнить разности C - T и C - T0.

Если разность отрицательна, её не рассматриваем (потому что момент в текущие сутки ещё не наступил).

Из положительных разностей выбираем меньшую и устанавливаем соответствующее ей значение яркости.

Теперь реальные случаи, они сложнее.

Так как мой смартфон может лежать несколько дней без включения, найти файл (или запись в базе данных, сейчас это не суть важно) с записями вчерашних изменений яркости нельзя. Тем более невероятно найти файл годичной давности. Хочется найти файл примерно годичной давности, но маскимально близкий к текущему календарному дню. Причём надо предусмотреть, что его вообще может не быть. Например, интересует 02 05 2018, а из близких есть только 23 04 2018 и 15 06 2018 (Как их найти - это отдельный вопрос). Но есть ещё файл за 30 04 2019 и очевидно, что при отсутствии 02 05 2018 лучше бы взять за основу именно его - файл за 30 04 2019.

То есть, надо найти файл с изменениями независимо от года, такой, что дата создания файла отстоит от текущей минимально. Этого можно добиться, если именовать файлы с изменениями количеством минут, прошедших с начала года до начала тех суток, в которые был создан файл.

На языке SQL это будет выглядеть примерно вот так:

SELECT * FROM table WHERE ABS(spectime - N) = (SELECT MIN( ABS(spectime - N) ) FROM table);

где N - время в минутах, прошедшее с начала года. Оно вычисляется для суток года без учета часов, прощедших с начала суток.

spectime - поле типа integer в котором и хранится N при каждом сохранении факта того, что яркость изменилась.

Итак, запросом получаем имя файла, из файла получаем актуальное значение яркости по описаному выше "тривиальному" алгоритму

Но, жизнь ещё сложнее. Например:

  • 01 04 2019 в 18 00 я убавил яркость, потому что уже стемнело и глазам стало слишком ярко.
  • Потом я уехал на месяц, а смартфон выключил и забыл. (Если смартфонов несколько, вполне реальная ситуация).
  • 01 05 2019, в 19 00 я убавил яркость, потому что уже стемнело и глазам стало слишком ярко.
  • 02 05 2019 я проснулся ночью в 2 часа, включил свет и прибавил яркость телефона, потому что было слишком слабо.
  • 02 05 2019 в 19 00 алгоритм пойдёт искать ближайший файл, найдет запись за 02 05 (она создалась в 2 часа ночи).
  • И не убавит яркость дисплея, так как ближайшая к нему точка в минутах с начала суток - 02 00, а в это время яркость была прибавлена.

Что делать?

Можно например выбрать три записи вместо одной. Или любое другое нечетное количество.

Тогда, по анализу файла за 01 05 будет "голос" - надо убавить

По анализу файла за 01 04 будет "голос" - надо убавить

Два против одного - убавляем. Всё классно.

Итак, как в итоге работает.

  • Ежеминутно сохраняется изменение яркости НЕ нашим приложением (пользователем или другими приложениями).
  • Журнал изменений за каждые сутки хранится в отдельном файле или в отдельной записи таблицы БД
  • Каждую минуту берутся три журнала ближайшие к текущей дате.
  • Из каждого журнала выбирается момент смены яркости, ближайший к текущему моменту. (в этом пункте надо оперировать минутами в пределах суток)
  • После анализа каждого журнала получаем "голос" - менять ли яркость, в какую сторону (увеличивать или уменьшать) и какое значение в итоге установить.
  • После подсчёта голосов принимается решение - менять яркость или нет и какое значение установить.
  • Если значения яркости для каждого голоса различны, выбирается значение из того журнала, который ближе к текущему моменту. Время считается в минутах с начала года, таким образом после года эксплуатации учитываются и "будущие" значения яркости.

Это казалось хорошим решением, но уже после реализации приложения я обнаружил, что если яркость изменялась например в меньшую сторону двое суток подряд в определенное время, на третьи она настойчиво изменялась туда же, даже если мне это было не нужно.

То есть, предусмотренный в самом начале пункт "менять яркость только в том случае, если пользователь только что не изменил яркость по своему усмотрению" не был реализован. Поэтому было добавлено следующее правило:

"Голоса" "за изменение" и "против изменений" не учитываются в одном только случае - если есть фиксированное изменеие за текущие сутки и этот "голос" - наиболее близкий к текущему времени.

Для определения, что изменение именно за текущие сутки пришлось помимо количества минут, прошедших с начала года сохранять ещё и год.

В итоге получилось именно то, что я хотел, скачать приложение можно с Google Play.