blob: 999e14198f570f04ca570a68d75ab2b95b3dba7e [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)) {
Wink Saville6618ff52009-04-02 11:00:56 -0700182 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 }
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
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
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 */
245 public synchronized EditText getTextField() {
246 return textField;
247 }
248
249 /**
250 * Synchronized setter for the EditText.
251 */
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 */
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
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
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}