DO NOT MERGE. Port "respond via SMS message" feature to new Telecomm. (2/4)

Bug: 15275904
Bug: 15196474
Change-Id: I3e2ee62b3e32ad5715457fee1b0e714f88ecea8e
diff --git a/src/com/android/telecomm/RespondViaSmsManager.java b/src/com/android/telecomm/RespondViaSmsManager.java
new file mode 100644
index 0000000..bf1a2a5
--- /dev/null
+++ b/src/com/android/telecomm/RespondViaSmsManager.java
@@ -0,0 +1,189 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.telecomm;
+
+import com.android.internal.os.SomeArgs;
+import com.android.internal.telephony.SmsApplication;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.content.res.Resources;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.telecomm.Response;
+import android.telephony.TelephonyManager;
+import android.widget.Toast;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Helper class to manage the "Respond via Message" feature for incoming calls.
+ */
+public class RespondViaSmsManager extends CallsManagerListenerBase {
+    private static final String SCHEME_SMSTO = "smsto";
+
+    /** SharedPreferences file name for our persistent settings. */
+    private static final String SHARED_PREFERENCES_NAME = "respond_via_sms_prefs";
+
+    // Preference keys for the 4 "canned responses"; see RespondViaSmsManager$Settings.
+    // Since (for now at least) the number of messages is fixed at 4, and since
+    // SharedPreferences can't deal with arrays anyway, just store the messages
+    // as 4 separate strings.
+    private static final int NUM_CANNED_RESPONSES = 4;
+    private static final String KEY_CANNED_RESPONSE_PREF_1 = "canned_response_pref_1";
+    private static final String KEY_CANNED_RESPONSE_PREF_2 = "canned_response_pref_2";
+    private static final String KEY_CANNED_RESPONSE_PREF_3 = "canned_response_pref_3";
+    private static final String KEY_CANNED_RESPONSE_PREF_4 = "canned_response_pref_4";
+
+    private static final int MSG_CANNED_TEXT_MESSAGES_READY = 1;
+    private static final int MSG_SHOW_SENT_TOAST = 2;
+
+    private static final RespondViaSmsManager sInstance = new RespondViaSmsManager();
+
+    private final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_CANNED_TEXT_MESSAGES_READY:
+                    SomeArgs args = (SomeArgs) msg.obj;
+                    try {
+                        Response<Void, List<String>> response =
+                                (Response<Void, List<String>>) args.arg1;
+                        List<String> textMessages =
+                                (List<String>) args.arg2;
+                        if (textMessages != null) {
+                            response.onResult(null, textMessages);
+                        } else {
+                            response.onError(null, 0, null);
+                        }
+                    } finally {
+                        args.recycle();
+                    }
+                    break;
+                case MSG_SHOW_SENT_TOAST:
+                    showMessageSentToast((String) msg.obj);
+                    break;
+            }
+        }
+    };
+
+    public static RespondViaSmsManager getInstance() { return sInstance; }
+
+    private RespondViaSmsManager() {}
+
+    /**
+     * Read the (customizable) canned responses from SharedPreferences,
+     * or from defaults if the user has never actually brought up
+     * the Settings UI.
+     *
+     * The interface of this method is asynchronous since it does disk I/O.
+     *
+     * @param response An object to receive an async reply, which will be called from
+     *                 the main thread.
+     */
+    public void loadCannedTextMessages(final Response<Void, List<String>> response) {
+        new Thread() {
+            @Override
+            public void run() {
+                Log.d(RespondViaSmsManager.this, "loadCannedResponses() starting");
+                final SharedPreferences prefs = TelecommApp.getInstance().getSharedPreferences(
+                        SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE);
+                final Resources res = TelecommApp.getInstance().getInstance().getResources();
+
+                final ArrayList<String> textMessages = new ArrayList<String>(NUM_CANNED_RESPONSES);
+
+                // Note the default values here must agree with the corresponding
+                // android:defaultValue attributes in respond_via_sms_settings.xml.
+
+                textMessages.add(0, prefs.getString(KEY_CANNED_RESPONSE_PREF_1,
+                        res.getString(R.string.respond_via_sms_canned_response_1)));
+                textMessages.add(1, prefs.getString(KEY_CANNED_RESPONSE_PREF_2,
+                        res.getString(R.string.respond_via_sms_canned_response_2)));
+                textMessages.add(2, prefs.getString(KEY_CANNED_RESPONSE_PREF_3,
+                        res.getString(R.string.respond_via_sms_canned_response_3)));
+                textMessages.add(3, prefs.getString(KEY_CANNED_RESPONSE_PREF_4,
+                        res.getString(R.string.respond_via_sms_canned_response_4)));
+
+                Log.d(RespondViaSmsManager.this,
+                        "loadCannedResponses() completed, found responses: %s",
+                        textMessages.toString());
+
+                SomeArgs args = SomeArgs.obtain();
+                args.arg1 = response;
+                args.arg2 = textMessages;
+                mHandler.obtainMessage(MSG_CANNED_TEXT_MESSAGES_READY, args).sendToTarget();
+            }
+        }.start();
+    }
+
+    @Override
+    public void onIncomingCallRejected(Call call, boolean rejectWithMessage, String textMessage) {
+        if (rejectWithMessage) {
+            rejectCallWithMessage(call.getHandle().getSchemeSpecificPart(), textMessage);
+        }
+    }
+
+    private void showMessageSentToast(final String phoneNumber) {
+        // ...and show a brief confirmation to the user (since
+        // otherwise it's hard to be sure that anything actually
+        // happened.)
+        final Resources res = TelecommApp.getInstance().getResources();
+        final String formatString = res.getString(
+                R.string.respond_via_sms_confirmation_format);
+        final String confirmationMsg = String.format(formatString, phoneNumber);
+        Toast.makeText(TelecommApp.getInstance(), confirmationMsg,
+                Toast.LENGTH_LONG).show();
+
+        // TODO: If the device is locked, this toast won't actually ever
+        // be visible!  (That's because we're about to dismiss the call
+        // screen, which means that the device will return to the
+        // keyguard.  But toasts aren't visible on top of the keyguard.)
+        // Possible fixes:
+        // (1) Is it possible to allow a specific Toast to be visible
+        //     on top of the keyguard?
+        // (2) Artificially delay the dismissCallScreen() call by 3
+        //     seconds to allow the toast to be seen?
+        // (3) Don't use a toast at all; instead use a transient state
+        //     of the InCallScreen (perhaps via the InCallUiState
+        //     progressIndication feature), and have that state be
+        //     visible for 3 seconds before calling dismissCallScreen().
+    }
+
+    /**
+     * Reject the call with the specified message. If message is null this call is ignored.
+     */
+    private void rejectCallWithMessage(String phoneNumber, String textMessage) {
+        if (textMessage != null) {
+            final ComponentName component =
+                    SmsApplication.getDefaultRespondViaMessageApplication(
+                            TelecommApp.getInstance(), true /*updateIfNeeded*/);
+            if (component != null) {
+                // Build and send the intent
+                final Uri uri = Uri.fromParts(SCHEME_SMSTO, phoneNumber, null);
+                final Intent intent = new Intent(TelephonyManager.ACTION_RESPOND_VIA_MESSAGE, uri);
+                intent.putExtra(Intent.EXTRA_TEXT, textMessage);
+                mHandler.obtainMessage(MSG_SHOW_SENT_TOAST, phoneNumber).sendToTarget();
+                intent.setComponent(component);
+                TelecommApp.getInstance().startService(intent);
+            }
+        }
+    }
+}