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