• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.support.v7.preference.PreferenceScreen;
25 import android.text.format.Formatter;
26 import android.view.Menu;
27 import android.view.MenuInflater;
28 import android.view.View;
29 import android.widget.Button;
30 import com.android.internal.logging.MetricsLogger;
31 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
32 import com.android.settingslib.HelpUtils;
33 import com.android.storagemanager.ButtonBarProvider;
34 import com.android.storagemanager.R;
35 import com.android.storagemanager.overlay.DeletionHelperFeatureProvider;
36 import com.android.storagemanager.overlay.FeatureFactory;
37 import java.util.ArrayList;
38 import java.util.HashSet;
39 import java.util.List;
40 
41 /**
42  * Settings screen for the deletion helper, which manually removes data which is not recently used.
43  */
44 public class DeletionHelperSettings extends PreferenceFragment
45         implements DeletionType.FreeableChangedListener, View.OnClickListener {
46     public static final boolean COUNT_UNCHECKED = true;
47     public static final boolean COUNT_CHECKED_ONLY = false;
48 
49     protected static final String APPS_KEY = "apps_group";
50     protected static final String KEY_DOWNLOADS_PREFERENCE = "delete_downloads";
51     protected static final String KEY_PHOTOS_VIDEOS_PREFERENCE = "delete_photos";
52 
53     private static final String THRESHOLD_KEY = "threshold_key";
54     private static final int DOWNLOADS_LOADER_ID = 1;
55     private static final int NUM_DELETION_TYPES = 3;
56 
57     private List<DeletionType> mDeletableContentList;
58     private AppDeletionPreferenceGroup mApps;
59     private AppDeletionType mAppBackend;
60     private DownloadsDeletionPreferenceGroup mDownloadsPreference;
61     private DownloadsDeletionType mDownloadsDeletion;
62     private PhotosDeletionPreference mPhotoPreference;
63     private DeletionType mPhotoVideoDeletion;
64     private Button mCancel, mFree;
65     private DeletionHelperFeatureProvider mProvider;
66     private int mThresholdType;
67 
newInstance(int thresholdType)68     public static DeletionHelperSettings newInstance(int thresholdType) {
69         DeletionHelperSettings instance = new DeletionHelperSettings();
70         Bundle bundle = new Bundle(1);
71         bundle.putInt(THRESHOLD_KEY, thresholdType);
72         instance.setArguments(bundle);
73         return instance;
74     }
75 
76     @Override
onCreatePreferences(Bundle savedInstanceState, String rootKey)77     public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
78         addPreferencesFromResource(R.xml.deletion_helper_list);
79         mThresholdType = getArguments().getInt(THRESHOLD_KEY, AppsAsyncLoader.NORMAL_THRESHOLD);
80         mApps = (AppDeletionPreferenceGroup) findPreference(APPS_KEY);
81         mPhotoPreference = (PhotosDeletionPreference) findPreference(KEY_PHOTOS_VIDEOS_PREFERENCE);
82         mProvider = FeatureFactory.getFactory(getActivity()).getDeletionHelperFeatureProvider();
83         if (mProvider != null) {
84             mPhotoVideoDeletion =
85                     mProvider.createPhotoVideoDeletionType(getContext(), mThresholdType);
86         }
87 
88         HashSet<String> checkedApplications = null;
89         if (savedInstanceState != null) {
90             checkedApplications =
91                     (HashSet<String>) savedInstanceState.getSerializable(
92                             AppDeletionType.EXTRA_CHECKED_SET);
93         }
94         mAppBackend = new AppDeletionType(this, checkedApplications, mThresholdType);
95         mAppBackend.registerView(mApps);
96         mAppBackend.registerFreeableChangedListener(this);
97         mApps.setDeletionType(mAppBackend);
98 
99         mDeletableContentList = new ArrayList<>(NUM_DELETION_TYPES);
100     }
101 
102     @Override
onActivityCreated(Bundle savedInstanceState)103     public void onActivityCreated(Bundle savedInstanceState) {
104         super.onActivityCreated(savedInstanceState);
105         initializeButtons();
106         setHasOptionsMenu(true);
107         Activity activity = getActivity();
108         if (activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
109                 != PackageManager.PERMISSION_GRANTED) {
110             activity.requestPermissions(
111                     new String[] {Manifest.permission.READ_EXTERNAL_STORAGE},
112                     0);
113         }
114 
115         if (mProvider != null && mPhotoVideoDeletion != null) {
116             mPhotoPreference.setDaysToKeep(mProvider.getDaysToKeep(mThresholdType));
117             mPhotoPreference.registerFreeableChangedListener(this);
118             mPhotoPreference.registerDeletionService(mPhotoVideoDeletion);
119             mDeletableContentList.add(mPhotoVideoDeletion);
120         } else {
121             getPreferenceScreen().removePreference(mPhotoPreference);
122             mPhotoPreference.setEnabled(false);
123         }
124 
125         String[] uncheckedFiles = null;
126         if (savedInstanceState != null) {
127             uncheckedFiles =
128                     savedInstanceState.getStringArray(
129                             DownloadsDeletionType.EXTRA_UNCHECKED_DOWNLOADS);
130         }
131         mDownloadsPreference =
132                 (DownloadsDeletionPreferenceGroup) findPreference(KEY_DOWNLOADS_PREFERENCE);
133         mDownloadsDeletion = new DownloadsDeletionType(getActivity(), uncheckedFiles);
134         mDownloadsPreference.registerFreeableChangedListener(this);
135         mDownloadsPreference.registerDeletionService(mDownloadsDeletion);
136         mDeletableContentList.add(mDownloadsDeletion);
137         if (isEmptyState()) {
138             setupEmptyState();
139         }
140         mDeletableContentList.add(mAppBackend);
141         updateFreeButtonText();
142     }
143 
setupEmptyState()144     private void setupEmptyState() {
145         mDownloadsPreference.setChecked(false);
146         final PreferenceScreen screen = getPreferenceScreen();
147         screen.removePreference(mDownloadsPreference);
148         screen.removePreference(mApps);
149     }
150 
isEmptyState()151     private boolean isEmptyState() {
152         // We know we are in the empty state if our loader is not using a threshold.
153         return mThresholdType == AppsAsyncLoader.NO_THRESHOLD;
154     }
155 
156     @Override
onResume()157     public void onResume() {
158         super.onResume();
159         for (int i = 0, size = mDeletableContentList.size(); i < size; i++) {
160             mDeletableContentList.get(i).onResume();
161         }
162 
163         if (mDownloadsDeletion != null
164                 && getActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
165                         == PackageManager.PERMISSION_GRANTED) {
166             getLoaderManager().initLoader(DOWNLOADS_LOADER_ID, new Bundle(), mDownloadsDeletion);
167         }
168     }
169 
170     @Override
onPause()171     public void onPause() {
172         super.onPause();
173         for (int i = 0, size = mDeletableContentList.size(); i < size; i++) {
174             mDeletableContentList.get(i).onPause();
175         }
176     }
177 
178     @Override
onSaveInstanceState(Bundle outState)179     public void onSaveInstanceState(Bundle outState) {
180         super.onSaveInstanceState(outState);
181         for (int i = 0, size = mDeletableContentList.size(); i < size; i++) {
182             mDeletableContentList.get(i).onSaveInstanceStateBundle(outState);
183         }
184     }
185 
186     @Override
onFreeableChanged(int numItems, long bytesFreeable)187     public void onFreeableChanged(int numItems, long bytesFreeable) {
188         // bytesFreeable is the number of bytes freed by a single deletion type. If it is non-zero,
189         // there is stuff to free and we can enable it. If it is zero, though, we still need to get
190         // getTotalFreeableSpace to check all deletion types.
191         mFree.setEnabled(bytesFreeable != 0 || getTotalFreeableSpace(COUNT_CHECKED_ONLY) != 0);
192         updateFreeButtonText();
193 
194         // Transition to empty state if all types have reported there is nothing to delete. Skip
195         // the transition if we are already in no threshold mode
196         if (allTypesEmpty() && !isEmptyState()) {
197             startEmptyState();
198         }
199     }
200 
allTypesEmpty()201     private boolean allTypesEmpty() {
202 
203         return mAppBackend.isEmpty()
204                 && mDownloadsDeletion.isEmpty()
205                 && (mPhotoVideoDeletion == null || mPhotoVideoDeletion.isEmpty());
206     }
207 
startEmptyState()208     private void startEmptyState() {
209         if (getActivity() instanceof DeletionHelperActivity) {
210             DeletionHelperActivity activity = (DeletionHelperActivity) getActivity();
211             activity.setIsEmptyState(true /* isEmptyState */);
212         }
213     }
214 
215     /** Clears out the selected apps and data from the device and closes the fragment. */
clearData()216     protected void clearData() {
217         // This should be fine as long as there is only one extra deletion feature.
218         // In the future, this should be done in an async queue in order to not
219         // interfere with the simultaneous PackageDeletionTask.
220         if (mPhotoPreference != null && mPhotoPreference.isChecked()) {
221             mPhotoVideoDeletion.clearFreeableData(getActivity());
222         }
223         if (mDownloadsPreference != null) {
224             mDownloadsDeletion.clearFreeableData(getActivity());
225         }
226         mAppBackend.clearFreeableData(getActivity());
227     }
228 
229     @Override
onClick(View v)230     public void onClick(View v) {
231         if (v.getId() == R.id.next_button) {
232             ConfirmDeletionDialog dialog =
233                     ConfirmDeletionDialog.newInstance(getTotalFreeableSpace(COUNT_CHECKED_ONLY));
234             // The 0 is a placeholder for an optional result code.
235             dialog.setTargetFragment(this, 0);
236             dialog.show(getFragmentManager(), ConfirmDeletionDialog.TAG);
237             MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_HELPER_CLEAR);
238         } else {
239             MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_HELPER_CANCEL);
240             getActivity().finish();
241         }
242     }
243 
244     @Override
onRequestPermissionsResult(int requestCode, String permissions[], int[] grantResults)245     public void onRequestPermissionsResult(int requestCode, String permissions[],
246                                            int[] grantResults) {
247         if (requestCode == 0) {
248             if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
249                 mDownloadsDeletion.onResume();
250                 getLoaderManager().initLoader(DOWNLOADS_LOADER_ID, new Bundle(),
251                         mDownloadsDeletion);
252             }
253         }
254     }
255 
256     @Override
onCreateOptionsMenu(Menu menu, MenuInflater menuInflater)257     public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
258         Activity activity = getActivity();
259         String mHelpUri = getResources().getString(R.string.help_uri_deletion_helper);
260         if (mHelpUri != null && activity != null) {
261             HelpUtils.prepareHelpMenuItem(activity, menu, mHelpUri, getClass().getName());
262         }
263     }
264 
initializeButtons()265     private void initializeButtons() {
266         ButtonBarProvider activity = (ButtonBarProvider) getActivity();
267         activity.getButtonBar().setVisibility(View.VISIBLE);
268 
269         mCancel = activity.getSkipButton();
270         mCancel.setText(R.string.cancel);
271         mCancel.setOnClickListener(this);
272         mCancel.setVisibility(View.VISIBLE);
273 
274         mFree = activity.getNextButton();
275         mFree.setText(R.string.storage_menu_free);
276         mFree.setOnClickListener(this);
277         mFree.setEnabled(false);
278     }
279 
updateFreeButtonText()280     private void updateFreeButtonText() {
281         Activity activity = getActivity();
282         if (activity == null) {
283             return;
284         }
285         mFree.setText(
286                 String.format(
287                         activity.getString(R.string.deletion_helper_free_button),
288                         Formatter.formatFileSize(
289                                 activity, getTotalFreeableSpace(COUNT_CHECKED_ONLY))));
290     }
291 
getTotalFreeableSpace(boolean countUnchecked)292     private long getTotalFreeableSpace(boolean countUnchecked) {
293         long freeableSpace = 0;
294         freeableSpace += mAppBackend.getTotalAppsFreeableSpace(countUnchecked);
295         if (mPhotoPreference != null) {
296             freeableSpace += mPhotoPreference.getFreeableBytes(countUnchecked);
297         }
298         if (mDownloadsPreference != null) {
299             freeableSpace += mDownloadsDeletion.getFreeableBytes(countUnchecked);
300         }
301         return freeableSpace;
302     }
303 }
304