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