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.Manifest; 20 import android.app.Activity; 21 import android.content.pm.PackageManager; 22 import android.os.Bundle; 23 import android.support.v14.preference.PreferenceFragment; 24 import android.text.format.Formatter; 25 import android.view.Menu; 26 import android.view.MenuInflater; 27 import android.view.View; 28 import android.widget.Button; 29 import com.android.internal.logging.MetricsLogger; 30 import com.android.internal.logging.MetricsProto.MetricsEvent; 31 import com.android.settingslib.HelpUtils; 32 import com.android.storagemanager.ButtonBarProvider; 33 import com.android.storagemanager.R; 34 35 import com.android.storagemanager.overlay.FeatureFactory; 36 import com.android.storagemanager.overlay.DeletionHelperFeatureProvider; 37 38 import java.util.ArrayList; 39 import java.util.Collections; 40 import java.util.HashSet; 41 42 /** 43 * Settings screen for the deletion helper, which manually removes data which is not recently used. 44 */ 45 public class DeletionHelperSettings extends PreferenceFragment implements 46 DeletionType.FreeableChangedListener, 47 View.OnClickListener { 48 private static final String APPS_KEY = "apps_group"; 49 private static final String KEY_DOWNLOADS_PREFERENCE = "delete_downloads"; 50 private static final String KEY_PHOTOS_VIDEOS_PREFERENCE = "delete_photos"; 51 private static final int DOWNLOADS_LOADER_ID = 1; 52 53 private AppDeletionPreferenceGroup mApps; 54 private AppDeletionType mAppBackend; 55 private DownloadsDeletionPreferenceGroup mDownloadsPreference; 56 private DownloadsDeletionType mDownloadsDeletion; 57 private PhotosDeletionPreference mPhotoPreference; 58 private DeletionType mPhotoVideoDeletion; 59 private Button mCancel, mFree; 60 private DeletionHelperFeatureProvider mProvider; 61 62 public static DeletionHelperSettings newInstance() { 63 return new DeletionHelperSettings(); 64 } 65 66 @Override 67 public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 68 addPreferencesFromResource(R.xml.deletion_helper_list); 69 mApps = (AppDeletionPreferenceGroup) findPreference(APPS_KEY); 70 71 HashSet<String> checkedApplications = null; 72 if (savedInstanceState != null) { 73 checkedApplications = 74 (HashSet<String>) savedInstanceState.getSerializable( 75 AppDeletionType.EXTRA_CHECKED_SET); 76 } 77 mAppBackend = new AppDeletionType(getActivity().getApplication(), checkedApplications); 78 mAppBackend.registerView(mApps); 79 mAppBackend.registerFreeableChangedListener(this); 80 mApps.setDeletionType(mAppBackend); 81 82 mPhotoPreference = (PhotosDeletionPreference) findPreference(KEY_PHOTOS_VIDEOS_PREFERENCE); 83 mProvider = FeatureFactory.getFactory(getActivity()).getDeletionHelperFeatureProvider(); 84 if (mProvider != null) { 85 mPhotoVideoDeletion = mProvider.createPhotoVideoDeletionType(getContext()); 86 } 87 } 88 89 @Override 90 public void onActivityCreated(Bundle savedInstanceState) { 91 super.onActivityCreated(savedInstanceState); 92 initializeButtons(); 93 setHasOptionsMenu(true); 94 95 Activity activity = getActivity(); 96 if (activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) 97 != PackageManager.PERMISSION_GRANTED) { 98 activity.requestPermissions( 99 new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, 100 0); 101 } 102 103 if (mProvider == null || mPhotoVideoDeletion == null) { 104 getPreferenceScreen().removePreference(mPhotoPreference); 105 mPhotoPreference = null; 106 } else { 107 mPhotoPreference.registerFreeableChangedListener(this); 108 mPhotoPreference.registerDeletionService(mPhotoVideoDeletion); 109 } 110 111 String[] uncheckedFiles = null; 112 if (savedInstanceState != null) { 113 uncheckedFiles = savedInstanceState.getStringArray( 114 DownloadsDeletionType.EXTRA_UNCHECKED_DOWNLOADS); 115 } 116 mDownloadsPreference = 117 (DownloadsDeletionPreferenceGroup) findPreference(KEY_DOWNLOADS_PREFERENCE); 118 mDownloadsDeletion = new DownloadsDeletionType(getActivity(), uncheckedFiles); 119 mDownloadsPreference.registerFreeableChangedListener(this); 120 mDownloadsPreference.registerDeletionService(mDownloadsDeletion); 121 updateFreeButtonText(); 122 } 123 124 @Override 125 public void onResume() { 126 super.onResume(); 127 mAppBackend.onResume(); 128 mDownloadsDeletion.onResume(); 129 130 if (getActivity().checkSelfPermission( 131 Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED) { 132 getLoaderManager().initLoader(DOWNLOADS_LOADER_ID, new Bundle(), mDownloadsDeletion); 133 } 134 135 if (mPhotoVideoDeletion != null) { 136 mPhotoVideoDeletion.onResume(); 137 } 138 } 139 140 141 @Override 142 public void onPause() { 143 super.onPause(); 144 mAppBackend.onPause(); 145 mDownloadsDeletion.onPause(); 146 147 if (mPhotoVideoDeletion != null) { 148 mPhotoVideoDeletion.onPause(); 149 } 150 } 151 152 @Override 153 public void onSaveInstanceState(Bundle outState) { 154 super.onSaveInstanceState(outState); 155 mAppBackend.onSaveInstanceStateBundle(outState); 156 mDownloadsDeletion.onSaveInstanceStateBundle(outState); 157 } 158 159 @Override 160 public void onFreeableChanged(int numItems, long bytesFreeable) { 161 // bytesFreeable is the number of bytes freed by a single deletion type. If it is non-zero, 162 // there is stuff to free and we can enable it. If it is zero, though, we still need to get 163 // getTotalFreeableSpace to check all deletion types. 164 mFree.setEnabled(bytesFreeable != 0 || getTotalFreeableSpace() != 0); 165 updateFreeButtonText(); 166 } 167 168 /** 169 * Clears out the selected apps and data from the device and closes the fragment. 170 */ 171 protected void clearData() { 172 // This should be fine as long as there is only one extra deletion feature. 173 // In the future, this should be done in an async queue in order to not 174 // interfere with the simultaneous PackageDeletionTask. 175 if (mPhotoPreference != null && mPhotoPreference.isChecked()) { 176 mPhotoVideoDeletion.clearFreeableData(getActivity()); 177 } 178 mDownloadsDeletion.clearFreeableData(getActivity()); 179 mAppBackend.clearFreeableData(getActivity()); 180 } 181 182 @Override 183 public void onClick(View v) { 184 if (v.getId() == R.id.next_button) { 185 ConfirmDeletionDialog dialog = 186 ConfirmDeletionDialog.newInstance(getTotalFreeableSpace()); 187 // The 0 is a placeholder for an optional result code. 188 dialog.setTargetFragment(this, 0); 189 dialog.show(getFragmentManager(), ConfirmDeletionDialog.TAG); 190 MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_HELPER_CLEAR); 191 } else { 192 MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_HELPER_CANCEL); 193 getActivity().finish(); 194 } 195 } 196 197 @Override 198 public void onRequestPermissionsResult(int requestCode, String permissions[], 199 int[] grantResults) { 200 if (requestCode == 0) { 201 if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 202 mDownloadsDeletion.onResume(); 203 getLoaderManager().initLoader(DOWNLOADS_LOADER_ID, new Bundle(), 204 mDownloadsDeletion); 205 } 206 } 207 } 208 209 @Override 210 public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { 211 Activity activity = getActivity(); 212 String mHelpUri = getResources().getString(R.string.help_uri_deletion_helper); 213 if (mHelpUri != null && activity != null) { 214 HelpUtils.prepareHelpMenuItem(activity, menu, mHelpUri, getClass().getName()); 215 } 216 } 217 218 private void initializeButtons() { 219 ButtonBarProvider activity = (ButtonBarProvider) getActivity(); 220 activity.getButtonBar().setVisibility(View.VISIBLE); 221 222 mCancel = activity.getSkipButton(); 223 mCancel.setText(R.string.cancel); 224 mCancel.setOnClickListener(this); 225 mCancel.setVisibility(View.VISIBLE); 226 227 mFree = activity.getNextButton(); 228 mFree.setText(R.string.storage_menu_free); 229 mFree.setOnClickListener(this); 230 mFree.setEnabled(false); 231 } 232 233 private void updateFreeButtonText() { 234 mFree.setText(String.format(getActivity().getString(R.string.deletion_helper_free_button), 235 Formatter.formatFileSize(getActivity(), getTotalFreeableSpace()))); 236 } 237 238 private long getTotalFreeableSpace() { 239 long freeableSpace = 0; 240 freeableSpace += mAppBackend.getTotalAppsFreeableSpace(false); 241 if (mPhotoPreference != null) { 242 freeableSpace += mPhotoPreference.getFreeableBytes(); 243 } 244 freeableSpace += mDownloadsDeletion.getFreeableBytes(); 245 return freeableSpace; 246 } 247 }