При разработке приложения, которое имеет доступ к важным данным пользователя (например, финансовые данные), не плохо бы добавить дополнительный слой безопасности. А именно — визуально защитить контент, пока пользователь не подтвердил свой отпечаток и скрыть контент приложения из меню “Недавние приложения” (Recent Apps).
Пользователь новых iOS сразу же может открыть любое плюс-минус адекватное приложение для мобильного банкинга и (скорее всего) он увидит, что проблема решается добавлением размытия контента (blur) в нужные моменты.
Именно такие входящие данные я и получил для разрешения данной задачи. Сразу скажу, что требование именно блюрить контент добавило работы и по итогу мы от него отказались. Но обо всем по порядку.
Защита контента в ожидании отпечатка пользователя
Юзкейс — пользователь запускает приложение или выводит его из фона. Нужно спросить отпечаток и показать контент только когда биометрика подтверждена.
Итак, как же скрыть контент?
Первое, что приходит в голову — размыть (заблюрить) его.
Начиная с Android 12 добавилось новое API RenderEffect. Android Compose тоже получил весьма удобный модификатор blur. Новость-то хорошая, но как насчет девайсов с меньшими API?

***
Для Android 8.0+ есть 3rd party решение blurkit. Все еще не идеально, во-первых, потому что это зависимость, а во-вторых, мое приложение работает и на низших версиях Андроида.
Помимо отсутствия нативных решений для младших версий Андроида, есть еще одна неочевидная особенность — если на UI, который под блюром, происходит какая-то операция или проигрывается анимация — её будет видно (хоть и плохо) и это отвлекает.
В итоге было принято решение просто показывать статичную view, что является очень прямолинейным и элегантным решением. Меньше кода — меньше багов.

***
Код будет в конце, а пока давайте посмотрим на вторую проблему — блюрить приложение в недавних.
Защита контента приложения в Recent Apps
Начал я с уже проделанного сообществом труда, т.е. со Stackoverflow.
Итого, есть следующие варианты:
-
Системное решение android:excludeFromRecents — скрывает приложение из недавних. Отбрасываем, т.е. ухудшает UX. Попробуй объясни потом пользователям, куда девается апка.
-
Сторонние решения, например myopic-app-switcher, который под капотом использует тот же blurkit-android. Как вариант, но работает с Android 8.0 и, опять-таки, это зависимость, которой надо поверить, что она действительно всё заблюрит, а не упадет на каком-то баге.
-
Системное решение FLAG_SECURE — весьма интересная фича. Суть в том, что в недавних юзер видит белый экран + не может сделать скрин апки стандартными средствами. Проблему невозможности сделать скрин можно обойти, устанавливая флаг в onPause и сбрасывая в onResume, таким образом вычленим из функционала только сокрытие контента из Recent Apps. У меня не завелось на Android 6.0 и 7.0, а соотв. работает тоже с Android 8.0

***
-
Показывать кастомную вьюху в onPause и скрывать ее в onResume. Отлично работает на Android 8.0+. На андроидах ниже система как-то по-другому делает скрин контента, который показывается в недавних, и поэтому способ не работает. Очень простое решение без зависимостей.




***
Итого, решения ниже Android 8 по факту нет (кроме как совсем убрать из недавних). Мы приняли решение двигаться с вариантом №4.
Код
Пишу на Compose, если не понимаете — мои статьи о Compose.
MainActivity.kt
class MainActivity : FragmentActivity() { private var recentAppsView: View? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) ... createRecentApppsView() addRecentAppsView() } override fun onResume() { super.onResume() viewModel.checkSecurityAuth( activity = this, onSuccess = ::removeRecentAppsView, onFailure = ::finish ) } override fun onPause() { addRecentAppsView() super.onPause() } private fun createRecentApppsView() { recentAppsView = ComposeView(this).apply { setContent { RecentApps() } } } private fun addRecentAppsView() { removeRecentAppsView() recentAppsView?.let { (window.decorView as ViewGroup).addView(it) } } private fun removeRecentAppsView() = recentAppsView?.let { (window.decorView as ViewGroup).removeView(it) } }
RecentApps.kt
@Composable fun RecentApps() = Box( modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center ) { StrictIcon(painter = painterResource(id = R.drawable.logo_light)) }
Edge case: Xiaomi MIUI

***
Чтобы включить размытие, пользователю надо сходить в настройки и сделать это руками. По умолчанию размытие выключено на 99% приложений, которые я устанавливал. Не понятно, какой логике следует MIUI. Если есть предположения — пишите.

***
Возможно, это не единственный edge case, ведь прошивок великое множество.
Итого
Внешне функция сокрытия контента кажется чем-то незамысловатым, но нужно понимать, что это безопасность пользователя, поэтому время ей уделить стоит.
Резюмируя, идеальным решением будет установить minSdk = 26 (Android 8) и использовать любое из удобный решений, описанных выше.
Разработка под Android никогда не была легким занятием и подобные кейсы это доказывают. Фрагментарность системы — это то, с чем Андроид разработчикам приходится жить. Надеюсь, эта статья немного облегчит нам жизнь.
Успехов!
P.S. заходите в мой блог✌️
ссылка на оригинал статьи https://habr.com/ru/post/649303/
Добавить комментарий