Resurrecting empty state UI

Change-Id: Idc1ee48d62d4f0c01756f9b7689e0f31235b61d9
diff --git a/res/layout-xlarge/contact_browser.xml b/res/layout-xlarge/contact_browser.xml
index b03fc47..4110eda 100644
--- a/res/layout-xlarge/contact_browser.xml
+++ b/res/layout-xlarge/contact_browser.xml
@@ -14,46 +14,69 @@
      limitations under the License.
 -->
 
-<com.android.contacts.widget.InterpolatingLayout
+<FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
-    android:id="@+id/two_pane_activity"
     android:layout_width="match_parent"
-    android:layout_height="match_parent"
-    android:splitMotionEvents="true">
+    android:layout_height="match_parent">
 
-    <FrameLayout
-        android:id="@+id/list_container"
-        android:layout_height="match_parent"
-        android:layout_width="wrap_content"
-        android:minWidth="100dip"
-        ex:layout_narrowParentWidth="800dip"
-        ex:layout_narrowWidth="310dip"
-        ex:layout_wideParentWidth="1280dip"
-        ex:layout_wideWidth="430dip"
-    />
-
-    <view
-        class="com.android.contacts.widget.TransitionAnimationView"
+    <com.android.contacts.widget.InterpolatingLayout
+        android:id="@+id/main_view"
         android:layout_width="match_parent"
         android:layout_height="match_parent"
-        ex:layout_narrowParentWidth="800dip"
-        ex:layout_narrowRightMargin="0dip"
-        ex:layout_wideParentWidth="1280dip"
-        ex:layout_wideRightMargin="48dip"
-        ex:clipMarginLeft="3dip"
-        ex:clipMarginTop="3dip"
-        ex:clipMarginRight="3dip"
-        ex:clipMarginBottom="9dip"
-        ex:enterAnimation="@android:anim/animator_fade_in"
-        ex:exitAnimation="@android:anim/animator_fade_out"
-        ex:animationDuration="80">
+        android:splitMotionEvents="true">
+
         <FrameLayout
-            android:id="@+id/detail_container"
+            android:id="@+id/list_container"
+            android:layout_height="match_parent"
+            android:layout_width="wrap_content"
+            android:minWidth="100dip"
+            ex:layout_narrowParentWidth="800dip"
+            ex:layout_narrowWidth="310dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideWidth="430dip" />
+
+        <view
+            class="com.android.contacts.widget.TransitionAnimationView"
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:paddingBottom="6dip"
-        />
-    </view>
+            ex:layout_narrowParentWidth="800dip"
+            ex:layout_narrowRightMargin="0dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideRightMargin="48dip"
+            ex:clipMarginLeft="3dip"
+            ex:clipMarginTop="3dip"
+            ex:clipMarginRight="3dip"
+            ex:clipMarginBottom="9dip"
+            ex:enterAnimation="@android:anim/animator_fade_in"
+            ex:exitAnimation="@android:anim/animator_fade_out"
+            ex:animationDuration="80">
+            <FrameLayout
+                android:id="@+id/detail_container"
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:paddingBottom="6dip" />
+        </view>
 
-</com.android.contacts.widget.InterpolatingLayout>
+    </com.android.contacts.widget.InterpolatingLayout>
+
+    <com.android.contacts.widget.InterpolatingLayout
+        android:id="@+id/contacts_unavailable_view"
+        android:layout_width="match_parent"
+        android:layout_height="match_parent"
+        android:visibility="gone">
+
+        <FrameLayout
+            android:id="@+id/contacts_unavailable_container"
+            android:layout_height="match_parent"
+            android:layout_width="match_parent"
+            ex:layout_narrowParentWidth="800dip"
+            ex:layout_narrowLeftMargin="80dip"
+            ex:layout_narrowRightMargin="80dip"
+            ex:layout_wideParentWidth="1280dip"
+            ex:layout_wideLeftMargin="200dip"
+            ex:layout_wideRightMargin="200dip"
+            android:paddingBottom="20dip" />
+
+    </com.android.contacts.widget.InterpolatingLayout>
+</FrameLayout>
diff --git a/res/layout-xlarge/contact_picker_content.xml b/res/layout-xlarge/contact_picker_content.xml
index 9112073..5cc6ac9 100644
--- a/res/layout-xlarge/contact_picker_content.xml
+++ b/res/layout-xlarge/contact_picker_content.xml
@@ -53,8 +53,6 @@
             android:scrollingCache="false"
         />
 
-        <include layout="@layout/contacts_list_empty"/>
-
         <ViewStub android:id="@+id/footer_stub"
             android:layout="@layout/footer_panel"
             android:layout_width="fill_parent"
diff --git a/res/layout-xlarge/contacts_list_content.xml b/res/layout-xlarge/contacts_list_content.xml
index a025751..4e1aa19 100644
--- a/res/layout-xlarge/contacts_list_content.xml
+++ b/res/layout-xlarge/contacts_list_content.xml
@@ -54,8 +54,6 @@
             android:scrollingCache="false"
         />
 
-        <include layout="@layout/contacts_list_empty"/>
-
         <ViewStub android:id="@+id/footer_stub"
             android:layout="@layout/footer_panel"
             android:layout_width="fill_parent"
diff --git a/res/layout/contact_picker_content.xml b/res/layout/contact_picker_content.xml
index 56e8340..0e46281 100644
--- a/res/layout/contact_picker_content.xml
+++ b/res/layout/contact_picker_content.xml
@@ -38,8 +38,6 @@
             android:layout_weight="1"
         />
 
-        <include layout="@layout/contacts_list_empty"/>
-
         <ViewStub android:id="@+id/footer_stub"
             android:layout="@layout/footer_panel"
             android:layout_width="fill_parent"
diff --git a/res/layout/contacts_list_content.xml b/res/layout/contacts_list_content.xml
index 56e8340..0e46281 100644
--- a/res/layout/contacts_list_content.xml
+++ b/res/layout/contacts_list_content.xml
@@ -38,8 +38,6 @@
             android:layout_weight="1"
         />
 
-        <include layout="@layout/contacts_list_empty"/>
-
         <ViewStub android:id="@+id/footer_stub"
             android:layout="@layout/footer_panel"
             android:layout_width="fill_parent"
diff --git a/res/layout/contacts_list_empty.xml b/res/layout/contacts_list_empty.xml
deleted file mode 100644
index d655899..0000000
--- a/res/layout/contacts_list_empty.xml
+++ /dev/null
@@ -1,66 +0,0 @@
-<?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.
--->
-<view 
-    xmlns:android="http://schemas.android.com/apk/res/android"
-    class="com.android.contacts.ContactListEmptyView"
-    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"
-          android:visibility="gone"
-      />
-      
-      <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>
-</view>
diff --git a/res/layout/contacts_unavailable_fragment.xml b/res/layout/contacts_unavailable_fragment.xml
new file mode 100644
index 0000000..44c51b5
--- /dev/null
+++ b/res/layout/contacts_unavailable_fragment.xml
@@ -0,0 +1,83 @@
+<?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.
+-->
+
+<com.android.contacts.widget.InterpolatingLayout
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:ex="http://schemas.android.com/apk/res/com.android.contacts"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:background="@drawable/panel_message">
+
+    <LinearLayout
+        android:layout_width="0dip"
+        android:layout_height="wrap_content"
+        android:layout_gravity="center"
+        android:orientation="vertical"
+        ex:layout_narrowParentWidth="600dip"
+        ex:layout_narrowWidth="400dip"
+        ex:layout_wideParentWidth="880dip"
+        ex:layout_wideWidth="600dip">
+
+        <TextView
+            android:id="@+id/message"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:textAppearance="?android:attr/textAppearanceLarge"
+            android:layout_marginBottom="20dip" />
+
+        <Button
+            android:id="@+id/create_contact_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dip"
+            android:text="@string/contacts_unavailable_create_contact" />
+
+        <Button
+            android:id="@+id/add_account_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dip"
+            android:text="@string/contacts_unavailable_add_account" />
+
+        <Button
+            android:id="@+id/import_contacts_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dip"
+            android:text="@string/contacts_unavailable_import_contacts" />
+
+        <Button
+            android:id="@+id/import_failure_uninstall_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dip"
+            android:text="@string/upgrade_out_of_memory_uninstall" />
+
+        <Button
+            android:id="@+id/import_failure_retry_button"
+            android:layout_width="match_parent"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15dip"
+            android:text="@string/upgrade_out_of_memory_retry" />
+
+        <ProgressBar
+            android:id="@+id/progress"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_gravity="center_horizontal"
+            android:layout_marginBottom="15dip" />
+    </LinearLayout>
+</com.android.contacts.widget.InterpolatingLayout>
diff --git a/res/layout/custom_list_filter.xml b/res/layout/custom_list_filter.xml
deleted file mode 100644
index 2b029a7..0000000
--- a/res/layout/custom_list_filter.xml
+++ /dev/null
@@ -1,62 +0,0 @@
-<?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.
--->
-
-<LinearLayout
-        xmlns:android="http://schemas.android.com/apk/res/android"
-        android:id="@+id/pinned_header_list_layout"
-        android:layout_width="match_parent"
-        android:layout_height="match_parent"
-        android:orientation="horizontal"
-        >
-
-    <LinearLayout
-            android:layout_width="0px"
-            android:layout_height="match_parent"
-            android:orientation="vertical"
-            android:layout_weight="1"
-            >
-
-        <Spinner
-            android:id="@+id/filter_spinner"
-            android:layout_width="match_parent"
-            android:layout_height="wrap_content"
-            android:prompt="@string/list_filter_prompt"
-            android:visibility="gone"
-        />
-
-        <view
-            class="com.android.contacts.list.ContactEntryListView"
-            android:id="@android:id/list"
-            android:layout_width="match_parent"
-            android:layout_height="0dip"
-            android:fastScrollEnabled="true"
-            android:layout_weight="1"
-        />
-
-        <include layout="@layout/contacts_list_empty"/>
-
-        <ViewStub android:id="@+id/footer_stub"
-            android:layout="@layout/footer_panel"
-            android:layout_width="fill_parent"
-            android:layout_height="wrap_content"
-        />
-    </LinearLayout>
-    <com.android.contacts.list.ContactListAizyView
-        android:id="@+id/contacts_list_aizy"
-        android:layout_width="30dip"
-        android:layout_height="match_parent"
-    />
-</LinearLayout>
diff --git a/res/values/strings.xml b/res/values/strings.xml
index e993a18..eda0d74 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1219,19 +1219,15 @@
     <string name="pick_new_photo">Select new photo from Gallery</string>
 
     <!-- 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>
+    <string name="locale_change_in_progress">Contact list is being updated to reflect the change of language.</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>
+    <string name="upgrade_in_progress">Contact list is being updated.</string>
 
     <!-- Text shown in the contacts app if the background process updating contacts fails because of memory shortage -->
-    <string name="upgrade_out_of_memory" product="tablet">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 tablet storage.\n\nChoose one of the following options:</string>
-    <!-- Text shown in the contacts app if the background process updating contacts fails because of memory shortage -->
-    <string name="upgrade_out_of_memory" product="default">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>
+    <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">%s</xliff:g>
+    Mb of internal 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>
@@ -1392,4 +1388,17 @@
 
     <!-- Message of widget while it is loading data [CHAR LIMIT=20] -->
     <string name="social_widget_loading">Loading \u2026</string>
+
+    <!-- Button shown on the main contacts screen when there are no contacts on the device.
+    Creates a new contact. [CHAR LIMIT=128] -->
+    <string name="contacts_unavailable_create_contact">Create a new contact</string>
+
+    <!-- Button shown on the main contacts screen when there are no contacts on the device.
+    Navigates to account setup [CHAR LIMIT=128] -->
+    <string name="contacts_unavailable_add_account">Sign in to an account</string>
+
+    <!-- Button shown on the main contacts screen when there are no contacts on the device.
+    Initiates a contact import dialog [CHAR LIMIT=128] -->
+    <string name="contacts_unavailable_import_contacts">Import contacts from a file</string>
+
 </resources>
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index b6c7586..45c3771 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -63,6 +63,8 @@
     private ContactListFilterController mFilterController;
     private View mFilterContainer;
 
+    private boolean mEnabled;
+
     public ActionBarAdapter(Context context) {
         mContext = context;
     }
@@ -99,6 +101,11 @@
         update();
     }
 
+    public void setEnabled(boolean enabled) {
+        mEnabled = enabled;
+        update();
+    }
+
     public void setListener(Listener listener) {
         mListener = listener;
     }
@@ -157,7 +164,10 @@
     }
 
     public void update() {
-        if (mSearchMode) {
+        if (!mEnabled) {
+            mNavigationBar.setVisibility(View.GONE);
+        } else if (mSearchMode) {
+            mNavigationBar.setVisibility(View.VISIBLE);
             mSearchLabel.setVisibility(View.VISIBLE);
             mFilterView.setVisibility(View.GONE);
             mFilterIndicator.setVisibility(View.INVISIBLE);
@@ -165,6 +175,7 @@
                 mFilterController.setEnabled(false);
             }
         } else {
+            mNavigationBar.setVisibility(View.VISIBLE);
             mSearchLabel.setVisibility(View.GONE);
             mFilterView.setVisibility(View.VISIBLE);
             boolean showIndicator = false;
diff --git a/src/com/android/contacts/activities/ContactBrowserActivity.java b/src/com/android/contacts/activities/ContactBrowserActivity.java
index 1f035a3..5c98fb4 100644
--- a/src/com/android/contacts/activities/ContactBrowserActivity.java
+++ b/src/com/android/contacts/activities/ContactBrowserActivity.java
@@ -27,13 +27,18 @@
 import com.android.contacts.list.ContactListFilterController;
 import com.android.contacts.list.ContactsIntentResolver;
 import com.android.contacts.list.ContactsRequest;
+import com.android.contacts.list.ContactsUnavailableFragment;
 import com.android.contacts.list.CustomContactListFilterActivity;
 import com.android.contacts.list.DefaultContactBrowseListFragment;
 import com.android.contacts.list.DirectoryListLoader;
 import com.android.contacts.list.OnContactBrowserActionListener;
+import com.android.contacts.list.OnContactsUnavailableActionListener;
+import com.android.contacts.list.ProviderStatusLoader;
+import com.android.contacts.list.ProviderStatusLoader.ProviderStatusListener;
 import com.android.contacts.list.StrequentContactListFragment;
 import com.android.contacts.model.AccountTypes;
 import com.android.contacts.preference.ContactsPreferenceActivity;
+import com.android.contacts.util.AccountSelectionUtil;
 import com.android.contacts.util.AccountsListAdapter;
 import com.android.contacts.util.DialogManager;
 import com.android.contacts.util.ThemeUtils;
@@ -57,6 +62,7 @@
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Contacts;
 import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.ProviderStatus;
 import android.provider.Settings;
 import android.util.Log;
 import android.view.KeyEvent;
@@ -81,7 +87,7 @@
 public class ContactBrowserActivity extends Activity
         implements View.OnCreateContextMenuListener, ActionBarAdapter.Listener,
         DialogManager.DialogShowingViewActivity,
-        ContactListFilterController.ContactListFilterListener {
+        ContactListFilterController.ContactListFilterListener, ProviderStatusListener {
 
     private static final String TAG = "ContactBrowserActivity";
 
@@ -141,10 +147,15 @@
 
     private Handler mHandler;
 
+    private ContactsUnavailableFragment mContactsUnavailableFragment;
+    private ProviderStatusLoader mProviderStatusLoader;
+    private int mProviderStatus = -1;
+
     public ContactBrowserActivity() {
         mIntentResolver = new ContactsIntentResolver(this);
         mContactListFilterController = new ContactListFilterController(this);
         mContactListFilterController.addListener(this);
+        mProviderStatusLoader = new ProviderStatusLoader(this);
     }
 
     private Handler getHandler() {
@@ -161,6 +172,10 @@
         return mHandler;
     }
 
+    public boolean areContactsAvailable() {
+        return mProviderStatus == ProviderStatus.STATUS_NORMAL;
+    }
+
     @Override
     public void onAttachFragment(Fragment fragment) {
         if (fragment instanceof ContactBrowseListFragment) {
@@ -170,6 +185,11 @@
         } else if (fragment instanceof ContactDetailFragment) {
             mDetailFragment = (ContactDetailFragment)fragment;
             mDetailFragment.setListener(mDetailFragmentListener);
+        } else if (fragment instanceof ContactsUnavailableFragment) {
+            mContactsUnavailableFragment = (ContactsUnavailableFragment)fragment;
+            mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
+            mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
+                    new ContactsUnavailableFragmentListener());
         }
     }
 
@@ -268,6 +288,7 @@
         if (mActionBarAdapter != null) {
             mActionBarAdapter.setListener(null);
         }
+        mProviderStatusLoader.setProviderStatusListener(null);
         super.onPause();
     }
 
@@ -277,6 +298,8 @@
         if (mActionBarAdapter != null) {
             mActionBarAdapter.setListener(this);
         }
+        mProviderStatusLoader.setProviderStatusListener(this);
+        updateFragmentVisibility();
     }
 
     @Override
@@ -596,9 +619,60 @@
         fragment.setAizyEnabled(false);
         fragment.setSelectionVisible(true);
         fragment.setQuickContactEnabled(!mContactContentDisplayed);
+        invalidateOptionsMenu();
         return fragment;
     }
 
+    @Override
+    public void onProviderStatusChange() {
+        updateFragmentVisibility();
+    }
+
+    private void updateFragmentVisibility() {
+        int providerStatus = mProviderStatusLoader.getProviderStatus();
+        if (providerStatus == mProviderStatus) {
+            return;
+        }
+
+        mProviderStatus = providerStatus;
+
+        View contactsUnavailableView = findViewById(R.id.contacts_unavailable_view);
+        View mainView = findViewById(R.id.main_view);
+
+        if (mProviderStatus == ProviderStatus.STATUS_NORMAL) {
+            if (mHasActionBar) {
+                mActionBarAdapter.setEnabled(true);
+            }
+            if (mListFragment != null) {
+                mListFragment.setEnabled(true);
+            }
+            contactsUnavailableView.setVisibility(View.GONE);
+            mainView.setVisibility(View.VISIBLE);
+        } else {
+            if (mHasActionBar) {
+                mActionBarAdapter.setEnabled(false);
+            }
+            if (mListFragment != null) {
+                mListFragment.setEnabled(false);
+            }
+            if (mContactsUnavailableFragment == null) {
+                mContactsUnavailableFragment = new ContactsUnavailableFragment();
+                mContactsUnavailableFragment.setProviderStatusLoader(mProviderStatusLoader);
+                mContactsUnavailableFragment.setOnContactsUnavailableActionListener(
+                        new ContactsUnavailableFragmentListener());
+                getFragmentManager().openTransaction()
+                        .replace(R.id.contacts_unavailable_container, mContactsUnavailableFragment)
+                        .commit();
+            } else {
+                mContactsUnavailableFragment.update();
+            }
+            contactsUnavailableView.setVisibility(View.VISIBLE);
+            mainView.setVisibility(View.INVISIBLE);
+        }
+
+        invalidateOptionsMenu();
+    }
+
     private final class ContactBrowserActionListener implements OnContactBrowserActionListener {
         @Override
         public void onViewContactAction(Uri contactLookupUri) {
@@ -711,6 +785,33 @@
         }
     }
 
+    private class ContactsUnavailableFragmentListener
+            implements OnContactsUnavailableActionListener {
+
+        @Override
+        public void onCreateNewContactAction() {
+            startActivity(new Intent(Intent.ACTION_INSERT, Contacts.CONTENT_URI));
+        }
+
+        @Override
+        public void onAddAccountAction() {
+            Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
+            intent.putExtra(Settings.EXTRA_AUTHORITIES,
+                    new String[] { ContactsContract.AUTHORITY });
+            startActivity(intent);
+        }
+
+        @Override
+        public void onImportContactsFromFileAction() {
+            AccountSelectionUtil.doImportFromSdCard(ContactBrowserActivity.this, null);
+        }
+
+        @Override
+        public void onFreeInternalStorageAction() {
+            startActivity(new Intent(Settings.ACTION_MANAGE_APPLICATIONS_SETTINGS));
+        }
+    }
+
     public void startActivityAndForwardResult(final Intent intent) {
         intent.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
 
@@ -724,7 +825,31 @@
     }
 
     @Override
+    public boolean onCreatePanelMenu(int featureId, Menu menu) {
+        // No menu if contacts are unavailable
+        if (!areContactsAvailable()) {
+            return false;
+        }
+
+        return super.onCreatePanelMenu(featureId, menu);
+    }
+
+    @Override
+    public boolean onPreparePanel(int featureId, View view, Menu menu) {
+        // No menu if contacts are unavailable
+        if (!areContactsAvailable()) {
+            return false;
+        }
+
+        return super.onPreparePanel(featureId, view, menu);
+    }
+
+    @Override
     public boolean onCreateOptionsMenu(Menu menu) {
+        if (!areContactsAvailable()) {
+            return false;
+        }
+
         super.onCreateOptionsMenu(menu);
 
         MenuInflater inflater = getMenuInflater();
@@ -749,6 +874,10 @@
 
     @Override
     public boolean onPrepareOptionsMenu(Menu menu) {
+        if (!areContactsAvailable()) {
+            return false;
+        }
+
         MenuItem displayGroups = menu.findItem(R.id.menu_display_groups);
         if (displayGroups != null) {
             displayGroups.setVisible(
@@ -822,11 +951,6 @@
     @Override
     public void startSearch(String initialQuery, boolean selectInitialQuery, Bundle appSearchData,
             boolean globalSearch) {
-// TODO
-//        if (mProviderStatus != ProviderStatus.STATUS_NORMAL) {
-//            return;
-//        }
-
         if (globalSearch) {
             super.startSearch(initialQuery, selectInitialQuery, appSearchData, globalSearch);
         } else {
diff --git a/src/com/android/contacts/list/ContactEntryListFragment.java b/src/com/android/contacts/list/ContactEntryListFragment.java
index 6ec43f8..d21ad41 100644
--- a/src/com/android/contacts/list/ContactEntryListFragment.java
+++ b/src/com/android/contacts/list/ContactEntryListFragment.java
@@ -30,13 +30,11 @@
 import android.app.LoaderManager;
 import android.app.LoaderManager.LoaderCallbacks;
 import android.content.ContentResolver;
-import android.content.ContentValues;
 import android.content.Context;
 import android.content.CursorLoader;
 import android.content.IContentService;
 import android.content.Intent;
 import android.content.Loader;
-import android.database.ContentObserver;
 import android.database.Cursor;
 import android.os.Bundle;
 import android.os.Handler;
@@ -45,15 +43,12 @@
 import android.os.RemoteException;
 import android.provider.ContactsContract;
 import android.provider.ContactsContract.Directory;
-import android.provider.ContactsContract.ProviderStatus;
-import android.provider.Settings;
 import android.telephony.TelephonyManager;
 import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
 import android.view.View;
-import android.view.View.OnClickListener;
 import android.view.View.OnFocusChangeListener;
 import android.view.View.OnTouchListener;
 import android.view.ViewGroup;
@@ -62,7 +57,6 @@
 import android.widget.AbsListView.OnScrollListener;
 import android.widget.AdapterView;
 import android.widget.AdapterView.OnItemClickListener;
-import android.widget.Button;
 import android.widget.ListView;
 import android.widget.TextView;
 
@@ -112,6 +106,8 @@
     private ContactsRequest mRequest;
     private boolean mLegacyCompatibility;
 
+    private boolean mEnabled = true;
+
     private T mAdapter;
     private View mView;
     private ListView mListView;
@@ -132,8 +128,6 @@
     private ProviderStatusLoader mProviderStatusLoader;
     private ContactsPreferences mContactsPrefs;
 
-    private int mProviderStatus = ProviderStatus.STATUS_NORMAL;
-
     private boolean mForceLoad;
 
     private static final int STATUS_NOT_LOADED = 0;
@@ -190,6 +184,15 @@
         return mContext;
     }
 
+    public void setEnabled(boolean enabled) {
+        if (mEnabled != enabled) {
+            mEnabled = enabled;
+            if (mEnabled && mAdapter != null) {
+                reloadData();
+            }
+        }
+    }
+
     /**
      * Overrides a loader manager for use in unit tests.
      */
@@ -393,7 +396,7 @@
 
     @Override
     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
-        if (!checkProviderStatus(false)) {
+        if (!mEnabled) {
             if (data != null) {
                 data.close();
             }
@@ -793,8 +796,6 @@
     public void onResume() {
         super.onResume();
 
-        registerProviderStatusObserver();
-
         if (isPhotoLoaderEnabled()) {
             mPhotoLoader.resume();
         }
@@ -847,7 +848,6 @@
     public void onPause() {
         super.onPause();
         removePendingDirectorySearchRequests();
-        unregisterProviderStatusObserver();
     }
 
     /**
@@ -868,128 +868,6 @@
         }
     }
 
-    private ContentObserver mProviderStatusObserver = new ContentObserver(new Handler()) {
-        @Override
-        public void onChange(boolean selfChange) {
-            checkProviderStatus(true);
-        }
-    };
-
-    /**
-     * Register an observer for provider status changes - we will need to
-     * reflect them in the UI.
-     */
-    private void registerProviderStatusObserver() {
-        mContext.getContentResolver().registerContentObserver(ProviderStatus.CONTENT_URI,
-                false, mProviderStatusObserver);
-    }
-
-    /**
-     * Register an observer for provider status changes - we will need to
-     * reflect them in the UI.
-     */
-    private void unregisterProviderStatusObserver() {
-        mContext.getContentResolver().unregisterContentObserver(mProviderStatusObserver);
-    }
-
-    /**
-     * Obtains the contacts provider status and configures the UI accordingly.
-     *
-     * @param loadData true if the method needs to start a query when the
-     *            provider is in the normal state
-     * @return true if the provider status is normal
-     */
-    private boolean checkProviderStatus(boolean loadData) {
-        View importFailureView = findViewById(R.id.import_failure);
-        if (importFailureView == null) {
-            return true;
-        }
-
-        // This query can be performed on the UI thread because
-        // the API explicitly allows such use.
-        Cursor cursor = mContext.getContentResolver().query(ProviderStatus.CONTENT_URI,
-                new String[] { ProviderStatus.STATUS, ProviderStatus.DATA1 }, null, null, null);
-        if (cursor != null) {
-            try {
-                if (cursor.moveToFirst()) {
-                    int status = cursor.getInt(0);
-                    if (status != mProviderStatus) {
-                        mProviderStatus = status;
-                        switch (status) {
-                            case ProviderStatus.STATUS_NORMAL:
-                                mAdapter.notifyDataSetInvalidated();
-                                if (loadData) {
-                                    reloadData();
-                                }
-                                break;
-
-                            case ProviderStatus.STATUS_CHANGING_LOCALE:
-                                setEmptyText(R.string.locale_change_in_progress);
-                                mAdapter.changeCursor(null);
-                                mAdapter.notifyDataSetInvalidated();
-                                break;
-
-                            case ProviderStatus.STATUS_UPGRADING:
-                                setEmptyText(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 = mContext.getResources().getString(
-                                        R.string.upgrade_out_of_memory, new Object[] {size});
-                                TextView messageView = (TextView) findViewById(R.id.emptyText);
-                                messageView.setText(message);
-                                messageView.setVisibility(View.VISIBLE);
-                                configureImportFailureView(importFailureView);
-                                mAdapter.changeCursor(null);
-                                mAdapter.notifyDataSetInvalidated();
-                                break;
-                        }
-                    }
-                }
-            } finally {
-                cursor.close();
-            }
-        }
-
-        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: {
-                        // TODO break into a separate method
-                        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);
-                        mContext.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 View findViewById(int id) {
         return mView.findViewById(id);
     }
diff --git a/src/com/android/contacts/list/ContactsUnavailableFragment.java b/src/com/android/contacts/list/ContactsUnavailableFragment.java
new file mode 100644
index 0000000..2b17bf0
--- /dev/null
+++ b/src/com/android/contacts/list/ContactsUnavailableFragment.java
@@ -0,0 +1,142 @@
+/*
+ * 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.contacts.list;
+
+import com.android.contacts.R;
+
+import android.app.Fragment;
+import android.os.Bundle;
+import android.provider.ContactsContract.ProviderStatus;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+/**
+ * Fragment shown when contacts are unavailable. It contains provider status
+ * messaging as well as instructions for the user.
+ */
+public class ContactsUnavailableFragment extends Fragment implements OnClickListener {
+
+    private ProviderStatusLoader mProviderStatusLoader;
+
+    private View mView;
+    private TextView mMessageView;
+    private Button mCreateContactButton;
+    private Button mAddAccountButton;
+    private Button mImportContactsButton;
+    private Button mUninstallAppsButton;
+    private Button mRetryUpgradeButton;
+    private ProgressBar mProgress;
+
+    private OnContactsUnavailableActionListener mListener;
+
+    @Override
+    public View onCreateView(
+            LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+        mView = inflater.inflate(R.layout.contacts_unavailable_fragment, null);
+        mMessageView = (TextView) mView.findViewById(R.id.message);
+        mCreateContactButton = (Button) mView.findViewById(R.id.create_contact_button);
+        mCreateContactButton.setOnClickListener(this);
+        mAddAccountButton = (Button) mView.findViewById(R.id.add_account_button);
+        mAddAccountButton.setOnClickListener(this);
+        mImportContactsButton = (Button) mView.findViewById(R.id.import_contacts_button);
+        mImportContactsButton.setOnClickListener(this);
+        mUninstallAppsButton = (Button) mView.findViewById(R.id.import_failure_uninstall_button);
+        mUninstallAppsButton.setOnClickListener(this);
+        mRetryUpgradeButton = (Button) mView.findViewById(R.id.import_failure_retry_button);
+        mRetryUpgradeButton.setOnClickListener(this);
+        mProgress = (ProgressBar) mView.findViewById(R.id.progress);
+        update();
+        return mView;
+    }
+
+    public void setOnContactsUnavailableActionListener(
+            OnContactsUnavailableActionListener listener) {
+        mListener = listener;
+    }
+
+    public void setProviderStatusLoader(ProviderStatusLoader loader) {
+        mProviderStatusLoader = loader;
+    }
+
+    public void update() {
+        int providerStatus = mProviderStatusLoader.getProviderStatus();
+        System.out.println("PROVIDER STATUS: " + providerStatus);
+        switch (providerStatus) {
+            case ProviderStatus.STATUS_CHANGING_LOCALE:
+                mMessageView.setText(R.string.locale_change_in_progress);
+                mMessageView.setGravity(Gravity.CENTER_HORIZONTAL);
+                mCreateContactButton.setVisibility(View.GONE);
+                mAddAccountButton.setVisibility(View.GONE);
+                mImportContactsButton.setVisibility(View.GONE);
+                mUninstallAppsButton.setVisibility(View.GONE);
+                mRetryUpgradeButton.setVisibility(View.GONE);
+                mProgress.setVisibility(View.VISIBLE);
+                break;
+            case ProviderStatus.STATUS_UPGRADING:
+                mMessageView.setText(R.string.upgrade_in_progress);
+                mMessageView.setGravity(Gravity.CENTER_HORIZONTAL);
+                mCreateContactButton.setVisibility(View.GONE);
+                mAddAccountButton.setVisibility(View.GONE);
+                mImportContactsButton.setVisibility(View.GONE);
+                mUninstallAppsButton.setVisibility(View.GONE);
+                mRetryUpgradeButton.setVisibility(View.GONE);
+                mProgress.setVisibility(View.VISIBLE);
+                break;
+            case ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY:
+                String message = getResources().getString(R.string.upgrade_out_of_memory,
+                        new Object[] { mProviderStatusLoader.getProviderStatusData() });
+                mMessageView.setText(message);
+                mMessageView.setGravity(Gravity.LEFT);
+                mCreateContactButton.setVisibility(View.GONE);
+                mAddAccountButton.setVisibility(View.GONE);
+                mImportContactsButton.setVisibility(View.GONE);
+                mUninstallAppsButton.setVisibility(View.VISIBLE);
+                mRetryUpgradeButton.setVisibility(View.VISIBLE);
+                mProgress.setVisibility(View.GONE);
+                break;
+        }
+    }
+
+    @Override
+    public void onClick(View v) {
+        if (mListener == null) {
+            return;
+        }
+        switch (v.getId()) {
+            case R.id.create_contact_button:
+                mListener.onCreateNewContactAction();
+                break;
+            case R.id.add_account_button:
+                mListener.onAddAccountAction();
+                break;
+            case R.id.import_contacts_button:
+                mListener.onImportContactsFromFileAction();
+                break;
+            case R.id.import_failure_uninstall_button:
+                mListener.onFreeInternalStorageAction();
+                break;
+            case R.id.import_failure_retry_button:
+                mProviderStatusLoader.retryUpgrade();
+                break;
+        }
+    }
+}
diff --git a/src/com/android/contacts/list/OnContactsUnavailableActionListener.java b/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
new file mode 100644
index 0000000..e8edea8
--- /dev/null
+++ b/src/com/android/contacts/list/OnContactsUnavailableActionListener.java
@@ -0,0 +1,43 @@
+/*
+ * 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.contacts.list;
+
+/**
+ * Action callbacks that can be sent by the "contacts unavailable" fragment.
+ */
+public interface OnContactsUnavailableActionListener  {
+
+    /**
+     * Creates a new contact.
+     */
+    void onCreateNewContactAction();
+
+    /**
+     * Initiates addition of a contacts account.
+     */
+    void onAddAccountAction();
+
+    /**
+     * Initiates contact import from a file.
+     */
+    void onImportContactsFromFileAction();
+
+    /**
+     * Initiates an interaction that frees up some internal storage for the purposes
+     * of a database upgrade.
+     */
+    void onFreeInternalStorageAction();
+}
diff --git a/src/com/android/contacts/list/ProviderStatusLoader.java b/src/com/android/contacts/list/ProviderStatusLoader.java
index 2eb9672..f3afae4 100644
--- a/src/com/android/contacts/list/ProviderStatusLoader.java
+++ b/src/com/android/contacts/list/ProviderStatusLoader.java
@@ -15,197 +15,112 @@
  */
 package com.android.contacts.list;
 
+import android.content.ContentResolver;
+import android.content.ContentValues;
 import android.content.Context;
+import android.database.ContentObserver;
 import android.database.Cursor;
+import android.os.Handler;
 import android.provider.ContactsContract.ProviderStatus;
 
 /**
  * Checks provider status and configures a list adapter accordingly.
  */
-public class ProviderStatusLoader {
+public class ProviderStatusLoader extends ContentObserver {
+
+    /**
+     * Callback interface invoked when the provider status changes.
+     */
+    public interface ProviderStatusListener {
+        public void onProviderStatusChange();
+    }
+
+    private static final String[] PROJECTION = new String[] {
+        ProviderStatus.STATUS,
+        ProviderStatus.DATA1
+    };
+
+    private static final int UNKNOWN = -1;
 
     private final Context mContext;
+    private int mProviderStatus = UNKNOWN;
+    private String mProviderData;
+    private ProviderStatusListener mListener;
+    private Handler mHandler = new Handler();
 
     public ProviderStatusLoader(Context context) {
+        super(null);
         this.mContext = context;
     }
 
     public int getProviderStatus() {
+        if (mProviderStatus == UNKNOWN) {
+            loadProviderStatus();
+        }
+
+        return mProviderStatus;
+    }
+
+    public String getProviderStatusData() {
+        if (mProviderStatus == UNKNOWN) {
+            loadProviderStatus();
+        }
+
+        return mProviderData;
+    }
+
+    protected void loadProviderStatus() {
+
+        // Default to normal status
+        mProviderStatus = ProviderStatus.STATUS_NORMAL;
+
         // This query can be performed on the UI thread because
         // the API explicitly allows such use.
-        Cursor cursor = mContext.getContentResolver().query(
-                ProviderStatus.CONTENT_URI,
-                new String[] { ProviderStatus.STATUS, ProviderStatus.DATA1 }, null, null, null);
+        Cursor cursor = mContext.getContentResolver().query(ProviderStatus.CONTENT_URI,
+                PROJECTION, null, null, null);
         if (cursor != null) {
             try {
                 if (cursor.moveToFirst()) {
-                    return cursor.getInt(0);
+                    mProviderStatus = cursor.getInt(0);
+                    mProviderData = cursor.getString(1);
                 }
             } finally {
                 cursor.close();
             }
         }
-
-        return ProviderStatus.STATUS_NORMAL;
     }
 
+    public void setProviderStatusListener(ProviderStatusListener listener) {
+        mListener = listener;
 
-//  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[] { ProviderStatus.STATUS, ProviderStatus.DATA1 }, null, null, null);
-//  if (cursor != null) {
-//      try {
-//          if (cursor.moveToFirst()) {
-//              int status = cursor.getInt(0);
-//              if (status != mProviderStatus) {
-//                  mProviderStatus = status;
-//                  switch (status) {
-//                      case ProviderStatus.STATUS_NORMAL:
-//                          mAdapter.notifyDataSetInvalidated();
-//                          if (loadData) {
-//                              startQuery();
-//                          }
-//                          break;
-//
-//                      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();
-//      }
-//  }
-//
-//  importFailureView.setVisibility(
-//          mProviderStatus == ProviderStatus.STATUS_UPGRADE_OUT_OF_MEMORY
-//                  ? View.VISIBLE
-//                  : View.GONE);
-//  return mProviderStatus == ProviderStatus.STATUS_NORMAL;
-//}
+        ContentResolver resolver = mContext.getContentResolver();
+        if (listener != null) {
+            resolver.registerContentObserver(ProviderStatus.CONTENT_URI, false, this);
+        } else {
+            resolver.unregisterContentObserver(this);
+        }
+    }
 
-//
-//    /**
-//     * Obtains the contacts provider status and configures the UI accordingly.
-//     *
-//     * @param loadData true if the method needs to start a query when the
-//     *            provider is in the normal state
-//     * @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[] { ProviderStatus.STATUS, ProviderStatus.DATA1 }, null, null, null);
-//        if (cursor != null) {
-//            try {
-//                if (cursor.moveToFirst()) {
-//                    int status = cursor.getInt(0);
-//                    if (status != mProviderStatus) {
-//                        mProviderStatus = status;
-//                        switch (status) {
-//                            case ProviderStatus.STATUS_NORMAL:
-//                                mAdapter.notifyDataSetInvalidated();
-//                                if (loadData) {
-//                                    startQuery();
-//                                }
-//                                break;
-//
-//                            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();
-//            }
-//        }
-//
-//        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);
-//    }
+    @Override
+    public void onChange(boolean selfChange) {
+        // Deliver a notification on the UI thread
+        mHandler.post(new Runnable() {
+            @Override
+            public void run() {
+                if (mListener != null) {
+                    mProviderStatus = UNKNOWN;
+                    mListener.onProviderStatusChange();
+                }
+            }
+        });
+    }
 
+    /**
+     * Sends a provider status update, which will trigger a retry of database upgrade
+     */
+    public void retryUpgrade() {
+        ContentValues values = new ContentValues();
+        values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_UPGRADING);
+        mContext.getContentResolver().update(ProviderStatus.CONTENT_URI, values, null, null);
+    }
 }