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