Home | History | Annotate | Download | only in deviceinfo
      1 /*
      2  * Copyright (C) 2016 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;
     18 
     19 import android.app.Activity;
     20 import android.app.LoaderManager;
     21 import android.app.usage.StorageStatsManager;
     22 import android.content.Context;
     23 import android.content.Loader;
     24 import android.graphics.drawable.Drawable;
     25 import android.os.Bundle;
     26 import android.os.UserHandle;
     27 import android.os.UserManager;
     28 import android.os.storage.StorageManager;
     29 import android.os.storage.VolumeInfo;
     30 import android.provider.SearchIndexableResource;
     31 import android.support.annotation.VisibleForTesting;
     32 import android.util.SparseArray;
     33 import android.view.View;
     34 
     35 import com.android.internal.logging.nano.MetricsProto;
     36 import com.android.settings.R;
     37 import com.android.settings.Utils;
     38 import com.android.settings.dashboard.DashboardFragment;
     39 import com.android.settings.deviceinfo.storage.AutomaticStorageManagementSwitchPreferenceController;
     40 import com.android.settings.deviceinfo.storage.CachedStorageValuesHelper;
     41 import com.android.settings.deviceinfo.storage.SecondaryUserController;
     42 import com.android.settings.deviceinfo.storage.StorageAsyncLoader;
     43 import com.android.settings.deviceinfo.storage.StorageItemPreferenceController;
     44 import com.android.settings.deviceinfo.storage.StorageSummaryDonutPreferenceController;
     45 import com.android.settings.deviceinfo.storage.UserIconLoader;
     46 import com.android.settings.deviceinfo.storage.VolumeSizesLoader;
     47 import com.android.settings.search.BaseSearchIndexProvider;
     48 import com.android.settings.search.Indexable;
     49 import com.android.settings.widget.EntityHeaderController;
     50 import com.android.settingslib.applications.StorageStatsSource;
     51 import com.android.settingslib.core.AbstractPreferenceController;
     52 import com.android.settingslib.deviceinfo.PrivateStorageInfo;
     53 import com.android.settingslib.deviceinfo.StorageManagerVolumeProvider;
     54 import com.android.settingslib.wrapper.PackageManagerWrapper;
     55 
     56 import java.util.ArrayList;
     57 import java.util.Arrays;
     58 import java.util.List;
     59 
     60 public class StorageDashboardFragment extends DashboardFragment implements
     61         LoaderManager.LoaderCallbacks<SparseArray<StorageAsyncLoader.AppsStorageResult>> {
     62     private static final String TAG = "StorageDashboardFrag";
     63     private static final int STORAGE_JOB_ID = 0;
     64     private static final int ICON_JOB_ID = 1;
     65     private static final int VOLUME_SIZE_JOB_ID = 2;
     66 
     67     private VolumeInfo mVolume;
     68     private PrivateStorageInfo mStorageInfo;
     69     private SparseArray<StorageAsyncLoader.AppsStorageResult> mAppsResult;
     70     private CachedStorageValuesHelper mCachedStorageValuesHelper;
     71 
     72     private StorageSummaryDonutPreferenceController mSummaryController;
     73     private StorageItemPreferenceController mPreferenceController;
     74     private PrivateVolumeOptionMenuController mOptionMenuController;
     75     private List<AbstractPreferenceController> mSecondaryUsers;
     76 
     77     @Override
     78     public void onCreate(Bundle icicle) {
     79         super.onCreate(icicle);
     80 
     81         // Initialize the storage sizes that we can quickly calc.
     82         final Activity activity = getActivity();
     83         StorageManager sm = activity.getSystemService(StorageManager.class);
     84         mVolume = Utils.maybeInitializeVolume(sm, getArguments());
     85         if (mVolume == null) {
     86             activity.finish();
     87             return;
     88         }
     89 
     90         initializeOptionsMenu(activity);
     91     }
     92 
     93     @VisibleForTesting
     94     void initializeOptionsMenu(Activity activity) {
     95         mOptionMenuController = new PrivateVolumeOptionMenuController(
     96                 activity, mVolume, new PackageManagerWrapper(activity.getPackageManager()));
     97         getLifecycle().addObserver(mOptionMenuController);
     98         setHasOptionsMenu(true);
     99         activity.invalidateOptionsMenu();
    100     }
    101 
    102     @Override
    103     public void onViewCreated(View v, Bundle savedInstanceState) {
    104         super.onViewCreated(v, savedInstanceState);
    105         initializeCacheProvider();
    106         maybeSetLoading(isQuotaSupported());
    107 
    108         final Activity activity = getActivity();
    109         EntityHeaderController.newInstance(activity, this /*fragment*/,
    110                 null /* header view */)
    111                 .setRecyclerView(getListView(), getLifecycle())
    112                 .styleActionBar(activity);
    113 
    114     }
    115 
    116     @Override
    117     public void onResume() {
    118         super.onResume();
    119         getLoaderManager().restartLoader(STORAGE_JOB_ID, Bundle.EMPTY, this);
    120         getLoaderManager()
    121                 .restartLoader(VOLUME_SIZE_JOB_ID, Bundle.EMPTY, new VolumeSizeCallbacks());
    122         getLoaderManager().initLoader(ICON_JOB_ID, Bundle.EMPTY, new IconLoaderCallbacks());
    123     }
    124 
    125     @Override
    126     public int getHelpResource() {
    127         return R.string.help_url_storage_dashboard;
    128     }
    129 
    130     private void onReceivedSizes() {
    131         if (mStorageInfo != null) {
    132             long privateUsedBytes = mStorageInfo.totalBytes - mStorageInfo.freeBytes;
    133             mSummaryController.updateBytes(privateUsedBytes, mStorageInfo.totalBytes);
    134             mPreferenceController.setVolume(mVolume);
    135             mPreferenceController.setUsedSize(privateUsedBytes);
    136             mPreferenceController.setTotalSize(mStorageInfo.totalBytes);
    137             for (int i = 0, size = mSecondaryUsers.size(); i < size; i++) {
    138                 AbstractPreferenceController controller = mSecondaryUsers.get(i);
    139                 if (controller instanceof SecondaryUserController) {
    140                     SecondaryUserController userController = (SecondaryUserController) controller;
    141                     userController.setTotalSize(mStorageInfo.totalBytes);
    142                 }
    143             }
    144         }
    145 
    146         if (mAppsResult == null) {
    147             return;
    148         }
    149 
    150         mPreferenceController.onLoadFinished(mAppsResult, UserHandle.myUserId());
    151         updateSecondaryUserControllers(mSecondaryUsers, mAppsResult);
    152 
    153         // setLoading always causes a flicker, so let's avoid doing it.
    154         if (getView().findViewById(R.id.loading_container).getVisibility() == View.VISIBLE) {
    155             setLoading(false, true);
    156         }
    157     }
    158 
    159     @Override
    160     public int getMetricsCategory() {
    161         return MetricsProto.MetricsEvent.SETTINGS_STORAGE_CATEGORY;
    162     }
    163 
    164     @Override
    165     protected String getLogTag() {
    166         return TAG;
    167     }
    168 
    169     @Override
    170     protected int getPreferenceScreenResId() {
    171         return R.xml.storage_dashboard_fragment;
    172     }
    173 
    174     @Override
    175     protected List<AbstractPreferenceController> createPreferenceControllers(Context context) {
    176         final List<AbstractPreferenceController> controllers = new ArrayList<>();
    177         mSummaryController = new StorageSummaryDonutPreferenceController(context);
    178         controllers.add(mSummaryController);
    179 
    180         StorageManager sm = context.getSystemService(StorageManager.class);
    181         mPreferenceController = new StorageItemPreferenceController(context, this,
    182                 mVolume, new StorageManagerVolumeProvider(sm));
    183         controllers.add(mPreferenceController);
    184 
    185         final UserManager userManager = context.getSystemService(UserManager.class);
    186         mSecondaryUsers = SecondaryUserController.getSecondaryUserControllers(context, userManager);
    187         controllers.addAll(mSecondaryUsers);
    188 
    189         final AutomaticStorageManagementSwitchPreferenceController asmController =
    190                 new AutomaticStorageManagementSwitchPreferenceController(
    191                         context, mMetricsFeatureProvider, getFragmentManager());
    192         getLifecycle().addObserver(asmController);
    193         controllers.add(asmController);
    194         return controllers;
    195     }
    196 
    197     @VisibleForTesting
    198     protected void setVolume(VolumeInfo info) {
    199         mVolume = info;
    200     }
    201 
    202     /**
    203      * Updates the secondary user controller sizes.
    204      */
    205     private void updateSecondaryUserControllers(List<AbstractPreferenceController> controllers,
    206             SparseArray<StorageAsyncLoader.AppsStorageResult> stats) {
    207         for (int i = 0, size = controllers.size(); i < size; i++) {
    208             AbstractPreferenceController controller = controllers.get(i);
    209             if (controller instanceof StorageAsyncLoader.ResultHandler) {
    210                 StorageAsyncLoader.ResultHandler userController =
    211                         (StorageAsyncLoader.ResultHandler) controller;
    212                 userController.handleResult(stats);
    213             }
    214         }
    215     }
    216 
    217     /**
    218      * For Search.
    219      */
    220     public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
    221             new BaseSearchIndexProvider() {
    222                 @Override
    223                 public List<SearchIndexableResource> getXmlResourcesToIndex(
    224                         Context context, boolean enabled) {
    225                     final SearchIndexableResource sir = new SearchIndexableResource(context);
    226                     sir.xmlResId = R.xml.storage_dashboard_fragment;
    227                     return Arrays.asList(sir);
    228                 }
    229 
    230                 @Override
    231                 public List<AbstractPreferenceController> createPreferenceControllers(
    232                         Context context) {
    233                     final StorageManager sm = context.getSystemService(StorageManager.class);
    234                     final UserManager userManager = context.getSystemService(UserManager.class);
    235                     final List<AbstractPreferenceController> controllers = new ArrayList<>();
    236                     controllers.add(new StorageSummaryDonutPreferenceController(context));
    237                     controllers.add(new StorageItemPreferenceController(context, null /* host */,
    238                             null /* volume */, new StorageManagerVolumeProvider(sm)));
    239                     controllers.addAll(SecondaryUserController.getSecondaryUserControllers(
    240                             context, userManager));
    241                     return controllers;
    242                 }
    243 
    244             };
    245 
    246     @Override
    247     public Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> onCreateLoader(int id,
    248             Bundle args) {
    249         final Context context = getContext();
    250         return new StorageAsyncLoader(context, context.getSystemService(UserManager.class),
    251                 mVolume.fsUuid,
    252                 new StorageStatsSource(context),
    253                 new PackageManagerWrapper(context.getPackageManager()));
    254     }
    255 
    256     @Override
    257     public void onLoadFinished(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader,
    258             SparseArray<StorageAsyncLoader.AppsStorageResult> data) {
    259         mAppsResult = data;
    260         maybeCacheFreshValues();
    261         onReceivedSizes();
    262     }
    263 
    264     @Override
    265     public void onLoaderReset(Loader<SparseArray<StorageAsyncLoader.AppsStorageResult>> loader) {
    266     }
    267 
    268     @VisibleForTesting
    269     public void setCachedStorageValuesHelper(CachedStorageValuesHelper helper) {
    270         mCachedStorageValuesHelper = helper;
    271     }
    272 
    273     @VisibleForTesting
    274     public PrivateStorageInfo getPrivateStorageInfo() {
    275         return mStorageInfo;
    276     }
    277 
    278     @VisibleForTesting
    279     public void setPrivateStorageInfo(PrivateStorageInfo info) {
    280         mStorageInfo = info;
    281     }
    282 
    283     @VisibleForTesting
    284     public SparseArray<StorageAsyncLoader.AppsStorageResult> getAppsStorageResult() {
    285         return mAppsResult;
    286     }
    287 
    288     @VisibleForTesting
    289     public void setAppsStorageResult(SparseArray<StorageAsyncLoader.AppsStorageResult> info) {
    290         mAppsResult = info;
    291     }
    292 
    293     @VisibleForTesting
    294     public void initializeCachedValues() {
    295         PrivateStorageInfo info = mCachedStorageValuesHelper.getCachedPrivateStorageInfo();
    296         SparseArray<StorageAsyncLoader.AppsStorageResult> loaderResult =
    297                 mCachedStorageValuesHelper.getCachedAppsStorageResult();
    298         if (info == null || loaderResult == null) {
    299             return;
    300         }
    301 
    302         mStorageInfo = info;
    303         mAppsResult = loaderResult;
    304     }
    305 
    306     @VisibleForTesting
    307     public void maybeSetLoading(boolean isQuotaSupported) {
    308         // If we have fast stats, we load until both have loaded.
    309         // If we have slow stats, we load when we get the total volume sizes.
    310         if ((isQuotaSupported && (mStorageInfo == null || mAppsResult == null)) ||
    311                 (!isQuotaSupported && mStorageInfo == null)) {
    312             setLoading(true /* loading */, false /* animate */);
    313         }
    314     }
    315 
    316     private void initializeCacheProvider() {
    317         mCachedStorageValuesHelper =
    318                 new CachedStorageValuesHelper(getContext(), UserHandle.myUserId());
    319         initializeCachedValues();
    320         onReceivedSizes();
    321     }
    322 
    323     private void maybeCacheFreshValues() {
    324         if (mStorageInfo != null && mAppsResult != null) {
    325             mCachedStorageValuesHelper.cacheResult(
    326                     mStorageInfo, mAppsResult.get(UserHandle.myUserId()));
    327         }
    328     }
    329 
    330     private boolean isQuotaSupported() {
    331         final StorageStatsManager stats = getActivity().getSystemService(StorageStatsManager.class);
    332         return stats.isQuotaSupported(mVolume.fsUuid);
    333     }
    334 
    335     /**
    336      * IconLoaderCallbacks exists because StorageDashboardFragment already implements
    337      * LoaderCallbacks for a different type.
    338      */
    339     public final class IconLoaderCallbacks
    340             implements LoaderManager.LoaderCallbacks<SparseArray<Drawable>> {
    341         @Override
    342         public Loader<SparseArray<Drawable>> onCreateLoader(int id, Bundle args) {
    343             return new UserIconLoader(
    344                     getContext(),
    345                     () -> UserIconLoader.loadUserIconsWithContext(getContext()));
    346         }
    347 
    348         @Override
    349         public void onLoadFinished(
    350                 Loader<SparseArray<Drawable>> loader, SparseArray<Drawable> data) {
    351             mSecondaryUsers
    352                     .stream()
    353                     .filter(controller -> controller instanceof UserIconLoader.UserIconHandler)
    354                     .forEach(
    355                             controller ->
    356                                     ((UserIconLoader.UserIconHandler) controller)
    357                                             .handleUserIcons(data));
    358         }
    359 
    360         @Override
    361         public void onLoaderReset(Loader<SparseArray<Drawable>> loader) {
    362         }
    363     }
    364 
    365     public final class VolumeSizeCallbacks
    366             implements LoaderManager.LoaderCallbacks<PrivateStorageInfo> {
    367         @Override
    368         public Loader<PrivateStorageInfo> onCreateLoader(int id, Bundle args) {
    369             Context context = getContext();
    370             StorageManager sm = context.getSystemService(StorageManager.class);
    371             StorageManagerVolumeProvider smvp = new StorageManagerVolumeProvider(sm);
    372             final StorageStatsManager stats = context.getSystemService(StorageStatsManager.class);
    373             return new VolumeSizesLoader(context, smvp, stats, mVolume);
    374         }
    375 
    376         @Override
    377         public void onLoaderReset(Loader<PrivateStorageInfo> loader) {
    378         }
    379 
    380         @Override
    381         public void onLoadFinished(
    382                 Loader<PrivateStorageInfo> loader, PrivateStorageInfo privateStorageInfo) {
    383             if (privateStorageInfo == null) {
    384                 getActivity().finish();
    385                 return;
    386             }
    387 
    388             mStorageInfo = privateStorageInfo;
    389             maybeCacheFreshValues();
    390             onReceivedSizes();
    391         }
    392     }
    393 }
    394