diff options
Diffstat (limited to 'src')
9 files changed, 326 insertions, 66 deletions
diff --git a/src/com/android/providers/media/ConfigStore.java b/src/com/android/providers/media/ConfigStore.java index a16e956e3..b2ae87e98 100644 --- a/src/com/android/providers/media/ConfigStore.java +++ b/src/com/android/providers/media/ConfigStore.java @@ -22,6 +22,7 @@ import static android.provider.DeviceConfig.NAMESPACE_STORAGE_NATIVE_BOOT; import static java.util.Objects.requireNonNull; import android.content.res.Resources; +import android.content.res.Resources.NotFoundException; import android.os.Binder; import android.os.Build; import android.os.SystemProperties; @@ -399,18 +400,24 @@ public interface ConfigStore { @Nullable @Override public String getDefaultCloudProviderPackage() { - String pkg = mResources.getString(R.string.config_default_cloud_media_provider_package); - if (pkg == null && Build.VERSION.SDK_INT <= TIRAMISU) { - // We are on Android T or below and do not have - // config_default_cloud_media_provider_package: let's see if we have now deprecated - // config_default_cloud_provider_authority. - final String authority = - mResources.getString(R.string.config_default_cloud_provider_authority); - if (authority != null) { - pkg = maybeExtractPackageNameFromCloudProviderAuthority(authority); + try { + String pkg = + mResources.getString(R.string.config_default_cloud_media_provider_package); + if (pkg == null && Build.VERSION.SDK_INT <= TIRAMISU) { + // We are on Android T or below and do not have + // config_default_cloud_media_provider_package: let's see if we have now + // deprecated + // config_default_cloud_provider_authority. + final String authority = + mResources.getString(R.string.config_default_cloud_provider_authority); + if (authority != null) { + pkg = maybeExtractPackageNameFromCloudProviderAuthority(authority); + } } + return pkg; + } catch (NotFoundException e) { + return null; } - return pkg; } @NonNull @@ -533,12 +540,16 @@ public interface ConfigStore { @Override public Optional<String> getDefaultOemMetadataServicePackage() { - String pkg = mResources.getString(R.string.config_default_oem_metadata_service_package); - if (pkg == null || pkg.isEmpty()) { + try { + String pkg = + mResources.getString(R.string.config_default_oem_metadata_service_package); + if (pkg == null || pkg.isEmpty()) { + return Optional.empty(); + } + return Optional.of(pkg); + } catch (NotFoundException e) { return Optional.empty(); } - - return Optional.of(pkg); } @Override diff --git a/src/com/android/providers/media/MediaProvider.java b/src/com/android/providers/media/MediaProvider.java index 4fc5f21e4..0beb07b8d 100644 --- a/src/com/android/providers/media/MediaProvider.java +++ b/src/com/android/providers/media/MediaProvider.java @@ -553,6 +553,14 @@ public class MediaProvider extends ContentProvider { @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA) static final long LOCKDOWN_MEDIASTORE_VERSION = 343977174L; + /** + * Number of uris sent to bulk write/delete/trash/favorite requests restricted at 2000. + * Attempting to send more than 2000 uris will result in an IllegalArgumentException. + */ + @ChangeId + @EnabledSince(targetSdkVersion = Build.VERSION_CODES.BAKLAVA) + static final long LIMIT_CREATE_REQUEST_URIS = 203408344L; + @GuardedBy("mPendingOpenInfo") private final Map<Integer, PendingOpenInfo> mPendingOpenInfo = new ArrayMap<>(); @@ -1337,13 +1345,19 @@ public class MediaProvider extends ContentProvider { } final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(getContext()); - // Get case insensitive exclusion list. - List<String> exclusionList = - Flags.enableExclusionListForDefaultFolders() - ? getFoldersToSkipInDefaultCreation().stream().map( - String::toLowerCase).collect(Collectors.toList()) - : List.of(); if (prefs.getInt(key, 0) == 0) { + // Get case insensitive exclusion list. + List<String> exclusionList = + Flags.enableExclusionListForDefaultFolders() + ? getFoldersToSkipInDefaultCreation().stream().map( + String::toLowerCase).collect(Collectors.toList()) + : List.of(); + if (exclusionList.size() > getDefaultFolderNames().length) { + Log.e(TAG, "Exclusion list has " + exclusionList.size() + + " items which exceeds the size of default folders list which has size " + + getDefaultFolderNames().length); + exclusionList = List.of(); + } for (String folderName : getDefaultFolderNames()) { final File folder = new File(volume.getPath(), folderName); if (folder.exists()) { @@ -8176,6 +8190,11 @@ public class MediaProvider extends ContentProvider { final ClipData clipData = extras.getParcelable(MediaStore.EXTRA_CLIP_DATA); final List<Uri> uris = collectUris(clipData); + if (getCallingPackageTargetSdkVersion() > Build.VERSION_CODES.VANILLA_ICE_CREAM + && CompatChanges.isChangeEnabled(LIMIT_CREATE_REQUEST_URIS) && uris.size() > 2000) { + throw new IllegalArgumentException("URI list restricted to 2000 per request"); + } + for (Uri uri : uris) { final int match = matchUri(uri, false); switch (match) { diff --git a/src/com/android/providers/media/photopicker/sync/MediaSetsResetWorker.java b/src/com/android/providers/media/photopicker/sync/MediaSetsResetWorker.java new file mode 100644 index 000000000..5991ae049 --- /dev/null +++ b/src/com/android/providers/media/photopicker/sync/MediaSetsResetWorker.java @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2024 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.sync; + +import static com.android.providers.media.photopicker.sync.PickerSyncManager.SYNC_WORKER_INPUT_SYNC_SOURCE; +import static com.android.providers.media.photopicker.sync.SyncTrackerRegistry.markMediaSetsSyncAsComplete; + +import android.content.Context; +import android.database.sqlite.SQLiteDatabase; +import android.util.Log; + +import androidx.annotation.NonNull; +import androidx.work.ListenableWorker; +import androidx.work.Worker; +import androidx.work.WorkerParameters; + +import com.android.providers.media.photopicker.PickerSyncController; +import com.android.providers.media.photopicker.util.exceptions.RequestObsoleteException; +import com.android.providers.media.photopicker.v2.sqlite.MediaInMediaSetsDatabaseUtil; +import com.android.providers.media.photopicker.v2.sqlite.MediaSetsDatabaseUtil; + +/** + * This worker is responsible for cleaning up the cached media sets or media sets content based + * on the type input reset parameter received + */ +public class MediaSetsResetWorker extends Worker { + private static final String TAG = "MediaSetsResetWorker"; + + public MediaSetsResetWorker( + @NonNull Context context, @NonNull WorkerParameters workerParameters) { + super(context, workerParameters); + } + + @Override + public ListenableWorker.Result doWork() { + + final int syncSource = getInputData().getInt(SYNC_WORKER_INPUT_SYNC_SOURCE, + /* defaultValue */ -1); + + // Do not allow endless re-runs of this worker, if this isn't the original run, + // just fail and wait until the next scheduled run. + if (getRunAttemptCount() > 0) { + Log.w(TAG, "Worker retry was detected, ending this run in failure."); + return ListenableWorker.Result.failure(); + } + + boolean isMediaSetsTableDeleted = clearMediaSetsCache(syncSource); + boolean isMediaInMediaSetsTabledDeleted = clearMediaSetsContentCache(); + + // Both the tables were cleared. Mark the worker's run as success + if (isMediaSetsTableDeleted && isMediaInMediaSetsTabledDeleted) { + return ListenableWorker.Result.success(); + } + + return ListenableWorker.Result.failure(); + } + + private boolean clearMediaSetsCache(int syncSource) { + SQLiteDatabase database = getDatabase(); + + try { + checkIfWorkerHasStopped(); + + database.beginTransaction(); + + MediaSetsDatabaseUtil.clearMediaSetsCache(database); + + if (database.inTransaction()) { + database.setTransactionSuccessful(); + return true; + } else { + return false; + } + } catch (Exception e) { + Log.e(TAG, "Could not clear media sets cache", e); + return false; + } finally { + // Ensure that the transaction ends and the DB lock is released. + if (database.inTransaction()) { + database.endTransaction(); + } + markMediaSetsSyncAsComplete(syncSource, getId()); + } + } + + private boolean clearMediaSetsContentCache() { + + SQLiteDatabase database = getDatabase(); + + try { + checkIfWorkerHasStopped(); + + database.beginTransaction(); + + MediaInMediaSetsDatabaseUtil.clearMediaInMediaSetsCache(database); + + if (database.inTransaction()) { + database.setTransactionSuccessful(); + return true; + } else { + return false; + } + } catch (Exception e) { + Log.e(TAG, "Could not clear media sets content cache", e); + return false; + } finally { + // Ensure that the transaction ends and the DB lock is released. + if (database.inTransaction()) { + database.endTransaction(); + } + } + } + + private void checkIfWorkerHasStopped() throws RequestObsoleteException { + if (isStopped()) { + throw new RequestObsoleteException("CategoriesResetWorker has stopped."); + } + } + + @NonNull + private SQLiteDatabase getDatabase() { + return PickerSyncController.getInstanceOrThrow().getDbFacade().getDatabase(); + } +} diff --git a/src/com/android/providers/media/photopicker/sync/MediaSetsSyncWorker.java b/src/com/android/providers/media/photopicker/sync/MediaSetsSyncWorker.java index c0126ef2f..8e151141a 100644 --- a/src/com/android/providers/media/photopicker/sync/MediaSetsSyncWorker.java +++ b/src/com/android/providers/media/photopicker/sync/MediaSetsSyncWorker.java @@ -71,6 +71,7 @@ public class MediaSetsSyncWorker extends Worker { @NonNull @Override public ListenableWorker.Result doWork() { + final int syncSource = getInputData().getInt(SYNC_WORKER_INPUT_SYNC_SOURCE, /* defaultValue */ INVALID_SYNC_SOURCE); final String categoryId = getInputData().getString(SYNC_WORKER_INPUT_CATEGORY_ID); diff --git a/src/com/android/providers/media/photopicker/sync/PickerSyncManager.java b/src/com/android/providers/media/photopicker/sync/PickerSyncManager.java index 6d879a306..7be806a6c 100644 --- a/src/com/android/providers/media/photopicker/sync/PickerSyncManager.java +++ b/src/com/android/providers/media/photopicker/sync/PickerSyncManager.java @@ -17,6 +17,7 @@ package com.android.providers.media.photopicker.sync; import static com.android.providers.media.photopicker.sync.SyncTrackerRegistry.markAlbumMediaSyncAsComplete; +import static com.android.providers.media.photopicker.sync.SyncTrackerRegistry.markAllMediaInMediaSetsSyncAsComplete; import static com.android.providers.media.photopicker.sync.SyncTrackerRegistry.markAllSearchResultsSyncAsComplete; import static com.android.providers.media.photopicker.sync.SyncTrackerRegistry.markMediaInMediaSetSyncAsComplete; import static com.android.providers.media.photopicker.sync.SyncTrackerRegistry.markMediaSetsSyncAsComplete; @@ -620,7 +621,10 @@ public class PickerSyncManager { } /** - * Creates OneTimeWork request for syncing media sets with the given provider + * Creates OneTimeWork request for syncing media sets with the given provider. + * The existing media sets cache and the media sets content cache is cleared before a new media + * sets sync is triggered to ensure accuracy of the media sets metadata stored in the database. + * The reset cache and sync requests are chained to ensure correctness of the entire operation. * @param requestParams The MediaSetsSyncRequestsParams object containing all input parameters * for creating a sync request * @param syncSource Indicates whether the sync is required with the local provider or @@ -628,18 +632,35 @@ public class PickerSyncManager { */ public void syncMediaSetsForProvider( MediaSetsSyncRequestParams requestParams, @SyncSource int syncSource) { - final Map<String, Object> inputMap = new HashMap<>(); - inputMap.put(SYNC_WORKER_INPUT_AUTHORITY, requestParams.getAuthority()); - inputMap.put(SYNC_WORKER_INPUT_SYNC_SOURCE, syncSource); - inputMap.put(SYNC_WORKER_INPUT_CATEGORY_ID, requestParams.getCategoryId()); + // Create media sets sync request + final Map<String, Object> syncRequestInputMap = new HashMap<>(); + syncRequestInputMap.put(SYNC_WORKER_INPUT_AUTHORITY, requestParams.getAuthority()); + syncRequestInputMap.put(SYNC_WORKER_INPUT_SYNC_SOURCE, syncSource); + syncRequestInputMap.put(SYNC_WORKER_INPUT_CATEGORY_ID, requestParams.getCategoryId()); if (requestParams.getMimeTypes() != null) { - inputMap.put(EXTRA_MIME_TYPES, requestParams.getMimeTypes().toArray(new String[0])); + syncRequestInputMap.put(EXTRA_MIME_TYPES, requestParams.getMimeTypes().toArray( + new String[0] + )); } - final Data inputData = new Data(inputMap); + final Data syncRequestInputData = new Data(syncRequestInputMap); final OneTimeWorkRequest syncRequest = - buildOneTimeWorkerRequest(MediaSetsSyncWorker.class, inputData); + buildOneTimeWorkerRequest(MediaSetsSyncWorker.class, syncRequestInputData); + + // Create media sets reset request. MediaSets sync are non-resumable. + // It's fine to delete the entire cache before a new set is triggered. + // The media sets content cache is also completely cleared before we start syncing + // any particular media set for its content. + // These tables are cleared once per picker session before the media sets sync for this + // session is triggered. This ensures that the data read from the cache in every session + // is always in sync with the cloud provider. + final Data resetRequestInputData = new Data(Map.of( + SYNC_WORKER_INPUT_SYNC_SOURCE, syncSource + )); + final OneTimeWorkRequest resetRequest = + buildOneTimeWorkerRequest(MediaSetsResetWorker.class, resetRequestInputData); - // track the new request + // Track the new requests + trackNewMediaSetsSyncRequest(syncSource, resetRequest.getId()); trackNewMediaSetsSyncRequest(syncSource, syncRequest.getId()); final String workName = syncSource == SYNC_LOCAL_ONLY @@ -647,15 +668,18 @@ public class PickerSyncManager { : IMMEDIATE_CLOUD_MEDIA_SETS_SYNC_WORK_NAME; // Enqueue local or cloud sync request try { - final Operation enqueueOperation = mWorkManager.enqueueUniqueWork( - workName, - ExistingWorkPolicy.APPEND_OR_REPLACE, - syncRequest); + final Operation enqueueOperation = mWorkManager + .beginUniqueWork( + workName, + ExistingWorkPolicy.APPEND_OR_REPLACE, + resetRequest) + .then(syncRequest).enqueue(); // Check that the request has been successfully enqueued. enqueueOperation.getResult().get(); } catch (Exception e) { Log.e(TAG, "Could not enqueue expedited media sets sync request", e); + markMediaSetsSyncAsComplete(syncSource, resetRequest.getId()); markMediaSetsSyncAsComplete(syncSource, syncRequest.getId()); } } @@ -668,7 +692,9 @@ public class PickerSyncManager { * the cloud provider. */ public void syncMediaInMediaSetForProvider( - MediaInMediaSetSyncRequestParams requestParams, @SyncSource int syncSource) { + MediaInMediaSetSyncRequestParams requestParams, + @SyncSource int syncSource) { + final Data inputData = new Data( Map.of( @@ -679,6 +705,8 @@ public class PickerSyncManager { final OneTimeWorkRequest syncRequest = buildOneTimeWorkerRequest(MediaInMediaSetsSyncWorker.class, inputData); + markAllMediaInMediaSetsSyncAsComplete(syncSource); + // track the new request trackNewMediaInMediaSetSyncRequest(syncSource, syncRequest.getId()); @@ -688,10 +716,10 @@ public class PickerSyncManager { // Enqueue local or cloud sync request try { final Operation enqueueOperation = mWorkManager.enqueueUniqueWork( - workName, - ExistingWorkPolicy.APPEND_OR_REPLACE, - syncRequest - ); + workName, + ExistingWorkPolicy.REPLACE, + syncRequest + ); // Check that the request has been successfully enqueued. enqueueOperation.getResult().get(); @@ -703,11 +731,17 @@ public class PickerSyncManager { @NonNull private OneTimeWorkRequest buildOneTimeWorkerRequest( - @NonNull Class<? extends Worker> workerClass, @NonNull Data inputData) { - return new OneTimeWorkRequest.Builder(workerClass) - .setInputData(inputData) - .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) - .build(); + @NonNull Class<? extends Worker> workerClass, Data inputData) { + if (inputData != null) { + return new OneTimeWorkRequest.Builder(workerClass) + .setInputData(inputData) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .build(); + } else { + return new OneTimeWorkRequest.Builder(workerClass) + .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST) + .build(); + } } @NonNull diff --git a/src/com/android/providers/media/photopicker/sync/SyncTrackerRegistry.java b/src/com/android/providers/media/photopicker/sync/SyncTrackerRegistry.java index 3c2898b96..82acd40fe 100644 --- a/src/com/android/providers/media/photopicker/sync/SyncTrackerRegistry.java +++ b/src/com/android/providers/media/photopicker/sync/SyncTrackerRegistry.java @@ -390,4 +390,17 @@ public class SyncTrackerRegistry { getCloudMediaInMediaSetTracker().markSyncCompleted(syncRequestId); } } + + /** + * Mark all media in media sets sync pending futures as complete + */ + public static void markAllMediaInMediaSetsSyncAsComplete( + @PickerSyncManager.SyncSource int syncSource) { + if (syncSource == SYNC_LOCAL_ONLY || syncSource == SYNC_LOCAL_AND_CLOUD) { + getLocalMediaInMediaSetTracker().markAllSyncsCompleted(); + } + if (syncSource == SYNC_CLOUD_ONLY || syncSource == SYNC_LOCAL_AND_CLOUD) { + getCloudMediaInMediaSetTracker().markAllSyncsCompleted(); + } + } } diff --git a/src/com/android/providers/media/photopicker/v2/PickerDataLayerV2.java b/src/com/android/providers/media/photopicker/v2/PickerDataLayerV2.java index b8b78d94e..b39bc5472 100644 --- a/src/com/android/providers/media/photopicker/v2/PickerDataLayerV2.java +++ b/src/com/android/providers/media/photopicker/v2/PickerDataLayerV2.java @@ -1452,7 +1452,8 @@ public class PickerDataLayerV2 { new MediaInMediaSetSyncRequestParams(extras); final Set<String> providers = new HashSet<>( Objects.requireNonNull(extras.getStringArrayList("providers"))); - scheduleMediaInMediaSetSync(requestParams, appContext, workManager, providers); + scheduleMediaInMediaSetSync( + requestParams, appContext, workManager, providers); } /** @@ -1466,6 +1467,7 @@ public class PickerDataLayerV2 { private static void scheduleMediaInMediaSetSync( @NonNull MediaInMediaSetSyncRequestParams requestParams, @NonNull Context context, @NonNull WorkManager workManager, @NonNull Set<String> providers) { + PickerSyncController syncController = PickerSyncController.getInstanceOrThrow(); PickerSyncManager syncManager = new PickerSyncManager(workManager, context); int syncSource = Objects.equals(requestParams.getAuthority(), @@ -1474,10 +1476,12 @@ public class PickerDataLayerV2 { // Sync MediaSet content only if the media sets can actually be queried if (syncSource == SYNC_LOCAL_ONLY && syncController.shouldQueryLocalMediaSets(providers)) { - syncManager.syncMediaInMediaSetForProvider(requestParams, SYNC_LOCAL_ONLY); + syncManager.syncMediaInMediaSetForProvider( + requestParams, SYNC_LOCAL_ONLY); } else if (syncController.shouldQueryCloudMediaSets( providers, requestParams.getAuthority())) { - syncManager.syncMediaInMediaSetForProvider(requestParams, SYNC_CLOUD_ONLY); + syncManager.syncMediaInMediaSetForProvider( + requestParams, SYNC_CLOUD_ONLY); } else { Log.e(TAG, "Unidentified provider authority: " + requestParams.getAuthority() + " skipping MediaSet content sync."); diff --git a/src/com/android/providers/media/photopicker/v2/sqlite/MediaInMediaSetsDatabaseUtil.java b/src/com/android/providers/media/photopicker/v2/sqlite/MediaInMediaSetsDatabaseUtil.java index 6f93998c9..0c43445fe 100644 --- a/src/com/android/providers/media/photopicker/v2/sqlite/MediaInMediaSetsDatabaseUtil.java +++ b/src/com/android/providers/media/photopicker/v2/sqlite/MediaInMediaSetsDatabaseUtil.java @@ -22,6 +22,8 @@ import static android.database.sqlite.SQLiteDatabase.CONFLICT_REPLACE; import static com.android.providers.media.photopicker.v2.sqlite.PickerMediaDatabaseUtil.addNextPageKey; import static com.android.providers.media.photopicker.v2.sqlite.PickerMediaDatabaseUtil.addPrevPageKey; +import static java.util.Objects.requireNonNull; + import android.content.ContentUris; import android.content.ContentValues; import android.database.Cursor; @@ -40,7 +42,6 @@ import com.android.providers.media.photopicker.PickerSyncController; import java.util.ArrayList; import java.util.List; import java.util.Locale; -import java.util.Objects; /** * Utility class for insertion or querying the media items in various media sets @@ -63,8 +64,8 @@ public class MediaInMediaSetsDatabaseUtil { @Nullable List<ContentValues> mediaListToInsert, @NonNull String authority) { - Objects.requireNonNull(database); - Objects.requireNonNull(authority); + requireNonNull(database); + requireNonNull(authority); final boolean isLocal = PickerSyncController.getInstanceOrThrow() .getLocalProvider() @@ -298,8 +299,8 @@ public class MediaInMediaSetsDatabaseUtil { */ public static List<ContentValues> getMediaContentValuesFromCursor( @NonNull Cursor mediaCursor, @NonNull Long mediaSetPickerId, boolean isLocal) { - Objects.requireNonNull(mediaSetPickerId); - Objects.requireNonNull(mediaCursor); + requireNonNull(mediaSetPickerId); + requireNonNull(mediaCursor); List<ContentValues> contentValuesList = new ArrayList<>(mediaCursor.getCount()); if (mediaCursor.moveToFirst()) { @@ -337,4 +338,23 @@ public class MediaInMediaSetsDatabaseUtil { } return contentValuesList; } + + /** + * Deletes all the rows from the MediaInMediaSets table + */ + public static void clearMediaInMediaSetsCache(@NonNull SQLiteDatabase database) { + + requireNonNull(database); + + try { + int deletedRows = database.delete( + PickerSQLConstants.Table.MEDIA_IN_MEDIA_SETS.name(), + /*whereClause*/ null, + /*whereArgs*/ null); + + Log.d(TAG, "Deleted " + deletedRows + " rows from the media in media sets table"); + } catch (Exception e) { + Log.d(TAG, "Couldn't clear the media in media sets table due to " + e); + } + } } diff --git a/src/com/android/providers/media/photopicker/v2/sqlite/MediaSetsDatabaseUtil.java b/src/com/android/providers/media/photopicker/v2/sqlite/MediaSetsDatabaseUtil.java index ee312fbed..f71b9d4ac 100644 --- a/src/com/android/providers/media/photopicker/v2/sqlite/MediaSetsDatabaseUtil.java +++ b/src/com/android/providers/media/photopicker/v2/sqlite/MediaSetsDatabaseUtil.java @@ -18,6 +18,8 @@ package com.android.providers.media.photopicker.v2.sqlite; import static android.database.sqlite.SQLiteDatabase.CONFLICT_IGNORE; +import static java.util.Objects.requireNonNull; + import android.content.ContentValues; import android.database.Cursor; import android.database.SQLException; @@ -35,7 +37,6 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.List; import java.util.Locale; -import java.util.Objects; /** * Utility class which holds functionality for inserting and querying media set data @@ -61,10 +62,10 @@ public class MediaSetsDatabaseUtil { @NonNull String categoryId, @NonNull String authority, @Nullable List<String> mimeTypes) { - Objects.requireNonNull(database); - Objects.requireNonNull(mediaSetMetadataCursor); - Objects.requireNonNull(categoryId); - Objects.requireNonNull(authority); + requireNonNull(database); + requireNonNull(mediaSetMetadataCursor); + requireNonNull(categoryId); + requireNonNull(authority); String mimeTypesAsString = getMimeTypesAsString(mimeTypes); List<ContentValues> insertValues = getMediaSetContentValues( @@ -126,8 +127,8 @@ public class MediaSetsDatabaseUtil { public static Pair<String, String[]> getMediaSetIdAndMimeType( @NonNull SQLiteDatabase database, @NonNull Long mediaSetPickerId) { - Objects.requireNonNull(database); - Objects.requireNonNull(mediaSetPickerId); + requireNonNull(database); + requireNonNull(mediaSetPickerId); SelectSQLiteQueryBuilder queryBuilder = new SelectSQLiteQueryBuilder(database) .setTables(PickerSQLConstants.Table.MEDIA_SETS.name()) @@ -168,8 +169,8 @@ public class MediaSetsDatabaseUtil { */ public static Cursor getMediaSetsForCategory( @NonNull SQLiteDatabase database, @NonNull MediaSetsSyncRequestParams requestParams) { - Objects.requireNonNull(database); - Objects.requireNonNull(requestParams); + requireNonNull(database); + requireNonNull(requestParams); final String categoryId = requestParams.getCategoryId(); final String authority = requestParams.getAuthority(); final List<String> mimeTypes; @@ -178,8 +179,8 @@ public class MediaSetsDatabaseUtil { } else { mimeTypes = null; } - Objects.requireNonNull(categoryId); - Objects.requireNonNull(authority); + requireNonNull(categoryId); + requireNonNull(authority); final List<String> projection = List.of( PickerSQLConstants.MediaSetsTableColumns.PICKER_ID.getColumnName(), @@ -223,8 +224,8 @@ public class MediaSetsDatabaseUtil { */ public static String getMediaResumeKey( @NonNull SQLiteDatabase database, @NonNull Long mediaPickerId) { - Objects.requireNonNull(database); - Objects.requireNonNull(mediaPickerId); + requireNonNull(database); + requireNonNull(mediaPickerId); final List<String> projection = List.of( PickerSQLConstants.MediaSetsTableColumns.MEDIA_IN_MEDIA_SET_SYNC_RESUME_KEY @@ -261,8 +262,8 @@ public class MediaSetsDatabaseUtil { */ public static void updateMediaInMediaSetSyncResumeKey(@NonNull SQLiteDatabase database, @NonNull Long mediaSetPickerId, @Nullable String resumeKey) { - Objects.requireNonNull(database); - Objects.requireNonNull(mediaSetPickerId); + requireNonNull(database); + requireNonNull(mediaSetPickerId); String table = PickerSQLConstants.Table.MEDIA_SETS.name(); @@ -285,6 +286,25 @@ public class MediaSetsDatabaseUtil { ); } + /** + * Deletes all the rows from the MediaSets table + */ + public static void clearMediaSetsCache(@NonNull SQLiteDatabase database) { + + requireNonNull(database); + + try { + int deletedRows = database.delete( + PickerSQLConstants.Table.MEDIA_SETS.name(), + /*whereClause*/ null, + /*whereClauseArgs*/ null); + + Log.d(TAG, "Deleted " + deletedRows + " rows from the media sets table."); + } catch (Exception exception) { + Log.e(TAG, "couldn't clear the media sets table due to " + exception); + } + } + private static List<ContentValues> getMediaSetContentValues( Cursor mediaSetCursor, String categoryId, String authority, String mimeTypes) { |