1 /* 2 * Copyright (C) 2018 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.documentsui; 18 19 import android.content.Context; 20 import android.database.Cursor; 21 import android.net.Uri; 22 import android.os.Bundle; 23 import android.provider.DocumentsContract; 24 25 import androidx.annotation.NonNull; 26 27 import com.android.documentsui.base.Lookup; 28 import com.android.documentsui.base.RootInfo; 29 import com.android.documentsui.base.State; 30 import com.android.documentsui.base.UserId; 31 import com.android.documentsui.roots.ProvidersAccess; 32 import com.android.documentsui.roots.RootCursorWrapper; 33 34 import java.util.List; 35 import java.util.concurrent.Executor; 36 37 /* 38 * The class to query multiple roots support {@link DocumentsContract.Root#FLAG_LOCAL_ONLY} 39 * and {@link DocumentsContract.Root#FLAG_SUPPORTS_SEARCH} from 40 * {@link android.provider.DocumentsProvider}. 41 */ 42 public class GlobalSearchLoader extends MultiRootDocumentsLoader { 43 private final Bundle mQueryArgs; 44 private final UserId mUserId; 45 46 /* 47 * Create the loader to query multiple roots support 48 * {@link DocumentsContract.Root#FLAG_LOCAL_ONLY} and 49 * {@link DocumentsContract.Root#FLAG_SUPPORTS_SEARCH} from 50 * {@link android.provider.DocumentsProvider}. 51 * 52 * @param context the context 53 * @param providers the providers 54 * @param state current state 55 * @param features the feature flags 56 * @param executors the executors of authorities 57 * @param fileTypeMap the map of mime types and file types 58 * @param queryArgs the bundle of query arguments 59 */ GlobalSearchLoader(Context context, ProvidersAccess providers, State state, Lookup<String, Executor> executors, Lookup<String, String> fileTypeMap, @NonNull Bundle queryArgs, UserId userId)60 GlobalSearchLoader(Context context, ProvidersAccess providers, State state, 61 Lookup<String, Executor> executors, Lookup<String, String> fileTypeMap, 62 @NonNull Bundle queryArgs, UserId userId) { 63 super(context, providers, state, executors, fileTypeMap); 64 mQueryArgs = queryArgs; 65 mUserId = userId; 66 } 67 68 @Override shouldIgnoreRoot(RootInfo root)69 protected boolean shouldIgnoreRoot(RootInfo root) { 70 // Only support local search in GlobalSearchLoader 71 if (!root.isLocalOnly() || !root.supportsSearch()) { 72 return true; 73 } 74 75 if (mState.supportsCrossProfile() && root.supportsCrossProfile() 76 && !mQueryArgs.containsKey(DocumentsContract.QUERY_ARG_DISPLAY_NAME) 77 && !mUserId.equals(root.userId)) { 78 // Ignore cross-profile roots if it is not a text search. For example, the user may 79 // just filter documents by mime type. 80 return true; 81 } 82 83 // To prevent duplicate files on search result, ignore storage root because its almost 84 // files include in media root. 85 return root.isStorage(); 86 } 87 88 @Override getQueryTask(String authority, List<RootInfo> rootInfos)89 protected QueryTask getQueryTask(String authority, List<RootInfo> rootInfos) { 90 return new SearchTask(authority, rootInfos); 91 } 92 93 private class SearchTask extends QueryTask { 94 SearchTask(String authority, List<RootInfo> rootInfos)95 public SearchTask(String authority, List<RootInfo> rootInfos) { 96 super(authority, rootInfos); 97 } 98 99 @Override addQueryArgs(@onNull Bundle queryArgs)100 protected void addQueryArgs(@NonNull Bundle queryArgs) { 101 queryArgs.putBoolean(DocumentsContract.QUERY_ARG_EXCLUDE_MEDIA, true); 102 queryArgs.putAll(mQueryArgs); 103 } 104 105 @Override getQueryUri(RootInfo rootInfo)106 protected Uri getQueryUri(RootInfo rootInfo) { 107 // For the new querySearchDocuments, we put the query string into queryArgs. 108 // Use the empty string to build the query uri. 109 return DocumentsContract.buildSearchDocumentsUri(authority, 110 rootInfo.rootId, "" /* query */); 111 } 112 113 @Override generateResultCursor(RootInfo rootInfo, Cursor oriCursor)114 protected RootCursorWrapper generateResultCursor(RootInfo rootInfo, Cursor oriCursor) { 115 return new RootCursorWrapper(rootInfo.userId, authority, rootInfo.rootId, oriCursor, 116 -1 /* maxCount */); 117 } 118 } 119 } 120