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 wybieranie obrazów i filmów, a także ochronę prywatności użytkowników bez konieczności proszenia o przyznanie uprawnień do przechowywania danych.

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 Wybrany dostęp do 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. Na poniższym obrazie pokazano przykład prośby o przyznanie uprawnień i wyboru 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.

Poproś o uprawnienia

Najpierw w pliku manifestu Androida poproś o odpowiednie uprawnienia do przechowywania danych 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 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 wspólnej pamięci masowej lub pobierać faktury w formacie PDF bez konieczności żądania uprawnień do pamięci masowej. 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 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 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 między wywołaniami obsługi cyklu życia aplikacji onStartonResume można zmienić przyznane uprawnienia, ponieważ użytkownik może zmienić uprawnienia 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 zapytań do biblioteki urządzenia

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 jest uproszczony, aby pokazać, jak działa funkcja 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 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)
}

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

Jeśli Twoja aplikacja jest zainstalowana na urządzeniu, które zostało zaktualizowane z wersji Androida na Androida 14, system zachowuje pełny dostęp do zdjęć i filmów użytkownika oraz automatycznie przyznaje 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. Gdy aplikacja będzie nadal zainstalowana, urządzenie zostanie zaktualizowane do Androida 14.
  4. Twoja aplikacja jest kierowana na Androida 14 (poziom API 34) lub nowszego.

W takim przypadku aplikacja nadal ma pełny dostęp do zdjęć i filmów użytkownika. System automatycznie przyznaje aplikacji uprawnienia READ_MEDIA_IMAGES i READ_MEDIA_VIDEO.

Uprawnienia w Androidzie 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 ma 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

W tej sekcji znajdziesz kilka sprawdzonych metod korzystania z uprawnienia READ_MEDIA_VISUAL_USER_SELECTED. Więcej informacji znajdziesz w naszych sprawdzonych metodach dotyczących uprawnień.

Nie przechowuj stanu uprawnień na stałe

Nie przechowuj stanu uprawnień w sposób trwały, w tym za pomocą SharedPreferences ani DataStore. Stan pamięci masowej może nie być zsynchronizowany ze stanem rzeczywistym. 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 do przechowywania danych, korzystając z 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 zapisuje w pamięci podręcznej dane MediaStore, 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.
  • przechowywać 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ń.

Traktowanie dostępu do identyfikatora URI jako tymczasowego

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.

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

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

  • Żądanie tylko elementu READ_MEDIA_IMAGES powoduje wyświetlenie tylko obrazów do wyboru.
  • 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 wymaga tylko wybranych filmów, poproś o wybranie tylko 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.

Zezwalanie użytkownikom na zarządzanie ich 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 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 używasz własnych selektorów galerii z uprawnieniami dostępu do pamięci, ale nie dostosowałeś(-aś) aplikacji do korzystania z nowego uprawnienia READ_MEDIA_VISUAL_USER_SELECTED, system będzie uruchamiać aplikację w trybie zgodności za każdym razem, gdy użytkownik będzie musiał wybrać lub ponownie wybrać plik multimedialny.

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łania te są takie same jak w przypadku innych jednorazowych uprawnień.

Działanie podczas ponownego wyboru multimedió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 Twoja aplikacja jest zgodna ze sprawdzonymi metodami dotyczącymi uprawnień, ta zmiana nie powinna spowodować jej nieprawidłowego działania. Jest to szczególnie ważne, jeśli aplikacja nie zakłada, że dostęp do identyfikatora URI jest zachowany, nie przechowuje stanu uprawnień systemowych 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.