Add reject via text capability to calls

* Add REJECT_VIA_TEXT capability to calls
* Move allowRespondViaSmsForCall to RejectWithTextManager

Bug: 10424370

Change-Id: I059550d82e3b44cc4cc42d5eb06e40551497f063
diff --git a/src/com/android/phone/CallCommandService.java b/src/com/android/phone/CallCommandService.java
index 1b13280..72273ee 100644
--- a/src/com/android/phone/CallCommandService.java
+++ b/src/com/android/phone/CallCommandService.java
@@ -79,17 +79,16 @@
         try {
             CallResult result = mCallModeler.getCallWithId(callId);
             if (result != null) {
-                if (rejectWithMessage) {
-                    if (message != null) {
-                        mRejectWithTextMessageManager.rejectCallWithMessage(
-                                result.getConnection().getCall(), message);
-                    } else {
-                        mRejectWithTextMessageManager.rejectCallWithNewMessage(
-                                result.getConnection().getCall());
-                    }
-                }
+                final String number = result.getConnection().getAddress();
                 Log.v(TAG, "Hanging up");
                 PhoneUtils.hangupRingingCall(result.getConnection().getCall());
+                if (rejectWithMessage) {
+                    if (message != null) {
+                        mRejectWithTextMessageManager.rejectCallWithMessage(number, message);
+                    } else {
+                        mRejectWithTextMessageManager.rejectCallWithNewMessage(number);
+                    }
+                }
             }
         } catch (Exception e) {
             Log.e(TAG, "Error during rejectCall().", e);
diff --git a/src/com/android/phone/CallModeler.java b/src/com/android/phone/CallModeler.java
index 393e054..9c7b9dd 100644
--- a/src/com/android/phone/CallModeler.java
+++ b/src/com/android/phone/CallModeler.java
@@ -16,13 +16,7 @@
 
 package com.android.phone;
 
-import com.google.android.collect.Lists;
-import com.google.android.collect.Maps;
-import com.google.android.collect.Sets;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.ImmutableMap;
-import com.google.common.collect.ImmutableSortedSet;
-
+import android.content.Context;
 import android.os.AsyncResult;
 import android.os.Handler;
 import android.os.Message;
@@ -40,11 +34,16 @@
 import com.android.services.telephony.common.Call.Capabilities;
 import com.android.services.telephony.common.Call.State;
 
+import com.google.android.collect.Lists;
+import com.google.android.collect.Maps;
+import com.google.common.base.Preconditions;
+import com.google.common.collect.ImmutableMap;
+import com.google.common.collect.ImmutableSortedSet;
+
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map.Entry;
-import java.util.SortedSet;
 import java.util.concurrent.atomic.AtomicInteger;
 
 /**
@@ -469,6 +468,7 @@
         boolean canAddCall = false;
         boolean canMergeCall = false;
         boolean canSwapCall = false;
+        boolean canRespondViaText = false;
 
         // only applies to active calls
         if (callIsActive) {
@@ -477,6 +477,9 @@
             canSwapCall = PhoneUtils.okToSwapCalls(mCallManager);
         }
 
+        canRespondViaText = RejectWithTextMessageManager.allowRespondViaSmsForCall(call,
+                connection);
+
         // special rules section!
         // CDMA always has Add
         if (phone.getPhoneType() == PhoneConstants.PHONE_TYPE_CDMA) {
@@ -500,6 +503,10 @@
             retval |= Capabilities.SWAP_CALLS;
         }
 
+        if (canRespondViaText) {
+            retval |= Capabilities.RESPOND_VIA_TEXT;
+        }
+
         return retval;
     }
 
diff --git a/src/com/android/phone/InCallScreen.java b/src/com/android/phone/InCallScreen.java
index 57006bb..8b67b77 100644
--- a/src/com/android/phone/InCallScreen.java
+++ b/src/com/android/phone/InCallScreen.java
@@ -3371,8 +3371,6 @@
         // it to always act on the first ringing call.
         Call ringingCall = mCM.getFirstActiveRingingCall();
 
-        mRespondViaSmsManager.showRespondViaSmsPopup(ringingCall);
-
         // Silence the ringer, since it would be distracting while you're trying
         // to pick a response.  (Note that we'll restart the ringer if you bail
         // out of the popup, though; see RespondViaSmsCancelListener.)
diff --git a/src/com/android/phone/InCallTouchUi.java b/src/com/android/phone/InCallTouchUi.java
index 59f2f32..1d08671 100644
--- a/src/com/android/phone/InCallTouchUi.java
+++ b/src/com/android/phone/InCallTouchUi.java
@@ -1189,48 +1189,6 @@
         }
         mIncomingCallWidget.setAlpha(1.0f);
 
-        // Update the GlowPadView widget's targets based on the state of
-        // the ringing call.  (Specifically, we need to disable the
-        // "respond via SMS" option for certain types of calls, like SIP
-        // addresses or numbers with blocked caller-id.)
-        final boolean allowRespondViaSms =
-                RespondViaSmsManager.allowRespondViaSmsForCall(mInCallScreen, ringingCall);
-        final int targetResourceId = allowRespondViaSms
-                ? R.array.incoming_call_widget_3way_targets
-                : R.array.incoming_call_widget_2way_targets;
-        // The widget should be updated only when appropriate; if the previous choice can be reused
-        // for this incoming call, we'll just keep using it. Otherwise we'll see UI glitch
-        // everytime when this method is called during a single incoming call.
-        if (targetResourceId != mIncomingCallWidget.getTargetResourceId()) {
-            if (allowRespondViaSms) {
-                // The GlowPadView widget is allowed to have all 3 choices:
-                // Answer, Decline, and Respond via SMS.
-                mIncomingCallWidget.setTargetResources(targetResourceId);
-                mIncomingCallWidget.setTargetDescriptionsResourceId(
-                        R.array.incoming_call_widget_3way_target_descriptions);
-                mIncomingCallWidget.setDirectionDescriptionsResourceId(
-                        R.array.incoming_call_widget_3way_direction_descriptions);
-            } else {
-                // You only get two choices: Answer or Decline.
-                mIncomingCallWidget.setTargetResources(targetResourceId);
-                mIncomingCallWidget.setTargetDescriptionsResourceId(
-                        R.array.incoming_call_widget_2way_target_descriptions);
-                mIncomingCallWidget.setDirectionDescriptionsResourceId(
-                        R.array.incoming_call_widget_2way_direction_descriptions);
-            }
-
-            // This will be used right after this block.
-            mIncomingCallWidgetShouldBeReset = true;
-        }
-        if (mIncomingCallWidgetShouldBeReset) {
-            // Watch out: be sure to call reset() and setVisibility() *after*
-            // updating the target resources, since otherwise the GlowPadView
-            // widget will make the targets visible initially (even before you
-            // touch the widget.)
-            mIncomingCallWidget.reset(false);
-            mIncomingCallWidgetShouldBeReset = false;
-        }
-
         // On an incoming call, if the layout is landscape, then align the "incoming call" text
         // to the left, because the incomingCallWidget (black background with glowing ring)
         // is aligned to the right and would cover the "incoming call" text.
diff --git a/src/com/android/phone/RejectWithTextMessageManager.java b/src/com/android/phone/RejectWithTextMessageManager.java
index 8324bd4..b03211a 100644
--- a/src/com/android/phone/RejectWithTextMessageManager.java
+++ b/src/com/android/phone/RejectWithTextMessageManager.java
@@ -32,7 +32,9 @@
 import android.graphics.drawable.Drawable;
 import android.net.Uri;
 import android.os.Handler;
+import android.telephony.PhoneNumberUtils;
 import android.telephony.TelephonyManager;
+import android.text.TextUtils;
 import android.util.Log;
 import android.view.LayoutInflater;
 import android.view.View;
@@ -46,6 +48,7 @@
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.PhoneConstants;
+
 import com.google.android.collect.Lists;
 
 import java.util.ArrayList;
@@ -128,29 +131,6 @@
     }
 
     /**
-     * @return true if the "Respond via SMS" feature should be enabled
-     * for the specified incoming call.
-     *
-     * The general rule is that we *do* allow "Respond via SMS" except for
-     * the few (relatively rare) cases where we know for sure it won't
-     * work, namely:
-     *   - a bogus or blank incoming number
-     *   - a call from a SIP address
-     *   - a "call presentation" that doesn't allow the number to be revealed
-     *
-     * In all other cases, we allow the user to respond via SMS.
-     *
-     * Note that this behavior isn't perfect; for example we have no way
-     * to detect whether the incoming call is from a landline (with most
-     * networks at least), so we still enable this feature even though
-     * SMSes to that number will silently fail.
-     */
-    public static boolean allowRespondViaSmsForCall(Context context, Call ringingCall) {
-        // TODO(klp) implement this!
-        return true;
-    }
-
-    /**
      * Sends a text message without any interaction from the user.
      */
     private void sendText(String phoneNumber, String message, ComponentName component) {
@@ -199,7 +179,7 @@
      * Queries the System to determine what packages contain services that can handle the instant
      * text response Action AND have permissions to do so.
      */
-    private ArrayList<ComponentName> getPackagesWithInstantTextPermission() {
+    private static ArrayList<ComponentName> getPackagesWithInstantTextPermission() {
         final PackageManager packageManager = PhoneGlobals.getInstance().getPackageManager();
 
         final ArrayList<ComponentName> componentsWithPermission = new ArrayList<ComponentName>();
@@ -251,8 +231,8 @@
         return intent;
     }
 
-    public void rejectCallWithNewMessage(Call call) {
-        launchSmsCompose(call.getLatestConnection().getAddress());
+    public void rejectCallWithNewMessage(String number) {
+        launchSmsCompose(number);
     }
 
     private ComponentName getSmsService() {
@@ -302,15 +282,105 @@
     }
 
 
-    public void rejectCallWithMessage(Call call, String message) {
+    public void rejectCallWithMessage(final String number, String message) {
         final ComponentName componentName = getSmsService();
 
         if (componentName != null) {
-            sendTextAndExit(call.getLatestConnection().getAddress(), message, componentName,
+            sendTextAndExit(number, message, componentName,
                     false);
         }
     }
 
+    /**
+     * @return true if the "Respond via SMS" feature should be enabled
+     * for the specified incoming call.
+     *
+     * The general rule is that we *do* allow "Respond via SMS" except for
+     * the few (relatively rare) cases where we know for sure it won't
+     * work, namely:
+     *   - a bogus or blank incoming number
+     *   - a call from a SIP address
+     *   - a "call presentation" that doesn't allow the number to be revealed
+     *
+     * In all other cases, we allow the user to respond via SMS.
+     *
+     * Note that this behavior isn't perfect; for example we have no way
+     * to detect whether the incoming call is from a landline (with most
+     * networks at least), so we still enable this feature even though
+     * SMSes to that number will silently fail.
+     */
+    public static boolean allowRespondViaSmsForCall(
+            com.android.services.telephony.common.Call call, Connection conn) {
+        if (DBG) log("allowRespondViaSmsForCall(" + call + ")...");
+
+        // First some basic sanity checks:
+        if (call == null) {
+            Log.w(TAG, "allowRespondViaSmsForCall: null ringingCall!");
+            return false;
+        }
+        if (!(call.getState() == com.android.services.telephony.common.Call.State.INCOMING) &&
+                !(call.getState() ==
+                        com.android.services.telephony.common.Call.State.CALL_WAITING)) {
+            // The call is in some state other than INCOMING or WAITING!
+            // (This should almost never happen, but it *could*
+            // conceivably happen if the ringing call got disconnected by
+            // the network just *after* we got it from the CallManager.)
+            Log.w(TAG, "allowRespondViaSmsForCall: ringingCall not ringing! state = "
+                    + call.getState());
+            return false;
+        }
+
+        if (conn == null) {
+            // The call doesn't have any connections! (Again, this can
+            // happen if the ringing call disconnects at the exact right
+            // moment, but should almost never happen in practice.)
+            Log.w(TAG, "allowRespondViaSmsForCall: null Connection!");
+            return false;
+        }
+
+        // Check the incoming number:
+        final String number = conn.getAddress();
+        if (DBG) log("- number: '" + number + "'");
+        if (TextUtils.isEmpty(number)) {
+            Log.w(TAG, "allowRespondViaSmsForCall: no incoming number!");
+            return false;
+        }
+        if (PhoneNumberUtils.isUriNumber(number)) {
+            // The incoming number is actually a URI (i.e. a SIP address),
+            // not a regular PSTN phone number, and we can't send SMSes to
+            // SIP addresses.
+            // (TODO: That might still be possible eventually, though. Is
+            // there some SIP-specific equivalent to sending a text message?)
+            Log.i(TAG, "allowRespondViaSmsForCall: incoming 'number' is a SIP address.");
+            return false;
+        }
+
+        // Finally, check the "call presentation":
+        int presentation = conn.getNumberPresentation();
+        if (DBG) log("- presentation: " + presentation);
+        if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
+            // PRESENTATION_RESTRICTED means "caller-id blocked".
+            // The user isn't allowed to see the number in the first
+            // place, so obviously we can't let you send an SMS to it.
+            Log.i(TAG, "allowRespondViaSmsForCall: PRESENTATION_RESTRICTED.");
+            return false;
+        }
+
+        // Allow the feature only when there's a destination for it.
+        if (getPackagesWithInstantTextPermission().size() < 1) {
+            return false;
+        }
+
+        // TODO: with some carriers (in certain countries) you *can* actually
+        // tell whether a given number is a mobile phone or not. So in that
+        // case we could potentially return false here if the incoming call is
+        // from a land line.
+
+        // If none of the above special cases apply, it's OK to enable the
+        // "Respond via SMS" feature.
+        return true;
+    }
+
     private static void log(String msg) {
         Log.d(TAG, msg);
     }
diff --git a/src/com/android/phone/RespondViaSmsManager.java b/src/com/android/phone/RespondViaSmsManager.java
index c851471..ffce899 100644
--- a/src/com/android/phone/RespondViaSmsManager.java
+++ b/src/com/android/phone/RespondViaSmsManager.java
@@ -61,6 +61,7 @@
 import com.android.internal.telephony.Call;
 import com.android.internal.telephony.Connection;
 import com.android.internal.telephony.PhoneConstants;
+
 import com.google.android.collect.Lists;
 
 import java.util.ArrayList;
@@ -769,94 +770,6 @@
         return responses;
     }
 
-    /**
-     * @return true if the "Respond via SMS" feature should be enabled
-     * for the specified incoming call.
-     *
-     * The general rule is that we *do* allow "Respond via SMS" except for
-     * the few (relatively rare) cases where we know for sure it won't
-     * work, namely:
-     *   - a bogus or blank incoming number
-     *   - a call from a SIP address
-     *   - a "call presentation" that doesn't allow the number to be revealed
-     *
-     * In all other cases, we allow the user to respond via SMS.
-     *
-     * Note that this behavior isn't perfect; for example we have no way
-     * to detect whether the incoming call is from a landline (with most
-     * networks at least), so we still enable this feature even though
-     * SMSes to that number will silently fail.
-     */
-    public static boolean allowRespondViaSmsForCall(Context context, Call ringingCall) {
-        if (DBG) log("allowRespondViaSmsForCall(" + ringingCall + ")...");
-
-        // First some basic sanity checks:
-        if (ringingCall == null) {
-            Log.w(TAG, "allowRespondViaSmsForCall: null ringingCall!");
-            return false;
-        }
-        if (!ringingCall.isRinging()) {
-            // The call is in some state other than INCOMING or WAITING!
-            // (This should almost never happen, but it *could*
-            // conceivably happen if the ringing call got disconnected by
-            // the network just *after* we got it from the CallManager.)
-            Log.w(TAG, "allowRespondViaSmsForCall: ringingCall not ringing! state = "
-                  + ringingCall.getState());
-            return false;
-        }
-        Connection conn = ringingCall.getLatestConnection();
-        if (conn == null) {
-            // The call doesn't have any connections!  (Again, this can
-            // happen if the ringing call disconnects at the exact right
-            // moment, but should almost never happen in practice.)
-            Log.w(TAG, "allowRespondViaSmsForCall: null Connection!");
-            return false;
-        }
-
-        // Check the incoming number:
-        final String number = conn.getAddress();
-        if (DBG) log("- number: '" + number + "'");
-        if (TextUtils.isEmpty(number)) {
-            Log.w(TAG, "allowRespondViaSmsForCall: no incoming number!");
-            return false;
-        }
-        if (PhoneNumberUtils.isUriNumber(number)) {
-            // The incoming number is actually a URI (i.e. a SIP address),
-            // not a regular PSTN phone number, and we can't send SMSes to
-            // SIP addresses.
-            // (TODO: That might still be possible eventually, though.  Is
-            // there some SIP-specific equivalent to sending a text message?)
-            Log.i(TAG, "allowRespondViaSmsForCall: incoming 'number' is a SIP address.");
-            return false;
-        }
-
-        // Finally, check the "call presentation":
-        int presentation = conn.getNumberPresentation();
-        if (DBG) log("- presentation: " + presentation);
-        if (presentation == PhoneConstants.PRESENTATION_RESTRICTED) {
-            // PRESENTATION_RESTRICTED means "caller-id blocked".
-            // The user isn't allowed to see the number in the first
-            // place, so obviously we can't let you send an SMS to it.
-            Log.i(TAG, "allowRespondViaSmsForCall: PRESENTATION_RESTRICTED.");
-            return false;
-        }
-
-        // Allow the feature only when there's a destination for it.
-        if (context.getPackageManager().resolveService(getInstantTextIntent(number, null, null) , 0)
-                == null) {
-            return false;
-        }
-
-        // TODO: with some carriers (in certain countries) you *can* actually
-        // tell whether a given number is a mobile phone or not.  So in that
-        // case we could potentially return false here if the incoming call is
-        // from a land line.
-
-        // If none of the above special cases apply, it's OK to enable the
-        // "Respond via SMS" feature.
-        return true;
-    }
-
     private int getIconSize() {
       if (mIconSize < 0) {
           final ActivityManager am =