Implementing contact upgrade under low storage conditions.

Bug: 2498528
Change-Id: I2c85b0cbd4c7b804e61957695b803e22f995b405
diff --git a/res/layout-finger/contacts_list_content.xml b/res/layout-finger/contacts_list_content.xml
index 2474975..36c03ce 100644
--- a/res/layout-finger/contacts_list_content.xml
+++ b/res/layout-finger/contacts_list_content.xml
@@ -30,22 +30,6 @@
         android:fastScrollEnabled="true"
     />
 
-    <ScrollView android:id="@android:id/empty"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:fillViewport="true"
-    >
-        <TextView android:id="@+id/emptyText"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:text="@string/noContacts"
-            android:textSize="20sp"
-            android:textColor="?android:attr/textColorSecondary"
-            android:paddingLeft="10dip"
-            android:paddingRight="10dip"
-            android:paddingTop="10dip"
-            android:lineSpacingMultiplier="0.92"
-        />
-    </ScrollView>
+    <include layout="@layout/contacts_list_empty"/>
 
 </LinearLayout>
diff --git a/res/layout-finger/contacts_list_empty.xml b/res/layout-finger/contacts_list_empty.xml
new file mode 100644
index 0000000..195da1e
--- /dev/null
+++ b/res/layout-finger/contacts_list_empty.xml
@@ -0,0 +1,64 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<ScrollView 
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@android:id/empty"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:fillViewport="true"
+>
+    <LinearLayout 
+      android:layout_width="match_parent"
+      android:layout_height="wrap_content"
+      android:orientation="vertical">
+      
+      <TextView android:id="@+id/emptyText"
+          android:layout_width="match_parent"
+          android:layout_height="wrap_content"
+          android:text="@string/noContacts"
+          android:textSize="20sp"
+          android:textColor="?android:attr/textColorSecondary"
+          android:paddingLeft="10dip"
+          android:paddingRight="10dip"
+          android:paddingTop="10dip"
+          android:lineSpacingMultiplier="0.92"
+      />
+      
+      <LinearLayout android:id="@+id/import_failure"
+        android:layout_width="match_parent"
+        android:layout_height="wrap_content"
+        android:orientation="vertical"
+        android:gravity="fill_horizontal"
+        android:padding="20dip"
+        android:visibility="gone">
+        
+        <Button
+            android:id="@+id/import_failure_uninstall_apps"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"            
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/upgrade_out_of_memory_uninstall"/>
+            
+        <Button
+            android:id="@+id/import_failure_retry_upgrade"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginTop="10dip"
+            android:textAppearance="?android:attr/textAppearanceMedium"
+            android:text="@string/upgrade_out_of_memory_retry"/>
+      </LinearLayout>
+    </LinearLayout>
+</ScrollView>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 38d63d0..39a2e18 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1114,4 +1114,18 @@
 
     <!-- Text shown in the contacts app while the background process updates contacts after a locale change -->
     <string name="locale_change_in_progress">Contact list is being updated to reflect the change of language.\n\nPlease wait...</string>
+
+    <!-- Text shown in the contacts app while the background process updates contacts after a system upgrade -->
+    <string name="upgrade_in_progress">Contact list is being updated.\n\nPlease wait...</string>
+
+    <!-- Text shown in the contacts app if the background process updating contacts fails because of memory shortage -->
+    <string name="upgrade_out_of_memory">Contacts are in the process of being upgraded.
+    \n\nThe upgrade process requires approximately <xliff:g id="size_in_megabytes">%d</xliff:g>Mb of 
+    internal phone storage.\n\nChoose one of the following options:</string>
+    
+    <!-- Button shown in the contacts app if the background process updating contacts fails because of memory shortage -->
+    <string name="upgrade_out_of_memory_uninstall">Uninstall some applications</string>
+    
+    <!-- Button shown in the contacts app if the background process updating contacts fails because of memory shortage -->
+    <string name="upgrade_out_of_memory_retry">Retry upgrade</string>
 </resources>
diff --git a/src/com/android/contacts/ContactsListActivity.java b/src/com/android/contacts/ContactsListActivity.java
index 2dfab29..4a35287 100644
--- a/src/com/android/contacts/ContactsListActivity.java
+++ b/src/com/android/contacts/ContactsListActivity.java
@@ -100,6 +100,7 @@
 import android.view.View;
 import android.view.ViewGroup;
 import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnClickListener;
 import android.view.View.OnFocusChangeListener;
 import android.view.View.OnTouchListener;
 import android.view.inputmethod.EditorInfo;
@@ -107,6 +108,7 @@
 import android.widget.AbsListView;
 import android.widget.AdapterView;
 import android.widget.ArrayAdapter;
+import android.widget.Button;
 import android.widget.Filter;
 import android.widget.ImageView;
 import android.widget.ListView;
@@ -440,7 +442,7 @@
     private static final int QUERY_MODE_MAILTO = 1;
     private static final int QUERY_MODE_TEL = 2;
 
-    private boolean mProviderStatusNormal = true;
+    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
 
     private boolean mSearchMode;
     private boolean mShowNumberOfContacts;
@@ -1012,7 +1014,7 @@
             mSearchEditText.requestFocus();
         }
 
-        if (!checkProviderState(mJustCreated)) {
+        if (!mSearchMode && !checkProviderState(mJustCreated)) {
             return;
         }
 
@@ -1032,6 +1034,13 @@
      * @return true if the provider status is normal
      */
     private boolean checkProviderState(boolean loadData) {
+        View importFailureView = findViewById(R.id.import_failure);
+        if (importFailureView == null) {
+            return true;
+        }
+
+        TextView messageView = (TextView) findViewById(R.id.emptyText);
+
         // This query can be performed on the UI thread because
         // the API explicitly allows such use.
         Cursor cursor = getContentResolver().query(ProviderStatus.CONTENT_URI, new String[] {
@@ -1040,27 +1049,76 @@
         try {
             if (cursor.moveToFirst()) {
                 int status = cursor.getInt(0);
-                switch (status) {
-                    case ProviderStatus.STATUS_NORMAL:
-                        mProviderStatusNormal = true;
-                        if (loadData) {
-                            startQuery();
-                        }
-                        return true;
+                if (status != mProviderStatus) {
+                    mProviderStatus = status;
+                    switch (status) {
+                        case ProviderStatus.STATUS_NORMAL:
+                            mAdapter.notifyDataSetInvalidated();
+                            if (loadData) {
+                                startQuery();
+                            }
+                            break;
 
-                    case ProviderStatus.STATUS_CHANGING_LOCALE:
-                        mProviderStatusNormal = false;
-                        TextView empty = (TextView) findViewById(R.id.emptyText);
-                        empty.setText(R.string.locale_change_in_progress);
-                        mAdapter.changeCursor(null);
-                        return false;
+                        case ProviderStatus.STATUS_CHANGING_LOCALE:
+                            messageView.setText(R.string.locale_change_in_progress);
+                            mAdapter.changeCursor(null);
+                            mAdapter.notifyDataSetInvalidated();
+                            break;
+
+                        case ProviderStatus.STATUS_UPGRADING:
+                            messageView.setText(R.string.upgrade_in_progress);
+                            mAdapter.changeCursor(null);
+                            mAdapter.notifyDataSetInvalidated();
+                            break;
+
+                        case ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY:
+                            long size = cursor.getLong(1);
+                            String message = getResources().getString(
+                                    R.string.upgrade_out_of_memory, new Object[] {size});
+                            messageView.setText(message);
+                            configureImportFailureView(importFailureView);
+                            mAdapter.changeCursor(null);
+                            mAdapter.notifyDataSetInvalidated();
+                            break;
+                    }
                 }
             }
         } finally {
             cursor.close();
         }
 
-        return true;
+        importFailureView.setVisibility(
+                mProviderStatus == ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY
+                        ? View.VISIBLE
+                        : View.GONE);
+        return mProviderStatus == ProviderStatus.STATUS_NORMAL;
+    }
+
+    private void configureImportFailureView(View importFailureView) {
+
+        OnClickListener listener = new OnClickListener(){
+
+            public void onClick(View v) {
+                switch(v.getId()) {
+                    case R.id.import_failure_uninstall_apps: {
+                        startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
+                        break;
+                    }
+                    case R.id.import_failure_retry_upgrade: {
+                        // Send a provider status update, which will trigger a retry
+                        ContentValues values = new ContentValues();
+                        values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_UPGRADING);
+                        getContentResolver().update(ProviderStatus.CONTENT_URI, values, null, null);
+                        break;
+                    }
+                }
+            }};
+
+        Button uninstallApps = (Button) findViewById(R.id.import_failure_uninstall_apps);
+        uninstallApps.setOnClickListener(listener);
+
+        Button retryUpgrade = (Button) findViewById(R.id.import_failure_retry_upgrade);
+        retryUpgrade.setOnClickListener(listener);
     }
 
     private String getTextFilter() {
@@ -1177,6 +1235,10 @@
     @Override
     public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
             boolean globalSearch) {
+        if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
+            return;
+        }
+
         if (globalSearch) {
             super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
         } else {
@@ -1246,7 +1308,7 @@
     }
 
     @Override
-    protected Dialog onCreateDialog(int id) {
+    protected Dialog onCreateDialog(int id, Bundle bundle) {
         switch (id) {
             case R.string.import_from_sim:
             case R.string.import_from_sdcard: {
@@ -1310,7 +1372,7 @@
                         }).create();
             }
         }
-        return super.onCreateDialog(id);
+        return super.onCreateDialog(id, bundle);
     }
 
     /**
@@ -1564,7 +1626,6 @@
         return super.onContextItemSelected(item);
     }
 
-
     /**
      * Event handler for the use case where the user starts typing without
      * bringing up the search UI first.
@@ -2768,7 +2829,7 @@
 
         @Override
         public boolean isEmpty() {
-            if (!mProviderStatusNormal) {
+            if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
                 return true;
             }
 
@@ -3223,7 +3284,9 @@
 
         @Override
         public void changeCursor(Cursor cursor) {
-            setLoading(false);
+            if (cursor != null) {
+                setLoading(false);
+            }
 
             // Get the split between starred and frequent items, if the mode is strequent
             mFrequentSeparatorPos = ListView.INVALID_POSITION;