1 /*
2  * Copyright (C) 2017 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.settings.deviceinfo.storage;
18 
19 import android.app.ActivityManager;
20 import android.content.Context;
21 import android.content.pm.UserInfo;
22 import android.graphics.drawable.Drawable;
23 import android.os.UserManager;
24 import android.util.SparseArray;
25 
26 import androidx.annotation.NonNull;
27 import androidx.annotation.Nullable;
28 import androidx.annotation.VisibleForTesting;
29 import androidx.preference.PreferenceGroup;
30 import androidx.preference.PreferenceScreen;
31 
32 import com.android.settings.core.PreferenceControllerMixin;
33 import com.android.settings.deviceinfo.StorageItemPreference;
34 import com.android.settingslib.core.AbstractPreferenceController;
35 
36 import java.util.ArrayList;
37 import java.util.List;
38 
39 /**
40  * NonCurrentUserController controls the preferences on the Storage screen which had to do with
41  * other users.
42  */
43 public class NonCurrentUserController extends AbstractPreferenceController implements
44         PreferenceControllerMixin, StorageAsyncLoader.ResultHandler,
45         UserIconLoader.UserIconHandler {
46     // PreferenceGroupKey to try to add our preference onto.
47     private static final String TARGET_PREFERENCE_GROUP_KEY = "pref_non_current_users";
48     private static final String PREFERENCE_KEY_BASE = "pref_user_";
49     private static final int SIZE_NOT_SET = -1;
50 
51     private @NonNull
52     UserInfo mUser;
53     private @Nullable
54     StorageItemPreference mStoragePreference;
55     private PreferenceGroup mPreferenceGroup;
56     private Drawable mUserIcon;
57     private long mSize;
58     private long mTotalSizeBytes;
59     private boolean mIsVisible;
60     private int[] mProfiles;
61     private StorageCacheHelper mStorageCacheHelper;
62 
63     /**
64      * Adds the appropriate controllers to a controller list for handling all full non current
65      * users on a device.
66      *
67      * @param context     Context for initializing the preference controllers.
68      * @param userManager UserManagerWrapper for figuring out which controllers to add.
69      */
getNonCurrentUserControllers( Context context, UserManager userManager)70     public static List<NonCurrentUserController> getNonCurrentUserControllers(
71             Context context, UserManager userManager) {
72         int currentUserId = ActivityManager.getCurrentUser();
73         List<NonCurrentUserController> controllers = new ArrayList<>();
74         List<UserInfo> infos = userManager.getUsers();
75         for (UserInfo info : infos) {
76             if (info.id == currentUserId || !info.isFull()) {
77                 continue;
78             }
79             int[] profiles = userManager.getProfileIds(info.id, false /* enabledOnly */);
80             controllers.add(new NonCurrentUserController(context, info, profiles));
81         }
82         return controllers;
83     }
84 
85     /**
86      * Constructor for a given non-current user.
87      *
88      * @param context  Context to initialize the underlying {@link AbstractPreferenceController}.
89      * @param info     {@link UserInfo} for the non-current user which these controllers cover.
90      * @param profiles list of IDs or user and its profiles
91      */
92     @VisibleForTesting
NonCurrentUserController(Context context, @NonNull UserInfo info, @NonNull int[] profiles)93     NonCurrentUserController(Context context, @NonNull UserInfo info, @NonNull int[] profiles) {
94         super(context);
95         mUser = info;
96         mSize = SIZE_NOT_SET;
97         mStorageCacheHelper = new StorageCacheHelper(context, info.id);
98         mProfiles = profiles;
99     }
100 
101     /**
102      * Constructor for a given non-current user.
103      *
104      * @param context Context to initialize the underlying {@link AbstractPreferenceController}.
105      * @param info    {@link UserInfo} for the non-current user which these controllers cover.
106      */
107     @VisibleForTesting
NonCurrentUserController(Context context, @NonNull UserInfo info)108     NonCurrentUserController(Context context, @NonNull UserInfo info) {
109         super(context);
110         mUser = info;
111         mSize = SIZE_NOT_SET;
112         mStorageCacheHelper = new StorageCacheHelper(context, info.id);
113         mProfiles = new int[]{info.id};
114     }
115 
116     @Override
displayPreference(PreferenceScreen screen)117     public void displayPreference(PreferenceScreen screen) {
118         if (mStoragePreference == null) {
119             mStoragePreference = new StorageItemPreference(screen.getContext());
120 
121             mPreferenceGroup = screen.findPreference(TARGET_PREFERENCE_GROUP_KEY);
122             mStoragePreference.setTitle(mUser.name);
123             mStoragePreference.setKey(PREFERENCE_KEY_BASE + mUser.id);
124             setSize(mStorageCacheHelper.retrieveUsedSize(), false /* animate */);
125 
126             mPreferenceGroup.setVisible(mIsVisible);
127             mPreferenceGroup.addPreference(mStoragePreference);
128             maybeSetIcon();
129         }
130     }
131 
132     @Override
isAvailable()133     public boolean isAvailable() {
134         return true;
135     }
136 
137     @Override
getPreferenceKey()138     public String getPreferenceKey() {
139         return mStoragePreference != null ? mStoragePreference.getKey() : null;
140     }
141 
142     /**
143      * Returns the user for which this is the non-current user controller.
144      */
145     @NonNull
getUser()146     public UserInfo getUser() {
147         return mUser;
148     }
149 
150     /**
151      * Sets the size for the preference.
152      *
153      * @param size Size in bytes.
154      */
setSize(long size, boolean animate)155     public void setSize(long size, boolean animate) {
156         mSize = size;
157         if (mStoragePreference != null) {
158             mStoragePreference.setStorageSize(mSize, mTotalSizeBytes, animate);
159         }
160     }
161 
162     /**
163      * Sets the total size for the preference for the progress bar.
164      *
165      * @param totalSizeBytes Total size in bytes.
166      */
setTotalSize(long totalSizeBytes)167     public void setTotalSize(long totalSizeBytes) {
168         mTotalSizeBytes = totalSizeBytes;
169     }
170 
171     /**
172      * Sets visibility of the PreferenceGroup of non-current user.
173      *
174      * @param visible Visibility of the PreferenceGroup.
175      */
setPreferenceGroupVisible(boolean visible)176     public void setPreferenceGroupVisible(boolean visible) {
177         mIsVisible = visible;
178         if (mPreferenceGroup != null) {
179             mPreferenceGroup.setVisible(mIsVisible);
180         }
181     }
182 
183     @Override
handleResult(SparseArray<StorageAsyncLoader.StorageResult> stats)184     public void handleResult(SparseArray<StorageAsyncLoader.StorageResult> stats) {
185         if (stats == null) {
186             setSize(mStorageCacheHelper.retrieveUsedSize(), false /* animate */);
187             return;
188         }
189         final StorageAsyncLoader.StorageResult result = stats.get(getUser().id);
190 
191         if (result != null) {
192             long totalSize = 0;
193             for (int id : mProfiles) {
194                 totalSize += stats.get(id).externalStats.totalBytes;
195             }
196             setSize(totalSize, true /* animate */);
197             // TODO(b/171758224): Update the source of size info
198             mStorageCacheHelper.cacheUsedSize(totalSize);
199         }
200     }
201 
202     @Override
handleUserIcons(SparseArray<Drawable> fetchedIcons)203     public void handleUserIcons(SparseArray<Drawable> fetchedIcons) {
204         mUserIcon = fetchedIcons.get(mUser.id);
205         maybeSetIcon();
206     }
207 
maybeSetIcon()208     private void maybeSetIcon() {
209         if (mUserIcon != null && mStoragePreference != null) {
210             mStoragePreference.setIcon(mUserIcon);
211         }
212     }
213 }
214