Loading src/com/android/documentsui/DirectoryLoader.java +52 −7 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.database.MergeCursor; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; Loading @@ -31,6 +32,7 @@ import android.os.RemoteException; import android.provider.DocumentsContract.Document; import android.util.Log; import androidx.annotation.Nullable; import androidx.loader.content.AsyncTaskLoader; import com.android.documentsui.archives.ArchivesProvider; Loading @@ -42,9 +44,12 @@ import com.android.documentsui.base.Lookup; import com.android.documentsui.base.MimeTypes; import com.android.documentsui.base.RootInfo; import com.android.documentsui.base.State; import com.android.documentsui.base.UserId; import com.android.documentsui.roots.RootCursorWrapper; import com.android.documentsui.sorting.SortModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { Loading @@ -57,6 +62,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private final LockingContentObserver mObserver; private final RootInfo mRoot; private final State mState; private final Uri mUri; private final SortModel mModel; private final Lookup<String, String> mFileTypeLookup; Loading @@ -81,6 +87,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { super(context); mFeatures = features; mState = state; mRoot = state.stack.getRoot(); mUri = uri; mModel = state.sortModel; Loading Loading @@ -124,8 +131,16 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { final Bundle queryArgs = new Bundle(); mModel.addQuerySortArgs(queryArgs); final List<UserId> userIds = new ArrayList<>(); if (mSearchMode) { queryArgs.putAll(mQueryArgs); if (mState.canShareAcrossProfile && mRoot.supportsCrossProfile()) { userIds.addAll( DocumentsApplication.getUserIdManager(getContext()).getUserIds()); } } if (userIds.isEmpty()) { userIds.add(mDoc.userId); } if (mFeatures.isContentPagingEnabled()) { Loading @@ -134,17 +149,13 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { DebugFlags.addForcedPagingArgs(queryArgs); } cursor = client.query(mUri, null, queryArgs, mSignal); cursor = queryOnUsers(userIds, authority, queryArgs); if (cursor == null) { throw new RemoteException("Provider returned null"); } cursor.registerContentObserver(mObserver); cursor = new RootCursorWrapper(mDoc.userId, mUri.getAuthority(), mRoot.rootId, cursor, -1); if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) { // There is no findDocumentPath API. Enable filtering on folders in search mode. cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES); Loading Loading @@ -176,6 +187,38 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { return result; } @Nullable private Cursor queryOnUsers(List<UserId> userIds, String authority, Bundle queryArgs) throws RemoteException { final List<Cursor> cursors = new ArrayList<>(userIds.size()); for (UserId userId : userIds) { try (ContentProviderClient userClient = DocumentsApplication.acquireUnstableProviderOrThrow( userId.getContentResolver(getContext()), authority)) { Cursor c = userClient.query(mUri, /* projection= */null, queryArgs, mSignal); if (c != null) { cursors.add(new RootCursorWrapper(userId, mUri.getAuthority(), mRoot.rootId, c, /* maxCount= */-1)); } } catch (RemoteException e) { Log.d(TAG, "Failed to query for user " + userId, e); // Searching on other profile may not succeed because profile may be in quiet mode. if (UserId.CURRENT_USER.equals(userId)) { throw e; } } } int size = cursors.size(); switch (size) { case 0: return null; case 1: return cursors.get(0); default: return new MergeCursor(cursors.toArray(new Cursor[size])); } } @Override public void cancelLoadInBackground() { super.cancelLoadInBackground(); Loading Loading @@ -232,9 +275,11 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { // Ensure the loader is stopped onStopLoading(); if (mResult.cursor != null && mObserver != null) { mResult.cursor.unregisterContentObserver(mObserver); } FileUtils.closeQuietly(mResult); mResult = null; mDoc.userId.getContentResolver(getContext()).unregisterContentObserver(mObserver); } } src/com/android/documentsui/base/FilteringCursorWrapper.java +11 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.documentsui.base.SharedMinimal.DEBUG; import static com.android.documentsui.base.SharedMinimal.TAG; import android.database.AbstractCursor; import android.database.ContentObserver; import android.database.Cursor; import android.os.Bundle; import android.provider.DocumentsContract.Document; Loading Loading @@ -136,4 +137,14 @@ public class FilteringCursorWrapper extends AbstractCursor { public boolean isNull(int column) { return mCursor.isNull(column); } @Override public void registerContentObserver(ContentObserver observer) { mCursor.registerContentObserver(observer); } @Override public void unregisterContentObserver(ContentObserver observer) { mCursor.unregisterContentObserver(observer); } } src/com/android/documentsui/base/State.java +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,11 @@ public class State implements android.os.Parcelable { public boolean openableOnly; /** * Represents whether the intent is a cross-profile intent */ public boolean canShareAcrossProfile = false; /** * This is basically a sub-type for the copy operation. It can be either COPY, * COMPRESS, EXTRACT or MOVE. Loading src/com/android/documentsui/picker/PickActivity.java +10 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static com.android.documentsui.base.State.ACTION_OPEN_TREE; import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Color; import android.net.Uri; Loading Loading @@ -206,6 +208,14 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons { final Intent moreApps = new Intent(intent); moreApps.setComponent(null); moreApps.setPackage(null); for (ResolveInfo info : getPackageManager().queryIntentActivities(moreApps, PackageManager.MATCH_DEFAULT_ONLY)) { if (RootsFragment.PROFILE_TARGET_ACTIVITY.equals( info.activityInfo.targetActivity)) { mState.canShareAcrossProfile = true; break; } } RootsFragment.show(getSupportFragmentManager(), moreApps); } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE || Loading src/com/android/documentsui/sidebar/RootsFragment.java +3 −1 Original line number Diff line number Diff line Loading @@ -91,7 +91,7 @@ public class RootsFragment extends Fragment { private static final String TAG = "RootsFragment"; private static final String EXTRA_INCLUDE_APPS = "includeApps"; private static final String PROFILE_TARGET_ACTIVITY = public static final String PROFILE_TARGET_ACTIVITY = "com.android.internal.app.IntentForwarderActivity"; private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500; Loading Loading @@ -384,6 +384,8 @@ public class RootsFragment extends Fragment { // for change personal profile root. if (PROFILE_TARGET_ACTIVITY.equals(info.activityInfo.targetActivity)) { // TODO: only set in current user getBaseActivity().getDisplayState().canShareAcrossProfile = true; profileItem = new ProfileItem(info, info.loadLabel(pm).toString(), mActionHandler); } else { Loading Loading
src/com/android/documentsui/DirectoryLoader.java +52 −7 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import android.content.ContentProviderClient; import android.content.ContentResolver; import android.content.Context; import android.database.Cursor; import android.database.MergeCursor; import android.net.Uri; import android.os.Bundle; import android.os.CancellationSignal; Loading @@ -31,6 +32,7 @@ import android.os.RemoteException; import android.provider.DocumentsContract.Document; import android.util.Log; import androidx.annotation.Nullable; import androidx.loader.content.AsyncTaskLoader; import com.android.documentsui.archives.ArchivesProvider; Loading @@ -42,9 +44,12 @@ import com.android.documentsui.base.Lookup; import com.android.documentsui.base.MimeTypes; import com.android.documentsui.base.RootInfo; import com.android.documentsui.base.State; import com.android.documentsui.base.UserId; import com.android.documentsui.roots.RootCursorWrapper; import com.android.documentsui.sorting.SortModel; import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executor; public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { Loading @@ -57,6 +62,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private final LockingContentObserver mObserver; private final RootInfo mRoot; private final State mState; private final Uri mUri; private final SortModel mModel; private final Lookup<String, String> mFileTypeLookup; Loading @@ -81,6 +87,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { super(context); mFeatures = features; mState = state; mRoot = state.stack.getRoot(); mUri = uri; mModel = state.sortModel; Loading Loading @@ -124,8 +131,16 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { final Bundle queryArgs = new Bundle(); mModel.addQuerySortArgs(queryArgs); final List<UserId> userIds = new ArrayList<>(); if (mSearchMode) { queryArgs.putAll(mQueryArgs); if (mState.canShareAcrossProfile && mRoot.supportsCrossProfile()) { userIds.addAll( DocumentsApplication.getUserIdManager(getContext()).getUserIds()); } } if (userIds.isEmpty()) { userIds.add(mDoc.userId); } if (mFeatures.isContentPagingEnabled()) { Loading @@ -134,17 +149,13 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { DebugFlags.addForcedPagingArgs(queryArgs); } cursor = client.query(mUri, null, queryArgs, mSignal); cursor = queryOnUsers(userIds, authority, queryArgs); if (cursor == null) { throw new RemoteException("Provider returned null"); } cursor.registerContentObserver(mObserver); cursor = new RootCursorWrapper(mDoc.userId, mUri.getAuthority(), mRoot.rootId, cursor, -1); if (mSearchMode && !mFeatures.isFoldersInSearchResultsEnabled()) { // There is no findDocumentPath API. Enable filtering on folders in search mode. cursor = new FilteringCursorWrapper(cursor, null, SEARCH_REJECT_MIMES); Loading Loading @@ -176,6 +187,38 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { return result; } @Nullable private Cursor queryOnUsers(List<UserId> userIds, String authority, Bundle queryArgs) throws RemoteException { final List<Cursor> cursors = new ArrayList<>(userIds.size()); for (UserId userId : userIds) { try (ContentProviderClient userClient = DocumentsApplication.acquireUnstableProviderOrThrow( userId.getContentResolver(getContext()), authority)) { Cursor c = userClient.query(mUri, /* projection= */null, queryArgs, mSignal); if (c != null) { cursors.add(new RootCursorWrapper(userId, mUri.getAuthority(), mRoot.rootId, c, /* maxCount= */-1)); } } catch (RemoteException e) { Log.d(TAG, "Failed to query for user " + userId, e); // Searching on other profile may not succeed because profile may be in quiet mode. if (UserId.CURRENT_USER.equals(userId)) { throw e; } } } int size = cursors.size(); switch (size) { case 0: return null; case 1: return cursors.get(0); default: return new MergeCursor(cursors.toArray(new Cursor[size])); } } @Override public void cancelLoadInBackground() { super.cancelLoadInBackground(); Loading Loading @@ -232,9 +275,11 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { // Ensure the loader is stopped onStopLoading(); if (mResult.cursor != null && mObserver != null) { mResult.cursor.unregisterContentObserver(mObserver); } FileUtils.closeQuietly(mResult); mResult = null; mDoc.userId.getContentResolver(getContext()).unregisterContentObserver(mObserver); } }
src/com/android/documentsui/base/FilteringCursorWrapper.java +11 −0 Original line number Diff line number Diff line Loading @@ -22,6 +22,7 @@ import static com.android.documentsui.base.SharedMinimal.DEBUG; import static com.android.documentsui.base.SharedMinimal.TAG; import android.database.AbstractCursor; import android.database.ContentObserver; import android.database.Cursor; import android.os.Bundle; import android.provider.DocumentsContract.Document; Loading Loading @@ -136,4 +137,14 @@ public class FilteringCursorWrapper extends AbstractCursor { public boolean isNull(int column) { return mCursor.isNull(column); } @Override public void registerContentObserver(ContentObserver observer) { mCursor.registerContentObserver(observer); } @Override public void unregisterContentObserver(ContentObserver observer) { mCursor.unregisterContentObserver(observer); } }
src/com/android/documentsui/base/State.java +5 −0 Original line number Diff line number Diff line Loading @@ -84,6 +84,11 @@ public class State implements android.os.Parcelable { public boolean openableOnly; /** * Represents whether the intent is a cross-profile intent */ public boolean canShareAcrossProfile = false; /** * This is basically a sub-type for the copy operation. It can be either COPY, * COMPRESS, EXTRACT or MOVE. Loading
src/com/android/documentsui/picker/PickActivity.java +10 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,8 @@ import static com.android.documentsui.base.State.ACTION_OPEN_TREE; import static com.android.documentsui.base.State.ACTION_PICK_COPY_DESTINATION; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.ResolveInfo; import android.content.res.Resources; import android.graphics.Color; import android.net.Uri; Loading Loading @@ -206,6 +208,14 @@ public class PickActivity extends BaseActivity implements ActionHandler.Addons { final Intent moreApps = new Intent(intent); moreApps.setComponent(null); moreApps.setPackage(null); for (ResolveInfo info : getPackageManager().queryIntentActivities(moreApps, PackageManager.MATCH_DEFAULT_ONLY)) { if (RootsFragment.PROFILE_TARGET_ACTIVITY.equals( info.activityInfo.targetActivity)) { mState.canShareAcrossProfile = true; break; } } RootsFragment.show(getSupportFragmentManager(), moreApps); } else if (mState.action == ACTION_OPEN || mState.action == ACTION_CREATE || Loading
src/com/android/documentsui/sidebar/RootsFragment.java +3 −1 Original line number Diff line number Diff line Loading @@ -91,7 +91,7 @@ public class RootsFragment extends Fragment { private static final String TAG = "RootsFragment"; private static final String EXTRA_INCLUDE_APPS = "includeApps"; private static final String PROFILE_TARGET_ACTIVITY = public static final String PROFILE_TARGET_ACTIVITY = "com.android.internal.app.IntentForwarderActivity"; private static final int CONTEXT_MENU_ITEM_TIMEOUT = 500; Loading Loading @@ -384,6 +384,8 @@ public class RootsFragment extends Fragment { // for change personal profile root. if (PROFILE_TARGET_ACTIVITY.equals(info.activityInfo.targetActivity)) { // TODO: only set in current user getBaseActivity().getDisplayState().canShareAcrossProfile = true; profileItem = new ProfileItem(info, info.loadLabel(pm).toString(), mActionHandler); } else { Loading