• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.server.telecom;
18 
19 // TODO: Needed for move to system service: import com.android.internal.R;
20 import com.android.internal.os.SomeArgs;
21 import com.android.internal.telephony.SmsApplication;
22 
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.Intent;
26 import android.content.SharedPreferences;
27 import android.content.res.Resources;
28 import android.net.Uri;
29 import android.os.Handler;
30 import android.os.Message;
31 import android.telecom.Response;
32 import android.telephony.TelephonyManager;
33 import android.widget.Toast;
34 
35 import java.util.ArrayList;
36 import java.util.List;
37 
38 /**
39  * Helper class to manage the "Respond via Message" feature for incoming calls.
40  */
41 public class RespondViaSmsManager extends CallsManagerListenerBase {
42     private static final int MSG_CANNED_TEXT_MESSAGES_READY = 1;
43     private static final int MSG_SHOW_SENT_TOAST = 2;
44 
45     private static final RespondViaSmsManager sInstance = new RespondViaSmsManager();
46 
47     private final Handler mHandler = new Handler() {
48         @Override
49         public void handleMessage(Message msg) {
50             switch (msg.what) {
51                 case MSG_CANNED_TEXT_MESSAGES_READY: {
52                     SomeArgs args = (SomeArgs) msg.obj;
53                     try {
54                         Response<Void, List<String>> response =
55                                 (Response<Void, List<String>>) args.arg1;
56                         List<String> textMessages =
57                                 (List<String>) args.arg2;
58                         if (textMessages != null) {
59                             response.onResult(null, textMessages);
60                         } else {
61                             response.onError(null, 0, null);
62                         }
63                     } finally {
64                         args.recycle();
65                     }
66                     break;
67                 }
68                 case MSG_SHOW_SENT_TOAST: {
69                     SomeArgs args = (SomeArgs) msg.obj;
70                     try {
71                         String toastMessage = (String) args.arg1;
72                         Context context = (Context) args.arg2;
73                         showMessageSentToast(toastMessage, context);
74                     } finally {
75                         args.recycle();
76                     }
77                     break;
78                 }
79             }
80         }
81     };
82 
getInstance()83     public static RespondViaSmsManager getInstance() { return sInstance; }
84 
RespondViaSmsManager()85     private RespondViaSmsManager() {}
86 
87     /**
88      * Read the (customizable) canned responses from SharedPreferences,
89      * or from defaults if the user has never actually brought up
90      * the Settings UI.
91      *
92      * The interface of this method is asynchronous since it does disk I/O.
93      *
94      * @param response An object to receive an async reply, which will be called from
95      *                 the main thread.
96      * @param context The context.
97      */
loadCannedTextMessages(final Response<Void, List<String>> response, final Context context)98     public void loadCannedTextMessages(final Response<Void, List<String>> response,
99             final Context context) {
100         new Thread() {
101             @Override
102             public void run() {
103                 Log.d(RespondViaSmsManager.this, "loadCannedResponses() starting");
104 
105                 // This function guarantees that QuickResponses will be in our
106                 // SharedPreferences with the proper values considering there may be
107                 // old QuickResponses in Telephony pre L.
108                 QuickResponseUtils.maybeMigrateLegacyQuickResponses(context);
109 
110                 final SharedPreferences prefs = context.getSharedPreferences(
111                         QuickResponseUtils.SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
112                 final Resources res = context.getResources();
113 
114                 final ArrayList<String> textMessages = new ArrayList<>(
115                         QuickResponseUtils.NUM_CANNED_RESPONSES);
116 
117                 // Note the default values here must agree with the corresponding
118                 // android:defaultValue attributes in respond_via_sms_settings.xml.
119                 textMessages.add(0, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_1,
120                         res.getString(R.string.respond_via_sms_canned_response_1)));
121                 textMessages.add(1, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_2,
122                         res.getString(R.string.respond_via_sms_canned_response_2)));
123                 textMessages.add(2, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_3,
124                         res.getString(R.string.respond_via_sms_canned_response_3)));
125                 textMessages.add(3, prefs.getString(QuickResponseUtils.KEY_CANNED_RESPONSE_PREF_4,
126                         res.getString(R.string.respond_via_sms_canned_response_4)));
127 
128                 Log.d(RespondViaSmsManager.this,
129                         "loadCannedResponses() completed, found responses: %s",
130                         textMessages.toString());
131 
132                 SomeArgs args = SomeArgs.obtain();
133                 args.arg1 = response;
134                 args.arg2 = textMessages;
135                 mHandler.obtainMessage(MSG_CANNED_TEXT_MESSAGES_READY, args).sendToTarget();
136             }
137         }.start();
138     }
139 
140     @Override
onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage)141     public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
142         if (rejectWithMessage) {
143 
144             rejectCallWithMessage(call.getContext(), call.getHandle().getSchemeSpecificPart(),
145                     textMessage);
146         }
147     }
148 
showMessageSentToast(final String phoneNumber, final Context context)149     private void showMessageSentToast(final String phoneNumber, final Context context) {
150         // ...and show a brief confirmation to the user (since
151         // otherwise it's hard to be sure that anything actually
152         // happened.)
153         final Resources res = context.getResources();
154         final String formatString = res.getString(
155                 R.string.respond_via_sms_confirmation_format);
156         final String confirmationMsg = String.format(formatString, phoneNumber);
157         Toast.makeText(context, confirmationMsg,
158                 Toast.LENGTH_LONG).show();
159 
160         // TODO: If the device is locked, this toast won't actually ever
161         // be visible!  (That's because we're about to dismiss the call
162         // screen, which means that the device will return to the
163         // keyguard.  But toasts aren't visible on top of the keyguard.)
164         // Possible fixes:
165         // (1) Is it possible to allow a specific Toast to be visible
166         //     on top of the keyguard?
167         // (2) Artificially delay the dismissCallScreen() call by 3
168         //     seconds to allow the toast to be seen?
169         // (3) Don't use a toast at all; instead use a transient state
170         //     of the InCallScreen (perhaps via the InCallUiState
171         //     progressIndication feature), and have that state be
172         //     visible for 3 seconds before calling dismissCallScreen().
173     }
174 
175     /**
176      * Reject the call with the specified message. If message is null this call is ignored.
177      */
rejectCallWithMessage(Context context, String phoneNumber, String textMessage)178     private void rejectCallWithMessage(Context context, String phoneNumber, String textMessage) {
179         if (textMessage != null) {
180             final ComponentName component =
181                     SmsApplication.getDefaultRespondViaMessageApplication(context,
182                             true /*updateIfNeeded*/);
183             if (component != null) {
184                 // Build and send the intent
185                 final Uri uri = Uri.fromParts(Constants.SCHEME_SMSTO, phoneNumber, null);
186                 final Intent intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, uri);
187                 intent.putExtra(Intent.EXTRA_TEXT, textMessage);
188 
189                 SomeArgs args = SomeArgs.obtain();
190                 args.arg1 = phoneNumber;
191                 args.arg2 = context;
192                 mHandler.obtainMessage(MSG_SHOW_SENT_TOAST, args).sendToTarget();
193                 intent.setComponent(component);
194                 context.startService(intent);
195             }
196         }
197     }
198 }
199