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