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

Commit 9183d06d authored by Tetiana Meronyk's avatar Tetiana Meronyk Committed by Android (Google) Code Review
Browse files

Merge "Show other users in storage settings"

parents fbf9c8fe 5c0bb75d
Loading
Loading
Loading
Loading
+1 −1
Original line number Diff line number Diff line
@@ -73,7 +73,7 @@
        android:order="108"/>
    <!-- Preference order 100~200 are 'ONLY' for storage category preferences above. -->
    <PreferenceCategory
        android:key="pref_secondary_users"
        android:key="pref_non_current_users"
        android:title="@string/storage_other_users"
        android:order="201" />
</PreferenceScreen>
+1 −1
Original line number Diff line number Diff line
@@ -92,7 +92,7 @@
        android:order="108"/>
    <!-- Preference order 100~200 are 'ONLY' for storage category preferences above. -->
    <PreferenceCategory
        android:key="pref_secondary_users"
        android:key="pref_non_current_users"
        android:title="@string/storage_other_users"
        android:order="201" />
</PreferenceScreen>
+34 −31
Original line number Diff line number Diff line
@@ -16,6 +16,8 @@

package com.android.settings.deviceinfo;

import static java.util.Collections.EMPTY_LIST;

import android.app.settings.SettingsEnums;
import android.app.usage.StorageStatsManager;
import android.content.Context;
@@ -29,13 +31,15 @@ import android.view.View;
import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.deviceinfo.storage.ManageStoragePreferenceController;
import com.android.settings.deviceinfo.storage.SecondaryUserController;
import com.android.settings.deviceinfo.storage.NonCurrentUserController;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageCacheHelper;
import com.android.settings.deviceinfo.storage.StorageEntry;
@@ -49,7 +53,6 @@ import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

/**
 * Storage Settings main UI is composed by 3 fragments:
@@ -70,6 +73,7 @@ public class StorageCategoryFragment extends DashboardFragment
    private static final String TAG = "StorageCategoryFrag";
    private static final String SELECTED_STORAGE_ENTRY_KEY = "selected_storage_entry_key";
    private static final String SUMMARY_PREF_KEY = "storage_summary";
    private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_non_current_users";
    private static final int STORAGE_JOB_ID = 0;
    private static final int ICON_JOB_ID = 1;
    private static final int VOLUME_SIZE_JOB_ID = 2;
@@ -81,7 +85,7 @@ public class StorageCategoryFragment extends DashboardFragment
    private SparseArray<StorageAsyncLoader.StorageResult> mAppsResult;

    private StorageItemPreferenceController mPreferenceController;
    private List<AbstractPreferenceController> mSecondaryUsers;
    private List<NonCurrentUserController> mNonCurrentUsers;
    private boolean mIsWorkProfile;
    private int mUserId;
    private boolean mIsLoadedFromCache;
@@ -98,9 +102,9 @@ public class StorageCategoryFragment extends DashboardFragment
            return;
        }

        // To prevent flicker, hides secondary users preference.
        // To prevent flicker, hides non-current users preference.
        // onReceivedSizes will set it visible for private storage.
        setSecondaryUsersVisible(false);
        setNonCurrentUsersVisible(false);

        if (!mSelectedStorageEntry.isMounted()) {
            // Set null volume to hide category stats.
@@ -150,8 +154,8 @@ public class StorageCategoryFragment extends DashboardFragment
            if (mSelectedStorageEntry != null) {
                refreshUi(mSelectedStorageEntry);
            }
            updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
            setSecondaryUsersVisible(true);
            updateNonCurrentUserControllers(mNonCurrentUsers, mAppsResult);
            setNonCurrentUsersVisible(true);
        }
    }

@@ -217,17 +221,13 @@ public class StorageCategoryFragment extends DashboardFragment
        // Cache total size infor and used size info
        mStorageCacheHelper
                .cacheTotalSizeAndTotalUsedSize(mStorageInfo.totalBytes, privateUsedBytes);
        for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
            final AbstractPreferenceController controller = mSecondaryUsers.get(i);
            if (controller instanceof SecondaryUserController) {
                SecondaryUserController userController = (SecondaryUserController) controller;
        for (NonCurrentUserController userController : mNonCurrentUsers) {
            userController.setTotalSize(mStorageInfo.totalBytes);
        }
        }

        mPreferenceController.onLoadFinished(mAppsResult, mUserId);
        updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
        setSecondaryUsersVisible(true);
        updateNonCurrentUserControllers(mNonCurrentUsers, mAppsResult);
        setNonCurrentUsersVisible(true);
    }

    @Override
@@ -253,20 +253,18 @@ public class StorageCategoryFragment extends DashboardFragment
                null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
        controllers.add(mPreferenceController);

        mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context,
                mUserManager, mIsWorkProfile /* isWorkProfileOnly */);
        controllers.addAll(mSecondaryUsers);

        mNonCurrentUsers = mIsWorkProfile ? EMPTY_LIST :
                NonCurrentUserController.getNonCurrentUserControllers(context, mUserManager);
        controllers.addAll(mNonCurrentUsers);
        return controllers;
    }

    /**
     * Updates the secondary user controller sizes.
     * Updates the non-current user controller sizes.
     */
    private void updateSecondaryUserControllers(List<AbstractPreferenceController> controllers,
    private void updateNonCurrentUserControllers(List<NonCurrentUserController> controllers,
            SparseArray<StorageAsyncLoader.StorageResult> stats) {
        for (int i = 0, size = controllers.size(); i < size; i++) {
            final AbstractPreferenceController controller = controllers.get(i);
        for (AbstractPreferenceController controller : controllers) {
            if (controller instanceof StorageAsyncLoader.ResultHandler) {
                StorageAsyncLoader.ResultHandler userController =
                        (StorageAsyncLoader.ResultHandler) controller;
@@ -296,6 +294,15 @@ public class StorageCategoryFragment extends DashboardFragment
    public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.StorageResult>> loader) {
    }

    @Override
    public void displayResourceTilesToScreen(PreferenceScreen screen) {
        final PreferenceGroup group = screen.findPreference(TARGET_PREFERENCE_GROUP_KEY);
        if (mNonCurrentUsers.isEmpty()) {
            screen.removePreference(group);
        }
        super.displayResourceTilesToScreen(screen);
    }

    @VisibleForTesting
    public PrivateStorageInfo getPrivateStorageInfo() {
        return mStorageInfo;
@@ -335,13 +342,9 @@ public class StorageCategoryFragment extends DashboardFragment
                        .isQuotaSupported(mSelectedStorageEntry.getFsUuid());
    }

    private void setSecondaryUsersVisible(boolean visible) {
        final Optional<SecondaryUserController> secondaryUserController = mSecondaryUsers.stream()
                .filter(controller -> controller instanceof SecondaryUserController)
                .map(controller -> (SecondaryUserController) controller)
                .findAny();
        if (secondaryUserController.isPresent()) {
            secondaryUserController.get().setPreferenceGroupVisible(visible);
    private void setNonCurrentUsersVisible(boolean visible) {
        if (!mNonCurrentUsers.isEmpty()) {
            mNonCurrentUsers.get(0).setPreferenceGroupVisible(visible);
        }
    }

@@ -361,7 +364,7 @@ public class StorageCategoryFragment extends DashboardFragment
        @Override
        public void onLoadFinished(
                Loader<SparseArray<Drawable>> loader, SparseArray<Drawable> data) {
            mSecondaryUsers
            mNonCurrentUsers
                    .stream()
                    .filter(controller -> controller instanceof UserIconLoader.UserIconHandler)
                    .forEach(
+36 −34
Original line number Diff line number Diff line
@@ -32,11 +32,12 @@ import android.os.storage.VolumeRecord;
import android.provider.SearchIndexableResource;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.View;

import androidx.annotation.VisibleForTesting;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;

import com.android.settings.R;
import com.android.settings.Utils;
@@ -44,7 +45,7 @@ import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
import com.android.settings.deviceinfo.storage.DiskInitFragment;
import com.android.settings.deviceinfo.storage.ManageStoragePreferenceController;
import com.android.settings.deviceinfo.storage.SecondaryUserController;
import com.android.settings.deviceinfo.storage.NonCurrentUserController;
import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
import com.android.settings.deviceinfo.storage.StorageCacheHelper;
import com.android.settings.deviceinfo.storage.StorageEntry;
@@ -64,7 +65,6 @@ import com.android.settingslib.search.SearchIndexable;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Optional;

/**
 * Storage Settings main UI is composed by 3 fragments:
@@ -86,6 +86,7 @@ public class StorageDashboardFragment extends DashboardFragment
    private static final String TAG = "StorageDashboardFrag";
    private static final String SUMMARY_PREF_KEY = "storage_summary";
    private static final String SELECTED_STORAGE_ENTRY_KEY = "selected_storage_entry_key";
    private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_non_current_users";
    private static final int STORAGE_JOB_ID = 0;
    private static final int ICON_JOB_ID = 1;
    private static final int VOLUME_SIZE_JOB_ID = 2;
@@ -101,7 +102,7 @@ public class StorageDashboardFragment extends DashboardFragment
    private VolumeOptionMenuController mOptionMenuController;
    private StorageSelectionPreferenceController mStorageSelectionController;
    private StorageUsageProgressBarPreferenceController mStorageUsageProgressBarController;
    private List<AbstractPreferenceController> mSecondaryUsers;
    private List<NonCurrentUserController> mNonCurrentUsers;
    private boolean mIsWorkProfile;
    private int mUserId;
    private boolean mIsLoadedFromCache;
@@ -232,9 +233,9 @@ public class StorageDashboardFragment extends DashboardFragment
        mOptionMenuController.setSelectedStorageEntry(mSelectedStorageEntry);
        getActivity().invalidateOptionsMenu();

        // To prevent flicker, hides secondary users preference.
        // To prevent flicker, hides non-current users preference.
        // onReceivedSizes will set it visible for private storage.
        setSecondaryUsersVisible(false);
        setNonCurrentUsersVisible(false);

        if (!mSelectedStorageEntry.isMounted()) {
            // Set null volume to hide category stats.
@@ -254,7 +255,7 @@ public class StorageDashboardFragment extends DashboardFragment
            mAppsResult = null;
            // Hide the loading spinner if there is cached data.
            if (mStorageCacheHelper.hasCachedSizeInfo()) {
                //TODO(b/220259287): apply cache mechanism to secondary user
                //TODO(b/220259287): apply cache mechanism to non-current user
                mPreferenceController.onLoadFinished(mAppsResult, mUserId);
            } else {
                maybeSetLoading(isQuotaSupported());
@@ -297,8 +298,8 @@ public class StorageDashboardFragment extends DashboardFragment
            mStorageEntries.addAll(
                    StorageUtils.getAllStorageEntries(getContext(), mStorageManager));
            refreshUi();
            updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
            setSecondaryUsersVisible(true);
            updateNonCurrentUserControllers(mNonCurrentUsers, mAppsResult);
            setNonCurrentUsersVisible(true);
        }
    }

@@ -393,17 +394,13 @@ public class StorageDashboardFragment extends DashboardFragment
        // Cache total size and used size
        mStorageCacheHelper
                .cacheTotalSizeAndTotalUsedSize(mStorageInfo.totalBytes, privateUsedBytes);
        for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
            final AbstractPreferenceController controller = mSecondaryUsers.get(i);
            if (controller instanceof SecondaryUserController) {
                SecondaryUserController userController = (SecondaryUserController) controller;
        for (NonCurrentUserController userController : mNonCurrentUsers) {
            userController.setTotalSize(mStorageInfo.totalBytes);
        }
        }

        mPreferenceController.onLoadFinished(mAppsResult, mUserId);
        updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
        setSecondaryUsersVisible(true);
        updateNonCurrentUserControllers(mNonCurrentUsers, mAppsResult);
        setNonCurrentUsersVisible(true);
    }

    @Override
@@ -429,20 +426,19 @@ public class StorageDashboardFragment extends DashboardFragment
                null /* volume */, new StorageManagerVolumeProvider(sm), mIsWorkProfile);
        controllers.add(mPreferenceController);

        mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context,
                mUserManager, mIsWorkProfile /* isWorkProfileOnly */);
        controllers.addAll(mSecondaryUsers);
        mNonCurrentUsers = NonCurrentUserController.getNonCurrentUserControllers(context,
                mUserManager);
        controllers.addAll(mNonCurrentUsers);

        return controllers;
    }

    /**
     * Updates the secondary user controller sizes.
     * Updates the non-current user controller sizes.
     */
    private void updateSecondaryUserControllers(List<AbstractPreferenceController> controllers,
    private void updateNonCurrentUserControllers(List<NonCurrentUserController> controllers,
            SparseArray<StorageAsyncLoader.StorageResult> stats) {
        for (int i = 0, size = controllers.size(); i < size; i++) {
            final AbstractPreferenceController controller = controllers.get(i);
        for (AbstractPreferenceController controller : controllers) {
            if (controller instanceof StorageAsyncLoader.ResultHandler) {
                StorageAsyncLoader.ResultHandler userController =
                        (StorageAsyncLoader.ResultHandler) controller;
@@ -473,8 +469,8 @@ public class StorageDashboardFragment extends DashboardFragment
                    controllers.add(new StorageItemPreferenceController(context, null /* host */,
                            null /* volume */, new StorageManagerVolumeProvider(sm),
                            false /* isWorkProfile */));
                    controllers.addAll(SecondaryUserController.getSecondaryUserControllers(
                            context, userManager, false /* isWorkProfileOnly */));
                    controllers.addAll(NonCurrentUserController.getNonCurrentUserControllers(
                            context, userManager));
                    return controllers;
                }

@@ -501,6 +497,16 @@ public class StorageDashboardFragment extends DashboardFragment
    public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.StorageResult>> loader) {
    }


    @Override
    public void displayResourceTilesToScreen(PreferenceScreen screen) {
        final PreferenceGroup group = screen.findPreference(TARGET_PREFERENCE_GROUP_KEY);
        if (mNonCurrentUsers.isEmpty()) {
            screen.removePreference(group);
        }
        super.displayResourceTilesToScreen(screen);
    }

    @VisibleForTesting
    public PrivateStorageInfo getPrivateStorageInfo() {
        return mStorageInfo;
@@ -540,13 +546,9 @@ public class StorageDashboardFragment extends DashboardFragment
                        .isQuotaSupported(mSelectedStorageEntry.getFsUuid());
    }

    private void setSecondaryUsersVisible(boolean visible) {
        final Optional<SecondaryUserController> secondaryUserController = mSecondaryUsers.stream()
                .filter(controller -> controller instanceof SecondaryUserController)
                .map(controller -> (SecondaryUserController) controller)
                .findAny();
        if (secondaryUserController.isPresent()) {
            secondaryUserController.get().setPreferenceGroupVisible(visible);
    private void setNonCurrentUsersVisible(boolean visible) {
        if (!mNonCurrentUsers.isEmpty()) {
            mNonCurrentUsers.get(0).setPreferenceGroupVisible(visible);
        }
    }

@@ -566,7 +568,7 @@ public class StorageDashboardFragment extends DashboardFragment
        @Override
        public void onLoadFinished(
                Loader<SparseArray<Drawable>> loader, SparseArray<Drawable> data) {
            mSecondaryUsers
            mNonCurrentUsers
                    .stream()
                    .filter(controller -> controller instanceof UserIconLoader.UserIconHandler)
                    .forEach(
+47 −69
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.settings.deviceinfo.storage;

import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.UserInfo;
import android.graphics.drawable.Drawable;
@@ -28,7 +29,6 @@ import androidx.annotation.VisibleForTesting;
import androidx.preference.PreferenceGroup;
import androidx.preference.PreferenceScreen;

import com.android.settings.Utils;
import com.android.settings.core.PreferenceControllerMixin;
import com.android.settings.deviceinfo.StorageItemPreference;
import com.android.settingslib.core.AbstractPreferenceController;
@@ -37,14 +37,14 @@ import java.util.ArrayList;
import java.util.List;

/**
 * SecondaryUserController controls the preferences on the Storage screen which had to do with
 * secondary users.
 * NonCurrentUserController controls the preferences on the Storage screen which had to do with
 * other users.
 */
public class SecondaryUserController extends AbstractPreferenceController implements
public class NonCurrentUserController extends AbstractPreferenceController implements
        PreferenceControllerMixin, StorageAsyncLoader.ResultHandler,
        UserIconLoader.UserIconHandler {
    // PreferenceGroupKey to try to add our preference onto.
    private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_secondary_users";
    private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_non_current_users";
    private static final String PREFERENCE_KEY_BASE = "pref_user_";
    private static final int SIZE_NOT_SET = -1;

@@ -57,60 +57,60 @@ public class SecondaryUserController extends AbstractPreferenceController implem
    private long mSize;
    private long mTotalSizeBytes;
    private boolean mIsVisible;
    private int[] mProfiles;
    private StorageCacheHelper mStorageCacheHelper;

    /**
     * Adds the appropriate controllers to a controller list for handling all secondary users on
     * a device.
     * Adds the appropriate controllers to a controller list for handling all full non current
     * users on a device.
     *
     * @param context           Context for initializing the preference controllers.
     * @param userManager       UserManagerWrapper for figuring out which controllers to add.
     * @param isWorkProfileOnly only shows secondary users of work profile.
     *                          (e.g., it should be true in work profile tab)
     */
    public static List<AbstractPreferenceController> getSecondaryUserControllers(
            Context context, UserManager userManager, boolean isWorkProfileOnly) {

        List<AbstractPreferenceController> controllers = new ArrayList<>();
        UserInfo primaryUser = userManager.getPrimaryUser();
        boolean addedUser = false;
    public static List<NonCurrentUserController> getNonCurrentUserControllers(
            Context context, UserManager userManager) {
        int currentUserId = ActivityManager.getCurrentUser();
        List<NonCurrentUserController> controllers = new ArrayList<>();
        List<UserInfo> infos = userManager.getUsers();
        for (int i = 0, size = infos.size(); i < size; i++) {
            UserInfo info = infos.get(i);
            if (info.isPrimary()) {
                continue;
            }

            if (Utils.isProfileOf(primaryUser, info)) {
        for (UserInfo info : infos) {
            if (info.id == currentUserId || info.isProfile()) {
                continue;
            }

            if (isWorkProfileOnly && !info.isManagedProfile()) {
                continue;
            int[] profiles = userManager.getProfileIds(info.id, false /* enabledOnly */);
            controllers.add(new NonCurrentUserController(context, info, profiles));
        }

            controllers.add(new SecondaryUserController(context, info));
            addedUser = true;
        return controllers;
    }

        if (!addedUser) {
            controllers.add(new NoSecondaryUserController(context));
        }
        return controllers;
    /**
     * Constructor for a given non-current user.
     *
     * @param context Context to initialize the underlying {@link AbstractPreferenceController}.
     * @param info    {@link UserInfo} for the non-current user which these controllers cover.
     * @param profiles list of IDs or user and its profiles
     */
    @VisibleForTesting
    NonCurrentUserController(Context context, @NonNull UserInfo info, @NonNull int[] profiles) {
        super(context);
        mUser = info;
        mSize = SIZE_NOT_SET;
        mStorageCacheHelper = new StorageCacheHelper(context, info.id);
        mProfiles = profiles;
    }

    /**
     * Constructor for a given secondary user.
     * Constructor for a given non-current user.
     *
     * @param context Context to initialize the underlying {@link AbstractPreferenceController}.
     * @param info    {@link UserInfo} for the secondary user which this controllers covers.
     * @param info    {@link UserInfo} for the non-current user which these controllers cover.
     */
    @VisibleForTesting
    SecondaryUserController(Context context, @NonNull UserInfo info) {
    NonCurrentUserController(Context context, @NonNull UserInfo info) {
        super(context);
        mUser = info;
        mSize = SIZE_NOT_SET;
        mStorageCacheHelper = new StorageCacheHelper(context, info.id);
        mProfiles = new int[]{info.id};
    }

    @Override
@@ -140,7 +140,7 @@ public class SecondaryUserController extends AbstractPreferenceController implem
    }

    /**
     * Returns the user for which this is the secondary user controller.
     * Returns the user for which this is the non-current user controller.
     */
    @NonNull
    public UserInfo getUser() {
@@ -169,7 +169,7 @@ public class SecondaryUserController extends AbstractPreferenceController implem
    }

    /**
     * Sets visibility of the PreferenceGroup of secondary user.
     * Sets visibility of the PreferenceGroup of non-current user.
     *
     * @param visible Visibility of the PreferenceGroup.
     */
@@ -187,10 +187,15 @@ public class SecondaryUserController extends AbstractPreferenceController implem
            return;
        }
        final StorageAsyncLoader.StorageResult result = stats.get(getUser().id);

        if (result != null) {
            setSize(result.externalStats.totalBytes, true /* animate */);
            long totalSize = 0;
            for (int id : mProfiles) {
                totalSize += stats.get(id).externalStats.totalBytes;
            }
            setSize(totalSize, true /* animate */);
            // TODO(b/171758224): Update the source of size info
            mStorageCacheHelper.cacheUsedSize(result.externalStats.totalBytes);
            mStorageCacheHelper.cacheUsedSize(totalSize);
        }
    }

@@ -205,31 +210,4 @@ public class SecondaryUserController extends AbstractPreferenceController implem
            mStoragePreference.setIcon(mUserIcon);
        }
    }

    @VisibleForTesting
    static class NoSecondaryUserController extends AbstractPreferenceController implements
            PreferenceControllerMixin {
        public NoSecondaryUserController(Context context) {
            super(context);
        }

        @Override
        public void displayPreference(PreferenceScreen screen) {
            final PreferenceGroup group = screen.findPreference(TARGET_PREFERENCE_GROUP_KEY);
            if (group == null) {
                return;
            }
            screen.removePreference(group);
        }

        @Override
        public boolean isAvailable() {
            return true;
        }

        @Override
        public String getPreferenceKey() {
            return null;
        }
    }
}
Loading