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