Home | History | Annotate | Download | only in deletionhelper
      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.storagemanager.deletionhelper;
     18 
     19 import android.content.Context;
     20 import android.support.annotation.VisibleForTesting;
     21 import android.support.v7.preference.Preference;
     22 import android.support.v7.preference.PreferenceScreen;
     23 import android.text.format.Formatter;
     24 import android.util.AttributeSet;
     25 import com.android.internal.logging.MetricsLogger;
     26 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     27 import com.android.storagemanager.R;
     28 import com.android.storagemanager.utils.PreferenceListCache;
     29 import java.util.List;
     30 
     31 /**
     32  * AppDeletionPreferenceGroup is a collapsible checkbox preference group which contains many
     33  * apps to be cleared in the Deletion Helper.
     34  */
     35 public class AppDeletionPreferenceGroup extends CollapsibleCheckboxPreferenceGroup
     36         implements AppDeletionType.AppListener, Preference.OnPreferenceChangeListener {
     37     private static final int ORDER_OFFSET = 100;
     38     private AppDeletionType mBackend;
     39 
     40     @VisibleForTesting PreferenceScreen mScreen;
     41 
     42     public AppDeletionPreferenceGroup(Context context) {
     43         this(context, null);
     44     }
     45 
     46     public AppDeletionPreferenceGroup(Context context, AttributeSet attrs) {
     47         super(context, attrs);
     48         setOnPreferenceChangeListener(this);
     49         updateText();
     50     }
     51 
     52     @Override
     53     public void onAppRebuild(List<AppsAsyncLoader.PackageInfo> apps) {
     54         int appCount = apps.size();
     55         int currentUserId = getContext().getUserId();
     56         PreferenceListCache cache = new PreferenceListCache(this);
     57         for (int i = 0; i < appCount; i++) {
     58             AppsAsyncLoader.PackageInfo app = apps.get(i);
     59 
     60             if (app.userId != currentUserId) {
     61                 continue;
     62             }
     63 
     64             final String packageName = app.packageName;
     65             AppDeletionPreference preference =
     66                     (AppDeletionPreference) cache.getCachedPreference(packageName);
     67             if (preference == null) {
     68                 preference = new AppDeletionPreference(getContext(), app);
     69                 preference.setKey(packageName);
     70                 preference.setOnPreferenceChangeListener(this);
     71             }
     72             addThresholdDependentPreference(preference, isNoThreshold());
     73             preference.setChecked(mBackend.isChecked(packageName));
     74             preference.setOrder(i + ORDER_OFFSET);
     75             preference.updateSummary();
     76         }
     77         cache.removeCachedPrefs();
     78         updateText();
     79     }
     80 
     81     private void addThresholdDependentPreference(
     82             AppDeletionPreference preference, boolean isThresholded) {
     83         if (isNoThreshold()) {
     84             addPreferenceToScreen(preference);
     85         } else {
     86             addPreference(preference);
     87         }
     88     }
     89 
     90     private boolean isNoThreshold() {
     91         return mBackend.getDeletionThreshold() == 0;
     92     }
     93 
     94     @VisibleForTesting
     95     void addPreferenceToScreen(AppDeletionPreference preference) {
     96         if (mScreen == null) {
     97             mScreen = getPreferenceManager().getPreferenceScreen();
     98         }
     99         mScreen.addPreference(preference);
    100     }
    101 
    102     @Override
    103     public boolean onPreferenceChange(Preference preference, Object newValue) {
    104         boolean isChecked = (boolean) newValue;
    105 
    106         // If we have no AppDeletionType, we have no apps to toggle.
    107         if (mBackend == null) {
    108             return true;
    109         }
    110 
    111         if (preference == this) {
    112             for (int i = 0; i < getPreferenceCount(); i++) {
    113                 AppDeletionPreference p = (AppDeletionPreference) getPreference(i);
    114                 p.setOnPreferenceChangeListener(null);
    115                 p.setChecked(isChecked);
    116                 mBackend.setChecked(p.getPackageName(), isChecked);
    117                 p.setOnPreferenceChangeListener(this);
    118             }
    119             updateText();
    120             MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_SELECTION_ALL_APPS,
    121                     isChecked);
    122             return true;
    123         }
    124 
    125         // If a single preference changed, we need to toggle just itself.
    126         AppDeletionPreference p = (AppDeletionPreference) preference;
    127         mBackend.setChecked(p.getPackageName(), isChecked);
    128         logAppToggle(isChecked, p.getPackageName());
    129         updateText();
    130         return true;
    131     }
    132 
    133     @Override
    134     public void onClick() {
    135         super.onClick();
    136         MetricsLogger.action(
    137                 getContext(), MetricsEvent.ACTION_DELETION_APPS_COLLAPSED, isCollapsed());
    138     }
    139 
    140     /**
    141      * Initializes the PreferenceGroup with a source of apps to list.
    142      *
    143      * @param type The AppDeletionType which provides the app list.
    144      */
    145     public void setDeletionType(AppDeletionType type) {
    146         mBackend = type;
    147     }
    148 
    149     private void updateText() {
    150         long freeableBytes = 0;
    151         long deletionThreshold = AppsAsyncLoader.UNUSED_DAYS_DELETION_THRESHOLD;
    152         if (mBackend != null) {
    153             freeableBytes =
    154                     mBackend.getTotalAppsFreeableSpace(DeletionHelperSettings.COUNT_UNCHECKED);
    155             deletionThreshold = mBackend.getDeletionThreshold();
    156             switchSpinnerToCheckboxOrDisablePreference(freeableBytes, mBackend.getLoadingStatus());
    157         }
    158         Context app = getContext();
    159         setTitle(app.getString(R.string.deletion_helper_apps_group_title));
    160         setSummary(
    161                 app.getString(
    162                         R.string.deletion_helper_apps_group_summary,
    163                         Formatter.formatFileSize(app, freeableBytes),
    164                         deletionThreshold));
    165     }
    166 
    167     private void logAppToggle(boolean checked, String packageName) {
    168         if (checked) {
    169             MetricsLogger.action(
    170                     getContext(), MetricsEvent.ACTION_DELETION_SELECTION_APP_ON, packageName);
    171         } else {
    172             MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_SELECTION_APP_OFF,
    173                     packageName);
    174         }
    175     }
    176 }
    177