Home | History | Annotate | Download | only in deletionhelper
      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.app.Activity;
     20 import android.app.LoaderManager.LoaderCallbacks;
     21 import android.content.Context;
     22 import android.content.Loader;
     23 import android.graphics.Bitmap;
     24 import android.os.AsyncTask;
     25 import android.os.Bundle;
     26 import android.os.Environment;
     27 import android.support.annotation.Nullable;
     28 import android.util.ArraySet;
     29 
     30 import com.android.internal.logging.MetricsLogger;
     31 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     32 import com.android.storagemanager.deletionhelper.FetchDownloadsLoader.DownloadsResult;
     33 
     34 import java.io.File;
     35 import java.util.Collections;
     36 import java.util.HashMap;
     37 import java.util.Set;
     38 
     39 /**
     40  * The DownloadsDeletionType provides stale download file information to the
     41  * {@link DownloadsDeletionPreferenceGroup}.
     42  */
     43 public class DownloadsDeletionType implements DeletionType, LoaderCallbacks<DownloadsResult> {
     44     public static final String EXTRA_UNCHECKED_DOWNLOADS = "uncheckedFiles";
     45     private long mBytes;
     46     private long mMostRecent;
     47     private FreeableChangedListener mListener;
     48     private Context mContext;
     49     private ArraySet<File> mFiles;
     50     private ArraySet<String> mUncheckedFiles;
     51     private HashMap<File, Bitmap> mThumbnails;
     52     private int mLoadingStatus;
     53 
     54     public DownloadsDeletionType(Context context, String[] uncheckedFiles) {
     55         mLoadingStatus = LoadingStatus.LOADING;
     56         mContext = context;
     57         mFiles = new ArraySet<>();
     58         mUncheckedFiles = new ArraySet<>();
     59         if (uncheckedFiles != null) {
     60             Collections.addAll(mUncheckedFiles, uncheckedFiles);
     61         }
     62     }
     63 
     64     @Override
     65     public void registerFreeableChangedListener(FreeableChangedListener listener) {
     66         mListener = listener;
     67         if (mFiles != null) {
     68             maybeUpdateListener();
     69         }
     70     }
     71 
     72     @Override
     73     public void onResume() {
     74     }
     75 
     76     @Override
     77     public void onPause() {
     78     }
     79 
     80     @Override
     81     public void onSaveInstanceStateBundle(Bundle savedInstanceState) {
     82         savedInstanceState.putStringArray(EXTRA_UNCHECKED_DOWNLOADS,
     83                 mUncheckedFiles.toArray(new String[mUncheckedFiles.size()]));
     84     }
     85 
     86     @Override
     87     public void clearFreeableData(Activity activity) {
     88         if (mFiles != null) {
     89             AsyncTask.execute(new Runnable() {
     90                 @Override
     91                 public void run() {
     92                     boolean succeeded = true;
     93                     for (File entry : mFiles) {
     94                         if (isChecked(entry)) {
     95                             succeeded = succeeded && entry.delete();
     96                         }
     97                     }
     98 
     99                     if (!succeeded) {
    100                         MetricsLogger.action(activity,
    101                                 MetricsEvent.ACTION_DELETION_HELPER_DOWNLOADS_DELETION_FAIL);
    102                     }
    103                 }
    104             });
    105         }
    106     }
    107 
    108     @Override
    109     public int getLoadingStatus() {
    110         return mLoadingStatus;
    111     }
    112 
    113     @Override
    114     public int getContentCount() {
    115         return mFiles.size();
    116     }
    117 
    118     @Override
    119     public void setLoadingStatus(@LoadingStatus int loadingStatus) {
    120         mLoadingStatus = loadingStatus;
    121     }
    122 
    123     @Override
    124     public Loader<DownloadsResult> onCreateLoader(int id, Bundle args) {
    125         return new FetchDownloadsLoader(mContext,
    126                 Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS));
    127     }
    128 
    129     @Override
    130     public void onLoadFinished(Loader<DownloadsResult> loader, DownloadsResult data) {
    131         mMostRecent = data.youngestLastModified;
    132         for (File file : data.files) {
    133             mFiles.add(file);
    134         }
    135         mBytes = data.totalSize;
    136         mThumbnails = data.thumbnails;
    137         updateLoadingStatus();
    138         maybeUpdateListener();
    139     }
    140 
    141     @Override
    142     public void onLoaderReset(Loader<DownloadsResult> loader) {
    143     }
    144 
    145     /**
    146      * Returns the most recent last modified time for any clearable file.
    147      * @return The last modified time.
    148      */
    149     public long getMostRecentLastModified() {
    150         return mMostRecent;
    151     }
    152 
    153     /**
    154      * Returns the files in the Downloads folder after the loader task finishes.
    155      */
    156     public Set<File> getFiles() {
    157         if (mFiles == null) {
    158             return null;
    159         }
    160         return mFiles;
    161     }
    162 
    163     /**
    164      * Set if a file should be deleted when the service is asked to clear files.
    165      */
    166     public void setFileChecked(File file, boolean checked) {
    167         if (checked) {
    168             mUncheckedFiles.remove(file.getPath());
    169         } else {
    170             mUncheckedFiles.add(file.getPath());
    171         }
    172     }
    173 
    174     /** Returns the number of bytes that would be cleared if the deletion tasks runs. */
    175     public long getFreeableBytes(boolean countUnchecked) {
    176         long freedBytes = 0;
    177         for (File file : mFiles) {
    178             if (isChecked(file) || countUnchecked) {
    179                 freedBytes += file.length();
    180             }
    181         }
    182         return freedBytes;
    183     }
    184 
    185     /** Returns a thumbnail for a given file, if it exists. If it does not exist, returns null. */
    186     public @Nullable Bitmap getCachedThumbnail(File imageFile) {
    187         if (mThumbnails == null) {
    188             return null;
    189         }
    190         return mThumbnails.get(imageFile);
    191     }
    192 
    193     /**
    194      * Return if a given file is checked for deletion.
    195      *
    196      * @param file The file to check.
    197      */
    198     public boolean isChecked(File file) {
    199         return !mUncheckedFiles.contains(file.getPath());
    200     }
    201 
    202     private void maybeUpdateListener() {
    203         if (mListener != null) {
    204             mListener.onFreeableChanged(mFiles.size(), mBytes);
    205         }
    206     }
    207 }
    208