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