109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi/* 209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * Copyright (C) 2016 The Android Open Source Project 309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * 409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * Licensed under the Apache License, Version 2.0 (the "License"); 509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * you may not use this file except in compliance with the License. 609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * You may obtain a copy of the License at 709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * 809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * http://www.apache.org/licenses/LICENSE-2.0 909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * 1009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * Unless required by applicable law or agreed to in writing, software 1109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * distributed under the License is distributed on an "AS IS" BASIS, 1209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * See the License for the specific language governing permissions and 1409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * limitations under the License. 1509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi */ 1609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 1709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishipackage com.android.storagemanager.deletionhelper; 1809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 1909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.Manifest; 2009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.app.Activity; 213bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishiimport android.content.Context; 223bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishiimport android.content.Intent; 2309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.content.pm.PackageManager; 2409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.os.Bundle; 253bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishiimport android.os.storage.StorageManager; 2683a168041789bbfce9058b5aedc3396464c9f348Daniel Nishiimport android.support.annotation.VisibleForTesting; 2709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.support.v14.preference.PreferenceFragment; 283bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishiimport android.support.v7.preference.Preference; 2933d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinezimport android.support.v7.preference.PreferenceScreen; 3009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.text.format.Formatter; 316cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishiimport android.view.LayoutInflater; 3296fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishiimport android.view.Menu; 3396fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishiimport android.view.MenuInflater; 3409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.view.View; 356cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishiimport android.view.ViewGroup; 3609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport android.widget.Button; 372ace931586ac5ee3d0633c051bc276690975f3eaDaniel Nishiimport com.android.internal.logging.MetricsLogger; 381efe581749de7f50de41bac1e31b1389ac964615Tamas Berghammerimport com.android.internal.logging.nano.MetricsProto.MetricsEvent; 393bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishiimport com.android.internal.util.Preconditions; 4096fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishiimport com.android.settingslib.HelpUtils; 413bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishiimport com.android.settingslib.applications.AppUtils; 4209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport com.android.storagemanager.ButtonBarProvider; 4309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport com.android.storagemanager.R; 44b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishiimport com.android.storagemanager.overlay.DeletionHelperFeatureProvider; 45dee882c960b4db9bae9f2c9144b84c6ba001a051Salvador Martinezimport com.android.storagemanager.overlay.FeatureFactory; 4620c31e6ef425e2127a1243cc679b31afeee47541Daniel Nishiimport java.util.ArrayList; 4709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishiimport java.util.HashSet; 489385b673317060523b66a40780d5ad1df1530a56Salvador Martinezimport java.util.List; 4909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 5009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi/** 5109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi * Settings screen for the deletion helper, which manually removes data which is not recently used. 5209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi */ 5333d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinezpublic class DeletionHelperSettings extends PreferenceFragment 5433d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez implements DeletionType.FreeableChangedListener, View.OnClickListener { 55a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez public static final boolean COUNT_UNCHECKED = true; 56a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez public static final boolean COUNT_CHECKED_ONLY = false; 57a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez 589385b673317060523b66a40780d5ad1df1530a56Salvador Martinez protected static final String APPS_KEY = "apps_group"; 599385b673317060523b66a40780d5ad1df1530a56Salvador Martinez protected static final String KEY_DOWNLOADS_PREFERENCE = "delete_downloads"; 609385b673317060523b66a40780d5ad1df1530a56Salvador Martinez protected static final String KEY_PHOTOS_VIDEOS_PREFERENCE = "delete_photos"; 613bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi protected static final String KEY_GAUGE_PREFERENCE = "deletion_gauge"; 62a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez 639385b673317060523b66a40780d5ad1df1530a56Salvador Martinez private static final String THRESHOLD_KEY = "threshold_key"; 6409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi private static final int DOWNLOADS_LOADER_ID = 1; 659385b673317060523b66a40780d5ad1df1530a56Salvador Martinez private static final int NUM_DELETION_TYPES = 3; 663bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi private static final long UNSET = -1; 6709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 68a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez private List<DeletionType> mDeletableContentList; 6909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi private AppDeletionPreferenceGroup mApps; 7060fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An @VisibleForTesting AppDeletionType mAppBackend; 7109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi private DownloadsDeletionPreferenceGroup mDownloadsPreference; 7209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi private DownloadsDeletionType mDownloadsDeletion; 73b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi private PhotosDeletionPreference mPhotoPreference; 743bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi private Preference mGaugePreference; 75b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi private DeletionType mPhotoVideoDeletion; 7609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi private Button mCancel, mFree; 77b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi private DeletionHelperFeatureProvider mProvider; 789385b673317060523b66a40780d5ad1df1530a56Salvador Martinez private int mThresholdType; 796cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi private LoadingSpinnerController mLoadingController; 8009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 819385b673317060523b66a40780d5ad1df1530a56Salvador Martinez public static DeletionHelperSettings newInstance(int thresholdType) { 829385b673317060523b66a40780d5ad1df1530a56Salvador Martinez DeletionHelperSettings instance = new DeletionHelperSettings(); 839385b673317060523b66a40780d5ad1df1530a56Salvador Martinez Bundle bundle = new Bundle(1); 849385b673317060523b66a40780d5ad1df1530a56Salvador Martinez bundle.putInt(THRESHOLD_KEY, thresholdType); 859385b673317060523b66a40780d5ad1df1530a56Salvador Martinez instance.setArguments(bundle); 869385b673317060523b66a40780d5ad1df1530a56Salvador Martinez return instance; 8709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 8809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 8909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 9009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onCreatePreferences(Bundle savedInstanceState, String rootKey) { 9109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi addPreferencesFromResource(R.xml.deletion_helper_list); 92dee882c960b4db9bae9f2c9144b84c6ba001a051Salvador Martinez mThresholdType = getArguments().getInt(THRESHOLD_KEY, AppsAsyncLoader.NORMAL_THRESHOLD); 9309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mApps = (AppDeletionPreferenceGroup) findPreference(APPS_KEY); 949385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mPhotoPreference = (PhotosDeletionPreference) findPreference(KEY_PHOTOS_VIDEOS_PREFERENCE); 959385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mProvider = FeatureFactory.getFactory(getActivity()).getDeletionHelperFeatureProvider(); 966cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi mLoadingController = new LoadingSpinnerController((DeletionHelperActivity) getActivity()); 979385b673317060523b66a40780d5ad1df1530a56Salvador Martinez if (mProvider != null) { 989385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mPhotoVideoDeletion = 999385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mProvider.createPhotoVideoDeletionType(getContext(), mThresholdType); 1009385b673317060523b66a40780d5ad1df1530a56Salvador Martinez } 10109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 10209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi HashSet<String> checkedApplications = null; 10309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi if (savedInstanceState != null) { 10409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi checkedApplications = 10509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi (HashSet<String>) savedInstanceState.getSerializable( 10609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi AppDeletionType.EXTRA_CHECKED_SET); 10709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 108dee882c960b4db9bae9f2c9144b84c6ba001a051Salvador Martinez mAppBackend = new AppDeletionType(this, checkedApplications, mThresholdType); 10909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mAppBackend.registerView(mApps); 11009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mAppBackend.registerFreeableChangedListener(this); 11109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mApps.setDeletionType(mAppBackend); 112b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi 1139385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDeletableContentList = new ArrayList<>(NUM_DELETION_TYPES); 1143bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi 1153bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi mGaugePreference = findPreference(KEY_GAUGE_PREFERENCE); 1163bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi Activity activity = getActivity(); 1173bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi if (activity != null && mGaugePreference != null) { 1183bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi Intent intent = activity.getIntent(); 1193bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi if (intent != null) { 1203bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi CharSequence gaugeTitle = 1213bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi getGaugeString(getContext(), intent, activity.getCallingPackage()); 1223bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi if (gaugeTitle != null) { 1233bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi mGaugePreference.setTitle(gaugeTitle); 1243bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi } else { 1253bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi getPreferenceScreen().removePreference(mGaugePreference); 1263bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi } 1273bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi } 1283bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi } 1293bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi } 1303bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi 1313bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi protected static CharSequence getGaugeString( 1323bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi Context context, Intent intent, String packageName) { 1333bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi Preconditions.checkNotNull(intent); 1343bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi long requestedBytes = intent.getLongExtra(StorageManager.EXTRA_REQUESTED_BYTES, UNSET); 1353bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi if (requestedBytes > 0) { 1363bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi CharSequence callerLabel = 1373bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi AppUtils.getApplicationLabel(context.getPackageManager(), packageName); 1383bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi // I really hope this isn't the case, but I can't ignore the possibility that we cannot 1393bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi // determine what app the referrer is. 1403bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi if (callerLabel == null) { 1413bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi return null; 1423bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi } 1433bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi return context.getString( 1443bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi R.string.app_requesting_space, 1453bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi callerLabel, 1463bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi Formatter.formatFileSize(context, requestedBytes)); 1473bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi } 1483bb1e61cb9db7d8773d2c75d7059b75337d62ca3Daniel Nishi return null; 14909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 15009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 15109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 15209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onActivityCreated(Bundle savedInstanceState) { 15309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi super.onActivityCreated(savedInstanceState); 15409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi initializeButtons(); 15596fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi setHasOptionsMenu(true); 15609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi Activity activity = getActivity(); 15709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi if (activity.checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) 15809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi != PackageManager.PERMISSION_GRANTED) { 15909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi activity.requestPermissions( 16009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi new String[] {Manifest.permission.READ_EXTERNAL_STORAGE}, 16109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 0); 16209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 163b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi 1649385b673317060523b66a40780d5ad1df1530a56Salvador Martinez if (mProvider != null && mPhotoVideoDeletion != null) { 1659385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mPhotoPreference.setDaysToKeep(mProvider.getDaysToKeep(mThresholdType)); 166b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi mPhotoPreference.registerFreeableChangedListener(this); 167b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi mPhotoPreference.registerDeletionService(mPhotoVideoDeletion); 1689385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDeletableContentList.add(mPhotoVideoDeletion); 169467f4467575cc52d61fa200bbaf18979db50474cSalvador Martinez } else { 170467f4467575cc52d61fa200bbaf18979db50474cSalvador Martinez getPreferenceScreen().removePreference(mPhotoPreference); 171467f4467575cc52d61fa200bbaf18979db50474cSalvador Martinez mPhotoPreference.setEnabled(false); 172b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi } 17320c31e6ef425e2127a1243cc679b31afeee47541Daniel Nishi 17420c31e6ef425e2127a1243cc679b31afeee47541Daniel Nishi String[] uncheckedFiles = null; 17520c31e6ef425e2127a1243cc679b31afeee47541Daniel Nishi if (savedInstanceState != null) { 1769385b673317060523b66a40780d5ad1df1530a56Salvador Martinez uncheckedFiles = 1779385b673317060523b66a40780d5ad1df1530a56Salvador Martinez savedInstanceState.getStringArray( 17820c31e6ef425e2127a1243cc679b31afeee47541Daniel Nishi DownloadsDeletionType.EXTRA_UNCHECKED_DOWNLOADS); 17920c31e6ef425e2127a1243cc679b31afeee47541Daniel Nishi } 18009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mDownloadsPreference = 18109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi (DownloadsDeletionPreferenceGroup) findPreference(KEY_DOWNLOADS_PREFERENCE); 18220c31e6ef425e2127a1243cc679b31afeee47541Daniel Nishi mDownloadsDeletion = new DownloadsDeletionType(getActivity(), uncheckedFiles); 18309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mDownloadsPreference.registerFreeableChangedListener(this); 18409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mDownloadsPreference.registerDeletionService(mDownloadsDeletion); 1859385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDeletableContentList.add(mDownloadsDeletion); 18633d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez if (isEmptyState()) { 18733d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez setupEmptyState(); 18833d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez } 1899385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDeletableContentList.add(mAppBackend); 19009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi updateFreeButtonText(); 19109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 19209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 19383a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi @VisibleForTesting 19483a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi void setupEmptyState() { 19533d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez final PreferenceScreen screen = getPreferenceScreen(); 19683a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi if (mDownloadsPreference != null) { 19783a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi mDownloadsPreference.setChecked(false); 19883a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi screen.removePreference(mDownloadsPreference); 19983a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi } 20033d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez screen.removePreference(mApps); 20183a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi 20283a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi // Nulling out the downloads preferences means we won't accidentally delete what isn't 20383a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi // visible. 20483a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi mDownloadsDeletion = null; 20583a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi mDownloadsPreference = null; 20633d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez } 20733d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez 20833d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez private boolean isEmptyState() { 20933d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez // We know we are in the empty state if our loader is not using a threshold. 21033d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez return mThresholdType == AppsAsyncLoader.NO_THRESHOLD; 21133d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez } 21233d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez 21309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 21409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onResume() { 21509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi super.onResume(); 2166cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi 2176cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi mLoadingController.initializeLoading(getListView()); 2186cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi 2199385b673317060523b66a40780d5ad1df1530a56Salvador Martinez for (int i = 0, size = mDeletableContentList.size(); i < size; i++) { 2209385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDeletableContentList.get(i).onResume(); 22109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 222b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi 2239385b673317060523b66a40780d5ad1df1530a56Salvador Martinez if (mDownloadsDeletion != null 2249385b673317060523b66a40780d5ad1df1530a56Salvador Martinez && getActivity().checkSelfPermission(Manifest.permission.READ_EXTERNAL_STORAGE) 2259385b673317060523b66a40780d5ad1df1530a56Salvador Martinez == PackageManager.PERMISSION_GRANTED) { 2269385b673317060523b66a40780d5ad1df1530a56Salvador Martinez getLoaderManager().initLoader(DOWNLOADS_LOADER_ID, new Bundle(), mDownloadsDeletion); 227b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi } 22809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 22909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 23009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 23109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onPause() { 23209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi super.onPause(); 2339385b673317060523b66a40780d5ad1df1530a56Salvador Martinez for (int i = 0, size = mDeletableContentList.size(); i < size; i++) { 2349385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDeletableContentList.get(i).onPause(); 235b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi } 23609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 23709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 23809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 23909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onSaveInstanceState(Bundle outState) { 24009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi super.onSaveInstanceState(outState); 2419385b673317060523b66a40780d5ad1df1530a56Salvador Martinez for (int i = 0, size = mDeletableContentList.size(); i < size; i++) { 2429385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDeletableContentList.get(i).onSaveInstanceStateBundle(outState); 2439385b673317060523b66a40780d5ad1df1530a56Salvador Martinez } 24409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 24509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 24609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 24709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onFreeableChanged(int numItems, long bytesFreeable) { 24800b4488ec14591753239e9ee507d04a62c4d146cDaniel Nishi if (numItems > 0 || bytesFreeable > 0 || allTypesEmpty()) { 24960fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An if (mLoadingController != null) { 25060fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An mLoadingController.onCategoryLoad(); 25160fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An } 2526cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi } 2536cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi 2542a508e16fa21573aa2fcb2f985136566b8301f35Daniel Nishi // bytesFreeable is the number of bytes freed by a single deletion type. If it is non-zero, 2552a508e16fa21573aa2fcb2f985136566b8301f35Daniel Nishi // there is stuff to free and we can enable it. If it is zero, though, we still need to get 2562a508e16fa21573aa2fcb2f985136566b8301f35Daniel Nishi // getTotalFreeableSpace to check all deletion types. 25760fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An if (mFree != null) { 25860fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An mFree.setEnabled(bytesFreeable != 0 || getTotalFreeableSpace(COUNT_CHECKED_ONLY) != 0); 25960fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An } 26009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi updateFreeButtonText(); 261a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez 26233d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez // Transition to empty state if all types have reported there is nothing to delete. Skip 26333d4cb4fd65e6846e0db80abd8b4ae3f242c04acSalvador Martinez // the transition if we are already in no threshold mode 264828974f55fa2aae46d8137747e284a7861d8086cSalvador Martinez if (allTypesEmpty() && !isEmptyState()) { 265a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez startEmptyState(); 266a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez } 267a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez } 268a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez 269828974f55fa2aae46d8137747e284a7861d8086cSalvador Martinez private boolean allTypesEmpty() { 270828974f55fa2aae46d8137747e284a7861d8086cSalvador Martinez return mAppBackend.isEmpty() 27160fc2bc6ff46da9d86893e4485c963fe5956db2dNg Zhi An && (mDownloadsDeletion == null || mDownloadsDeletion.isEmpty()) 272828974f55fa2aae46d8137747e284a7861d8086cSalvador Martinez && (mPhotoVideoDeletion == null || mPhotoVideoDeletion.isEmpty()); 273a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez } 274a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez 275a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez private void startEmptyState() { 276a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez if (getActivity() instanceof DeletionHelperActivity) { 277a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez DeletionHelperActivity activity = (DeletionHelperActivity) getActivity(); 278a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez activity.setIsEmptyState(true /* isEmptyState */); 279a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez } 28009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 28109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 282467f4467575cc52d61fa200bbaf18979db50474cSalvador Martinez /** Clears out the selected apps and data from the device and closes the fragment. */ 28309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi protected void clearData() { 284b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi // This should be fine as long as there is only one extra deletion feature. 285b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi // In the future, this should be done in an async queue in order to not 286b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi // interfere with the simultaneous PackageDeletionTask. 287b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi if (mPhotoPreference != null && mPhotoPreference.isChecked()) { 288b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi mPhotoVideoDeletion.clearFreeableData(getActivity()); 289b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi } 2909385b673317060523b66a40780d5ad1df1530a56Salvador Martinez if (mDownloadsPreference != null) { 2919385b673317060523b66a40780d5ad1df1530a56Salvador Martinez mDownloadsDeletion.clearFreeableData(getActivity()); 2929385b673317060523b66a40780d5ad1df1530a56Salvador Martinez } 29383a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi if (mAppBackend != null) { 29483a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi mAppBackend.clearFreeableData(getActivity()); 29583a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi } 29609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 29709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 29809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 29909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onClick(View v) { 300aff68018da784efc08b532597c2691465546957bDaniel Nishi if (v.getId() == R.id.next_button) { 30109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi ConfirmDeletionDialog dialog = 302a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez ConfirmDeletionDialog.newInstance(getTotalFreeableSpace(COUNT_CHECKED_ONLY)); 30309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi // The 0 is a placeholder for an optional result code. 30409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi dialog.setTargetFragment(this, 0); 30509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi dialog.show(getFragmentManager(), ConfirmDeletionDialog.TAG); 3062ace931586ac5ee3d0633c051bc276690975f3eaDaniel Nishi MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_HELPER_CLEAR); 30709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } else { 3082ace931586ac5ee3d0633c051bc276690975f3eaDaniel Nishi MetricsLogger.action(getContext(), MetricsEvent.ACTION_DELETION_HELPER_CANCEL); 309aff68018da784efc08b532597c2691465546957bDaniel Nishi getActivity().finish(); 31009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 31109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 31209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 31309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi @Override 31409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi public void onRequestPermissionsResult(int requestCode, String permissions[], 31509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi int[] grantResults) { 31609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi if (requestCode == 0) { 31709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { 31809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mDownloadsDeletion.onResume(); 31909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi getLoaderManager().initLoader(DOWNLOADS_LOADER_ID, new Bundle(), 32009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mDownloadsDeletion); 32109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 32209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 32309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 32409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 32596fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi @Override 32696fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) { 32796fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi Activity activity = getActivity(); 32896fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi String mHelpUri = getResources().getString(R.string.help_uri_deletion_helper); 32996fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi if (mHelpUri != null && activity != null) { 33096fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi HelpUtils.prepareHelpMenuItem(activity, menu, mHelpUri, getClass().getName()); 33196fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi } 33296fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi } 33396fda63c3eeabf90a5420730d4729726dce504b3Daniel Nishi 3346cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi @Override 3356cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi public View onCreateView( 3366cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 3376cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi View view = super.onCreateView(inflater, container, savedInstanceState); 3386cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi return view; 3396cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi } 3406cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi 34183a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi @VisibleForTesting 34283a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi void setDownloadsDeletionType(DownloadsDeletionType downloadsDeletion) { 34383a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi mDownloadsDeletion = downloadsDeletion; 34483a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi } 34583a168041789bbfce9058b5aedc3396464c9f348Daniel Nishi 34609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi private void initializeButtons() { 34709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi ButtonBarProvider activity = (ButtonBarProvider) getActivity(); 34809fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi activity.getButtonBar().setVisibility(View.VISIBLE); 34909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 35009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mCancel = activity.getSkipButton(); 35109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mCancel.setText(R.string.cancel); 35209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mCancel.setOnClickListener(this); 35309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mCancel.setVisibility(View.VISIBLE); 35409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 35509fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mFree = activity.getNextButton(); 35609fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mFree.setText(R.string.storage_menu_free); 35709fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi mFree.setOnClickListener(this); 3582a508e16fa21573aa2fcb2f985136566b8301f35Daniel Nishi mFree.setEnabled(false); 35909fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 36009fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 36109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi private void updateFreeButtonText() { 362dbf58432eba75178987aa239d739947c1ab36f45Daniel Nishi Activity activity = getActivity(); 363dbf58432eba75178987aa239d739947c1ab36f45Daniel Nishi if (activity == null) { 364dbf58432eba75178987aa239d739947c1ab36f45Daniel Nishi return; 365dbf58432eba75178987aa239d739947c1ab36f45Daniel Nishi } 366a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez mFree.setText( 367a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez String.format( 368a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez activity.getString(R.string.deletion_helper_free_button), 369a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez Formatter.formatFileSize( 370a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez activity, getTotalFreeableSpace(COUNT_CHECKED_ONLY)))); 37109fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 37209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi 373a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez private long getTotalFreeableSpace(boolean countUnchecked) { 37409fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi long freeableSpace = 0; 375a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez freeableSpace += mAppBackend.getTotalAppsFreeableSpace(countUnchecked); 376b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi if (mPhotoPreference != null) { 377a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez freeableSpace += mPhotoPreference.getFreeableBytes(countUnchecked); 378b26f457e30d6d444c873efaaf26181139bb92aedDaniel Nishi } 3799385b673317060523b66a40780d5ad1df1530a56Salvador Martinez if (mDownloadsPreference != null) { 380a077e8d3f8c99fd3769d93c4094d434115072a9cSalvador Martinez freeableSpace += mDownloadsDeletion.getFreeableBytes(countUnchecked); 3819385b673317060523b66a40780d5ad1df1530a56Salvador Martinez } 38209fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi return freeableSpace; 38309fa1388d090666e631f9d764f935604a2c753e9Daniel Nishi } 3846cec0d1675449e0e5339a2ce924a44513c41bf51Daniel Nishi 3851efe581749de7f50de41bac1e31b1389ac964615Tamas Berghammer} 386