1package com.android.phone;
2
3import com.android.internal.telephony.CallForwardInfo;
4import com.android.internal.telephony.CommandException;
5import com.android.internal.telephony.CommandsInterface;
6import com.android.internal.telephony.Phone;
7
8import android.app.AlertDialog;
9import android.content.Context;
10import android.content.DialogInterface;
11import android.content.res.TypedArray;
12import android.os.AsyncResult;
13import android.os.Handler;
14import android.os.Message;
15import android.telephony.PhoneNumberUtils;
16import android.text.BidiFormatter;
17import android.text.SpannableString;
18import android.text.TextDirectionHeuristics;
19import android.text.TextUtils;
20import android.util.AttributeSet;
21import android.util.Log;
22import android.view.View;
23
24import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
25import static com.android.phone.TimeConsumingPreferenceActivity.EXCEPTION_ERROR;
26
27public class CallForwardEditPreference extends EditPhoneNumberPreference {
28    private static final String LOG_TAG = "CallForwardEditPreference";
29    private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
30
31    private static final String SRC_TAGS[]       = {"{0}"};
32    private CharSequence mSummaryOnTemplate;
33    /**
34     * Remembers which button was clicked by a user. If no button is clicked yet, this should have
35     * {@link DialogInterface#BUTTON_NEGATIVE}, meaning "cancel".
36     *
37     * TODO: consider removing this variable and having getButtonClicked() in
38     * EditPhoneNumberPreference instead.
39     */
40    private int mButtonClicked;
41    private int mServiceClass;
42    private MyHandler mHandler = new MyHandler();
43    int reason;
44    private Phone mPhone;
45    CallForwardInfo callForwardInfo;
46    private TimeConsumingPreferenceListener mTcpListener;
47
48    public CallForwardEditPreference(Context context, AttributeSet attrs) {
49        super(context, attrs);
50
51        mSummaryOnTemplate = this.getSummaryOn();
52
53        TypedArray a = context.obtainStyledAttributes(attrs,
54                R.styleable.CallForwardEditPreference, 0, R.style.EditPhoneNumberPreference);
55        mServiceClass = a.getInt(R.styleable.CallForwardEditPreference_serviceClass,
56                CommandsInterface.SERVICE_CLASS_VOICE);
57        reason = a.getInt(R.styleable.CallForwardEditPreference_reason,
58                CommandsInterface.CF_REASON_UNCONDITIONAL);
59        a.recycle();
60
61        if (DBG) Log.d(LOG_TAG, "mServiceClass=" + mServiceClass + ", reason=" + reason);
62    }
63
64    public CallForwardEditPreference(Context context) {
65        this(context, null);
66    }
67
68    void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
69        mPhone = phone;
70        mTcpListener = listener;
71
72        if (!skipReading) {
73            mPhone.getCallForwardingOption(reason,
74                    mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
75                            // unused in this case
76                            CommandsInterface.CF_ACTION_DISABLE,
77                            MyHandler.MESSAGE_GET_CF, null));
78            if (mTcpListener != null) {
79                mTcpListener.onStarted(this, true);
80            }
81        }
82    }
83
84    @Override
85    protected void onBindDialogView(View view) {
86        // default the button clicked to be the cancel button.
87        mButtonClicked = DialogInterface.BUTTON_NEGATIVE;
88        super.onBindDialogView(view);
89    }
90
91    @Override
92    public void onClick(DialogInterface dialog, int which) {
93        super.onClick(dialog, which);
94        mButtonClicked = which;
95    }
96
97    @Override
98    protected void onDialogClosed(boolean positiveResult) {
99        super.onDialogClosed(positiveResult);
100
101        if (DBG) Log.d(LOG_TAG, "mButtonClicked=" + mButtonClicked
102                + ", positiveResult=" + positiveResult);
103        // Ignore this event if the user clicked the cancel button, or if the dialog is dismissed
104        // without any button being pressed (back button press or click event outside the dialog).
105        if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
106            int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
107                    CommandsInterface.CF_ACTION_REGISTRATION :
108                    CommandsInterface.CF_ACTION_DISABLE;
109            int time = (reason != CommandsInterface.CF_REASON_NO_REPLY) ? 0 : 20;
110            final String number = getPhoneNumber();
111
112            if (DBG) Log.d(LOG_TAG, "callForwardInfo=" + callForwardInfo);
113
114            if (action == CommandsInterface.CF_ACTION_REGISTRATION
115                    && callForwardInfo != null
116                    && callForwardInfo.status == 1
117                    && number.equals(callForwardInfo.number)) {
118                // no change, do nothing
119                if (DBG) Log.d(LOG_TAG, "no change, do nothing");
120            } else {
121                // set to network
122                if (DBG) Log.d(LOG_TAG, "reason=" + reason + ", action=" + action
123                        + ", number=" + number);
124
125                // Display no forwarding number while we're waiting for
126                // confirmation
127                setSummaryOn("");
128
129                // the interface of Phone.setCallForwardingOption has error:
130                // should be action, reason...
131                mPhone.setCallForwardingOption(action,
132                        reason,
133                        number,
134                        time,
135                        mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
136                                action,
137                                MyHandler.MESSAGE_SET_CF));
138
139                if (mTcpListener != null) {
140                    mTcpListener.onStarted(this, false);
141                }
142            }
143        }
144    }
145
146    void handleCallForwardResult(CallForwardInfo cf) {
147        callForwardInfo = cf;
148        if (DBG) Log.d(LOG_TAG, "handleGetCFResponse done, callForwardInfo=" + callForwardInfo);
149
150        setToggled(callForwardInfo.status == 1);
151        setPhoneNumber(callForwardInfo.number);
152    }
153
154    private void updateSummaryText() {
155        if (isToggled()) {
156            final String number = getRawPhoneNumber();
157            if (number != null && number.length() > 0) {
158                // Wrap the number to preserve presentation in RTL languages.
159                String wrappedNumber = BidiFormatter.getInstance().unicodeWrap(
160                        number, TextDirectionHeuristics.LTR);
161                String values[] = { wrappedNumber };
162                String summaryOn = String.valueOf(
163                        TextUtils.replace(mSummaryOnTemplate, SRC_TAGS, values));
164                int start = summaryOn.indexOf(wrappedNumber);
165
166                SpannableString spannableSummaryOn = new SpannableString(summaryOn);
167                PhoneNumberUtils.addTtsSpan(spannableSummaryOn,
168                        start, start + wrappedNumber.length());
169                setSummaryOn(spannableSummaryOn);
170            } else {
171                setSummaryOn(getContext().getString(R.string.sum_cfu_enabled_no_number));
172            }
173        }
174
175    }
176
177    // Message protocol:
178    // what: get vs. set
179    // arg1: action -- register vs. disable
180    // arg2: get vs. set for the preceding request
181    private class MyHandler extends Handler {
182        static final int MESSAGE_GET_CF = 0;
183        static final int MESSAGE_SET_CF = 1;
184
185        @Override
186        public void handleMessage(Message msg) {
187            switch (msg.what) {
188                case MESSAGE_GET_CF:
189                    handleGetCFResponse(msg);
190                    break;
191                case MESSAGE_SET_CF:
192                    handleSetCFResponse(msg);
193                    break;
194            }
195        }
196
197        private void handleGetCFResponse(Message msg) {
198            if (DBG) Log.d(LOG_TAG, "handleGetCFResponse: done");
199
200            mTcpListener.onFinished(CallForwardEditPreference.this, msg.arg2 != MESSAGE_SET_CF);
201
202            AsyncResult ar = (AsyncResult) msg.obj;
203
204            callForwardInfo = null;
205            if (ar.exception != null) {
206                if (DBG) Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
207                if (ar.exception instanceof CommandException) {
208                    mTcpListener.onException(CallForwardEditPreference.this,
209                            (CommandException) ar.exception);
210                } else {
211                    // Most likely an ImsException and we can't handle it the same way as
212                    // a CommandException. The best we can do is to handle the exception
213                    // the same way as mTcpListener.onException() does when it is not of type
214                    // FDN_CHECK_FAILURE.
215                    mTcpListener.onError(CallForwardEditPreference.this, EXCEPTION_ERROR);
216                }
217            } else {
218                if (ar.userObj instanceof Throwable) {
219                    mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
220                }
221                CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
222                if (cfInfoArray.length == 0) {
223                    if (DBG) Log.d(LOG_TAG, "handleGetCFResponse: cfInfoArray.length==0");
224                    setEnabled(false);
225                    mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
226                } else {
227                    for (int i = 0, length = cfInfoArray.length; i < length; i++) {
228                        if (DBG) Log.d(LOG_TAG, "handleGetCFResponse, cfInfoArray[" + i + "]="
229                                + cfInfoArray[i]);
230                        if ((mServiceClass & cfInfoArray[i].serviceClass) != 0) {
231                            // corresponding class
232                            CallForwardInfo info = cfInfoArray[i];
233                            handleCallForwardResult(info);
234
235                            // Show an alert if we got a success response but
236                            // with unexpected values.
237                            // Currently only handle the fail-to-disable case
238                            // since we haven't observed fail-to-enable.
239                            if (msg.arg2 == MESSAGE_SET_CF &&
240                                    msg.arg1 == CommandsInterface.CF_ACTION_DISABLE &&
241                                    info.status == 1) {
242                                CharSequence s;
243                                switch (reason) {
244                                    case CommandsInterface.CF_REASON_BUSY:
245                                        s = getContext().getText(R.string.disable_cfb_forbidden);
246                                        break;
247                                    case CommandsInterface.CF_REASON_NO_REPLY:
248                                        s = getContext().getText(R.string.disable_cfnry_forbidden);
249                                        break;
250                                    default: // not reachable
251                                        s = getContext().getText(R.string.disable_cfnrc_forbidden);
252                                }
253                                AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
254                                builder.setNeutralButton(R.string.close_dialog, null);
255                                builder.setTitle(getContext().getText(R.string.error_updating_title));
256                                builder.setMessage(s);
257                                builder.setCancelable(true);
258                                builder.create().show();
259                            }
260                        }
261                    }
262                }
263            }
264
265            // Now whether or not we got a new number, reset our enabled
266            // summary text since it may have been replaced by an empty
267            // placeholder.
268            updateSummaryText();
269        }
270
271        private void handleSetCFResponse(Message msg) {
272            AsyncResult ar = (AsyncResult) msg.obj;
273
274            if (ar.exception != null) {
275                if (DBG) Log.d(LOG_TAG, "handleSetCFResponse: ar.exception=" + ar.exception);
276                // setEnabled(false);
277            }
278            if (DBG) Log.d(LOG_TAG, "handleSetCFResponse: re get");
279            mPhone.getCallForwardingOption(reason,
280                    obtainMessage(MESSAGE_GET_CF, msg.arg1, MESSAGE_SET_CF, ar.exception));
281        }
282    }
283}
284