1 /* 2 * Copyright (C) 2007 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 android.view.Window.PROGRESS_VISIBILITY_OFF; 20 import static android.view.Window.PROGRESS_VISIBILITY_ON; 21 22 import android.app.ListActivity; 23 import android.content.AsyncQueryHandler; 24 import android.content.ContentResolver; 25 import android.content.Intent; 26 import android.database.Cursor; 27 import android.net.Uri; 28 import android.os.Bundle; 29 import android.text.BidiFormatter; 30 import android.text.TextDirectionHeuristics; 31 import android.util.Log; 32 import android.view.View; 33 import android.view.Window; 34 import android.widget.CursorAdapter; 35 import android.widget.SimpleCursorAdapter; 36 import android.widget.TextView; 37 38 /** 39 * Abbreviated Dial Numbers (ADN) list activity for the Phone app. By default, this class will show 40 * you all Service Dialing Numbers (SDN) that are supported by a service provider. SDNs are a form 41 * of speed dial for accessing service provider contacts like "#MIN" for getting user minutes. 42 * To see this class in use, trigger the radio info screen by dialing *#*#INFO#*#* and open the 43 * menu. 44 * This class can also be used as a base class for simple contact lists that can be represented with 45 * only labels and numbers. 46 */ 47 public class ADNList extends ListActivity { 48 protected static final String TAG = "ADNList"; 49 protected static final boolean DBG = false; 50 51 private static final String[] COLUMN_NAMES = new String[] { 52 "name", 53 "number", 54 "emails" 55 }; 56 57 protected static final int NAME_COLUMN = 0; 58 protected static final int NUMBER_COLUMN = 1; 59 protected static final int EMAILS_COLUMN = 2; 60 61 private static final int[] VIEW_NAMES = new int[] { 62 android.R.id.text1, 63 android.R.id.text2 64 }; 65 66 protected static final int QUERY_TOKEN = 0; 67 protected static final int INSERT_TOKEN = 1; 68 protected static final int UPDATE_TOKEN = 2; 69 protected static final int DELETE_TOKEN = 3; 70 71 72 protected QueryHandler mQueryHandler; 73 protected CursorAdapter mCursorAdapter; 74 protected Cursor mCursor = null; 75 76 private TextView mEmptyText; 77 78 protected int mInitialSelection = -1; 79 80 @Override onCreate(Bundle icicle)81 protected void onCreate(Bundle icicle) { 82 super.onCreate(icicle); 83 getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS); 84 setContentView(R.layout.adn_list); 85 mEmptyText = (TextView) findViewById(android.R.id.empty); 86 mQueryHandler = new QueryHandler(getContentResolver()); 87 } 88 89 @Override onResume()90 protected void onResume() { 91 super.onResume(); 92 query(); 93 } 94 95 @Override onStop()96 protected void onStop() { 97 super.onStop(); 98 if (mCursor != null) { 99 mCursor.deactivate(); 100 } 101 } 102 resolveIntent()103 protected Uri resolveIntent() { 104 Intent intent = getIntent(); 105 if (intent.getData() == null) { 106 intent.setData(Uri.parse("content://icc/adn")); 107 } 108 109 return intent.getData(); 110 } 111 query()112 private void query() { 113 Uri uri = resolveIntent(); 114 if (DBG) log("query: starting an async query"); 115 mQueryHandler.startQuery(QUERY_TOKEN, null, uri, COLUMN_NAMES, 116 null, null, null); 117 displayProgress(true); 118 } 119 reQuery()120 private void reQuery() { 121 query(); 122 } 123 setAdapter()124 private void setAdapter() { 125 // NOTE: 126 // As it it written, the positioning code below is NOT working. 127 // However, this current non-working state is in compliance with 128 // the UI paradigm, so we can't really do much to change it. 129 130 // In the future, if we wish to get this "positioning" correct, 131 // we'll need to do the following: 132 // 1. Change the layout to in the cursor adapter to: 133 // android.R.layout.simple_list_item_checked 134 // 2. replace the selection / focus code with: 135 // getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); 136 // getListView().setItemChecked(mInitialSelection, true); 137 138 // Since the positioning is really only useful for the dialer's 139 // SpecialCharSequence case (dialing '2#' to get to the 2nd 140 // contact for instance), it doesn't make sense to mess with 141 // the usability of the activity just for this case. 142 143 // These artifacts include: 144 // 1. UI artifacts (checkbox and highlight at the same time) 145 // 2. Allowing the user to edit / create new SIM contacts when 146 // the user is simply trying to retrieve a number into the d 147 // dialer. 148 149 if (mCursorAdapter == null) { 150 mCursorAdapter = newAdapter(); 151 152 setListAdapter(mCursorAdapter); 153 } else { 154 mCursorAdapter.changeCursor(mCursor); 155 } 156 157 if (mInitialSelection >=0 && mInitialSelection < mCursorAdapter.getCount()) { 158 setSelection(mInitialSelection); 159 getListView().setFocusableInTouchMode(true); 160 boolean gotfocus = getListView().requestFocus(); 161 } 162 } 163 newAdapter()164 protected CursorAdapter newAdapter() { 165 SimpleCursorAdapter sca = new SimpleCursorAdapter(this, 166 android.R.layout.simple_list_item_2, 167 mCursor, COLUMN_NAMES, VIEW_NAMES); 168 169 // This code block is for displaying a phone number including "+ country code" correctly 170 // in bidirectional language (b/35180168). 171 // Without this code, e.g. "+0123456789" is wrongly displayed as "0123456789+". 172 sca.setViewBinder(new SimpleCursorAdapter.ViewBinder() { 173 public boolean setViewValue(View view, Cursor cursor, int columnIndex) { 174 view.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); 175 if (columnIndex == NUMBER_COLUMN) { 176 String num = cursor.getString(NUMBER_COLUMN); 177 if (num != null) { 178 BidiFormatter bf = BidiFormatter.getInstance(); 179 num = bf.unicodeWrap(num, TextDirectionHeuristics.LTR, true); 180 } 181 if (view instanceof TextView) { 182 ((TextView) view).setText(num); 183 } 184 return true; 185 } 186 return false; 187 } 188 }); 189 return sca; 190 } 191 displayProgress(boolean loading)192 private void displayProgress(boolean loading) { 193 if (DBG) log("displayProgress: " + loading); 194 195 mEmptyText.setText(loading ? R.string.simContacts_emptyLoading: 196 R.string.simContacts_empty); 197 getWindow().setFeatureInt( 198 Window.FEATURE_INDETERMINATE_PROGRESS, 199 loading ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF); 200 } 201 202 private class QueryHandler extends AsyncQueryHandler { QueryHandler(ContentResolver cr)203 public QueryHandler(ContentResolver cr) { 204 super(cr); 205 } 206 207 @Override onQueryComplete(int token, Object cookie, Cursor c)208 protected void onQueryComplete(int token, Object cookie, Cursor c) { 209 if (DBG) log("onQueryComplete: cursor.count=" + c.getCount()); 210 mCursor = c; 211 setAdapter(); 212 displayProgress(false); 213 214 // Cursor is refreshed and inherited classes may have menu items depending on it. 215 invalidateOptionsMenu(); 216 } 217 218 @Override onInsertComplete(int token, Object cookie, Uri uri)219 protected void onInsertComplete(int token, Object cookie, Uri uri) { 220 if (DBG) log("onInsertComplete: requery"); 221 reQuery(); 222 } 223 224 @Override onUpdateComplete(int token, Object cookie, int result)225 protected void onUpdateComplete(int token, Object cookie, int result) { 226 if (DBG) log("onUpdateComplete: requery"); 227 reQuery(); 228 } 229 230 @Override onDeleteComplete(int token, Object cookie, int result)231 protected void onDeleteComplete(int token, Object cookie, int result) { 232 if (DBG) log("onDeleteComplete: requery"); 233 reQuery(); 234 } 235 } 236 log(String msg)237 protected void log(String msg) { 238 Log.d(TAG, "[ADNList] " + msg); 239 } 240 } 241