Android 14 уже здесь, поэтому я взялся за документацию, разбор экспертов и другие доступные ресурсы, чтобы разобрать все важные изменения, которые коснутся большинства прикладных разработчиков. Разберем новые ограничения на работу в фоне, изменения в Foreground Service, новые ограничения на работу Intent и BroadcastReceiver. В этом релизе много ограничений, но есть и новые фичи.

Если вам интересно следить за самыми последними новостями Android-разработки и получать подборку интересных статей по этой тематике, тогда вам стоит подписаться на Телеграм-канал Android Broadcast и мой YouTube канал "Android Broadcast"

Predictive Back Gesture в бой

 Наглядная анимация при навигации назад
Наглядная анимация при навигации назад

Уже в Android 13 нас предупредили о том, что в следующей версии Android нас ждет обновление жестов назад и предсказуемая навигация с анимацией в виде превью экрана, куда мы будем переходить. Анимация показа пока все также не работает и ее нужно отдельно включать в настройках разработчика.

Пример собственной анимации при выполнении жеста "Назад"
Пример собственной анимации при выполнении жеста "Назад"

Также добавили возможность создавать собственные анимации перехода внутри приложения. Для этого в OnBackPressedCallback добавили метод handleOnBackProgressed(), который вызывается с прогрессом жеста “Назад”, а также методы handleOnBackPressed() и handleOnBackCancelled(), вызываемые при окончании анимации жеста “Назад” и его отмене соответственно. На экране вы можете видеть пример реализации собственной анимации с помощью библиотеки Jetpack AppCompat 1.8.0

class MainActivity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        ...
        val box = findViewById<View>(R.id.box)
        val screenWidth = Resources.getSystem().displayMetrics.widthPixels
        val maxXShift = (screenWidth / 20)

        val callback = object : OnBackPressedCallback(enabled = true) {

            override fun handleOnBackProgressed(backEvent: BackEvent) {
                when (backEvent.swipeEdge) {
                    BackEvent.EDGE_LEFT ->
                        box.translationX = backEvent.progress * maxXShift
                    BackEvent.EDGE_RIGHT ->
                        box.translationX = -(backEvent.progress * maxXShift)
                }
                box.scaleX = 1F - (0.1F * backEvent.progress)
                box.scaleY = 1F - (0.1F * backEvent.progress)
            }

            override fun handleOnBackPressed() {
                // Жест "Назад" завершен
            }

            override fun handleOnBackCancelled() {
                // Жест назад отменён
                // сбрасываем объекты аниманиции в начальное состояние
            }
        }
        this.onBackPressedDispatcher.addCallback(callback)
    }
}

Также вместо метода overidePendingTransition(), который теперь помечен как deprecated, надо вызывать новый метод overrideActivityTransition(). Существующий метод не работает корректно с predictive back, так как имеет выше приоритет при выполнении анимации перехода.

// Новое API
overrideActivityTransition(
    enterAnim = R.anim.open_trans,
    exitAnim = R.anim.exit_trans,
    backgroundColor = R.color.bgr_color
)

// deprecated
overridePendingTransition(R.anim.open_trans, R.anim.exit_trans)

Ограничение на установку старых приложений

Одним из первых анонсированных изменений в Android 14 станет невозможность установки приложений с targetSdk <= 23 (Android 6.0). Не путайте с minSdk. Про их различие у меня есть ролик на канале.

Изменение призвано остановить распространение приложений, которые не переходят на новые версии targetSdk и распространяются за пределами Google Play, чтобы пользоваться старыми уязвимостями Android. Таким образом, приложения могут получать все разрешения при установке, обходя механизм Runtime Permission.

При попытке установки такого приложения пользователь увидит ошибку, а в Logcat появится лог с подробностями

INSTALL_FAILED_DEPRECATED_SDK_VERSION: App package must target at least SDK version 23, but found 7

Установка приложений с любым targetSdk будет доступна через adb с указанием специального флага для игнорирования ограничения

adb install --bypass-low-target-sdk-block FILENAME.apk

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

Интернационализация

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

Grammatical Inflection API

Русский язык отличается от английского тем, что у нас есть понятие рода и, соответственно, меняются глаголы, предлоги и другие части языка. В новой версии Android появляется Grammatical Inflection API. Теперь приложению можно указать, какого пола пользователь через системный сервис GrammaticalInflectionManager, что приведет к пересозданию Activity, так как является частью конфигурации.

// Указываем пол пользователя для грамматики
val gIM: GrammaticalInflectionManager = сontext.getSystemService()
gIM.setRequestedApplicationGrammaticalGender(
    Configuration.GRAMMATICAL_GENDER_FEMININE)

В ресурсах теперь можно создавать отдельные строки для разных полов

Квалификатор пола

Значение строк

Пример

Женский

feminine

res/values-ru-feminine/strings.xml

Мужской

masculine

res/values-ru-masculine/strings.xml

Нейтральный

neuter

res/values-ru-neuter/strings.xml

Без информации

-

res/values-ru/strings.xml

Нелинейное увеличение размера текста

Размер текста в Android рекомендуется задавать в sp, специальной единице измерения, которая учитывает увеличение размера текста заданного пользователям в системных настройках. Минусом является то, что весь текст увеличивался, и если маленький текст становился читаемым, то вот большие названия становились нечитаемыми из-за обрезания.

В Android 14 вводится новая система масштабирования текста в соответствии с Web Content Accessibility Guidelines (WCAG). Теперь будет применяться нелинейная шкала масштабирования. Это значит, что большой текст не будет увеличиваться также, как это происходит с мелким. Помимо этого, увеличили максимальный масштаб текста. На Pixel 7 Pro с Android 13 максимальным значением было 130%, в Android 14 - 200%.

Сравнение масштабов текста в Android 13 и Android 14 на Pixel 7 Pro
Сравнение масштабов текста в Android 13 и Android 14 на Pixel 7 Pro

Чтобы корректно перевести пиксели в sp и обратно, вам надо использовать TypedValue.applyDimension() и TypedValue.deriveDimension(). Эти API учитывают особенности нелинейного масштабирования текста, в отличие от простого умножения размера текста в SP на коэффициент масштабирования, который можно получить в Configuration.fontScale и DisplayMetrics.scaledDensity.

// Конвертируем 10 SP в PX
TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 10F, displayMetrics)

// Конвертируем 50 PX в SP
TypedValue.deriveDimension(TypedValue.COMPLEX_UNIT_SP, 50F, displayMetrics)

Share Sheet

В Android 14 обновили дизайн и возможности Share Sheet - системного диалога, который вы видите, когда делитесь текстом, картинкой или другим контентом.

На Pixel 7 Pro в разделе Direct Share вместо 4 элементов на Android 13 стало размещаться 5 в свежей версии ОС. Также изменили систему ранжирования того, что вы видите в этой секции. Используйте ShortcutManagerCompat.pushDynamicShortcut() , когда отправляете сообщение пользователю, а при создании ShortcutInfo вызывайте addCapabilityBinding() со значением “actions.intent.SEND_MESSAGE”. Очень странное решение, сделанное таким образом, еще и без добавления констант.

ShortcutManagerCompat.pushDynamicShortcut(context,
    ShortcutInfoCompat.Builder(context, id)
        // Настраиваем Shortcut
        .addCapabilityBinding("actions.intent.SEND_MESSAGE")
        .build()
)
Обновленный Share Sheet в Android 14
Обновленный Share Sheet в Android 14

Теперь в стандартном системном UI появится возможность добавить дополнительные действия в видео объекта ChooserAction, содержащий иконку, название и PendingIntent, который будет отправлен при выборе действия. Ограничений на количество собственных действий нет.

Также есть еще одно специальное действие, которое предназначено для правки отправляемого контента, тоже в виду ChooserActions.

Все созданные дополнительные действия надо добавить в Intent через специальные новые EXTRA. Как это сделать, вы можете увидеть ниже.

val intent: Intent = // Создаем Inent и кладем контент для шаринга

val modifyAction: ChooserAction = 
    ChooserAction.Builder(modifyIcon, "Modify Share", modifyActionIntent).build()
// Задаем действие для правки контент
intent.putExtra(EXTRA_CHOOSER_MODIFY_SHARE_ACTION, modifyAction)


// Дополнительный действия, которые можно сделать с контентом
val customActions: Array<ChooserAction> = arrayOf(
		ChooserAсtion.Builder(copyIcon, "Copy", copyIntent).build(),
		ChooserAсtion.Builder(albumIcon, "Create Album", createAlbumIntent).build(),
		ChooserAсtion.Builder(createLinkIcon, "Create link", createLinkIntent).build()
)
// Добавляем допонительные действия, которые можно сделать с контентом
inent.putExtra(Intent.EXTRA_CHOOSER_CUSTOM_ACTIONS, customActions)


// Создаем Intent для Share Sheet
val chooserIntent = Intent.createChooser(customActions, "Share 1 image")
// Запускаем Share Sheet
context.startActivity(chooserIntent)

Хотелось бы чтобы из коробки добавили какие-то стандартные действия, как это было в Android 13, но на момент выхода этой статьи я не смог найти никаких действий.

SCHEDULE_EXACT_ALARM запрещены по умолчанию

Для приложений с targetSdk 33+

В Android 12 появилось новое разрешение SCHEDULE_EXACT_ALARM для использования API AlarmManager, связанного с точным временем срабатывания будильников. В Android 13 появилось новое разрешение USE_EXACT_ALARMS. В Android 14 нового не добавили, но меняют работу SCHEDULE_EXACT_ALARM. Теперь оно не будет выдаваться всем приложениям с targetSdk 33 и выше. При обновлении устройства и восстановлении бэкапа разрешение будет выдано, а вот для новых установок - нет. В исключения попадают приложения, подписанные системным сертификатом и имеющие специальные привилегии, а также те приложения, для которых отключены оптимизации энергопотребления.

Тип Foreground Service становится обязательным

В Android 10 для всех Foreground Service появилась возможность объявить тип сервиса, которое указывает цель его запуска. В Android 14 становится обязательным указывать тип для всех Service, которые могут запускаться как Foreground, а в современном Android - это практически все случаи. Чтобы покрыть все варианты использования Foreground Service, добавили новые типы сервисов и каждый из них имеет специальное разрешение для объявления.

<!-- AndroidManifest.xml -->
<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <!-- Рзарешение для запуска Foreground Service -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />

    <!-- Рзарешение для запуска Foreground Service с типом dataSync -->
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_DATA_SYNC" />

    <application>
        <service
            android:name=".SyncService"
            android:foregroundServiceType="dataSync"
        />
    </application>
</manifest>

Нововведение позволит четко понимать, попадают ли операции, выполняемые в Service, под разрешенные категории. Система сможет лучше понимать, что делает приложение и не является ли это чем-то подозрительным. Google настоятельно рекомендует использовать WorkManager и другие специальные API, и в случае если они вам не подходят, реализовывать ForegroundService.

Google Play также займется проверкой Foreground Service на этапе загрузки сборок. Любое приложение с поддержкой Android 14 (targetSdk>= 34) должно подтвердить, что Service с объявленным типом задач нужен для работы приложения. В противном случае вам не опубликоваться в магазине

Для запуска Foreground Service, помимо добавления разрешения на запуск Foreground Service, надо будет добавлять разрешение на запуск сервиса определенного типа. Все эти разрешения имеют тип “normal”, т.е. разрешения выдаются автоматом при установке приложения и запрашивать их отдельно не придется. Помимо разрешения есть и другие требования, например, для Service по работе с камерой надо, чтобы в AndroidManifest было также разрешение CAMERA.

Тип

Разрешения

Цель

camera

FOREGROUND_SERVICE_CAMERA

Доступ к камере из фона, например, видеочаты

connectedDevice

FOREGROUND_SERVICE_CONNECTED_DEVICE

Коммуникация с устройствами по Bluetooth, NFC и другим каналам

dataSync

FOREGROUND_SERVICE_DATA_SYNC

Операции с процессингом данными на устройстве и сервером

health

FOREGROUND_SERVICE_HEALTH

Фитнес приложения

location

FOREGROUND_SERVICE_LOCATION

Навигация или предоставления доступа к местоположению

mediaPlayback

FOREGROUND_SERVICE_MEDIA_PLAYBACK

Проигрывание аудио и видео в фоне

mediaProjection

FOREGROUND_SERVICE_MEDIA_PROJECTION

Воспроизведение контента на внешнем дисплее (медиа, шаринг экрана)

microphone

FOREGROUND_SERVICE_MICROPHONE

Запись аудио с микрофона в фоне

phoneCall

FOREGROUND_SERVICE_PHONE_CALL

Приложения для звонков с использованием ConnectionService API

remoteMessaging

FOREGROUND_SERVICE_REMOTE_MESSAGING

Передача сообщений между устройствами

shortService

-

Высокоприоритетная короткая (до 3 минут) задача, которую необходимо закончить как можно быстрее

systemExempted

FOREGROUND_SERVICE_SYSTEM_EXEMPTED

Специальный тип для системных приложений и приложений с разрешением для работы с exact alarm API в AlarmManager

specialUse

FOREGROUND_SERVICE_SPECIAL_USE

Специальный тип, предназначенный для задач, которые не попали в остальные категории

Все типы связаны с каким-то конкретным сценарием использования за исключением трех:

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

  • Тип specialUse применяется в тех случаях, когда все другие типы Foreground Service вам не подошли. Его объявление, помимо типа и разрешения, требует указать причину использования этого типа в специальном свойстве в AndroidManifest. Если у вас есть идеи, какую операцию делать из всех стандартных типов, напишите о ней в комментариях.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
    <uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />

    <application>

        <service
            android:name=".SpecialService"
            android:foregroundServiceType="specialUse">
            <property
                android:name="android.app.PROPERTY_SPECIAL_USE_FGS_SUBTYPE"
                android:value="Reason to use special service type" />
        </service>
    </application>
</manifest>
  • Тип shortService предназначается для выполнения операций, которые не могут быть прерваны или отложены. Данный тип не требует дополнительных разрешений, но имеет несколько особенных свойств: ограничен во времени выполнения (не больше 3 минут), не могут запускать другие Foreground Service, не перезапускаются при остановке процесса приложения системой и др.

Время запуска Service отсчитывается с момента вызова startForeground() и ожидается, что будет закончено вызовом stopSelf() или stopForeground(). По истечению таймаута выполнения будет вызван новый callback метод в Service - onTimeot(), который предупреждает, что у вас осталась последняя возможность остановить Service. В противном случае происходит ANR, даже если приложение выполняет другие операции в Foreground Service. Отключение оптимизаций энергопотребления для приложения не влияет на ограничения по времени выполнения.

Возможно, продление жизни Short Foreground Service в случае, если приложение в текущий момент своей жизни удовлетворяет запуску Foreground Service, то повторный запуск Short Services приведет к увеличению времени выполнения Service на 3 минуты.

Ограничения для неявных Intent

Для приложения с поддержкой Android 14 (targetSdk 34+)

В Android 14 продолжаются форсирования лучших практик для предотвращения использования зловредами возможностей доступа к внутренним компонентам приложений. Теперь неявные intent будут доставляться только экспортированным компонентам приложения, для неэкспортированных - надо делать явные Intent.

<activity
    android:name=".AppActivity"
    android:exported="false">
    <intent-filter>
        <action android:name="dev.androidbroadcast.action.APP_ACTION" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>
// Выброситься исключение в случае targetSdk 34+
context.startActivity(Intent("dev.androidbroadcast.action.APP_ACTION"))

// Делаем явный Intent, указанием пакета приложения
val explicitIntent = 
    Intent("dev.androidbroadcast.action.APP_ACTION")
        // Указываем пакет приложения где находится компонент
				// В случае вашего приложения - пакет вашего приложения
        .setPackage(context.packageName)
context.startActivity(explicitIntent)

Вторым изменением в работе механизма Intent является то, что любой мутабельный PendingIntent с неявным Intent приведет к выбрасыванию исключения.

Изменения работы BroadcastReceiver

Помимо этого изменяются поведения Intent, которые доставляются в BroadcastRecevier, зарегистрированные из кода для всех приложений независимо от targetSdk. Все Intent, которые они могут получить, НЕ будут доставляться, когда приложение находится в закешированном состоянии, т.е. свернуто и пользователь какое-то время им не пользовался. Доставка произойдет, когда приложение станет снова активным. Помимо этого, при отправке нескольких одинаковых Intent может быть доставлен только один из них. BroadcastReceiver-ы, зарегистрированные в AndroidManifest, будут работать, как и прежде.

Также стала строже регистрация BroadcastReceiver из кода. Теперь надо указывать, экспортируемые они или нет по аналогии, как это делается для всех компонентов в AndroidManifest. При регистрации надо будет указать флаг RECEIVER_EXPORTED или RECEIVER_NOT_EXPORTED. Изменение работает только для приложений с поддержкой Android 14.

class DataReceiver: BroadcastReceiver() {

    override fun onReceive(context: Context, intent: Intent) {
        // Обарабатываем данные
    }
}

val intentFilter = IntentFilter() // настраиваем IntentFilter
context.registerReceiver(DataReceiver(), intentFilter, Context.RECEIVER_NOT_EXPORTED)

Обновления JobScheduler

JobScheduler обзавёлся возможностью запускать задачи для долгих передач данных между устройством и сервером. Такой тип задач получил название user-initiated data transfer job. Запуск такого типа Job может быть осуществлен, только когда приложение видно пользователю или в случаях, когда приложение может запустить Activity из фона.

Для запуска user-initieated data transfer job необходимо в AndroidManifest указать разрешение RUN_USER_INITIATED_JOBS и добавить Service, который расширяет класс JobService. При создании JobInfo надо вызвать методы setUserInitiated() и выставить значение в true. Обязательным условием запуска является показ системного уведомления, что нужно сделать при вызове onStartJob()

<manifest xmlns:android="http://schemas.android.com/apk/res/android">

    <uses-permission android:name="android.permission.RUN_USER_INITIATED_JOBS" />

    <application>
        <service
            android:name="dev.androidbroadcast.CustomTransferService"
            android:exported="false"
            android:permission="android.permission.BIND_JOB_SERVICE" />
    </application>
</manifest>
val networkRequest = NetworkRequest.Builder()
        .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)
        .addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED)
        // Дополнительные требования к сетевому запросу
        .build()

    val jobInfo = JobInfo.Builder(1, ComponentName(context, CustomTransferService::class.java))
        .setUserInitiated(true)
        .setRequiredNetwork(networkRequest)
        .setEstimatedNetworkBytes(ONE_GIGABYTE, ZERO_BYTES)
        // ...
        .build()

    val jobScheduler: JobScheduler = checkNotNull(context.getSystemService())
    jobScheduler.schedule(jobInfo)
class CustomTransferService : JobService() {

    override fun onStopJob(job: JobParameters) {
        if (job.jobId == JOB_ID) {
            // Показываем уведомление
            setNotification(job, NOTIF_ID, newNotification(), JobService.JOB_END_NOTIFICATION_POLICY_REMOVE)
            val success = doDataTransfer()
						jobFinished(job, wantsReschedule = !success)
            return false
        }
        error("Job isnn't handled")
    }
}

Для успешного старта понадобится выполнение всех условий запуска и наличие в системе ресурсов на её выполнение. Важным отличием user-initiated data transfer job является то, что на неё не распространяются квоты App Standby Buckets на запуск Job. При определенных условиях система всё также может остановить выполнение задачи, если посчитает это необходимым. Перед этим будет вызван метод onStopJob(), в котором вам рекомендуется сохранить текущий прогресс выполнения сетевой операции, чтобы после перезапуска не выполнять работу с начала, а доделать необходимую часть.

class CustomTransferService : JobService() {

    override fun onStopJob(job: JobParameters): Boolean {
        if (job.jobId == JOB_ID) {
            // Сохраняем прогресс передачи данных
            return true
        }
    }
}

Из API кажется, что пометка User Initiated может быть сделана для любого типа Job, но в документации по методу говорится, что в Android 14 он должен быть вызван только в комбинации с setRequiredNetwork() или setRequiredNetworkType(). Возможно, в будущих версиях Android мы увидим больше типов задач, которые могут быть инициированы пользователем и явно помечены.

На момент выхода Android 14 в Jetpack WorkManager, рекомендуемый Google к использованию вместо JobScheduler, пока явно не добавлял в API. Возможно, это случится в будущих релизах, либо изменение будет использоваться под капотом уже существующего API WorkManager.

Частичный доступ к фото и видео

По аналогии с iOS в Android делают частичный доступ к фото и видео. Теперь при запросе медиа из приложения пользователь будет видеть диалог, в котором будет предлагаться дать доступ ко всей медиа или только к отдельным фото/видео. Изменение будет работать для всех приложений, независимо от их targetSdk.

Слева - Android 14, справа - iOS 14,
Слева - Android 14, справа - iOS 14,

В Android 13 уже появились отдельные разрешения для доступа к фото и видео, а теперь появится еще одно дополнительное READ_MEDIA_VISUAL_USER_SELECTED, которое позволяет повторно запросить выбор к отдельным фото/видео. Новое разрешение должно использоваться в дополнение к уже существующим READ_MEDIA_IMAGES и READ_MEDIA_VIDEO, чтобы поддержать новое поведение. Его объявление означает, что вы поддерживаете из кода его повторный запрос.

<manifest xmlns:android="http://schemas.android.com/apk/res/android" />

    <!-- Разрешение на доступ к фото и видео из Android 13 !-->
    <uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
    <uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

    <!-- Доп. разрешение на повторный выбор медиа для доступа из Android 14 !-->
    <uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

</manifest>

Доступ к отдельным медиа будет выдаваться временно, а вот как надолго - в документации не описано. В случае если новое разрешение не объявлено, то отзываться частичный доступ будет сразу же при переходе приложения в фон или пользователь убивает приложение. Нечто подобное тому, как работает одноразовое разрешение. В связи с этим хранить состояние получения разрешения  READ_MEDIA_VISUAL_USER_SELECTED нельзя и нужно проверять его каждый раз.

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

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

Если вашему приложение нужен доступ к фото/видео при работе из фона, то настоятельно рекомендуется поддержать новое разрешение для дальнейшей корректной работы, иначе у вас регулярно может происходить отзыв этих разрешений на Android 14.

Улучшения для магазинов приложений

В Android 14 упрощает жизнь сторонним магазинам добавлением новых API в PackageInstaller. Начнем с ограничения на установку приложений. Сейчас она может произойти в любом момент при вызове соответствующего API. В Android U добавили Install Constraints API, которая позволяет задать условия, при которых можно выполнить установку

  • Приложение не видно пользователю

  • С приложением не происходит взаимодействий

  • Приложение видно, но не на переднем плане

  • Устройство не используется

  • Нет текущего телефонного звонка

PackageInstaller.InstallConstraints.Builder()
    // Приложение не видно пользователю
    .setAppNotForegroundRequired()
    // С приложением не происходит взаимодействий
    .setAppNotInteractingRequired()
    // Приложение видно, но не на переднем плане
    .setAppNotTopVisibleRequired()
    // Устройство не используется
    .setDeviceIdleRequired()
    // Нет текущего телефонного звонка
    .setNotInCallRequired()
    .build()

Затем можно проверить, удовлетворяют ли требованиям указанные приложения с помощью метода checkInstallConstraints()

val packageInstaller: PackageInstaller = //...
packageInstaller.checkInstallConstraints(
    packageNames = listOf("dev.androidbroadcast"),
    contraints,
    Executors.newSingleThreadExecutor()
) { result: PackageInstaller.InstallConstraintsResult ->
    if (result.areAllConstraintsSatisfied()) {
        // устанавливаем APK файлы
    }
}

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

packageInstaller.waitForInstallConstraints(
    listOf("dev.androidbroadcast"),
    contraints,
    intentSender,
    timeout = Duration.ofHours(1).toMillis()
)

Отложить обновление на момент, когда условия будут выполнены, можно с помощью метода commitSessionAfterInstallConstraintsAreMet()

packageInstaller.commitSessionAfterInstallConstraintsAreMet(
    sessionId,
    intentSender,
    contraints,
    timeout = Duration.ofHours(1).toMillis()
)

Появилась возможность перед установкой нескольких APK через PackageSession API получить разрешение пользователя на это. Нужно вызвать новый метод requestUserPreapproval(). Это позволяет удобно работать с App Bundle без необходимости многократного получения разрешения на установку отдельных APK. Таким образом, обновления из магазина нескольких приложений потребуют одного одобрения пользователя.

val sessionId: Int = packageInstaller.createSession(
    PackageInstaller.SessionParams(SessionParams.MODE_INHERIT_EXISTING)
)
val pendingIntent: PendingIntent = // ...

val session: Session = packageInstaller.openSession(sessionId)
// Вызываем системный диалог с запросом разрешения у пользователя
// intentSender определяет, куда будет отправляться результат
session.requestUserPreapproval(
    PreapprovalDetails.Builder()
        .setIcon(iconBitmap)
        .setLabel(message)
        .setLocale(locale)
        .build(),
    pendingIntent.intentSender
)
// Дожидаемся разрешения пользователя и после стартует выполнение сессии
session.commit(intentSender)

Для защиты наката приложений из другого магазина в API PackageInstaller появился метод setRequestUpdateOwnership(), который потребует от пользователя подтвердить, что он действительно хочет сменить приложение-установщик. Такая ситуация обычно происходит, когда вы установили приложения из одного приложения, например, Google Play, а пытаетесь обновить его из другого приложения-магазина. Из того что мне удалось выяснить, это предназначено для критичных пакетов: Google Play Services, Health Connect, Chrome WebView.

 Предупреждение при обновлении приложения
Предупреждение при обновлении приложения

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

Определение, когда пользователь делает скриншот

Для обнаружения скриншотов в Android 14 появляется специальное API в Activity, которое вызывает Callback после того, как скриншот был сделан. API может обнаружить только скриншоты, которые были сделаны пользователем на устройстве с помощью комбинации специальных клавиш. Если экран будет захвачен во время теста с помощью специальных команд или по ADB, то callback не сработает. Для работы API надо будет добавить в AndroidManifest разрешение DETECT_SCREEN_CAPTURE и затем регистрируем callback в onStart(), и не забываем убрать в onStop() либо можно воспользоваться Jetpack Lifecycle.

<manifest xmlns:android="http://schemas.android.com/apk/res/android">
    <uses-permission android:name="android.permission.DETECT_SCREEN_CAPTURE" />
</manifest>
class MainActivity : Activity() {

    private val mainExecutor = MainEcxector()
    
    private val screenshotCallback = ScreenCaptureCallback {
        // Был сделан скриншот
    }

    override fun onStart() {
        super.onStart()
        registerScreenCaptureCallback(mainExecutor, screenshotCallback)
    }

    override fun onStop() {
        super.onStop()
        unregisterScreenCaptureCallback(screenshotCallback)
    }
}

Отдельное разрешение для показа полноэкранных системных уведомлений

 Полноэкранное уведомление на примере будильника
Полноэкранное уведомление на примере будильника

В Android 11 появилась возможность показывать полноэкранные уведомления, которые будут видны на экране блокировки и при работе полноэкранных приложений. Такой формат показа предназначался для показа таких очень важных уведомлений, как выходящие звонки и будильники. Начиная с Android 14, получить существующее разрешение USE_FULL_SCREEN_INTENT смогут только те приложения, из категории для которых оно предназначалось. Следить за этим будет Google Play при модерации билдов. После обновления устройства на Android 14 все приложения, которые уже использует это разрешение, смогут делать это и дальше, но вот при загрузке новой сборки в Google Play придётся его убрать, если вы не подходите под политику.

Также надо не забывать, что пользователь всегда сможет отозвать разрешение, поэтому перед показом полноэкранного уведомления важно проверить доступность этой возможности с помощью вызова NotificationManager.canUseFullScreenIntent. Чтобы попросить пользователя выдать вам это разрешение, можно использовать новый Intent с ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT, который откроет экран, где пользователь сможет выдать разрешение.

val intent = Intent(Settings.ACTION_MANAGE_APP_USE_FULL_SCREEN_INTENT)
try {
     context.startActivity(intent)
} catch (e: ActivityNotFoundException) {
     // Activity не может быть запущено
}

Data Safety из Google Play в Android системе

В 2023 в Google Play стало обязательным заполнение данных об обеспечении безопасности данных, с которыми работает приложение. Теперь эти данные будут видны не только на странице приложения в магазине, но и показываться в системе. В Android 14 при запросе разрешения на доступ к местоположению в системном диалоге можно будет увидеть, для чего приложениям нужен достук к вашей локации и с кем этими данными делится сервис. Эта секция будет показываться только в том случае, если приложение шарит локацию со сторонними сервисами.

Data Safety в Android 14
Data Safety в Android 14

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

Уведомление об изменении Data Safety
Уведомление об изменении Data Safety

Изменение призвано сделать прозрачным шаринг данных для пользователя, а также приложения теперь не смогут изменить правила незаметно от пользователя. Будет хорошо получать такие уведомления касательно шаринга не только местоположения, но и всех других типов данных.

Foreground Service Samsung

Небольшое, но важное изменение - Google начала активно работать с вендорами, чтобы стандартизировать поведение работы приложения в фоне на оболочках разных производителей. Samsung уже заявила, что в One UI 6.0 на основе Android 14 будет гарантирована работа Foreground Services в соответствии с документацией и новой политикой. Пока остаётся только надеяться, что это не пустые обещания и ситуация будет меняться в лучшую сторону.

Прочее

В Android 14 произошло много изменений но часть их них мелких и заденет немногих, поэтому пройдемся по ним быстро:

  • При самостоятельной отправке приложениям PendingIntent теперь придется явно указывать, что его фоновая Activity может быть запущена. Для этого при создании PendingIntent надо передать Bundle ActivityOptions и вызвать метод setPendingIntentBackgroundActivityStartMode() с параметром MODE_BACKGROUND_ACTIVITY_START_ALLOWED

val options = ActivityOptions.makeBasic()
    .setPendingIntentBackgroundActivityStartMode(
         ActivityOptions.MODE_BACKGROUND_ACTIVITY_START_ALLOWED
    )

val pendingIntent = 
    PendingIntent.getActivity(context, REQUEST_CODE, intent, options)

pendingIntent.send()
  • При байдинге Service из активного приложения придется явно указывать, что фоновому приложению можно запускать Activity добавлением флага BIND_ALLOW_ACTIVITY_STARTS при вызове метода bindService()

bindService(intent, serviceConnection, Context.BIND_ALLOW_ACTIVITY_STARTS)
  • Для Path API добавили возможность получить итератор и пройтись по его сегментам.

// Создаём path
val path = Path().apply {
    moveTo(1.0f, 1.0f)
    lineTo(2.0f, 2.0f)
    close()
}

val pathIterator: PathIterator = path.pathIterator
for (segment in pathIterator) {
    println("segment: ${segment.verb}, ${segment.points}")
}

Также появилась возможность интерполяции. Это будет полезно в анимировании между двумя Path.

val interpolatedResult = Path()
if (path.isInterpolatable(otherPath)) {
    path.interpolate(otherPath, t = .5f, interpolatedResult)
}

Все возможности API из Android 14 доступны на версиях Android, начиная с 5.0, в библиотеки Jetpack Path

  • Android уведомления, связанные с Foreground Service или с пометкой ongoing, нельзя убрать. Изменения произошли с появлением Task Manager. В Android 14 практически все уведомления можно будет убрать. Ongoing уведомления не будут убираться с экрана блокировки или по нажатию кнопки “Удалить все”, а также уведомления для звонков и часть уведомлений от Android Enterprise.

  • Стали строже правила для запуска задач в фоне. Через пару секунд после перехода приложения в состояние cached любая фоновая работа запрещена, за исключением API, предназначенных для этого: WorkManager, JobScheduler или Foreground Service. Фоновая работа приложения становится недоступной на порядок быстрее, чем это происходит в Android 13.

  • В Android 14 продолжают добавлять возможность из последнего OpenJDK. В этот раз появились обновления из библиотеки и фичей языка Java 17: многострочные литералы, pattern matching в instanceof, sealed классы и др. Возможности будут перенесены на Android 12 и 13 благодаря модульной системе обновлений. Если кто из вас ждал этих возможностей, пишите в комментариях. Кажется, всех устраивает JDK 8 и Kotlin вокруг

  • Исправлена уязвимость с обходом Zip файлов

  • Все приложения с поддержкой Android 14, использующие динамическую загрузку кода, должны будут это делать только с файлами доступными для чтения

val jar = File("DYNAMICALLY_LOADED_FILE.jar")
// Делаем файл доступным только для чтения
jar.setReadOnly()
// Загружаем код
val cl = PathClassLoader(jar, parentClassLoader)
  • Все приложения на Android 14 теперь смогут останавливать фоновые процессы в рамках этого же приложения. При попытке остановки процесса другого приложения не произойдет ничего, а в LogCat появится сообщение

  • CredentialManager и HealthConnect стали системными сервисами в Android, но всё также рекомендуется использовать эти API через соответствующие Jetpack библиотеки

  • Стало возможно помечать View, чтобы ограничить их видимость только для Accessibility Service, которые нужны для помощи пользователям с ограниченными возможностями

view.setAccessibilityDataSensitive(
		View.ACCESSIBILITY_DATA_SENSITIVE_YES
)
  • Для устройств, которые выходят на Android 14, будет обязательна аппаратная поддержка кодека AV1 и Identity Credential, которая позволит хранить водительские права на устройствах.

  • Для приложений появилась новая роль - NOTES. Предназначено для приложений, которые используются для создания заметок и по умолчанию будут срабатывать при отправке Intent с действием CREATE_NOTE

  • Публичной частью Android SDK стали аннотации @CriticalNative и @FastNative для отметки native методов для ускорения работы JNI в ART

  • Полностью удалили поддержку Android Beam из Android SDK

  • Появилась поддержка передачи аудио по USB без потерь. Аудиофилы будут рады. Google работает с партнерами, чтобы эта возможность появилась на устройствах.

  • Появилось аппаратное ускорение буфера рендеринга в Canvas

  • Все устройства на Android 14 с чипами на ARM v9 должны будут выходить только с поддержкой 64-битных приложений

  • Cross-device SDK стало частью Android SDK

  • Появилась поддержка 10-битных HDR изображений, позволяющая сохранить больше информации о цветах и контрастности. Формат называется Ultra HDR. Google камера и Google Photos будут его поддерживать

  • Добавлены новые Mainline модули: Health Connect, модуль для управления фича флагами, Cronet, Device Lock Controller, Remote Key Provisioner и Root Certificate

  • Улучшена поддержка рукописного ввода. Фича будет использоваться на планшетах

  • Поддержка спутниковых звонков

  • Появился системный сервис VirtualDeviceManager для создания и управления виртуальными устройствами. Не путать с эмуляторами в Android Studio

Заключение

Android с каждым годом все меньше и меньше добавляет новых возможностей, а идет работа над улучшением Android SDK и более строгими правилами для работы со стандартными компонентами Android-системы. Много новинок API уже давно стали развиваться в рамках Jetpack, т.к. они не привязаны к версии Android и обновляются независимо. Сам же Android SDK именно стал мостом между ОС и Jetpack, а разработчики все больше и больше используют последнее, библиотеки Kotlin и другие сторонние решения.

Делитесь в комментариях своим мнением касательно новой версии Android, что вам понравилось, а что нет. Может даже вы расскажите про то, что я упустил.

Комментарии (26)


  1. CrashLogger
    08.09.2023 18:15
    +7

    >Android с каждым годом все меньше и меньше добавляет новых возможностей

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


    1. kirich1409 Автор
      08.09.2023 18:15
      -3

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


      1. Goron_Dekar
        08.09.2023 18:15
        +4

        Возмсожностей из коробки хватает всем

        Очень спорное утверждение.


        1. kirich1409 Автор
          08.09.2023 18:15
          +2

          У каждого свои требования, я озвучил свою точку зрения


          1. Goron_Dekar
            08.09.2023 18:15
            +3

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


            1. kirich1409 Автор
              08.09.2023 18:15

              Расскажите чего вам не хватает и что нашли в кастомных ромах?


              1. Goron_Dekar
                08.09.2023 18:15
                +8

                Мне?

                1) Возможности удалить неиспользуемый функционал, приводящий к потере заряда аккумулятора.

                2) Возможности иметь доступ к файловой системе телефона с компьютера и помещать/забирать файлы при подключении.

                3) Возможности запускать приложения, не подвергая свои чувствительные данные риску быть скомпрометированными (геопозицию, фото) приложениями, разработчикам которых нет доверия (государственные приложения, приложения отечественных банков). Без предоставления фэйковых данных часть функционала в приложений оказывалась недоступна.

                Увы, 3 пункт сейчас опять не решен. Был вынужден отказаться от использования таких приложений.

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

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


                1. fulcrum7
                  08.09.2023 18:15
                  -1

                  Почему вы доверяете сборке неизвестных авторов, а не сборке вендора ?


                  1. Goron_Dekar
                    08.09.2023 18:15
                    +1

                    Коротко: по опыту использования программных продуктов вендоров.

                    Развёрнуто: Вендор, не испытывая давления конкуренции за прошивку на своём телефоне, не занимается ни оптимизацией, ни соблюдениями интересов пользователя. У отдела, который пишет адварь для вендора, совсем другие задачи и KPI. Для них важны переходы по рекламным ссылкам, покупки новых устройств. Напротив же, "неизвестные авторы" (которые подчас гораздо более известны, чем вендор, особенно если читать твит/реддит в нужных местах) испытывают жестчайшую конкуренцию. Так как такие как вы постоянно задают подобные вопросы они регулярно публикуют релиз инфо, где подробно описывают, что делают с прошивкой. И часто предоставляют инструкции по собственноручной сборки фирмвари. Такой процесс разработки явно более прозрачный и доверительный, чем в "подвалах" Хуявея или Самсунга. Вишенкой на торте является рут и возможность отключить все сомнительные фичи.


                1. mitua1331
                  08.09.2023 18:15
                  +2

                  Дело в том, что именно этим google и занимается.

                  1. Ограничение работы в фоне / ограничения, которые рекомендуют удалять приложения, чтобы заряда хватало на подольше. Почитайте статью, гугл планомерно год за годом уничтожает сервисы, чтобы приложения в фоне не выполняли много работы (или прикладывали усилия, чтобы это использование минимизировать. И это именно то, что вы просите - только для всех пользователей сразу и незаметно для юзабилити.

                  2. Просто по шнуру подкючаешь и качаешь что нужно, лол) В чем претензия, не понятно.

                  3. Система пермишенов ровно это и делает. На современных девайсах очень сложно получить доступ к чему то в обход пермишенов, хотя и возможно. Вот гугл и идёт в этом направлении. Также на некоторых девайсах есть изолированные среды для запуска "опасных" или "недоверенных" приложений. Андроид это поддерживает. В чем проблема?

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


                  1. Goron_Dekar
                    08.09.2023 18:15

                    1. но сама гугловская активность всё ещё есть. Например, когда телефон теряет/подключает wifi гугл сервисы дёргуют gps. А мне этого не надо.

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

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

                    4. Ну если вы не понимаете, значит увы. Я пытался объяснить как мог. Гоню, как вы выразились, я не на гугл, а скорее на вендоров и их маркеты и лаунчеры. Вы же эту тему как-то обходите, как будто на телефон всегда можно поставить ванильный android.


                    1. anonimNO
                      08.09.2023 18:15

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

                      Можете объяснить в чем проблема? Я даже перепроверил, фильмы заливаются, mp3 с телеги доступны


                      1. Goron_Dekar
                        08.09.2023 18:15

                        Опять проверил. Без рута доступ к корню ФС с компа не добыть, а через те точки, которые шарятся, внутренняя папка кэша mp3 телеги не видна.


                      1. anonimNO
                        08.09.2023 18:15

                        Точно, для доступа к mp3 в телеге надо им предварительно сделать "Save to Music". А с фильмом какие проблемы?


                      1. Goron_Dekar
                        08.09.2023 18:15

                        Проблемы не с фильмом, а с идеологией ОС изолировать приложения. Был плеер фильмов, который мог играть только из "своей" папочки, которая также была не доступна без рута.

                        Теперь вам понятен этот кейс?


                      1. kirich1409 Автор
                        08.09.2023 18:15

                        Не проще ли вытащить эти песни из десктопного клиента Телеги?


                1. kirich1409 Автор
                  08.09.2023 18:15

                  зачем пользоваться смартфоном и компьютером тогда? Читается как "весь софт не из России - лучше и безопаснее"


                  1. Goron_Dekar
                    08.09.2023 18:15

                    Я ничго такого не говорил, похоже приплетаете.

                    Хотя, доля правды в вашем высказывании имеется. В свете последний событий предвзятое отношение к софту от государства с подобной политикой, как внешней так и внутренней и его крупных госкорпораций выглядит вполне понятным, вам не кажется?


              1. AlexanderS
                08.09.2023 18:15
                +1

                На моём новом телефоне очень сильно не хватает функционала TWRP. Удивительно, что за столько лет развития андроида ни гугл, ни один вендор так и не запили функционал полного снятия/развертывания образа разделов в оригинальном рекавери.


                1. Mike-M
                  08.09.2023 18:15

                  Точно. Поскольку встроенные средства бэкапирования сохраняют лишь небольшую часть настроек, тоже мечтаю о некоем аналоге Acronis True Image.


                1. kirich1409 Автор
                  08.09.2023 18:15

                  Богатый инструмент бэкапа у Samsung, но я им пользовался давно


      1. Dolios
        08.09.2023 18:15
        +5

        Возмсожностей из коробки хватает всем

        Отучаемся говорить за всю сеть (с) Фидо


  1. anonymous
    08.09.2023 18:15

    НЛО прилетело и опубликовало эту надпись здесь


  1. feivur
    08.09.2023 18:15

    Спасибо за разбор!


  1. Mike-M
    08.09.2023 18:15

    В статье масса полезной информации.
    И такая же масса ошибок правописания.
    Хотел поставить плюс карме, но пришлось ограничится плюсом только статье.


    1. kirich1409 Автор
      08.09.2023 18:15

      Буду работать в будущем над этим