Розробка розширення 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 помре.
Збереження дозволу URI. Uri дійсна тільки поки activity відкрита (якщо не взяти persistedUriPermission). Якщо потрібно обробити файл у фоні — спочатку копіюємо в 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 тижнів. Вартість розраховується після аналізу типів контенту та інтеграції з основним додатком.







