MiscFilesHandler.java revision 104ea2873dff5fa3a5f6bd4cfdb893bac3527fdf
1/*
2 * Copyright (C) 2010 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
17package com.android.settings.deviceinfo;
18
19import android.app.Activity;
20import android.app.ListActivity;
21import android.content.Context;
22import android.os.Bundle;
23import android.os.storage.StorageVolume;
24import android.text.format.Formatter;
25import android.util.Log;
26import android.util.SparseBooleanArray;
27import android.view.ActionMode;
28import android.view.LayoutInflater;
29import android.view.Menu;
30import android.view.MenuInflater;
31import android.view.MenuItem;
32import android.view.View;
33import android.view.View.OnClickListener;
34import android.view.View.OnLongClickListener;
35import android.view.ViewGroup;
36import android.widget.BaseAdapter;
37import android.widget.CompoundButton;
38import android.widget.CompoundButton.OnCheckedChangeListener;
39import android.widget.ListView;
40
41import com.android.settings.R;
42import com.android.settings.deviceinfo.StorageMeasurement.FileInfo;
43
44import java.io.File;
45import java.util.ArrayList;
46import java.util.List;
47
48/**
49 * This class handles the selection and removal of Misc files.
50 */
51public class MiscFilesHandler extends ListActivity {
52    private static final String TAG = "MemorySettings";
53    private String mNumSelectedFormat;
54    private String mNumBytesSelectedFormat;
55    private MemoryMearurementAdapter mAdapter;
56    private LayoutInflater mInflater;
57
58    @Override
59    public void onCreate(Bundle savedInstanceState) {
60        super.onCreate(savedInstanceState);
61        setFinishOnTouchOutside(true);
62        setTitle(R.string.misc_files);
63        mNumSelectedFormat = getString(R.string.misc_files_selected_count);
64        mNumBytesSelectedFormat = getString(R.string.misc_files_selected_count_bytes);
65        mAdapter = new MemoryMearurementAdapter(this);
66        mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE);
67        setContentView(R.layout.settings_storage_miscfiles_list);
68        ListView lv = getListView();
69        lv.setItemsCanFocus(true);
70        lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
71        lv.setMultiChoiceModeListener(new ModeCallback(this));
72        setListAdapter(mAdapter);
73    }
74
75    private class ModeCallback implements ListView.MultiChoiceModeListener {
76        private int mDataCount;
77        private final Context mContext;
78
79        public ModeCallback(Context context) {
80            mContext = context;
81            mDataCount = mAdapter.getCount();
82        }
83
84        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
85            final MenuInflater inflater = getMenuInflater();
86            inflater.inflate(R.menu.misc_files_menu, menu);
87            return true;
88        }
89
90        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
91            return true;
92        }
93
94        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
95            ListView lv = getListView();
96            switch (item.getItemId()) {
97            case R.id.action_delete:
98                // delete the files selected
99                SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
100                int checkedCount = getListView().getCheckedItemCount();
101                if (checkedCount > mDataCount) {
102                    throw new IllegalStateException("checked item counts do not match. " +
103                            "checkedCount: " + checkedCount + ", dataSize: " + mDataCount);
104                }
105                if (mDataCount > 0) {
106                    ArrayList<Object> toRemove = new ArrayList<Object>();
107                    for (int i = 0; i < mDataCount; i++) {
108                        if (!checkedItems.get(i)) {
109                            //item not selected
110                            continue;
111                        }
112                        if (StorageMeasurement.LOGV) {
113                            Log.i(TAG, "deleting: " + mAdapter.getItem(i));
114                        }
115                        // delete the file
116                        File file = new File(mAdapter.getItem(i).mFileName);
117                        if (file.isDirectory()) {
118                            deleteDir(file);
119                        } else {
120                            file.delete();
121                        }
122                        toRemove.add(mAdapter.getItem(i));
123                    }
124                    mAdapter.removeAll(toRemove);
125                    mAdapter.notifyDataSetChanged();
126                    mDataCount = mAdapter.getCount();
127                }
128                mode.finish();
129                break;
130
131            case R.id.action_select_all:
132                // check ALL items
133                for (int i = 0; i < mDataCount; i++) {
134                    lv.setItemChecked(i, true);
135                }
136                // update the title and subtitle with number selected and numberBytes selected
137                onItemCheckedStateChanged(mode, 1, 0, true);
138                break;
139            }
140            return true;
141        }
142
143        // Deletes all files and subdirectories under given dir.
144        // Returns true if all deletions were successful.
145        // If a deletion fails, the method stops attempting to delete and returns false.
146        private boolean deleteDir(File dir) {
147            if (dir.isDirectory()) {
148                String[] children = dir.list();
149                for (int i=0; i < children.length; i++) {
150                    boolean success = deleteDir(new File(dir, children[i]));
151                    if (!success) {
152                        return false;
153                    }
154                }
155            }
156            // The directory is now empty so delete it
157            return dir.delete();
158        }
159
160        public void onDestroyActionMode(ActionMode mode) {
161            // This block intentionally left blank
162        }
163
164        public void onItemCheckedStateChanged(ActionMode mode, int position, long id,
165                boolean checked) {
166            ListView lv = getListView();
167            int numChecked = lv.getCheckedItemCount();
168            mode.setTitle(String.format(mNumSelectedFormat, numChecked, mAdapter.getCount()));
169
170            // total the sizes of all items selected so far
171            SparseBooleanArray checkedItems = lv.getCheckedItemPositions();
172            long selectedDataSize = 0;
173            if (numChecked > 0) {
174                for (int i = 0; i < mDataCount; i++) {
175                    if (checkedItems.get(i)) {
176                        // item is checked
177                        selectedDataSize += mAdapter.getItem(i).mSize;
178                    }
179                }
180            }
181            mode.setSubtitle(String.format(mNumBytesSelectedFormat,
182                    Formatter.formatFileSize(mContext, selectedDataSize),
183                    Formatter.formatFileSize(mContext, mAdapter.getDataSize())));
184        }
185    }
186
187    class MemoryMearurementAdapter extends BaseAdapter {
188        private ArrayList<StorageMeasurement.FileInfo> mData = null;
189        private long mDataSize = 0;
190        private Context mContext;
191
192        public MemoryMearurementAdapter(Activity activity) {
193            mContext = activity;
194            final Bundle extras = activity.getIntent().getExtras();
195            final StorageVolume storageVolume = extras.getParcelable(
196                    StorageVolumePreferenceCategory.STORAGE_VOLUME);
197            StorageMeasurement mMeasurement =
198                StorageMeasurement.getInstance(activity, storageVolume, false);
199            mData = (ArrayList<StorageMeasurement.FileInfo>) mMeasurement.mFileInfoForMisc;
200            if (mData != null) {
201                for (StorageMeasurement.FileInfo info : mData) {
202                    mDataSize += info.mSize;
203                }
204            }
205        }
206
207        @Override
208        public int getCount() {
209            return (mData == null) ? 0 : mData.size();
210        }
211
212        @Override
213        public StorageMeasurement.FileInfo getItem(int position) {
214            if (mData == null || mData.size() <= position) {
215                return null;
216            }
217            return mData.get(position);
218        }
219
220        @Override
221        public long getItemId(int position) {
222            if (mData == null || mData.size() <= position) {
223                return 0;
224            }
225            return mData.get(position).mId;
226        }
227
228        public void removeAll(List<Object> objs) {
229            if (mData == null) {
230                return;
231            }
232            for (Object o : objs) {
233                mData.remove(o);
234                mDataSize -= ((StorageMeasurement.FileInfo) o).mSize;
235            }
236        }
237
238        public long getDataSize() {
239            return mDataSize;
240        }
241
242        @Override
243        public void notifyDataSetChanged() {
244            super.notifyDataSetChanged();
245        }
246
247        @Override
248        public View getView(int position, View convertView, ViewGroup parent) {
249            final FileItemInfoLayout view = (convertView == null) ?
250                    (FileItemInfoLayout) mInflater.inflate(R.layout.settings_storage_miscfiles,
251                            parent, false) : (FileItemInfoLayout) convertView;
252            FileInfo item = getItem(position);
253            view.setFileName(item.mFileName);
254            view.setFileSize(Formatter.formatFileSize(mContext, item.mSize));
255            final ListView listView = (ListView) parent;
256            final int listPosition = position;
257            view.getCheckBox().setOnCheckedChangeListener(new OnCheckedChangeListener() {
258
259                @Override
260                public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
261                    listView.setItemChecked(listPosition, isChecked);
262                }
263
264            });
265            view.setOnLongClickListener(new OnLongClickListener() {
266                @Override
267                public boolean onLongClick(View v) {
268                    if (listView.getCheckedItemCount() > 0) {
269                        return false;
270                    }
271                    listView.setItemChecked(listPosition, !view.isChecked());
272                    return true;
273                }
274            });
275            view.setOnClickListener(new OnClickListener() {
276                @Override
277                public void onClick(View v) {
278                    if (listView.getCheckedItemCount() > 0) {
279                        listView.setItemChecked(listPosition, !view.isChecked());
280                    }
281                }
282            });
283            return view;
284        }
285    }
286}