blob: 8618af0edac30797012d646f1d5ba8e33bc6e76e [file] [log] [blame]
Ihab Awadff7493a2014-06-10 13:47:44 -07001/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.telecomm;
18
19import com.android.internal.os.SomeArgs;
20import com.android.internal.telephony.SmsApplication;
21
22import android.content.ComponentName;
23import android.content.Context;
24import android.content.Intent;
25import android.content.SharedPreferences;
26import android.content.res.Resources;
27import android.net.Uri;
28import android.os.Handler;
29import android.os.Message;
30import android.telecomm.Response;
31import android.telephony.TelephonyManager;
32import android.widget.Toast;
33
34import java.util.ArrayList;
35import java.util.List;
36
37/**
38 * Helper class to manage the "Respond via Message" feature for incoming calls.
39 */
40public class RespondViaSmsManager extends CallsManagerListenerBase {
41 private static final String SCHEME_SMSTO = "smsto";
42
43 /** SharedPreferences file name for our persistent settings. */
44 private static final String SHARED_PREFERENCES_NAME = "respond_via_sms_prefs";
45
46 // Preference keys for the 4 "canned responses"; see RespondViaSmsManager$Settings.
47 // Since (for now at least) the number of messages is fixed at 4, and since
48 // SharedPreferences can't deal with arrays anyway, just store the messages
49 // as 4 separate strings.
50 private static final int NUM_CANNED_RESPONSES = 4;
51 private static final String KEY_CANNED_RESPONSE_PREF_1 = "canned_response_pref_1";
52 private static final String KEY_CANNED_RESPONSE_PREF_2 = "canned_response_pref_2";
53 private static final String KEY_CANNED_RESPONSE_PREF_3 = "canned_response_pref_3";
54 private static final String KEY_CANNED_RESPONSE_PREF_4 = "canned_response_pref_4";
55
56 private static final int MSG_CANNED_TEXT_MESSAGES_READY = 1;
57 private static final int MSG_SHOW_SENT_TOAST = 2;
58
59 private static final RespondViaSmsManager sInstance = new RespondViaSmsManager();
60
61 private final Handler mHandler = new Handler() {
62 @Override
63 public void handleMessage(Message msg) {
64 switch (msg.what) {
65 case MSG_CANNED_TEXT_MESSAGES_READY:
66 SomeArgs args = (SomeArgs) msg.obj;
67 try {
68 Response<Void, List<String>> response =
69 (Response<Void, List<String>>) args.arg1;
70 List<String> textMessages =
71 (List<String>) args.arg2;
72 if (textMessages != null) {
73 response.onResult(null, textMessages);
74 } else {
75 response.onError(null, 0, null);
76 }
77 } finally {
78 args.recycle();
79 }
80 break;
81 case MSG_SHOW_SENT_TOAST:
82 showMessageSentToast((String) msg.obj);
83 break;
84 }
85 }
86 };
87
88 public static RespondViaSmsManager getInstance() { return sInstance; }
89
90 private RespondViaSmsManager() {}
91
92 /**
93 * Read the (customizable) canned responses from SharedPreferences,
94 * or from defaults if the user has never actually brought up
95 * the Settings UI.
96 *
97 * The interface of this method is asynchronous since it does disk I/O.
98 *
99 * @param response An object to receive an async reply, which will be called from
100 * the main thread.
101 */
102 public void loadCannedTextMessages(final Response<Void, List<String>> response) {
103 new Thread() {
104 @Override
105 public void run() {
106 Log.d(RespondViaSmsManager.this, "loadCannedResponses() starting");
107 final SharedPreferences prefs = TelecommApp.getInstance().getSharedPreferences(
108 SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
109 final Resources res = TelecommApp.getInstance().getInstance().getResources();
110
Sailesh Nepal905dfba2014-07-14 08:20:41 -0700111 final ArrayList<String> textMessages = new ArrayList<>(NUM_CANNED_RESPONSES);
Ihab Awadff7493a2014-06-10 13:47:44 -0700112
113 // Note the default values here must agree with the corresponding
114 // android:defaultValue attributes in respond_via_sms_settings.xml.
115
116 textMessages.add(0, prefs.getString(KEY_CANNED_RESPONSE_PREF_1,
117 res.getString(R.string.respond_via_sms_canned_response_1)));
118 textMessages.add(1, prefs.getString(KEY_CANNED_RESPONSE_PREF_2,
119 res.getString(R.string.respond_via_sms_canned_response_2)));
120 textMessages.add(2, prefs.getString(KEY_CANNED_RESPONSE_PREF_3,
121 res.getString(R.string.respond_via_sms_canned_response_3)));
122 textMessages.add(3, prefs.getString(KEY_CANNED_RESPONSE_PREF_4,
123 res.getString(R.string.respond_via_sms_canned_response_4)));
124
125 Log.d(RespondViaSmsManager.this,
126 "loadCannedResponses() completed, found responses: %s",
127 textMessages.toString());
128
129 SomeArgs args = SomeArgs.obtain();
130 args.arg1 = response;
131 args.arg2 = textMessages;
132 mHandler.obtainMessage(MSG_CANNED_TEXT_MESSAGES_READY, args).sendToTarget();
133 }
134 }.start();
135 }
136
137 @Override
138 public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
139 if (rejectWithMessage) {
140 rejectCallWithMessage(call.getHandle().getSchemeSpecificPart(), textMessage);
141 }
142 }
143
144 private void showMessageSentToast(final String phoneNumber) {
145 // ...and show a brief confirmation to the user (since
146 // otherwise it's hard to be sure that anything actually
147 // happened.)
148 final Resources res = TelecommApp.getInstance().getResources();
149 final String formatString = res.getString(
150 R.string.respond_via_sms_confirmation_format);
151 final String confirmationMsg = String.format(formatString, phoneNumber);
152 Toast.makeText(TelecommApp.getInstance(), confirmationMsg,
153 Toast.LENGTH_LONG).show();
154
155 // TODO: If the device is locked, this toast won't actually ever
156 // be visible! (That's because we're about to dismiss the call
157 // screen, which means that the device will return to the
158 // keyguard. But toasts aren't visible on top of the keyguard.)
159 // Possible fixes:
160 // (1) Is it possible to allow a specific Toast to be visible
161 // on top of the keyguard?
162 // (2) Artificially delay the dismissCallScreen() call by 3
163 // seconds to allow the toast to be seen?
164 // (3) Don't use a toast at all; instead use a transient state
165 // of the InCallScreen (perhaps via the InCallUiState
166 // progressIndication feature), and have that state be
167 // visible for 3 seconds before calling dismissCallScreen().
168 }
169
170 /**
171 * Reject the call with the specified message. If message is null this call is ignored.
172 */
173 private void rejectCallWithMessage(String phoneNumber, String textMessage) {
174 if (textMessage != null) {
175 final ComponentName component =
176 SmsApplication.getDefaultRespondViaMessageApplication(
177 TelecommApp.getInstance(), true /*updateIfNeeded*/);
178 if (component != null) {
179 // Build and send the intent
180 final Uri uri = Uri.fromParts(SCHEME_SMSTO, phoneNumber, null);
181 final Intent intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, uri);
182 intent.putExtra(Intent.EXTRA_TEXT, textMessage);
183 mHandler.obtainMessage(MSG_SHOW_SENT_TOAST, phoneNumber).sendToTarget();
184 intent.setComponent(component);
185 TelecommApp.getInstance().startService(intent);
186 }
187 }
188 }
189}