1 package com.android.phone;
2 
3 import com.android.internal.telephony.CallForwardInfo;
4 import com.android.internal.telephony.CommandException;
5 import com.android.internal.telephony.CommandsInterface;
6 import com.android.internal.telephony.Phone;
7 
8 import android.app.AlertDialog;
9 import android.content.Context;
10 import android.content.DialogInterface;
11 import android.content.res.TypedArray;
12 import android.os.AsyncResult;
13 import android.os.Handler;
14 import android.os.Message;
15 import android.telephony.PhoneNumberUtils;
16 import android.text.BidiFormatter;
17 import android.text.SpannableString;
18 import android.text.TextDirectionHeuristics;
19 import android.text.TextUtils;
20 import android.util.AttributeSet;
21 import android.util.Log;
22 import android.view.View;
23 
24 import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
25 import static com.android.phone.TimeConsumingPreferenceActivity.EXCEPTION_ERROR;
26 
27 public 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 
CallForwardEditPreference(Context context, AttributeSet attrs)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 
CallForwardEditPreference(Context context)64     public CallForwardEditPreference(Context context) {
65         this(context, null);
66     }
67 
init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone)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
onBindDialogView(View view)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
onClick(DialogInterface dialog, int which)92     public void onClick(DialogInterface dialog, int which) {
93         super.onClick(dialog, which);
94         mButtonClicked = which;
95     }
96 
97     @Override
onDialogClosed(boolean positiveResult)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 
handleCallForwardResult(CallForwardInfo cf)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 
updateSummaryText()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
handleMessage(Message msg)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 
handleGetCFResponse(Message msg)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 
handleSetCFResponse(Message msg)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