• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.providers.media.photopicker;
18 
19 import static android.provider.CloudMediaProviderContract.EXTRA_AUTHORITY;
20 import static android.provider.CloudMediaProviderContract.EXTRA_LOOPING_PLAYBACK_ENABLED;
21 import static android.provider.CloudMediaProviderContract.EXTRA_MEDIASTORE_THUMB;
22 import static android.provider.CloudMediaProviderContract.EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED;
23 
24 import android.content.ContentProviderClient;
25 import android.content.ContentResolver;
26 import android.content.res.AssetFileDescriptor;
27 import android.database.Cursor;
28 import android.graphics.Point;
29 import android.net.Uri;
30 import android.os.Bundle;
31 import android.os.CancellationSignal;
32 import android.os.OperationCanceledException;
33 import android.os.ParcelFileDescriptor;
34 import android.provider.CloudMediaProvider;
35 import android.provider.MediaStore;
36 
37 import androidx.annotation.NonNull;
38 import androidx.annotation.Nullable;
39 
40 import com.android.providers.media.LocalCallingIdentity;
41 import com.android.providers.media.MediaProvider;
42 import com.android.providers.media.photopicker.data.CloudProviderQueryExtras;
43 import com.android.providers.media.photopicker.data.ExternalDbFacade;
44 import com.android.providers.media.photopicker.ui.remotepreview.RemotePreviewHandler;
45 import com.android.providers.media.photopicker.ui.remotepreview.RemoteSurfaceController;
46 
47 import java.io.FileNotFoundException;
48 
49 /**
50  * Implements the {@link CloudMediaProvider} interface over the local items in the MediaProvider
51  * database.
52  */
53 public class PhotoPickerProvider extends CloudMediaProvider {
54     private static final String TAG = "PhotoPickerProvider";
55 
56     private MediaProvider mMediaProvider;
57     private ExternalDbFacade mDbFacade;
58 
59     @Override
onCreate()60     public boolean onCreate() {
61         mMediaProvider = getMediaProvider();
62         mDbFacade = mMediaProvider.getExternalDbFacade();
63         return true;
64     }
65 
66     @Override
onQueryMedia(@ullable Bundle extras)67     public Cursor onQueryMedia(@Nullable Bundle extras) {
68         // TODO(b/190713331): Handle extra_page
69         final CloudProviderQueryExtras queryExtras =
70                 CloudProviderQueryExtras.fromCloudMediaBundle(extras);
71 
72         return mDbFacade.queryMedia(queryExtras.getGeneration(), queryExtras.getAlbumId(),
73                 queryExtras.getMimeType());
74     }
75 
76     @Override
onQueryDeletedMedia(@ullable Bundle extras)77     public Cursor onQueryDeletedMedia(@Nullable Bundle extras) {
78         final CloudProviderQueryExtras queryExtras =
79                 CloudProviderQueryExtras.fromCloudMediaBundle(extras);
80 
81         return mDbFacade.queryDeletedMedia(queryExtras.getGeneration());
82     }
83 
84     @Override
onQueryAlbums(@ullable Bundle extras)85     public Cursor onQueryAlbums(@Nullable Bundle extras) {
86         final CloudProviderQueryExtras queryExtras =
87                 CloudProviderQueryExtras.fromCloudMediaBundle(extras);
88 
89         return mDbFacade.queryAlbums(queryExtras.getMimeType());
90     }
91 
92     @Override
onOpenPreview(@onNull String mediaId, @NonNull Point size, @NonNull Bundle extras, @NonNull CancellationSignal signal)93     public AssetFileDescriptor onOpenPreview(@NonNull String mediaId, @NonNull Point size,
94             @NonNull Bundle extras, @NonNull CancellationSignal signal)
95             throws FileNotFoundException {
96         final Bundle opts = new Bundle();
97         opts.putParcelable(ContentResolver.EXTRA_SIZE, size);
98 
99         String mimeTypeFilter = null;
100         if (extras.getBoolean(EXTRA_MEDIASTORE_THUMB)) {
101             // This is a request for thumbnail, set "image/*" to get cached thumbnails from
102             // MediaProvider.
103             mimeTypeFilter = "image/*";
104         }
105 
106         final LocalCallingIdentity token = mMediaProvider.clearLocalCallingIdentity();
107         try {
108             return mMediaProvider.openTypedAssetFile(fromMediaId(mediaId), mimeTypeFilter, opts);
109         } finally {
110             mMediaProvider.restoreLocalCallingIdentity(token);
111         }
112     }
113 
114     @Override
onOpenMedia(@onNull String mediaId, @NonNull Bundle extras, @NonNull CancellationSignal signal)115     public ParcelFileDescriptor onOpenMedia(@NonNull String mediaId,
116             @NonNull Bundle extras, @NonNull CancellationSignal signal)
117             throws FileNotFoundException {
118         final LocalCallingIdentity token = mMediaProvider.clearLocalCallingIdentity();
119         try {
120             return mMediaProvider.openFile(fromMediaId(mediaId), "r");
121         } finally {
122             mMediaProvider.restoreLocalCallingIdentity(token);
123         }
124     }
125 
126     @Override
onGetMediaCollectionInfo(@ullable Bundle extras)127     public Bundle onGetMediaCollectionInfo(@Nullable Bundle extras) {
128         final CloudProviderQueryExtras queryExtras =
129                 CloudProviderQueryExtras.fromCloudMediaBundle(extras);
130 
131         return mDbFacade.getMediaCollectionInfo(queryExtras.getGeneration());
132     }
133 
134     @Override
135     @Nullable
onCreateCloudMediaSurfaceController(@onNull Bundle config, CloudMediaSurfaceStateChangedCallback callback)136     public CloudMediaSurfaceController onCreateCloudMediaSurfaceController(@NonNull Bundle config,
137             CloudMediaSurfaceStateChangedCallback callback) {
138         if (RemotePreviewHandler.isRemotePreviewEnabled()) {
139             final String authority = config.getString(EXTRA_AUTHORITY,
140                     PickerSyncController.LOCAL_PICKER_PROVIDER_AUTHORITY);
141             final boolean enableLoop = config.getBoolean(EXTRA_LOOPING_PLAYBACK_ENABLED, false);
142             final boolean muteAudio = config.getBoolean(EXTRA_SURFACE_CONTROLLER_AUDIO_MUTE_ENABLED,
143                     false);
144             return new RemoteSurfaceController(getContext(), authority, enableLoop, muteAudio,
145                     callback);
146         }
147         return null;
148     }
149 
getMediaProvider()150     private MediaProvider getMediaProvider() {
151         ContentResolver cr = getContext().getContentResolver();
152         try (ContentProviderClient cpc = cr.acquireContentProviderClient(MediaStore.AUTHORITY)) {
153             return (MediaProvider) cpc.getLocalContentProvider();
154         } catch (OperationCanceledException e) {
155             throw new IllegalStateException("Failed to acquire MediaProvider", e);
156         }
157     }
158 
fromMediaId(String mediaId)159     private static Uri fromMediaId(String mediaId) {
160         return MediaStore.Files.getContentUri(MediaStore.VOLUME_EXTERNAL,
161                 Long.parseLong(mediaId));
162     }
163 }
164