RespondViaSmsManager.java revision fcfd76719875e86742b16f5cc901689bff5bf43a
19e25b785cc7edae682144536f58080edb3a374e4David Brown/* 29e25b785cc7edae682144536f58080edb3a374e4David Brown * Copyright (C) 2011 The Android Open Source Project 39e25b785cc7edae682144536f58080edb3a374e4David Brown * 49e25b785cc7edae682144536f58080edb3a374e4David Brown * Licensed under the Apache License, Version 2.0 (the "License"); 59e25b785cc7edae682144536f58080edb3a374e4David Brown * you may not use this file except in compliance with the License. 69e25b785cc7edae682144536f58080edb3a374e4David Brown * You may obtain a copy of the License at 79e25b785cc7edae682144536f58080edb3a374e4David Brown * 89e25b785cc7edae682144536f58080edb3a374e4David Brown * http://www.apache.org/licenses/LICENSE-2.0 99e25b785cc7edae682144536f58080edb3a374e4David Brown * 109e25b785cc7edae682144536f58080edb3a374e4David Brown * Unless required by applicable law or agreed to in writing, software 119e25b785cc7edae682144536f58080edb3a374e4David Brown * distributed under the License is distributed on an "AS IS" BASIS, 129e25b785cc7edae682144536f58080edb3a374e4David Brown * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 139e25b785cc7edae682144536f58080edb3a374e4David Brown * See the License for the specific language governing permissions and 149e25b785cc7edae682144536f58080edb3a374e4David Brown * limitations under the License. 159e25b785cc7edae682144536f58080edb3a374e4David Brown */ 169e25b785cc7edae682144536f58080edb3a374e4David Brown 179e25b785cc7edae682144536f58080edb3a374e4David Brownpackage com.android.phone; 189e25b785cc7edae682144536f58080edb3a374e4David Brown 192c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawaimport com.android.internal.telephony.Call; 202c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawaimport com.android.internal.telephony.Connection; 21552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawaimport com.android.internal.telephony.Phone; 222c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 232c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawaimport android.app.ActionBar; 2401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.app.AlertDialog; 2501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.app.Dialog; 269e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.content.Context; 2701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.content.DialogInterface; 289e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.content.Intent; 29f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.content.SharedPreferences; 30f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.content.res.Resources; 319e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.net.Uri; 32f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.os.Bundle; 33a841177ae2676d3ad92f82f8d378bc4915f238c9David Brownimport android.os.SystemProperties; 34f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.preference.EditTextPreference; 35f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.preference.Preference; 36f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.preference.PreferenceActivity; 372058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brownimport android.telephony.PhoneNumberUtils; 382058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brownimport android.text.TextUtils; 399e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.util.Log; 402c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawaimport android.view.MenuItem; 419e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.view.View; 4201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.widget.AdapterView; 4301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.widget.ArrayAdapter; 4401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.widget.ListView; 45e662446d37d0f8bea51151488af461efe264c315David Brownimport android.widget.Toast; 469e25b785cc7edae682144536f58080edb3a374e4David Brown 4701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport java.util.Arrays; 489e25b785cc7edae682144536f58080edb3a374e4David Brown 499e25b785cc7edae682144536f58080edb3a374e4David Brown/** 50524486403a387c324dee5aff7fb78ca784d15255David Brown * Helper class to manage the "Respond via SMS" feature for incoming calls. 51524486403a387c324dee5aff7fb78ca784d15255David Brown * @see InCallScreen.internalRespondViaSms() 529e25b785cc7edae682144536f58080edb3a374e4David Brown */ 53524486403a387c324dee5aff7fb78ca784d15255David Brownpublic class RespondViaSmsManager { 54524486403a387c324dee5aff7fb78ca784d15255David Brown private static final String TAG = "RespondViaSmsManager"; 55a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown private static final boolean DBG = 56a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 57a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown // Do not check in with VDBG = true, since that may write PII to the system log. 58a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown private static final boolean VDBG = false; 599e25b785cc7edae682144536f58080edb3a374e4David Brown 60524486403a387c324dee5aff7fb78ca784d15255David Brown /** 61524486403a387c324dee5aff7fb78ca784d15255David Brown * Reference to the InCallScreen activity that owns us. This may be 62524486403a387c324dee5aff7fb78ca784d15255David Brown * null if we haven't been initialized yet *or* after the InCallScreen 63524486403a387c324dee5aff7fb78ca784d15255David Brown * activity has been destroyed. 64524486403a387c324dee5aff7fb78ca784d15255David Brown */ 65524486403a387c324dee5aff7fb78ca784d15255David Brown private InCallScreen mInCallScreen; 66524486403a387c324dee5aff7fb78ca784d15255David Brown 67524486403a387c324dee5aff7fb78ca784d15255David Brown /** 68524486403a387c324dee5aff7fb78ca784d15255David Brown * The popup showing the list of canned responses. 69524486403a387c324dee5aff7fb78ca784d15255David Brown * 70524486403a387c324dee5aff7fb78ca784d15255David Brown * This is an AlertDialog containing a ListView showing the possible 71524486403a387c324dee5aff7fb78ca784d15255David Brown * choices. This may be null if the InCallScreen hasn't ever called 72524486403a387c324dee5aff7fb78ca784d15255David Brown * showRespondViaSmsPopup() yet, or if the popup was visible once but 73524486403a387c324dee5aff7fb78ca784d15255David Brown * then got dismissed. 74524486403a387c324dee5aff7fb78ca784d15255David Brown */ 75524486403a387c324dee5aff7fb78ca784d15255David Brown private Dialog mPopup; 76524486403a387c324dee5aff7fb78ca784d15255David Brown 77f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** The array of "canned responses"; see loadCannedResponses(). */ 78f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private String[] mCannedResponses; 79f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 80f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** SharedPreferences file name for our persistent settings. */ 81f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String SHARED_PREFERENCES_NAME = "respond_via_sms_prefs"; 82f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 83f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference keys for the 4 "canned responses"; see RespondViaSmsManager$Settings. 84f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Since (for now at least) the number of messages is fixed at 4, and since 85f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // SharedPreferences can't deal with arrays anyway, just store the messages 86f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // as 4 separate strings. 87f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final int NUM_CANNED_RESPONSES = 4; 88f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_1 = "canned_response_pref_1"; 89f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_2 = "canned_response_pref_2"; 90f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_3 = "canned_response_pref_3"; 91f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_4 = "canned_response_pref_4"; 92f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 93fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa private static final String ACTION_SENDTO_NO_CONFIRMATION = 94fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa "com.android.mms.intent.action.SENDTO_NO_CONFIRMATION"; 95f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 96524486403a387c324dee5aff7fb78ca784d15255David Brown /** 97524486403a387c324dee5aff7fb78ca784d15255David Brown * RespondViaSmsManager constructor. 98524486403a387c324dee5aff7fb78ca784d15255David Brown */ 99524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsManager() { 100524486403a387c324dee5aff7fb78ca784d15255David Brown } 101524486403a387c324dee5aff7fb78ca784d15255David Brown 102524486403a387c324dee5aff7fb78ca784d15255David Brown public void setInCallScreenInstance(InCallScreen inCallScreen) { 103524486403a387c324dee5aff7fb78ca784d15255David Brown mInCallScreen = inCallScreen; 104f4ef613d8e6f203248d459cd77a3a7074c8f817cDaisuke Miyakawa 105565a7e471b8f2f27d3061406f0f3981843d892dfDaisuke Miyakawa if (mInCallScreen != null) { 106565a7e471b8f2f27d3061406f0f3981843d892dfDaisuke Miyakawa // Prefetch shared preferences to make the first canned response lookup faster 107565a7e471b8f2f27d3061406f0f3981843d892dfDaisuke Miyakawa // (and to prevent StrictMode violation) 108565a7e471b8f2f27d3061406f0f3981843d892dfDaisuke Miyakawa mInCallScreen.getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); 109565a7e471b8f2f27d3061406f0f3981843d892dfDaisuke Miyakawa } 1109e25b785cc7edae682144536f58080edb3a374e4David Brown } 1119e25b785cc7edae682144536f58080edb3a374e4David Brown 1129e25b785cc7edae682144536f58080edb3a374e4David Brown /** 11301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * Brings up the "Respond via SMS" popup for an incoming call. 1149e25b785cc7edae682144536f58080edb3a374e4David Brown * 1159e25b785cc7edae682144536f58080edb3a374e4David Brown * @param ringingCall the current incoming call 1169e25b785cc7edae682144536f58080edb3a374e4David Brown */ 117524486403a387c324dee5aff7fb78ca784d15255David Brown public void showRespondViaSmsPopup(Call ringingCall) { 1189e25b785cc7edae682144536f58080edb3a374e4David Brown if (DBG) log("showRespondViaSmsPopup()..."); 1199e25b785cc7edae682144536f58080edb3a374e4David Brown 120524486403a387c324dee5aff7fb78ca784d15255David Brown ListView lv = new ListView(mInCallScreen); 12101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 122f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Refresh the array of "canned responses". 123f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown mCannedResponses = loadCannedResponses(); 124f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 125f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Build the list: start with the canned responses, but manually add 126f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // "Custom message..." as the last choice. 127f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown int numPopupItems = mCannedResponses.length + 1; 128f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown String[] popupItems = Arrays.copyOf(mCannedResponses, numPopupItems); 129524486403a387c324dee5aff7fb78ca784d15255David Brown popupItems[numPopupItems - 1] = mInCallScreen.getResources() 13001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .getString(R.string.respond_via_sms_custom_message); 13101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 13201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown ArrayAdapter<String> adapter = 133524486403a387c324dee5aff7fb78ca784d15255David Brown new ArrayAdapter<String>(mInCallScreen, 13401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown android.R.layout.simple_list_item_1, 13501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown android.R.id.text1, 13601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown popupItems); 13701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown lv.setAdapter(adapter); 13801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 13901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // Create a RespondViaSmsItemClickListener instance to handle item 14001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // clicks from the popup. 1419e25b785cc7edae682144536f58080edb3a374e4David Brown // (Note we create a fresh instance for each incoming call, and 1429e25b785cc7edae682144536f58080edb3a374e4David Brown // stash away the call's phone number, since we can't necessarily 1439e25b785cc7edae682144536f58080edb3a374e4David Brown // assume this call will still be ringing when the user finally 14401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // chooses a response.) 1459e25b785cc7edae682144536f58080edb3a374e4David Brown 1469e25b785cc7edae682144536f58080edb3a374e4David Brown Connection c = ringingCall.getLatestConnection(); 147a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- connection: " + c); 1489e25b785cc7edae682144536f58080edb3a374e4David Brown 149717392cacea72751cbffb57b0424354b19da0868David Brown if (c == null) { 150717392cacea72751cbffb57b0424354b19da0868David Brown // Uh oh -- the "ringingCall" doesn't have any connections any more. 151717392cacea72751cbffb57b0424354b19da0868David Brown // (In other words, it's no longer ringing.) This is rare, but can 152717392cacea72751cbffb57b0424354b19da0868David Brown // happen if the caller hangs up right at the exact moment the user 153717392cacea72751cbffb57b0424354b19da0868David Brown // selects the "Respond via SMS" option. 154717392cacea72751cbffb57b0424354b19da0868David Brown // There's nothing to do here (since the incoming call is gone), 155717392cacea72751cbffb57b0424354b19da0868David Brown // so just bail out. 156717392cacea72751cbffb57b0424354b19da0868David Brown Log.i(TAG, "showRespondViaSmsPopup: null connection; bailing out..."); 157717392cacea72751cbffb57b0424354b19da0868David Brown return; 158717392cacea72751cbffb57b0424354b19da0868David Brown } 159717392cacea72751cbffb57b0424354b19da0868David Brown 1609e25b785cc7edae682144536f58080edb3a374e4David Brown // TODO: at this point we probably should re-check c.getAddress() 1619e25b785cc7edae682144536f58080edb3a374e4David Brown // and c.getNumberPresentation() for validity. (i.e. recheck the 1629e25b785cc7edae682144536f58080edb3a374e4David Brown // same cases in InCallTouchUi.showIncomingCallWidget() where we 1639e25b785cc7edae682144536f58080edb3a374e4David Brown // should have disallowed the "respond via SMS" feature in the 1649e25b785cc7edae682144536f58080edb3a374e4David Brown // first place.) 1659e25b785cc7edae682144536f58080edb3a374e4David Brown 1669e25b785cc7edae682144536f58080edb3a374e4David Brown String phoneNumber = c.getAddress(); 167a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- phoneNumber: " + phoneNumber); 168524486403a387c324dee5aff7fb78ca784d15255David Brown lv.setOnItemClickListener(new RespondViaSmsItemClickListener(phoneNumber)); 16911544975bb29ede282333209e945a75430b221a8David Brown 170524486403a387c324dee5aff7fb78ca784d15255David Brown AlertDialog.Builder builder = new AlertDialog.Builder(mInCallScreen) 17101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .setCancelable(true) 172524486403a387c324dee5aff7fb78ca784d15255David Brown .setOnCancelListener(new RespondViaSmsCancelListener()) 17301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .setView(lv); 174524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup = builder.create(); 175524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup.show(); 176524486403a387c324dee5aff7fb78ca784d15255David Brown } 1779e25b785cc7edae682144536f58080edb3a374e4David Brown 178524486403a387c324dee5aff7fb78ca784d15255David Brown /** 179524486403a387c324dee5aff7fb78ca784d15255David Brown * Dismiss the "Respond via SMS" popup if it's visible. 180524486403a387c324dee5aff7fb78ca784d15255David Brown * 181524486403a387c324dee5aff7fb78ca784d15255David Brown * This is safe to call even if the popup is already dismissed, and 182524486403a387c324dee5aff7fb78ca784d15255David Brown * even if you never called showRespondViaSmsPopup() in the first 183524486403a387c324dee5aff7fb78ca784d15255David Brown * place. 184524486403a387c324dee5aff7fb78ca784d15255David Brown */ 185524486403a387c324dee5aff7fb78ca784d15255David Brown public void dismissPopup() { 186524486403a387c324dee5aff7fb78ca784d15255David Brown if (mPopup != null) { 187524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup.dismiss(); // safe even if already dismissed 188524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup = null; 189524486403a387c324dee5aff7fb78ca784d15255David Brown } 1909e25b785cc7edae682144536f58080edb3a374e4David Brown } 1919e25b785cc7edae682144536f58080edb3a374e4David Brown 192552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa public boolean isShowingPopup() { 193552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa return mPopup != null && mPopup.isShowing(); 194552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } 195552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 1969e25b785cc7edae682144536f58080edb3a374e4David Brown /** 19701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * OnItemClickListener for the "Respond via SMS" popup. 1989e25b785cc7edae682144536f58080edb3a374e4David Brown */ 199524486403a387c324dee5aff7fb78ca784d15255David Brown public class RespondViaSmsItemClickListener implements AdapterView.OnItemClickListener { 2009e25b785cc7edae682144536f58080edb3a374e4David Brown // Phone number to send the SMS to. 2019e25b785cc7edae682144536f58080edb3a374e4David Brown private String mPhoneNumber; 2029e25b785cc7edae682144536f58080edb3a374e4David Brown 203524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsItemClickListener(String phoneNumber) { 2049e25b785cc7edae682144536f58080edb3a374e4David Brown mPhoneNumber = phoneNumber; 2059e25b785cc7edae682144536f58080edb3a374e4David Brown } 2069e25b785cc7edae682144536f58080edb3a374e4David Brown 20711544975bb29ede282333209e945a75430b221a8David Brown /** 20811544975bb29ede282333209e945a75430b221a8David Brown * Handles the user selecting an item from the popup. 20911544975bb29ede282333209e945a75430b221a8David Brown */ 210552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa @Override 21101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown public void onItemClick(AdapterView<?> parent, // The ListView 21201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown View view, // The TextView that was clicked 21301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown int position, 21401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown long id) { 21501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("RespondViaSmsItemClickListener.onItemClick(" + position + ")..."); 21601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown String message = (String) parent.getItemAtPosition(position); 217a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- message: '" + message + "'"); 21801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 21901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // The "Custom" choice is a special case. 22001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // (For now, it's guaranteed to be the last item.) 22101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (position == (parent.getCount() - 1)) { 2229e25b785cc7edae682144536f58080edb3a374e4David Brown // Take the user to the standard SMS compose UI. 223524486403a387c324dee5aff7fb78ca784d15255David Brown launchSmsCompose(mPhoneNumber); 2249e25b785cc7edae682144536f58080edb3a374e4David Brown } else { 22501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // Send the selected message immediately with no user interaction. 226524486403a387c324dee5aff7fb78ca784d15255David Brown sendText(mPhoneNumber, message); 227e662446d37d0f8bea51151488af461efe264c315David Brown 228e662446d37d0f8bea51151488af461efe264c315David Brown // ...and show a brief confirmation to the user (since 229e662446d37d0f8bea51151488af461efe264c315David Brown // otherwise it's hard to be sure that anything actually 230e662446d37d0f8bea51151488af461efe264c315David Brown // happened.) 231e662446d37d0f8bea51151488af461efe264c315David Brown final Resources res = mInCallScreen.getResources(); 232e662446d37d0f8bea51151488af461efe264c315David Brown String formatString = res.getString(R.string.respond_via_sms_confirmation_format); 233e662446d37d0f8bea51151488af461efe264c315David Brown String confirmationMsg = String.format(formatString, mPhoneNumber); 234e662446d37d0f8bea51151488af461efe264c315David Brown Toast.makeText(mInCallScreen, 235e662446d37d0f8bea51151488af461efe264c315David Brown confirmationMsg, 236e662446d37d0f8bea51151488af461efe264c315David Brown Toast.LENGTH_LONG).show(); 237e662446d37d0f8bea51151488af461efe264c315David Brown 238e662446d37d0f8bea51151488af461efe264c315David Brown // TODO: If the device is locked, this toast won't actually ever 239e662446d37d0f8bea51151488af461efe264c315David Brown // be visible! (That's because we're about to dismiss the call 240e662446d37d0f8bea51151488af461efe264c315David Brown // screen, which means that the device will return to the 241e662446d37d0f8bea51151488af461efe264c315David Brown // keyguard. But toasts aren't visible on top of the keyguard.) 242e662446d37d0f8bea51151488af461efe264c315David Brown // Possible fixes: 243e662446d37d0f8bea51151488af461efe264c315David Brown // (1) Is it possible to allow a specific Toast to be visible 244e662446d37d0f8bea51151488af461efe264c315David Brown // on top of the keyguard? 245e662446d37d0f8bea51151488af461efe264c315David Brown // (2) Artifically delay the dismissCallScreen() call by 3 246e662446d37d0f8bea51151488af461efe264c315David Brown // seconds to allow the toast to be seen? 247e662446d37d0f8bea51151488af461efe264c315David Brown // (3) Don't use a toast at all; instead use a transient state 248e662446d37d0f8bea51151488af461efe264c315David Brown // of the InCallScreen (perhaps via the InCallUiState 249e662446d37d0f8bea51151488af461efe264c315David Brown // progressIndication feature), and have that state be 250e662446d37d0f8bea51151488af461efe264c315David Brown // visible for 3 seconds before calling dismissCallScreen(). 2519e25b785cc7edae682144536f58080edb3a374e4David Brown } 2529e25b785cc7edae682144536f58080edb3a374e4David Brown 253717392cacea72751cbffb57b0424354b19da0868David Brown // At this point the user is done dealing with the incoming call, so 254717392cacea72751cbffb57b0424354b19da0868David Brown // there's no reason to keep it around. (It's also confusing for 255717392cacea72751cbffb57b0424354b19da0868David Brown // the "incoming call" icon in the status bar to still be visible.) 256717392cacea72751cbffb57b0424354b19da0868David Brown // So reject the call now. 257717392cacea72751cbffb57b0424354b19da0868David Brown mInCallScreen.hangupRingingCall(); 258717392cacea72751cbffb57b0424354b19da0868David Brown 259552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa dismissPopup(); 260552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 261552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa final Phone.State state = PhoneApp.getInstance().mCM.getState(); 262552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa if (state == Phone.State.IDLE) { 263552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // There's no other phone call to interact. Exit the entire in-call screen. 264552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa PhoneApp.getInstance().dismissCallScreen(); 265552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } else { 266552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // The user is still in the middle of other phone calls, so we should keep the 267552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // in-call screen. 268552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa mInCallScreen.requestUpdateScreen(); 269552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } 2709e25b785cc7edae682144536f58080edb3a374e4David Brown } 2719e25b785cc7edae682144536f58080edb3a374e4David Brown } 2729e25b785cc7edae682144536f58080edb3a374e4David Brown 2739e25b785cc7edae682144536f58080edb3a374e4David Brown /** 27401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * OnCancelListener for the "Respond via SMS" popup. 27511544975bb29ede282333209e945a75430b221a8David Brown */ 276524486403a387c324dee5aff7fb78ca784d15255David Brown public class RespondViaSmsCancelListener implements DialogInterface.OnCancelListener { 277524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsCancelListener() { 27811544975bb29ede282333209e945a75430b221a8David Brown } 27911544975bb29ede282333209e945a75430b221a8David Brown 28011544975bb29ede282333209e945a75430b221a8David Brown /** 28101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * Handles the user canceling the popup, either by touching 28211544975bb29ede282333209e945a75430b221a8David Brown * outside the popup or by pressing Back. 28311544975bb29ede282333209e945a75430b221a8David Brown */ 284552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa @Override 28501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown public void onCancel(DialogInterface dialog) { 28601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("RespondViaSmsCancelListener.onCancel()..."); 28711544975bb29ede282333209e945a75430b221a8David Brown 288552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa dismissPopup(); 28911544975bb29ede282333209e945a75430b221a8David Brown 290552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa final Phone.State state = PhoneApp.getInstance().mCM.getState(); 291552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa if (state == Phone.State.IDLE) { 292552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // This means the incoming call is already hung up when the user chooses not to 293552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // use "Respond via SMS" feature. Let's just exit the whole in-call screen. 294552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa PhoneApp.getInstance().dismissCallScreen(); 295552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } else { 29611544975bb29ede282333209e945a75430b221a8David Brown 297552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // If the user cancels the popup, this presumably means that 298552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // they didn't actually mean to bring up the "Respond via SMS" 299552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // UI in the first place (and instead want to go back to the 300552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // state where they can either answer or reject the call.) 301552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // So restart the ringer and bring back the regular incoming 302552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // call UI. 303552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 304552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // This will have no effect if the incoming call isn't still ringing. 305552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa PhoneApp.getInstance().notifier.restartRinger(); 306552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 307552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // We hid the MultiWaveView widget way back in 308552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // InCallTouchUi.onTrigger(), when the user first selected 309552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // the "SMS" trigger. 310552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // 311552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // To bring it back, just force the entire InCallScreen to 312552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // update itself based on the current telephony state. 313552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // (Assuming the incoming call is still ringing, this will 314552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // cause the incoming call widget to reappear.) 315552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa mInCallScreen.requestUpdateScreen(); 316552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } 31711544975bb29ede282333209e945a75430b221a8David Brown } 31811544975bb29ede282333209e945a75430b221a8David Brown } 31911544975bb29ede282333209e945a75430b221a8David Brown 32011544975bb29ede282333209e945a75430b221a8David Brown /** 3219e25b785cc7edae682144536f58080edb3a374e4David Brown * Sends a text message without any interaction from the user. 3229e25b785cc7edae682144536f58080edb3a374e4David Brown */ 323524486403a387c324dee5aff7fb78ca784d15255David Brown private void sendText(String phoneNumber, String message) { 324a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("sendText: number " 325a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown + phoneNumber + ", message '" + message + "'"); 3269e25b785cc7edae682144536f58080edb3a374e4David Brown 327fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa mInCallScreen.startService(getInstantTextIntent(phoneNumber, message)); 3289e25b785cc7edae682144536f58080edb3a374e4David Brown } 3299e25b785cc7edae682144536f58080edb3a374e4David Brown 3309e25b785cc7edae682144536f58080edb3a374e4David Brown /** 3319e25b785cc7edae682144536f58080edb3a374e4David Brown * Brings up the standard SMS compose UI. 3329e25b785cc7edae682144536f58080edb3a374e4David Brown */ 333524486403a387c324dee5aff7fb78ca784d15255David Brown private void launchSmsCompose(String phoneNumber) { 334a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("launchSmsCompose: number " + phoneNumber); 3359e25b785cc7edae682144536f58080edb3a374e4David Brown 336fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa Intent intent = getInstantTextIntent(phoneNumber, null); 3379e25b785cc7edae682144536f58080edb3a374e4David Brown 338a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- Launching SMS compose UI: " + intent); 33941e4c430d08ecbd8dfee7d72bfe3880640caf3baTom Taylor mInCallScreen.startService(intent); 3409e25b785cc7edae682144536f58080edb3a374e4David Brown } 3419e25b785cc7edae682144536f58080edb3a374e4David Brown 342fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa /** 343fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa * @param phoneNumber Must not be null. 344fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa * @param message Can be null. If message is null, the returned Intent will be configured to 345fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa * launch the SMS compose UI. If non-null, the returned Intent will cause the specified message 346fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa * to be sent with no interaction from the user. 347fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa * @return Service Intent for the instant response. 348fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa */ 349fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa private static Intent getInstantTextIntent(String phoneNumber, String message) { 350fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null); 351fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa Intent intent = new Intent(ACTION_SENDTO_NO_CONFIRMATION, uri); 352fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa if (message != null) { 353fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa intent.putExtra(Intent.EXTRA_TEXT, message); 354fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa } else { 355fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa intent.putExtra("exit_on_sent", true); 356fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa intent.putExtra("showUI", true); 357fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa } 358fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa return intent; 359fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa } 360f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 361f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** 362f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * Settings activity under "Call settings" to let you manage the 363f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * canned responses; see respond_via_sms_settings.xml 364f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown */ 365f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown public static class Settings extends PreferenceActivity 366f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown implements Preference.OnPreferenceChangeListener { 367f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown @Override 368f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown protected void onCreate(Bundle icicle) { 369f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown super.onCreate(icicle); 370f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("Settings: onCreate()..."); 371f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 372f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown getPreferenceManager().setSharedPreferencesName(SHARED_PREFERENCES_NAME); 373f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 374f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // This preference screen is ultra-simple; it's just 4 plain 375f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // <EditTextPreference>s, one for each of the 4 "canned responses". 376f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 377f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // The only nontrivial thing we do here is copy the text value of 378f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // each of those EditTextPreferences and use it as the preference's 379f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // "title" as well, so that the user will immediately see all 4 380f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // strings when they arrive here. 381f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 382f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Also, listen for change events (since we'll need to update the 383f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // title any time the user edits one of the strings.) 384f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 385f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown addPreferencesFromResource(R.xml.respond_via_sms_settings); 386f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 387f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown EditTextPreference pref; 388f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_1); 389f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 390f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 391f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 392f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_2); 393f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 394f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 395f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 396f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_3); 397f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 398f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 399f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 400f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_4); 401f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 402f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 4032c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 4042c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa ActionBar actionBar = getActionBar(); 4052c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa if (actionBar != null) { 4062c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa // android.R.id.home will be triggered in onOptionsItemSelected() 4072c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa actionBar.setDisplayHomeAsUpEnabled(true); 4082c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 409f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 410f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 411f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference.OnPreferenceChangeListener implementation 412552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa @Override 413f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown public boolean onPreferenceChange(Preference preference, Object newValue) { 414f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("onPreferenceChange: key = " + preference.getKey()); 415a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log(" preference = '" + preference + "'"); 416a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log(" newValue = '" + newValue + "'"); 417f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 418f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown EditTextPreference pref = (EditTextPreference) preference; 419f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 420f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Copy the new text over to the title, just like in onCreate(). 421f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // (Watch out: onPreferenceChange() is called *before* the 422f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference itself gets updated, so we need to use newValue here 423f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // rather than pref.getText().) 424f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle((String) newValue); 425f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 426f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown return true; // means it's OK to update the state of the Preference with the new value 427f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 4282c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 4292c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa @Override 4302c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa public boolean onOptionsItemSelected(MenuItem item) { 4312c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa final int itemId = item.getItemId(); 4322c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled() 4332c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa CallFeaturesSetting.goUpToTopLevelSetting(this); 4342c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa return true; 4352c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 4362c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa return super.onOptionsItemSelected(item); 4372c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 438f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 439f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 440f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** 441f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * Read the (customizable) canned responses from SharedPreferences, 442f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * or from defaults if the user has never actually brought up 443f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * the Settings UI. 444f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * 445f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * This method does disk I/O (reading the SharedPreferences file) 446f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * so don't call it from the main thread. 447f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * 448f4ef613d8e6f203248d459cd77a3a7074c8f817cDaisuke Miyakawa * @see RespondViaSmsManager.Settings 449f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown */ 450f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private String[] loadCannedResponses() { 451f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("loadCannedResponses()..."); 452f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 453f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown SharedPreferences prefs = 454f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown mInCallScreen.getSharedPreferences(SHARED_PREFERENCES_NAME, 455f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown Context.MODE_PRIVATE); 456f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown final Resources res = mInCallScreen.getResources(); 457f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 458f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown String[] responses = new String[NUM_CANNED_RESPONSES]; 459f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 460f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Note the default values here must agree with the corresponding 461f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // android:defaultValue attributes in respond_via_sms_settings.xml. 462f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 463f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[0] = prefs.getString(KEY_CANNED_RESPONSE_PREF_1, 464f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_1)); 465f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[1] = prefs.getString(KEY_CANNED_RESPONSE_PREF_2, 466f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_2)); 467f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[2] = prefs.getString(KEY_CANNED_RESPONSE_PREF_3, 468f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_3)); 469f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[3] = prefs.getString(KEY_CANNED_RESPONSE_PREF_4, 470f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_4)); 471f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown return responses; 472f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 473f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 4742058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown /** 4752058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * @return true if the "Respond via SMS" feature should be enabled 4762058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * for the specified incoming call. 4772058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * 4782058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * The general rule is that we *do* allow "Respond via SMS" except for 4792058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * the few (relatively rare) cases where we know for sure it won't 4802058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * work, namely: 4812058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * - a bogus or blank incoming number 4822058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * - a call from a SIP address 4832058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * - a "call presentation" that doesn't allow the number to be revealed 4842058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * 4852058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * In all other cases, we allow the user to respond via SMS. 4862058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * 4872058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * Note that this behavior isn't perfect; for example we have no way 4882058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * to detect whether the incoming call is from a landline (with most 4892058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * networks at least), so we still enable this feature even though 4902058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * SMSes to that number will silently fail. 4912058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown */ 492fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa public static boolean allowRespondViaSmsForCall(Context context, Call ringingCall) { 4932058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (DBG) log("allowRespondViaSmsForCall(" + ringingCall + ")..."); 4942058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 4952058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // First some basic sanity checks: 4962058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (ringingCall == null) { 4972058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: null ringingCall!"); 4982058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 4992058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5002058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (!ringingCall.isRinging()) { 5012058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The call is in some state other than INCOMING or WAITING! 5022058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // (This should almost never happen, but it *could* 5032058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // conceivably happen if the ringing call got disconnected by 5042058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // the network just *after* we got it from the CallManager.) 5052058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: ringingCall not ringing! state = " 5062058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown + ringingCall.getState()); 5072058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5082058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5092058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Connection conn = ringingCall.getLatestConnection(); 5102058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (conn == null) { 5112058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The call doesn't have any connections! (Again, this can 5122058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // happen if the ringing call disconnects at the exact right 5132058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // moment, but should almost never happen in practice.) 5142058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: null Connection!"); 5152058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5162058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5172058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 5182058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // Check the incoming number: 5192058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown final String number = conn.getAddress(); 5202058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (DBG) log("- number: '" + number + "'"); 5212058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (TextUtils.isEmpty(number)) { 5222058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: no incoming number!"); 5232058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5242058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5252058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (PhoneNumberUtils.isUriNumber(number)) { 5262058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The incoming number is actually a URI (i.e. a SIP address), 5272058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // not a regular PSTN phone number, and we can't send SMSes to 5282058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // SIP addresses. 5292058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // (TODO: That might still be possible eventually, though. Is 5302058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // there some SIP-specific equivalent to sending a text message?) 5312058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.i(TAG, "allowRespondViaSmsForCall: incoming 'number' is a SIP address."); 5322058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5332058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5342058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 5352058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // Finally, check the "call presentation": 5362058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown int presentation = conn.getNumberPresentation(); 5372058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (DBG) log("- presentation: " + presentation); 5382058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (presentation == Connection.PRESENTATION_RESTRICTED) { 5392058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // PRESENTATION_RESTRICTED means "caller-id blocked". 5402058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The user isn't allowed to see the number in the first 5412058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // place, so obviously we can't let you send an SMS to it. 5422058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.i(TAG, "allowRespondViaSmsForCall: PRESENTATION_RESTRICTED."); 5432058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5442058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5452058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 546fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa // Allow the feature only when there's a destination for it. 547fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa if (context.getPackageManager().resolveService(getInstantTextIntent(number, null) , 0) 548fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa == null) { 549fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa return false; 550fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa } 551fcfd76719875e86742b16f5cc901689bff5bf43aDaisuke Miyakawa 5522058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // TODO: with some carriers (in certain countries) you *can* actually 5532058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // tell whether a given number is a mobile phone or not. So in that 5542058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // case we could potentially return false here if the incoming call is 5552058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // from a land line. 5562058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 5572058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // If none of the above special cases apply, it's OK to enable the 5582058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // "Respond via SMS" feature. 5592058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return true; 5602058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5612058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 562f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 563f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static void log(String msg) { 5649e25b785cc7edae682144536f58080edb3a374e4David Brown Log.d(TAG, msg); 5659e25b785cc7edae682144536f58080edb3a374e4David Brown } 5669e25b785cc7edae682144536f58080edb3a374e4David Brown} 567