Записки гиканутого

@channel_n5 Нравится 0
Это ваш канал? Подтвердите владение для дополнительных возможностей

Android: хакинг, кодинг и прочая жесть @ezobnin
Гео и язык канала
Россия, Русский
Категория
Технологии


Гео канала
Россия
Язык канала
Русский
Категория
Технологии
Добавлен в индекс
28.10.2017 23:53
реклама
Все новости Армении в одной ленте
Новости Армении и диаспоры 24/7
BRANDSHOP
Релизы редких кроссовок онлайн и офлайн.
Этимология
О происхождении русских слов коротко и без картинок
269
подписчиков
~0
охват 1 публикации
~6
дневной охват
N/A
постов в день
N/A
ERR %
0.45
индекс цитирования
Последние публикации
Удалённые
С упоминаниями
Репосты
When You Should Use Null in Kotlin - небольшая заметка о пользе значения null.

В среде программистов на Kotlin использование nullable-типов данных считается дурным тоном. Но автор объясняет, что благодаря особенностям Kotlin (null safety) использование null становится скорее преимуществом, чем недостатком.

Значение null можно использовать для индикации отсутствия какого-либо значения или недоступности данных. И если в Java в этом случае ты легко мог совершить "ошибку на миллион долларов" (например, обратится к методу null-объекта и уронить приложения), то Kotlin просто не позволит тебе этого сделать. Несколько примеров:

1. Если объект != null, то выполняем код:

user?.let {
handleNonNullUser(user)
}

2. Не выполняем функцию, если аргумент = null:

fun handleUser(user : User?) {
user ?: return
// твой код
}

3. Если объект = null то:

val userName = user?.getName() ?: "Unknown"
Читать полностью
Recovering data from a failing Android adoptable storage - статья о том, как восстановить данные с карты памяти, отформатированной с помощью механизма Adoptable Storage. В отличие от обычного подключения SD-карты, Adoptable Storage создает на карте памяти зашифрованный том, форматирует его в файловую систему Ext4 а затем подключает ее к основному хранилищу данных так, что на нее можно сохранять не только фотки с пляжа, но и приложения, их данные и любую другую информацию, которая обычно хранится только во внутренней памяти устройства. Другими словами Adoptable Storage позволяет расширить встроенную память устройства.

Но есть одна, а точнее две смежные проблемы: 1) если вставить карту памяти в другой телефон - он ее не увидит из-за отсутствия ключа для расшифровки данных, 2) если что-то пойдет не так (например, карта памяти начнет сбоить) восстановить данные с нее не получится, точнее получится, но через одно место. Как через это место восстанавливать данные описано в статье.

Для начала на телефоне необходимо получить права root. Затем подключить карту памяти к Linux-машине (macOS тоже должна подойти, но действия описаны именно для Linux) и снять ее образ. Обычный dd в этом случае не подойдет, так как если карта памяти начала сбоить он скорее всего вывалится с ошибкой "Input/output error". Выручит ddrescue, который предназначен как раз для таких случаев:

$ sudo ddrescue -f -n /dev/mmcblk0 herolte_sd_recovery_fulldisk.img herolte_sd_recovery_fulldisk.log

Далее необходимо извлечь из памяти устройства ключ шифрования (на устройствах с активным модулем TEE такой трюк скорее всего не пройдет):

$ adb root
> adb pull /data/misc/vold
/data/misc/vold/: 1 file pulled. 0.0 MB/s (16 bytes in 0.013s)
> ls vold
bench expand_ffffffffffffffffffffffffffffffff.key
> hexdump -e '16/1 "%02x" "\n"' vold/expand_ffffffffffffffffffffffffffffffff.key
aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa%

Используем полученный ключ чтобы смонтировать файловую систему:

$ sudo losetup --show --find -P herolte_sd_recovery_fulldisk.img
$ sudo dmsetup create crypt1 --table "0 `blockdev --getsize /dev/loop0p2` crypt aes-cbc-essiv:sha256 aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa 0 /dev/loop0p2 0"
$ mkdir mnt
$ sudo mount /dev/mapper/crypt1 mnt
$ ls mnt
app local lost+found media misc user user_de

Это все. Далее автор рассказывает как залить файлы на другую карту памяти и изменить размер файловой системы. Про это можно прочитать в оригинальной статье. Также стоит отметить, что если карты памяти одинакового объема, то можно вообще не заморачиваться с подключением файловой системы на компе и копированием ключа, а просто залить полученный на первом шаге образ на новую карту памяти с помощью тог же ddrescue:

$ sudo ddrescue -f -n herolte_sd_recovery_fulldisk.img /dev/mmcblk0 herolte_sd_recovery_fulldisk.log
Читать полностью
How Reified Type makes Kotlin so much better - краткая статья о том, как параметры вещественного типа могу облегчить твою жизнь. В официальной документации приводится только один пример использования такого типа параметров - для облегчения передачи классов внутрь функции. Например, вместо того, чтобы писать так:

private fun Activity.startActivity(
context: Context, clazz: Class) {
startActivity(Intent(context, clazz))
}

startActivity(context, NewActivity::class.java)

можно написать так:

inline fun Activity.startActivity(
context: Context) {
startActivity(Intent(context, T::class.java))
}

startActivity(context)

Но на этом полезные качества вещественных типов не заканчиваются. Еще один пример - функция для получения данных из бандла:

fun Bundle.getDataOrNull(): T? {
return getSerializable(DATA_KEY) as? T
}

val bundle: Bundle? = Bundle()
bundle?.putSerializable(DATA_KEY, "Testing")
val strData: String? = bundle?.getDataOrNull()
val intData: Int? = bundle?.getDataOrNull() // Crash

Последняя строка приведет к падению приложения по причине несовпадения ожидаемого типа данных и типа, возвращаемого функцией. Исправить это можно так:

private inline fun Bundle.getDataOrNull(): T? {
return getSerializable(DATA_KEY) as? T
}

val bundle: Bundle? = Bundle()
bundle?.putSerializable(DATA_KEY, "Testing")
val strData: String? = bundle?.getDataOrNull()
val intData: Int? = bundle?.getDataOrNull() // Null

Также вещественные типы можно использовать для эмуляции перегрузки методов на основе возвращаемого значения (Kotlin и Java по умолчанию разрешают выполнять перегрузки только на основе аргументов):

inline fun Resources.dpToPx(value: Int): T {
val result = TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
value.toFloat(), displayMetrics)

return when (T::class) {
Float::class -> result as T
Int::class -> result.toInt() as T
else -> throw IllegalStateException("Type not supported")
}
}

val intValue: Int = resource.dpToPx(64)
val floatValue: Float = resource.dpToPx(64)
Читать полностью
The Android Platform Security Model - написанный сотрудниками Google вайтпепер, описывающий теорию и практику реализации подсистем безопасности в Android. В документе много воды, но из него можно вычленить хоть и не новую, но полезную новичкам информацию.

- Android использует три вида аутентификации (проще говоря: метода разблокировки экрана) с разным уровнем надежности и, соответственно, уровнем доступа: 1) пароль или пин-код - считается наиболее надежным и поэтому дает полный контроль над устройством без всяких ограничений, 2) отпечаток пальца или снимок лица - менее надежный, поэтому система запрашивает пароль после каждой перезагрузки телефона, а также через каждые 72 часа, 3) аутентификация по местоположению или близости определенного Bluetooth-устройства - наименее надежный метод, поэтому на него накладываются те же ограничения, что и на биометрический метод, плюс он не позволяет получить доступ к аутентификационным ключам Keymaster (например, тем, что используются для платежей), а принудительный запрос пароля происходит не через 72 часа, а уже через 4.

- Песочницы (изолированная среда исполнения) для приложений в Android реализованы с помощью запуска каждого приложения от имени созданного специально для него Linux-пользователя. Приложение имеет полный доступ к файлам своей песочницы (/data/data/имя_пакета), но не может получить доступ к файлам других приложений и многим системным файлам. Система также использует UID (идентификатор пользователя) для контроля полномочий приложения.

- Контроль доступа на основе UID не распространяется на карты памяти и USB-накопители, так как зачастую они используют файловую систему FAT, которая не позволяет назначить права доступа к файлам. Чтобы решить эту проблему Android использует виртуальную файловую систему (sdcardfs), которая подключается к каталогу /sdcard/Android. Приложения могут хранить данные внутри нее, без опасения, что другие приложения получат к ним доступ.

- Единственный способ покинуть песочницу - получить права root. В Linux пользователь root имеет неограниченный доступ к файловой системе (ядро отключает любые проверки доступа для этого пользователя).

- Для защиты ключей шифрования/аутентификации Android и приложения могут использовать Keymaster. Это подсистема, позволяющая хранить данные в TEE (Trusted Execution Environment), специальном микрокомпьютере внутри SoC, к которому имеет доступ только система. TEE позволяет защитить данные даже в том случае, если злоумышленник получил права root. Начиная с девятой версии Android также поддерживает StrongBox, выделенный чип TEE, разработанный самой Google.

- Для защиты от эксплуатации уязвимостей в системных компонентах Android использует SELinux, подсистему ядра Linux, позволяющую более тонко управлять правами доступа, а также контролировать доступ процессов к системным вызовам. К примеру, обнаружив в одном из системных компонентов уязвимость, взломщик может попытаться принудить этот компонент выполнить системный вызов exec для запуска root shell, но если правила SELinux запрещают это делать данному компоненту, вызов будет отклонен.

- Начиная с седьмой версии Android способен гарантровать, что ни операционная система, ни загрузчик, не были скомпрментированы. Такая проверка выполняется на этапе загрузки и называется Verified boot: сначала загрузчик сверяет контрольную сумму раздела boot, затем один из следующих компонентов загрузки сверяет контрольные суммы файлов в разделе system. Тот же механизм используется для защиты от отката на предыдущую версию прошивки, которая может содержать уязвимости. Производители вольны сами выбирать как должна повести себя система в случае обнаружения нарушения: вывести на экран предупреждающее сообщение или прекратить загрузку.
Читать полностью
Kotlin : When if-else is too mainstream - краткая заметка о том, как создать более удобный аналог оператора if-else, который можно использовать так:

val condition = true
val output = condition then { 1 + 1 } elze { 2 + 2 }

Реализация такого "оператора" умещается в 10 строк:

infix fun Boolean.then(action : () -> T): T? {
return if (this)
action.invoke()
else null
}

infix fun T?.elze(action: () -> T): T {
return if (this == null)
action.invoke()
else this
}

Ключевое слово infix используется чтобы функции-расширения then и elze можно было взывать без необходимости ставить точку перед ней.
Читать полностью
Очередная подборка библиотек.

- ExpandableBottomBar - кастомизируемая анимированная панель навигации для нижней части экрана приложения;
- bubble-navigation - еще одна панель навигации;
- android_dbinspector - библиотека для просмотра содержимого базы данных приложения прямо на устройстве;
- Android-BackgroundChart - фоновое изображение с графиком;
- ShowMoreText - кастомный TextView, позволяющий показывать больше текста по клику на ссылку (например, "Подробнее");
- headless-wifi-manager - библиотека для управления конфигурацией WiFi других устройств используя Nearby API;
- crashx - очередная библиотека крэш-репортинга, показывает окно с информацией об ошибке при падении приложения;
- MultiProgressBar - множественный прогресс-бар в стиле Instagram Stories;
- mr-clean - библиотека для очистки логов от важной персональной информации;
- KBarcode - библиотека для реализации сканера штрих-кодов;
- ProfileBar - эффектная реализация страницы профиля пользователя/автора/героя;
- Kaskade - однонаправленный state-контейнер для хранения состояния объектов;
- audio-visualizer-android - библиотека визуализации аудио;
- glimpse-android - библиотека для обрезки изображений, учитывающая положение объектов;
Читать полностью
Extracting Android KeyStores from apps - статья об извлечении SSL-сертификатов из приложения с помощью Frida.

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

KeyStore имеет методы load(KeyStore.LoadStoreParameter param) и load(InputStream stream, char[] password) для извлечения данных из хранилища. Мы можем переписать код этих функций с помощью Frida и сохранить данные на своей машине.

Код скрипта для Frida выглядит так:

setTimeout(function() {
Java.perform(function () {
keyStoreLoadStream = Java.use('java.security.KeyStore')['load'].overload('java.io.InputStream', '[C');

/* Переписываем функцию Keystore.load(InputStream stream, char[] password) */
keyStoreLoadStream.implementation = function(stream, charArray) {

/* Если первый параметр null - запускаем оригинальную функцию */
if (stream == null) {
this.load(stream, charArray);
return;
}

/* Отправляем сообщение, что функция найдена */
send({event: '+found'});

/* Читаем InputStream в буфер */
var hexString = readStreamToHex (stream);

/* Отправляем тип KeyStore */
send({event: '+type', certType: this.getType()});

/* Отправляем пароль */
send({event: '+pass', password: charArray});

/* Отправляем сертификат в текстовой форме */
send({event: '+write', cert: hexString});

/* Запускаем оригинальную функцию */
this.load(stream, charArray);
}
});
},0);

/* Функция для чтения InputStream и его конвертации в ASCII */
function readStreamToHex (stream) {
var data = [];
var byteRead = stream.read();
while (byteRead != -1)
{
data.push( ('0' + (byteRead & 0xFF).toString(16)).slice(-2) );
/* */
byteRead = stream.read();
}
stream.close();
return data.join('');
}

Кроме приведенного выше скрипта также понадобится скрипт, работающий на компе (именно ему приведенный скрипт отправляет данные с помощью функции send). Как работать с Frida и запустить скрипт мы уже рассказывали в статье Инъекция для андроида.
Читать полностью
Privacy in Android Q - документация Google об изменениях в механизмах доступа к информации в анонсированном Android Q Beta 1. Основные нововведения:

1. Запрет фонового доступа к местоположению. Раньше, если пользователь давал приложению разрешение на использование местоположения, оно могло сделать это в любой момент, даже если находилось в фоне. В новой версии у пользователя есть возможность выбрать в каких ситуациях отдавать приложению свои координаты: в любое время или только пока приложение находится на экране.

2. Запрет фонового доступа к буферу обмена. В Android нет отдельного разрешения на доступ к буферу обмена, любое приложение может прочитать или записать его содержимое (так получилось из-за технических особенностей этого механизма). Теперь доступ к буферу обмена могут получить только приложения, которые в данный момент находятся на экране, либо являются клавиатурами и другими системами ввода. Нововведение убивает целый класс приложений: менеджеры буфера обмена.

3. Запрет фонового запуска активностей. Текущие версии Android позволяют приложениям запускать другие приложения (а точнее их активности) в любое время, независимо от того, находятся они на экране или нет. Это создает некоторые проблемы как в плане запутывания пользования, так и в плане возможностей для фишинга и других зловредных действий. Android Q запрещает фоновым приложениям запускать активности. В качестве альтернативы предлагается использовать уведомления, которые в свою очередь запустят нужную активность с помощью PendingIntent.

4. Запрет прямого доступу к карте памяти. Начиная с Android Q приложения больше не смогут получить прямой доступ к карте памяти (внутренней или внешней) с помощью разрешений READ_EXTERNAL_STORAGE и WRITE_EXTERNAL_STORAGE. Вместо этого следует использовать либо личный каталог приложения внутри /sdcard/Android (он создается автоматически и не требует разрешений) либо одно из разрешений, допускающих доступ к каталогам с фотографиями, видео и загрузками.

5. Возможность отзыва разрешений у старых приложений. Система подтверждения разрешений приложений пользователем появилась еще в Android 6.0. Но работала она только в отношении софта, собранного для Android 6.0 и выше. Весь старый софт продолжал получать все нужные ему разрешения автоматически. В Android Q ситуация не изменилась, но теперь при запуске старого приложения пользователь будет видеть экран, с помощью которого сможет отозвать уже выданные системой разрешения.

6. Запрет на включение/выключение WiFi. В Android Q приложения больше не смогут включать и выключать WiFi, вместо этого они должны использовать новую функцию settings-panels, которая показывает всплывающий диалог с переключателем выбранной настройки.

7. Ограничение на доступ к IMEI и серийному номеру устройства. Чтобы прочитать эту информацию теперь требуется использовать разрешение READ_PRIVILEGED_PHONE_STATE.

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

9. Рандомизация MAC-адреса. При скане сетей Android Q будет использовать рандомизированный MAC-адрес вместо настоящего. Это изменение должно защитить от отслеживания пользователя: некоторые магазины используют MAC-адрес для отслеживания посетителей, а торговые центры - для трекинга их перемещения.

10. Другие изменения: запрет на доступ к файловой системе /proc/net, запрет на чтение серийных номеров подключенных USB-устройств до получения соответствующего разрешения, запрет на чтение параметров камеры без получения разрешения на использование камеры, необходимость иметь разрешение ACCESS_FINE_LOCATION чтобы получить доступ ко многим функциям телефонии, WiFi и Bluetooth, запрет на скрытое получение содержимого экрана устройства обходными путями.
Читать полностью
Очередная подборка библиотек.

- InternalAppStore - набор библиотек и инструментов, позволяющий быстро развернуть свой собственный магазин приложений;
- FlutterExamples - шпаргалка и сборник примеров по Flutter;
- android-checkout - обертка, позволяющая быстро и без хлопот интегрировать библиотеку биллинга Google в приложение;
- AndroidRibbon - View, реализующий переливающуюся ленточку поверх изображения;
- belvedere - диалог выбора изображений;
- ScratchCardView - реализация скретч-карты как в лотерейном билете;
- BubbleGum - библиотека для применения анимированного градиента к любым элементам интерфейса;
- Thumby - библиотека для интерактивного создания превью из видео;
- ExpandableHintText - красивая анимированная реализация EditText;
- Tomo - коллекция эффектов для обработки изображений;
- StfalconImageViewer - встраиваемый просмотрщик изображений;
- TimetableLayout - RecyclerView.LayoutManager для показа данных в виде сетки расписания;
- Boots - библиотека для организации слаженного процесса загрузки приложения;
- RoundImageView - круглый ImageView, который поддерживает векторные изображения;
Читать полностью
Five tips to get your code base in shape - несколько советов о том, как поддерживать свой код в чистоте о порядке. Кому-то они покажутся банальными, но для новичков могут стать открытием.

1. Не переписывай код. В определенный момент тебе в руки может попасть устаревшее приложение, построенное на старых технологиях и, как тебе кажется, не слишком эффективное. У тебя может возникнуть желание переписать большую часть кода, но не стоит спешить. Скорее всего этот код до сих пор отлично работает и пользователи в целом довольны приложением. Когда ты возьмешься за его переписывание работа над новыми функциями остановится, ты потратишь огромную уйму времени чтобы фактически переписать приложение с нуля и в итоге пользователи, не подозревающие о внутренних изменениях, получат то же самое приложение. Да, часто нельзя смотреть без слез на чужой код, но может быть стоит начать с самых плачевных мест и постепенно избавляться от легаси?

2. Больше релизов. Опять же имея дело с легаси-кодом, который слабо или совсем не покрыт тестами, ты будешь часто сталкиваться с проблемой "изменил строку кода в одной части приложения - сломал функцию совсем в другом месте". Такая проблема может существенно растянуть релиз-цикл из-за необходимости постоянно ручного тестирования, которое в свою очередь может привести к появлению множества веток приложения (работа над новыми функциями не должна останавливаться пока приложение проходит через тестировщиков) и других проблем. Но есть решение: выпускать релизы часто. Если вместо одного релиза с множеством изменений ты будешь выпускать множество мелких релизов с одной-двумя новыми функциями, тестирование приложения существенно упростится и ускорится.

3. Разделяй и властвуй. Самый банальный совет статьи, который сводится к тому, что стоит разделить приложение на независимые компоненты, которые можно изменять отдельно друг от друга не беспокоясь, что изменение в одном месте вызовет проблему в другом. Да, писать спагетти-код с кучей зависимостей между компонентами гораздо проще, но в будущем такой код заставит тебя тратить уйму времени на тестирование и сожрет добрую половину нервных клеток.

4. Изучи свои инструменты. Ты можешь удивиться сколько отличных функций для автоматизированного рефакторинга включает в себя Android Studio. Многие из этих функций рассматриваются в другой статье автора.
Читать полностью
Understanding CPU- and I/O-bound for asynchronous operations - статья о многопоточном программировании, посвященная разнице между потоками, предназначенными для выполнения расчетов (CPU-bound) и потоками, в которых должны быть выполнены операции ввода-вывода (I/O-bound).

Все мы знаем, что тяжелые расчеты и сетевые операции необходимо выносить в отдельные потоки. Также все мы знаем, что создание нового потока - дорогостоящая в плане ресурсов операция. Поэтому современные системы не заставляют программиста создавать новые потоки на каждый чих, а предлагают использовать так называемые пулы потоков (thread pool): если нужно вынести какую-то работу в отдельный поток ты достаешь поток из пула и отдаешь эту работу ему.

RxJava и Koltin Coroutines предлагают два вида пулов: I/O-bound и CPU-bound. Чтобы понять в чем их различия необходимо разобраться, почему те или иные операции необходимо выносить в отдельные потоки.

Операции ввода-вывода, такие как сетевые запросы и чтение больших файлов необходимо выносить в отдельный поток просто потому, что они блокируют его: поток отправляет сетевой запрос и, до тех пор пока не получит ответ, блокируется без возможности делать что-либо еще. Если выполнить сетевой запрос в основном потоке приложения оно просто "зависнет" до получения ответа.

Приложения могут отправлять большое количество сетевых запросов во время своей работы, поэтому пул I/O-потоков должен быть большим. Таким большим, чтобы в любой момент времени приложение могло запросить еще один поток и не заблокироваться из-за того, что они закончились.

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

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

Ок, а как быть с приложениями, которые сначала выполняют операции ввода-вывода, а затем вычислительные операции? Для этого можно переключаться между потоками. Kotlin позволяет делать это легко и удобно:

launch(Dispatchers.IO) {
val image = api.fetchImageAsync(url).await()
withContext(Dispatchers.Default) {
val blurred = image.blur()
withContext(Dispatchers.Main) {
displayImage(blurred)
}
}
}

Тот же пример с использованием RxJava:

api.fetchImageObservable(url)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map { blurImageSync(it) }
.observeOn(AndroidSchedulers.mainThread())
.subscribe { displayImage(it) }
Читать полностью
Android Studio Project Marble: Apply Changes - статья разработчиков Android Studio о функции Apply Changes, которая должна ускорить повторный запуск приложения в эмуляторе или на реальном устройстве.

Apply Changes должна придти на смену плохо зарекомендовавшему себя механизму Intsant Run, который часто работал неверно и был несовместим со многими типами приложений. Смысл функции остался прежний - попытаться избежать перезапуска приложения при небольших изменениях кода и ресурсов и таким образом ускорить тестирование и отладку. Однако детали реализации изменились:

- В этот раз разработчики решили сделать упор на стабильность работы механизма, даже если для этого приходится жертвовать скоростью.
- Apply Changes работает только на Android 8 и выше.
- Если в новой сборке приложения изменились только ресурсы Android Studio перезапускает только активности приложения.
- Если изменился код, Android Studio подменяет код на новый прямо в памяти без перезапуска приложения. Однако эта функция работает только в том случае, если изменился код методов, но не их сигнатуры.
- Если были изменены AndroidManifest.xml или нативные библиотеки Android Studio придется перезапустить приложение.
Читать полностью
One still surprisingly valid reason to use the old Animation (API 1+) - интересная заметка о том, почему устаревший и давно никем неиспользуемый API анимации из древних версий Android еще может быть полезным для программистов.

Начиная с самых первых версий в Android был API анимации android.view.animation. По сравнению с современными ValueAnimator и ObjectAnimator его возможности были сильно ограничены, к тому же он страдал от бага, который заставлял систему думать, что анимируемый объект находится на своем месте даже если визуально он в другом месте. Например, если сделать эффект выезда элемента интерфейса из-за экрана, во время анимации система будет предполагать, что он уже на своем финальном месте, поэтому клик по текущему положению элемента ничего не даст, но клик по его ожидаемому положению будет обработан как будто он уже там. В новых API баг был исправлен.

Однажды у автора статьи возникла проблема. Он хотел, чтобы пользователи смогли использовать его приложение максимально быстро, кликая на элементы интерфейса не дожидаясь окончания их анимации. И как раз сдесь возникла проблема с новым API, который правильно рассчитывает текущее положение элементов интерфейса: клик по тому месту, где пользователь ожидал увидеть элемент интерфейса просто не проходил потому что элемент еще не "доехал" до него (анимация не закончилась). А вот старый API со его багом работал в данной ситуации отлично.

Для примера. Анимация с использованием нового API:

appearingGroup.setTranslationY(offset);
appearingGroup.setAlpha(0f);
appearingGroup
.animate()
.translationY(0f)
.alpha(1f)
.setDuration(100L)
.setStartDelay(35L)
.setInterpolator(interpolator)
.start();

С использованием старого API:

AnimationSet animSet = new AnimationSet(true);
animSet.addAnimation(new TranslateAnimation(0f, 0f, offset, 0f));
animSet.addAnimation(new AlphaAnimation(0f, 1f));
animSet.setDuration(100L);
animSet.setStartOffset(35L);
animSet.setInterpolator(interpolator);

appearingGroup.startAnimation(animSet);
Читать полностью
Launching a Kotlin Coroutine for immediate execution on the Main thread - интересное исследование на тему того, почему короутины Kotlin не запускаются сразу если они используют Disaptcher.Main.

Рассмотрим такой код:

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

launch(Dispatchers.Main) {
log("A")
}

log("B")
}

Вопреки интуиции он выведет на экран "B A" вместо "A B". Причина такого поведения заключается в том, что несмотря на то, что оба вызова функции log должны быть выполнены в основном потоке приложения, вызов log("A") все равно будет поставлен в очередь и таким образом будет выполнен уже после log("B").

Этого можно избежать если использовать Dispatchers.Main.immediate вместо Dispatchers.Main. Но разработчики Kotlin настоятельно не рекомендуют этого делать, так как в этом случае ты можешь столкнуться с багами, которые очень трудно поймать.
Читать полностью
Android App Bundles: Getting Started - большая статья о новом формате распространения приложений App Bundle, который позволяет существенно сократить размер приложения, который придется скачивать и устанавливать пользователям.

Идея App Bundle достаточно проста. Android-смартфоны могут быть очень разными, поэтому разработчиком необходимо заботится о таких вещах как разные размеры иконок для разных разрешений экрана, переводы на различные языки и сборки нативных библиотек для разных архитектур процессоров. В результате пользователям приходится скачивать громоздкое приложение, которое включает в себя код и данные, которые на их конкретном телефоне не нужны.

Частично эту проблему можно решить самостоятельно разделив приложение на несколько разных версий с помощью директив buildTypes и splits системы сборки Gradle. Однако в этом случае их все приходится заливать в Google Play по отдельности, а это может превратиться в кошмар если в результате разделения он получится десяток различных версий.

App Bundle позволяет упаковать код и данные всех версий в один пакет с расширением .aab и оставить работу по его разделению для разных смартфонов на усмотрение гуглу.

Также статья содержит другую интересную информацию:

- App Bundle - это обычный zip-архив. Его можно распаковать любым архиватором, поддерживающим формат zip.

- Google не просто создает из App Bundle несколько вариантов APK для разных смартфонов. Он дробит их на мелкие части, которые могут быть скачаны по мере необходимости. Например, при смене языка смартфон автоматически докачает и установит APK с нужным языковым пакетом.

- Google уже работает над функцией Dynamic delivery которая позволит дробить App Bundle на еще более мелкие части путем разделения не только ресурсов и нативных библиотек, но и кода самого приложения. В этом случае сначала пользователи будут устанавливать только "самую используемую" часть приложения, а дополнительные компоненты будут докачиваться по мере необходимости (точно также работают Instant Apps).

- Расщепить App Boundle на набор APK можно и самостоятельно. Для этого необходимо скачать и установить утилиту BundleTool. Использовать так (все APK будут упакованы в zip-архив app.apks):

$ java -jar bundletool.jar build-apks --bundle=app.aab --output=app.apks

Или так, если ты хочешь установить их на смартфон:

$ java -jar bundletool.jar build-apks --bundle=app.aab --output=app.apks
--ks=keystore.jks
--ks-pass=
--ks-key-alias=
--key-pass=

Чтобы сразу установить приложение на смартфон используй такую команду:

$ java -jar bundletool.jar install-apks --apks=app.apks
Читать полностью
Очередная подборка библиотек.

- GradleKotlinConverter - скрипт для упрощения миграции файлов Gradle на Kotlin;
- ChocoBar - расширенная кастомизированная версия снэкбара (Snackbar), информационного сообщения в нижней части экрана;
- syndication - написанная на Kotlin библиотека для парзинга фидов RSS 2.0 and ATOM 1.0;
- CarMarker-Animation - анимация автомобиля, движущегося по карте;
- Eiffel - легковесная библиотека для управления состояниями View с использованием Jetpack Architecture Components;
- TrackEyes - приложение-пример, способное отслеживать движение глаз с помощью Google Vision API;
- Kissme - мультиплафторменная Kotlin-библиотека для сохранения пар ключ:значение в зашифрованном виде (поддерживается Android и iOS);
- Tri-State-Checkbox - чекбокс с тремя состояниями;
- currency-edittext - EditText для ввода сумм в различных валютах.
Читать полностью
Tips I wish I had when I started with Android apps - семь простых советов начинающим разработчикам.

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

2. Сторонние магазины приложений - потеря времени. Публикация приложения за пределами Google Play тебе вряд ли что-то даст, но отнимет массу времени.

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

4. Crash reporting - наше все. Обязательно используй сервис для сбора отчетов об ошибках или вкладку Android Vitals в консоли разработчика Google Play. Ты можешь сколько угодно долго тестировать приложение, но всегда найдется немало странных смартфонов, на которых оно будет крашиться.

5. Не поддавайся соблазну заработать больше. Любой начинающий разработчик, приложение которого пользуется спросом, задумывается о интеграции большего количества рекламных баннеров и повышении цены. Будь осторожен, реклама не должна отвлекать и мешать использованию приложения. Иначе ты рискуешь потерять всю базу пользователей.

6. Регулярно обновляй приложение. Забыв о своем детище на полгода-год ты можешь удивиться как трудно его обновить в случае изменений правил Google Play или других событий. Библиотеки устаревают, инструменты обновляются, а ты получаешь множество несовместимостей одновременно.

7. Слушай пользователей. Любой отзыв о приложении важен. Просто ответив на него, а еще лучше выполнив просьбу автора, ты можешь получить множество дополнительных установок и избежать деинсталляций.
Читать полностью
An introduction to context-oriented programming in Kotlin - несмотря на громкое желтушное название, интересная и полезная статья об особенностях Kotlin, которые позволяют решать задачи, для которых они, казалось бы, не предназначены.

Первая особенность - функции-расширения, которые позволяют добавить свой собственный метод в любой класс, независимо от того, есть у разработчика доступ к его исходному коду или нет (пример: fun String.doSomthing() {}. Вторая - блоки области действия, которые позволяют выполнять код в контексте того или иного объекта без указания его имени (пример: with (string) { doSomthing() }).

Объединив их вместе мы получим неожиданный результат:

class B

class A{
fun B.doBSomething(){}
}

fun main(){
val a = A()
val b = B()
with(a){
b.doBSomething() // так можно
}
b.doBSomething() // а так нельзя
}

Но что это дает? Возможность писать код, который будет работать по-разному в зависимости от контекста. Взгляни на следующий пример:

interface NumberOperations{
operator fun Number.plus(other: Number) : Number
operator fun Number.minus(other: Number) : Number
operator fun Number.times(other: Number) : Number
operator fun Number.div(other: Number) : Number
}

object DoubleOperations: NumberOperations{
override fun Number.plus(other: Number) = this.toDouble() + other.toDouble()
override fun Number.minus(other: Number) = this.toDouble() - other.toDouble()
override fun Number.times(other: Number) = this.toDouble() * other.toDouble()
override fun Number.div(other: Number) = this.toDouble() / other.toDouble()
}

fun main(){
val n1: Number = 1.0
val n2: Number = 2

val res = with(DoubleOperations){
(n1 + n2)/2
}

println(res)
}

Этот код показывает как выполнять математические операции над числами по правилам типа Double. Если программисту понадобится выполнить их по правилам другого типа он может создать другой "контекст" и указать его в аргументе функции with.
Читать полностью
8 Productivity Tips for GitHub - краткая статья с восемью дельными советами по работе с GitHub.

1. Быстрый поиск по репозиторию. Открой любой репозиторий, нажми t и начни вводить имя файла. Появится выпадающий список по которому ты сможешь перемещаться с помощью стрелок.
2. Предложения в pull-реквестах. При комментировании pull-реквеста ты можешь предложить альтернативный код, разместив его в markdown-блоке кода с тегом suggestion.
3. Удобная навигация по исходному коду. Chrome-плагин Octotree добавляет на страницы репозиториев сайдбар, с помощью которого можно "ходить" по репозиторию также как ты делаешь это в IDE.
4. Прыжок к функции во время code review. Во время ревью кода из pull-реквеста ты в любой момент можешь нажать t и ввести имя функции, к которой ты хочешь переместиться.
5. Постоянная ссылка на файл или каталог. При просмотре файла или каталога нажми 'y' чтобы получить постоянную ссылку, гарантирующию, что даже если в будущем файл исчезнет из репозитория, его всегда можно будет найти по ссылке.
6. Кто изменял файл? Просматривая файл ты можешь нажать b чтобы увидеть кто и как давно изменял каждую строчку файла. Более свежие изменения будут отмечены более ярким цветом.
7. Поиск по репозиторию. Нажми / чтобы выполнить поиск по репозиторию. Аналог клика по строке поиска слева сверху.
8. Шаблоны ответов. Если тебе надоело каждый раз писать один и тот же ответ в комментариях, просто создай заготовку с помощью опции Saved replies.
Читать полностью
Hacking North Korea’s Android - презентация, посвященная взлому северокорейского смартфона Pyongyang 2407. Интересные факты:

- В устройстве используется материнская плата WBW5511, такая же как в BLU Life Play 2 и некоторых других смартфонах. Чипсет - Mediatek MT6582.
- Как многие другие устройства Северной Кореи смартфон работает на устаревшей Версии Android: 4.2.2 Jellybean с интерфейсом в стиле старых версий iOS.
- Браузер залочен на местный интранет KwangMyong. WiFi отключен полностью, но имеется поддержка местных 3G/CDMA-провайдеров Koroyolink, Kang Song и Byol.
- Смартфон использует систему DRM, которая позволяет просматривать, прослушивать и читать либо файлы, созданные на самом устройстве, либо одобренные правительством Серверной Кореи (для этого используются цифровые подписи RSA2048). Если закинуть видео-клип на карту памяти устройство откажется его воспроизводить.
- Система логирует все попытки открытия неподписанных файлов.
- Проверка на доступ к файлам осуществляется на низком уровне (нельзя установить сторонний файловый менеджер и открыть файл), но ее легко взломать.
Читать полностью