1 /*
2  * Copyright (C) 2021 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.usage.StorageStatsManager;
20 import android.content.Context;
21 import android.os.UserHandle;
22 import android.util.Log;
23 
24 import androidx.annotation.VisibleForTesting;
25 import androidx.preference.Preference;
26 import androidx.preference.PreferenceScreen;
27 
28 import com.android.settings.R;
29 import com.android.settings.core.BasePreferenceController;
30 import com.android.settingslib.utils.ThreadUtils;
31 import com.android.settingslib.widget.UsageProgressBarPreference;
32 
33 import java.io.File;
34 import java.io.IOException;
35 
36 /**
37  * Shows storage summary and progress.
38  */
39 public class StorageUsageProgressBarPreferenceController extends BasePreferenceController {
40 
41     private static final String TAG = "StorageProgressCtrl";
42 
43     private final StorageStatsManager mStorageStatsManager;
44     @VisibleForTesting
45     long mUsedBytes;
46     @VisibleForTesting
47     long mTotalBytes;
48     private UsageProgressBarPreference mUsageProgressBarPreference;
49     private StorageEntry mStorageEntry;
50     boolean mIsUpdateStateFromSelectedStorageEntry;
51     private StorageCacheHelper mStorageCacheHelper;
52 
StorageUsageProgressBarPreferenceController(Context context, String key)53     public StorageUsageProgressBarPreferenceController(Context context, String key) {
54         super(context, key);
55 
56         mStorageStatsManager = context.getSystemService(StorageStatsManager.class);
57         mStorageCacheHelper = new StorageCacheHelper(context, UserHandle.myUserId());
58     }
59 
60     /** Set StorageEntry to display. */
setSelectedStorageEntry(StorageEntry storageEntry)61     public void setSelectedStorageEntry(StorageEntry storageEntry) {
62         mStorageEntry = storageEntry;
63         getStorageStatsAndUpdateUi();
64     }
65 
66     @Override
getAvailabilityStatus()67     public int getAvailabilityStatus() {
68         return AVAILABLE_UNSEARCHABLE;
69     }
70 
71     @Override
displayPreference(PreferenceScreen screen)72     public void displayPreference(PreferenceScreen screen) {
73         mUsageProgressBarPreference = screen.findPreference(getPreferenceKey());
74     }
75 
getStorageStatsAndUpdateUi()76     private void getStorageStatsAndUpdateUi() {
77         // Use cached data for both total size and used size.
78         if (mStorageEntry != null && mStorageEntry.isMounted() && mStorageEntry.isPrivate()) {
79             StorageCacheHelper.StorageCache cachedData = mStorageCacheHelper.retrieveCachedSize();
80             mTotalBytes = cachedData.totalSize;
81             mUsedBytes = cachedData.totalUsedSize;
82             mIsUpdateStateFromSelectedStorageEntry = true;
83             updateState(mUsageProgressBarPreference);
84         }
85         // Get the latest data from StorageStatsManager.
86         ThreadUtils.postOnBackgroundThread(() -> {
87             try {
88                 if (mStorageEntry == null || !mStorageEntry.isMounted()) {
89                     throw new IOException();
90                 }
91 
92                 if (mStorageEntry.isPrivate()) {
93                     // StorageStatsManager can only query private storages.
94                     mTotalBytes = mStorageStatsManager.getTotalBytes(mStorageEntry.getFsUuid());
95                     mUsedBytes = mTotalBytes
96                             - mStorageStatsManager.getFreeBytes(mStorageEntry.getFsUuid());
97                 } else {
98                     final File rootFile = mStorageEntry.getPath();
99                     if (rootFile == null) {
100                         Log.d(TAG, "Mounted public storage has null root path: " + mStorageEntry);
101                         throw new IOException();
102                     }
103                     mTotalBytes = rootFile.getTotalSpace();
104                     mUsedBytes = mTotalBytes - rootFile.getFreeSpace();
105                 }
106             } catch (IOException e) {
107                 // The storage device isn't present.
108                 mTotalBytes = 0;
109                 mUsedBytes = 0;
110             }
111 
112             if (mUsageProgressBarPreference == null) {
113                 return;
114             }
115             mIsUpdateStateFromSelectedStorageEntry = true;
116             ThreadUtils.postOnMainThread(() -> updateState(mUsageProgressBarPreference));
117         });
118     }
119 
120     @Override
updateState(Preference preference)121     public void updateState(Preference preference) {
122         if (!mIsUpdateStateFromSelectedStorageEntry) {
123             // Returns here to avoid jank by unnecessary UI update.
124             return;
125         }
126         mIsUpdateStateFromSelectedStorageEntry = false;
127         mUsageProgressBarPreference.setUsageSummary(StorageUtils.getStorageSummary(
128                 mContext, R.string.storage_usage_summary, mUsedBytes));
129         mUsageProgressBarPreference.setTotalSummary(StorageUtils.getStorageSummary(
130                 mContext, R.string.storage_total_summary, mTotalBytes));
131         mUsageProgressBarPreference.setPercent(mUsedBytes, mTotalBytes);
132     }
133 }
134