Loading src/com/android/documentsui/AbstractActionHandler.java +22 −17 Original line number Diff line number Diff line Loading @@ -471,7 +471,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA } Intent intent = new QuickViewIntentBuilder( mActivity.getPackageManager(), mActivity, mActivity.getResources(), doc, mModel, Loading Loading @@ -575,6 +575,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA mState.stack.push(doc); } else { if (!Objects.equals(mState.stack.getRoot(), stack.getRoot())) { // It is now possible when opening cross-profile folder. Log.w(TAG, "Provider returns " + stack.getRoot() + " rather than expected " + mState.stack.getRoot()); } Loading Loading @@ -757,17 +758,11 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA @Override public void loadDocumentsForCurrentStack() { DocumentStack stack = mState.stack; if (!stack.isRecents() && stack.isEmpty()) { // TODO: we may also need to reload cross-profile supported root with empty stack DirectoryResult result = new DirectoryResult(); // TODO (b/35996595): Consider plumbing through the actual exception, though it might // not be very useful (always pointing to DatabaseUtils#readExceptionFromParcel()). result.exception = new IllegalStateException("Failed to load root document."); mInjector.getModel().update(result); return; } // mState.stack may be empty when we cannot load the root document. // However, we still want to restart loader because we may need to perform search in a // cross-profile scenario. // For RecentsLoader and GlobalSearchLoader, they do not require rootDoc so it is no-op. // For DirectoryLoader, the loader needs to handle the case when stack.peek() returns null. mActivity.getSupportLoaderManager().restartLoader(LOADER_ID, null, mBindings); } Loading Loading @@ -890,14 +885,24 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA loader.setObserver(observer); return loader; } else { // There maybe no root docInfo DocumentInfo rootDoc = mState.stack.peek(); String authority = rootDoc == null ? mState.stack.getRoot().authority : rootDoc.authority; String documentId = rootDoc == null ? mState.stack.getRoot().documentId : rootDoc.documentId; Uri contentsUri = mSearchMgr.isSearching() ? DocumentsContract.buildSearchDocumentsUri( mState.stack.getRoot().authority, mState.stack.getRoot().rootId, mSearchMgr.getCurrentSearch()) : DocumentsContract.buildChildDocumentsUri( mState.stack.peek().authority, mState.stack.peek().documentId); authority, documentId); final Bundle queryArgs = mSearchMgr.isSearching() ? mSearchMgr.buildQueryArgs() Loading src/com/android/documentsui/BaseActivity.java +27 −2 Original line number Diff line number Diff line Loading @@ -267,13 +267,38 @@ public abstract class BaseActivity }); mNavigator.setProfileTabsListener(userId -> { // Reload the roots with the selected user is changed. // There are several possible cases that may trigger this callback. // 1. A user click on tab layout. // 2. A user click on tab layout, when filter is checked. (searching = true) // 3. A user click on a open a dir of a different user in search (stack size > 1) // 4. After tab layout is initialized. if (!mState.stack.isInitialized()) { return; } // Reload the roots when the selected user is changed. // After reloading, we have visually same roots in the drawer. But they are // different by holding different userId. Next time when user select a root, it can // bring the user to correct root doc. final RootsFragment roots = RootsFragment.get(getSupportFragmentManager()); if (roots != null) { roots.onSelectedUserChanged(); } if (mState.stack.size() <= 1) { // We do not load cross-profile root if the stack contains two documents. The // stack may contain >1 docs when the user select a folder of the other user in // search. In that case, we don't want to reload the root. The whole stack // and the root will be updated in openFolderInSearchResult. // When a user filters files by search chips on the root doc, we will be in // searching mode and with stack size 1 (0 if rootDoc cannot be loaded). // The activity will clear search on root picked. If we don't clear the search, // user may see the search result screen show up briefly and then get cleared. mSearchManager.cancelSearch(); mInjector.actions.loadCrossProfileRoot(getCurrentRoot(), userId); } }); mSortController = SortController.create(this, mState.derivedMode, mState.sortModel); Loading src/com/android/documentsui/DirectoryLoader.java +14 −7 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private final Bundle mQueryArgs; private final boolean mPhotoPicking; @Nullable private DocumentInfo mDoc; private CancellationSignal mSignal; private DirectoryResult mResult; Loading Loading @@ -113,7 +114,6 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { mSignal = new CancellationSignal(); } final ContentResolver resolver = mDoc.userId.getContentResolver(getContext()); final String authority = mUri.getAuthority(); final DirectoryResult result = new DirectoryResult(); Loading @@ -138,24 +138,31 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { } } if (userIds.isEmpty()) { userIds.add(mDoc.userId); userIds.add(mRoot.userId); } if (userIds.size() == 1) { if (!mState.canInteractWith(mDoc.userId)) { if (!mState.canInteractWith(mRoot.userId)) { result.exception = new CrossProfileNoPermissionException(); return result; } else if (mDoc.userId.isQuietModeEnabled(getContext())) { } else if (mRoot.userId.isQuietModeEnabled(getContext())) { result.exception = new CrossProfileQuietModeException(); return result; } else if (mDoc == null) { // TODO (b/35996595): Consider plumbing through the actual exception, though it // might not be very useful (always pointing to // DatabaseUtils#readExceptionFromParcel()). result.exception = new IllegalStateException("Failed to load root document."); return result; } } if (mDoc != null && mDoc.isInArchive()) { final ContentResolver resolver = mRoot.userId.getContentResolver(getContext()); client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); if (mDoc.isInArchive()) { ArchivesProvider.acquireArchive(client, mUri); } result.client = client; } if (mFeatures.isContentPagingEnabled()) { // TODO: At some point we don't want forced flags to override real paging... Loading src/com/android/documentsui/IconUtils.java +1 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ public class IconUtils { } else { packageIcon = userId.getDrawable(context, icon); } if (maybeShowBadge) { if (packageIcon != null && maybeShowBadge) { return userId.getUserBadgedIcon(context, packageIcon); } else { return packageIcon; Loading src/com/android/documentsui/NavigationViewManager.java +2 −3 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.documentsui; import static com.android.documentsui.base.SharedMinimal.VERBOSE; import android.app.Activity; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.drawable.Drawable; Loading Loading @@ -60,7 +59,7 @@ public class NavigationViewManager { private final boolean mShowSearchBar; public NavigationViewManager( Activity activity, BaseActivity activity, DrawerController drawer, State state, NavigationViewManager.Environment env, Loading @@ -74,7 +73,7 @@ public class NavigationViewManager { mEnv = env; mBreadcrumb = breadcrumb; mBreadcrumb.setup(env, state, this::onNavigationItemSelected); mProfileTabs = new ProfileTabs(tabLayout, mState, userIdManager, mEnv); mProfileTabs = new ProfileTabs(tabLayout, mState, userIdManager, mEnv, activity); mToolbar.setNavigationOnClickListener( new View.OnClickListener() { Loading Loading
src/com/android/documentsui/AbstractActionHandler.java +22 −17 Original line number Diff line number Diff line Loading @@ -471,7 +471,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA } Intent intent = new QuickViewIntentBuilder( mActivity.getPackageManager(), mActivity, mActivity.getResources(), doc, mModel, Loading Loading @@ -575,6 +575,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA mState.stack.push(doc); } else { if (!Objects.equals(mState.stack.getRoot(), stack.getRoot())) { // It is now possible when opening cross-profile folder. Log.w(TAG, "Provider returns " + stack.getRoot() + " rather than expected " + mState.stack.getRoot()); } Loading Loading @@ -757,17 +758,11 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA @Override public void loadDocumentsForCurrentStack() { DocumentStack stack = mState.stack; if (!stack.isRecents() && stack.isEmpty()) { // TODO: we may also need to reload cross-profile supported root with empty stack DirectoryResult result = new DirectoryResult(); // TODO (b/35996595): Consider plumbing through the actual exception, though it might // not be very useful (always pointing to DatabaseUtils#readExceptionFromParcel()). result.exception = new IllegalStateException("Failed to load root document."); mInjector.getModel().update(result); return; } // mState.stack may be empty when we cannot load the root document. // However, we still want to restart loader because we may need to perform search in a // cross-profile scenario. // For RecentsLoader and GlobalSearchLoader, they do not require rootDoc so it is no-op. // For DirectoryLoader, the loader needs to handle the case when stack.peek() returns null. mActivity.getSupportLoaderManager().restartLoader(LOADER_ID, null, mBindings); } Loading Loading @@ -890,14 +885,24 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA loader.setObserver(observer); return loader; } else { // There maybe no root docInfo DocumentInfo rootDoc = mState.stack.peek(); String authority = rootDoc == null ? mState.stack.getRoot().authority : rootDoc.authority; String documentId = rootDoc == null ? mState.stack.getRoot().documentId : rootDoc.documentId; Uri contentsUri = mSearchMgr.isSearching() ? DocumentsContract.buildSearchDocumentsUri( mState.stack.getRoot().authority, mState.stack.getRoot().rootId, mSearchMgr.getCurrentSearch()) : DocumentsContract.buildChildDocumentsUri( mState.stack.peek().authority, mState.stack.peek().documentId); authority, documentId); final Bundle queryArgs = mSearchMgr.isSearching() ? mSearchMgr.buildQueryArgs() Loading
src/com/android/documentsui/BaseActivity.java +27 −2 Original line number Diff line number Diff line Loading @@ -267,13 +267,38 @@ public abstract class BaseActivity }); mNavigator.setProfileTabsListener(userId -> { // Reload the roots with the selected user is changed. // There are several possible cases that may trigger this callback. // 1. A user click on tab layout. // 2. A user click on tab layout, when filter is checked. (searching = true) // 3. A user click on a open a dir of a different user in search (stack size > 1) // 4. After tab layout is initialized. if (!mState.stack.isInitialized()) { return; } // Reload the roots when the selected user is changed. // After reloading, we have visually same roots in the drawer. But they are // different by holding different userId. Next time when user select a root, it can // bring the user to correct root doc. final RootsFragment roots = RootsFragment.get(getSupportFragmentManager()); if (roots != null) { roots.onSelectedUserChanged(); } if (mState.stack.size() <= 1) { // We do not load cross-profile root if the stack contains two documents. The // stack may contain >1 docs when the user select a folder of the other user in // search. In that case, we don't want to reload the root. The whole stack // and the root will be updated in openFolderInSearchResult. // When a user filters files by search chips on the root doc, we will be in // searching mode and with stack size 1 (0 if rootDoc cannot be loaded). // The activity will clear search on root picked. If we don't clear the search, // user may see the search result screen show up briefly and then get cleared. mSearchManager.cancelSearch(); mInjector.actions.loadCrossProfileRoot(getCurrentRoot(), userId); } }); mSortController = SortController.create(this, mState.derivedMode, mState.sortModel); Loading
src/com/android/documentsui/DirectoryLoader.java +14 −7 Original line number Diff line number Diff line Loading @@ -70,6 +70,7 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { private final Bundle mQueryArgs; private final boolean mPhotoPicking; @Nullable private DocumentInfo mDoc; private CancellationSignal mSignal; private DirectoryResult mResult; Loading Loading @@ -113,7 +114,6 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { mSignal = new CancellationSignal(); } final ContentResolver resolver = mDoc.userId.getContentResolver(getContext()); final String authority = mUri.getAuthority(); final DirectoryResult result = new DirectoryResult(); Loading @@ -138,24 +138,31 @@ public class DirectoryLoader extends AsyncTaskLoader<DirectoryResult> { } } if (userIds.isEmpty()) { userIds.add(mDoc.userId); userIds.add(mRoot.userId); } if (userIds.size() == 1) { if (!mState.canInteractWith(mDoc.userId)) { if (!mState.canInteractWith(mRoot.userId)) { result.exception = new CrossProfileNoPermissionException(); return result; } else if (mDoc.userId.isQuietModeEnabled(getContext())) { } else if (mRoot.userId.isQuietModeEnabled(getContext())) { result.exception = new CrossProfileQuietModeException(); return result; } else if (mDoc == null) { // TODO (b/35996595): Consider plumbing through the actual exception, though it // might not be very useful (always pointing to // DatabaseUtils#readExceptionFromParcel()). result.exception = new IllegalStateException("Failed to load root document."); return result; } } if (mDoc != null && mDoc.isInArchive()) { final ContentResolver resolver = mRoot.userId.getContentResolver(getContext()); client = DocumentsApplication.acquireUnstableProviderOrThrow(resolver, authority); if (mDoc.isInArchive()) { ArchivesProvider.acquireArchive(client, mUri); } result.client = client; } if (mFeatures.isContentPagingEnabled()) { // TODO: At some point we don't want forced flags to override real paging... Loading
src/com/android/documentsui/IconUtils.java +1 −1 Original line number Diff line number Diff line Loading @@ -38,7 +38,7 @@ public class IconUtils { } else { packageIcon = userId.getDrawable(context, icon); } if (maybeShowBadge) { if (packageIcon != null && maybeShowBadge) { return userId.getUserBadgedIcon(context, packageIcon); } else { return packageIcon; Loading
src/com/android/documentsui/NavigationViewManager.java +2 −3 Original line number Diff line number Diff line Loading @@ -18,7 +18,6 @@ package com.android.documentsui; import static com.android.documentsui.base.SharedMinimal.VERBOSE; import android.app.Activity; import android.content.res.Resources; import android.graphics.Outline; import android.graphics.drawable.Drawable; Loading Loading @@ -60,7 +59,7 @@ public class NavigationViewManager { private final boolean mShowSearchBar; public NavigationViewManager( Activity activity, BaseActivity activity, DrawerController drawer, State state, NavigationViewManager.Environment env, Loading @@ -74,7 +73,7 @@ public class NavigationViewManager { mEnv = env; mBreadcrumb = breadcrumb; mBreadcrumb.setup(env, state, this::onNavigationItemSelected); mProfileTabs = new ProfileTabs(tabLayout, mState, userIdManager, mEnv); mProfileTabs = new ProfileTabs(tabLayout, mState, userIdManager, mEnv, activity); mToolbar.setNavigationOnClickListener( new View.OnClickListener() { Loading