Add toasts to blocked numbers.

- Add toasts on blocked/unblocked/911/duplicate.

- BlockedNumberContract.isBlocked() is a look up by number
 which is indexed so should be fast, however, to be safe
 we run in an async task. So I moved all work for adding
 blocked number to the asynctask.

Bug: 27495710
Change-Id: Ia56de55f3161c5d413a55adb37668b720f634ef3
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 22e0adf..a1d0766 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -143,7 +143,14 @@
     <string name="blocked_numbers_butter_bar_body">After you dial or text an emergency number, blocking is turned off to ensure that emergency services can contact you.</string>
     <!-- Button to re-enable blocking shown in butter bar shown when call blocking is disabled. -->
     <string name="blocked_numbers_butter_bar_button">Re-enable now</string>
-
+    <!-- Message to show when a number is blocked. -->
+    <string name="blocked_numbers_number_blocked_message"><xliff:g id="blocked_number">%1$s</xliff:g> blocked</string>
+    <!-- Message to show when a number is unblocked. -->
+    <string name="blocked_numbers_number_unblocked_message"><xliff:g id="unblocked_number">%1$s</xliff:g> unblocked</string>
+    <!-- Message to show when a number cannot be blocked because it is associated with emergency services.. -->
+    <string name="blocked_numbers_block_emergency_number_message">Unable to block emergency number.</string>
+    <!-- Message to show when a number is already blocked. -->
+    <string name="blocked_numbers_number_already_blocked_message"><xliff:g id="blocked_number">%1$s</xliff:g> is already blocked.</string>
 
     <!-- DO NOT TRANSLATE. Label for test Subscription 0. -->
     <string name="test_account_0_label">Q Mobile</string>
diff --git a/src/com/android/server/telecom/settings/BlockNumberTaskFragment.java b/src/com/android/server/telecom/settings/BlockNumberTaskFragment.java
new file mode 100644
index 0000000..d96b3e1
--- /dev/null
+++ b/src/com/android/server/telecom/settings/BlockNumberTaskFragment.java
@@ -0,0 +1,102 @@
+/*
+ * 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.server.telecom.settings;
+
+import android.annotation.Nullable;
+import android.app.Fragment;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.provider.BlockedNumberContract;
+import com.android.server.telecom.R;
+
+/**
+ * Retained fragment that runs an async task to add a blocked number.
+ *
+ * <p>We run the task inside a retained fragment so that if the screen orientation changed, the
+ * task does not get lost.
+ */
+public class BlockNumberTaskFragment extends Fragment {
+    @Nullable private BlockNumberTask mTask;
+    @Nullable Listener mListener;
+
+    /**
+     * Task to block a number.
+     */
+    private class BlockNumberTask extends AsyncTask<String, Void, Boolean> {
+        private String mNumber;
+
+        /**
+         * @return true if number was blocked; false if number is already blocked.
+         */
+        @Override
+        protected Boolean doInBackground(String... params) {
+            mNumber = params[0];
+            if (BlockedNumberContract.isBlocked(getContext(), mNumber)) {
+                return false;
+            } else {
+                ContentResolver contentResolver = getContext().getContentResolver();
+                ContentValues newValues = new ContentValues();
+                newValues.put(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER,
+                        mNumber);
+                contentResolver.insert(BlockedNumberContract.BlockedNumbers.CONTENT_URI,
+                        newValues);
+                return true;
+            }
+        }
+
+        @Override
+        protected void onPostExecute(Boolean result) {
+            mTask = null;
+            if (mListener != null) {
+                mListener.onBlocked(mNumber, !result /* alreadyBlocked */);
+            }
+            mListener = null;
+        }
+    }
+
+    public interface Listener {
+        void onBlocked(String number, boolean alreadyBlocked);
+    }
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setRetainInstance(true);
+    }
+
+    @Override
+    public void onDestroy() {
+        if (mTask != null) {
+            mTask.cancel(true /* mayInterruptIfRunning */);
+        }
+        super.onDestroy();
+    }
+
+    /**
+     * Runs an async task to write the number to the blocked numbers provider if it does not already
+     * exist.
+     *
+     * Triggers {@link Listener#onBlocked(String, boolean)} when task finishes to show proper UI.
+     */
+    public void blockIfNotAlreadyBlocked(String number, Listener listener) {
+        mListener = listener;
+        mTask = new BlockNumberTask();
+        mTask.execute(number);
+    }
+}
\ No newline at end of file
diff --git a/src/com/android/server/telecom/settings/BlockedNumbersActivity.java b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
index 46db581..dbf3cb8 100644
--- a/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
+++ b/src/com/android/server/telecom/settings/BlockedNumbersActivity.java
@@ -19,12 +19,18 @@
 import android.annotation.Nullable;
 import android.app.ActionBar;
 import android.app.AlertDialog;
+import android.app.FragmentManager;
 import android.app.ListActivity;
 import android.app.LoaderManager;
-import android.content.*;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.CursorLoader;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.Loader;
 import android.database.Cursor;
 import android.os.Bundle;
-import android.os.UserManager;
 import android.provider.BlockedNumberContract;
 import android.telephony.PhoneNumberFormattingTextWatcher;
 import android.telephony.PhoneNumberUtils;
@@ -35,16 +41,25 @@
 import android.view.MenuItem;
 import android.view.View;
 import android.view.inputmethod.InputMethodManager;
-import android.widget.*;
+import android.widget.Button;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import android.widget.ProgressBar;
+import android.widget.RelativeLayout;
+import android.widget.TextView;
+import android.widget.Toast;
 import com.android.server.telecom.R;
 
 /**
  * Activity to manage blocked numbers using {@link BlockedNumberContract}.
  */
 public class BlockedNumbersActivity extends ListActivity
-        implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener, TextWatcher {
+        implements LoaderManager.LoaderCallbacks<Cursor>, View.OnClickListener, TextWatcher,
+        BlockNumberTaskFragment.Listener {
     private static final String ACTION_MANAGE_BLOCKED_NUMBERS =
             "android.telecom.action.MANAGE_BLOCKED_NUMBERS";
+    private static final String TAG_BLOCK_NUMBER_TASK_FRAGMENT = "block_number_task_fragment";
     private static final String TELECOM_PACKAGE = "com.android.server.telecom";
     private static final String[] PROJECTION = new String[] {
             BlockedNumberContract.BlockedNumbers.COLUMN_ID,
@@ -55,6 +70,7 @@
             BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " NOTNULL) AND (" +
             BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + " != '' ))";
 
+    private BlockNumberTaskFragment mBlockNumberTaskFragment;
     private BlockedNumbersAdapter mAdapter;
     private TextView mAddButton;
     private ProgressBar mProgressBar;
@@ -95,6 +111,17 @@
             manageBlockedNumbersUi.setVisibility(View.GONE);
             return;
         }
+
+        FragmentManager fm = getFragmentManager();
+        mBlockNumberTaskFragment =
+                (BlockNumberTaskFragment) fm.findFragmentByTag(TAG_BLOCK_NUMBER_TASK_FRAGMENT);
+
+        if (mBlockNumberTaskFragment == null) {
+            mBlockNumberTaskFragment = new BlockNumberTaskFragment();
+            fm.beginTransaction()
+                    .add(mBlockNumberTaskFragment, TAG_BLOCK_NUMBER_TASK_FRAGMENT).commit();
+        }
+
         mAddButton = (TextView) findViewById(R.id.add_blocked);
         mAddButton.setOnClickListener(this);
 
@@ -224,17 +251,16 @@
      * Add blocked number if it does not exist.
      */
     private void addBlockedNumber(String number) {
-        ContentResolver contentResolver = getContentResolver();
-        Cursor cursor = contentResolver.query(
-                BlockedNumberContract.BlockedNumbers.CONTENT_URI,
-                PROJECTION,
-                BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?",
-                new String[] {number},
-                null);
-        if (cursor == null || cursor.getCount() == 0) {
-            ContentValues newValues = new ContentValues();
-            newValues.put(BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER, number);
-            contentResolver.insert(BlockedNumberContract.BlockedNumbers.CONTENT_URI, newValues);
+        if (PhoneNumberUtils.isEmergencyNumber(number)) {
+            Toast.makeText(
+                    this,
+                    getString(R.string.blocked_numbers_block_emergency_number_message),
+                    Toast.LENGTH_SHORT).show();
+        } else {
+            // We disable the add button, to prevent the user from adding other numbers until the
+            // current number is added.
+            mAddButton.setEnabled(false);
+            mBlockNumberTaskFragment.blockIfNotAlreadyBlocked(number, this);
         }
     }
 
@@ -254,4 +280,16 @@
     public void afterTextChanged(Editable s) {
         // no-op
     }
+
+    @Override
+    public void onBlocked(String number, boolean alreadyBlocked) {
+        if (alreadyBlocked) {
+            BlockedNumbersUtil.showToastWithFormattedNumber(this,
+                    R.string.blocked_numbers_number_already_blocked_message, number);
+        } else {
+            BlockedNumbersUtil.showToastWithFormattedNumber(this,
+                    R.string.blocked_numbers_number_blocked_message, number);
+        }
+        mAddButton.setEnabled(true);
+    }
 }
diff --git a/src/com/android/server/telecom/settings/BlockedNumbersAdapter.java b/src/com/android/server/telecom/settings/BlockedNumbersAdapter.java
index 5da230b..705e031 100644
--- a/src/com/android/server/telecom/settings/BlockedNumbersAdapter.java
+++ b/src/com/android/server/telecom/settings/BlockedNumbersAdapter.java
@@ -30,8 +30,6 @@
 import android.widget.TextView;
 import com.android.server.telecom.R;
 
-import java.util.Locale;
-
 public class BlockedNumbersAdapter extends SimpleCursorAdapter {
     public BlockedNumbersAdapter(Context context, int layout, Cursor c, String[] from, int[] to,
             int flags) {
@@ -44,7 +42,7 @@
         final String rawNumber = cursor.getString(cursor.getColumnIndex(
                 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER));
         String formattedNumber = PhoneNumberUtils.formatNumber(rawNumber,
-                getLocaleDefaultToUS());
+                BlockedNumbersUtil.getLocaleDefaultToUS());
         final String finalFormattedNumber = formattedNumber == null ? rawNumber : formattedNumber;
 
         TextView numberView = (TextView) view.findViewById(R.id.blocked_number);
@@ -61,14 +59,6 @@
         });
     }
 
-    private String getLocaleDefaultToUS() {
-        String countryIso = Locale.getDefault().getCountry();
-        if (countryIso == null || countryIso.length() != 2) {
-            countryIso = "US";
-        }
-        return countryIso;
-    }
-
     private void showDeleteBlockedNumberDialog(final Context context, final String rawNumber,
             final String formattedNumber) {
         String message = context.getString(R.string.unblock_dialog_body, formattedNumber);
@@ -101,5 +91,7 @@
         contentResolver.delete(BlockedNumberContract.BlockedNumbers.CONTENT_URI,
                 BlockedNumberContract.BlockedNumbers.COLUMN_ORIGINAL_NUMBER + "=?",
                 new String[] {number});
+        BlockedNumbersUtil.showToastWithFormattedNumber(mContext,
+                R.string.blocked_numbers_number_unblocked_message, number);
     }
 }
diff --git a/src/com/android/server/telecom/settings/BlockedNumbersUtil.java b/src/com/android/server/telecom/settings/BlockedNumbersUtil.java
new file mode 100644
index 0000000..5d2e238
--- /dev/null
+++ b/src/com/android/server/telecom/settings/BlockedNumbersUtil.java
@@ -0,0 +1,60 @@
+/*
+ * 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.server.telecom.settings;
+
+import android.content.Context;
+import android.telephony.PhoneNumberUtils;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.widget.Toast;
+import com.android.server.telecom.R;
+
+import java.util.Locale;
+
+public final class BlockedNumbersUtil {
+    private BlockedNumbersUtil() {}
+
+    /**
+     * @return locale and default to US if no locale was returned.
+     */
+    public static String getLocaleDefaultToUS() {
+        String countryIso = Locale.getDefault().getCountry();
+        if (countryIso == null || countryIso.length() != 2) {
+            countryIso = "US";
+        }
+        return countryIso;
+    }
+
+    /**
+     * Formats the number in the string and shows a toast for {@link Toast#LENGTH_SHORT}.
+     *
+     * <p>Adds the number in a TsSpan so that it reads as a phone number when talk back is on.
+     */
+    public static void showToastWithFormattedNumber(Context context, int stringId, String number) {
+        String formattedNumber = PhoneNumberUtils.formatNumber(number, getLocaleDefaultToUS());
+        String finalFormattedNumber = formattedNumber == null ? number : formattedNumber;
+        String message = context.getString(stringId, finalFormattedNumber);
+        int startingPosition = message.indexOf(finalFormattedNumber);
+        Spannable messageSpannable = new SpannableString(message);
+        PhoneNumberUtils.addTtsSpan(messageSpannable, startingPosition,
+                startingPosition + finalFormattedNumber.length());
+        Toast.makeText(
+                context,
+                messageSpannable,
+                Toast.LENGTH_SHORT).show();
+    }
+}