Przyznawanie częściowego dostępu do zdjęć i filmów

Android 14 wprowadza dostęp do wybranych zdjęć, który pozwala użytkownikom przyznawać aplikacjom dostęp do konkretnych obrazów i filmów w bibliotece, zamiast przyznawać dostęp do wszystkich multimediów danego typu.

Ta zmiana jest dostępna tylko wtedy, gdy Twoja aplikacja jest kierowana na Androida 14 (poziom API 34) lub nowszego. Jeśli nie korzystasz jeszcze z selektora zdjęć, zalecamy wdrożenie go w aplikacji, aby zapewnić spójne działanie funkcji wybierania obrazów i filmów, a także zwiększyć prywatność użytkowników bez konieczności uzyskiwania uprawnień do przechowywania.

Jeśli masz własną aplikację do wybierania zdjęć, która korzysta z uprawnień dostępu do pamięci, i chcesz zachować pełną kontrolę nad jej implementacją, zmodyfikuj implementację, aby używała nowego uprawnienia READ_MEDIA_VISUAL_USER_SELECTED. Jeśli aplikacja nie korzysta z nowych uprawnień, system uruchamia ją w trybie zgodności.

Docelowy pakiet SDK Zadeklarowane READ_MEDIA_VISUAL_USER_SELECTED Dostęp do wybranych zdjęć włączony Zachowanie związane z UX
Pakiet SDK 33 Nie Nie Nie dotyczy
Tak Tak Kontrolowane przez aplikację
Pakiet SDK 34 Nie Tak Kontrolowane przez system (zachowanie zgodności)
Tak Tak Kontrolowane przez aplikację

Tworzenie własnego selektora galerii wymaga zaawansowanych działań deweloperskich i konserwacji, a aplikacja musi poprosić o uprawnienia dostępu do pamięci, aby uzyskać wyraźną zgodę użytkownika. Użytkownicy mogą odmówić udzielenia dostępu do tych mediów. Jeśli Twoja aplikacja działa na urządzeniu z Androidem 14 i jest kierowana na Androida 14 (poziom interfejsu API 34) lub nowszego, możesz ograniczyć dostęp do wybranych mediów. Poniższy obraz przedstawia przykład żądania uprawnień i wybierania multimediów za pomocą nowych opcji.

Format
Rysunek 1. Nowe okno dialogowe umożliwia użytkownikowi wybranie konkretnych zdjęć i filmów, które chce udostępnić aplikacji, oprócz zwykłych opcji przyznawania pełnego dostępu lub odmowy dostępu.

W tej sekcji pokazujemy zalecany sposób tworzenia własnego selektora galerii za pomocą MediaStore. Jeśli masz już w swojej aplikacji selektor galerii i chcesz zachować pełną kontrolę, możesz dostosować implementację, korzystając z tych przykładów. Jeśli nie zaktualizujesz implementacji, aby obsługiwać dostęp do wybranych zdjęć, system uruchomi aplikację w trybie zgodności.

Prośba o uprawnienia

Najpierw poproś o odpowiednie uprawnienia dostępu do pamięci w pliku manifestu Androida (w zależności od wersji systemu operacyjnego):

<!-- Devices running Android 12L (API level 32) or lower  -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="32" />

<!-- Devices running Android 13 (API level 33) or higher -->
<uses-permission android:name="android.permission.READ_MEDIA_IMAGES" />
<uses-permission android:name="android.permission.READ_MEDIA_VIDEO" />

<!-- To handle the reselection within the app on devices running Android 14
     or higher if your app targets Android 14 (API level 34) or higher.  -->
<uses-permission android:name="android.permission.READ_MEDIA_VISUAL_USER_SELECTED" />

Następnie poproś o odpowiednie uprawnienia w czasie wykonywania, które zależą też od wersji systemu operacyjnego:

// Register ActivityResult handler
val requestPermissions = registerForActivityResult(RequestMultiplePermissions()) { results ->
    // Handle permission requests results
    // See the permission example in the Android platform samples: https://github.com/android/platform-samples
}

// Permission request logic
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
    requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED))
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
    requestPermissions.launch(arrayOf(READ_MEDIA_IMAGES, READ_MEDIA_VIDEO))
} else {
    requestPermissions.launch(arrayOf(READ_EXTERNAL_STORAGE))
}

Niektóre aplikacje nie potrzebują uprawnień

Od Androida 10 (poziom API 29) aplikacje nie potrzebują już uprawnień do przechowywania, aby dodawać pliki do pamięci współdzielonej. Oznacza to, że aplikacje mogą dodawać obrazy do galerii, nagrywać filmy i zapisywać je w pamięci współdzielonej lub pobierać faktury w formacie PDF bez prośby o zgodę na przechowywanie danych. Jeśli Twoja aplikacja tylko dodaje pliki do współdzielonej pamięci i nie wysyła zapytań o obrazy ani filmy, nie proś o uprawnienia dostępu do pamięci i ustaw maxSdkVersion interfejsu API 28 w swoim AndroidManifest.xml:

<!-- No permission is needed to add files to shared storage on Android 10 (API level 29) or higher  -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" android:maxSdkVersion="28" />

Obsługa ponownego wyboru multimediów

W Androidzie 14 funkcja dostępu do wybranych zdjęć wymaga od aplikacji obsługi nowego uprawnienia READ_MEDIA_VISUAL_USER_SELECTED, które umożliwia kontrolowanie ponownego wyboru multimediów. Aplikacja powinna też zaktualizować interfejs, aby umożliwić użytkownikom przyznawanie dostępu do innego zestawu obrazów i filmów. Na ilustracji poniżej pokazano przykład żądania uprawnień i ponownie wybrania multimediów:

Format
Rysunek 2. Nowe okno dialogowe umożliwia też ponowne wybranie zdjęć i filmów, które użytkownik chce udostępnić Twojej aplikacji.

Gdy otworzysz okno wyboru, wyświetlą się zdjęcia, filmy lub oba te elementy, w zależności od wymaganych uprawnień. Jeśli na przykład żądasz uprawnienia READ_MEDIA_VIDEO bez uprawnienia READ_MEDIA_IMAGES, w interfejsie będą wyświetlane tylko filmy, które użytkownicy mogą wybrać.

// Allow the user to select only videos
requestPermissions.launch(arrayOf(READ_MEDIA_VIDEO, READ_MEDIA_VISUAL_USER_SELECTED))

Możesz sprawdzić, czy Twoja aplikacja ma pełny, częściowy czy odmówiony dostęp do biblioteki zdjęć na urządzeniu i odpowiednio zaktualizować interfejs. Proś o te uprawnienia, gdy aplikacja potrzebuje dostępu do pamięci, a nie podczas uruchamiania. Pamiętaj, że uprawnienia dostępu można zmienić między wywołaniami zwrotnymi cyklu życia aplikacji onStart i onResume, ponieważ użytkownik może zmienić dostęp w ustawieniach bez zamykania aplikacji.

if (
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU &&
    (
        ContextCompat.checkSelfPermission(context, READ_MEDIA_IMAGES) == PERMISSION_GRANTED ||
        ContextCompat.checkSelfPermission(context, READ_MEDIA_VIDEO) == PERMISSION_GRANTED
    )
) {
    // Full access on Android 13 (API level 33) or higher
} else if (
    Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE &&
    ContextCompat.checkSelfPermission(context, READ_MEDIA_VISUAL_USER_SELECTED) == PERMISSION_GRANTED
) {
    // Partial access on Android 14 (API level 34) or higher
}  else if (ContextCompat.checkSelfPermission(context, READ_EXTERNAL_STORAGE) == PERMISSION_GRANTED) {
    // Full access up to Android 12 (API level 32)
} else {
    // Access denied
}

Wysyłanie zapytania do biblioteki urządzeń

Po sprawdzeniu, czy masz dostęp do odpowiednich uprawnień do przechowywania danych, możesz korzystać z funkcji MediaStore, aby wysyłać zapytania do biblioteki urządzenia (to samo podejście działa niezależnie od tego, czy przyznany dostęp jest częściowy czy pełny):

data class Media(
    val uri: Uri,
    val name: String,
    val size: Long,
    val mimeType: String,
)

// Run the querying logic in a coroutine outside of the main thread to keep the app responsive.
// Keep in mind that this code snippet is querying only images of the shared storage.
suspend fun getImages(contentResolver: ContentResolver): List<Media> = withContext(Dispatchers.IO) {
    val projection = arrayOf(
        Images.Media._ID,
        Images.Media.DISPLAY_NAME,
        Images.Media.SIZE,
        Images.Media.MIME_TYPE,
    )

    val collectionUri = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
        // Query all the device storage volumes instead of the primary only
        Images.Media.getContentUri(MediaStore.VOLUME_EXTERNAL)
    } else {
        Images.Media.EXTERNAL_CONTENT_URI
    }

    val images = mutableListOf<Media>()

    contentResolver.query(
        collectionUri,
        projection,
        null,
        null,
        "${Images.Media.DATE_ADDED} DESC"
    )?.use { cursor ->
        val idColumn = cursor.getColumnIndexOrThrow(Images.Media._ID)
        val displayNameColumn = cursor.getColumnIndexOrThrow(Images.Media.DISPLAY_NAME)
        val sizeColumn = cursor.getColumnIndexOrThrow(Images.Media.SIZE)
        val mimeTypeColumn = cursor.getColumnIndexOrThrow(Images.Media.MIME_TYPE)

        while (cursor.moveToNext()) {
            val uri = ContentUris.withAppendedId(collectionUri, cursor.getLong(idColumn))
            val name = cursor.getString(displayNameColumn)
            val size = cursor.getLong(sizeColumn)
            val mimeType = cursor.getString(mimeTypeColumn)

            val image = Media(uri, name, size, mimeType)
            images.add(image)
        }
    }

    return@withContext images
}

Ten fragment kodu został uproszczony i pokazuje, jak korzystać z narzędzia MediaStore. W wersji aplikacji przeznaczonej do wdrożenia użyj podziału na strony, np. za pomocą biblioteki Paging, aby zapewnić dobrą wydajność.

Zapytanie o ostatni wybór

Aplikacje na Androidzie w wersji 15 lub nowszej oraz na Androidzie 14 z aktualizacjami systemowymi Google Play mogą zapytać o ostatni wybór obrazów i filmów dokonany przez użytkownika z częściowym dostępem. Aby to zrobić, muszą mieć włączoną opcję QUERY_ARG_LATEST_SELECTION_ONLY:

if (getExtensionVersion(Build.VERSION_CODES.U) >= 12) {
    val queryArgs = bundleOf(
        QUERY_ARG_SQL_SORT_ORDER to "${Images.Media.DATE_ADDED} DESC"
        QUERY_ARG_LATEST_SELECTION_ONLY to true
    )

    contentResolver.query(collectionUri, projection, queryArgs, null)
}

Dostęp do zdjęć i filmów jest zachowany podczas aktualizacji urządzenia

Jeśli Twoja aplikacja jest zainstalowana na urządzeniu, którego wersja Androida zostanie uaktualniona z wcześniejszej wersji do Androida 14, system zachowa pełny dostęp do zdjęć i filmów użytkownika, a także automatycznie przyzna Twojej aplikacji niektóre uprawnienia. Dokładne działanie zależy od zestawu uprawnień, które aplikacja otrzymała przed aktualizacją do Androida 14.

Uprawnienia w Androidzie 13

Rozważ tę sytuację:

  1. Aplikacja jest zainstalowana na urządzeniu z Androidem 13.
  2. Użytkownik przyznał aplikacji uprawnienia READ_MEDIA_IMAGESREAD_MEDIA_VIDEO.
  3. Następnie urządzenie zostanie zaktualizowane do Androida w wersji 14, gdy aplikacja jest nadal zainstalowana.
  4. Twoja aplikacja jest kierowana na Androida 14 (poziom API 34) lub nowszego.

W takim przypadku aplikacja nadal będzie miała pełny dostęp do zdjęć i filmów użytkownika. System automatycznie zachowuje też uprawnienia READ_MEDIA_IMAGESREAD_MEDIA_VIDEO przyznawane Twojej aplikacji.

Uprawnienia Androida 12 i starszych

Rozważ tę sytuację:

  1. Aplikacja jest zainstalowana na urządzeniu z Androidem 13.
  2. Użytkownik przyznał aplikacji uprawnienia READ_EXTERNAL_STORAGE lub WRITE_EXTERNAL_STORAGE.
  3. Następnie urządzenie zostanie zaktualizowane do Androida w wersji 14, gdy aplikacja jest nadal zainstalowana.
  4. Twoja aplikacja jest kierowana na Androida 14 (poziom API 34) lub nowszego.

W takim przypadku aplikacja nadal będzie miała pełny dostęp do zdjęć i filmów użytkownika. System automatycznie przyznaje też aplikacji uprawnienia READ_MEDIA_IMAGESREAD_MEDIA_VIDEO.

Sprawdzone metody

W tej sekcji znajdziesz kilka sprawdzonych metod korzystania z uprawnienia READ_MEDIA_VISUAL_USER_SELECTED. Więcej informacji znajdziesz w artykule Sprawdzone metody dotyczące uprawnień.

Nie zapisuj na stałe stanu uprawnień

Nie zapisuj stanu uprawnień na stałe, np. SharedPreferences czy DataStore. Zapisany stan może nie być zsynchronizowany z rzeczywistym stanem. Stan uprawnień może się zmienić po zresetowaniu uprawnień, hibernacji aplikacji, zmianie ustawień aplikacji przez użytkownika lub gdy aplikacja przechodzi do trybu uśpienia. Zamiast tego sprawdź uprawnienia dostępu do pamięci za pomocą ContextCompat.checkSelfPermission().

Nie zakładaj, że masz pełny dostęp do zdjęć i filmów

Ze względu na zmiany wprowadzone w Androidzie 14 Twoja aplikacja może mieć tylko częściowy dostęp do biblioteki zdjęć na urządzeniu. Jeśli aplikacja przechowuje w pamięci podręcznej dane MediaStore, gdy jest zapytanie wysyłane za pomocą ContentResolver, pamięć podręczna może nie być aktualna.

  • Zawsze wysyłaj zapytanie do MediaStore, używając parametru ContentResolver, zamiast polegać na przechowywanej pamięci podręcznej.
  • przechowywać wyniki w pamięci, gdy aplikacja działa na pierwszym planie.
  • Odświeżaj wyniki, gdy aplikacja przechodzi przez cykl życia onResume, ponieważ użytkownik może w ustawieniach uprawnień przełączyć się z pełnego na częściowy dostęp.

Traktowanie dostępu do identyfikatora URI jako tymczasowego

Jeśli użytkownik w oknie uprawnień systemowych wybierze Wybierz zdjęcia i filmy, dostęp aplikacji do wybranych zdjęć i filmów wygaśnie. Aplikacja powinna zawsze obsługiwać przypadek braku dostępu do Uri, niezależnie od autorytetu.

Filtrowanie dostępnych typów multimediów według uprawnień

Okno wyboru zależy od żądanego typu uprawnienia:

  • Przesyłanie tylko READ_MEDIA_IMAGES powoduje wyświetlenie tylko obrazów, które można wybrać.
  • Wybranie tylko opcji READ_MEDIA_VIDEO powoduje, że można wybrać tylko film.
  • Żądanie READ_MEDIA_IMAGES i READ_MEDIA_VIDEO pokazuje całą bibliotekę zdjęć do wyboru.

W zależności od przypadków użycia aplikacji należy poprosić o odpowiednie uprawnienia, aby nie pogarszać wrażeń użytkowników. Jeśli funkcja oczekuje, że tylko filmy zostaną wybrane, poproś o to jedynie READ_MEDIA_VIDEO.

Poproś o uprawnienia w jednej operacji

Aby zapobiec wyświetlaniu użytkownikom wielu okien dialogowych systemu w czasie działania, poproś o uprawnienia READ_MEDIA_VISUAL_USER_SELECTED, ACCESS_MEDIA_LOCATION oraz „Czytaj media” (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO lub oba te uprawnienia) w ramach jednej operacji.

Zezwalaj użytkownikom na zarządzanie swoim wyborem

Gdy użytkownik wybierze tryb częściowego dostępu, aplikacja nie powinna zakładać, że biblioteka zdjęć na urządzeniu jest pusta. Powinna umożliwić użytkownikowi przyznanie dostępu do większej liczby plików.

Użytkownik może zmienić pełny dostęp na częściowy w ustawieniach uprawnień, nie przyznając dostępu do niektórych plików multimediów.

Tryb zgodności

Jeśli korzystasz z własnego selektora galerii za pomocą uprawnień do przechowywania, ale nie dostosowujesz aplikacji do nowego uprawnienia READ_MEDIA_VISUAL_USER_SELECTED, system uruchomi ją w trybie zgodności za każdym razem, gdy użytkownik będzie musiał wybrać multimedia.

Zachowanie podczas początkowego wyboru multimediów

Jeśli podczas początkowego wyboru użytkownik wybierze opcję „Wybierz zdjęcia i filmy” (zobacz ilustrację 1), w trakcie sesji aplikacji zostaną przyznane uprawnienia READ_MEDIA_IMAGES i READ_MEDIA_VIDEO, czyli tymczasowe i tymczasowy dostęp do zdjęć i filmów wybranych przez użytkownika. Gdy aplikacja przejdzie do działania w tle lub użytkownik ją zamknie, system ostatecznie odmówi udzielenia tych uprawnień. Działania te są takie same jak w przypadku innych jednorazowych uprawnień.

Działanie podczas ponownego wyboru multimediów

Jeśli Twoja aplikacja później będzie potrzebować dostępu do dodatkowych zdjęć i filmów, musisz ręcznie poprosić o uprawnienie READ_MEDIA_IMAGES lub READ_MEDIA_VIDEO. System postępuje tak samo jak w przypadku początkowego żądania uprawnień, prosząc użytkowników o wybranie zdjęć i filmów (patrz rycina 2).

Jeśli aplikacja jest zgodna ze sprawdzonymi metodami dotyczącymi uprawnień, ta zmiana nie powinna zepsuć aplikacji. Zwłaszcza wtedy, gdy aplikacja nie zakłada, że dostęp za pomocą identyfikatora URI jest zachowywany, nie przechowuje stanu uprawnień systemu ani nie odświeża zestawu wyświetlanych obrazów po zmianie uprawnień. W zależności od zastosowania aplikacji takie działanie może jednak nie być optymalne. Aby zapewnić użytkownikom jak najlepsze wrażenia, zalecamy wdrożenie selektora zdjęć lub dostosowanie selektora galerii aplikacji, aby obsługiwać to zachowanie bezpośrednio za pomocą uprawnienia READ_MEDIA_VISUAL_USER_SELECTED.