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.v7.preference.Preference; 21 import android.text.format.Formatter; 22 import android.util.AttributeSet; 23 import android.view.View; 24 import com.android.internal.logging.MetricsLogger; 25 import com.android.internal.logging.MetricsProto.MetricsEvent; 26 import com.android.settingslib.applications.ApplicationsState; 27 import com.android.storagemanager.deletionhelper.AppStateUsageStatsBridge.UsageStatsState; 28 import com.android.storagemanager.PreferenceListCache; 29 import com.android.storagemanager.R; 30 31 import java.util.List; 32 33 /** 34 * AppDeletionPreferenceGroup is a collapsible checkbox preference group which contains many 35 * apps to be cleared in the Deletion Helper. 36 */ 37 public class AppDeletionPreferenceGroup extends CollapsibleCheckboxPreferenceGroup 38 implements AppDeletionType.AppListener, Preference.OnPreferenceChangeListener { 39 private AppDeletionType mBackend; 40 41 public AppDeletionPreferenceGroup(Context context) { 42 this(context, null); 43 } 44 45 public AppDeletionPreferenceGroup(Context context, AttributeSet attrs) { 46 super(context, attrs); 47 setOnPreferenceChangeListener(this); 48 updateText(); 49 } 50 51 @Override 52 public void onAppRebuild(List<ApplicationsState.AppEntry> apps) { 53 int entryCount = apps.size(); 54 int currentUserId = getContext().getUserId(); 55 PreferenceListCache cache = new PreferenceListCache(this); 56 for (int i = 0; i < entryCount; i++) { 57 ApplicationsState.AppEntry entry = apps.get(i); 58 59 // If it's a different user's app, skip it. 60 UsageStatsState extraData = (UsageStatsState) entry.extraInfo; 61 if (extraData.userId != currentUserId) { 62 continue; 63 } 64 65 final String packageName; 66 synchronized (entry) { 67 packageName = entry.info.packageName; 68 } 69 AppDeletionPreference preference = 70 (AppDeletionPreference) cache.getCachedPreference(packageName); 71 if (preference == null) { 72 preference = new AppDeletionPreference(getContext(), entry); 73 preference.setKey(packageName); 74 preference.setOnPreferenceChangeListener(this); 75 } 76 77 addPreference(preference); 78 preference.setChecked(mBackend.isChecked(packageName)); 79 preference.setOrder(i); 80 preference.updateSummary(); 81 } 82 cache.removeCachedPrefs(); 83 updateText(); 84 } 85 86 @Override 87 public boolean onPreferenceChange(Preference preference, Object newValue) { 88 boolean isChecked = (boolean) newValue; 89 90 // If we have no AppDeletionType, we have no apps to toggle. 91 if (mBackend == null) { 92 return true; 93 } 94 95 if (preference == this) { 96 for (int i = 0; i < getPreferenceCount(); i++) { 97 AppDeletionPreference p = (AppDeletionPreference) getPreference(i); 98 p.setOnPreferenceChangeListener(null); 99 p.setChecked(isChecked); 100 mBackend.setChecked(p.getPackageName(), isChecked); 101 p.setOnPreferenceChangeListener(this); 102 } 103 updateText(); 104 MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_SELECTION_ALL_APPS, 105 isChecked); 106 return true; 107 } 108 109 // If a single preference changed, we need to toggle just itself. 110 AppDeletionPreference p = (AppDeletionPreference) preference; 111 mBackend.setChecked(p.getPackageName(), isChecked); 112 logAppToggle(isChecked, p.getPackageName()); 113 updateText(); 114 return true; 115 } 116 117 @Override 118 public void onClick() { 119 super.onClick(); 120 MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_APPS_COLLAPSED, 121 isCollapsed()); 122 } 123 124 /** 125 * Initializes the PreferenceGroup with a source of apps to list. 126 * @param type The AppDeletionType which provides the app list. 127 */ 128 public void setDeletionType(AppDeletionType type) { 129 mBackend = type; 130 } 131 132 private void updateText() { 133 int eligibleApps = 0; 134 long freeableBytes = 0; 135 if (mBackend != null) { 136 eligibleApps = mBackend.getEligibleApps(); 137 freeableBytes = mBackend.getTotalAppsFreeableSpace(true); 138 } 139 Context app = getContext(); 140 setTitle(app.getString(R.string.deletion_helper_apps_group_title, eligibleApps)); 141 setSummary(app.getString(R.string.deletion_helper_apps_group_summary, 142 Formatter.formatFileSize(app, freeableBytes), 143 AppStateUsageStatsBridge.UNUSED_DAYS_DELETION_THRESHOLD)); 144 } 145 146 private void logAppToggle(boolean checked, String packageName) { 147 if (checked) { 148 MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_SELECTION_APP_ON, 149 packageName); 150 } else { 151 MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_SELECTION_APP_OFF, 152 packageName); 153 } 154 } 155 } 156