• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2006 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.contacts;
18 
19 import com.android.internal.telephony.ITelephony;
20 
21 import android.app.AlertDialog;
22 import android.app.KeyguardManager;
23 import android.app.ProgressDialog;
24 import android.content.AsyncQueryHandler;
25 import android.content.ContentResolver;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.database.Cursor;
30 import android.net.Uri;
31 import android.os.RemoteException;
32 import android.os.ServiceManager;
33 import android.provider.Telephony.Intents;
34 import android.telephony.PhoneNumberUtils;
35 import android.telephony.TelephonyManager;
36 import android.util.Log;
37 import android.view.WindowManager;
38 import android.widget.EditText;
39 import android.widget.Toast;
40 
41 /**
42  * Helper class to listen for some magic character sequences
43  * that are handled specially by the dialer.
44  */
45 public class SpecialCharSequenceMgr {
46     private static final String TAG = "SpecialCharSequenceMgr";
47     private static final String MMI_IMEI_DISPLAY = "*#06#";
48 
49     /** This class is never instantiated. */
SpecialCharSequenceMgr()50     private SpecialCharSequenceMgr() {
51     }
52 
handleChars(Context context, String input, EditText textField)53     static boolean handleChars(Context context, String input, EditText textField) {
54         return handleChars(context, input, false, textField);
55     }
56 
handleChars(Context context, String input)57     static boolean handleChars(Context context, String input) {
58         return handleChars(context, input, false, null);
59     }
60 
handleChars(Context context, String input, boolean useSystemWindow, EditText textField)61     static boolean handleChars(Context context, String input, boolean useSystemWindow,
62             EditText textField) {
63 
64         //get rid of the separators so that the string gets parsed correctly
65         String dialString = PhoneNumberUtils.stripSeparators(input);
66 
67         if (handleIMEIDisplay(context, dialString, useSystemWindow)
68                 || handlePinEntry(context, dialString)
69                 || handleAdnEntry(context, dialString, textField)
70                 || handleSecretCode(context, dialString)) {
71             return true;
72         }
73 
74         return false;
75     }
76 
77     /**
78      * Handles secret codes to launch arbitrary activities in the form of *#*#<code>#*#*.
79      * If a secret code is encountered an Intent is started with the android_secret_code://<code>
80      * URI.
81      *
82      * @param context the context to use
83      * @param input the text to check for a secret code in
84      * @return true if a secret code was encountered
85      */
handleSecretCode(Context context, String input)86     static boolean handleSecretCode(Context context, String input) {
87         // Secret codes are in the form *#*#<code>#*#*
88         int len = input.length();
89         if (len > 8 && input.startsWith("*#*#") && input.endsWith("#*#*")) {
90             Intent intent = new Intent(Intents.SECRET_CODE_ACTION,
91                     Uri.parse("android_secret_code://" + input.substring(4, len - 4)));
92             context.sendBroadcast(intent);
93             return true;
94         }
95 
96         return false;
97     }
98 
99     /**
100      * Handle ADN requests by filling in the SIM contact number into the requested
101      * EditText.
102      *
103      * This code works alongside the Asynchronous query handler {@link QueryHandler}
104      * and query cancel handler implemented in {@link SimContactQueryCookie}.
105      */
handleAdnEntry(Context context, String input, EditText textField)106     static boolean handleAdnEntry(Context context, String input, EditText textField) {
107         /* ADN entries are of the form "N(N)(N)#" */
108 
109         // if the phone is keyguard-restricted, then just ignore this
110         // input.  We want to make sure that sim card contacts are NOT
111         // exposed unless the phone is unlocked, and this code can be
112         // accessed from the emergency dialer.
113         KeyguardManager keyguardManager =
114                 (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
115         if (keyguardManager.inKeyguardRestrictedInputMode()) {
116             return false;
117         }
118 
119         int len = input.length();
120         if ((len > 1) && (len < 5) && (input.endsWith("#"))) {
121             try {
122                 // get the ordinal number of the sim contact
123                 int index = Integer.parseInt(input.substring(0, len-1));
124 
125                 // The original code that navigated to a SIM Contacts list view did not
126                 // highlight the requested contact correctly, a requirement for PTCRB
127                 // certification.  This behaviour is consistent with the UI paradigm
128                 // for touch-enabled lists, so it does not make sense to try to work
129                 // around it.  Instead we fill in the the requested phone number into
130                 // the dialer text field.
131 
132                 // create the async query handler
133                 QueryHandler handler = new QueryHandler (context.getContentResolver());
134 
135                 // create the cookie object
136                 SimContactQueryCookie sc = new SimContactQueryCookie(index - 1, handler,
137                         ADN_QUERY_TOKEN);
138 
139                 // setup the cookie fields
140                 sc.contactNum = index - 1;
141                 sc.setTextField(textField);
142 
143                 // create the progress dialog
144                 sc.progressDialog = new ProgressDialog(context);
145                 sc.progressDialog.setTitle(R.string.simContacts_title);
146                 sc.progressDialog.setMessage(context.getText(R.string.simContacts_emptyLoading));
147                 sc.progressDialog.setIndeterminate(true);
148                 sc.progressDialog.setCancelable(true);
149                 sc.progressDialog.setOnCancelListener(sc);
150                 sc.progressDialog.getWindow().addFlags(
151                         WindowManager.LayoutParams.FLAG_BLUR_BEHIND);
152 
153                 // display the progress dialog
154                 sc.progressDialog.show();
155 
156                 // run the query.
157                 handler.startQuery(ADN_QUERY_TOKEN, sc, Uri.parse("content://icc/adn"),
158                         new String[]{ADN_PHONE_NUMBER_COLUMN_NAME}, null, null, null);
159                 return true;
160             } catch (NumberFormatException ex) {
161                 // Ignore
162             }
163         }
164         return false;
165     }
166 
handlePinEntry(Context context, String input)167     static boolean handlePinEntry(Context context, String input) {
168         if ((input.startsWith("**04") || input.startsWith("**05")) && input.endsWith("#")) {
169             try {
170                 return ITelephony.Stub.asInterface(ServiceManager.getService("phone"))
171                         .handlePinMmi(input);
172             } catch (RemoteException e) {
173                 Log.e(TAG, "Failed to handlePinMmi due to remote exception");
174                 return false;
175             }
176         }
177         return false;
178     }
179 
handleIMEIDisplay(Context context, String input, boolean useSystemWindow)180     static boolean handleIMEIDisplay(Context context, String input, boolean useSystemWindow) {
181         if (input.equals(MMI_IMEI_DISPLAY)) {
182             int networkType = ((TelephonyManager)context.getSystemService(
183                     Context.TELEPHONY_SERVICE)).getNetworkType();
184             // check for GSM
185             if(networkType == TelephonyManager.NETWORK_TYPE_GPRS ||
186                     networkType == TelephonyManager.NETWORK_TYPE_EDGE ||
187                     networkType == TelephonyManager.NETWORK_TYPE_UMTS ) {
188 
189                 showIMEIPanel(context, useSystemWindow);
190                 return true;
191             }
192         }
193 
194         return false;
195     }
196 
showIMEIPanel(Context context, boolean useSystemWindow)197     static void showIMEIPanel(Context context, boolean useSystemWindow) {
198         String imeiStr = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE))
199                 .getDeviceId();
200 
201         AlertDialog alert = new AlertDialog.Builder(context)
202                 .setTitle(R.string.imei)
203                 .setMessage(imeiStr)
204                 .setPositiveButton(android.R.string.ok, null)
205                 .setCancelable(false)
206                 .show();
207         alert.getWindow().setType(WindowManager.LayoutParams.TYPE_PRIORITY_PHONE);
208     }
209 
210     /*******
211      * This code is used to handle SIM Contact queries
212      *******/
213     private static final String ADN_PHONE_NUMBER_COLUMN_NAME = "number";
214     private static final String ADN_NAME_COLUMN_NAME = "name";
215     private static final int ADN_QUERY_TOKEN = -1;
216 
217     /**
218      * Cookie object that contains everything we need to communicate to the
219      * handler's onQuery Complete, as well as what we need in order to cancel
220      * the query (if requested).
221      *
222      * Note, access to the textField field is going to be synchronized, because
223      * the user can request a cancel at any time through the UI.
224      */
225     private static class SimContactQueryCookie implements DialogInterface.OnCancelListener{
226         public ProgressDialog progressDialog;
227         public int contactNum;
228 
229         // Used to identify the query request.
230         private int mToken;
231         private QueryHandler mHandler;
232 
233         // The text field we're going to update
234         private EditText textField;
235 
SimContactQueryCookie(int number, QueryHandler handler, int token)236         public SimContactQueryCookie(int number, QueryHandler handler, int token) {
237             contactNum = number;
238             mHandler = handler;
239             mToken = token;
240         }
241 
242         /**
243          * Synchronized getter for the EditText.
244          */
getTextField()245         public synchronized EditText getTextField() {
246             return textField;
247         }
248 
249         /**
250          * Synchronized setter for the EditText.
251          */
setTextField(EditText text)252         public synchronized void setTextField(EditText text) {
253             textField = text;
254         }
255 
256         /**
257          * Cancel the ADN query by stopping the operation and signaling
258          * the cookie that a cancel request is made.
259          */
onCancel(DialogInterface dialog)260         public synchronized void onCancel(DialogInterface dialog) {
261             // close the progress dialog
262             if (progressDialog != null) {
263                 progressDialog.dismiss();
264             }
265 
266             // setting the textfield to null ensures that the UI does NOT get
267             // updated.
268             textField = null;
269 
270             // Cancel the operation if possible.
271             mHandler.cancelOperation(mToken);
272         }
273     }
274 
275     /**
276      * Asynchronous query handler that services requests to look up ADNs
277      *
278      * Queries originate from {@link handleAdnEntry}.
279      */
280     private static class QueryHandler extends AsyncQueryHandler {
281 
QueryHandler(ContentResolver cr)282         public QueryHandler(ContentResolver cr) {
283             super(cr);
284         }
285 
286         /**
287          * Override basic onQueryComplete to fill in the textfield when
288          * we're handed the ADN cursor.
289          */
290         @Override
onQueryComplete(int token, Object cookie, Cursor c)291         protected void onQueryComplete(int token, Object cookie, Cursor c) {
292             SimContactQueryCookie sc = (SimContactQueryCookie) cookie;
293 
294             // close the progress dialog.
295             sc.progressDialog.dismiss();
296 
297             // get the EditText to update or see if the request was cancelled.
298             EditText text = sc.getTextField();
299 
300             // if the textview is valid, and the cursor is valid and postionable
301             // on the Nth number, then we update the text field and display a
302             // toast indicating the caller name.
303             if ((c != null) && (text != null) && (c.moveToPosition(sc.contactNum))) {
304                 String name = c.getString(c.getColumnIndexOrThrow(ADN_NAME_COLUMN_NAME));
305                 String number = c.getString(c.getColumnIndexOrThrow(ADN_PHONE_NUMBER_COLUMN_NAME));
306 
307                 // fill the text in.
308                 text.getText().replace(0, 0, number);
309 
310                 // display the name as a toast
311                 Context context = sc.progressDialog.getContext();
312                 name = context.getString(R.string.menu_callNumber, name);
313                 Toast.makeText(context, name, Toast.LENGTH_SHORT)
314                     .show();
315             }
316         }
317     }
318 }
319