• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2018 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.tv.settings.device;
18 
19 import android.content.Context;
20 import android.os.Bundle;
21 import android.os.Handler;
22 import android.os.storage.DiskInfo;
23 import android.os.storage.StorageManager;
24 import android.os.storage.VolumeInfo;
25 import android.os.storage.VolumeRecord;
26 import android.support.annotation.Keep;
27 import android.support.v7.preference.Preference;
28 import android.support.v7.preference.PreferenceCategory;
29 import android.util.ArraySet;
30 import android.util.Log;
31 
32 import com.android.internal.logging.nano.MetricsProto;
33 import com.android.tv.settings.R;
34 import com.android.tv.settings.SettingsPreferenceFragment;
35 import com.android.tv.settings.device.storage.MissingStorageFragment;
36 import com.android.tv.settings.device.storage.NewStorageActivity;
37 import com.android.tv.settings.device.storage.StorageFragment;
38 import com.android.tv.settings.device.storage.StoragePreference;
39 
40 import java.io.File;
41 import java.util.ArrayList;
42 import java.util.List;
43 import java.util.Set;
44 
45 /**
46  * The "Storage" screen in TV settings.
47  */
48 @Keep
49 public class StorageSummaryFragment extends SettingsPreferenceFragment {
50     private static final String TAG = "StorageSummaryFragment";
51 
52     private static final String KEY_DEVICE_CATEGORY = "device_storage";
53     private static final String KEY_REMOVABLE_CATEGORY = "removable_storage";
54 
55     private static final int REFRESH_DELAY_MILLIS = 500;
56 
57     private StorageManager mStorageManager;
58     private final StorageSummaryFragment.StorageEventListener
59             mStorageEventListener = new StorageSummaryFragment.StorageEventListener();
60 
61     private final Handler mHandler = new Handler();
62     private final Runnable mRefreshRunnable = new Runnable() {
63         @Override
64         public void run() {
65             refresh();
66         }
67     };
68 
newInstance()69     public static StorageSummaryFragment newInstance() {
70         return new StorageSummaryFragment();
71     }
72 
73     @Override
onCreate(Bundle savedInstanceState)74     public void onCreate(Bundle savedInstanceState) {
75         mStorageManager = getContext().getSystemService(StorageManager.class);
76         super.onCreate(savedInstanceState);
77     }
78 
79     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)80     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
81         setPreferencesFromResource(R.xml.storage_summary, null);
82         findPreference(KEY_REMOVABLE_CATEGORY).setVisible(false);
83     }
84 
85     @Override
onStart()86     public void onStart() {
87         super.onStart();
88         mStorageManager.registerListener(mStorageEventListener);
89     }
90 
91     @Override
onResume()92     public void onResume() {
93         super.onResume();
94         mHandler.removeCallbacks(mRefreshRunnable);
95         // Delay to allow entrance animations to complete
96         mHandler.postDelayed(mRefreshRunnable, REFRESH_DELAY_MILLIS);
97     }
98 
99     @Override
onPause()100     public void onPause() {
101         super.onPause();
102         mHandler.removeCallbacks(mRefreshRunnable);
103     }
104 
105     @Override
onStop()106     public void onStop() {
107         super.onStop();
108         mStorageManager.unregisterListener(mStorageEventListener);
109     }
110 
refresh()111     private void refresh() {
112         if (!isResumed()) {
113             return;
114         }
115         final Context themedContext = getPreferenceManager().getContext();
116 
117         final List<VolumeInfo> volumes = mStorageManager.getVolumes();
118         volumes.sort(VolumeInfo.getDescriptionComparator());
119 
120         final List<VolumeInfo> privateVolumes = new ArrayList<>(volumes.size());
121         final List<VolumeInfo> publicVolumes = new ArrayList<>(volumes.size());
122 
123         // Find mounted volumes
124         for (final VolumeInfo vol : volumes) {
125             if (vol.getType() == VolumeInfo.TYPE_PRIVATE) {
126                 privateVolumes.add(vol);
127             } else if (vol.getType() == VolumeInfo.TYPE_PUBLIC) {
128                 publicVolumes.add(vol);
129             } else {
130                 Log.d(TAG, "Skipping volume " + vol.toString());
131             }
132         }
133 
134         // Find missing private filesystems
135         final List<VolumeRecord> volumeRecords = mStorageManager.getVolumeRecords();
136         final List<VolumeRecord> privateMissingVolumes = new ArrayList<>(volumeRecords.size());
137 
138         for (final VolumeRecord record : volumeRecords) {
139             if (record.getType() == VolumeInfo.TYPE_PRIVATE
140                     && mStorageManager.findVolumeByUuid(record.getFsUuid()) == null) {
141                 privateMissingVolumes.add(record);
142             }
143         }
144 
145         // Find unreadable disks
146         final List<DiskInfo> disks = mStorageManager.getDisks();
147         final List<DiskInfo> unsupportedDisks = new ArrayList<>(disks.size());
148         for (final DiskInfo disk : disks) {
149             if (disk.volumeCount == 0 && disk.size > 0) {
150                 unsupportedDisks.add(disk);
151             }
152         }
153 
154         // Add the prefs
155         final PreferenceCategory deviceCategory =
156                 (PreferenceCategory) findPreference(KEY_DEVICE_CATEGORY);
157         final Set<String> touchedDeviceKeys =
158                 new ArraySet<>(privateVolumes.size() + privateMissingVolumes.size());
159 
160         for (final VolumeInfo volumeInfo : privateVolumes) {
161             final String key = StorageSummaryFragment.VolPreference.makeKey(volumeInfo);
162             touchedDeviceKeys.add(key);
163             StorageSummaryFragment.VolPreference volPreference =
164                         (StorageSummaryFragment.VolPreference) deviceCategory.findPreference(key);
165             if (volPreference == null) {
166                 volPreference = new StorageSummaryFragment.VolPreference(themedContext, volumeInfo);
167             }
168             volPreference.refresh(themedContext, mStorageManager, volumeInfo);
169             deviceCategory.addPreference(volPreference);
170         }
171 
172         for (final VolumeRecord volumeRecord : privateMissingVolumes) {
173             final String key = StorageSummaryFragment.MissingPreference.makeKey(volumeRecord);
174             touchedDeviceKeys.add(key);
175             StorageSummaryFragment.MissingPreference missingPreference =
176                     (StorageSummaryFragment.MissingPreference) deviceCategory.findPreference(key);
177             if (missingPreference == null) {
178                 missingPreference = new StorageSummaryFragment.MissingPreference(
179                             themedContext, volumeRecord);
180             }
181             deviceCategory.addPreference(missingPreference);
182         }
183 
184         for (int i = 0; i < deviceCategory.getPreferenceCount();) {
185             final Preference pref = deviceCategory.getPreference(i);
186             if (touchedDeviceKeys.contains(pref.getKey())) {
187                 i++;
188             } else {
189                 deviceCategory.removePreference(pref);
190             }
191         }
192 
193         final PreferenceCategory removableCategory =
194                 (PreferenceCategory) findPreference(KEY_REMOVABLE_CATEGORY);
195         final int publicCount = publicVolumes.size() + unsupportedDisks.size();
196         final Set<String> touchedRemovableKeys = new ArraySet<>(publicCount);
197         // Only show section if there are public/unknown volumes present
198         removableCategory.setVisible(publicCount > 0);
199 
200         for (final VolumeInfo volumeInfo : publicVolumes) {
201             final String key = StorageSummaryFragment.VolPreference.makeKey(volumeInfo);
202             touchedRemovableKeys.add(key);
203             StorageSummaryFragment.VolPreference volPreference =
204                     (StorageSummaryFragment.VolPreference) removableCategory.findPreference(key);
205             if (volPreference == null) {
206                 volPreference = new StorageSummaryFragment.VolPreference(themedContext, volumeInfo);
207             }
208             volPreference.refresh(themedContext, mStorageManager, volumeInfo);
209             removableCategory.addPreference(volPreference);
210         }
211         for (final DiskInfo diskInfo : unsupportedDisks) {
212             final String key = StorageSummaryFragment.UnsupportedDiskPreference.makeKey(diskInfo);
213             touchedRemovableKeys.add(key);
214             StorageSummaryFragment.UnsupportedDiskPreference unsupportedDiskPreference =
215                     (StorageSummaryFragment.UnsupportedDiskPreference) findPreference(key);
216             if (unsupportedDiskPreference == null) {
217                 unsupportedDiskPreference = new StorageSummaryFragment.UnsupportedDiskPreference(
218                             themedContext, diskInfo);
219             }
220             removableCategory.addPreference(unsupportedDiskPreference);
221         }
222 
223         for (int i = 0; i < removableCategory.getPreferenceCount();) {
224             final Preference pref = removableCategory.getPreference(i);
225             if (touchedRemovableKeys.contains(pref.getKey())) {
226                 i++;
227             } else {
228                 removableCategory.removePreference(pref);
229             }
230         }
231     }
232 
233     private static class VolPreference extends Preference {
VolPreference(Context context, VolumeInfo volumeInfo)234         VolPreference(Context context, VolumeInfo volumeInfo) {
235             super(context);
236             setKey(makeKey(volumeInfo));
237         }
238 
refresh(Context context, StorageManager storageManager, VolumeInfo volumeInfo)239         private void refresh(Context context, StorageManager storageManager,
240                 VolumeInfo volumeInfo) {
241             final String description = storageManager
242                     .getBestVolumeDescription(volumeInfo);
243             setTitle(description);
244             if (volumeInfo.isMountedReadable()) {
245                 setSummary(getSizeString(volumeInfo));
246                 setFragment(StorageFragment.class.getName());
247                 StorageFragment.prepareArgs(getExtras(), volumeInfo);
248             } else {
249                 setSummary(context.getString(R.string.storage_unmount_success, description));
250             }
251         }
252 
getSizeString(VolumeInfo vol)253         private String getSizeString(VolumeInfo vol) {
254             final File path = vol.getPath();
255             if (vol.isMountedReadable() && path != null) {
256                 return String.format(getContext().getString(R.string.storage_size),
257                         StoragePreference.formatSize(getContext(), path.getTotalSpace()));
258             } else {
259                 return null;
260             }
261         }
262 
makeKey(VolumeInfo volumeInfo)263         public static String makeKey(VolumeInfo volumeInfo) {
264             return "VolPref:" + volumeInfo.getId();
265         }
266     }
267 
268     private static class MissingPreference extends Preference {
MissingPreference(Context context, VolumeRecord volumeRecord)269         MissingPreference(Context context, VolumeRecord volumeRecord) {
270             super(context);
271             setKey(makeKey(volumeRecord));
272             setTitle(volumeRecord.getNickname());
273             setSummary(R.string.storage_not_connected);
274             setFragment(MissingStorageFragment.class.getName());
275             MissingStorageFragment.prepareArgs(getExtras(), volumeRecord.getFsUuid());
276         }
277 
makeKey(VolumeRecord volumeRecord)278         public static String makeKey(VolumeRecord volumeRecord) {
279             return "MissingPref:" + volumeRecord.getFsUuid();
280         }
281     }
282 
283     private static class UnsupportedDiskPreference extends Preference {
UnsupportedDiskPreference(Context context, DiskInfo info)284         UnsupportedDiskPreference(Context context, DiskInfo info) {
285             super(context);
286             setKey(makeKey(info));
287             setTitle(info.getDescription());
288             setIntent(NewStorageActivity.getNewStorageLaunchIntent(context, null, info.getId()));
289         }
290 
makeKey(DiskInfo info)291         public static String makeKey(DiskInfo info) {
292             return "UnsupportedPref:" + info.getId();
293         }
294     }
295 
296     private class StorageEventListener extends android.os.storage.StorageEventListener {
297         @Override
onStorageStateChanged(String path, String oldState, String newState)298         public void onStorageStateChanged(String path, String oldState, String newState) {
299             refresh();
300         }
301 
302         @Override
onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)303         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
304             refresh();
305         }
306 
307         @Override
onVolumeRecordChanged(VolumeRecord rec)308         public void onVolumeRecordChanged(VolumeRecord rec) {
309             refresh();
310         }
311 
312         @Override
onVolumeForgotten(String fsUuid)313         public void onVolumeForgotten(String fsUuid) {
314             refresh();
315         }
316 
317         @Override
onDiskScanned(DiskInfo disk, int volumeCount)318         public void onDiskScanned(DiskInfo disk, int volumeCount) {
319             refresh();
320         }
321 
322         @Override
onDiskDestroyed(DiskInfo disk)323         public void onDiskDestroyed(DiskInfo disk) {
324             refresh();
325         }
326 
327     }
328 
329     @Override
getMetricsCategory()330     public int getMetricsCategory() {
331         return MetricsProto.MetricsEvent.SETTINGS_STORAGE_CATEGORY;
332     }
333 }
334