Merge "[DO NOT MERGE] Fix NPE caused by misplaced parenthesis." into nyc-dev
diff --git a/InCallUI/src/com/android/incallui/CallCardFragment.java b/InCallUI/src/com/android/incallui/CallCardFragment.java
index 785d3d3..39dd5ea 100644
--- a/InCallUI/src/com/android/incallui/CallCardFragment.java
+++ b/InCallUI/src/com/android/incallui/CallCardFragment.java
@@ -45,6 +45,7 @@
 import android.view.ViewTreeObserver;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
 import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
 import android.view.animation.Animation;
 import android.view.animation.AnimationUtils;
 import android.widget.ImageButton;
@@ -1154,13 +1155,21 @@
         mHandler.postDelayed(new Runnable() {
             @Override
             public void run() {
-                if (getView() != null && getView().getParent() != null) {
+                if (getView() != null && getView().getParent() != null &&
+                        isAccessibilityEnabled(getContext())) {
                     AccessibilityEvent event = AccessibilityEvent.obtain(
                             AccessibilityEvent.TYPE_ANNOUNCEMENT);
                     dispatchPopulateAccessibilityEvent(event);
                     getView().getParent().requestSendAccessibilityEvent(getView(), event);
                 }
             }
+
+            private boolean isAccessibilityEnabled(Context context) {
+                AccessibilityManager accessibilityManager =
+                        (AccessibilityManager) context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+                return accessibilityManager != null && accessibilityManager.isEnabled();
+
+            }
         }, ACCESSIBILITY_ANNOUNCEMENT_DELAY_MS);
     }
 
diff --git a/res/drawable/selectable_primary_flat_button.xml b/res/drawable/selectable_primary_flat_button.xml
new file mode 100644
index 0000000..c6eb7a2
--- /dev/null
+++ b/res/drawable/selectable_primary_flat_button.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/*
+ * Copyright (C) 2016 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.
+ */
+-->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_enabled="false">
+        <shape><solid android:color="@color/material_grey_300" /></shape>
+    </item>
+  <item>
+    <shape><solid android:color="@color/dialer_theme_color" /></shape>
+  </item>
+</selector>
\ No newline at end of file
diff --git a/res/layout/blocked_number_header.xml b/res/layout/blocked_number_header.xml
index 0af9429..e4b795f 100644
--- a/res/layout/blocked_number_header.xml
+++ b/res/layout/blocked_number_header.xml
@@ -61,7 +61,7 @@
             android:orientation="vertical">
 
             <TextView
-                android:id="@+id/textView"
+                android:id="@+id/blocked_number_text_view"
                 style="@android:style/TextAppearance.Material.Subhead"
                 android:layout_width="wrap_content"
                 android:layout_height="48dp"
@@ -119,6 +119,51 @@
             </RelativeLayout>
 
             <LinearLayout
+                android:id="@+id/migrate_promo"
+                android:layout_width="match_parent"
+                android:layout_height="wrap_content"
+                android:orientation="vertical"
+                android:visibility="gone">
+
+                <TextView
+                    android:id="@+id/migrate_promo_header"
+                    style="@android:style/TextAppearance.Material.Subhead"
+                    android:layout_width="match_parent"
+                    android:layout_height="48dp"
+                    android:paddingStart="@dimen/blocked_number_container_padding"
+                    android:paddingEnd="@dimen/blocked_number_container_padding"
+                    android:gravity="center_vertical"
+                    android:textStyle="bold"
+                    android:text="@string/migrate_blocked_numbers_dialog_title"
+                    android:textColor="@color/blocked_number_header_color"/>
+
+                <TextView
+                  android:id="@+id/migrate_promo_description"
+                  android:layout_width="match_parent"
+                  android:layout_height="wrap_content"
+                  android:layout_marginStart="@dimen/blocked_number_container_padding"
+                  android:layout_marginEnd="@dimen/blocked_number_container_padding"
+                  android:layout_marginBottom="@dimen/blocked_number_container_padding"
+                  android:text="@string/migrate_blocked_numbers_dialog_message"
+                  android:textColor="@color/secondary_text_color"/>
+
+                <Button
+                  android:id="@+id/migrate_promo_allow_button"
+                  android:layout_width="wrap_content"
+                  android:layout_height="wrap_content"
+                  android:text="@string/migrate_blocked_numbers_dialog_allow_button"
+                  android:layout_marginStart="@dimen/blocked_number_container_padding"
+                  android:layout_marginEnd="@dimen/blocked_number_container_padding"
+                  android:layout_gravity="end"
+                  style="@style/DialerPrimaryFlatButtonStyle"
+                  android:layout_marginBottom="@dimen/blocked_number_container_padding"/>
+
+                <View
+                  style="@style/FullWidthDivider"/>
+
+            </LinearLayout>
+
+            <LinearLayout
                 android:id="@+id/add_number_linear_layout"
                 android:layout_width="match_parent"
                 android:layout_height="wrap_content"
diff --git a/res/values-uz-rUZ/strings.xml b/res/values-uz-rUZ/strings.xml
index 20f931b..01b73d6 100644
--- a/res/values-uz-rUZ/strings.xml
+++ b/res/values-uz-rUZ/strings.xml
@@ -126,7 +126,7 @@
     <string name="menu_callNumber" msgid="997146291983360266">"Qo‘ng‘iroq: <xliff:g id="NUMBER">%s</xliff:g>"</string>
     <string name="unknown" msgid="740067747858270469">"Noma’lum"</string>
     <string name="voicemail" msgid="3851469869202611441">"Ovozli xabar"</string>
-    <string name="private_num" msgid="6374339738119166953">"Shaxsiy raqam"</string>
+    <string name="private_num" msgid="6374339738119166953">"Yashirin raqam"</string>
     <string name="payphone" msgid="7726415831153618726">"Taksofon"</string>
     <string name="callDetailsShortDurationFormat" msgid="3988146235579303592">"<xliff:g id="SECONDS">%s</xliff:g> soniya"</string>
     <string name="callDetailsDurationFormat" msgid="6061406028764382234">"<xliff:g id="MINUTES">%s</xliff:g> daq <xliff:g id="SECONDS">%s</xliff:g> son"</string>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index 66a45f9..6a40d09 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -322,10 +322,25 @@
         <item name="android:textColor">@color/dialer_flat_button_text_color</item>
     </style>
 
+    <!-- Style for the 'primary' button in a view. Unlike the DialerFlatButtonStyle, this button -->
+    <!-- is not colored white, to draw more attention to it. -->
+    <style name="DialerPrimaryFlatButtonStyle" parent="@android:style/Widget.Material.Button">
+        <item name="android:background">@drawable/selectable_primary_flat_button</item>
+        <item name="android:paddingEnd">@dimen/button_horizontal_padding</item>
+        <item name="android:paddingStart">@dimen/button_horizontal_padding</item>
+        <item name="android:textColor">@android:color/white</item>
+    </style>
+
     <style name="BlockedNumbersDescriptionTextStyle">
         <item name="android:lineSpacingMultiplier">1.43</item>
         <item name="android:paddingTop">8dp</item>
         <item name="android:paddingBottom">8dp</item>
         <item name="android:textSize">@dimen/blocked_number_settings_description_text_size</item>
     </style>
+
+    <style name="FullWidthDivider">
+        <item name="android:layout_width">match_parent</item>
+        <item name="android:layout_height">1dp</item>
+        <item name="android:background">?android:attr/listDivider</item>
+    </style>
 </resources>
diff --git a/src/com/android/dialer/DialerApplication.java b/src/com/android/dialer/DialerApplication.java
index 189c682..1a0497b 100644
--- a/src/com/android/dialer/DialerApplication.java
+++ b/src/com/android/dialer/DialerApplication.java
@@ -19,11 +19,13 @@
 import android.app.Application;
 import android.content.Context;
 import android.os.Trace;
+import android.preference.PreferenceManager;
 import android.support.annotation.Nullable;
 
 import com.android.contacts.common.extensions.ExtensionsFactory;
 import com.android.contacts.common.testing.NeededForTesting;
-import com.android.dialer.compat.FilteredNumberCompat;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.filterednumber.BlockedNumbersAutoMigrator;
 
 public class DialerApplication extends Application {
 
@@ -39,6 +41,8 @@
         Trace.beginSection(TAG + " ExtensionsFactory initialization");
         ExtensionsFactory.init(getApplicationContext());
         Trace.endSection();
+        new BlockedNumbersAutoMigrator(PreferenceManager.getDefaultSharedPreferences(this),
+                new FilteredNumberAsyncQueryHandler(getContentResolver())).autoMigrate();
         Trace.endSection();
     }
 
diff --git a/src/com/android/dialer/calllog/MissedCallNotifier.java b/src/com/android/dialer/calllog/MissedCallNotifier.java
index 98d02d0..20fc5d5 100644
--- a/src/com/android/dialer/calllog/MissedCallNotifier.java
+++ b/src/com/android/dialer/calllog/MissedCallNotifier.java
@@ -254,7 +254,8 @@
         intent.setAction(
                 CallLogNotificationsService.ACTION_CALL_BACK_FROM_MISSED_CALL_NOTIFICATION);
         intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
-        return PendingIntent.getService(mContext, 0, intent, 0);
+        // Use FLAG_ONE_SHOT to avoid reusing previous PendingIntent with different number.
+        return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
     }
 
     private PendingIntent createSendSmsFromNotificationPendingIntent(String number) {
@@ -262,7 +263,8 @@
         intent.setAction(
                 CallLogNotificationsService.ACTION_SEND_SMS_FROM_MISSED_CALL_NOTIFICATION);
         intent.putExtra(CallLogNotificationsService.EXTRA_MISSED_CALL_NUMBER, number);
-        return PendingIntent.getService(mContext, 0, intent, 0);
+        // Use FLAG_ONE_SHOT to avoid reusing previous PendingIntent with different number.
+        return PendingIntent.getService(mContext, 0, intent, PendingIntent.FLAG_ONE_SHOT);
     }
 
     /**
diff --git a/src/com/android/dialer/compat/FilteredNumberCompat.java b/src/com/android/dialer/compat/FilteredNumberCompat.java
index 91563dc..008782d 100644
--- a/src/com/android/dialer/compat/FilteredNumberCompat.java
+++ b/src/com/android/dialer/compat/FilteredNumberCompat.java
@@ -16,6 +16,7 @@
 
 package com.android.dialer.compat;
 
+import com.google.common.base.MoreObjects;
 import com.google.common.base.Preconditions;
 
 import android.app.FragmentManager;
@@ -67,6 +68,8 @@
 
     private static Boolean isEnabledForTest;
 
+    private static Context contextForTest;
+
     /**
      * @return The column name for ID in the filtered number database.
      */
@@ -154,7 +157,8 @@
      */
     @NeededForTesting
     public static void setHasMigratedToNewBlocking(boolean hasMigrated) {
-        PreferenceManager.getDefaultSharedPreferences(DialerApplication.getContext()).edit()
+        PreferenceManager.getDefaultSharedPreferences(
+                MoreObjects.firstNonNull(contextForTest, DialerApplication.getContext())).edit()
                 .putBoolean(HAS_MIGRATED_TO_NEW_BLOCKING_KEY, hasMigrated).apply();
     }
 
@@ -163,6 +167,11 @@
         isEnabledForTest = isEnabled;
     }
 
+    @NeededForTesting
+    public static void setContextForTest(Context context) {
+        contextForTest = context;
+    }
+
     /**
      * Gets the content {@link Uri} for number filtering.
      *
diff --git a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
index 52ef49a..68a2e85 100644
--- a/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
+++ b/src/com/android/dialer/database/FilteredNumberAsyncQueryHandler.java
@@ -125,7 +125,7 @@
                 null, null, null);
     }
 
-    public final void hasBlockedNumbers(final OnHasBlockedNumbersListener listener) {
+    public void hasBlockedNumbers(final OnHasBlockedNumbersListener listener) {
         startQuery(NO_TOKEN,
                 new Listener() {
                     @Override
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java b/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java
new file mode 100644
index 0000000..ed0faab
--- /dev/null
+++ b/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigrator.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2016 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.dialer.filterednumber;
+
+import com.google.common.base.Preconditions;
+
+import android.content.SharedPreferences;
+import android.util.Log;
+
+import com.android.dialer.compat.FilteredNumberCompat;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
+
+/**
+ * Class responsible for checking if the user can be auto-migrated to {@link
+ * android.provider.BlockedNumberContract} blocking. In order for this to happen, the user cannot
+ * have any numbers that are blocked in the Dialer solution.
+ */
+public class BlockedNumbersAutoMigrator {
+
+    private static final String TAG = "BlockedNumbersAuto";
+
+    private static final String HAS_CHECKED_AUTO_MIGRATE_KEY = "checkedAutoMigrate";
+
+    private final SharedPreferences sharedPreferences;
+    private final FilteredNumberAsyncQueryHandler queryHandler;
+
+    /**
+     * Constructs the BlockedNumbersAutoMigrator with the given {@link SharedPreferences} and {@link
+     * FilteredNumberAsyncQueryHandler}.
+     *
+     * @param sharedPreferences The SharedPreferences used to persist information.
+     * @param queryHandler The FilteredNumberAsyncQueryHandler used to determine if there are
+     * blocked numbers.
+     * @throws NullPointerException if sharedPreferences or queryHandler are null.
+     */
+    public BlockedNumbersAutoMigrator(SharedPreferences sharedPreferences,
+            FilteredNumberAsyncQueryHandler queryHandler) {
+        this.sharedPreferences = Preconditions.checkNotNull(sharedPreferences);
+        this.queryHandler = Preconditions.checkNotNull(queryHandler);
+    }
+
+    /**
+     * Attempts to perform the auto-migration. Auto-migration will only be attempted once and can be
+     * performed only when the user has no blocked numbers. As a result of this method, the user
+     * will be migrated to the framework blocking solution, as determined by {@link
+     * FilteredNumberCompat#hasMigratedToNewBlocking()}.
+     */
+    public void autoMigrate() {
+        if (!shouldAttemptAutoMigrate()) {
+            return;
+        }
+
+        Log.i(TAG, "Attempting to auto-migrate.");
+        queryHandler.hasBlockedNumbers(new OnHasBlockedNumbersListener() {
+            @Override
+            public void onHasBlockedNumbers(boolean hasBlockedNumbers) {
+                if (hasBlockedNumbers) {
+                    Log.i(TAG, "Not auto-migrating: blocked numbers exist.");
+                    return;
+                }
+                Log.i(TAG, "Auto-migrating: no blocked numbers.");
+                FilteredNumberCompat.setHasMigratedToNewBlocking(true);
+            }
+        });
+    }
+
+    private boolean shouldAttemptAutoMigrate() {
+        if (sharedPreferences.contains(HAS_CHECKED_AUTO_MIGRATE_KEY)) {
+            Log.d(TAG, "Not attempting auto-migrate: already checked once.");
+            return false;
+        }
+        Log.i(TAG, "Updating state as already checked for auto-migrate.");
+        sharedPreferences.edit().putBoolean(HAS_CHECKED_AUTO_MIGRATE_KEY, true).apply();
+
+        if (!FilteredNumberCompat.canUseNewFiltering()) {
+            Log.i(TAG, "Not attempting auto-migrate: not available.");
+            return false;
+        }
+
+        if (FilteredNumberCompat.hasMigratedToNewBlocking()) {
+            Log.i(TAG, "Not attempting auto-migrate: already migrated.");
+            return false;
+        }
+        return true;
+    }
+}
diff --git a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java b/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
index 87ba8be..b64f186 100644
--- a/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
+++ b/src/com/android/dialer/filterednumber/BlockedNumbersFragment.java
@@ -15,6 +15,8 @@
  */
 package com.android.dialer.filterednumber;
 
+import com.google.common.base.MoreObjects;
+
 import android.app.ListFragment;
 import android.app.LoaderManager;
 import android.content.Context;
@@ -32,11 +34,12 @@
 import android.widget.ImageView;
 import android.widget.TextView;
 
-import com.android.contacts.common.compat.CompatUtils;
 import com.android.contacts.common.lettertiles.LetterTileDrawable;
+import com.android.contacts.common.testing.NeededForTesting;
 import com.android.dialer.R;
 import com.android.dialer.compat.FilteredNumberCompat;
 import com.android.dialer.database.FilteredNumberContract;
+import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
 import com.android.dialer.filterednumber.FilteredNumbersUtil.CheckForSendToVoicemailContactListener;
 import com.android.dialer.filterednumber.FilteredNumbersUtil.ImportSendToVoicemailContactsListener;
 import com.android.dialer.voicemail.VisualVoicemailEnabledChecker;
@@ -46,9 +49,12 @@
         VisualVoicemailEnabledChecker.Callback {
     private static final char ADD_BLOCKED_NUMBER_ICON_LETTER = '+';
 
+    private BlockedNumbersMigrator blockedNumbersMigratorForTest;
+    protected View migratePromoView;
+    private TextView blockedNumbersText;
+    private TextView footerText;
     private BlockedNumbersAdapter mAdapter;
     private VisualVoicemailEnabledChecker mVoicemailEnabledChecker;
-
     private View mImportSettings;
     private View mBlockedNumbersDisabledForEmergency;
     private View mBlockedNumberListDivider;
@@ -81,6 +87,9 @@
         }
         setListAdapter(mAdapter);
 
+        blockedNumbersText = (TextView) getListView().findViewById(R.id.blocked_number_text_view);
+        migratePromoView = getListView().findViewById(R.id.migrate_promo);
+        getListView().findViewById(R.id.migrate_promo_allow_button).setOnClickListener(this);
         mImportSettings = getListView().findViewById(R.id.import_settings);
         mBlockedNumbersDisabledForEmergency =
                 getListView().findViewById(R.id.blocked_numbers_disabled_for_emergency);
@@ -89,6 +98,8 @@
         getListView().findViewById(R.id.view_numbers_button).setOnClickListener(this);
         getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(this);
 
+        footerText = (TextView) getActivity().findViewById(
+            R.id.blocked_number_footer_textview);
         mVoicemailEnabledChecker = new VisualVoicemailEnabledChecker(getContext(),this);
         mVoicemailEnabledChecker.asyncUpdate();
         updateActiveVoicemailProvider();
@@ -124,13 +135,16 @@
         // new blocked numbers from the Blocked Management UI. They will be shown a promo card
         // asking them to migrate to new blocking instead.
         if (FilteredNumberCompat.canUseNewFiltering()) {
+            migratePromoView.setVisibility(View.VISIBLE);
+            blockedNumbersText.setVisibility(View.GONE);
             getListView().findViewById(R.id.add_number_linear_layout).setVisibility(View.GONE);
             getListView().findViewById(R.id.add_number_linear_layout).setOnClickListener(null);
-            mBlockedNumberListDivider.setVisibility(View.INVISIBLE);
-
-            mImportSettings.setSystemUiVisibility(View.GONE);
+            mBlockedNumberListDivider.setVisibility(View.GONE);
+            mImportSettings.setVisibility(View.GONE);
             getListView().findViewById(R.id.import_button).setOnClickListener(null);
             getListView().findViewById(R.id.view_numbers_button).setOnClickListener(null);
+            mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE);
+            footerText.setVisibility(View.GONE);
         } else {
             FilteredNumbersUtil.checkForSendToVoicemailContact(
                     getActivity(), new CheckForSendToVoicemailContactListener() {
@@ -143,7 +157,9 @@
                     });
         }
 
-        if (FilteredNumbersUtil.hasRecentEmergencyCall(getContext())) {
+        // All views except migrate and the block list are hidden when new filtering is available
+        if (!FilteredNumberCompat.canUseNewFiltering()
+                && FilteredNumbersUtil.hasRecentEmergencyCall(getContext())) {
             mBlockedNumbersDisabledForEmergency.setVisibility(View.VISIBLE);
         } else {
             mBlockedNumbersDisabledForEmergency.setVisibility(View.GONE);
@@ -190,7 +206,8 @@
 
     @Override
     public void onClick(final View view) {
-        BlockedNumbersSettingsActivity activity = (BlockedNumbersSettingsActivity) getActivity();
+        final BlockedNumbersSettingsActivity activity =
+                (BlockedNumbersSettingsActivity) getActivity();
         if (activity == null) {
             return;
         }
@@ -208,8 +225,22 @@
                             mImportSettings.setVisibility(View.GONE);
                         }
                     });
+        } else if (resId == R.id.migrate_promo_allow_button) {
+            view.setEnabled(false);
+            MoreObjects.firstNonNull(blockedNumbersMigratorForTest,
+                new BlockedNumbersMigrator(getContext().getContentResolver()))
+                .migrate(new Listener() {
+                    @Override
+                    public void onComplete() {
+                        getContext().startActivity(
+                            FilteredNumberCompat.createManageBlockedNumbersIntent(getContext()));
+                        // Remove this activity from the backstack
+                        activity.finish();
+                }
+            });
         }
     }
+
     @Override
     public void onVisualVoicemailEnabledStatusChanged(boolean newStatus){
         updateActiveVoicemailProvider();
@@ -219,12 +250,15 @@
         if (getActivity() == null || getActivity().isFinishing()) {
             return;
         }
-        TextView footerText = (TextView) getActivity().findViewById(
-            R.id.blocked_number_footer_textview);
         if (mVoicemailEnabledChecker.isVisualVoicemailEnabled()) {
             footerText.setText(R.string.block_number_footer_message_vvm);
         } else {
             footerText.setText(R.string.block_number_footer_message_no_vvm);
         }
     }
+
+    @NeededForTesting
+    void setBlockedNumbersMigratorForTest(BlockedNumbersMigrator blockedNumbersMigrator) {
+        blockedNumbersMigratorForTest = blockedNumbersMigrator;
+    }
 }
diff --git a/src/com/android/dialer/settings/DialerSettingsActivity.java b/src/com/android/dialer/settings/DialerSettingsActivity.java
index fc689d0..9db3e05 100644
--- a/src/com/android/dialer/settings/DialerSettingsActivity.java
+++ b/src/com/android/dialer/settings/DialerSettingsActivity.java
@@ -38,6 +38,7 @@
 
 public class DialerSettingsActivity extends AppCompatPreferenceActivity {
     protected SharedPreferences mPreferences;
+    private boolean migrationStatusOnBuildHeaders;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -46,6 +47,18 @@
     }
 
     @Override
+    protected void onResume() {
+        super.onResume();
+        /*
+         * The headers need to be recreated if the migration status changed between when the headers
+         * were created and now.
+         */
+        if (migrationStatusOnBuildHeaders != FilteredNumberCompat.hasMigratedToNewBlocking()) {
+            invalidateHeaders();
+        }
+    }
+
+    @Override
     public void onBuildHeaders(List<Header> target) {
         Header displayOptionsHeader = new Header();
         displayOptionsHeader.titleRes = R.string.display_options_title;
@@ -99,6 +112,7 @@
             blockedCallsHeader.titleRes = R.string.manage_blocked_numbers_label;
             blockedCallsHeader.intent = FilteredNumberCompat.createManageBlockedNumbersIntent(this);
             target.add(blockedCallsHeader);
+            migrationStatusOnBuildHeaders = FilteredNumberCompat.hasMigratedToNewBlocking();
         }
         if (isPrimaryUser
                 && (TelephonyManagerCompat.isTtyModeSupported(telephonyManager)
diff --git a/tests/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigratorTest.java b/tests/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigratorTest.java
new file mode 100644
index 0000000..5058555
--- /dev/null
+++ b/tests/src/com/android/dialer/filterednumber/BlockedNumbersAutoMigratorTest.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2016 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.dialer.filterednumber;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.doAnswer;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+
+import android.content.SharedPreferences;
+import android.preference.PreferenceManager;
+import android.test.AndroidTestCase;
+
+import com.android.contacts.common.compat.CompatUtils;
+import com.android.dialer.compat.FilteredNumberCompat;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler;
+import com.android.dialer.database.FilteredNumberAsyncQueryHandler.OnHasBlockedNumbersListener;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.mockito.invocation.InvocationOnMock;
+import org.mockito.stubbing.Answer;
+
+public class BlockedNumbersAutoMigratorTest extends AndroidTestCase {
+
+    private static final String HAS_CHECKED_AUTO_MIGRATE_KEY_FOR_TEST = "checkedAutoMigrateForTest";
+
+    @Mock
+    private FilteredNumberAsyncQueryHandler mockQueryHandler;
+
+    private SharedPreferences sharedPreferences;
+
+    private BlockedNumbersAutoMigrator blockedNumbersAutoMigrator;
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        MockitoAnnotations.initMocks(this);
+        FilteredNumberCompat.setContextForTest(getContext());
+        FilteredNumberCompat.setHasMigratedToNewBlocking(false);
+
+        sharedPreferences = PreferenceManager.getDefaultSharedPreferences(getContext());
+        // SharedPreference state isn't cleaned up between each test automatically, clear it now
+        sharedPreferences.edit().clear().apply();
+
+        blockedNumbersAutoMigrator = new BlockedNumbersAutoMigrator(sharedPreferences,
+                mockQueryHandler);
+    }
+
+    public void testConstructor_NullSharedPreferences() {
+        try {
+            new BlockedNumbersAutoMigrator(null, mockQueryHandler);
+            fail();
+        } catch (NullPointerException e) {
+        }
+    }
+
+    public void testConstructor_NullQueryHandler() {
+        try {
+            new BlockedNumbersAutoMigrator(sharedPreferences, null);
+            fail();
+        } catch (NullPointerException e) {
+        }
+    }
+
+    public void testAutoMigrate_M() {
+        if (CompatUtils.isNCompatible()) {
+            return;
+        }
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler, never()).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+    }
+
+    public void testAutoMigrate_AlreadyMigrated() {
+        if (!CompatUtils.isNCompatible()) {
+            return;
+        }
+        FilteredNumberCompat.setHasMigratedToNewBlocking(true);
+
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler, never()).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+    }
+
+    public void testAutoMigrate_AlreadyChecked() {
+        if (!CompatUtils.isNCompatible()) {
+            return;
+        }
+        sharedPreferences.edit()
+                .putBoolean(HAS_CHECKED_AUTO_MIGRATE_KEY_FOR_TEST, true)
+                .apply();
+
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler, never()).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+    }
+
+    public void testAutoMigrate_HasNumbers() {
+        if (!CompatUtils.isNCompatible()) {
+            return;
+        }
+        setupFilteredNumberHasBlockedNumbersExpectation(true);
+
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+        assertFalse(FilteredNumberCompat.hasMigratedToNewBlocking());
+    }
+
+    public void testAutoMigrate_HasNumbers_MultipleCalls() {
+        if (!CompatUtils.isNCompatible()) {
+            return;
+        }
+        setupFilteredNumberHasBlockedNumbersExpectation(true);
+
+        blockedNumbersAutoMigrator.autoMigrate();
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler, times(1))
+                .hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+        assertFalse(FilteredNumberCompat.hasMigratedToNewBlocking());
+    }
+
+    public void testAutoMigrate_NoNumbers() {
+        if (!CompatUtils.isNCompatible()) {
+            return;
+        }
+        setupFilteredNumberHasBlockedNumbersExpectation(false);
+
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+        assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
+    }
+
+    public void testAutoMigrate_NoNumbers_MultipleCalls() {
+        if (!CompatUtils.isNCompatible()) {
+            return;
+        }
+        setupFilteredNumberHasBlockedNumbersExpectation(false);
+
+        blockedNumbersAutoMigrator.autoMigrate();
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler, times(1))
+                .hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+        assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
+    }
+
+
+    public void testAutoMigrate_SimulateClearingAppData() {
+        if (!CompatUtils.isNCompatible()) {
+            return;
+        }
+        setupFilteredNumberHasBlockedNumbersExpectation(true);
+
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        // Clearing app data removes the sharedPreferences and all of the blocked numbers
+        sharedPreferences.edit().clear().apply();
+        setupFilteredNumberHasBlockedNumbersExpectation(false);
+
+        blockedNumbersAutoMigrator.autoMigrate();
+
+        verify(mockQueryHandler, times(2))
+                .hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+        assertTrue(FilteredNumberCompat.hasMigratedToNewBlocking());
+    }
+
+    /*
+     * Sets up the {@link #mockQueryHandler} to call the {@link OnHasBlockedNumbersListener} with
+     * the given hasBlockedNumbers value as the parameter, when
+     * {@link FilteredNumberAsyncQueryHandler#hasBlockedNumbers} is called.
+     */
+    private void setupFilteredNumberHasBlockedNumbersExpectation(final boolean hasBlockedNumbers) {
+        doAnswer(new Answer<Void>() {
+            @Override
+            public Void answer(InvocationOnMock invocation) throws Throwable {
+                ((OnHasBlockedNumbersListener) invocation.getArguments()[0])
+                        .onHasBlockedNumbers(hasBlockedNumbers);
+                return null;
+            }
+        }).when(mockQueryHandler).hasBlockedNumbers(any(OnHasBlockedNumbersListener.class));
+    }
+}
diff --git a/tests/src/com/android/dialer/filterednumber/BlockedNumbersFragmentInstrumentationTest.java b/tests/src/com/android/dialer/filterednumber/BlockedNumbersFragmentInstrumentationTest.java
new file mode 100644
index 0000000..ea4c51e
--- /dev/null
+++ b/tests/src/com/android/dialer/filterednumber/BlockedNumbersFragmentInstrumentationTest.java
@@ -0,0 +1,93 @@
+/*
+ * Copyright (C) 2016 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.dialer.filterednumber;
+
+import static org.mockito.Matchers.any;
+import static org.mockito.Mockito.verify;
+
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+
+import com.android.contacts.common.compat.CompatUtils;
+import com.android.dialer.R;
+import com.android.dialer.compat.FilteredNumberCompat;
+import com.android.dialer.filterednumber.BlockedNumbersMigrator.Listener;
+
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+/**
+ * Instrumentation tests for {@link BlockedNumbersFragment}. Note for these tests to work properly,
+ * the device's screen must be on.
+ */
+public class BlockedNumbersFragmentInstrumentationTest extends
+    ActivityInstrumentationTestCase2<BlockedNumbersSettingsActivity> {
+
+  private static final String FRAGMENT_TAG = "blocked_management";
+
+  private BlockedNumbersFragment blockedNumbersFragment;
+  @Mock private BlockedNumbersMigrator blockedNumbersMigrator;
+
+  public BlockedNumbersFragmentInstrumentationTest() {
+    super(BlockedNumbersSettingsActivity.class);
+  }
+
+  @Override
+  public void setUp() throws Exception {
+    super.setUp();
+    MockitoAnnotations.initMocks(this);
+    FilteredNumberCompat.setIsEnabledForTest(true);
+    blockedNumbersFragment = new BlockedNumbersFragment();
+    blockedNumbersFragment.setBlockedNumbersMigratorForTest(blockedNumbersMigrator);
+    getActivity().getFragmentManager().beginTransaction()
+        .replace(R.id.blocked_numbers_activity_container, blockedNumbersFragment, FRAGMENT_TAG)
+        .commit();
+    getInstrumentation().waitForIdleSync();
+  }
+
+  public void testMigrationPromo_NotShown_M() {
+    if (CompatUtils.isNCompatible()) {
+      return;
+    }
+    assertEquals(View.GONE, blockedNumbersFragment.migratePromoView.getVisibility());
+  }
+
+  public void testMigrationPromo_Shown_N() {
+    if (!CompatUtils.isNCompatible()) {
+      return;
+    }
+    assertEquals(View.VISIBLE, blockedNumbersFragment.migratePromoView.getVisibility());
+  }
+
+  public void testOnClick_Migrate() {
+    if (!CompatUtils.isNCompatible()) {
+      return;
+    }
+
+    getInstrumentation().runOnMainSync(new Runnable() {
+      @Override
+      public void run() {
+        blockedNumbersFragment.getListView().findViewById(R.id.migrate_promo_allow_button)
+            .performClick();
+      }
+    });
+    getInstrumentation().waitForIdleSync();
+    assertFalse(blockedNumbersFragment.getListView().findViewById(R.id.migrate_promo_allow_button)
+        .isEnabled());
+    verify(blockedNumbersMigrator).migrate(any(Listener.class));
+  }
+}