Przyznaj częściowy dostęp do zdjęć i filmów

Android 14 wprowadza dostęp do wybranych zdjęć, który pozwala użytkownikom przyznawać aplikacjom dostęp do określonych 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 aplikacja jest kierowana na Androida 14 (poziom interfejsu API 34) lub nowszego. Jeśli nie korzystasz jeszcze z selektora zdjęć, zalecamy wdrożenie go w aplikacji, aby zapewnić spójne wybieranie obrazów i filmów oraz ochronę prywatności użytkowników bez konieczności proszenia o przyznanie uprawnień do przechowywania danych.

Jeśli korzystasz z własnego selektora galerii za pomocą uprawnień do przechowywania danych i chcesz zachować pełną kontrolę nad implementacją, dostosuj swoją implementację tak, aby wykorzystywała nowe uprawnienia READ_MEDIA_VISUAL_USER_SELECTED. Jeśli Twoja aplikacja nie korzysta z nowych uprawnień, system uruchomi ją w trybie zgodności.

Docelowy pakiet SDK Zadeklarowano READ_MEDIA_VISUAL_USER_SELECTED Dostęp do wybranych zdjęć włączony Zachowanie UX
Pakiet SDK 33 Nie Nie Nie dotyczy
Tak Tak Kontrolowane przez aplikację
Pakiet SDK 34 Nie Tak Kontrolowane przez system (działanie zgodne)
Tak Tak Kontrolowane przez aplikację

Utworzenie własnego selektora galerii wymaga kompleksowego programowania i obsługi, a Twoja aplikacja musi prosić o uprawnienia do przechowywania danych, aby uzyskać wyraźną zgodę użytkownika. Użytkownicy mogą odrzucać te żądania lub, jeśli Twoja aplikacja działa na urządzeniu z Androidem 14, a aplikacja jest kierowana na Androida 14 (poziom interfejsu API 34) lub nowszego, ograniczyć dostęp do wybranych multimediów. Poniższy obraz przedstawia przykład żądania uprawnień i wybierania multimediów za pomocą nowych opcji.

Format
Rysunek 1. Umożliwia ono użytkownikom wybranie konkretnych zdjęć i filmów, które chce udostępnić aplikacji, a nie tylko standardowe opcje przyznawania pełnego dostępu lub odmowy dostępu.

W tej sekcji opisujemy zalecane podejście do tworzenia własnego selektora galerii za pomocą MediaStore. Jeśli masz już selektor galerii w swojej aplikacji i chcesz zachować pełną kontrolę, możesz wykorzystać te przykłady, aby dostosować implementację. Jeśli nie zaktualizujesz implementacji, aby obsługiwała wybrane zdjęcia, system uruchomi Twoją aplikację w trybie zgodności.

Poproś 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 działania aplikacji w zależności 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 wymagają uprawnień

Od Androida 10 (poziom interfejsu API 29) aplikacje nie potrzebują już uprawnień do przechowywania danych, 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 dodaje tylko pliki do pamięci współdzielonej i nie wysyła zapytań dotyczących zdjęć ani filmów, przestań prosić o uprawnienia do przechowywania danych i ustaw w narzędziu AndroidManifest.xml wartość maxSdkVersion interfejsu API 28:

<!-- 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" />

Ponowne wybieranie multimediów

Dzięki funkcji dostępu do wybranych zdjęć na Androidzie 14 Twoja aplikacja powinna korzystać z nowych uprawnień READ_MEDIA_VISUAL_USER_SELECTED do kontrolowania ponownego wyboru multimediów oraz zaktualizować interfejs aplikacji, aby umożliwić użytkownikom przyznawanie jej dostępu do innego zestawu obrazów i filmów. Na tej ilustracji widać przykład prośby o uprawnienia i ponownego wyboru multimediów:

Format
Rysunek 2. W nowym oknie użytkownicy mogą też ponownie wybrać zdjęcia i filmy, które chcą udostępnić aplikacji.

Po otwarciu okna wyboru wyświetlane są zdjęcia, filmy lub oba te elementy w zależności od wymaganych uprawnień. Jeśli na przykład prosisz o uprawnienie READ_MEDIA_VIDEO bez uprawnienia READ_MEDIA_IMAGES, w interfejsie pojawią się tylko filmy, w których użytkownicy będą mogli wybrać pliki.

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

Możesz sprawdzić, czy aplikacja ma pełny, częściowy czy zabroniony dostęp do biblioteki zdjęć urządzenia, i odpowiednio zaktualizować interfejs. Poproś o te uprawnienia, gdy aplikacja wymaga 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ń

Gdy już upewnisz się, że masz odpowiednie uprawnienia dostępu do pamięci, możesz użyć usługi MediaStore, aby przesłać zapytanie do biblioteki urządzenia (ta sama metoda 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. Aby zapewnić wysoką wydajność w aplikacji gotowej do działania w środowisku produkcyjnym, użyj podziału na strony, np. w bibliotece stron docelowych.

Zapytanie dotyczące ostatniego wyboru

Aplikacje na Androida 15 i nowsze oraz Androida 14 z obsługą aktualizacji systemowych Google Play mogą wysyłać zapytania dotyczące ostatniego wyboru obrazów i filmów wybranych przez użytkownika w przypadku częściowego dostępu. Wystarczy, że włączysz 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)
}

Po uaktualnieniu urządzenia dostęp do zdjęć i filmów zostanie zachowany

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 Androida 13

Przyjrzyjmy się takiej sytuacji:

  1. Aplikacja jest zainstalowana na urządzeniu z Androidem 13.
  2. Użytkownik przyznał Twojej aplikacji uprawnienia READ_MEDIA_IMAGES i READ_MEDIA_VIDEO.
  3. Gdy aplikacja będzie nadal zainstalowana, urządzenie zostanie zaktualizowane do Androida 14.
  4. Twoja aplikacja zaczyna być kierowana na Androida 14 (poziom interfejsu 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 aplikacji uprawnienia READ_MEDIA_IMAGES i READ_MEDIA_VIDEO.

Uprawnienia Androida 12 i starszych

Przyjrzyjmy się takiej sytuacji:

  1. Aplikacja jest zainstalowana na urządzeniu z Androidem 13.
  2. użytkownik przyznał Twojej aplikacji uprawnienie READ_EXTERNAL_STORAGE lub WRITE_EXTERNAL_STORAGE.
  3. Gdy aplikacja będzie nadal zainstalowana, urządzenie zostanie zaktualizowane do Androida 14.
  4. Twoja aplikacja zaczyna być kierowana na Androida 14 (poziom interfejsu 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 przyzna też Twojej aplikacji uprawnienia READ_MEDIA_IMAGES i READ_MEDIA_VIDEO.

Sprawdzone metody

Ta sekcja zawiera kilka sprawdzonych metod korzystania z uprawnień READ_MEDIA_VISUAL_USER_SELECTED. Więcej informacji znajdziesz w naszych sprawdzonych metodach dotyczących uprawnień.

Nie zapisuj na stałe stanu uprawnień

Nie zapisuj stanu uprawnień na stałe, np. SharedPreferences czy DataStore. Stan pamięci masowej może nie być zsynchronizowany ze stanem rzeczywistym. Stan uprawnień może się zmienić po zresetowaniu uprawnień, hibernacji aplikacji, zainicjowanej przez użytkownika zmianie w ustawieniach aplikacji lub gdy aplikacja działa w tle. Zamiast tego sprawdź uprawnienia dostępu do pamięci za pomocą ContextCompat.checkSelfPermission().

Nie zakładaj pełnego dostępu 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 zapisuje dane MediaStore w pamięci podręcznej, gdy wysyłane jest zapytanie przy użyciu ContentResolver, pamięć podręczna może być nieaktualna.

  • Zawsze wysyłaj zapytania do MediaStore za pomocą ContentResolver, zamiast korzystać z pamięci podręcznej.
  • Zachowuj wyniki w pamięci, gdy aplikacja działa na pierwszym planie.
  • Odśwież wyniki, gdy aplikacja przejdzie przez cykl życia aplikacji onResume, ponieważ użytkownik może przejść z pełnego dostępu na częściowy dostęp w ustawieniach uprawnień.

Traktuj dostęp do identyfikatora URI jako tymczasowy

Jeśli użytkownik wybierze Wybierz zdjęcia i filmy w oknie uprawnień systemowych, dostęp aplikacji do wybranych zdjęć i filmów w końcu wygaśnie. Aplikacja powinna zawsze reagować na brak dostępu do elementów Uri bez względu na ich uprawnienia.

Filtruj do wyboru typy mediów według uprawnień

Okno wyboru jest wrażliwe na żądany typ uprawnień:

  • Żądanie tylko elementu READ_MEDIA_IMAGES powoduje wyświetlenie tylko obrazów do wyboru.
  • Żądanie tylko READ_MEDIA_VIDEO powoduje wyświetlenie tylko filmu, który można wybrać.
  • Żądanie READ_MEDIA_IMAGES i READ_MEDIA_VIDEO pokazuje całą bibliotekę zdjęć do wyboru.

W zależności od przypadków użycia aplikacji sprawdź, czy na pewno prosisz o odpowiednie uprawnienia, aby zadbać o wygodę 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 uniemożliwić użytkownikom wyświetlanie kilku okien dialogowych środowiska wykonawczego, poproś o uprawnienia READ_MEDIA_VISUAL_USER_SELECTED, ACCESS_MEDIA_LOCATION i uprawnienia do odczytu multimediów (READ_MEDIA_IMAGES, READ_MEDIA_VIDEO lub oba) 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ęć urządzenia jest pusta i powinna zezwalać mu na przyznawanie większej liczby plików.

Użytkownik może przejść z pełnego dostępu na częściowy dostęp w ustawieniach uprawnień bez przyznawania dostępu do niektórych wizualnych plików multimedialnych.

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. Po tym, jak aplikacja będzie działać w tle lub użytkownik ją wyłączy, system odmówi przyznania tych uprawnień. Działa to tak samo jak w przypadku innych jednorazowych uprawnień.

Zachowanie podczas ponownego wyboru mediów

Jeśli w przyszłości Twoja aplikacja będzie potrzebować dostępu do dodatkowych zdjęć i filmów, musisz ręcznie poprosić o uprawnienia READ_MEDIA_IMAGES lub READ_MEDIA_VIDEO. Proces przebiega tak samo jak w przypadku początkowej prośby o zgodę, zachęcając użytkowników do wybrania zdjęć i filmów (zobacz ilustrację 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, że nie przechowuje stanu uprawnień systemu ani nie odświeża zestawu wyświetlanych obrazów po zmianie uprawnień. Takie działanie może jednak nie być idealne w zależności od przypadku użycia aplikacji. Aby zapewnić użytkownikom jak najlepsze wrażenia, zalecamy zaimplementowanie selektora zdjęć lub dostosowanie selektora galerii w aplikacji w taki sposób, aby umożliwiał obsługę tego zachowania bezpośrednio za pomocą uprawnień READ_MEDIA_VISUAL_USER_SELECTED.