blob: 11d933884625e2fe881fdbe518996fc3e31c409a [file] [log] [blame]
/*
* Copyright (C) 2021 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.android.providers.media.photopicker;
import static android.provider.CloudMediaProviderContract.EXTRA_GENERATION;
import static android.provider.CloudMediaProviderContract.MediaColumns;
import static android.provider.CloudMediaProviderContract.MediaInfo;
import static com.android.providers.media.PickerUriResolver.getAlbumUri;
import static com.android.providers.media.PickerUriResolver.getMediaUri;
import static com.android.providers.media.PickerUriResolver.getDeletedMediaUri;
import static com.android.providers.media.PickerUriResolver.getMediaInfoUri;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LIMIT_DEFAULT;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.LONG_DEFAULT;
import static com.android.providers.media.photopicker.data.PickerDbFacade.QueryFilterBuilder.STRING_DEFAULT;
import android.content.Context;
import android.database.Cursor;
import android.database.MatrixCursor;
import android.database.MergeCursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.CloudMediaProviderContract.AlbumColumns;
import android.provider.MediaStore;
import android.util.Log;
import com.android.providers.media.photopicker.data.CloudProviderQueryExtras;
import com.android.providers.media.photopicker.data.PickerDbFacade;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Fetches data for the picker UI from the db and cloud/local providers
*/
public class PickerDataLayer {
private static final String TAG = "PickerDataLayer";
private final PickerDbFacade mDbFacade;
private final Context mContext;
private final String mLocalProvider;
public PickerDataLayer(Context context, PickerDbFacade dbFacade) {
mContext = context;
mDbFacade = dbFacade;
mLocalProvider = dbFacade.getLocalProvider();
}
public Cursor fetchMedia(Bundle queryArgs) {
final CloudProviderQueryExtras queryExtras
= CloudProviderQueryExtras.fromMediaStoreBundle(queryArgs);
if (Objects.equals(queryExtras.getAlbumId(), STRING_DEFAULT) || queryExtras.isFavorite()) {
// Fetch merged and deduped media from picker db
return mDbFacade.queryMedia(queryExtras.toQueryFilter());
} else {
// Fetch unique media directly from provider
final String cloudProvider = validateCloudProvider(queryExtras);
final Bundle extras = queryExtras.toCloudMediaBundle();
if (cloudProvider == null) {
return queryProviderMedia(mLocalProvider, extras);
} else if (queryExtras.getAlbumType() == null) {
// TODO(b/193668830): Replace null check with AlbumColumns.TYPE_CLOUD after
// moving test to CTS
return queryProviderMedia(cloudProvider, extras);
} else {
Log.w(TAG, "Unexpected album media query for cloud provider: " + cloudProvider);
return new MatrixCursor(new String[] {});
}
}
}
public Cursor fetchAlbums(Bundle queryArgs) {
final String cloudProvider = mDbFacade.getCloudProvider();
final CloudProviderQueryExtras queryExtras
= CloudProviderQueryExtras.fromMediaStoreBundle(queryArgs);
final Bundle cloudMediaArgs = queryExtras.toCloudMediaBundle();
final List<Cursor> cursors = new ArrayList<>();
final Bundle cursorExtra = new Bundle();
cursorExtra.putString(MediaStore.EXTRA_CLOUD_PROVIDER, queryExtras.getCloudProvider());
final Cursor localAlbums = queryProviderAlbums(mLocalProvider, cloudMediaArgs);
if (localAlbums != null) {
cursors.add(localAlbums);
}
// TODO(b/195009148): Verify if 'Videos' should be a merged album view, hence if we should
// refactor to mDbFacade.getMergedAlbums
final Cursor favoriteAlbums = mDbFacade.getFavoriteAlbum(queryExtras.toQueryFilter());
if (favoriteAlbums != null) {
cursors.add(favoriteAlbums);
}
final Cursor cloudAlbums = queryProviderAlbums(cloudProvider, cloudMediaArgs);
if (cloudAlbums != null) {
cursors.add(cloudAlbums);
}
if (cursors.isEmpty()) {
return null;
}
MergeCursor mergeCursor = new MergeCursor(cursors.toArray(new Cursor[cursors.size()]));
mergeCursor.setExtras(cursorExtra);
return mergeCursor;
}
private Cursor queryProviderAlbums(String authority, Bundle queryArgs) {
if (authority == null) {
// Can happen if there is no cloud provider
return null;
}
return query(getAlbumUri(authority), queryArgs);
}
private Cursor queryProviderMedia(String authority, Bundle queryArgs) {
final Bundle bundle = new Bundle();
bundle.putString(MediaColumns.AUTHORITY, authority);
final Cursor cursor = query(getMediaUri(authority), queryArgs);
cursor.setExtras(bundle);
return cursor;
}
private Cursor query(Uri uri, Bundle extras) {
return mContext.getContentResolver().query(uri, /* projection */ null, extras,
/* cancellationSignal */ null);
}
private String validateCloudProvider(CloudProviderQueryExtras extras) {
final String extrasCloudProvider = extras.getCloudProvider();
final String enabledCloudProvider = mDbFacade.getCloudProvider();
if (Objects.equals(enabledCloudProvider, extrasCloudProvider)) {
return enabledCloudProvider;
}
// Cloud provider has switched since last query, so no longer valid
return null;
}
}