• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.telephony.TelephonyManager;
17 import android.text.BidiFormatter;
18 import android.text.SpannableString;
19 import android.text.TextDirectionHeuristics;
20 import android.text.TextUtils;
21 import android.util.AttributeSet;
22 import android.util.Log;
23 import android.view.View;
24 
25 import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
26 import static com.android.phone.TimeConsumingPreferenceActivity.EXCEPTION_ERROR;
27 
28 public class CallForwardEditPreference extends EditPhoneNumberPreference {
29     private static final String LOG_TAG = "CallForwardEditPreference";
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     // Should we replace CF queries containing an invalid number with "Voicemail"
48     private boolean mReplaceInvalidCFNumber = false;
49 
CallForwardEditPreference(Context context, AttributeSet attrs)50     public CallForwardEditPreference(Context context, AttributeSet attrs) {
51         super(context, attrs);
52 
53         mSummaryOnTemplate = this.getSummaryOn();
54 
55         TypedArray a = context.obtainStyledAttributes(attrs,
56                 R.styleable.CallForwardEditPreference, 0, R.style.EditPhoneNumberPreference);
57         mServiceClass = a.getInt(R.styleable.CallForwardEditPreference_serviceClass,
58                 CommandsInterface.SERVICE_CLASS_VOICE);
59         reason = a.getInt(R.styleable.CallForwardEditPreference_reason,
60                 CommandsInterface.CF_REASON_UNCONDITIONAL);
61         a.recycle();
62 
63         Log.d(LOG_TAG, "mServiceClass=" + mServiceClass + ", reason=" + reason);
64     }
65 
CallForwardEditPreference(Context context)66     public CallForwardEditPreference(Context context) {
67         this(context, null);
68     }
69 
init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone, boolean replaceInvalidCFNumber)70     void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone,
71             boolean replaceInvalidCFNumber) {
72         mPhone = phone;
73         mTcpListener = listener;
74         mReplaceInvalidCFNumber = replaceInvalidCFNumber;
75 
76         if (!skipReading) {
77             mPhone.getCallForwardingOption(reason,
78                     mHandler.obtainMessage(MyHandler.MESSAGE_GET_CF,
79                             // unused in this case
80                             CommandsInterface.CF_ACTION_DISABLE,
81                             MyHandler.MESSAGE_GET_CF, null));
82             if (mTcpListener != null) {
83                 mTcpListener.onStarted(this, true);
84             }
85         }
86     }
87 
88     @Override
onBindDialogView(View view)89     protected void onBindDialogView(View view) {
90         // default the button clicked to be the cancel button.
91         mButtonClicked = DialogInterface.BUTTON_NEGATIVE;
92         super.onBindDialogView(view);
93     }
94 
95     @Override
onClick(DialogInterface dialog, int which)96     public void onClick(DialogInterface dialog, int which) {
97         super.onClick(dialog, which);
98         mButtonClicked = which;
99     }
100 
101     @Override
onDialogClosed(boolean positiveResult)102     protected void onDialogClosed(boolean positiveResult) {
103         super.onDialogClosed(positiveResult);
104 
105         Log.d(LOG_TAG, "mButtonClicked=" + mButtonClicked + ", positiveResult=" + positiveResult);
106         // Ignore this event if the user clicked the cancel button, or if the dialog is dismissed
107         // without any button being pressed (back button press or click event outside the dialog).
108         if (this.mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
109             int action = (isToggled() || (mButtonClicked == DialogInterface.BUTTON_POSITIVE)) ?
110                     CommandsInterface.CF_ACTION_REGISTRATION :
111                     CommandsInterface.CF_ACTION_DISABLE;
112             int time = (reason != CommandsInterface.CF_REASON_NO_REPLY) ? 0 : 20;
113             final String number = getPhoneNumber();
114 
115             Log.d(LOG_TAG, "callForwardInfo=" + callForwardInfo);
116 
117             if (action == CommandsInterface.CF_ACTION_REGISTRATION
118                     && callForwardInfo != null
119                     && callForwardInfo.status == 1
120                     && number.equals(callForwardInfo.number)) {
121                 // no change, do nothing
122                 Log.d(LOG_TAG, "no change, do nothing");
123             } else {
124                 // set to network
125                 Log.d(LOG_TAG, "reason=" + reason + ", action=" + action
126                         + ", number=" + number);
127 
128                 // Display no forwarding number while we're waiting for
129                 // confirmation
130                 setSummaryOn("");
131 
132                 // the interface of Phone.setCallForwardingOption has error:
133                 // should be action, reason...
134                 mPhone.setCallForwardingOption(action,
135                         reason,
136                         number,
137                         time,
138                         mHandler.obtainMessage(MyHandler.MESSAGE_SET_CF,
139                                 action,
140                                 MyHandler.MESSAGE_SET_CF));
141 
142                 if (mTcpListener != null) {
143                     mTcpListener.onStarted(this, false);
144                 }
145             }
146         }
147     }
148 
handleCallForwardResult(CallForwardInfo cf)149     void handleCallForwardResult(CallForwardInfo cf) {
150         callForwardInfo = cf;
151         Log.d(LOG_TAG, "handleGetCFResponse done, callForwardInfo=" + callForwardInfo);
152         // In some cases, the network can send call forwarding URIs for voicemail that violate the
153         // 3gpp spec. This can cause us to receive "numbers" that are sequences of letters. In this
154         // case, we must detect these series of characters and replace them with "Voicemail".
155         // PhoneNumberUtils#formatNumber returns null if the number is not valid.
156         if (mReplaceInvalidCFNumber && (PhoneNumberUtils.formatNumber(callForwardInfo.number,
157                 getCurrentCountryIso()) == null)) {
158             callForwardInfo.number = getContext().getString(R.string.voicemail);
159             Log.i(LOG_TAG, "handleGetCFResponse: Overridding CF number");
160         }
161 
162         setToggled(callForwardInfo.status == 1);
163         setPhoneNumber(callForwardInfo.number);
164     }
165 
updateSummaryText()166     private void updateSummaryText() {
167         if (isToggled()) {
168             final String number = getRawPhoneNumber();
169             if (number != null && number.length() > 0) {
170                 // Wrap the number to preserve presentation in RTL languages.
171                 String wrappedNumber = BidiFormatter.getInstance().unicodeWrap(
172                         number, TextDirectionHeuristics.LTR);
173                 String values[] = { wrappedNumber };
174                 String summaryOn = String.valueOf(
175                         TextUtils.replace(mSummaryOnTemplate, SRC_TAGS, values));
176                 int start = summaryOn.indexOf(wrappedNumber);
177 
178                 SpannableString spannableSummaryOn = new SpannableString(summaryOn);
179                 PhoneNumberUtils.addTtsSpan(spannableSummaryOn,
180                         start, start + wrappedNumber.length());
181                 setSummaryOn(spannableSummaryOn);
182             } else {
183                 setSummaryOn(getContext().getString(R.string.sum_cfu_enabled_no_number));
184             }
185         }
186 
187     }
188 
189     /**
190      * @return The ISO 3166-1 two letters country code of the country the user is in based on the
191      *      network location.
192      */
getCurrentCountryIso()193     private String getCurrentCountryIso() {
194         final TelephonyManager telephonyManager =
195                 (TelephonyManager) getContext().getSystemService(Context.TELEPHONY_SERVICE);
196         if (telephonyManager == null) {
197             return "";
198         }
199         return telephonyManager.getNetworkCountryIso().toUpperCase();
200     }
201 
202     // Message protocol:
203     // what: get vs. set
204     // arg1: action -- register vs. disable
205     // arg2: get vs. set for the preceding request
206     private class MyHandler extends Handler {
207         static final int MESSAGE_GET_CF = 0;
208         static final int MESSAGE_SET_CF = 1;
209 
210         @Override
handleMessage(Message msg)211         public void handleMessage(Message msg) {
212             switch (msg.what) {
213                 case MESSAGE_GET_CF:
214                     handleGetCFResponse(msg);
215                     break;
216                 case MESSAGE_SET_CF:
217                     handleSetCFResponse(msg);
218                     break;
219             }
220         }
221 
handleGetCFResponse(Message msg)222         private void handleGetCFResponse(Message msg) {
223             Log.d(LOG_TAG, "handleGetCFResponse: done");
224 
225             mTcpListener.onFinished(CallForwardEditPreference.this, msg.arg2 != MESSAGE_SET_CF);
226 
227             AsyncResult ar = (AsyncResult) msg.obj;
228 
229             callForwardInfo = null;
230             if (ar.exception != null) {
231                 Log.d(LOG_TAG, "handleGetCFResponse: ar.exception=" + ar.exception);
232                 if (ar.exception instanceof CommandException) {
233                     mTcpListener.onException(CallForwardEditPreference.this,
234                             (CommandException) ar.exception);
235                 } else {
236                     // Most likely an ImsException and we can't handle it the same way as
237                     // a CommandException. The best we can do is to handle the exception
238                     // the same way as mTcpListener.onException() does when it is not of type
239                     // FDN_CHECK_FAILURE.
240                     mTcpListener.onError(CallForwardEditPreference.this, EXCEPTION_ERROR);
241                 }
242             } else {
243                 if (ar.userObj instanceof Throwable) {
244                     mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
245                 }
246                 CallForwardInfo cfInfoArray[] = (CallForwardInfo[]) ar.result;
247                 if (cfInfoArray.length == 0) {
248                     Log.d(LOG_TAG, "handleGetCFResponse: cfInfoArray.length==0");
249                     setEnabled(false);
250                     mTcpListener.onError(CallForwardEditPreference.this, RESPONSE_ERROR);
251                 } else {
252                     for (int i = 0, length = cfInfoArray.length; i < length; i++) {
253                         Log.d(LOG_TAG, "handleGetCFResponse, cfInfoArray[" + i + "]="
254                                 + cfInfoArray[i]);
255                         if ((mServiceClass & cfInfoArray[i].serviceClass) != 0) {
256                             // corresponding class
257                             CallForwardInfo info = cfInfoArray[i];
258                             handleCallForwardResult(info);
259 
260                             // Show an alert if we got a success response but
261                             // with unexpected values.
262                             // Currently only handle the fail-to-disable case
263                             // since we haven't observed fail-to-enable.
264                             if (msg.arg2 == MESSAGE_SET_CF &&
265                                     msg.arg1 == CommandsInterface.CF_ACTION_DISABLE &&
266                                     info.status == 1) {
267                                 CharSequence s;
268                                 switch (reason) {
269                                     case CommandsInterface.CF_REASON_BUSY:
270                                         s = getContext().getText(R.string.disable_cfb_forbidden);
271                                         break;
272                                     case CommandsInterface.CF_REASON_NO_REPLY:
273                                         s = getContext().getText(R.string.disable_cfnry_forbidden);
274                                         break;
275                                     default: // not reachable
276                                         s = getContext().getText(R.string.disable_cfnrc_forbidden);
277                                 }
278                                 AlertDialog.Builder builder = new AlertDialog.Builder(getContext());
279                                 builder.setNeutralButton(R.string.close_dialog, null);
280                                 builder.setTitle(getContext().getText(R.string.error_updating_title));
281                                 builder.setMessage(s);
282                                 builder.setCancelable(true);
283                                 builder.create().show();
284                             }
285                         }
286                     }
287                 }
288             }
289 
290             // Now whether or not we got a new number, reset our enabled
291             // summary text since it may have been replaced by an empty
292             // placeholder.
293             updateSummaryText();
294         }
295 
handleSetCFResponse(Message msg)296         private void handleSetCFResponse(Message msg) {
297             AsyncResult ar = (AsyncResult) msg.obj;
298 
299             if (ar.exception != null) {
300                 Log.d(LOG_TAG, "handleSetCFResponse: ar.exception=" + ar.exception);
301                 // setEnabled(false);
302             }
303             Log.d(LOG_TAG, "handleSetCFResponse: re get");
304             mPhone.getCallForwardingOption(reason,
305                     obtainMessage(MESSAGE_GET_CF, msg.arg1, MESSAGE_SET_CF, ar.exception));
306         }
307     }
308 }
309