blob: 644b66f623356af4cb6c403919219edddf44bb7c [file] [log] [blame]
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -08001/*
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
17package com.android.contacts;
18
19import com.android.internal.telephony.ITelephony;
20
21import android.app.AlertDialog;
22import android.app.KeyguardManager;
23import android.app.ProgressDialog;
24import android.content.AsyncQueryHandler;
25import android.content.ContentResolver;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.database.Cursor;
30import android.net.Uri;
31import android.os.RemoteException;
32import android.os.ServiceManager;
33import android.provider.Telephony.Intents;
34import android.telephony.PhoneNumberUtils;
35import android.telephony.TelephonyManager;
36import android.util.Log;
37import android.view.WindowManager;
38import android.widget.EditText;
39import android.widget.Toast;
40
41/**
42 * Helper class to listen for some magic character sequences
43 * that are handled specially by the dialer.
44 */
45public 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. */
50 private SpecialCharSequenceMgr() {
51 }
52
53 static boolean handleChars(Context context, String input, EditText textField) {
54 return handleChars(context, input, false, textField);
55 }
56
57 static boolean handleChars(Context context, String input) {
58 return handleChars(context, input, false, null);
59 }
60
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 */
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 */
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.
Wink Saville6618ff52009-04-02 11:00:56 -0700157 handler.startQuery(ADN_QUERY_TOKEN, sc, Uri.parse("content://icc/adn"),
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800158 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
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
180 static boolean handleIMEIDisplay(Context context, String input, boolean useSystemWindow) {
181 if (input.equals(MMI_IMEI_DISPLAY)) {
Li Zhed9a31682009-07-30 14:08:42 +0800182 int phoneType = ((TelephonyManager)context.getSystemService(
183 Context.TELEPHONY_SERVICE)).getPhoneType();
Wink Saville6618ff52009-04-02 11:00:56 -0700184
Li Zhed9a31682009-07-30 14:08:42 +0800185 if (phoneType == TelephonyManager.PHONE_TYPE_GSM) {
Tracy Pu29a1cd82009-07-02 14:39:18 -0400186 showIMEIPanel(context, useSystemWindow);
187 return true;
Li Zhed9a31682009-07-30 14:08:42 +0800188 } else if (phoneType == TelephonyManager.PHONE_TYPE_CDMA) {
Tracy Pu29a1cd82009-07-02 14:39:18 -0400189 showMEIDPanel(context, useSystemWindow);
Wink Saville6618ff52009-04-02 11:00:56 -0700190 return true;
191 }
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800192 }
193
194 return false;
195 }
196
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
Tracy Pu29a1cd82009-07-02 14:39:18 -0400210 static void showMEIDPanel(Context context, boolean useSystemWindow) {
211 String meidStr = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE))
212 .getDeviceId();
213
214 AlertDialog alert = new AlertDialog.Builder(context)
215 .setTitle(R.string.meid)
216 .setMessage(meidStr)
217 .setPositiveButton(android.R.string.ok, null)
218 .setCancelable(false)
219 .show();
220 alert.getWindow().setType(WindowManager.LayoutParams.TYPE_PRIORITY_PHONE);
221 }
222
The Android Open Source Project7aa0e4c2009-03-03 19:32:21 -0800223 /*******
224 * This code is used to handle SIM Contact queries
225 *******/
226 private static final String ADN_PHONE_NUMBER_COLUMN_NAME = "number";
227 private static final String ADN_NAME_COLUMN_NAME = "name";
228 private static final int ADN_QUERY_TOKEN = -1;
229
230 /**
231 * Cookie object that contains everything we need to communicate to the
232 * handler's onQuery Complete, as well as what we need in order to cancel
233 * the query (if requested).
234 *
235 * Note, access to the textField field is going to be synchronized, because
236 * the user can request a cancel at any time through the UI.
237 */
238 private static class SimContactQueryCookie implements DialogInterface.OnCancelListener{
239 public ProgressDialog progressDialog;
240 public int contactNum;
241
242 // Used to identify the query request.
243 private int mToken;
244 private QueryHandler mHandler;
245
246 // The text field we're going to update
247 private EditText textField;
248
249 public SimContactQueryCookie(int number, QueryHandler handler, int token) {
250 contactNum = number;
251 mHandler = handler;
252 mToken = token;
253 }
254
255 /**
256 * Synchronized getter for the EditText.
257 */
258 public synchronized EditText getTextField() {
259 return textField;
260 }
261
262 /**
263 * Synchronized setter for the EditText.
264 */
265 public synchronized void setTextField(EditText text) {
266 textField = text;
267 }
268
269 /**
270 * Cancel the ADN query by stopping the operation and signaling
271 * the cookie that a cancel request is made.
272 */
273 public synchronized void onCancel(DialogInterface dialog) {
274 // close the progress dialog
275 if (progressDialog != null) {
276 progressDialog.dismiss();
277 }
278
279 // setting the textfield to null ensures that the UI does NOT get
280 // updated.
281 textField = null;
282
283 // Cancel the operation if possible.
284 mHandler.cancelOperation(mToken);
285 }
286 }
287
288 /**
289 * Asynchronous query handler that services requests to look up ADNs
290 *
291 * Queries originate from {@link handleAdnEntry}.
292 */
293 private static class QueryHandler extends AsyncQueryHandler {
294
295 public QueryHandler(ContentResolver cr) {
296 super(cr);
297 }
298
299 /**
300 * Override basic onQueryComplete to fill in the textfield when
301 * we're handed the ADN cursor.
302 */
303 @Override
304 protected void onQueryComplete(int token, Object cookie, Cursor c) {
305 SimContactQueryCookie sc = (SimContactQueryCookie) cookie;
306
307 // close the progress dialog.
308 sc.progressDialog.dismiss();
309
310 // get the EditText to update or see if the request was cancelled.
311 EditText text = sc.getTextField();
312
313 // if the textview is valid, and the cursor is valid and postionable
314 // on the Nth number, then we update the text field and display a
315 // toast indicating the caller name.
316 if ((c != null) && (text != null) && (c.moveToPosition(sc.contactNum))) {
317 String name = c.getString(c.getColumnIndexOrThrow(ADN_NAME_COLUMN_NAME));
318 String number = c.getString(c.getColumnIndexOrThrow(ADN_PHONE_NUMBER_COLUMN_NAME));
319
320 // fill the text in.
321 text.getText().replace(0, 0, number);
322
323 // display the name as a toast
324 Context context = sc.progressDialog.getContext();
325 name = context.getString(R.string.menu_callNumber, name);
326 Toast.makeText(context, name, Toast.LENGTH_SHORT)
327 .show();
328 }
329 }
330 }
331}