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
diff --git a/res/layout/manage_applications.xml b/res/layout/manage_applications.xml
index 078322a..2d6678a 100755
--- a/res/layout/manage_applications.xml
+++ b/res/layout/manage_applications.xml
@@ -43,9 +43,11 @@
                 android:layout_height="wrap_content"
                 android:layout_marginTop="-5dp"
                 android:orientation="horizontal"
+                android:clipChildren="false"
+                android:clipToPadding="false"
                 android:paddingTop="30dp"
-                android:paddingLeft="2dp"
-                android:paddingRight="2dp"
+                android:paddingLeft="4dp"
+                android:paddingRight="4dp"
                 android:paddingBottom="1dp">
             <TextView android:id="@+id/usedStorageText"
                 android:layout_width="0px"
@@ -58,8 +60,11 @@
                 android:layout_width="wrap_content"
                 android:layout_height="wrap_content"
                 android:layout_weight="0"
+                android:layout_marginTop="-23dp"
                 android:textAppearance="?android:attr/textAppearanceSmallInverse"
-                android:textColor="#000"
+                android:textColor="#ccc"
+                android:shadowColor="#000"
+                android:shadowRadius="5"
                 android:textStyle="bold"
                 android:singleLine="true"
                 android:text="@string/internal_storage" />
diff --git a/res/layout/running_processes_view.xml b/res/layout/running_processes_view.xml
index 9f01eda..2e62c48 100644
--- a/res/layout/running_processes_view.xml
+++ b/res/layout/running_processes_view.xml
@@ -41,9 +41,11 @@
             android:layout_height="wrap_content"
             android:layout_marginTop="-5dp"
             android:orientation="horizontal"
+            android:clipChildren="false"
+            android:clipToPadding="false"
             android:paddingTop="30dp"
-            android:paddingLeft="2dp"
-            android:paddingRight="2dp"
+            android:paddingLeft="4dp"
+            android:paddingRight="4dp"
             android:paddingBottom="1dp">
         <TextView android:id="@+id/foregroundText"
             android:layout_width="0px"
@@ -58,8 +60,11 @@
             android:layout_width="wrap_content"
             android:layout_height="wrap_content"
             android:layout_weight="0"
+            android:layout_marginTop="-23dp"
             android:textAppearance="?android:attr/textAppearanceSmallInverse"
-            android:textColor="#000"
+            android:textColor="#ccc"
+            android:shadowColor="#000"
+            android:shadowRadius="5"
             android:textStyle="bold"
             android:singleLine="true"
             android:text="@string/memory" />
diff --git a/src/com/android/settings/applications/ApplicationsState.java b/src/com/android/settings/applications/ApplicationsState.java
index 9515624..c08ae5a 100644
--- a/src/com/android/settings/applications/ApplicationsState.java
+++ b/src/com/android/settings/applications/ApplicationsState.java
@@ -21,6 +21,7 @@
 import android.text.format.Formatter;
 import android.util.Log;
 
+import java.io.File;
 import java.text.Collator;
 import java.text.Normalizer;
 import java.text.Normalizer.Form;
@@ -66,14 +67,17 @@
     }
 
     public static class AppEntry {
-        final String label;
+        final File apkFile;
         final long id;
+        String label;
         long size;
         
         long cacheSize;
         long codeSize;
         long dataSize;
 
+        boolean mounted;
+        
         String getNormalizedLabel() {
             if (normalizedLabel != null) {
                 return normalizedLabel;
@@ -92,12 +96,47 @@
         String normalizedLabel;
 
         AppEntry(Context context, ApplicationInfo info, long id) {
-            CharSequence label = info.loadLabel(context.getPackageManager());
-            this.label = label != null ? label.toString() : info.packageName;
+            apkFile = new File(info.sourceDir);
             this.id = id;
             this.info = info;
             this.size = SIZE_UNKNOWN;
             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 @@
                      return;
                  }
                  boolean avail = Intent.ACTION_EXTERNAL_APPLICATIONS_AVAILABLE.equals(actionStr);
-                 for (String pkgName : pkgList) {
-                     if (avail) addPackage(pkgName);
-                     else removePackage(pkgName);
+                 if (avail) {
+                     for (String pkgName : pkgList) {
+                         removePackage(pkgName);
+                         addPackage(pkgName);
+                     }
                  }
              }
          }
@@ -312,6 +353,13 @@
             for (int i=0; i<mAppEntries.size(); i++) {
                 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;
             if (!mBackgroundHandler.hasMessages(BackgroundHandler.MSG_LOAD_ENTRIES)) {
                 mBackgroundHandler.sendEmptyMessage(BackgroundHandler.MSG_LOAD_ENTRIES);
@@ -391,6 +439,7 @@
             if (filter == null || filter.filterApp(info)) {
                 synchronized (mEntriesMap) {
                     AppEntry entry = getEntryLocked(info);
+                    entry.ensureLabel(mContext);
                     if (DEBUG) Log.i(TAG, "Using " + info.packageName + ": " + entry);
                     filteredApps.add(entry);
                 }
@@ -438,12 +487,10 @@
             return;
         }
         synchronized (entry) {
-            if (entry.icon == null) {
-                entry.icon = entry.info.loadIcon(mPm);
-            }
+            entry.ensureIconLocked(mContext, mPm);
         }
     }
-
+    
     void requestSize(String packageName) {
         synchronized (mEntriesMap) {
             AppEntry entry = mEntriesMap.get(packageName);
@@ -453,6 +500,16 @@
         }
     }
 
+    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) {
         for (int i=mApplications.size()-1; i>=0; i--) {
             if (mApplications.get(i).packageName.equals(pkgName)) {
@@ -630,16 +687,17 @@
                     synchronized (mEntriesMap) {
                         for (int i=0; i<mAppEntries.size() && numDone<2; i++) {
                             AppEntry entry = mAppEntries.get(i);
-                            if (entry.icon == null) {
-                                if (!mRunning) {
-                                    mRunning = true;
-                                    Message m = mMainHandler.obtainMessage(
-                                            MainHandler.MSG_RUNNING_STATE_CHANGED, 1);
-                                    mMainHandler.sendMessage(m);
-                                }
-                                numDone++;
+                            if (entry.icon == null || !entry.mounted) {
                                 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++;
+                                    }
                                 }
                             }
                         }
diff --git a/src/com/android/settings/applications/LinearColorBar.java b/src/com/android/settings/applications/LinearColorBar.java
index 74164c4..0b9f7b0 100644
--- a/src/com/android/settings/applications/LinearColorBar.java
+++ b/src/com/android/settings/applications/LinearColorBar.java
@@ -16,7 +16,7 @@
 
 public class LinearColorBar extends LinearLayout {
     static final int LEFT_COLOR = 0xffa0a0a0;
-    static final int MIDDLE_COLOR = 0xff7070ff;
+    static final int MIDDLE_COLOR = 0xffa0a0a0;
     static final int RIGHT_COLOR = 0xffa0c0a0;
 
     private float mRedRatio;
@@ -47,6 +47,7 @@
                 ? 2 : 1;
         mEdgeGradientPaint.setStrokeWidth(mLineWidth);
         mEdgeGradientPaint.setAntiAlias(true);
+        
     }
 
     public void setRatios(float red, float yellow, float green) {
@@ -111,22 +112,33 @@
             mColorPath.reset();
             mEdgePath.reset();
             if (indicatorLeft < indicatorRight) {
+                final int midTopY = mRect.top;
+                final int midBottomY = 0;
+                final int xoff = 2;
                 mColorPath.moveTo(indicatorLeft, mRect.top);
-                mColorPath.lineTo(-1, 0);
-                mColorPath.lineTo(width, 0);
-                mColorPath.lineTo(indicatorRight, mRect.top);
+                mColorPath.cubicTo(indicatorLeft, midBottomY,
+                        -xoff, midTopY,
+                        -xoff, 0);
+                mColorPath.lineTo(width+xoff-1, 0);
+                mColorPath.cubicTo(width+xoff-1, midTopY,
+                        indicatorRight, midBottomY,
+                        indicatorRight, mRect.top);
                 mColorPath.close();
-                float lineOffset = mLineWidth+.5f;
-                mEdgePath.moveTo(indicatorLeft+lineOffset, mRect.top);
-                mEdgePath.lineTo(-1+lineOffset, 0);
-                mEdgePath.moveTo(indicatorRight-lineOffset, mRect.top);
-                mEdgePath.lineTo(width-lineOffset, 0);
+                final float lineOffset = mLineWidth+.5f;
+                mEdgePath.moveTo(-xoff+lineOffset, 0);
+                mEdgePath.cubicTo(-xoff+lineOffset, midTopY,
+                        indicatorLeft+lineOffset, midBottomY,
+                        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;
             mLastInterestingRight = indicatorRight;
         }
 
-        if (!mColorPath.isEmpty()) {
+        if (!mEdgePath.isEmpty()) {
             canvas.drawPath(mEdgePath, mEdgeGradientPaint);
             canvas.drawPath(mColorPath, mColorGradientPaint);
         }
diff --git a/src/com/android/settings/applications/ManageApplications.java b/src/com/android/settings/applications/ManageApplications.java
index b4cd26ef..345766e 100644
--- a/src/com/android/settings/applications/ManageApplications.java
+++ b/src/com/android/settings/applications/ManageApplications.java
@@ -36,6 +36,7 @@
 import android.provider.Settings;
 import android.text.format.Formatter;
 import android.util.Log;
+import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.Menu;
 import android.view.MenuItem;
@@ -43,6 +44,7 @@
 import android.view.ViewGroup;
 import android.view.Window;
 import android.view.animation.AnimationUtils;
+import android.view.inputmethod.InputMethodManager;
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.BaseAdapter;
@@ -57,7 +59,6 @@
 
 import java.util.ArrayList;
 import java.util.Comparator;
-import java.util.List;
 
 final class CanBeOnSdCardChecker {
     final IPackageManager mPm;
@@ -118,7 +119,7 @@
     
     // constant value that can be used to check return code from sub activity.
     private static final int INSTALLED_APP_DETAILS = 1;
-    
+
     // sort order that can be changed through the menu can be sorted alphabetically
     // or size(descending)
     private static final int MENU_OPTIONS_BASE = 0;
@@ -676,6 +677,18 @@
         }
         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,
             long id) {
@@ -738,8 +751,8 @@
             for (int i=0; i<N; i++) {
                 ApplicationsState.AppEntry ae = mApplicationsAdapter.getAppEntry(i);
                 appStorage += ae.codeSize + ae.dataSize;
-                freeStorage += ae.cacheSize;
             }
+            freeStorage += mApplicationsState.sumCacheSizes();
         }
         if (newLabel != null) {
             mStorageChartLabel.setText(newLabel);
@@ -831,6 +844,8 @@
         } else if (TAB_SDCARD.equalsIgnoreCase(tabId)) {
             newOption = FILTER_APPS_SDCARD;
         } else if (TAB_RUNNING.equalsIgnoreCase(tabId)) {
+            ((InputMethodManager)getSystemService(Context.INPUT_METHOD_SERVICE))
+                    .hideSoftInputFromWindow(getWindow().getDecorView().getWindowToken(), 0);
             selectView(VIEW_RUNNING);
             return;
         } else {
diff --git a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
index f3e0056..23107da 100644
--- a/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
+++ b/src/com/android/settings/fuelgauge/BatteryHistoryChart.java
@@ -285,6 +285,8 @@
             }
         }
         
+        a.recycle();
+        
         mTextPaint.setColor(textColor.getDefaultColor());
         mTextPaint.setTextSize(textSize);