Fix issue #3114356: Storage visualization in Manage Apps is confusing

Also fixes issue #3097388: If you launch Manage Applications when SD
card app info isn't available, incomplete information gets cached

Change-Id: If3377a965653590e5bc1df25e38764a83e96b820
This commit is contained in:
Dianne Hackborn
2010-10-20 16:01:50 -07:00
parent 916adce02d
commit d2be882d8f
6 changed files with 135 additions and 38 deletions

View File

@@ -43,9 +43,11 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="-5dp" android:layout_marginTop="-5dp"
android:orientation="horizontal" android:orientation="horizontal"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="30dp" android:paddingTop="30dp"
android:paddingLeft="2dp" android:paddingLeft="4dp"
android:paddingRight="2dp" android:paddingRight="4dp"
android:paddingBottom="1dp"> android:paddingBottom="1dp">
<TextView android:id="@+id/usedStorageText" <TextView android:id="@+id/usedStorageText"
android:layout_width="0px" android:layout_width="0px"
@@ -58,8 +60,11 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0" android:layout_weight="0"
android:layout_marginTop="-23dp"
android:textAppearance="?android:attr/textAppearanceSmallInverse" android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:textColor="#000" android:textColor="#ccc"
android:shadowColor="#000"
android:shadowRadius="5"
android:textStyle="bold" android:textStyle="bold"
android:singleLine="true" android:singleLine="true"
android:text="@string/internal_storage" /> android:text="@string/internal_storage" />

View File

@@ -41,9 +41,11 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="-5dp" android:layout_marginTop="-5dp"
android:orientation="horizontal" android:orientation="horizontal"
android:clipChildren="false"
android:clipToPadding="false"
android:paddingTop="30dp" android:paddingTop="30dp"
android:paddingLeft="2dp" android:paddingLeft="4dp"
android:paddingRight="2dp" android:paddingRight="4dp"
android:paddingBottom="1dp"> android:paddingBottom="1dp">
<TextView android:id="@+id/foregroundText" <TextView android:id="@+id/foregroundText"
android:layout_width="0px" android:layout_width="0px"
@@ -58,8 +60,11 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_weight="0" android:layout_weight="0"
android:layout_marginTop="-23dp"
android:textAppearance="?android:attr/textAppearanceSmallInverse" android:textAppearance="?android:attr/textAppearanceSmallInverse"
android:textColor="#000" android:textColor="#ccc"
android:shadowColor="#000"
android:shadowRadius="5"
android:textStyle="bold" android:textStyle="bold"
android:singleLine="true" android:singleLine="true"
android:text="@string/memory" /> android:text="@string/memory" />

View File

@@ -21,6 +21,7 @@ import android.os.SystemClock;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.util.Log; import android.util.Log;
import java.io.File;
import java.text.Collator; import java.text.Collator;
import java.text.Normalizer; import java.text.Normalizer;
import java.text.Normalizer.Form; import java.text.Normalizer.Form;
@@ -66,14 +67,17 @@ public class ApplicationsState {
} }
public static class AppEntry { public static class AppEntry {
final String label; final File apkFile;
final long id; final long id;
String label;
long size; long size;
long cacheSize; long cacheSize;
long codeSize; long codeSize;
long dataSize; long dataSize;
boolean mounted;
String getNormalizedLabel() { String getNormalizedLabel() {
if (normalizedLabel != null) { if (normalizedLabel != null) {
return normalizedLabel; return normalizedLabel;
@@ -92,12 +96,47 @@ public class ApplicationsState {
String normalizedLabel; String normalizedLabel;
AppEntry(Context context, ApplicationInfo info, long id) { AppEntry(Context context, ApplicationInfo info, long id) {
CharSequence label = info.loadLabel(context.getPackageManager()); apkFile = new File(info.sourceDir);
this.label = label != null ? label.toString() : info.packageName;
this.id = id; this.id = id;
this.info = info; this.info = info;
this.size = SIZE_UNKNOWN; this.size = SIZE_UNKNOWN;
this.sizeStale = true; this.sizeStale = true;
ensureLabel(context);
}
void ensureLabel(Context context) {
if (this.label == null || !this.mounted) {
if (!this.apkFile.exists()) {
this.mounted = false;
this.label = info.packageName;
} else {
this.mounted = true;
CharSequence label = info.loadLabel(context.getPackageManager());
this.label = label != null ? label.toString() : info.packageName;
}
}
}
boolean ensureIconLocked(Context context, PackageManager pm) {
if (this.icon == null) {
if (this.apkFile.exists()) {
this.icon = this.info.loadIcon(pm);
return true;
} else {
this.mounted = false;
this.icon = context.getResources().getDrawable(
com.android.internal.R.drawable.sym_app_on_sd_unavailable_icon);
}
} else if (!this.mounted) {
// If the app wasn't mounted but is now mounted, reload
// its icon.
if (this.apkFile.exists()) {
this.mounted = true;
this.icon = this.info.loadIcon(pm);
return true;
}
}
return false;
} }
} }
@@ -217,9 +256,11 @@ public class ApplicationsState {
return; return;
} }
boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr); boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
for (String pkgName : pkgList) { if (avail) {
if (avail) addPackage(pkgName); for (String pkgName : pkgList) {
else removePackage(pkgName); removePackage(pkgName);
addPackage(pkgName);
}
} }
} }
} }
@@ -312,6 +353,13 @@ public class ApplicationsState {
for (int i=0; i<mAppEntries.size(); i++) { for (int i=0; i<mAppEntries.size(); i++) {
mAppEntries.get(i).sizeStale = true; mAppEntries.get(i).sizeStale = true;
} }
for (int i=0; i<mApplications.size(); i++) {
final ApplicationInfo info = mApplications.get(i);
final AppEntry entry = mEntriesMap.get(info.packageName);
if (entry != null) {
entry.info = info;
}
}
mCurComputingSizePkg = null; mCurComputingSizePkg = null;
if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) { if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES); mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
@@ -391,6 +439,7 @@ public class ApplicationsState {
if (filter == null || filter.filterApp(info)) { if (filter == null || filter.filterApp(info)) {
synchronized (mEntriesMap) { synchronized (mEntriesMap) {
AppEntry entry = getEntryLocked(info); AppEntry entry = getEntryLocked(info);
entry.ensureLabel(mContext);
if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry); if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
filteredApps.add(entry); filteredApps.add(entry);
} }
@@ -438,12 +487,10 @@ public class ApplicationsState {
return; return;
} }
synchronized (entry) { synchronized (entry) {
if (entry.icon == null) { entry.ensureIconLocked(mContext, mPm);
entry.icon = entry.info.loadIcon(mPm);
}
} }
} }
void requestSize(String packageName) { void requestSize(String packageName) {
synchronized (mEntriesMap) { synchronized (mEntriesMap) {
AppEntry entry = mEntriesMap.get(packageName); AppEntry entry = mEntriesMap.get(packageName);
@@ -453,6 +500,16 @@ public class ApplicationsState {
} }
} }
long sumCacheSizes() {
long sum = 0;
synchronized (mEntriesMap) {
for (int i=mAppEntries.size()-1; i>=0; i--) {
sum += mAppEntries.get(i).cacheSize;
}
}
return sum;
}
int indexOfApplicationInfoLocked(String pkgName) { int indexOfApplicationInfoLocked(String pkgName) {
for (int i=mApplications.size()-1; i>=0; i--) { for (int i=mApplications.size()-1; i>=0; i--) {
if (mApplications.get(i).packageName.equals(pkgName)) { if (mApplications.get(i).packageName.equals(pkgName)) {
@@ -630,16 +687,17 @@ public class ApplicationsState {
synchronized (mEntriesMap) { synchronized (mEntriesMap) {
for (int i=0; i<mAppEntries.size() && numDone<2; i++) { for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
AppEntry entry = mAppEntries.get(i); AppEntry entry = mAppEntries.get(i);
if (entry.icon == null) { if (entry.icon == null || !entry.mounted) {
if (!mRunning) {
mRunning = true;
Message m = mMainHandler.obtainMessage(
MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
mMainHandler.sendMessage(m);
}
numDone++;
synchronized (entry) { synchronized (entry) {
entry.icon = entry.info.loadIcon(mPm); if (entry.ensureIconLocked(mContext, mPm)) {
if (!mRunning) {
mRunning = true;
Message m = mMainHandler.obtainMessage(
MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
mMainHandler.sendMessage(m);
}
numDone++;
}
} }
} }
} }

View File

@@ -16,7 +16,7 @@ import android.widget.LinearLayout;
public class LinearColorBar extends LinearLayout { public class LinearColorBar extends LinearLayout {
static final int LEFT_COLOR = 0xffa0a0a0; static final int LEFT_COLOR = 0xffa0a0a0;
static final int MIDDLE_COLOR = 0xff7070ff; static final int MIDDLE_COLOR = 0xffa0a0a0;
static final int RIGHT_COLOR = 0xffa0c0a0; static final int RIGHT_COLOR = 0xffa0c0a0;
private float mRedRatio; private float mRedRatio;
@@ -47,6 +47,7 @@ public class LinearColorBar extends LinearLayout {
? 2 : 1; ? 2 : 1;
mEdgeGradientPaint.setStrokeWidth(mLineWidth); mEdgeGradientPaint.setStrokeWidth(mLineWidth);
mEdgeGradientPaint.setAntiAlias(true); mEdgeGradientPaint.setAntiAlias(true);
} }
public void setRatios(float red, float yellow, float green) { public void setRatios(float red, float yellow, float green) {
@@ -111,22 +112,33 @@ public class LinearColorBar extends LinearLayout {
mColorPath.reset(); mColorPath.reset();
mEdgePath.reset(); mEdgePath.reset();
if (indicatorLeft < indicatorRight) { if (indicatorLeft < indicatorRight) {
final int midTopY = mRect.top;
final int midBottomY = 0;
final int xoff = 2;
mColorPath.moveTo(indicatorLeft, mRect.top); mColorPath.moveTo(indicatorLeft, mRect.top);
mColorPath.lineTo(-1, 0); mColorPath.cubicTo(indicatorLeft, midBottomY,
mColorPath.lineTo(width, 0); -xoff, midTopY,
mColorPath.lineTo(indicatorRight, mRect.top); -xoff, 0);
mColorPath.lineTo(width+xoff-1, 0);
mColorPath.cubicTo(width+xoff-1, midTopY,
indicatorRight, midBottomY,
indicatorRight, mRect.top);
mColorPath.close(); mColorPath.close();
float lineOffset = mLineWidth+.5f; final float lineOffset = mLineWidth+.5f;
mEdgePath.moveTo(indicatorLeft+lineOffset, mRect.top); mEdgePath.moveTo(-xoff+lineOffset, 0);
mEdgePath.lineTo(-1+lineOffset, 0); mEdgePath.cubicTo(-xoff+lineOffset, midTopY,
mEdgePath.moveTo(indicatorRight-lineOffset, mRect.top); indicatorLeft+lineOffset, midBottomY,
mEdgePath.lineTo(width-lineOffset, 0); indicatorLeft+lineOffset, mRect.top);
mEdgePath.moveTo(width+xoff-1-lineOffset, 0);
mEdgePath.cubicTo(width+xoff-1-lineOffset, midTopY,
indicatorRight-lineOffset, midBottomY,
indicatorRight-lineOffset, mRect.top);
} }
mLastInterestingLeft = indicatorLeft; mLastInterestingLeft = indicatorLeft;
mLastInterestingRight = indicatorRight; mLastInterestingRight = indicatorRight;
} }
if (!mColorPath.isEmpty()) { if (!mEdgePath.isEmpty()) {
canvas.drawPath(mEdgePath, mEdgeGradientPaint); canvas.drawPath(mEdgePath, mEdgeGradientPaint);
canvas.drawPath(mColorPath, mColorGradientPaint); canvas.drawPath(mColorPath, mColorGradientPaint);
} }

View File

@@ -36,6 +36,7 @@ import android.os.StatFs;
import android.provider.Settings; import android.provider.Settings;
import android.text.format.Formatter; import android.text.format.Formatter;
import android.util.Log; import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
@@ -43,6 +44,7 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.view.animation.AnimationUtils; import android.view.animation.AnimationUtils;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView; import android.widget.AbsListView;
import android.widget.AdapterView; import android.widget.AdapterView;
import android.widget.BaseAdapter; import android.widget.BaseAdapter;
@@ -57,7 +59,6 @@ import android.widget.AdapterView.OnItemClickListener;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Comparator; import java.util.Comparator;
import java.util.List;
final class CanBeOnSdCardChecker { final class CanBeOnSdCardChecker {
final IPackageManager mPm; final IPackageManager mPm;
@@ -118,7 +119,7 @@ public class ManageApplications extends TabActivity implements
// constant value that can be used to check return code from sub activity. // constant value that can be used to check return code from sub activity.
private static final int INSTALLED_APP_DETAILS = 1; private static final int INSTALLED_APP_DETAILS = 1;
// sort order that can be changed through the menu can be sorted alphabetically // sort order that can be changed through the menu can be sorted alphabetically
// or size(descending) // or size(descending)
private static final int MENU_OPTIONS_BASE = 0; private static final int MENU_OPTIONS_BASE = 0;
@@ -676,6 +677,18 @@ public class ManageApplications extends TabActivity implements
} }
return true; return true;
} }
@Override
public boolean onKeyUp(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_SEARCH && event.isTracking()) {
if (mCurView != VIEW_RUNNING) {
((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE))
.showSoftInputUnchecked(0, null);
}
return true;
}
return super.onKeyUp(keyCode, event);
}
public void onItemClick(AdapterView<?> parent, View view, int position, public void onItemClick(AdapterView<?> parent, View view, int position,
long id) { long id) {
@@ -738,8 +751,8 @@ public class ManageApplications extends TabActivity implements
for (int i=0; i<N; i++) { for (int i=0; i<N; i++) {
ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i); ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
appStorage += ae.codeSize + ae.dataSize; appStorage += ae.codeSize + ae.dataSize;
freeStorage += ae.cacheSize;
} }
freeStorage += mApplicationsState.sumCacheSizes();
} }
if (newLabel != null) { if (newLabel != null) {
mStorageChartLabel.setText(newLabel); mStorageChartLabel.setText(newLabel);
@@ -831,6 +844,8 @@ public class ManageApplications extends TabActivity implements
} else if (TAB_SDCARD.equalsIgnoreCase(tabId)) { } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
newOption = FILTER_APPS_SDCARD; newOption = FILTER_APPS_SDCARD;
} else if (TAB_RUNNING.equalsIgnoreCase(tabId)) { } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE))
.hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);
selectView(VIEW_RUNNING); selectView(VIEW_RUNNING);
return; return;
} else { } else {

View File

@@ -285,6 +285,8 @@ public class BatteryHistoryChart extends View {
} }
} }
a.recycle();
mTextPaint.setColor(textColor.getDefaultColor()); mTextPaint.setColor(textColor.getDefaultColor());
mTextPaint.setTextSize(textSize); mTextPaint.setTextSize(textSize);