RespondViaSmsManager.java revision 2c8c40738e9b8a8e767aa061721ebaa5b5591a4c
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; 212c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 222c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawaimport android.app.ActionBar; 2301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.app.AlertDialog; 2401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.app.Dialog; 259e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.content.Context; 2601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.content.DialogInterface; 279e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.content.Intent; 28f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.content.SharedPreferences; 29f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.content.res.Resources; 309e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.net.Uri; 31f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.os.Bundle; 32f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.preference.EditTextPreference; 33f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.preference.Preference; 34f40c04a87e30bcfb795a5879d932418f0821fdb0David Brownimport android.preference.PreferenceActivity; 359e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.telephony.SmsManager; 369e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.util.Log; 372c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawaimport android.view.MenuItem; 389e25b785cc7edae682144536f58080edb3a374e4David Brownimport android.view.View; 3901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.widget.AdapterView; 4001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.widget.ArrayAdapter; 4101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport android.widget.ListView; 429e25b785cc7edae682144536f58080edb3a374e4David Brown 4301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brownimport java.util.Arrays; 449e25b785cc7edae682144536f58080edb3a374e4David Brown 459e25b785cc7edae682144536f58080edb3a374e4David Brown/** 46524486403a387c324dee5aff7fb78ca784d15255David Brown * Helper class to manage the "Respond via SMS" feature for incoming calls. 47524486403a387c324dee5aff7fb78ca784d15255David Brown * @see InCallScreen.internalRespondViaSms() 489e25b785cc7edae682144536f58080edb3a374e4David Brown */ 49524486403a387c324dee5aff7fb78ca784d15255David Brownpublic class RespondViaSmsManager { 50524486403a387c324dee5aff7fb78ca784d15255David Brown private static final String TAG = "RespondViaSmsManager"; 5111544975bb29ede282333209e945a75430b221a8David Brown private static final boolean DBG = true; 5211544975bb29ede282333209e945a75430b221a8David Brown // STOPSHIP: reduce DBG to 5311544975bb29ede282333209e945a75430b221a8David Brown // (PhoneApp.DBG_LEVEL >= 1) && (SystemProperties.getInt("ro.debuggable", 0) == 1); 549e25b785cc7edae682144536f58080edb3a374e4David Brown 55524486403a387c324dee5aff7fb78ca784d15255David Brown /** 56524486403a387c324dee5aff7fb78ca784d15255David Brown * Reference to the InCallScreen activity that owns us. This may be 57524486403a387c324dee5aff7fb78ca784d15255David Brown * null if we haven't been initialized yet *or* after the InCallScreen 58524486403a387c324dee5aff7fb78ca784d15255David Brown * activity has been destroyed. 59524486403a387c324dee5aff7fb78ca784d15255David Brown */ 60524486403a387c324dee5aff7fb78ca784d15255David Brown private InCallScreen mInCallScreen; 61524486403a387c324dee5aff7fb78ca784d15255David Brown 62524486403a387c324dee5aff7fb78ca784d15255David Brown /** 63524486403a387c324dee5aff7fb78ca784d15255David Brown * The popup showing the list of canned responses. 64524486403a387c324dee5aff7fb78ca784d15255David Brown * 65524486403a387c324dee5aff7fb78ca784d15255David Brown * This is an AlertDialog containing a ListView showing the possible 66524486403a387c324dee5aff7fb78ca784d15255David Brown * choices. This may be null if the InCallScreen hasn't ever called 67524486403a387c324dee5aff7fb78ca784d15255David Brown * showRespondViaSmsPopup() yet, or if the popup was visible once but 68524486403a387c324dee5aff7fb78ca784d15255David Brown * then got dismissed. 69524486403a387c324dee5aff7fb78ca784d15255David Brown */ 70524486403a387c324dee5aff7fb78ca784d15255David Brown private Dialog mPopup; 71524486403a387c324dee5aff7fb78ca784d15255David Brown 72f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** The array of "canned responses"; see loadCannedResponses(). */ 73f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private String[] mCannedResponses; 74f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 75f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** SharedPreferences file name for our persistent settings. */ 76f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String SHARED_PREFERENCES_NAME = "respond_via_sms_prefs"; 77f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 78f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference keys for the 4 "canned responses"; see RespondViaSmsManager$Settings. 79f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Since (for now at least) the number of messages is fixed at 4, and since 80f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // SharedPreferences can't deal with arrays anyway, just store the messages 81f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // as 4 separate strings. 82f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final int NUM_CANNED_RESPONSES = 4; 83f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_1 = "canned_response_pref_1"; 84f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_2 = "canned_response_pref_2"; 85f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_3 = "canned_response_pref_3"; 86f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static final String KEY_CANNED_RESPONSE_PREF_4 = "canned_response_pref_4"; 87f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 88f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 89524486403a387c324dee5aff7fb78ca784d15255David Brown /** 90524486403a387c324dee5aff7fb78ca784d15255David Brown * RespondViaSmsManager constructor. 91524486403a387c324dee5aff7fb78ca784d15255David Brown */ 92524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsManager() { 93524486403a387c324dee5aff7fb78ca784d15255David Brown } 94524486403a387c324dee5aff7fb78ca784d15255David Brown 95524486403a387c324dee5aff7fb78ca784d15255David Brown public void setInCallScreenInstance(InCallScreen inCallScreen) { 96524486403a387c324dee5aff7fb78ca784d15255David Brown mInCallScreen = inCallScreen; 979e25b785cc7edae682144536f58080edb3a374e4David Brown } 989e25b785cc7edae682144536f58080edb3a374e4David Brown 999e25b785cc7edae682144536f58080edb3a374e4David Brown /** 10001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * Brings up the "Respond via SMS" popup for an incoming call. 1019e25b785cc7edae682144536f58080edb3a374e4David Brown * 1029e25b785cc7edae682144536f58080edb3a374e4David Brown * @param ringingCall the current incoming call 1039e25b785cc7edae682144536f58080edb3a374e4David Brown */ 104524486403a387c324dee5aff7fb78ca784d15255David Brown public void showRespondViaSmsPopup(Call ringingCall) { 1059e25b785cc7edae682144536f58080edb3a374e4David Brown if (DBG) log("showRespondViaSmsPopup()..."); 1069e25b785cc7edae682144536f58080edb3a374e4David Brown 107524486403a387c324dee5aff7fb78ca784d15255David Brown ListView lv = new ListView(mInCallScreen); 10801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 109f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Refresh the array of "canned responses". 110f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // TODO: don't do this here in the UI thread! (This lookup is very 111f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // cheap, but it's still a StrictMode violation. See the TODO comment 112f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // following loadCannedResponses() for more info.) 113f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown mCannedResponses = loadCannedResponses(); 114f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 115f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Build the list: start with the canned responses, but manually add 116f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // "Custom message..." as the last choice. 117f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown int numPopupItems = mCannedResponses.length + 1; 118f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown String[] popupItems = Arrays.copyOf(mCannedResponses, numPopupItems); 119524486403a387c324dee5aff7fb78ca784d15255David Brown popupItems[numPopupItems - 1] = mInCallScreen.getResources() 12001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .getString(R.string.respond_via_sms_custom_message); 12101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 12201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown ArrayAdapter<String> adapter = 123524486403a387c324dee5aff7fb78ca784d15255David Brown new ArrayAdapter<String>(mInCallScreen, 12401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown android.R.layout.simple_list_item_1, 12501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown android.R.id.text1, 12601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown popupItems); 12701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown lv.setAdapter(adapter); 12801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 12901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // Create a RespondViaSmsItemClickListener instance to handle item 13001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // clicks from the popup. 1319e25b785cc7edae682144536f58080edb3a374e4David Brown // (Note we create a fresh instance for each incoming call, and 1329e25b785cc7edae682144536f58080edb3a374e4David Brown // stash away the call's phone number, since we can't necessarily 1339e25b785cc7edae682144536f58080edb3a374e4David Brown // assume this call will still be ringing when the user finally 13401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // chooses a response.) 1359e25b785cc7edae682144536f58080edb3a374e4David Brown 1369e25b785cc7edae682144536f58080edb3a374e4David Brown Connection c = ringingCall.getLatestConnection(); 1379e25b785cc7edae682144536f58080edb3a374e4David Brown if (DBG) log("- connection: " + c); 1389e25b785cc7edae682144536f58080edb3a374e4David Brown 1399e25b785cc7edae682144536f58080edb3a374e4David Brown // TODO: at this point we probably should re-check c.getAddress() 1409e25b785cc7edae682144536f58080edb3a374e4David Brown // and c.getNumberPresentation() for validity. (i.e. recheck the 1419e25b785cc7edae682144536f58080edb3a374e4David Brown // same cases in InCallTouchUi.showIncomingCallWidget() where we 1429e25b785cc7edae682144536f58080edb3a374e4David Brown // should have disallowed the "respond via SMS" feature in the 1439e25b785cc7edae682144536f58080edb3a374e4David Brown // first place.) 1449e25b785cc7edae682144536f58080edb3a374e4David Brown 1459e25b785cc7edae682144536f58080edb3a374e4David Brown String phoneNumber = c.getAddress(); 14601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("- phoneNumber: " + phoneNumber); // STOPSHIP: don't log PII 147524486403a387c324dee5aff7fb78ca784d15255David Brown lv.setOnItemClickListener(new RespondViaSmsItemClickListener(phoneNumber)); 14811544975bb29ede282333209e945a75430b221a8David Brown 149524486403a387c324dee5aff7fb78ca784d15255David Brown AlertDialog.Builder builder = new AlertDialog.Builder(mInCallScreen) 15001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .setCancelable(true) 151524486403a387c324dee5aff7fb78ca784d15255David Brown .setOnCancelListener(new RespondViaSmsCancelListener()) 15201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown .setView(lv); 153524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup = builder.create(); 154524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup.show(); 155524486403a387c324dee5aff7fb78ca784d15255David Brown } 1569e25b785cc7edae682144536f58080edb3a374e4David Brown 157524486403a387c324dee5aff7fb78ca784d15255David Brown /** 158524486403a387c324dee5aff7fb78ca784d15255David Brown * Dismiss the "Respond via SMS" popup if it's visible. 159524486403a387c324dee5aff7fb78ca784d15255David Brown * 160524486403a387c324dee5aff7fb78ca784d15255David Brown * This is safe to call even if the popup is already dismissed, and 161524486403a387c324dee5aff7fb78ca784d15255David Brown * even if you never called showRespondViaSmsPopup() in the first 162524486403a387c324dee5aff7fb78ca784d15255David Brown * place. 163524486403a387c324dee5aff7fb78ca784d15255David Brown */ 164524486403a387c324dee5aff7fb78ca784d15255David Brown public void dismissPopup() { 165524486403a387c324dee5aff7fb78ca784d15255David Brown if (mPopup != null) { 166524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup.dismiss(); // safe even if already dismissed 167524486403a387c324dee5aff7fb78ca784d15255David Brown mPopup = null; 168524486403a387c324dee5aff7fb78ca784d15255David Brown } 1699e25b785cc7edae682144536f58080edb3a374e4David Brown } 1709e25b785cc7edae682144536f58080edb3a374e4David Brown 1719e25b785cc7edae682144536f58080edb3a374e4David Brown /** 17201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * OnItemClickListener for the "Respond via SMS" popup. 1739e25b785cc7edae682144536f58080edb3a374e4David Brown */ 174524486403a387c324dee5aff7fb78ca784d15255David Brown public class RespondViaSmsItemClickListener implements AdapterView.OnItemClickListener { 1759e25b785cc7edae682144536f58080edb3a374e4David Brown // Phone number to send the SMS to. 1769e25b785cc7edae682144536f58080edb3a374e4David Brown private String mPhoneNumber; 1779e25b785cc7edae682144536f58080edb3a374e4David Brown 178524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsItemClickListener(String phoneNumber) { 1799e25b785cc7edae682144536f58080edb3a374e4David Brown mPhoneNumber = phoneNumber; 1809e25b785cc7edae682144536f58080edb3a374e4David Brown } 1819e25b785cc7edae682144536f58080edb3a374e4David Brown 18211544975bb29ede282333209e945a75430b221a8David Brown /** 18311544975bb29ede282333209e945a75430b221a8David Brown * Handles the user selecting an item from the popup. 18411544975bb29ede282333209e945a75430b221a8David Brown */ 18501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown public void onItemClick(AdapterView<?> parent, // The ListView 18601ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown View view, // The TextView that was clicked 18701ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown int position, 18801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown long id) { 18901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("RespondViaSmsItemClickListener.onItemClick(" + position + ")..."); 19001ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown String message = (String) parent.getItemAtPosition(position); 19101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("- message: '" + message + "'"); 19201ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown 19301ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // The "Custom" choice is a special case. 19401ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // (For now, it's guaranteed to be the last item.) 19501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (position == (parent.getCount() - 1)) { 1969e25b785cc7edae682144536f58080edb3a374e4David Brown // Take the user to the standard SMS compose UI. 197524486403a387c324dee5aff7fb78ca784d15255David Brown launchSmsCompose(mPhoneNumber); 1989e25b785cc7edae682144536f58080edb3a374e4David Brown } else { 19901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // Send the selected message immediately with no user interaction. 200524486403a387c324dee5aff7fb78ca784d15255David Brown sendText(mPhoneNumber, message); 2019e25b785cc7edae682144536f58080edb3a374e4David Brown } 2029e25b785cc7edae682144536f58080edb3a374e4David Brown 2039e25b785cc7edae682144536f58080edb3a374e4David Brown PhoneApp.getInstance().dismissCallScreen(); 2049e25b785cc7edae682144536f58080edb3a374e4David Brown } 2059e25b785cc7edae682144536f58080edb3a374e4David Brown } 2069e25b785cc7edae682144536f58080edb3a374e4David Brown 2079e25b785cc7edae682144536f58080edb3a374e4David Brown /** 20801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * OnCancelListener for the "Respond via SMS" popup. 20911544975bb29ede282333209e945a75430b221a8David Brown */ 210524486403a387c324dee5aff7fb78ca784d15255David Brown public class RespondViaSmsCancelListener implements DialogInterface.OnCancelListener { 211524486403a387c324dee5aff7fb78ca784d15255David Brown public RespondViaSmsCancelListener() { 21211544975bb29ede282333209e945a75430b221a8David Brown } 21311544975bb29ede282333209e945a75430b221a8David Brown 21411544975bb29ede282333209e945a75430b221a8David Brown /** 21501ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown * Handles the user canceling the popup, either by touching 21611544975bb29ede282333209e945a75430b221a8David Brown * outside the popup or by pressing Back. 21711544975bb29ede282333209e945a75430b221a8David Brown */ 21801ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown public void onCancel(DialogInterface dialog) { 21901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown if (DBG) log("RespondViaSmsCancelListener.onCancel()..."); 22011544975bb29ede282333209e945a75430b221a8David Brown 22101ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown // If the user cancels the popup, this presumably means that 22211544975bb29ede282333209e945a75430b221a8David Brown // they didn't actually mean to bring up the "Respond via SMS" 22311544975bb29ede282333209e945a75430b221a8David Brown // UI in the first place (and instead want to go back to the 22411544975bb29ede282333209e945a75430b221a8David Brown // state where they can either answer or reject the call.) 22511544975bb29ede282333209e945a75430b221a8David Brown // So restart the ringer and bring back the regular incoming 22611544975bb29ede282333209e945a75430b221a8David Brown // call UI. 22711544975bb29ede282333209e945a75430b221a8David Brown 22811544975bb29ede282333209e945a75430b221a8David Brown // This will have no effect if the incoming call isn't still ringing. 22901ad18615f81d3f75ec4eb7f9b7943ef9521d7c1David Brown PhoneApp.getInstance().notifier.restartRinger(); 23011544975bb29ede282333209e945a75430b221a8David Brown 23111544975bb29ede282333209e945a75430b221a8David Brown // We hid the MultiWaveView widget way back in 23211544975bb29ede282333209e945a75430b221a8David Brown // InCallTouchUi.onTrigger(), when the user first selected 23311544975bb29ede282333209e945a75430b221a8David Brown // the "SMS" trigger. 23411544975bb29ede282333209e945a75430b221a8David Brown // 23511544975bb29ede282333209e945a75430b221a8David Brown // To bring it back, just force the entire InCallScreen to 23611544975bb29ede282333209e945a75430b221a8David Brown // update itself based on the current telephony state. 23711544975bb29ede282333209e945a75430b221a8David Brown // (Assuming the incoming call is still ringing, this will 23811544975bb29ede282333209e945a75430b221a8David Brown // cause the incoming call widget to reappear.) 23911544975bb29ede282333209e945a75430b221a8David Brown mInCallScreen.requestUpdateScreen(); 24011544975bb29ede282333209e945a75430b221a8David Brown } 24111544975bb29ede282333209e945a75430b221a8David Brown } 24211544975bb29ede282333209e945a75430b221a8David Brown 24311544975bb29ede282333209e945a75430b221a8David Brown /** 2449e25b785cc7edae682144536f58080edb3a374e4David Brown * Sends a text message without any interaction from the user. 2459e25b785cc7edae682144536f58080edb3a374e4David Brown */ 246524486403a387c324dee5aff7fb78ca784d15255David Brown private void sendText(String phoneNumber, String message) { 2479e25b785cc7edae682144536f58080edb3a374e4David Brown // STOPSHIP: disable all logging of PII (everywhere in this file) 2489e25b785cc7edae682144536f58080edb3a374e4David Brown if (DBG) log("sendText: number " 2499e25b785cc7edae682144536f58080edb3a374e4David Brown + phoneNumber + ", message '" + message + "'"); 2509e25b785cc7edae682144536f58080edb3a374e4David Brown 2519e25b785cc7edae682144536f58080edb3a374e4David Brown // TODO: This code should use the new 2529e25b785cc7edae682144536f58080edb3a374e4David Brown // com.android.mms.intent.action.SENDTO_NO_CONFIRMATION 2539e25b785cc7edae682144536f58080edb3a374e4David Brown // intent once change https://android-git.corp.google.com/g/114664 2549e25b785cc7edae682144536f58080edb3a374e4David Brown // gets checked in. 2559e25b785cc7edae682144536f58080edb3a374e4David Brown // But use the old-school SmsManager API for now. 2569e25b785cc7edae682144536f58080edb3a374e4David Brown 2579e25b785cc7edae682144536f58080edb3a374e4David Brown final SmsManager smsManager = SmsManager.getDefault(); 2589e25b785cc7edae682144536f58080edb3a374e4David Brown smsManager.sendTextMessage(phoneNumber, 2599e25b785cc7edae682144536f58080edb3a374e4David Brown null /* scAddress; null means "use default" */, 2609e25b785cc7edae682144536f58080edb3a374e4David Brown message, 2619e25b785cc7edae682144536f58080edb3a374e4David Brown null /* sentIntent */, 2629e25b785cc7edae682144536f58080edb3a374e4David Brown null /* deliveryIntent */); 2639e25b785cc7edae682144536f58080edb3a374e4David Brown } 2649e25b785cc7edae682144536f58080edb3a374e4David Brown 2659e25b785cc7edae682144536f58080edb3a374e4David Brown /** 2669e25b785cc7edae682144536f58080edb3a374e4David Brown * Brings up the standard SMS compose UI. 2679e25b785cc7edae682144536f58080edb3a374e4David Brown */ 268524486403a387c324dee5aff7fb78ca784d15255David Brown private void launchSmsCompose(String phoneNumber) { 2699e25b785cc7edae682144536f58080edb3a374e4David Brown if (DBG) log("launchSmsCompose: number " + phoneNumber); 2709e25b785cc7edae682144536f58080edb3a374e4David Brown 2719e25b785cc7edae682144536f58080edb3a374e4David Brown // TODO: confirm with SMS guys that this is the correct intent to use. 27265454c803eb305c4740885ad4995a871b034a58aDavid Brown Uri uri = Uri.fromParts(Constants.SCHEME_SMS, phoneNumber, null); 2739e25b785cc7edae682144536f58080edb3a374e4David Brown Intent intent = new Intent(Intent.ACTION_VIEW, uri); 2749e25b785cc7edae682144536f58080edb3a374e4David Brown 2759e25b785cc7edae682144536f58080edb3a374e4David Brown if (DBG) log("- Launching SMS compose UI: " + intent); 276524486403a387c324dee5aff7fb78ca784d15255David Brown mInCallScreen.startActivity(intent); 2779e25b785cc7edae682144536f58080edb3a374e4David Brown 2789e25b785cc7edae682144536f58080edb3a374e4David Brown // TODO: One open issue here: if the user selects "Custom message" 2799e25b785cc7edae682144536f58080edb3a374e4David Brown // for an incoming call while the device was locked, and the user 2809e25b785cc7edae682144536f58080edb3a374e4David Brown // does *not* have a secure keyguard set, we bring up the 2819e25b785cc7edae682144536f58080edb3a374e4David Brown // non-secure keyguard at this point :-( 2829e25b785cc7edae682144536f58080edb3a374e4David Brown // Instead, we should immediately go to the SMS compose UI. 2839e25b785cc7edae682144536f58080edb3a374e4David Brown // 2849e25b785cc7edae682144536f58080edb3a374e4David Brown // I *believe* the fix is for the SMS compose activity to set the 2859e25b785cc7edae682144536f58080edb3a374e4David Brown // FLAG_DISMISS_KEYGUARD window flag (which will cause the 2869e25b785cc7edae682144536f58080edb3a374e4David Brown // keyguard to be dismissed *only* if it is not a secure lock 2879e25b785cc7edae682144536f58080edb3a374e4David Brown // keyguard.) 2889e25b785cc7edae682144536f58080edb3a374e4David Brown // 2899e25b785cc7edae682144536f58080edb3a374e4David Brown // But it there an equivalent way for me to accomplish that here, 2909e25b785cc7edae682144536f58080edb3a374e4David Brown // without needing to change the SMS app? 2919e25b785cc7edae682144536f58080edb3a374e4David Brown // 2929e25b785cc7edae682144536f58080edb3a374e4David Brown // In any case, I'm pretty sure the SMS UI should *not* to set 2939e25b785cc7edae682144536f58080edb3a374e4David Brown // FLAG_SHOW_WHEN_LOCKED, since we do want the force the user to 2949e25b785cc7edae682144536f58080edb3a374e4David Brown // enter their lock pattern or PIN at this point if they have a 2959e25b785cc7edae682144536f58080edb3a374e4David Brown // secure keyguard set. 2969e25b785cc7edae682144536f58080edb3a374e4David Brown } 2979e25b785cc7edae682144536f58080edb3a374e4David Brown 298f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 299f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** 300f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * Settings activity under "Call settings" to let you manage the 301f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * canned responses; see respond_via_sms_settings.xml 302f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown */ 303f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown public static class Settings extends PreferenceActivity 304f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown implements Preference.OnPreferenceChangeListener { 305f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown @Override 306f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown protected void onCreate(Bundle icicle) { 307f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown super.onCreate(icicle); 308f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("Settings: onCreate()..."); 309f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 310f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown getPreferenceManager().setSharedPreferencesName(SHARED_PREFERENCES_NAME); 311f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 312f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // This preference screen is ultra-simple; it's just 4 plain 313f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // <EditTextPreference>s, one for each of the 4 "canned responses". 314f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 315f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // The only nontrivial thing we do here is copy the text value of 316f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // each of those EditTextPreferences and use it as the preference's 317f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // "title" as well, so that the user will immediately see all 4 318f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // strings when they arrive here. 319f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 320f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Also, listen for change events (since we'll need to update the 321f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // title any time the user edits one of the strings.) 322f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 323f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown addPreferencesFromResource(R.xml.respond_via_sms_settings); 324f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 325f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown EditTextPreference pref; 326f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_1); 327f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 328f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 329f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 330f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_2); 331f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 332f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 333f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 334f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_3); 335f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 336f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 337f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 338f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref = (EditTextPreference) findPreference(KEY_CANNED_RESPONSE_PREF_4); 339f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle(pref.getText()); 340f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setOnPreferenceChangeListener(this); 3412c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 3422c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa ActionBar actionBar = getActionBar(); 3432c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa if (actionBar != null) { 3442c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa // android.R.id.home will be triggered in onOptionsItemSelected() 3452c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa actionBar.setDisplayHomeAsUpEnabled(true); 3462c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 347f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 348f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 349f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference.OnPreferenceChangeListener implementation 350f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown public boolean onPreferenceChange(Preference preference, Object newValue) { 351f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("onPreferenceChange: key = " + preference.getKey()); 352f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log(" preference = '" + preference + "'"); 353f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log(" newValue = '" + newValue + "'"); 354f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 355f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown EditTextPreference pref = (EditTextPreference) preference; 356f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 357f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Copy the new text over to the title, just like in onCreate(). 358f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // (Watch out: onPreferenceChange() is called *before* the 359f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Preference itself gets updated, so we need to use newValue here 360f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // rather than pref.getText().) 361f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown pref.setTitle((String) newValue); 362f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 363f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown return true; // means it's OK to update the state of the Preference with the new value 364f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 3652c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa 3662c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa @Override 3672c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa public boolean onOptionsItemSelected(MenuItem item) { 3682c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa final int itemId = item.getItemId(); 3692c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa if (itemId == android.R.id.home) { // See ActionBar#setDisplayHomeAsUpEnabled() 3702c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa CallFeaturesSetting.goUpToTopLevelSetting(this); 3712c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa return true; 3722c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 3732c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa return super.onOptionsItemSelected(item); 3742c8c40738e9b8a8e767aa061721ebaa5b5591a4cDaisuke Miyakawa } 375f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 376f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 377f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown /** 378f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * Read the (customizable) canned responses from SharedPreferences, 379f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * or from defaults if the user has never actually brought up 380f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * the Settings UI. 381f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * 382f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * This method does disk I/O (reading the SharedPreferences file) 383f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * so don't call it from the main thread. 384f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * 385f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown * @see RespondViaSmsManager$Settings 386f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown */ 387f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private String[] loadCannedResponses() { 388f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown if (DBG) log("loadCannedResponses()..."); 389f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 390f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown SharedPreferences prefs = 391f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown mInCallScreen.getSharedPreferences(SHARED_PREFERENCES_NAME, 392f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown Context.MODE_PRIVATE); 393f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown final Resources res = mInCallScreen.getResources(); 394f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 395f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown String[] responses = new String[NUM_CANNED_RESPONSES]; 396f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 397f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Note the default values here must agree with the corresponding 398f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // android:defaultValue attributes in respond_via_sms_settings.xml. 399f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 400f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[0] = prefs.getString(KEY_CANNED_RESPONSE_PREF_1, 401f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_1)); 402f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[1] = prefs.getString(KEY_CANNED_RESPONSE_PREF_2, 403f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_2)); 404f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[2] = prefs.getString(KEY_CANNED_RESPONSE_PREF_3, 405f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_3)); 406f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown responses[3] = prefs.getString(KEY_CANNED_RESPONSE_PREF_4, 407f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown res.getString(R.string.respond_via_sms_canned_response_4)); 408f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown return responses; 409f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown } 410f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 411f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // TODO: Don't call loadCannedResponses() from the UI thread. 412f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 413f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // We should either (1) kick off a background task when the call first 414f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // starts ringing (probably triggered from the InCallScreen 415f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // onNewRingingConnection() method) which would run loadCannedResponses() 416f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // and stash the result away in mCannedResponses, or (2) use an 417f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // OnSharedPreferenceChangeListener to listen for changes to this 418f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // SharedPreferences instance, and use that to kick off the background task. 419f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 420f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // In either case: 421f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 422f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // - Make sure we recover sanely if mCannedResponses is still null when it's 423f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // actually time to show the popup (i.e. if the background task was too 424f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // slow, or if the background task never got started for some reason) 425f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 426f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // - Make sure that all setting and getting of mCannedResponses happens 427f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // inside a synchronized block 428f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // 429f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // - If we kick off the background task when the call first starts ringing, 430f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // consider delaying that until the incoming-call UI actually comes to the 431f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // foreground; this way we won't steal any CPU away from the caller-id 432f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // query. Maybe do it from InCallScreen.onResume()? 433f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown // Or InCallTouchUi.showIncomingCallWidget()? 434f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 435f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown 436f40c04a87e30bcfb795a5879d932418f0821fdb0David Brown private static void log(String msg) { 4379e25b785cc7edae682144536f58080edb3a374e4David Brown Log.d(TAG, msg); 4389e25b785cc7edae682144536f58080edb3a374e4David Brown } 4399e25b785cc7edae682144536f58080edb3a374e4David Brown} 440