Dostawca multimediów w chmurze udostępnia dodatkowe treści multimedialne w chmurze systemowi Android
. Użytkownicy mogą wybierać zdjęcia lub filmy przesłane przez
dostawcą multimediów w chmurze, gdy aplikacja używa ACTION_PICK_IMAGES
lub
ACTION_GET_CONTENT
, aby zażądać plików multimedialnych od użytkownika. Plik multimedialny w chmurze
może również podać informacje o albumach, które można przeglądać w
Selektor zdjęć na Androidzie.
Zanim zaczniesz
Zanim zaczniesz tworzyć swoją chmurę, weź pod uwagę te kwestie dostawcy multimediów.
Warunki
Android prowadzi program pilotażowy, który umożliwia aplikacjom nominowanym przez OEM jako chmurę dostawców multimediów. W programie mogą uczestniczyć tylko aplikacje nominowane przez producentów OEM ten program, aby zostać dostawcą multimediów w chmurze na Androida. Każdy OEM może wskazać maksymalnie 3 aplikacje. Po zatwierdzeniu aplikacje będą dostępne jako: dostawców usług multimedialnych w chmurze na dowolnym urządzeniu GMS z systemem Android, na którym Zainstalowano.
Android prowadzi po stronie serwera listę wszystkich kwalifikujących się dostawców chmury. Każdy OEM mogą wybrać domyślnego dostawcę chmury za pomocą konfigurowanej nakładki. Nominacja muszą spełniać wszystkie wymagania techniczne i przejść wszystkie testy jakości. Aby się uczyć więcej informacji o procesie pilotażowym programu pilotażowego dla dostawców mediów w chmurze OEM wymagań, wypełnij formularz zapytania.
Określ, czy chcesz utworzyć dostawcę multimediów w chmurze
Dostawcy multimediów w chmurze to aplikacje lub usługi, które są do tworzenia kopii zapasowych i pobierania zdjęć oraz filmów z chmury. Jeśli aplikacja zawiera bibliotekę przydatnych treści, ale nie jest zwykle wykorzystywana rozwiązanie do przechowywania zdjęć, warto rozważyć utworzenie dostawcy dokumentów .
1 aktywny dostawca chmury na profil
Dla każdego urządzenia Android może istnieć tylko jeden aktywny dostawca multimediów w chmurze jednocześnie profil. Użytkownicy mogą usunąć lub zmienić wybranego dostawcę usług multimedialnych w chmurze w dowolnym momencie w ustawieniach selektora zdjęć.
Domyślnie selektor zdjęć na Androidzie spróbuje wybrać dostawcę usług w chmurze automatycznie.
- Jeśli na urządzeniu jest tylko jeden kwalifikujący się dostawca chmury, ta aplikacja zostanie automatycznie wybrane jako obecny dostawca.
Jeśli na urządzeniu jest więcej niż 1 kwalifikujący się dostawca usług w chmurze i jeden z tych dostawców: zgodne z domyślnymi ustawieniami OEM, zostanie wybrana aplikacja wybrana przez OEM.
Jeśli na urządzeniu jest więcej niż jeden kwalifikujący się dostawca chmury, a żaden z są zgodne z domyślnym ustawieniem OEM, więc nie zostanie wybrana żadna aplikacja.
Utwórz dostawcę multimediów w chmurze
Poniższy diagram przedstawia sekwencję zdarzeń zarówno przed, jak i w trakcie
sesja wyboru zdjęć pomiędzy aplikacją na Androida, selektorem zdjęć na Androidzie,
urządzenia lokalnego: MediaProvider
i CloudMediaProvider
.
- System inicjuje preferowanego dostawcę chmury użytkownika i okresowo synchronizuje metadane multimediów z backendem selektora zdjęć na Androidzie.
- Gdy aplikacja na Androida uruchamia selektor zdjęć, przed wyświetleniem scalonych zdjęć lokalnych lub siatkę elementów w chmurze, selektor zdjęć uwzględnia czas oczekiwania przyrostowej synchronizacji z dostawcą usług w chmurze, aby wyniki były jak najbardziej aktualne jak to tylko możliwe. Po otrzymaniu odpowiedzi lub upłynięciu terminu w siatce selektora zdjęć są teraz widoczne wszystkie dostępne zdjęcia, łączące lokalnie na urządzeniu z tymi zsynchronizowanymi z danymi z chmury.
- Gdy użytkownik przewija ekran, selektor zdjęć pobiera miniatury multimediów z dostawcę multimediów w chmurze do wyświetlenia w interfejsie.
- Gdy użytkownik zakończy sesję, a wyniki obejmują plik multimedialny w chmurze. wybiera deskryptory plików dla treści, generuje URI i przyznaje dostęp do pliku aplikacji wywołującej.
- Aplikacja może teraz otwierać identyfikator URI i ma dostęp tylko do odczytu do multimediów treści. Domyślnie poufne metadane są pomijane. Selektor zdjęć wykorzystuje system plików FUSE do koordynowania wymiany danych między Aplikacja na Androida i dostawca multimediów w chmurze.
Typowe problemy
Oto kilka ważnych kwestii, o których warto pamiętać, rozważając implementacja:
Unikanie duplikatów plików
Selektor zdjęć na Androidzie nie ma możliwości sprawdzenia stanu multimediów w chmurze,
funkcja CloudMediaProvider
musi zawierać MEDIA_STORE_URI
w miejscu kursora
wiersza dowolnego pliku, który istnieje zarówno w chmurze, jak i na urządzeniu lokalnym, lub
użytkownik zobaczy duplikaty plików w selektorze zdjęć.
Optymalizowanie rozmiarów obrazów na potrzeby wyświetlania podglądu
Bardzo ważne jest, aby plik zwrócony z usługi onOpenPreview
nie był pełny
obrazu w rozdzielczości i jest zgodny z żądanym Size
. Zbyt duży obraz
Wczyta się to w interfejsie i zbyt mały obraz może ulec pikselizacji lub
nie są rozmyte w zależności od rozmiaru ekranu urządzenia.
Uchwyt w prawidłowej orientacji
Jeśli miniatury zwrócone w onOpenPreview
nie zawierają danych EXIF,
powinny być zwracane w prawidłowej orientacji, aby uniknąć obracania miniatur.
nie są prawidłowo w siatce podglądu.
Zapobieganie nieautoryzowanemu dostępowi
Przed zwróceniem danych do usługi sprawdź MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION
elementu wywołującego z komponentu ContentProvider. Dzięki temu nieautoryzowane aplikacje nie będą
dostępu do danych w chmurze.
Klasa CloudMediaProvider
Źródło: android.content.ContentProvider
, CloudMediaProvider
class obejmuje metody takie jak te pokazane w tym przykładzie:
Kotlin
abstract class CloudMediaProvider : ContentProvider() {
@NonNull
abstract override fun onGetMediaCollectionInfo(@NonNull bundle: Bundle): Bundle
@NonNull
override fun onQueryAlbums(@NonNull bundle: Bundle): Cursor = TODO("Implement onQueryAlbums")
@NonNull
abstract override fun onQueryDeletedMedia(@NonNull bundle: Bundle): Cursor
@NonNull
abstract override fun onQueryMedia(@NonNull bundle: Bundle): Cursor
@NonNull
abstract override fun onOpenMedia(
@NonNull string: String,
@Nullable bundle: Bundle?,
@Nullable cancellationSignal: CancellationSignal?
): ParcelFileDescriptor
@NonNull
abstract override fun onOpenPreview(
@NonNull string: String,
@NonNull point: Point,
@Nullable bundle: Bundle?,
@Nullable cancellationSignal: CancellationSignal?
): AssetFileDescriptor
@Nullable
override fun onCreateCloudMediaSurfaceController(
@NonNull bundle: Bundle,
@NonNull callback: CloudMediaSurfaceStateChangedCallback
): CloudMediaSurfaceController? = null
}
Java
public abstract class CloudMediaProvider extends android.content.ContentProvider {
@NonNull
public abstract android.os.Bundle onGetMediaCollectionInfo(@NonNull android.os.Bundle);
@NonNull
public android.database.Cursor onQueryAlbums(@NonNull android.os.Bundle);
@NonNull
public abstract android.database.Cursor onQueryDeletedMedia(@NonNull android.os.Bundle);
@NonNull
public abstract android.database.Cursor onQueryMedia(@NonNull android.os.Bundle);
@NonNull
public abstract android.os.ParcelFileDescriptor onOpenMedia(@NonNull String, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
@NonNull
public abstract android.content.res.AssetFileDescriptor onOpenPreview(@NonNull String, @NonNull android.graphics.Point, @Nullable android.os.Bundle, @Nullable android.os.CancellationSignal) throws java.io.FileNotFoundException;
@Nullable
public android.provider.CloudMediaProvider.CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull android.os.Bundle, @NonNull android.provider.CloudMediaProvider.CloudMediaSurfaceStateChangedCallback);
}
Klasa CloudMediaProviderContract
Oprócz podstawowej klasy implementacji CloudMediaProvider
funkcja
Selektor zdjęć na Androidzie zawiera klasę CloudMediaProviderContract
.
Te zajęcia przedstawiają interoperacyjność selektora zdjęć z chmurą
dostawcy multimediów, który obejmuje takie aspekty jak MediaCollectionInfo
,
synchronizacji, oczekiwanych kolumn Cursor
i Bundle
dodatków.
Kotlin
object CloudMediaProviderContract {
const val EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID"
const val EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED"
const val EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID"
const val EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE"
const val EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN"
const val EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL"
const val EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED"
const val EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION"
const val MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS"
const val PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER"
object MediaColumns {
const val DATE_TAKEN_MILLIS = "date_taken_millis"
const val DURATION_MILLIS = "duration_millis"
const val HEIGHT = "height"
const val ID = "id"
const val IS_FAVORITE = "is_favorite"
const val MEDIA_STORE_URI = "media_store_uri"
const val MIME_TYPE = "mime_type"
const val ORIENTATION = "orientation"
const val SIZE_BYTES = "size_bytes"
const val STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension"
const val STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3 // 0x3
const val STANDARD_MIME_TYPE_EXTENSION_GIF = 1 // 0x1
const val STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2 // 0x2
const val STANDARD_MIME_TYPE_EXTENSION_NONE = 0 // 0x0
const val SYNC_GENERATION = "sync_generation"
const val WIDTH = "width"
}
object AlbumColumns {
const val DATE_TAKEN_MILLIS = "date_taken_millis"
const val DISPLAY_NAME = "display_name"
const val ID = "id"
const val MEDIA_COUNT = "album_media_count"
const val MEDIA_COVER_ID = "album_media_cover_id"
}
object MediaCollectionInfo {
const val ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent"
const val ACCOUNT_NAME = "account_name"
const val LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation"
const val MEDIA_COLLECTION_ID = "media_collection_id"
}
}
Java
public final class CloudMediaProviderContract {
public static final String EXTRA_ALBUM_ID = "android.provider.extra.ALBUM_ID";
public static final String EXTRA_LOOPING_PLAYBACK_ENABLED = "android.provider.extra.LOOPING_PLAYBACK_ENABLED";
public static final String EXTRA_MEDIA_COLLECTION_ID = "android.provider.extra.MEDIA_COLLECTION_ID";
public static final String EXTRA_PAGE_SIZE = "android.provider.extra.PAGE_SIZE";
public static final String EXTRA_PAGE_TOKEN = "android.provider.extra.PAGE_TOKEN";
public static final String EXTRA_PREVIEW_THUMBNAIL = "android.provider.extra.PREVIEW_THUMBNAIL";
public static final String EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED = "android.provider.extra.SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED";
public static final String EXTRA_SYNC_GENERATION = "android.provider.extra.SYNC_GENERATION";
public static final String MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION = "com.android.providers.media.permission.MANAGE_CLOUD_MEDIA_PROVIDERS";
public static final String PROVIDER_INTERFACE = "android.content.action.CLOUD_MEDIA_PROVIDER";
}
// Columns available for every media item
public static final class CloudMediaProviderContract.MediaColumns {
public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
public static final String DURATION_MILLIS = "duration_millis";
public static final String HEIGHT = "height";
public static final String ID = "id";
public static final String IS_FAVORITE = "is_favorite";
public static final String MEDIA_STORE_URI = "media_store_uri";
public static final String MIME_TYPE = "mime_type";
public static final String ORIENTATION = "orientation";
public static final String SIZE_BYTES = "size_bytes";
public static final String STANDARD_MIME_TYPE_EXTENSION = "standard_mime_type_extension";
public static final int STANDARD_MIME_TYPE_EXTENSION_ANIMATED_WEBP = 3; // 0x3
public static final int STANDARD_MIME_TYPE_EXTENSION_GIF = 1; // 0x1
public static final int STANDARD_MIME_TYPE_EXTENSION_MOTION_PHOTO = 2; // 0x2
public static final int STANDARD_MIME_TYPE_EXTENSION_NONE = 0; // 0x0
public static final String SYNC_GENERATION = "sync_generation";
public static final String WIDTH = "width";
}
// Columns available for every album item
public static final class CloudMediaProviderContract.AlbumColumns {
public static final String DATE_TAKEN_MILLIS = "date_taken_millis";
public static final String DISPLAY_NAME = "display_name";
public static final String ID = "id";
public static final String MEDIA_COUNT = "album_media_count";
public static final String MEDIA_COVER_ID = "album_media_cover_id";
}
// Media Collection metadata that is cached by the OS to compare sync states.
public static final class CloudMediaProviderContract.MediaCollectionInfo {
public static final String ACCOUNT_CONFIGURATION_INTENT = "account_configuration_intent";
public static final String ACCOUNT_NAME = "account_name";
public static final String LAST_MEDIA_SYNC_GENERATION = "last_media_sync_generation";
public static final String MEDIA_COLLECTION_ID = "media_collection_id";
}
onGetMediaCollectionInfo
Metoda onGetMediaCollectionInfo()
jest używana przez system operacyjny do
Pozwala ocenić prawidłowość przechowywanych w chmurze elementów multimedialnych w chmurze i określić niezbędne
synchronizację z dostawcą multimediów w chmurze. Ze względu na możliwość częstego
dla wszystkich wywołań systemu operacyjnego, wartość onGetMediaCollectionInfo()
jest uznawana za
kluczowe dla wydajności; należy unikać długotrwałych operacji lub
które mogą negatywnie wpłynąć na skuteczność. System operacyjny zapisuje w pamięci podręcznej.
poprzednie odpowiedzi użyte w tej metodzie i porównano je z kolejnymi odpowiedziami.
aby określić odpowiednie działania.
Kotlin
abstract fun onGetMediaCollectionInfo(extras: Bundle): Bundle
Java
@NonNull
public abstract Bundle onGetMediaCollectionInfo(@NonNull Bundle extras);
Zwrócony pakiet MediaCollectionInfo
zawiera te stałe:
onQueryMedia
Metoda onQueryMedia()
służy do wypełniania głównej siatki zdjęć w:
w różnych widokach. Te połączenia mogą być wrażliwe na czas oczekiwania.
można wywołać w ramach aktywnej synchronizacji w tle lub podczas wybierania zdjęć
gdy wymagany jest stan pełnej lub przyrostowej synchronizacji. Selektor zdjęć
nie czeka w nieskończoność na odpowiedź z wyświetleniem wyników;
może przekroczyć limit czasu na żądania związane z interfejsem. Zwrócony kursor
będzie nadal próbować przetworzyć ją w bazie danych selektora zdjęć
sesji.
Ta metoda zwraca identyfikator Cursor
reprezentujący wszystkie elementy multimedialne w mediach.
kolekcja opcjonalnie filtrowana według podanych dodatków i posortowana w odwrotnej kolejności
kolejność chronologiczna: MediaColumns#DATE_TAKEN_MILLIS
(najnowsze elementy)
).
Zwrócony pakiet CloudMediaProviderContract
zawiera te elementy:
stałe:
EXTRA_ALBUM_ID
EXTRA_LOOPING_PLAYBACK_ENABLED
EXTRA_MEDIA_COLLECTION_ID
EXTRA_PAGE_SIZE
EXTRA_PAGE_TOKEN
EXTRA_PREVIEW_THUMBNAIL
EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED
EXTRA_SYNC_GENERATION
MANAGE_CLOUD_MEDIA_PROVIDERS_PERMISSION
PROVIDER_INTERFACE
Dostawca multimediów w chmurze musi skonfigurować
CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID
jako część zwróconych
Bundle
Jeśli nie ustawisz tego ustawienia, zostanie to uznane za błąd i unieważnienie zwróconego elementu Cursor
. Jeśli
dostawca mediów w chmurze sprawdził wszystkie filtry w dostarczonych dodatkach, musi dodać
klucz do ContentResolver#EXTRA_HONORED_ARGS
w ramach zwróconego
Cursor#setExtras
onQueryRemoveMedia
Metoda onQueryDeletedMedia()
służy do usuwania elementów w
konto w chmurze jest prawidłowo usuwane z interfejsu selektora zdjęć. Z powodu
potencjalną czułość opóźnienia, te wywołania mogą być zainicjowane w ramach:
- Proaktywna synchronizacja w tle
- Sesje selektora zdjęć (gdy wymagany jest stan pełnej lub przyrostowej synchronizacji)
Interfejs selektora zdjęć nadaje priorytet elastycznym obsłudze.
nie będą czekać w nieskończoność na odpowiedź. Aby utrzymać płynność interakcji,
mogą wystąpić przekroczenia limitu czasu. Nadal będzie próbować przetworzyć każdy zwrócony plik Cursor
do bazy danych selektora zdjęć na potrzeby przyszłych sesji.
Ta metoda zwraca wartość Cursor
reprezentującą wszystkie usunięte elementy multimedialne w
cały zbiór multimediów w bieżącej wersji dostawcy, zwracany przez
onGetMediaCollectionInfo()
Te elementy możesz opcjonalnie filtrować według dodatków.
Dostawca treści w chmurze musi ustawić
CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID
jako część zwróconych
Cursor#setExtras
Jeśli nie ustawisz tej opcji, jest to błąd i unieważnia Cursor
. Jeśli
dostawca obsługiwał wszystkie filtry w dostarczonych dodatkach, musi dodać klucz do
ContentResolver#EXTRA_HONORED_ARGS
.
onQueryAlbums
Metoda onQueryAlbums()
służy do pobierania listy albumów Cloud, które
oraz powiązane z nimi metadane. Zobacz
CloudMediaProviderContract.AlbumColumns
, aby uzyskać dodatkowe informacje.
Ta metoda zwraca element Cursor
reprezentujący wszystkie elementy albumu w multimediach
kolekcja opcjonalnie filtrowana według podanych dodatków i posortowana w odwrotnej kolejności
kolejność chronologiczna: AlbumColumns#DATE_TAKEN_MILLIS
, najnowsze elementy
. Dostawca treści w chmurze musi ustawić
CloudMediaProviderContract#EXTRA_MEDIA_COLLECTION_ID
jako część zwróconych
Cursor
Jeśli nie ustawisz tego ustawienia, zostanie to uznane za błąd i unieważnienie zwróconego elementu Cursor
. Jeśli
dostawca obsługiwał wszystkie filtry w dostarczonych dodatkach, musi dodać klucz do
ContentResolver#EXTRA_HONORED_ARGS
jako część zwróconego Cursor
.
onOpenMedia
Metoda onOpenMedia()
powinna zwracać multimedia w pełnym rozmiarze określonym przez
podany mediaId
. Jeśli ta metoda zostanie zablokowana podczas pobierania treści do
urządzenia, należy okresowo sprawdzać podany CancellationSignal
, aby przerwać
porzuconych żądań.
onOpenPreview
Metoda onOpenPreview()
powinna zwrócić miniaturę podanego
size
dla elementu o podanym identyfikatorze media. Miniatura powinna znajdować się w
oryginalny plik CloudMediaProviderContract.MediaColumns#MIME_TYPE
i spodziewany jest
ma znacznie niższą rozdzielczość niż produkt zwracany przez użytkownika onOpenMedia
. Jeśli ta metoda
jest zablokowane podczas pobierania treści na urządzenie, należy okresowo
sprawdź podany CancellationSignal
, aby przerwać porzucone żądania.
kontroler onCreateCloudMediaSurfaceController
Metoda onCreateCloudMediaSurfaceController()
powinna zwracać błąd
CloudMediaSurfaceController
służy do renderowania podglądu elementów multimedialnych lub
null
, jeśli renderowanie podglądu nie jest obsługiwane.
CloudMediaSurfaceController
zarządza renderowaniem podglądu elementów multimedialnych
w przypadku danych instancji Surface
. Metody zajęć mają
asynchronicznie i nie powinny blokować
przez wykonywanie intensywnych operacji. Jeden
Instancja CloudMediaSurfaceController
jest odpowiedzialna za renderowanie wielu
elementy multimedialne powiązane z wieloma platformami.
CloudMediaSurfaceController
obsługuje tę listę
Wywołania zwrotne cyklu życia:
onConfigChange
onDestroy
onMediaPause
onMediaPlay
onMediaSeekTo
onPlayerCreate
onPlayerRelease
onSurfaceChanged
onSurfaceCreated
onSurfaceDestroyed