1 /*
2  * Copyright (C) 2015 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.storage;
18 
19 import android.app.ActivityManager;
20 import android.content.pm.PackageManager;
21 import android.os.Bundle;
22 import android.os.Environment;
23 import android.os.storage.StorageManager;
24 import android.os.storage.VolumeInfo;
25 import android.support.v17.preference.LeanbackPreferenceFragment;
26 import android.support.v7.preference.Preference;
27 import android.support.v7.preference.PreferenceScreen;
28 import android.text.TextUtils;
29 import android.util.Log;
30 
31 import com.android.settingslib.deviceinfo.StorageMeasurement;
32 import com.android.tv.settings.R;
33 import com.android.tv.settings.device.apps.AppsFragment;
34 
35 import java.util.HashMap;
36 import java.util.List;
37 
38 public class StorageFragment extends LeanbackPreferenceFragment {
39     private static final String TAG = "StorageFragment";
40 
41     private static final String KEY_MIGRATE = "migrate";
42     private static final String KEY_EJECT = "eject";
43     private static final String KEY_ERASE = "erase";
44     private static final String KEY_APPS_USAGE = "apps_usage";
45     private static final String KEY_DCIM_USAGE = "dcim_usage";
46     private static final String KEY_MUSIC_USAGE = "music_usage";
47     private static final String KEY_DOWNLOADS_USAGE = "downloads_usage";
48     private static final String KEY_CACHE_USAGE = "cache_usage";
49     private static final String KEY_MISC_USAGE = "misc_usage";
50     private static final String KEY_AVAILABLE = "available";
51 
52     private StorageManager mStorageManager;
53     private PackageManager mPackageManager;
54 
55     private VolumeInfo mVolumeInfo;
56 
57     private StorageMeasurement mMeasure;
58     private final StorageMeasurement.MeasurementReceiver mMeasurementReceiver =
59             new MeasurementReceiver();
60     private final StorageEventListener mStorageEventListener = new StorageEventListener();
61 
62     private Preference mMigratePref;
63     private Preference mEjectPref;
64     private Preference mErasePref;
65     private StoragePreference mAppsUsagePref;
66     private StoragePreference mDcimUsagePref;
67     private StoragePreference mMusicUsagePref;
68     private StoragePreference mDownloadsUsagePref;
69     private StoragePreference mCacheUsagePref;
70     private StoragePreference mMiscUsagePref;
71     private StoragePreference mAvailablePref;
72 
prepareArgs(Bundle bundle, VolumeInfo volumeInfo)73     public static void prepareArgs(Bundle bundle, VolumeInfo volumeInfo) {
74         bundle.putString(VolumeInfo.EXTRA_VOLUME_ID, volumeInfo.getId());
75     }
76 
77     @Override
onCreate(Bundle savedInstanceState)78     public void onCreate(Bundle savedInstanceState) {
79         mStorageManager = getContext().getSystemService(StorageManager.class);
80         mPackageManager = getContext().getPackageManager();
81 
82         mVolumeInfo = mStorageManager.findVolumeById(
83                 getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID));
84 
85         super.onCreate(savedInstanceState);
86     }
87 
88     @Override
onStart()89     public void onStart() {
90         super.onStart();
91         mStorageManager.registerListener(mStorageEventListener);
92         startMeasurement();
93     }
94 
95     @Override
onResume()96     public void onResume() {
97         super.onResume();
98         mVolumeInfo = mStorageManager.findVolumeById(
99                 getArguments().getString(VolumeInfo.EXTRA_VOLUME_ID));
100         if (mVolumeInfo == null || !mVolumeInfo.isMountedReadable()) {
101             getFragmentManager().popBackStack();
102         } else {
103             refresh();
104         }
105     }
106 
107     @Override
onStop()108     public void onStop() {
109         super.onStop();
110         mStorageManager.unregisterListener(mStorageEventListener);
111         stopMeasurement();
112     }
113 
114     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)115     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
116         setPreferencesFromResource(R.xml.storage, null);
117 
118         final PreferenceScreen screen = getPreferenceScreen();
119         screen.setTitle(mStorageManager.getBestVolumeDescription(mVolumeInfo));
120 
121         mMigratePref = findPreference(KEY_MIGRATE);
122         mEjectPref = findPreference(KEY_EJECT);
123         mErasePref = findPreference(KEY_ERASE);
124 
125         mAppsUsagePref = (StoragePreference) findPreference(KEY_APPS_USAGE);
126         mDcimUsagePref = (StoragePreference) findPreference(KEY_DCIM_USAGE);
127         mMusicUsagePref = (StoragePreference) findPreference(KEY_MUSIC_USAGE);
128         mDownloadsUsagePref = (StoragePreference) findPreference(KEY_DOWNLOADS_USAGE);
129         mCacheUsagePref = (StoragePreference) findPreference(KEY_CACHE_USAGE);
130         mMiscUsagePref = (StoragePreference) findPreference(KEY_MISC_USAGE);
131         mAvailablePref = (StoragePreference) findPreference(KEY_AVAILABLE);
132     }
133 
refresh()134     private void refresh() {
135         boolean showMigrate = false;
136         final VolumeInfo currentExternal = mPackageManager.getPrimaryStorageCurrentVolume();
137         // currentExternal will be null if the drive is not mounted. Don't offer the option to
138         // migrate if so.
139         if (currentExternal != null
140                 && !TextUtils.equals(currentExternal.getId(), mVolumeInfo.getId())) {
141             final List<VolumeInfo> candidates =
142                     mPackageManager.getPrimaryStorageCandidateVolumes();
143             for (final VolumeInfo candidate : candidates) {
144                 if (TextUtils.equals(candidate.getId(), mVolumeInfo.getId())) {
145                     showMigrate = true;
146                     break;
147                 }
148             }
149         }
150 
151         mMigratePref.setVisible(showMigrate);
152         mMigratePref.setIntent(
153                 MigrateStorageActivity.getLaunchIntent(getContext(), mVolumeInfo.getId(), true));
154 
155         final String description = mStorageManager.getBestVolumeDescription(mVolumeInfo);
156 
157         final boolean privateInternal = VolumeInfo.ID_PRIVATE_INTERNAL.equals(mVolumeInfo.getId());
158         final boolean isPrivate = mVolumeInfo.getType() == VolumeInfo.TYPE_PRIVATE;
159 
160         mEjectPref.setVisible(!privateInternal);
161         mEjectPref.setIntent(UnmountActivity.getIntent(getContext(), mVolumeInfo.getId(),
162                 description));
163         mErasePref.setVisible(!privateInternal);
164         if (isPrivate) {
165             mErasePref.setIntent(
166                     FormatActivity.getFormatAsPublicIntent(getContext(), mVolumeInfo.getDiskId()));
167             mErasePref.setTitle(R.string.storage_format_as_public);
168         } else {
169             mErasePref.setIntent(
170                     FormatActivity.getFormatAsPrivateIntent(getContext(), mVolumeInfo.getDiskId()));
171             mErasePref.setTitle(R.string.storage_format_as_private);
172         }
173 
174         mAppsUsagePref.setVisible(isPrivate);
175         mAppsUsagePref.setFragment(AppsFragment.class.getName());
176         AppsFragment.prepareArgs(mAppsUsagePref.getExtras(), mVolumeInfo.fsUuid, description);
177         mDcimUsagePref.setVisible(isPrivate);
178         mMusicUsagePref.setVisible(isPrivate);
179         mDownloadsUsagePref.setVisible(isPrivate);
180         mCacheUsagePref.setVisible(isPrivate);
181         mCacheUsagePref.setFragment(ConfirmClearCacheFragment.class.getName());
182     }
183 
startMeasurement()184     private void startMeasurement() {
185         if (mVolumeInfo != null && mVolumeInfo.isMountedReadable()) {
186             final VolumeInfo sharedVolume = mStorageManager.findEmulatedForPrivate(mVolumeInfo);
187             mMeasure = new StorageMeasurement(getContext(), mVolumeInfo, sharedVolume);
188             mMeasure.setReceiver(mMeasurementReceiver);
189             mMeasure.forceMeasure();
190         }
191     }
192 
stopMeasurement()193     private void stopMeasurement() {
194         if (mMeasure != null) {
195             mMeasure.onDestroy();
196         }
197     }
198 
updateDetails(StorageMeasurement.MeasurementDetails details)199     private void updateDetails(StorageMeasurement.MeasurementDetails details) {
200         final int currentUser = ActivityManager.getCurrentUser();
201         final long dcimSize = totalValues(details.mediaSize.get(currentUser),
202                 Environment.DIRECTORY_DCIM,
203                 Environment.DIRECTORY_MOVIES, Environment.DIRECTORY_PICTURES);
204 
205         final long musicSize = totalValues(details.mediaSize.get(currentUser),
206                 Environment.DIRECTORY_MUSIC,
207                 Environment.DIRECTORY_ALARMS, Environment.DIRECTORY_NOTIFICATIONS,
208                 Environment.DIRECTORY_RINGTONES, Environment.DIRECTORY_PODCASTS);
209 
210         final long downloadsSize = totalValues(details.mediaSize.get(currentUser),
211                 Environment.DIRECTORY_DOWNLOADS);
212 
213         mAvailablePref.setSize(details.availSize);
214         mAppsUsagePref.setSize(details.appsSize.get(currentUser));
215         mDcimUsagePref.setSize(dcimSize);
216         mMusicUsagePref.setSize(musicSize);
217         mDownloadsUsagePref.setSize(downloadsSize);
218         mCacheUsagePref.setSize(details.cacheSize);
219         mMiscUsagePref.setSize(details.miscSize.get(currentUser));
220     }
221 
totalValues(HashMap<String, Long> map, String... keys)222     private static long totalValues(HashMap<String, Long> map, String... keys) {
223         long total = 0;
224         if (map != null) {
225             for (String key : keys) {
226                 if (map.containsKey(key)) {
227                     total += map.get(key);
228                 }
229             }
230         } else {
231             Log.w(TAG,
232                     "MeasurementDetails mediaSize array does not have key for current user " +
233                             ActivityManager.getCurrentUser());
234         }
235         return total;
236     }
237 
238     private class MeasurementReceiver implements StorageMeasurement.MeasurementReceiver {
239 
240         @Override
onDetailsChanged(StorageMeasurement.MeasurementDetails details)241         public void onDetailsChanged(StorageMeasurement.MeasurementDetails details) {
242             updateDetails(details);
243         }
244     }
245 
246     private class StorageEventListener extends android.os.storage.StorageEventListener {
247         @Override
onVolumeStateChanged(VolumeInfo vol, int oldState, int newState)248         public void onVolumeStateChanged(VolumeInfo vol, int oldState, int newState) {
249             mVolumeInfo = vol;
250             if (isResumed()) {
251                 if (mVolumeInfo.isMountedReadable()) {
252                     refresh();
253                 } else {
254                     getFragmentManager().popBackStack();
255                 }
256             }
257         }
258     }
259 }
260