diff options
Diffstat (limited to 'src/com/android/settings')
-rw-r--r-- | src/com/android/settings/deviceinfo/Constants.java | 102 | ||||
-rw-r--r-- | src/com/android/settings/deviceinfo/FileItemInfoLayout.java | 78 | ||||
-rw-r--r-- | src/com/android/settings/deviceinfo/Memory.java | 133 | ||||
-rw-r--r-- | src/com/android/settings/deviceinfo/MemoryMeasurement.java | 120 | ||||
-rw-r--r-- | src/com/android/settings/deviceinfo/MiscFilesHandler.java | 279 |
5 files changed, 667 insertions, 45 deletions
diff --git a/src/com/android/settings/deviceinfo/Constants.java b/src/com/android/settings/deviceinfo/Constants.java new file mode 100644 index 0000000..9f49479 --- /dev/null +++ b/src/com/android/settings/deviceinfo/Constants.java @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.android.settings.deviceinfo; + +import android.os.Environment; + +import java.util.ArrayList; +import java.util.List; + +/** + * Some of the constants used in this package + */ +class Constants { + static final int MEDIA_INDEX = 0; + static final int DOWNLOADS_INDEX = 1; + static final int PIC_VIDEO_INDEX = 2; + static final int MUSIC_INDEX = 3; + static final int MEDIA_APPS_DATA_INDEX = 4; + static final int MEDIA_MISC_INDEX = 5; + static final int NUM_MEDIA_DIRS_TRACKED = MEDIA_MISC_INDEX + 1; + + static class MediaDirectory { + final String[] mDirPaths; + final String mKey; + final String mPreferenceName; + MediaDirectory(String pref, String debugInfo, String... paths) { + mDirPaths = paths; + mKey = debugInfo; + mPreferenceName = pref; + } + } + static final ArrayList<MediaDirectory> mMediaDirs = new ArrayList<MediaDirectory>(); + static final List<String> ExclusionTargetsForMiscFiles = new ArrayList<String>(); + static { + mMediaDirs.add(MEDIA_INDEX, + new MediaDirectory(null, + "/sdcard", + Environment.getExternalStorageDirectory().getAbsolutePath())); + mMediaDirs.add(DOWNLOADS_INDEX, + new MediaDirectory("memory_internal_downloads", + "/sdcard/download", + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DOWNLOADS).getAbsolutePath())); + mMediaDirs.add(PIC_VIDEO_INDEX, + new MediaDirectory("memory_internal_dcim", + "/sdcard/pic_video", + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_DCIM).getAbsolutePath(), + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_MOVIES).getAbsolutePath(), + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PICTURES).getAbsolutePath())); + mMediaDirs.add(MUSIC_INDEX, + new MediaDirectory("memory_internal_music", + "/sdcard/audio", + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_MUSIC).getAbsolutePath(), + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_ALARMS).getAbsolutePath(), + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_NOTIFICATIONS).getAbsolutePath(), + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_RINGTONES).getAbsolutePath(), + Environment.getExternalStoragePublicDirectory( + Environment.DIRECTORY_PODCASTS).getAbsolutePath())); + mMediaDirs.add(MEDIA_APPS_DATA_INDEX, + new MediaDirectory(null, + "/sdcard/Android", + Environment.getExternalStorageAndroidDataDir().getAbsolutePath())); + mMediaDirs.add(MEDIA_MISC_INDEX, + new MediaDirectory("memory_internal_media_misc", + "misc on /sdcard", + "not relevant")); + // prepare a lit of strings representing dirpaths that should be skipped while looking + // for 'other' files + for (int j = 0; j < Constants.NUM_MEDIA_DIRS_TRACKED - 1; j++) { + String[] dirs = Constants.mMediaDirs.get(j).mDirPaths; + int len = dirs.length; + if (len > 0) { + for (int k = 0; k < len; k++) { + ExclusionTargetsForMiscFiles.add(dirs[k]); + } + } + // also add /sdcard/Android + ExclusionTargetsForMiscFiles.add( + Environment.getExternalStorageDirectory().getAbsolutePath() + "/Android"); + } + } +} diff --git a/src/com/android/settings/deviceinfo/FileItemInfoLayout.java b/src/com/android/settings/deviceinfo/FileItemInfoLayout.java new file mode 100644 index 0000000..990f7f2 --- /dev/null +++ b/src/com/android/settings/deviceinfo/FileItemInfoLayout.java @@ -0,0 +1,78 @@ +// Copyright 2011 Google Inc. All Rights Reserved. + +package com.android.settings.deviceinfo; + +import com.android.settings.R; + +import android.content.Context; +import android.os.Environment; +import android.util.AttributeSet; +import android.view.ViewDebug; +import android.widget.CheckBox; +import android.widget.Checkable; +import android.widget.RelativeLayout; +import android.widget.TextView; + +/** + * Handles display of a single row entry on Settings --> Storage --> Misc Files screen + */ +public class FileItemInfoLayout extends RelativeLayout implements Checkable { + private TextView mFileNameView; + private TextView mFileSizeView; + private CheckBox mCheckbox; + private static final int mLengthExternalStorageDirPrefix = + Environment.getExternalStorageDirectory().getAbsolutePath().length() + 1; + + public FileItemInfoLayout(Context context) { + this(context, null); + } + + public FileItemInfoLayout(Context context, AttributeSet attrs) { + this(context, attrs, 0); + } + + public FileItemInfoLayout(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + public void toggle() { + setChecked(!mCheckbox.isChecked()); + } + + /* (non-Javadoc) + * @see android.view.View#onFinishInflate() + */ + @Override + protected void onFinishInflate() { + super.onFinishInflate(); + mFileNameView = (TextView) findViewById(R.id.misc_filename); + mFileSizeView = (TextView) findViewById(R.id.misc_filesize); + mCheckbox = (CheckBox) findViewById(R.id.misc_checkbox); + } + + public void setFileName(String fileName) { + mFileNameView.setText(fileName.substring(mLengthExternalStorageDirPrefix)); + } + + public void setFileSize(String filesize) { + mFileSizeView.setText(filesize); + } + + @ViewDebug.ExportedProperty + public boolean isChecked() { + return mCheckbox.isChecked(); + } + + public CheckBox getCheckBox() { + return mCheckbox; + } + + /** + * <p>Changes the checked state of this text view.</p> + * + * @param checked true to check the text, false to uncheck it + */ + public void setChecked(boolean checked) { + mCheckbox.setChecked(checked); + } +}
\ No newline at end of file diff --git a/src/com/android/settings/deviceinfo/Memory.java b/src/com/android/settings/deviceinfo/Memory.java index db1ff65..7cb378c 100644 --- a/src/com/android/settings/deviceinfo/Memory.java +++ b/src/com/android/settings/deviceinfo/Memory.java @@ -23,6 +23,7 @@ import com.android.settings.deviceinfo.MemoryMeasurement.MeasurementReceiver; import android.app.ActivityManager; import android.app.AlertDialog; import android.app.Dialog; +import android.app.DownloadManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.DialogInterface; @@ -32,9 +33,7 @@ import android.content.DialogInterface.OnCancelListener; import android.content.pm.ApplicationInfo; import android.content.res.Resources; import android.graphics.drawable.ShapeDrawable; -import android.graphics.drawable.shapes.RectShape; import android.graphics.drawable.shapes.RoundRectShape; -import android.hardware.UsbManager; import android.os.Bundle; import android.os.Environment; import android.os.Handler; @@ -56,8 +55,7 @@ import java.util.List; public class Memory extends SettingsPreferenceFragment implements OnCancelListener, MeasurementReceiver { - private static final String TAG = "Memory"; - static final boolean localLOGV = false; + private static final String TAG = "MemorySettings"; private static final String MEMORY_SD_SIZE = "memory_sd_size"; @@ -75,8 +73,6 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen private static final String MEMORY_INTERNAL_APPS = "memory_internal_apps"; - private static final String MEMORY_INTERNAL_MEDIA = "memory_internal_media"; - private static final String MEMORY_INTERNAL_CHART = "memory_internal_chart"; private static final int DLG_CONFIRM_UNMOUNT = 1; @@ -94,13 +90,13 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen // Internal storage preferences private Preference mInternalSize; private Preference mInternalAvail; - private Preference mInternalMediaUsage; private Preference mInternalAppsUsage; + private final Preference[] mMediaPreferences = new Preference[Constants.NUM_MEDIA_DIRS_TRACKED]; private UsageBarPreference mInternalUsageChart; // Internal storage chart colors - private int mInternalMediaColor; private int mInternalAppsColor; + private int mInternalAvailColor; private int mInternalUsedColor; boolean mSdMountToggleAdded = true; @@ -134,9 +130,12 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen Bundle bundle = msg.getData(); final long totalSize = bundle.getLong(MemoryMeasurement.TOTAL_SIZE); final long availSize = bundle.getLong(MemoryMeasurement.AVAIL_SIZE); - final long mediaUsed = bundle.getLong(MemoryMeasurement.MEDIA_USED); final long appsUsed = bundle.getLong(MemoryMeasurement.APPS_USED); - updateUiExact(totalSize, availSize, mediaUsed, appsUsed); + final long[] mediaSizes = new long[Constants.NUM_MEDIA_DIRS_TRACKED]; + for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { + mediaSizes[i] = bundle.getLong(Constants.mMediaDirs.get(i).mKey); + } + updateUiExact(totalSize, availSize, appsUsed, mediaSizes); break; } case MSG_UI_UPDATE_EXTERNAL_APPROXIMATE: { @@ -175,31 +174,59 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen } mInternalSize = findPreference(MEMORY_INTERNAL_SIZE); - mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL); - mInternalMediaUsage = findPreference(MEMORY_INTERNAL_MEDIA); - mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS); - - mInternalMediaColor = mRes.getColor(R.color.memory_media_usage); mInternalAppsColor = mRes.getColor(R.color.memory_apps_usage); mInternalUsedColor = android.graphics.Color.GRAY; - + mInternalAvailColor = mRes.getColor(R.color.memory_avail); + final int buttonSize = (int) mRes.getDimension(R.dimen.device_memory_usage_button_size); float[] radius = new float[] { 5f, 5f, 5f, 5f, 5f, 5f, 5f, 5f }; RoundRectShape shape1 = new RoundRectShape(radius, null, null); - ShapeDrawable mediaShape = new ShapeDrawable(shape1); - mediaShape.setIntrinsicWidth(32); - mediaShape.setIntrinsicHeight(32); - mediaShape.getPaint().setColor(mInternalMediaColor); - mInternalMediaUsage.setIcon(mediaShape); + // total available space + mInternalAvail = findPreference(MEMORY_INTERNAL_AVAIL); + ShapeDrawable availShape = new ShapeDrawable(shape1); + availShape.setIntrinsicWidth(buttonSize); + availShape.setIntrinsicHeight(buttonSize); + availShape.getPaint().setColor(mInternalAvailColor); + mInternalAvail.setIcon(availShape); + // used by apps + mInternalAppsUsage = findPreference(MEMORY_INTERNAL_APPS); ShapeDrawable appsShape = new ShapeDrawable(shape1); - appsShape.setIntrinsicWidth(32); - appsShape.setIntrinsicHeight(32); + appsShape.setIntrinsicWidth(buttonSize); + appsShape.setIntrinsicHeight(buttonSize); appsShape.getPaint().setColor(mInternalAppsColor); mInternalAppsUsage.setIcon(appsShape); + // space used by individual major directories on /sdcard + for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { + // nothing to be displayed for certain entries in Constants.mMediaDirs + if (Constants.mMediaDirs.get(i).mPreferenceName == null) { + continue; + } + mMediaPreferences[i] = findPreference(Constants.mMediaDirs.get(i).mPreferenceName); + ShapeDrawable shape = new ShapeDrawable(shape1); + shape.setIntrinsicWidth(buttonSize); + shape.setIntrinsicHeight(buttonSize); + int color = 0; + switch (i) { + case Constants.DOWNLOADS_INDEX: + color = mRes.getColor(R.color.memory_downloads); + break; + case Constants.PIC_VIDEO_INDEX: + color = mRes.getColor(R.color.memory_video); + break; + case Constants.MUSIC_INDEX: + color = mRes.getColor(R.color.memory_audio); + break; + case Constants.MEDIA_MISC_INDEX: + color = mRes.getColor(R.color.memory_misc); + break; + } + shape.getPaint().setColor(color); + mMediaPreferences[i].setIcon(shape); + } mInternalUsageChart = (UsageBarPreference) findPreference(MEMORY_INTERNAL_CHART); mMeasurement = MemoryMeasurement.getInstance(getActivity()); @@ -209,7 +236,7 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen @Override public void onResume() { super.onResume(); - + mMeasurement.setReceiver(this); IntentFilter intentFilter = new IntentFilter(Intent.ACTION_MEDIA_SCANNER_STARTED); intentFilter.addAction(Intent.ACTION_MEDIA_SCANNER_FINISHED); intentFilter.addDataScheme("file"); @@ -282,6 +309,27 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen com.android.settings.Settings.ManageApplicationsActivity.class); startActivity(intent); return true; + } else if (preference == mMediaPreferences[Constants.DOWNLOADS_INDEX]) { + Intent intent = new Intent(DownloadManager.ACTION_VIEW_DOWNLOADS) + .putExtra(DownloadManager.INTENT_EXTRAS_SORT_BY_SIZE, true); + startActivity(intent); + return true; + } else if (preference == mMediaPreferences[Constants.MUSIC_INDEX]) { + Intent intent = new Intent("android.intent.action.GET_CONTENT"); + intent.setType("audio/mp3"); + startActivity(intent); + return true; + } else if (preference == mMediaPreferences[Constants.PIC_VIDEO_INDEX]) { + Intent intent = new Intent("android.intent.action.GET_CONTENT"); + intent.setType("image/jpeg"); + startActivity(intent); + return true; + } else if (preference == mMediaPreferences[Constants.MEDIA_MISC_INDEX]) { + Context context = getActivity().getApplicationContext(); + if (MemoryMeasurement.getInstance(context).isSizeOfMiscCategorynonZero()) { + startActivity(new Intent(context, MiscFilesHandler.class)); + } + return true; } return false; @@ -375,7 +423,6 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen // Check if external media is in use. try { if (hasAppsAccessingStorage()) { - if (localLOGV) Log.i(TAG, "Do have storage users accessing media"); // Present dialog to user showDialogInner(DLG_CONFIRM_UNMOUNT); } else { @@ -400,19 +447,45 @@ public class Memory extends SettingsPreferenceFragment implements OnCancelListen } } - private void updateUiExact(long totalSize, long availSize, long mediaSize, long appsSize) { + private void updateUiExact(long totalSize, long availSize, long appsSize, long[] mediaSizes) { // There are other things that can take up storage, but we didn't measure it. // add that unaccounted-for-usage to Apps Usage - final long appsPlusRemaining = totalSize - availSize - mediaSize; - + long appsPlusRemaining = totalSize - availSize - mediaSizes[Constants.DOWNLOADS_INDEX] - + mediaSizes[Constants.PIC_VIDEO_INDEX] - mediaSizes[Constants.MUSIC_INDEX] - + mediaSizes[Constants.MEDIA_MISC_INDEX]; mInternalSize.setSummary(formatSize(totalSize)); mInternalAvail.setSummary(formatSize(availSize)); - mInternalMediaUsage.setSummary(formatSize(mediaSize)); mInternalAppsUsage.setSummary(formatSize(appsPlusRemaining)); mInternalUsageChart.clear(); - mInternalUsageChart.addEntry(mediaSize / (float) totalSize, mInternalMediaColor); mInternalUsageChart.addEntry(appsPlusRemaining / (float) totalSize, mInternalAppsColor); + + for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { + if (Constants.mMediaDirs.get(i).mPreferenceName == null) { + continue; + } + this.mMediaPreferences[i].setSummary(formatSize(mediaSizes[i])); + // don't add entry to color chart for media usage and for zero-sized dirs + if (i != Constants.MEDIA_INDEX && mediaSizes[i] > 0) { + int color = 0; + switch (i) { + case Constants.DOWNLOADS_INDEX: + color = mRes.getColor(R.color.memory_downloads); + break; + case Constants.PIC_VIDEO_INDEX: + color = mRes.getColor(R.color.memory_video); + break; + case Constants.MUSIC_INDEX: + color = mRes.getColor(R.color.memory_audio); + break; + case Constants.MEDIA_MISC_INDEX: + color = mRes.getColor(R.color.memory_misc); + break; + } + mInternalUsageChart.addEntry(mediaSizes[i] / (float) totalSize, color); + } + } + mInternalUsageChart.addEntry(availSize / (float) totalSize, mInternalAvailColor); mInternalUsageChart.commit(); } diff --git a/src/com/android/settings/deviceinfo/MemoryMeasurement.java b/src/com/android/settings/deviceinfo/MemoryMeasurement.java index 1aef202..3f57f21 100644 --- a/src/com/android/settings/deviceinfo/MemoryMeasurement.java +++ b/src/com/android/settings/deviceinfo/MemoryMeasurement.java @@ -23,6 +23,7 @@ import android.util.Log; import java.io.File; import java.lang.ref.WeakReference; import java.util.ArrayList; +import java.util.Collections; import java.util.List; /** @@ -35,14 +36,16 @@ import java.util.List; * * Filesystem stats (using StatFs) * Directory measurements (using DefaultContainerService.measureDir) - * Applicaiton measurements (using PackageManager) + * Application measurements (using PackageManager) * * Then the calling application would just specify the type and an argument. * This class would keep track of it while the calling application would * decide on how to use it. */ public class MemoryMeasurement { - private static final String TAG = "MemoryMeasurement"; + private static final String TAG = "MemorySettings"; + private static final boolean LOCAL_LOGV = true; + static final boolean LOGV = LOCAL_LOGV && Log.isLoggable(TAG, Log.VERBOSE); public static final String TOTAL_SIZE = "total_size"; @@ -50,7 +53,7 @@ public class MemoryMeasurement { public static final String APPS_USED = "apps_used"; - public static final String MEDIA_USED = "media_used"; + private long[] mMediaSizes = new long[Constants.NUM_MEDIA_DIRS_TRACKED]; private static final String DEFAULT_CONTAINER_PACKAGE = "com.android.defcontainer"; @@ -66,13 +69,13 @@ public class MemoryMeasurement { // Internal memory fields private long mInternalTotalSize; private long mInternalAvailSize; - private long mInternalMediaSize; private long mInternalAppsSize; // External memory fields private long mExternalTotalSize; private long mExternalAvailSize; + List<FileInfo> mFileInfoForMisc; private MemoryMeasurement(Context context) { // Start the thread that will measure the disk usage. @@ -98,7 +101,9 @@ public class MemoryMeasurement { } public void setReceiver(MeasurementReceiver receiver) { - mReceiver = new WeakReference<MeasurementReceiver>(receiver); + if (mReceiver == null || mReceiver.get() == null) { + mReceiver = new WeakReference<MeasurementReceiver>(receiver); + } } public void measureExternal() { @@ -134,6 +139,9 @@ public class MemoryMeasurement { private void sendInternalExactUpdate() { MeasurementReceiver receiver = (mReceiver != null) ? mReceiver.get() : null; if (receiver == null) { + if (LOGV) { + Log.i(TAG, "measurements dropped because receiver is null! wasted effort"); + } return; } @@ -141,7 +149,9 @@ public class MemoryMeasurement { bundle.putLong(TOTAL_SIZE, mInternalTotalSize); bundle.putLong(AVAIL_SIZE, mInternalAvailSize); bundle.putLong(APPS_USED, mInternalAppsSize); - bundle.putLong(MEDIA_USED, mInternalMediaSize); + for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED; i++) { + bundle.putLong(Constants.mMediaDirs.get(i).mKey, mMediaSizes[i]); + } receiver.updateExactInternal(bundle); } @@ -252,6 +262,7 @@ public class MemoryMeasurement { case MSG_CONNECTED: { IMediaContainerService imcs = (IMediaContainerService) msg.obj; measureExactInternalStorage(imcs); + break; } case MSG_DISCONNECT: { synchronized (mLock) { @@ -265,6 +276,7 @@ public class MemoryMeasurement { context.unbindService(mDefContainerConn); } } + break; } case MSG_COMPLETED: { mMeasured = true; @@ -356,24 +368,40 @@ public class MemoryMeasurement { if (context == null) { return; } - // We have to get installd to measure the package sizes. PackageManager pm = context.getPackageManager(); if (pm == null) { return; } + // measure sizes for all except "media_misc" - which is computed + for (int i = 0; i < Constants.NUM_MEDIA_DIRS_TRACKED - 1; i++) { + mMediaSizes[i] = 0; + String[] dirs = Constants.mMediaDirs.get(i).mDirPaths; + int len = dirs.length; + if (len > 0) { + for (int k = 0; k < len; k++) { + long dirSize = getSize(imcs, dirs[k]); + mMediaSizes[i] += dirSize; + if (LOGV) { + Log.i(TAG, "size of " + dirs[k] + ": " + dirSize); + } + } + } + } - long mediaSize; - try { - mediaSize = imcs.calculateDirectorySize( - Environment.getExternalStorageDirectory().getAbsolutePath()); - } catch (Exception e) { - Log.i(TAG, "Could not read memory from default container service"); - return; + // compute the size of "misc" + mMediaSizes[Constants.MEDIA_MISC_INDEX] = mMediaSizes[Constants.MEDIA_INDEX]; + for (int i = 1; i < Constants.NUM_MEDIA_DIRS_TRACKED - 1; i++) { + mMediaSizes[Constants.MEDIA_MISC_INDEX] -= mMediaSizes[i]; + } + if (LOGV) { + Log.i(TAG, "media_misc size: " + mMediaSizes[Constants.MEDIA_MISC_INDEX]); } - mInternalMediaSize = mediaSize; + // compute the sizes of each of the files/directories under 'misc' category + measureSizesOfMisc(imcs); + // compute apps sizes final List<ApplicationInfo> apps = pm .getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES | PackageManager.GET_DISABLED_COMPONENTS); @@ -393,6 +421,43 @@ public class MemoryMeasurement { // Sending of the message back to the MeasurementReceiver is // completed in the PackageObserver } + private void measureSizesOfMisc(IMediaContainerService imcs) { + File top = Environment.getExternalStorageDirectory(); + mFileInfoForMisc = new ArrayList<FileInfo>(); + File[] files = top.listFiles(); + int len = files.length; + if (len == 0) { + return; + } + // get sizes of all top level nodes in /sdcard dir except the ones already computed... + long counter = 0; + for (int i = 0; i < len; i++) { + String path = files[i].getAbsolutePath(); + if (Constants.ExclusionTargetsForMiscFiles.contains(path)) { + continue; + } + if (files[i].isFile()) { + mFileInfoForMisc.add(new FileInfo(path, files[i].length(), counter++)); + } else if (files[i].isDirectory()) { + long dirSize = getSize(imcs, path); + mFileInfoForMisc.add(new FileInfo(path, dirSize, counter++)); + } else { + } + } + // sort the list of FileInfo objects collected above in descending order of their sizes + Collections.sort(mFileInfoForMisc); + } + + private long getSize(IMediaContainerService imcs, String dir) { + try { + long size = imcs.calculateDirectorySize(dir); + return size; + } catch (Exception e) { + Log.w(TAG, "Could not read memory from default container service for " + + dir, e); + return -1; + } + } public void measureApproximateExternalStorage() { File path = Environment.getExternalStorageDirectory(); @@ -412,4 +477,29 @@ public class MemoryMeasurement { public void invalidate() { mHandler.sendEmptyMessage(MeasurementHandler.MSG_INVALIDATE); } + + boolean isSizeOfMiscCategorynonZero() { + return mFileInfoForMisc.size() > 0; + } + + static class FileInfo implements Comparable<FileInfo> { + String mFileName; + long mSize; + long mId; + FileInfo(String fileName, long size, long id) { + mFileName = fileName; + mSize = size; + mId = id; + } + @Override + public int compareTo(FileInfo that) { + if (this == that || mSize == that.mSize) return 0; + else if (mSize < that.mSize) return 1; // for descending sort + else return -1; + } + @Override + public String toString() { + return mFileName + " : " + mSize + ", id:" + mId; + } + } } diff --git a/src/com/android/settings/deviceinfo/MiscFilesHandler.java b/src/com/android/settings/deviceinfo/MiscFilesHandler.java new file mode 100644 index 0000000..5a92115 --- /dev/null +++ b/src/com/android/settings/deviceinfo/MiscFilesHandler.java @@ -0,0 +1,279 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.settings.deviceinfo; + +import com.android.settings.R; +import com.android.settings.deviceinfo.MemoryMeasurement.FileInfo; + +import android.app.ListActivity; +import android.content.Context; +import android.os.Bundle; +import android.text.format.Formatter; +import android.util.Log; +import android.util.SparseBooleanArray; +import android.view.ActionMode; +import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuInflater; +import android.view.MenuItem; +import android.view.View; +import android.view.View.OnClickListener; +import android.view.View.OnLongClickListener; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.CompoundButton; +import android.widget.CompoundButton.OnCheckedChangeListener; +import android.widget.ListView; + +import java.io.File; +import java.util.ArrayList; +import java.util.List; + +/** + * This class handles the selection and removal of Misc files. + */ +public class MiscFilesHandler extends ListActivity { + private static final String TAG = "MemorySettings"; + private String mNumSelectedStr; + private String mNumSelectedOutOfStr; + private MemoryMearurementAdapter mAdapter; + private LayoutInflater mInflater; + + @Override + public void onCreate(Bundle savedInstanceState) { + super.onCreate(savedInstanceState); + setFinishOnTouchOutside(true); + setTitle(R.string.misc_files); + mNumSelectedStr = getString(R.string.misc_files_selected_count); + mNumSelectedOutOfStr = getString(R.string.misc_files_selected_count_out_of); + mAdapter = new MemoryMearurementAdapter(this); + mInflater = (LayoutInflater)getSystemService(Context.LAYOUT_INFLATER_SERVICE); + setContentView(R.layout.settings_storage_miscfiles_list); + ListView lv = getListView(); + lv.setItemsCanFocus(true); + lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); + lv.setMultiChoiceModeListener(new ModeCallback(this)); + setListAdapter(mAdapter); + } + + private class ModeCallback implements ListView.MultiChoiceModeListener { + private int mDataCount; + private final Context mContext; + + public ModeCallback(Context context) { + mContext = context; + mDataCount = mAdapter.getCount(); + } + + public boolean onCreateActionMode(ActionMode mode, Menu menu) { + final MenuInflater inflater = getMenuInflater(); + inflater.inflate(R.menu.misc_files_menu, menu); + return true; + } + + public boolean onPrepareActionMode(ActionMode mode, Menu menu) { + return true; + } + + public boolean onActionItemClicked(ActionMode mode, MenuItem item) { + ListView lv = getListView(); + switch (item.getItemId()) { + case R.id.action_delete: + // delete the files selected + SparseBooleanArray checkedItems = lv.getCheckedItemPositions(); + int checkedCount = getListView().getCheckedItemCount(); + if (checkedCount > mDataCount) { + throw new IllegalStateException("checked item counts do not match. " + + "checkedCount: " + checkedCount + ", dataSize: " + mDataCount); + } + if (mDataCount > 0) { + ArrayList<Object> toRemove = new ArrayList<Object>(); + for (int i = 0; i < mDataCount; i++) { + if (!checkedItems.get(i)) { + //item not selected + continue; + } + if (MemoryMeasurement.LOGV) { + Log.i(TAG, "deleting: " + mAdapter.getItem(i)); + } + // delete the file + File file = new File(mAdapter.getItem(i).mFileName); + if (file.isDirectory()) { + deleteDir(file); + } else { + file.delete(); + } + toRemove.add(mAdapter.getItem(i)); + } + mAdapter.removeAll(toRemove); + mAdapter.notifyDataSetChanged(); + mDataCount = mAdapter.getCount(); + } + mode.finish(); + break; + + case R.id.action_select_all: + // check ALL items + for (int i = 0; i < mDataCount; i++) { + lv.setItemChecked(i, true); + } + // update the title and subtitle with number selected and numberBytes selected + onItemCheckedStateChanged(mode, 1, 0, true); + break; + } + return true; + } + + // Deletes all files and subdirectories under given dir. + // Returns true if all deletions were successful. + // If a deletion fails, the method stops attempting to delete and returns false. + private boolean deleteDir(File dir) { + if (dir.isDirectory()) { + String[] children = dir.list(); + for (int i=0; i < children.length; i++) { + boolean success = deleteDir(new File(dir, children[i])); + if (!success) { + return false; + } + } + } + // The directory is now empty so delete it + return dir.delete(); + } + + public void onDestroyActionMode(ActionMode mode) { + } + + public void onItemCheckedStateChanged(ActionMode mode, int position, long id, + boolean checked) { + ListView lv = getListView(); + int numChecked = lv.getCheckedItemCount(); + mode.setTitle(mNumSelectedStr + " : " + numChecked + + " " + mNumSelectedOutOfStr + " " + mAdapter.getCount()); + + // total the sizes of all items selected so far + SparseBooleanArray checkedItems = lv.getCheckedItemPositions(); + long selectedDataSize = 0; + if (numChecked > 0) { + for (int i = 0; i < mDataCount; i++) { + if (checkedItems.get(i)) { + // item is checked + selectedDataSize += mAdapter.getItem(i).mSize; + } + } + } + mode.setSubtitle(Formatter.formatFileSize(mContext, selectedDataSize) + + " " + mNumSelectedOutOfStr + " " + + Formatter.formatFileSize(mContext, mAdapter.getDataSize())); + } + } + + public class MemoryMearurementAdapter extends BaseAdapter { + private ArrayList<MemoryMeasurement.FileInfo> mData = null; + private long mDataSize = 0; + private Context mContext; + + public MemoryMearurementAdapter(Context context) { + mContext = context; + MemoryMeasurement mMeasurement = MemoryMeasurement.getInstance(context); + mData = (ArrayList<MemoryMeasurement.FileInfo>)mMeasurement.mFileInfoForMisc; + if (mData != null) { + for (MemoryMeasurement.FileInfo info : mData) { + mDataSize += info.mSize; + } + } + } + + @Override + public int getCount() { + return (mData == null) ? 0 : mData.size(); + } + + @Override + public MemoryMeasurement.FileInfo getItem(int position) { + if (mData == null || mData.size() <= position) { + return null; + } + return mData.get(position); + } + + @Override + public long getItemId(int position) { + if (mData == null || mData.size() <= position) { + return 0; + } + return mData.get(position).mId; + } + public void removeAll(List<Object> objs) { + if (mData == null) { + return; + } + for (Object o : objs) { + mData.remove(o); + mDataSize -= ((MemoryMeasurement.FileInfo) o).mSize; + } + } + + public long getDataSize() { + return mDataSize; + } + + @Override + public void notifyDataSetChanged() { + super.notifyDataSetChanged(); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + final FileItemInfoLayout view = (convertView == null) ? + (FileItemInfoLayout) mInflater.inflate(R.layout.settings_storage_miscfiles, + parent, false) : (FileItemInfoLayout) convertView; + FileInfo item = getItem(position); + view.setFileName(item.mFileName); + view.setFileSize(Formatter.formatFileSize(mContext, item.mSize)); + final ListView listView = (ListView) parent; + final int listPosition = position; + view.getCheckBox().setOnCheckedChangeListener(new OnCheckedChangeListener() { + + @Override + public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { + listView.setItemChecked(listPosition, isChecked); + } + + }); + view.setOnLongClickListener(new OnLongClickListener() { + @Override + public boolean onLongClick(View v) { + if (listView.getCheckedItemCount() > 0) { + return false; + } + listView.setItemChecked(listPosition, !view.isChecked()); + return true; + } + }); + view.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + if (listView.getCheckedItemCount() > 0) { + listView.setItemChecked(listPosition, !view.isChecked()); + } + } + }); + return view; + } + } +}
\ No newline at end of file |