Merge "Fix crash when voice dialing an emergency number" into klp-dev
diff --git a/res/drawable-hdpi/list_pressed_holo_dark.9.png b/res/drawable-hdpi/list_pressed_holo_dark.9.png
index 5654cd6..596accb 100644
--- a/res/drawable-hdpi/list_pressed_holo_dark.9.png
+++ b/res/drawable-hdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/res/drawable-mdpi/list_pressed_holo_dark.9.png b/res/drawable-mdpi/list_pressed_holo_dark.9.png
index 6e77525..fd0e8d7 100644
--- a/res/drawable-mdpi/list_pressed_holo_dark.9.png
+++ b/res/drawable-mdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/res/drawable-xhdpi/list_pressed_holo_dark.9.png b/res/drawable-xhdpi/list_pressed_holo_dark.9.png
index e4b3393..29037a0 100644
--- a/res/drawable-xhdpi/list_pressed_holo_dark.9.png
+++ b/res/drawable-xhdpi/list_pressed_holo_dark.9.png
Binary files differ
diff --git a/src/com/android/phone/CallCommandService.java b/src/com/android/phone/CallCommandService.java
index 212ce45..f26fff7 100644
--- a/src/com/android/phone/CallCommandService.java
+++ b/src/com/android/phone/CallCommandService.java
@@ -44,17 +44,14 @@
private final CallModeler mCallModeler;
private final DTMFTonePlayer mDtmfTonePlayer;
private final AudioRouter mAudioRouter;
- private final RejectWithTextMessageManager mRejectWithTextMessageManager;
public CallCommandService(Context context, CallManager callManager, CallModeler callModeler,
- DTMFTonePlayer dtmfTonePlayer, AudioRouter audioRouter,
- RejectWithTextMessageManager rejectWithTextMessageManager) {
+ DTMFTonePlayer dtmfTonePlayer, AudioRouter audioRouter) {
mContext = context;
mCallManager = callManager;
mCallModeler = callModeler;
mDtmfTonePlayer = dtmfTonePlayer;
mAudioRouter = audioRouter;
- mRejectWithTextMessageManager = rejectWithTextMessageManager;
}
/**
@@ -82,13 +79,13 @@
if (result != null) {
final String number = result.getConnection().getAddress();
- Log.v(TAG, "Hanging up");
- PhoneUtils.hangupRingingCall(result.getConnection().getCall());
-
if (rejectWithMessage) {
- mRejectWithTextMessageManager.rejectCallWithMessage(
+ RejectWithTextMessageManager.rejectCallWithMessage(
result.getConnection().getCall(), message);
}
+
+ Log.v(TAG, "Hanging up");
+ PhoneUtils.hangupRingingCall(result.getConnection().getCall());
}
} 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 23f442a..51d3a37 100644
--- a/src/com/android/phone/CallModeler.java
+++ b/src/com/android/phone/CallModeler.java
@@ -91,16 +91,13 @@
private final HashMap<Connection, Call> mConfCallMap = Maps.newHashMap();
private final AtomicInteger mNextCallId = new AtomicInteger(CALL_ID_START_VALUE);
private final ArrayList<Listener> mListeners = new ArrayList<Listener>();
- private RejectWithTextMessageManager mRejectWithTextMessageManager;
private Connection mCdmaIncomingConnection;
private Connection mCdmaOutgoingConnection;
public CallModeler(CallStateMonitor callStateMonitor, CallManager callManager,
- RejectWithTextMessageManager rejectWithTextMessageManager,
CallGatewayManager callGatewayManager) {
mCallStateMonitor = callStateMonitor;
mCallManager = callManager;
- mRejectWithTextMessageManager = rejectWithTextMessageManager;
mCallGatewayManager = callGatewayManager;
mCallStateMonitor.addListener(this);
diff --git a/src/com/android/phone/HfaActivity.java b/src/com/android/phone/HfaActivity.java
index e3c9345..bcd3652 100644
--- a/src/com/android/phone/HfaActivity.java
+++ b/src/com/android/phone/HfaActivity.java
@@ -19,9 +19,7 @@
import android.app.Activity;
import android.app.AlertDialog;
import android.app.PendingIntent;
-import android.app.PendingIntent.CanceledException;
import android.content.DialogInterface;
-import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -39,22 +37,16 @@
public class HfaActivity extends Activity {
private static final String TAG = HfaActivity.class.getSimpleName();
- private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
-
- public static final int OTASP_UNKNOWN = 0;
- public static final int OTASP_USER_SKIPPED = 1;
- public static final int OTASP_SUCCESS = 2;
- public static final int OTASP_FAILURE = 3;
-
- private boolean mCanSkip;
private AlertDialog mDialog;
private HfaLogic mHfaLogic;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
+ Log.i(TAG, "onCreate");
- if (VERBOSE) Log.v(TAG, "onCreate");
+ final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
+ OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
mHfaLogic = new HfaLogic(this.getApplicationContext(), new HfaLogic.HfaLogicCallback() {
@Override
@@ -66,7 +58,7 @@
public void onError(String error) {
onHfaError(error);
}
- });
+ }, otaResponseIntent);
startProvisioning();
}
@@ -75,7 +67,7 @@
protected void onDestroy() {
super.onDestroy();
- if (VERBOSE) Log.v(TAG, "onDestroy");
+ Log.i(TAG, "onDestroy");
if (mDialog != null && mDialog.isShowing()) {
mDialog.dismiss();
@@ -89,8 +81,6 @@
}
private void buildAndShowDialog() {
- mCanSkip = true;
-
mDialog = new AlertDialog.Builder(this)
.setTitle(R.string.ota_hfa_activation_title)
.setMessage(R.string.ota_hfa_activation_dialog_message)
@@ -98,9 +88,7 @@
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface di, int which) {
- if (mCanSkip) {
- sendFinalResponse(OTASP_USER_SKIPPED);
- }
+ onUserSkip();
}})
/*.setOnCancelListener(new DialogInterface.OnCancelListener() {
@Override
@@ -113,7 +101,7 @@
mDialog.setCanceledOnTouchOutside(false);
mDialog.setCancelable(false);
- if (VERBOSE) Log.v(TAG, "showing dialog");
+ Log.i(TAG, "showing dialog");
mDialog.show();
}
@@ -127,7 +115,7 @@
@Override
public void onClick(DialogInterface di, int which) {
di.dismiss();
- sendFinalResponse(OTASP_USER_SKIPPED);
+ onUserSkip();
}
})
.setNegativeButton(R.string.ota_try_again,
@@ -144,29 +132,11 @@
}
private void onHfaSuccess() {
- // User can no longer skip after success.
- mCanSkip = false;
-
- sendFinalResponse(OTASP_SUCCESS);
- }
-
- private void sendFinalResponse(int responseCode) {
- final PendingIntent otaResponseIntent = getIntent().getParcelableExtra(
- OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
-
- if (otaResponseIntent != null) {
- final Intent extraStuff = new Intent();
- extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode);
-
- try {
- if (VERBOSE) Log.v(TAG, "Sending OTASP confirmation with result code: "
- + responseCode);
- otaResponseIntent.send(this, 0 /* resultCode (not used) */, extraStuff);
- } catch (CanceledException e) {
- Log.e(TAG, "Pending Intent canceled");
- }
- }
-
finish();
}
+
+ private void onUserSkip() {
+ finish();
+ }
+
}
diff --git a/src/com/android/phone/HfaLogic.java b/src/com/android/phone/HfaLogic.java
index 7fd37cf..5a5e4b4 100644
--- a/src/com/android/phone/HfaLogic.java
+++ b/src/com/android/phone/HfaLogic.java
@@ -16,6 +16,8 @@
package com.android.phone;
+import android.app.PendingIntent;
+import android.app.PendingIntent.CanceledException;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -46,7 +48,6 @@
*/
public class HfaLogic {
private static final String TAG = HfaLogic.class.getSimpleName();
- private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final String ACTION_START = "com.android.action.START_HFA";
private static final String ACTION_ERROR = "com.android.action.ERROR_HFA";
@@ -59,9 +60,15 @@
public static final int WAITING_FOR_RADIO_OFF = 1;
public static final int WAITING_FOR_RADIO_ON = 2;
+ public static final int OTASP_UNKNOWN = 0;
+ public static final int OTASP_USER_SKIPPED = 1;
+ public static final int OTASP_SUCCESS = 2;
+ public static final int OTASP_FAILURE = 3;
+
private int mPhoneMonitorState = NOT_WAITING;
private BroadcastReceiver mReceiver;
private HfaLogicCallback mCallback;
+ private PendingIntent mResponseIntent;
private Context mContext;
public interface HfaLogicCallback {
@@ -69,9 +76,10 @@
public void onError(String errorMsg);
}
- public HfaLogic(Context context, HfaLogicCallback callback) {
+ public HfaLogic(Context context, HfaLogicCallback callback, PendingIntent intent) {
mCallback = Preconditions.checkNotNull(callback);
mContext = Preconditions.checkNotNull(context);
+ mResponseIntent = intent;
}
public void start() {
@@ -85,21 +93,25 @@
}
private void sendHfaCommand(String action) {
- if (VERBOSE) Log.v(TAG, "Sending command: " + action);
+ Log.i(TAG, "Sending command: " + action);
mContext.sendBroadcast(new Intent(action));
}
private void onHfaError(String errorMsg) {
+ Log.i(TAG, "HfaError");
stopHfaIntentReceiver();
+ sendFinalResponse(OTASP_FAILURE, errorMsg);
mCallback.onError(errorMsg);
}
private void onHfaSuccess() {
+ Log.i(TAG, "HfaSuccess");
stopHfaIntentReceiver();
bounceRadio();
}
private void onTotalSuccess() {
+ sendFinalResponse(OTASP_SUCCESS, null);
mCallback.onSuccess();
}
@@ -116,7 +128,7 @@
final boolean radioIsOff = state.getVoiceRegState() == ServiceState.STATE_POWER_OFF;
final Phone phone = PhoneGlobals.getInstance().getPhone();
- if (VERBOSE) Log.v(TAG, "Radio is on: " + !radioIsOff);
+ Log.i(TAG, "Radio is on: " + !radioIsOff);
if (mPhoneMonitorState == WAITING_FOR_RADIO_OFF) {
if (radioIsOff) {
@@ -144,7 +156,7 @@
if (action.equals(ACTION_ERROR)) {
onHfaError(intent.getStringExtra("errorCode"));
} else if (action.equals(ACTION_COMPLETE)) {
- if (VERBOSE) Log.v(TAG, "Hfa Successful");
+ Log.i(TAG, "Hfa Successful");
onHfaSuccess();
}
}
@@ -160,6 +172,25 @@
}
}
+ private void sendFinalResponse(int responseCode, String errorCode) {
+ if (mResponseIntent != null) {
+ final Intent extraStuff = new Intent();
+ extraStuff.putExtra(OtaUtils.EXTRA_OTASP_RESULT_CODE, responseCode);
+
+ if (responseCode == OTASP_FAILURE && errorCode != null) {
+ extraStuff.putExtra(OtaUtils.EXTRA_OTASP_ERROR_CODE, errorCode);
+ }
+
+ try {
+ Log.i(TAG, "Sending OTASP confirmation with result code: "
+ + responseCode);
+ mResponseIntent.send(mContext, 0 /* resultCode (not used) */, extraStuff);
+ } catch (CanceledException e) {
+ Log.e(TAG, "Pending Intent canceled");
+ }
+ }
+ }
+
private Handler mHandler = new Handler() {
@Override
public void handleMessage(Message msg) {
diff --git a/src/com/android/phone/HfaService.java b/src/com/android/phone/HfaService.java
index a4d13f2..3aeed4d 100644
--- a/src/com/android/phone/HfaService.java
+++ b/src/com/android/phone/HfaService.java
@@ -16,6 +16,7 @@
package com.android.phone;
+import android.app.PendingIntent;
import android.app.Service;
import android.content.Intent;
import android.os.IBinder;
@@ -29,6 +30,14 @@
@Override
public void onCreate() {
+ Log.i(TAG, "service started");
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ final PendingIntent otaResponseIntent = intent.getParcelableExtra(
+ OtaUtils.EXTRA_OTASP_RESULT_CODE_PENDING_INTENT);
+
new HfaLogic(this, new HfaLogic.HfaLogicCallback() {
@Override
public void onSuccess() {
@@ -43,9 +52,9 @@
// we do the same thing...finish.
onComplete();
}
- }).start();
+ }, otaResponseIntent).start();
- Log.i(TAG, "service started");
+ return START_STICKY;
}
@Override
diff --git a/src/com/android/phone/OtaUtils.java b/src/com/android/phone/OtaUtils.java
index 8b67148..fe11831 100644
--- a/src/com/android/phone/OtaUtils.java
+++ b/src/com/android/phone/OtaUtils.java
@@ -125,8 +125,11 @@
// Extra attached to the above PendingIntent that indicates
// success or failure.
- public static final String EXTRA_OTASP_RESULT_CODE =
- "otasp_result_code";
+ public static final String EXTRA_OTASP_RESULT_CODE = "otasp_result_code";
+
+ // Extra attached to the above PendingIntent that contains an error code.
+ public static final String EXTRA_OTASP_ERROR_CODE = "otasp_error_code";
+
public static final int OTASP_UNKNOWN = 0;
public static final int OTASP_USER_SKIPPED = 1; // Only meaningful with interactive OTASP
public static final int OTASP_SUCCESS = 2;
diff --git a/src/com/android/phone/PhoneGlobals.java b/src/com/android/phone/PhoneGlobals.java
index 5474a62..3f35900 100644
--- a/src/com/android/phone/PhoneGlobals.java
+++ b/src/com/android/phone/PhoneGlobals.java
@@ -175,7 +175,6 @@
private CallModeler callModeler;
private CallStateMonitor callStateMonitor;
private DTMFTonePlayer dtmfTonePlayer;
- private RejectWithTextMessageManager rejectWithTextMessageManager;
private IBluetoothHeadsetPhone mBluetoothPhone;
private Ringer ringer;
private WiredHeadsetManager wiredHeadsetManager;
@@ -464,12 +463,8 @@
// Monitors call activity from the telephony layer
callStateMonitor = new CallStateMonitor(mCM);
- // Rejects calls with TextMessages
- rejectWithTextMessageManager = new RejectWithTextMessageManager();
-
// Creates call models for use with CallHandlerService.
- callModeler = new CallModeler(callStateMonitor, mCM, rejectWithTextMessageManager,
- callGatewayManager);
+ callModeler = new CallModeler(callStateMonitor, mCM, callGatewayManager);
// Plays DTMF Tones
dtmfTonePlayer = new DTMFTonePlayer(mCM, callModeler);
@@ -488,7 +483,7 @@
// Service used by in-call UI to control calls
callCommandService = new CallCommandService(this, mCM, callModeler, dtmfTonePlayer,
- audioRouter, rejectWithTextMessageManager);
+ audioRouter);
// Sends call state to the UI
callHandlerServiceProxy = new CallHandlerServiceProxy(this, callModeler,
diff --git a/src/com/android/phone/RejectWithTextMessageManager.java b/src/com/android/phone/RejectWithTextMessageManager.java
index b816eb0..8ad1257 100644
--- a/src/com/android/phone/RejectWithTextMessageManager.java
+++ b/src/com/android/phone/RejectWithTextMessageManager.java
@@ -16,20 +16,11 @@
package com.android.phone;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.app.Dialog;
import android.content.ComponentName;
import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.pm.ResolveInfo;
-import android.content.pm.ServiceInfo;
import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Handler;
import android.os.Looper;
@@ -38,24 +29,14 @@
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.TextView;
import android.widget.Toast;
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 com.android.internal.telephony.SmsApplication;
import java.util.ArrayList;
-import java.util.List;
/**
* Helper class to manage the "Respond via Message" feature for incoming calls.
@@ -63,24 +44,13 @@
* @see com.android.phone.InCallScreen.internalRespondViaSms()
*/
public class RejectWithTextMessageManager {
-
private static final String TAG = RejectWithTextMessageManager.class.getSimpleName();
private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
- private static final String PERMISSION_SEND_RESPOND_VIA_MESSAGE =
- "android.permission.SEND_RESPOND_VIA_MESSAGE";
-
- /** The array of "canned responses"; see loadCannedResponses(). */
- private String[] mCannedResponses;
-
/** SharedPreferences file name for our persistent settings. */
private static final String SHARED_PREFERENCES_NAME = "respond_via_sms_prefs";
- private Intent mIntent;
-
- private ArrayList<ComponentName> mComponentsWithPermission = new ArrayList<ComponentName>();
-
- // Preference keys for the 4 "canned responses"; see RespondViaSmsManager$Settings.
+ // Preference keys for the 4 "canned responses"; see RespondViaSmsManager$Settings.
// Since (for now at least) the number of messages is fixed at 4, and since
// SharedPreferences can't deal with arrays anyway, just store the messages
// as 4 separate strings.
@@ -89,12 +59,6 @@
private static final String KEY_CANNED_RESPONSE_PREF_2 = "canned_response_pref_2";
private static final String KEY_CANNED_RESPONSE_PREF_3 = "canned_response_pref_3";
private static final String KEY_CANNED_RESPONSE_PREF_4 = "canned_response_pref_4";
- /* package */ static final String KEY_INSTANT_TEXT_DEFAULT_COMPONENT =
- "instant_text_def_component";
-
- /* package */ static final String TAG_ALL_SMS_SERVICES = "com.android.phone.AvailablePackages";
- /* package */ static final String TAG_SEND_SMS = "com.android.phone.MessageIntent";
- /* package */ static final String TAG_SMS_DESTINATION = "com.android.phone.SmsDestination";
/**
* Read the (customizable) canned responses from SharedPreferences,
@@ -129,42 +93,37 @@
return responses;
}
- private void sendTextAndExit(final String phoneNumber) {
- // Send the selected message immediately with no user interaction.
- if (mIntent.getComponent() != null) {
- PhoneGlobals.getInstance().startService(mIntent);
+ private static void showMessageSentToast(final String phoneNumber) {
+ // ...and show a brief confirmation to the user (since
+ // otherwise it's hard to be sure that anything actually
+ // happened.)
+ // Ugly hack to show a toaster from a service.
+ (new Thread(new Runnable() {
+ @Override
+ public void run() {
+ Looper.prepare();
+ Handler innerHandler = new Handler() {
+ @Override
+ public void handleMessage(Message message) {
+ final Resources res = PhoneGlobals.getInstance().getResources();
+ final String formatString = res.getString(
+ R.string.respond_via_sms_confirmation_format);
+ final String confirmationMsg = String.format(formatString, phoneNumber);
+ Toast.makeText(PhoneGlobals.getInstance(), confirmationMsg,
+ Toast.LENGTH_LONG).show();
+ }
- // ...and show a brief confirmation to the user (since
- // otherwise it's hard to be sure that anything actually
- // happened.)
- // Ugly hack to show a toaster from a service.
- (new Thread(new Runnable() {
- @Override
- public void run() {
- Looper.prepare();
- Handler innerHandler = new Handler() {
- @Override
- public void handleMessage(Message message) {
- final Resources res = PhoneGlobals.getInstance().getResources();
- final String formatString = res.getString(
- R.string.respond_via_sms_confirmation_format);
- final String confirmationMsg = String.format(formatString, phoneNumber);
- Toast.makeText(PhoneGlobals.getInstance(), confirmationMsg,
- Toast.LENGTH_LONG).show();
- }
+ @Override
+ public void dispatchMessage(Message message) {
+ handleMessage(message);
+ }
+ };
- @Override
- public void dispatchMessage(Message message) {
- handleMessage(message);
- }
- };
-
- Message message = innerHandler.obtainMessage();
- innerHandler.dispatchMessage(message);
- Looper.loop();
- }
- })).start();
- }
+ Message message = innerHandler.obtainMessage();
+ innerHandler.dispatchMessage(message);
+ Looper.loop();
+ }
+ })).start();
// TODO: If the device is locked, this toast won't actually ever
// be visible! (That's because we're about to dismiss the call
@@ -173,7 +132,7 @@
// Possible fixes:
// (1) Is it possible to allow a specific Toast to be visible
// on top of the keyguard?
- // (2) Artifically delay the dismissCallScreen() call by 3
+ // (2) Artificially delay the dismissCallScreen() call by 3
// seconds to allow the toast to be seen?
// (3) Don't use a toast at all; instead use a transient state
// of the InCallScreen (perhaps via the InCallUiState
@@ -182,125 +141,29 @@
}
/**
- * Queries the System to determine what packages contain services that can handle the instant
- * text response Action AND have permissions to do so.
+ * Reject the call with the specified message (or launch messaging UX if null message)
*/
- private static ArrayList<ComponentName> getPackagesWithInstantTextPermission() {
- final PackageManager packageManager = PhoneGlobals.getInstance().getPackageManager();
-
- final ArrayList<ComponentName> componentsWithPermission = new ArrayList<ComponentName>();
-
- // Get list of all services set up to handle the Instant Text intent.
- final List<ResolveInfo> infos = packageManager.queryIntentServices(
- getInstantTextIntent("", null, null), 0);
-
- // Collect all the valid services
- for (ResolveInfo resolveInfo : infos) {
- final ServiceInfo serviceInfo = resolveInfo.serviceInfo;
- if (serviceInfo == null) {
- Log.w(TAG, "Ignore package without proper service.");
- continue;
+ public static void rejectCallWithMessage(Call call, String message) {
+ Connection conn = call.getLatestConnection();
+ if (conn != null) {
+ final String phoneNumber = conn.getAddress();
+ final ComponentName component =
+ SmsApplication.getDefaultRespondViaMessageApplication(
+ PhoneGlobals.getInstance(), true /*updateIfNeeded*/);
+ if (component != null) {
+ // Build and send the intent
+ final Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null);
+ final Intent intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, uri);
+ if (message != null) {
+ intent.putExtra(Intent.EXTRA_TEXT, message);
+ showMessageSentToast(phoneNumber);
+ } else {
+ intent.putExtra("exit_on_sent", true);
+ intent.putExtra("showUI", true);
+ }
+ intent.setComponent(component);
+ PhoneGlobals.getInstance().startService(intent);
}
-
- // A Service is valid only if it requires the permission
- // PERMISSION_SEND_RESPOND_VIA_MESSAGE
- if (PERMISSION_SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
- componentsWithPermission.add(new ComponentName(serviceInfo.packageName,
- serviceInfo.name));
- }
- }
-
- return componentsWithPermission;
- }
-
- /**
- * @param phoneNumber Must not be null.
- * @param message Can be null. If message is null, the returned Intent will be configured to
- * launch the SMS compose UI. If non-null, the returned Intent will cause the specified message
- * to be sent with no interaction from the user.
- * @param component The component that should handle this intent.
- * @return Service Intent for the instant response.
- */
- private static Intent getInstantTextIntent(String phoneNumber, String message,
- ComponentName component) {
- final Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null);
- final Intent intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, uri);
- if (message != null) {
- intent.putExtra(Intent.EXTRA_TEXT, message);
- } else {
- intent.putExtra("exit_on_sent", true);
- intent.putExtra("showUI", true);
- }
- if (component != null) {
- intent.setComponent(component);
- }
- return intent;
- }
-
- private boolean getSmsService(String phoneNumber) {
- if (DBG) log("sendTextToDefaultActivity()...");
- final PackageManager packageManager = PhoneGlobals.getInstance().getPackageManager();
-
- // Check to see if the default component to receive this intent is already saved
- // and check to see if it still has the corrent permissions.
- final SharedPreferences prefs = PhoneGlobals.getInstance().
- getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
- final String flattenedName = prefs.getString(KEY_INSTANT_TEXT_DEFAULT_COMPONENT, null);
- if (flattenedName != null) {
- if (DBG) log("Default package was found." + flattenedName);
-
- final ComponentName componentName = ComponentName.unflattenFromString(flattenedName);
- ServiceInfo serviceInfo = null;
- try {
- serviceInfo = packageManager.getServiceInfo(componentName, 0);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Default service does not have permission.");
- }
-
- if (serviceInfo != null &&
- PERMISSION_SEND_RESPOND_VIA_MESSAGE.equals(serviceInfo.permission)) {
- mIntent.setComponent(componentName);
- return true;
- } else {
- SharedPreferences.Editor editor = prefs.edit();
- editor.remove(KEY_INSTANT_TEXT_DEFAULT_COMPONENT);
- editor.apply();
- }
- }
-
- mComponentsWithPermission = getPackagesWithInstantTextPermission();
-
- final int size = mComponentsWithPermission.size();
- if (size == 0) {
- Log.e(TAG, "No appropriate package receiving the Intent. Don't send anything");
- return false;
- } else if (size == 1) {
- mIntent.setComponent(mComponentsWithPermission.get(0));
- return true;
- } else {
- Log.v(TAG, "Choosing from one of the apps");
- final Intent intent = new Intent(Intent.ACTION_VIEW, null);
- intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
- Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS |
- Intent.FLAG_ACTIVITY_NO_ANIMATION |
- Intent.FLAG_ACTIVITY_NO_HISTORY |
- Intent.FLAG_FROM_BACKGROUND);
- intent.setClass(PhoneGlobals.getInstance(), TextMessagePackageChooser.class);
- intent.putExtra(TAG_ALL_SMS_SERVICES, mComponentsWithPermission);
- intent.putExtra(TAG_SEND_SMS, mIntent);
- intent.putExtra(TAG_SMS_DESTINATION, phoneNumber);
- PhoneGlobals.getInstance().startActivity(intent);
- return false;
- // return componentsWithPermission.get(0);
- }
- }
-
- public void rejectCallWithMessage(Call call, String message) {
- mComponentsWithPermission.clear();
- final String phoneNumber = call.getLatestConnection().getAddress();
- mIntent = getInstantTextIntent(phoneNumber, message, null);
- if (getSmsService(phoneNumber)) {
- sendTextAndExit(phoneNumber);
}
}
@@ -379,8 +242,9 @@
return false;
}
- // Allow the feature only when there's a destination for it.
- if (getPackagesWithInstantTextPermission().size() < 1) {
+ // Is there a valid SMS application on the phone?
+ if (SmsApplication.getDefaultRespondViaMessageApplication(PhoneGlobals.getInstance(),
+ true /*updateIfNeeded*/) == null) {
return false;
}
diff --git a/src/com/android/phone/TextMessagePackageChooser.java b/src/com/android/phone/TextMessagePackageChooser.java
deleted file mode 100644
index 6760a3a..0000000
--- a/src/com/android/phone/TextMessagePackageChooser.java
+++ /dev/null
@@ -1,231 +0,0 @@
-/*
-
- * Copyright (C) 2013 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.phone;
-
-import android.app.Activity;
-import android.app.ActivityManager;
-import android.app.AlertDialog;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
-import android.content.Intent;
-import android.content.SharedPreferences;
-import android.content.pm.ApplicationInfo;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.graphics.drawable.Drawable;
-import android.os.Bundle;
-import android.util.Log;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.BaseAdapter;
-import android.widget.CheckBox;
-import android.widget.CompoundButton;
-import android.widget.ImageView;
-import android.widget.TextView;
-import android.widget.Toast;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class TextMessagePackageChooser extends Activity {
- private static final String TAG = TextMessagePackageChooser.class.getSimpleName();
-
- /** SharedPreferences file name for our persistent settings. */
- private static final String SHARED_PREFERENCES_NAME = "respond_via_sms_prefs";
-
- private int mIconSize = -1;
-
- @Override
- public void onCreate(Bundle savedInstanceState) {
- super.onCreate(savedInstanceState);
- final ArrayList<ComponentName> components = getIntent().getParcelableArrayListExtra(
- RejectWithTextMessageManager.TAG_ALL_SMS_SERVICES);
- BaseAdapter adapter = new PackageSelectionAdapter(this, components);
-
- PackageClickListener clickListener = new PackageClickListener(components);
-
- final CharSequence title = getResources().getText(
- com.android.internal.R.string.whichApplication);
- LayoutInflater inflater =
- (LayoutInflater) getSystemService(Context.LAYOUT_INFLATER_SERVICE);
-
- final View view = inflater.inflate(com.android.internal.R.layout.always_use_checkbox,
- null);
- final CheckBox alwaysUse = (CheckBox) view.findViewById(
- com.android.internal.R.id.alwaysUse);
- alwaysUse.setText(com.android.internal.R.string.alwaysUse);
- alwaysUse.setOnCheckedChangeListener(clickListener);
-
- AlertDialog.Builder builder = new AlertDialog.Builder(this)
- .setTitle(title)
- .setCancelable(true)
- .setOnCancelListener(new RespondViaSmsCancelListener())
- .setAdapter(adapter, clickListener)
- .setView(view);
-
- builder.create().show();
- }
-
- private class PackageSelectionAdapter extends BaseAdapter {
- private final LayoutInflater mInflater;
- private final List<ComponentName> mComponents;
-
- public PackageSelectionAdapter(Context context, List<ComponentName> components) {
- mInflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mComponents = components;
- }
-
- @Override
- public int getCount() {
- return mComponents.size();
- }
-
- @Override
- public Object getItem(int position) {
- return mComponents.get(position);
- }
-
- @Override
- public long getItemId(int position) {
- return position;
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- if (convertView == null) {
- convertView = mInflater.inflate(
- com.android.internal.R.layout.activity_chooser_view_list_item, parent,
- false);
- }
-
- final ComponentName component = mComponents.get(position);
- final String packageName = component.getPackageName();
- final PackageManager packageManager = getPackageManager();
-
- // Set the application label
- final TextView text = (TextView) convertView.findViewById(
- com.android.internal.R.id.title);
-
- text.setText("");
- try {
- final ApplicationInfo appInfo = packageManager.getApplicationInfo(packageName, 0);
- final CharSequence label = packageManager.getApplicationLabel(appInfo);
- if (label != null) {
- text.setText(label);
- }
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to load app label because package was not found.");
- }
-
- // Set the application icon
- final ImageView icon = (ImageView) convertView.findViewById(android.R.id.icon);
- Drawable drawable = null;
- try {
- drawable = getPackageManager().getApplicationIcon(packageName);
- } catch (PackageManager.NameNotFoundException e) {
- Log.w(TAG, "Failed to load icon because it wasn't found.");
- }
- if (drawable == null) {
- drawable = getPackageManager().getDefaultActivityIcon();
- }
- icon.setImageDrawable(drawable);
- ViewGroup.LayoutParams lp = (ViewGroup.LayoutParams) icon.getLayoutParams();
- lp.width = lp.height = getIconSize();
-
- return convertView;
- }
-
- }
-
- private class PackageClickListener implements DialogInterface.OnClickListener,
- CompoundButton.OnCheckedChangeListener {
- final private List<ComponentName> mComponents;
- private boolean mMakeDefault = false;
-
- public PackageClickListener(List<ComponentName> components) {
- mComponents = components;
- }
-
- @Override
- public void onClick(DialogInterface dialog, int which) {
- final ComponentName component = mComponents.get(which);
-
- if (mMakeDefault) {
- final SharedPreferences prefs = PhoneGlobals.getInstance().getSharedPreferences(
- SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
- prefs.edit().putString(
- RejectWithTextMessageManager.KEY_INSTANT_TEXT_DEFAULT_COMPONENT,
- component.flattenToString()).apply();
- }
-
- final Intent messageIntent = (Intent) getIntent().getParcelableExtra(
- RejectWithTextMessageManager.TAG_SEND_SMS);
- if (messageIntent != null) {
- messageIntent.setComponent(component);
- PhoneGlobals.getInstance().startService(messageIntent);
-
- // ...and show a brief confirmation to the user (since
- // otherwise it's hard to be sure that anything actually
- // happened.)
- final Resources res = getResources();
- final String formatString = res.getString(
- R.string.respond_via_sms_confirmation_format);
- final String phoneNumber = (String) getIntent().getStringExtra(
- RejectWithTextMessageManager.TAG_SMS_DESTINATION);
- final String confirmationMsg = String.format(formatString, phoneNumber);
- Toast.makeText(PhoneGlobals.getInstance(), confirmationMsg, Toast.LENGTH_LONG).show();
- }
- finish();
- }
-
- @Override
- public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
- Log.i(TAG, "mMakeDefault : " + isChecked);
- mMakeDefault = isChecked;
- }
- }
-
- /**
- * OnCancelListener for the "Respond via SMS" popup.
- */
- public class RespondViaSmsCancelListener implements DialogInterface.OnCancelListener {
- public RespondViaSmsCancelListener() {
- }
-
- /**
- * Handles the user canceling the popup, either by touching
- * outside the popup or by pressing Back.
- */
- @Override
- public void onCancel(DialogInterface dialog) {
- finish();
- }
- }
-
- private int getIconSize() {
- if (mIconSize < 0) {
- final ActivityManager am =
- (ActivityManager) getSystemService(Context.ACTIVITY_SERVICE);
- mIconSize = am.getLauncherLargeIconSize();
- }
-
- return mIconSize;
- }
-}