RespondViaSmsManager.java revision 41e4c430d08ecbd8dfee7d72bfe3880640caf3ba
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 93f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 94524486403a387c324dee5aff7fb78ca784d15255David Brown /** 95524486403a387c324dee5aff7fb78ca784d15255David Brown * RespondViaSmsManager constructor. 96524486403a387c324dee5aff7fb78ca784d15255David Brown */ 97524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsManager() { 98524486403a387c324dee5aff7fb78ca784d15255David Brown } 99524486403a387c324dee5aff7fb78ca784d15255David Brown 100524486403a387c324dee5aff7fb78ca784d15255David Brown public void setInCallScreenInstance(InCallScreen inCallScreen) { 101524486403a387c324dee5aff7fb78ca784d15255David Brown mInCallScreen = inCallScreen; 1029e25b785cc7edae682144536f58080edb3a374e4David Brown } 1039e25b785cc7edae682144536f58080edb3a374e4David Brown 1049e25b785cc7edae682144536f58080edb3a374e4David Brown /** 10501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * Brings up the "Respond via SMS" popup for an incoming call. 1069e25b785cc7edae682144536f58080edb3a374e4David Brown * 1079e25b785cc7edae682144536f58080edb3a374e4David Brown * @param ringingCall the current incoming call 1089e25b785cc7edae682144536f58080edb3a374e4David Brown */ 109524486403a387c324dee5aff7fb78ca784d15255David Brown public void showRespondViaSmsPopup(Call ringingCall) { 1109e25b785cc7edae682144536f58080edb3a374e4David Brown if (DBG) log("showRespondViaSmsPopup()..."); 1119e25b785cc7edae682144536f58080edb3a374e4David Brown 112524486403a387c324dee5aff7fb78ca784d15255David Brown ListView lv = new ListView(mInCallScreen); 11301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 114f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Refresh the array of "canned responses". 115f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // TODO: don't do this here in the UI thread! (This lookup is very 116f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // cheap, but it's still a StrictMode violation. See the TODO comment 117f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // following loadCannedResponses() for more info.) 118f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown mCannedResponses = loadCannedResponses(); 119f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 120f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Build the list: start with the canned responses, but manually add 121f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // "Custom message..." as the last choice. 122f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown int numPopupItems = mCannedResponses.length + 1; 123f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown String[] popupItems = Arrays.copyOf(mCannedResponses, numPopupItems); 124524486403a387c324dee5aff7fb78ca784d15255David Brown popupItems[numPopupItems - 1] = mInCallScreen.getResources() 12501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .getString(R.string.respond_via_sms_custom_message); 12601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 12701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown ArrayAdapter<String> adapter = 128524486403a387c324dee5aff7fb78ca784d15255David Brown new ArrayAdapter<String>(mInCallScreen, 12901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown android.R.layout.simple_list_item_1, 13001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown android.R.id.text1, 13101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown popupItems); 13201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown lv.setAdapter(adapter); 13301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 13401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // Create a RespondViaSmsItemClickListener instance to handle item 13501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // clicks from the popup. 1369e25b785cc7edae682144536f58080edb3a374e4David Brown // (Note we create a fresh instance for each incoming call, and 1379e25b785cc7edae682144536f58080edb3a374e4David Brown // stash away the call's phone number, since we can't necessarily 1389e25b785cc7edae682144536f58080edb3a374e4David Brown // assume this call will still be ringing when the user finally 13901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // chooses a response.) 1409e25b785cc7edae682144536f58080edb3a374e4David Brown 1419e25b785cc7edae682144536f58080edb3a374e4David Brown Connection c = ringingCall.getLatestConnection(); 142a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- connection: " + c); 1439e25b785cc7edae682144536f58080edb3a374e4David Brown 144717392cacea72751cbffb57b0424354b19da0868David Brown if (c == null) { 145717392cacea72751cbffb57b0424354b19da0868David Brown // Uh oh -- the "ringingCall" doesn't have any connections any more. 146717392cacea72751cbffb57b0424354b19da0868David Brown // (In other words, it's no longer ringing.) This is rare, but can 147717392cacea72751cbffb57b0424354b19da0868David Brown // happen if the caller hangs up right at the exact moment the user 148717392cacea72751cbffb57b0424354b19da0868David Brown // selects the "Respond via SMS" option. 149717392cacea72751cbffb57b0424354b19da0868David Brown // There's nothing to do here (since the incoming call is gone), 150717392cacea72751cbffb57b0424354b19da0868David Brown // so just bail out. 151717392cacea72751cbffb57b0424354b19da0868David Brown Log.i(TAG, "showRespondViaSmsPopup: null connection; bailing out..."); 152717392cacea72751cbffb57b0424354b19da0868David Brown return; 153717392cacea72751cbffb57b0424354b19da0868David Brown } 154717392cacea72751cbffb57b0424354b19da0868David Brown 1559e25b785cc7edae682144536f58080edb3a374e4David Brown // TODO: at this point we probably should re-check c.getAddress() 1569e25b785cc7edae682144536f58080edb3a374e4David Brown // and c.getNumberPresentation() for validity. (i.e. recheck the 1579e25b785cc7edae682144536f58080edb3a374e4David Brown // same cases in InCallTouchUi.showIncomingCallWidget() where we 1589e25b785cc7edae682144536f58080edb3a374e4David Brown // should have disallowed the "respond via SMS" feature in the 1599e25b785cc7edae682144536f58080edb3a374e4David Brown // first place.) 1609e25b785cc7edae682144536f58080edb3a374e4David Brown 1619e25b785cc7edae682144536f58080edb3a374e4David Brown String phoneNumber = c.getAddress(); 162a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- phoneNumber: " + phoneNumber); 163524486403a387c324dee5aff7fb78ca784d15255David Brown lv.setOnItemClickListener(new RespondViaSmsItemClickListener(phoneNumber)); 16411544975bb29ede282333209e945a75430b221a8David Brown 165524486403a387c324dee5aff7fb78ca784d15255David Brown AlertDialog.Builder builder = new AlertDialog.Builder(mInCallScreen) 16601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .setCancelable(true) 167524486403a387c324dee5aff7fb78ca784d15255David Brown .setOnCancelListener(new RespondViaSmsCancelListener()) 16801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .setView(lv); 169524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup = builder.create(); 170524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup.show(); 171524486403a387c324dee5aff7fb78ca784d15255David Brown } 1729e25b785cc7edae682144536f58080edb3a374e4David Brown 173524486403a387c324dee5aff7fb78ca784d15255David Brown /** 174524486403a387c324dee5aff7fb78ca784d15255David Brown * Dismiss the "Respond via SMS" popup if it's visible. 175524486403a387c324dee5aff7fb78ca784d15255David Brown * 176524486403a387c324dee5aff7fb78ca784d15255David Brown * This is safe to call even if the popup is already dismissed, and 177524486403a387c324dee5aff7fb78ca784d15255David Brown * even if you never called showRespondViaSmsPopup() in the first 178524486403a387c324dee5aff7fb78ca784d15255David Brown * place. 179524486403a387c324dee5aff7fb78ca784d15255David Brown */ 180524486403a387c324dee5aff7fb78ca784d15255David Brown public void dismissPopup() { 181524486403a387c324dee5aff7fb78ca784d15255David Brown if (mPopup != null) { 182524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup.dismiss(); // safe even if already dismissed 183524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup = null; 184524486403a387c324dee5aff7fb78ca784d15255David Brown } 1859e25b785cc7edae682144536f58080edb3a374e4David Brown } 1869e25b785cc7edae682144536f58080edb3a374e4David Brown 187552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa public boolean isShowingPopup() { 188552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa return mPopup != null && mPopup.isShowing(); 189552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } 190552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 1919e25b785cc7edae682144536f58080edb3a374e4David Brown /** 19201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * OnItemClickListener for the "Respond via SMS" popup. 1939e25b785cc7edae682144536f58080edb3a374e4David Brown */ 194524486403a387c324dee5aff7fb78ca784d15255David Brown public class RespondViaSmsItemClickListener implements AdapterView.OnItemClickListener { 1959e25b785cc7edae682144536f58080edb3a374e4David Brown // Phone number to send the SMS to. 1969e25b785cc7edae682144536f58080edb3a374e4David Brown private String mPhoneNumber; 1979e25b785cc7edae682144536f58080edb3a374e4David Brown 198524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsItemClickListener(String phoneNumber) { 1999e25b785cc7edae682144536f58080edb3a374e4David Brown mPhoneNumber = phoneNumber; 2009e25b785cc7edae682144536f58080edb3a374e4David Brown } 2019e25b785cc7edae682144536f58080edb3a374e4David Brown 20211544975bb29ede282333209e945a75430b221a8David Brown /** 20311544975bb29ede282333209e945a75430b221a8David Brown * Handles the user selecting an item from the popup. 20411544975bb29ede282333209e945a75430b221a8David Brown */ 205552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa @Override 20601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown public void onItemClick(AdapterView<?> parent, // The ListView 20701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown View view, // The TextView that was clicked 20801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown int position, 20901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown long id) { 21001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("RespondViaSmsItemClickListener.onItemClick(" + position + ")..."); 21101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown String message = (String) parent.getItemAtPosition(position); 212a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- message: '" + message + "'"); 21301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 21401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // The "Custom" choice is a special case. 21501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // (For now, it's guaranteed to be the last item.) 21601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (position == (parent.getCount() - 1)) { 2179e25b785cc7edae682144536f58080edb3a374e4David Brown // Take the user to the standard SMS compose UI. 218524486403a387c324dee5aff7fb78ca784d15255David Brown launchSmsCompose(mPhoneNumber); 2199e25b785cc7edae682144536f58080edb3a374e4David Brown } else { 22001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // Send the selected message immediately with no user interaction. 221524486403a387c324dee5aff7fb78ca784d15255David Brown sendText(mPhoneNumber, message); 222e662446d37d0f8bea51151488af461efe264c315David Brown 223e662446d37d0f8bea51151488af461efe264c315David Brown // ...and show a brief confirmation to the user (since 224e662446d37d0f8bea51151488af461efe264c315David Brown // otherwise it's hard to be sure that anything actually 225e662446d37d0f8bea51151488af461efe264c315David Brown // happened.) 226e662446d37d0f8bea51151488af461efe264c315David Brown final Resources res = mInCallScreen.getResources(); 227e662446d37d0f8bea51151488af461efe264c315David Brown String formatString = res.getString(R.string.respond_via_sms_confirmation_format); 228e662446d37d0f8bea51151488af461efe264c315David Brown String confirmationMsg = String.format(formatString, mPhoneNumber); 229e662446d37d0f8bea51151488af461efe264c315David Brown Toast.makeText(mInCallScreen, 230e662446d37d0f8bea51151488af461efe264c315David Brown confirmationMsg, 231e662446d37d0f8bea51151488af461efe264c315David Brown Toast.LENGTH_LONG).show(); 232e662446d37d0f8bea51151488af461efe264c315David Brown 233e662446d37d0f8bea51151488af461efe264c315David Brown // TODO: If the device is locked, this toast won't actually ever 234e662446d37d0f8bea51151488af461efe264c315David Brown // be visible! (That's because we're about to dismiss the call 235e662446d37d0f8bea51151488af461efe264c315David Brown // screen, which means that the device will return to the 236e662446d37d0f8bea51151488af461efe264c315David Brown // keyguard. But toasts aren't visible on top of the keyguard.) 237e662446d37d0f8bea51151488af461efe264c315David Brown // Possible fixes: 238e662446d37d0f8bea51151488af461efe264c315David Brown // (1) Is it possible to allow a specific Toast to be visible 239e662446d37d0f8bea51151488af461efe264c315David Brown // on top of the keyguard? 240e662446d37d0f8bea51151488af461efe264c315David Brown // (2) Artifically delay the dismissCallScreen() call by 3 241e662446d37d0f8bea51151488af461efe264c315David Brown // seconds to allow the toast to be seen? 242e662446d37d0f8bea51151488af461efe264c315David Brown // (3) Don't use a toast at all; instead use a transient state 243e662446d37d0f8bea51151488af461efe264c315David Brown // of the InCallScreen (perhaps via the InCallUiState 244e662446d37d0f8bea51151488af461efe264c315David Brown // progressIndication feature), and have that state be 245e662446d37d0f8bea51151488af461efe264c315David Brown // visible for 3 seconds before calling dismissCallScreen(). 2469e25b785cc7edae682144536f58080edb3a374e4David Brown } 2479e25b785cc7edae682144536f58080edb3a374e4David Brown 248717392cacea72751cbffb57b0424354b19da0868David Brown // At this point the user is done dealing with the incoming call, so 249717392cacea72751cbffb57b0424354b19da0868David Brown // there's no reason to keep it around. (It's also confusing for 250717392cacea72751cbffb57b0424354b19da0868David Brown // the "incoming call" icon in the status bar to still be visible.) 251717392cacea72751cbffb57b0424354b19da0868David Brown // So reject the call now. 252717392cacea72751cbffb57b0424354b19da0868David Brown mInCallScreen.hangupRingingCall(); 253717392cacea72751cbffb57b0424354b19da0868David Brown 254552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa dismissPopup(); 255552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 256552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa final Phone.State state = PhoneApp.getInstance().mCM.getState(); 257552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa if (state == Phone.State.IDLE) { 258552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // There's no other phone call to interact. Exit the entire in-call screen. 259552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa PhoneApp.getInstance().dismissCallScreen(); 260552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } else { 261552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // The user is still in the middle of other phone calls, so we should keep the 262552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // in-call screen. 263552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa mInCallScreen.requestUpdateScreen(); 264552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } 2659e25b785cc7edae682144536f58080edb3a374e4David Brown } 2669e25b785cc7edae682144536f58080edb3a374e4David Brown } 2679e25b785cc7edae682144536f58080edb3a374e4David Brown 2689e25b785cc7edae682144536f58080edb3a374e4David Brown /** 26901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * OnCancelListener for the "Respond via SMS" popup. 27011544975bb29ede282333209e945a75430b221a8David Brown */ 271524486403a387c324dee5aff7fb78ca784d15255David Brown public class RespondViaSmsCancelListener implements DialogInterface.OnCancelListener { 272524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsCancelListener() { 27311544975bb29ede282333209e945a75430b221a8David Brown } 27411544975bb29ede282333209e945a75430b221a8David Brown 27511544975bb29ede282333209e945a75430b221a8David Brown /** 27601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * Handles the user canceling the popup, either by touching 27711544975bb29ede282333209e945a75430b221a8David Brown * outside the popup or by pressing Back. 27811544975bb29ede282333209e945a75430b221a8David Brown */ 279552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa @Override 28001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown public void onCancel(DialogInterface dialog) { 28101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("RespondViaSmsCancelListener.onCancel()..."); 28211544975bb29ede282333209e945a75430b221a8David Brown 283552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa dismissPopup(); 28411544975bb29ede282333209e945a75430b221a8David Brown 285552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa final Phone.State state = PhoneApp.getInstance().mCM.getState(); 286552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa if (state == Phone.State.IDLE) { 287552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // This means the incoming call is already hung up when the user chooses not to 288552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // use "Respond via SMS" feature. Let's just exit the whole in-call screen. 289552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa PhoneApp.getInstance().dismissCallScreen(); 290552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } else { 29111544975bb29ede282333209e945a75430b221a8David Brown 292552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // If the user cancels the popup, this presumably means that 293552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // they didn't actually mean to bring up the "Respond via SMS" 294552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // UI in the first place (and instead want to go back to the 295552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // state where they can either answer or reject the call.) 296552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // So restart the ringer and bring back the regular incoming 297552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // call UI. 298552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 299552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // This will have no effect if the incoming call isn't still ringing. 300552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa PhoneApp.getInstance().notifier.restartRinger(); 301552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa 302552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // We hid the MultiWaveView widget way back in 303552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // InCallTouchUi.onTrigger(), when the user first selected 304552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // the "SMS" trigger. 305552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // 306552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // To bring it back, just force the entire InCallScreen to 307552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // update itself based on the current telephony state. 308552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // (Assuming the incoming call is still ringing, this will 309552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa // cause the incoming call widget to reappear.) 310552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa mInCallScreen.requestUpdateScreen(); 311552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa } 31211544975bb29ede282333209e945a75430b221a8David Brown } 31311544975bb29ede282333209e945a75430b221a8David Brown } 31411544975bb29ede282333209e945a75430b221a8David Brown 31511544975bb29ede282333209e945a75430b221a8David Brown /** 3169e25b785cc7edae682144536f58080edb3a374e4David Brown * Sends a text message without any interaction from the user. 3179e25b785cc7edae682144536f58080edb3a374e4David Brown */ 318524486403a387c324dee5aff7fb78ca784d15255David Brown private void sendText(String phoneNumber, String message) { 319a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("sendText: number " 320a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown + phoneNumber + ", message '" + message + "'"); 3219e25b785cc7edae682144536f58080edb3a374e4David Brown 322a29c610482c8db105c3c7f562cd72873d3ac4db6David Brown Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null); 323a29c610482c8db105c3c7f562cd72873d3ac4db6David Brown Intent intent = new Intent("com.android.mms.intent.action.SENDTO_NO_CONFIRMATION", uri); 324a29c610482c8db105c3c7f562cd72873d3ac4db6David Brown intent.putExtra(Intent.EXTRA_TEXT, message); 325a29c610482c8db105c3c7f562cd72873d3ac4db6David Brown mInCallScreen.startService(intent); 3269e25b785cc7edae682144536f58080edb3a374e4David Brown } 3279e25b785cc7edae682144536f58080edb3a374e4David Brown 3289e25b785cc7edae682144536f58080edb3a374e4David Brown /** 3299e25b785cc7edae682144536f58080edb3a374e4David Brown * Brings up the standard SMS compose UI. 3309e25b785cc7edae682144536f58080edb3a374e4David Brown */ 331524486403a387c324dee5aff7fb78ca784d15255David Brown private void launchSmsCompose(String phoneNumber) { 332a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("launchSmsCompose: number " + phoneNumber); 3339e25b785cc7edae682144536f58080edb3a374e4David Brown 33441e4c430d08ecbd8dfee7d72bfe3880640caf3baTom Taylor Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null); 33541e4c430d08ecbd8dfee7d72bfe3880640caf3baTom Taylor Intent intent = new Intent("com.android.mms.intent.action.SENDTO_NO_CONFIRMATION", uri); 33641e4c430d08ecbd8dfee7d72bfe3880640caf3baTom Taylor intent.putExtra("exit_on_sent", true); 33741e4c430d08ecbd8dfee7d72bfe3880640caf3baTom Taylor intent.putExtra("showUI", true); 3389e25b785cc7edae682144536f58080edb3a374e4David Brown 339a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log("- Launching SMS compose UI: " + intent); 34041e4c430d08ecbd8dfee7d72bfe3880640caf3baTom Taylor mInCallScreen.startService(intent); 3419e25b785cc7edae682144536f58080edb3a374e4David Brown } 3429e25b785cc7edae682144536f58080edb3a374e4David Brown 343f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 344f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** 345f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * Settings activity under "Call settings" to let you manage the 346f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * canned responses; see respond_via_sms_settings.xml 347f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown */ 348f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown public static class Settings extends PreferenceActivity 349f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown implements Preference.OnPreferenceChangeListener { 350f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown @Override 351f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown protected void onCreate(Bundle icicle) { 352f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown super.onCreate(icicle); 353f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("Settings: onCreate()..."); 354f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 355f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown getPreferenceManager().setSharedPreferencesName(SHARED_PREFERENCES_NAME); 356f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 357f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // This preference screen is ultra-simple; it's just 4 plain 358f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // <EditTextPreference>s, one for each of the 4 "canned responses". 359f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 360f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // The only nontrivial thing we do here is copy the text value of 361f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // each of those EditTextPreferences and use it as the preference's 362f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // "title" as well, so that the user will immediately see all 4 363f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // strings when they arrive here. 364f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 365f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Also, listen for change events (since we'll need to update the 366f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // title any time the user edits one of the strings.) 367f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 368f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown addPreferencesFromResource(R.xml.respond_via_sms_settings); 369f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 370f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown EditTextPreference pref; 371f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_1); 372f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 373f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 374f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 375f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_2); 376f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 377f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 378f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 379f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_3); 380f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 381f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 382f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 383f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_4); 384f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 385f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 3862c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 3872c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa ActionBar actionBar = getActionBar(); 3882c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa if (actionBar != null) { 3892c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa // android.R.id.home will be triggered in onOptionsItemSelected() 3902c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa actionBar.setDisplayHomeAsUpEnabled(true); 3912c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 392f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 393f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 394f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference.OnPreferenceChangeListener implementation 395552eaf42c27575b408ed3087f3dbc7a33680bd56Daisuke Miyakawa @Override 396f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown public boolean onPreferenceChange(Preference preference, Object newValue) { 397f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("onPreferenceChange: key = " + preference.getKey()); 398a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log(" preference = '" + preference + "'"); 399a841177ae2676d3ad92f82f8d378bc4915f238c9David Brown if (VDBG) log(" newValue = '" + newValue + "'"); 400f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 401f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown EditTextPreference pref = (EditTextPreference) preference; 402f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 403f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Copy the new text over to the title, just like in onCreate(). 404f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // (Watch out: onPreferenceChange() is called *before* the 405f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference itself gets updated, so we need to use newValue here 406f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // rather than pref.getText().) 407f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle((String) newValue); 408f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 409f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown return true; // means it's OK to update the state of the Preference with the new value 410f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 4112c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 4122c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa @Override 4132c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa public boolean onOptionsItemSelected(MenuItem item) { 4142c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa final int itemId = item.getItemId(); 4152c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled() 4162c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa CallFeaturesSetting.goUpToTopLevelSetting(this); 4172c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa return true; 4182c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 4192c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa return super.onOptionsItemSelected(item); 4202c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 421f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 422f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 423f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** 424f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * Read the (customizable) canned responses from SharedPreferences, 425f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * or from defaults if the user has never actually brought up 426f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * the Settings UI. 427f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * 428f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * This method does disk I/O (reading the SharedPreferences file) 429f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * so don't call it from the main thread. 430f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * 431f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * @see RespondViaSmsManager$Settings 432f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown */ 433f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private String[] loadCannedResponses() { 434f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("loadCannedResponses()..."); 435f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 436f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown SharedPreferences prefs = 437f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown mInCallScreen.getSharedPreferences(SHARED_PREFERENCES_NAME, 438f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown Context.MODE_PRIVATE); 439f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown final Resources res = mInCallScreen.getResources(); 440f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 441f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown String[] responses = new String[NUM_CANNED_RESPONSES]; 442f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 443f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Note the default values here must agree with the corresponding 444f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // android:defaultValue attributes in respond_via_sms_settings.xml. 445f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 446f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[0] = prefs.getString(KEY_CANNED_RESPONSE_PREF_1, 447f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_1)); 448f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[1] = prefs.getString(KEY_CANNED_RESPONSE_PREF_2, 449f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_2)); 450f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[2] = prefs.getString(KEY_CANNED_RESPONSE_PREF_3, 451f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_3)); 452f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[3] = prefs.getString(KEY_CANNED_RESPONSE_PREF_4, 453f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_4)); 454f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown return responses; 455f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 456f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // TODO: Don't call loadCannedResponses() from the UI thread. 457f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 458f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // We should either (1) kick off a background task when the call first 459f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // starts ringing (probably triggered from the InCallScreen 460f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // onNewRingingConnection() method) which would run loadCannedResponses() 461f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // and stash the result away in mCannedResponses, or (2) use an 462f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // OnSharedPreferenceChangeListener to listen for changes to this 463f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // SharedPreferences instance, and use that to kick off the background task. 464f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 465f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // In either case: 466f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 467f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // - Make sure we recover sanely if mCannedResponses is still null when it's 468f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // actually time to show the popup (i.e. if the background task was too 469f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // slow, or if the background task never got started for some reason) 470f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 471f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // - Make sure that all setting and getting of mCannedResponses happens 472f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // inside a synchronized block 473f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 474f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // - If we kick off the background task when the call first starts ringing, 475f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // consider delaying that until the incoming-call UI actually comes to the 476f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // foreground; this way we won't steal any CPU away from the caller-id 477f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // query. Maybe do it from InCallScreen.onResume()? 478f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Or InCallTouchUi.showIncomingCallWidget()? 479f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 4802058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown /** 4812058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * @return true if the "Respond via SMS" feature should be enabled 4822058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * for the specified incoming call. 4832058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * 4842058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * The general rule is that we *do* allow "Respond via SMS" except for 4852058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * the few (relatively rare) cases where we know for sure it won't 4862058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * work, namely: 4872058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * - a bogus or blank incoming number 4882058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * - a call from a SIP address 4892058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * - a "call presentation" that doesn't allow the number to be revealed 4902058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * 4912058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * In all other cases, we allow the user to respond via SMS. 4922058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * 4932058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * Note that this behavior isn't perfect; for example we have no way 4942058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * to detect whether the incoming call is from a landline (with most 4952058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * networks at least), so we still enable this feature even though 4962058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown * SMSes to that number will silently fail. 4972058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown */ 4982058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown public static boolean allowRespondViaSmsForCall(Call ringingCall) { 4992058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (DBG) log("allowRespondViaSmsForCall(" + ringingCall + ")..."); 5002058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 5012058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // First some basic sanity checks: 5022058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (ringingCall == null) { 5032058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: null ringingCall!"); 5042058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5052058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5062058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (!ringingCall.isRinging()) { 5072058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The call is in some state other than INCOMING or WAITING! 5082058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // (This should almost never happen, but it *could* 5092058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // conceivably happen if the ringing call got disconnected by 5102058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // the network just *after* we got it from the CallManager.) 5112058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: ringingCall not ringing! state = " 5122058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown + ringingCall.getState()); 5132058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5142058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5152058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Connection conn = ringingCall.getLatestConnection(); 5162058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (conn == null) { 5172058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The call doesn't have any connections! (Again, this can 5182058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // happen if the ringing call disconnects at the exact right 5192058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // moment, but should almost never happen in practice.) 5202058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: null Connection!"); 5212058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5222058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5232058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 5242058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // Check the incoming number: 5252058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown final String number = conn.getAddress(); 5262058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (DBG) log("- number: '" + number + "'"); 5272058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (TextUtils.isEmpty(number)) { 5282058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.w(TAG, "allowRespondViaSmsForCall: no incoming number!"); 5292058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5302058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5312058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (PhoneNumberUtils.isUriNumber(number)) { 5322058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The incoming number is actually a URI (i.e. a SIP address), 5332058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // not a regular PSTN phone number, and we can't send SMSes to 5342058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // SIP addresses. 5352058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // (TODO: That might still be possible eventually, though. Is 5362058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // there some SIP-specific equivalent to sending a text message?) 5372058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.i(TAG, "allowRespondViaSmsForCall: incoming 'number' is a SIP address."); 5382058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5392058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5402058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 5412058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // Finally, check the "call presentation": 5422058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown int presentation = conn.getNumberPresentation(); 5432058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (DBG) log("- presentation: " + presentation); 5442058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown if (presentation == Connection.PRESENTATION_RESTRICTED) { 5452058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // PRESENTATION_RESTRICTED means "caller-id blocked". 5462058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // The user isn't allowed to see the number in the first 5472058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown // place, so obviously we can't let you send an SMS to it. 5482058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown Log.i(TAG, "allowRespondViaSmsForCall: PRESENTATION_RESTRICTED."); 5492058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown return false; 5502058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown } 5512058f72e52da7f4a751f78787a693918d3a3ca9aDavid Brown 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