Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 339c7bcd authored by Kelvin Kwan's avatar Kelvin Kwan
Browse files

Enable DirectoryFragment.onRefresh works when there is no root doc.

This enables us to reload the directory correctly. On next CL,
we will refresh directory when we listened to some event. (e.g. work
profile is unlocked)

Also now we can perform search in directory loader on a disabled user.

* In DirectoryFragment, refresh stack without root doc
DirectoryFragment will now try to reload rootDoc and push it to stack
onRefresh. This is useful when the root doc was temporarily unable to
load (e.g. work profile was turned off)

* Search across profile on an empty stack
Directory takes care of empty stack now. We will restart loader in
loadDocumentsForCurrentStack. If there is only one queriable user,
the same exception message will be updated.

* ProfileTabs
The tab layout will be updated if the current root does not match.
This could happen when opening a search folder from the other user.

* QuickViewIntentBuilder
Now use a correct packageManager to test intent

Bug: 148270816
Bug: 150600030
Bug: 150799134
Test: atest DocumentsUIGoogleTests
Test: manual

Change-Id: Ib6684e4dd2a257f92ee3784297b5568cbd3d21b2
parent 4d5551ae
Loading
Loading
Loading
Loading
+22 −17
Original line number Diff line number Diff line
@@ -471,7 +471,7 @@ public abstract class AbstractActionHandler<T extends FragmentActivity & CommonA
        }

        Intent intent = new QuickViewIntentBuilder(
                mActivity.getPackageManager(),
                mActivity,
                mActivity.getResources(),
                doc,
                mModel,
@@ -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());
            }
@@ -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);
    }
@@ -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()
+27 −2
Original line number Diff line number Diff line
@@ -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);
+14 −7
Original line number Diff line number Diff line
@@ -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;
@@ -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();
@@ -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...
+1 −1
Original line number Diff line number Diff line
@@ -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;
+2 −3
Original line number Diff line number Diff line
@@ -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;
@@ -60,7 +59,7 @@ public class NavigationViewManager {
    private final boolean mShowSearchBar;

    public NavigationViewManager(
            Activity activity,
            BaseActivity activity,
            DrawerController drawer,
            State state,
            NavigationViewManager.Environment env,
@@ -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