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