Merge "display amount of storage on /sdcard by downloads, music etc"

This commit is contained in:
Vasu Nori
2011-02-18 12:06:41 -08:00
committed by Android (Google) Code Review
13 changed files with 831 additions and 55 deletions

View File

@@ -4,6 +4,7 @@
<original-package android:name="com.android.settings" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
<uses-permission android:name="android.permission.DEVICE_POWER" />
@@ -763,6 +764,9 @@
android:resource="@id/storage_settings" />
</activity>
<activity android:name=".deviceinfo.MiscFilesHandler"
android:theme="@android:style/Theme.Holo.DialogWhenLarge"/>
<activity android:name="ApnEditor"
android:label="@string/apn_edit"
android:theme="@android:style/Theme.Holo.DialogWhenLarge">

View File

@@ -0,0 +1,55 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->
<com.android.settings.deviceinfo.FileItemInfoLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="?android:attr/listPreferredItemHeight"
android:paddingRight="?android:attr/scrollbarSize"
android:background="?android:attr/selectableItemBackground"
android:gravity="center_vertical"
android:focusable="true">
<CheckBox android:id="@+id/misc_checkbox"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_alignParentLeft="true"
android:paddingLeft="16dip"
android:scaleType="fitCenter"
android:layout_centerVertical="true"/>
<TextView android:id="@+id/misc_filename"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/misc_checkbox"
android:maxLines="1"
android:paddingLeft="16dip"
android:textAppearance="?android:attr/textAppearanceSmall"/>
<TextView android:id="@+id/misc_filesize"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/misc_checkbox"
android:layout_below="@id/misc_filename"
android:maxLines="1"
android:paddingLeft="16dip"
android:textStyle="bold"
android:textAppearance="?android:attr/textAppearanceSmall" />
</com.android.settings.deviceinfo.FileItemInfoLayout>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
** Copyright 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.
*/
-->
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingRight="16dip"
android:paddingLeft="16dip">
<ListView android:id="@android:id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</FrameLayout>

View File

@@ -0,0 +1,25 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2011 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.
-->
<menu xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@+id/action_delete"
android:title="@string/delete"
android:icon="@android:drawable/ic_menu_delete"/>
<item android:id="@+id/action_select_all"
android:title="@string/select_all"
android:showAsAction="ifRoom" />
</menu>

View File

@@ -19,9 +19,12 @@
<color name="red">#F00</color>
<color name="memory_avail">#333</color>
<color name="memory_media_usage">#F33</color>
<color name="memory_apps_usage">#3F3</color>
<color name="memory_used">#FFF</color>
<color name="memory_apps_usage">#FFF</color>
<color name="memory_used">#F00</color>
<color name="memory_downloads">#F00</color>
<color name="memory_video">#0F0</color>
<color name="memory_audio">#00F</color>
<color name="memory_misc">#FF0</color>
<color name="crypt_keeper_clock_background">#ff9a9a9a</color>
<color name="crypt_keeper_clock_foreground">#ff666666</color>

View File

@@ -18,4 +18,5 @@
<dimen name="vpn_connect_margin_right">10sp</dimen>
<dimen name="vpn_connect_normal_text_size">16sp</dimen>
<dimen name="vpn_connect_input_box_label_width">90sp</dimen>
<dimen name="device_memory_usage_button_size">32dip</dimen>
</resources>

View File

@@ -1566,10 +1566,18 @@
<string name="memory_size">Total</string>
<!-- SD card & phone storage settings summary. Displayed when the total memory usage is being calculated. Will be replaced with a number like "12.3 GB" when finished calucating. [CHAR LIMIT=30] -->
<string name="memory_calculating_size">Calculating…</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of applications installed. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=30] -->
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of applications installed. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_apps_usage">Applications</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of media on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=30] -->
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of media on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_media_usage">Media</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of /sdcard/Download on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_downloads_usage">Downloads</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of all pictures, videos in /sdcard/DCIM, /sdcard/Pictures folders on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_dcim_usage">Pictures, Videos</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of audio files in /sdcard on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_music_usage">Audio (music, ringtones, podcasts etc)</string>
<!-- SD card & phone storage settings title. Displayed as a title when showing the total usage of misc files on the device. Below it will be a number like "123.4 MB" indicating used storage. [CHAR LIMIT=50] -->
<string name="memory_media_misc_usage">Misc.</string>
<!-- SD card & phone storage settings item title that will result in the phone unmounting the SD card. This will be done before the user phyiscally removes the SD card from the phone. Kind of like the "Safely remove" on some operating systems. [CHAR LIMIT=25] -->
<string name="sd_eject" product="nosdcard">Unmount shared storage</string>
<!-- SD card & phone storage settings item title that will result in the phone unmounting the SD card. This will be done before the user phyiscally removes the SD card from the phone. Kind of like the "Safely remove" on some operating systems. -->
@@ -3257,4 +3265,15 @@ found in the list of installed applications.</string>
<!-- This error message is displayed when the vpn profile is going to be saved but the vpn service is busy [CHAR LIMIT=NONE] -->
<string name="service_busy">Service busy, try again</string>
<!-- the following are for Settings Storage screen -->
<!-- Menu item/button 'delete' -->
<string name="delete">Delete</string>
<!-- Misc files [CHAR LIMIT=25] -->
<string name="misc_files">Misc Files</string>
<!-- number of misc files selected [CHAR LIMIT=20] -->
<string name="misc_files_selected_count">selected</string>
<!-- the string 'out of' displayed when saying "selected N out of M" [CHAR LIMIT=20] -->
<string name="misc_files_selected_count_out_of">out of</string>
<!-- action to select all [CHAR LIMIT=30] -->
<string name="select_all">Select All</string>
</resources>

View File

@@ -41,14 +41,29 @@
<Preference android:key="memory_internal_size"
android:title="@string/memory_size"
android:summary="@string/memory_calculating_size"/>
<Preference android:key="memory_internal_media"
android:title="@string/memory_media_usage"
<Preference android:key="memory_internal_avail"
android:title="@string/memory_available"
android:summary="@string/memory_calculating_size"/>
<Preference android:key="memory_internal_apps"
android:title="@string/memory_apps_usage"
android:summary="@string/memory_calculating_size"/>
<Preference android:key="memory_internal_avail"
android:title="@string/memory_available"
<Preference android:key="memory_internal_downloads"
android:title="@string/memory_downloads_usage"
android:summary="@string/memory_calculating_size"/>
<Preference android:key="memory_internal_dcim"
android:title="@string/memory_dcim_usage"
android:summary="@string/memory_calculating_size"/>
<Preference android:key="memory_internal_music"
android:title="@string/memory_music_usage"
android:summary="@string/memory_calculating_size"/>
<Preference android:key="memory_internal_media_misc"
android:title="@string/memory_media_misc_usage"
android:summary="@string/memory_calculating_size"/>
</PreferenceCategory>

View File

@@ -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");
}
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}

View File

@@ -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;
}
long mediaSize;
try {
mediaSize = imcs.calculateDirectorySize(
Environment.getExternalStorageDirectory().getAbsolutePath());
} catch (Exception e) {
Log.i(TAG, "Could not read memory from default container service");
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);
}
}
}
}
mInternalMediaSize = mediaSize;
// 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]);
}
// 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;
}
}
}

View File

@@ -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;
}
}
}