1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.phone;
18 
19 import static com.android.phone.TimeConsumingPreferenceActivity.RESPONSE_ERROR;
20 
21 import android.app.AlertDialog;
22 import android.content.Context;
23 import android.content.DialogInterface;
24 import android.content.res.TypedArray;
25 import android.os.AsyncResult;
26 import android.os.Bundle;
27 import android.os.Handler;
28 import android.os.Message;
29 import android.os.PersistableBundle;
30 import android.telephony.CarrierConfigManager;
31 import android.text.method.DigitsKeyListener;
32 import android.text.method.PasswordTransformationMethod;
33 import android.util.AttributeSet;
34 import android.util.Log;
35 import android.view.View;
36 import android.widget.EditText;
37 import android.widget.TextView;
38 import android.widget.Toast;
39 
40 import com.android.internal.telephony.CommandException;
41 import com.android.internal.telephony.Phone;
42 import com.android.internal.telephony.PhoneFactory;
43 import com.android.phone.settings.fdn.EditPinPreference;
44 
45 import java.lang.ref.WeakReference;
46 
47 /**
48  * This preference represents the status of call barring options, enabling/disabling
49  * the call barring option will prompt the user for the current password.
50  */
51 public class CallBarringEditPreference extends EditPinPreference {
52     private static final String LOG_TAG = "CallBarringEditPreference";
53     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
54 
55     private String mFacility;
56     boolean mIsActivated = false;
57     private CharSequence mEnableText;
58     private CharSequence mDisableText;
59     private CharSequence mSummaryOn;
60     private CharSequence mSummaryOff;
61     private int mButtonClicked;
62     private final MyHandler mHandler = new MyHandler(this);
63     private Phone mPhone;
64     private TimeConsumingPreferenceListener mTcpListener;
65 
66     private static final int PW_LENGTH = 4;
67 
68     /**
69      * CallBarringEditPreference constructor.
70      *
71      * @param context The context of view.
72      * @param attrs The attributes of the XML tag that is inflating EditTextPreference.
73      */
CallBarringEditPreference(Context context, AttributeSet attrs)74     public CallBarringEditPreference(Context context, AttributeSet attrs) {
75         super(context, attrs);
76         // Get the summary settings, use CheckBoxPreference as the standard.
77         TypedArray typedArray = context.obtainStyledAttributes(attrs,
78                 android.R.styleable.CheckBoxPreference, 0, 0);
79         mSummaryOn = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOn);
80         mSummaryOff = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOff);
81         mDisableText = context.getText(R.string.disable);
82         mEnableText = context.getText(R.string.enable);
83         typedArray.recycle();
84 
85         // Get default phone
86         mPhone = PhoneFactory.getDefaultPhone();
87 
88         typedArray = context.obtainStyledAttributes(attrs,
89                 R.styleable.CallBarringEditPreference, 0, R.style.EditPhoneNumberPreference);
90         mFacility = typedArray.getString(R.styleable.CallBarringEditPreference_facility);
91         typedArray.recycle();
92     }
93 
94     /**
95      * CallBarringEditPreference constructor.
96      *
97      * @param context The context of view.
98      */
CallBarringEditPreference(Context context)99     public CallBarringEditPreference(Context context) {
100         this(context, null);
101     }
102 
init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone)103     void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
104         Log.d(LOG_TAG, "init: phone id = " + phone.getPhoneId());
105 
106         mPhone = phone;
107 
108         mTcpListener = listener;
109         if (!skipReading) {
110             // Query call barring status
111             mPhone.getCallBarring(mFacility, "", mHandler.obtainMessage(
112                     MyHandler.MESSAGE_GET_CALL_BARRING), getServiceClassForCallBarring(mPhone));
113             if (mTcpListener != null) {
114                 mTcpListener.onStarted(this, true);
115             }
116         }
117     }
118 
119     @Override
onClick(DialogInterface dialog, int which)120     public void onClick(DialogInterface dialog, int which) {
121         super.onClick(dialog, which);
122         mButtonClicked = which;
123     }
124 
125     @Override
showDialog(Bundle state)126     protected void showDialog(Bundle state) {
127         setDialogMessage(getContext().getString(R.string.messageCallBarring));
128         super.showDialog(state);
129     }
130 
131     @Override
onBindView(View view)132     protected void onBindView(View view) {
133         super.onBindView(view);
134 
135         // Sync the summary view
136         TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
137         if (summaryView != null) {
138             CharSequence sum;
139             int vis;
140 
141             // Set summary depending upon mode
142             if (mIsActivated) {
143                 sum = (mSummaryOn == null) ? getSummary() : mSummaryOn;
144             } else {
145                 sum = (mSummaryOff == null) ? getSummary() : mSummaryOff;
146             }
147 
148             if (sum != null) {
149                 summaryView.setText(sum);
150                 vis = View.VISIBLE;
151             } else {
152                 vis = View.GONE;
153             }
154 
155             if (vis != summaryView.getVisibility()) {
156                 summaryView.setVisibility(vis);
157             }
158         }
159     }
160 
161     @Override
onPrepareDialogBuilder(AlertDialog.Builder builder)162     protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
163         builder.setPositiveButton(null, null);
164         builder.setNeutralButton(mIsActivated ? mDisableText : mEnableText, this);
165     }
166 
167     @Override
onBindDialogView(View view)168     protected void onBindDialogView(View view) {
169         super.onBindDialogView(view);
170         // Default the button clicked to be the cancel button.
171         mButtonClicked = DialogInterface.BUTTON_NEGATIVE;
172 
173         final EditText editText = (EditText) view.findViewById(android.R.id.edit);
174         if (editText != null) {
175             editText.setSingleLine(true);
176             editText.setTransformationMethod(PasswordTransformationMethod.getInstance());
177             editText.setKeyListener(DigitsKeyListener.getInstance());
178 
179             editText.setVisibility(View.VISIBLE);
180         }
181     }
182 
183     @Override
onDialogClosed(boolean positiveResult)184     protected void onDialogClosed(boolean positiveResult) {
185         super.onDialogClosed(positiveResult);
186         Log.d(LOG_TAG, "onDialogClosed: mButtonClicked=" + mButtonClicked + ", positiveResult="
187                 + positiveResult);
188 
189         if (mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
190             String password = getEditText().getText().toString();
191 
192             // Check if the password is valid.
193             if (password == null || password.length() != PW_LENGTH) {
194                 Toast.makeText(getContext(),
195                         getContext().getString(R.string.call_barring_right_pwd_number),
196                         Toast.LENGTH_SHORT).show();
197                 return;
198             }
199 
200             Log.d(LOG_TAG, "onDialogClosed");
201 
202             // Send set call barring message to RIL layer.
203             mPhone.setCallBarring(mFacility, !mIsActivated, password,
204                     mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING),
205                     getServiceClassForCallBarring(mPhone));
206             if (mTcpListener != null) {
207                 mTcpListener.onStarted(this, false);
208             }
209         }
210     }
211 
handleCallBarringResult(boolean status)212     void handleCallBarringResult(boolean status) {
213         mIsActivated = status;
214         Log.i(LOG_TAG, "handleCallBarringResult: mIsActivated=" + mIsActivated);
215     }
216 
getServiceClassForCallBarring(Phone phone)217     private static int getServiceClassForCallBarring(Phone phone) {
218         int serviceClass = CarrierConfigManager.SERVICE_CLASS_VOICE;
219         PersistableBundle carrierConfig = PhoneGlobals.getInstance()
220                 .getCarrierConfigForSubId(phone.getSubId());
221         if (carrierConfig != null) {
222             serviceClass = carrierConfig.getInt(
223                     CarrierConfigManager.KEY_CALL_BARRING_DEFAULT_SERVICE_CLASS_INT,
224                     CarrierConfigManager.SERVICE_CLASS_VOICE);
225         }
226         return serviceClass;
227     }
228 
updateSummaryText()229     void updateSummaryText() {
230         notifyChanged();
231         notifyDependencyChange(shouldDisableDependents());
232     }
233 
234     @Override
shouldDisableDependents()235     public boolean shouldDisableDependents() {
236         return mIsActivated;
237     }
238 
239     // Message protocol:
240     // what: get vs. set
241     // arg1: action -- register vs. disable
242     // arg2: get vs. set for the preceding request
243     private static class MyHandler extends Handler {
244         private static final int MESSAGE_GET_CALL_BARRING = 0;
245         private static final int MESSAGE_SET_CALL_BARRING = 1;
246 
247         private final WeakReference<CallBarringEditPreference> mCallBarringEditPreference;
248 
MyHandler(CallBarringEditPreference callBarringEditPreference)249         private MyHandler(CallBarringEditPreference callBarringEditPreference) {
250             mCallBarringEditPreference =
251                     new WeakReference<CallBarringEditPreference>(callBarringEditPreference);
252         }
253 
254         @Override
handleMessage(Message msg)255         public void handleMessage(Message msg) {
256             switch (msg.what) {
257                 case MESSAGE_GET_CALL_BARRING:
258                     handleGetCallBarringResponse(msg);
259                     break;
260                 case MESSAGE_SET_CALL_BARRING:
261                     handleSetCallBarringResponse(msg);
262                     break;
263                 default:
264                     break;
265             }
266         }
267 
268         // Handle the response message for query CB status.
handleGetCallBarringResponse(Message msg)269         private void handleGetCallBarringResponse(Message msg) {
270             final CallBarringEditPreference pref = mCallBarringEditPreference.get();
271             if (pref == null) {
272                 return;
273             }
274 
275             Log.i(LOG_TAG, "handleGetCallBarringResponse: done");
276 
277             AsyncResult ar = (AsyncResult) msg.obj;
278 
279             if (msg.arg2 == MESSAGE_SET_CALL_BARRING) {
280                 pref.mTcpListener.onFinished(pref, false);
281             } else {
282                 pref.mTcpListener.onFinished(pref, true);
283             }
284 
285             // Unsuccessful query for call barring.
286             if (ar.exception != null) {
287                 Log.i(LOG_TAG, "handleGetCallBarringResponse: ar.exception=" + ar.exception);
288                 pref.mTcpListener.onException(pref, (CommandException) ar.exception);
289             } else {
290                 if (ar.userObj instanceof Throwable) {
291                     pref.mTcpListener.onError(pref, RESPONSE_ERROR);
292                 }
293                 int[] ints = (int[]) ar.result;
294                 if (ints.length == 0) {
295                     Log.i(LOG_TAG, "handleGetCallBarringResponse: ar.result.length==0");
296                     pref.setEnabled(false);
297                     pref.mTcpListener.onError(pref, RESPONSE_ERROR);
298                 } else {
299                     pref.handleCallBarringResult(ints[0] != 0);
300                     Log.i(LOG_TAG,
301                             "handleGetCallBarringResponse: CB state successfully queried: "
302                                     + ints[0]);
303                 }
304             }
305             // Update call barring status.
306             pref.updateSummaryText();
307         }
308 
309         // Handle the response message for CB settings.
handleSetCallBarringResponse(Message msg)310         private void handleSetCallBarringResponse(Message msg) {
311             final CallBarringEditPreference pref = mCallBarringEditPreference.get();
312             if (pref == null) {
313                 return;
314             }
315 
316             AsyncResult ar = (AsyncResult) msg.obj;
317 
318             if (ar.exception != null || ar.userObj instanceof Throwable) {
319                 Log.i(LOG_TAG, "handleSetCallBarringResponse: ar.exception=" + ar.exception);
320             }
321             Log.i(LOG_TAG, "handleSetCallBarringResponse: re-get call barring option");
322             pref.mPhone.getCallBarring(
323                     pref.mFacility,
324                     "",
325                     obtainMessage(MESSAGE_GET_CALL_BARRING, 0, MESSAGE_SET_CALL_BARRING,
326                             ar.exception), getServiceClassForCallBarring(pref.mPhone));
327         }
328     }
329 }
330