Разработка расширения для Share Sheet (Android)
В Android нет «расширения» как отдельного таргета — приложение регистрирует обычную Activity как получателя Intent через intent-filter в манифесте. Когда пользователь делится контентом из любого приложения, система показывает список доступных получателей. Ваше приложение в этом списке.
Intent filter и MIME-типы
Регистрация в AndroidManifest.xml:
<activity android:name=".ShareReceiverActivity">
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
Отдельный <intent-filter> на каждый тип — это важно. Один фильтр с несколькими <data> не всегда работает ожидаемо из-за особенностей Intent matching.
Получение данных
В ShareReceiverActivity.onCreate() проверяем action и извлекаем данные:
when (intent.action) {
Intent.ACTION_SEND -> {
if (intent.type?.startsWith("image/") == true) {
val imageUri = intent.getParcelableExtra<Uri>(Intent.EXTRA_STREAM)
// URI нужно обработать через ContentResolver
} else if (intent.type == "text/plain") {
val sharedText = intent.getStringExtra(Intent.EXTRA_TEXT)
}
}
Intent.ACTION_SEND_MULTIPLE -> {
val imageUris = intent.getParcelableArrayListExtra<Uri>(Intent.EXTRA_STREAM)
}
}
URI и ContentResolver. Это главная точка боли. Uri из EXTRA_STREAM — это content:// URI от приложения-отправителя. Читать его нужно через contentResolver.openInputStream(uri), а не через File API. На Android 10+ прямое чтение через /data/... пути заблокировано scope storage. Копируем файл в собственный storage до того, как activity умрёт.
Retention URI permission. Uri действителен только пока activity открыта (если не взять persistedUriPermission). Если нужно обработать файл в background — сначала копируем в app-specific storage:
val inputStream = contentResolver.openInputStream(sourceUri)
val destFile = File(cacheDir, "shared_${System.currentTimeMillis()}.jpg")
inputStream?.use { it.copyTo(destFile.outputStream()) }
// Теперь destFile живёт независимо от исходного Uri
Direct Share — персонализированные получатели
С Android 10 можно показывать конкретных получателей прямо в шер-меню — например, «Поделиться с Иваном» с аватаркой. Реализуется через ChooserTargetService (deprecated) или новый ShortcutManager.pushDynamicShortcut() с setLongLived(true) и категорией SHORTCUT_CATEGORY_CONVERSATION.
Это отдельная работа: нужно публиковать dynamic shortcuts для каждого контакта/получателя, обновлять их при изменениях, обрабатывать их в ShortcutManagerCompat. Если не сделать — в шер-меню будет просто иконка приложения без персонализации.
Share как точка входа в приложение
Если приложение ещё не запущено — ShareReceiverActivity запускает его с нуля. Нужно учесть: задача (task stack) создаётся заново, back-стек пустой. После обработки шера нажатие Back должно вести в home, а не в предыдущее приложение. Настраиваем android:taskAffinity и android:launchMode правильно для этой activity.
Сроки
Базовое Share Extension (текст + изображения, простой UI): 2–3 недели. С Direct Share, фоновой обработкой файлов, синхронизацией с сервером: 4–6 недель. Стоимость рассчитывается после анализа типов контента и интеграции с основным приложением.







