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.text.method.DigitsKeyListener;
30 import android.text.method.PasswordTransformationMethod;
31 import android.util.AttributeSet;
32 import android.util.Log;
33 import android.view.View;
34 import android.widget.EditText;
35 import android.widget.TextView;
36 import android.widget.Toast;
37 
38 import com.android.internal.telephony.CommandsInterface;
39 import com.android.internal.telephony.CommandException;
40 import com.android.internal.telephony.Phone;
41 import com.android.internal.telephony.PhoneFactory;
42 import com.android.phone.settings.fdn.EditPinPreference;
43 
44 import java.lang.ref.WeakReference;
45 
46 /**
47  * This preference represents the status of call barring options, enabling/disabling
48  * the call barring option will prompt the user for the current password.
49  */
50 public class CallBarringEditPreference extends EditPinPreference {
51     private static final String LOG_TAG = "CallBarringEditPreference";
52     private static final boolean DBG = (PhoneGlobals.DBG_LEVEL >= 2);
53 
54     private String mFacility;
55     boolean mIsActivated = false;
56     private CharSequence mEnableText;
57     private CharSequence mDisableText;
58     private CharSequence mSummaryOn;
59     private CharSequence mSummaryOff;
60     private int mButtonClicked;
61     private final MyHandler mHandler = new MyHandler(this);
62     private Phone mPhone;
63     private TimeConsumingPreferenceListener mTcpListener;
64 
65     private static final int PW_LENGTH = 4;
66 
67     /**
68      * CallBarringEditPreference constructor.
69      *
70      * @param context The context of view.
71      * @param attrs The attributes of the XML tag that is inflating EditTextPreference.
72      */
CallBarringEditPreference(Context context, AttributeSet attrs)73     public CallBarringEditPreference(Context context, AttributeSet attrs) {
74         super(context, attrs);
75         // Get the summary settings, use CheckBoxPreference as the standard.
76         TypedArray typedArray = context.obtainStyledAttributes(attrs,
77                 android.R.styleable.CheckBoxPreference, 0, 0);
78         mSummaryOn = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOn);
79         mSummaryOff = typedArray.getString(android.R.styleable.CheckBoxPreference_summaryOff);
80         mDisableText = context.getText(R.string.disable);
81         mEnableText = context.getText(R.string.enable);
82         typedArray.recycle();
83 
84         // Get default phone
85         mPhone = PhoneFactory.getDefaultPhone();
86 
87         typedArray = context.obtainStyledAttributes(attrs,
88                 R.styleable.CallBarringEditPreference, 0, R.style.EditPhoneNumberPreference);
89         mFacility = typedArray.getString(R.styleable.CallBarringEditPreference_facility);
90         typedArray.recycle();
91     }
92 
93     /**
94      * CallBarringEditPreference constructor.
95      *
96      * @param context The context of view.
97      */
CallBarringEditPreference(Context context)98     public CallBarringEditPreference(Context context) {
99         this(context, null);
100     }
101 
init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone)102     void init(TimeConsumingPreferenceListener listener, boolean skipReading, Phone phone) {
103         if (DBG) {
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), CommandsInterface.SERVICE_CLASS_VOICE);
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         if (DBG) {
187             Log.d(LOG_TAG, "onDialogClosed: mButtonClicked=" + mButtonClicked + ", positiveResult="
188                     + positiveResult);
189         }
190         if (mButtonClicked != DialogInterface.BUTTON_NEGATIVE) {
191             String password = getEditText().getText().toString();
192 
193             // Check if the password is valid.
194             if (password == null || password.length() != PW_LENGTH) {
195                 Toast.makeText(getContext(),
196                         getContext().getString(R.string.call_barring_right_pwd_number),
197                         Toast.LENGTH_SHORT).show();
198                 return;
199             }
200 
201             if (DBG) {
202                 Log.d(LOG_TAG, "onDialogClosed: password=" + password);
203             }
204             // Send set call barring message to RIL layer.
205             mPhone.setCallBarring(mFacility, !mIsActivated, password,
206                     mHandler.obtainMessage(MyHandler.MESSAGE_SET_CALL_BARRING),
207                     CommandsInterface.SERVICE_CLASS_VOICE);
208             if (mTcpListener != null) {
209                 mTcpListener.onStarted(this, false);
210             }
211         }
212     }
213 
handleCallBarringResult(boolean status)214     void handleCallBarringResult(boolean status) {
215         mIsActivated = status;
216         if (DBG) {
217             Log.d(LOG_TAG, "handleCallBarringResult: mIsActivated=" + mIsActivated);
218         }
219     }
220 
updateSummaryText()221     void updateSummaryText() {
222         notifyChanged();
223         notifyDependencyChange(shouldDisableDependents());
224     }
225 
226     @Override
shouldDisableDependents()227     public boolean shouldDisableDependents() {
228         return mIsActivated;
229     }
230 
231     // Message protocol:
232     // what: get vs. set
233     // arg1: action -- register vs. disable
234     // arg2: get vs. set for the preceding request
235     private static class MyHandler extends Handler {
236         private static final int MESSAGE_GET_CALL_BARRING = 0;
237         private static final int MESSAGE_SET_CALL_BARRING = 1;
238 
239         private final WeakReference<CallBarringEditPreference> mCallBarringEditPreference;
240 
MyHandler(CallBarringEditPreference callBarringEditPreference)241         private MyHandler(CallBarringEditPreference callBarringEditPreference) {
242             mCallBarringEditPreference =
243                     new WeakReference<CallBarringEditPreference>(callBarringEditPreference);
244         }
245 
246         @Override
handleMessage(Message msg)247         public void handleMessage(Message msg) {
248             switch (msg.what) {
249                 case MESSAGE_GET_CALL_BARRING:
250                     handleGetCallBarringResponse(msg);
251                     break;
252                 case MESSAGE_SET_CALL_BARRING:
253                     handleSetCallBarringResponse(msg);
254                     break;
255                 default:
256                     break;
257             }
258         }
259 
260         // Handle the response message for query CB status.
handleGetCallBarringResponse(Message msg)261         private void handleGetCallBarringResponse(Message msg) {
262             final CallBarringEditPreference pref = mCallBarringEditPreference.get();
263             if (pref == null) {
264                 return;
265             }
266 
267             if (DBG) {
268                 Log.d(LOG_TAG, "handleGetCallBarringResponse: done");
269             }
270 
271             AsyncResult ar = (AsyncResult) msg.obj;
272 
273             if (msg.arg2 == MESSAGE_SET_CALL_BARRING) {
274                 pref.mTcpListener.onFinished(pref, false);
275             } else {
276                 pref.mTcpListener.onFinished(pref, true);
277             }
278 
279             // Unsuccessful query for call barring.
280             if (ar.exception != null) {
281                 if (DBG) {
282                     Log.d(LOG_TAG, "handleGetCallBarringResponse: ar.exception=" + ar.exception);
283                 }
284                 pref.mTcpListener.onException(pref, (CommandException) ar.exception);
285             } else {
286                 if (ar.userObj instanceof Throwable) {
287                     pref.mTcpListener.onError(pref, RESPONSE_ERROR);
288                 }
289                 int[] ints = (int[]) ar.result;
290                 if (ints.length == 0) {
291                     if (DBG) {
292                         Log.d(LOG_TAG, "handleGetCallBarringResponse: ar.result.length==0");
293                     }
294                     pref.setEnabled(false);
295                     pref.mTcpListener.onError(pref, RESPONSE_ERROR);
296                 } else {
297                     pref.handleCallBarringResult(ints[0] != 0);
298                     if (DBG) {
299                         Log.d(LOG_TAG,
300                                 "handleGetCallBarringResponse: CB state successfully queried: "
301                                         + ints[0]);
302                     }
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                 if (DBG) {
320                     Log.d(LOG_TAG, "handleSetCallBarringResponse: ar.exception=" + ar.exception);
321                 }
322             }
323             if (DBG) {
324                 Log.d(LOG_TAG, "handleSetCallBarringResponse: re-get call barring option");
325             }
326             pref.mPhone.getCallBarring(
327                     pref.mFacility,
328                     "",
329                     obtainMessage(MESSAGE_GET_CALL_BARRING, 0, MESSAGE_SET_CALL_BARRING,
330                             ar.exception),
331                     CommandsInterface.SERVICE_CLASS_VOICE);
332         }
333     }
334 }
335