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 getWindow().addSystemFlags( 85 android.view.WindowManager.LayoutParams 86 .SYSTEM_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS); 87 setContentView(R.layout.adn_list); 88 mEmptyText = (TextView) findViewById(android.R.id.empty); 89 mQueryHandler = new QueryHandler(getContentResolver()); 90 } 91 92 @Override onResume()93 protected void onResume() { 94 super.onResume(); 95 query(); 96 } 97 98 @Override onStop()99 protected void onStop() { 100 super.onStop(); 101 if (mCursor != null) { 102 mCursor.deactivate(); 103 } 104 } 105 resolveIntent()106 protected Uri resolveIntent() { 107 Intent intent = getIntent(); 108 if (intent.getData() == null) { 109 intent.setData(Uri.parse("content://icc/adn")); 110 } 111 112 return intent.getData(); 113 } 114 query()115 private void query() { 116 Uri uri = resolveIntent(); 117 if (DBG) log("query: starting an async query"); 118 mQueryHandler.startQuery(QUERY_TOKEN, null, uri, COLUMN_NAMES, 119 null, null, null); 120 displayProgress(true); 121 } 122 reQuery()123 private void reQuery() { 124 query(); 125 } 126 setAdapter()127 private void setAdapter() { 128 // NOTE: 129 // As it it written, the positioning code below is NOT working. 130 // However, this current non-working state is in compliance with 131 // the UI paradigm, so we can't really do much to change it. 132 133 // In the future, if we wish to get this "positioning" correct, 134 // we'll need to do the following: 135 // 1. Change the layout to in the cursor adapter to: 136 // android.R.layout.simple_list_item_checked 137 // 2. replace the selection / focus code with: 138 // getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); 139 // getListView().setItemChecked(mInitialSelection, true); 140 141 // Since the positioning is really only useful for the dialer's 142 // SpecialCharSequence case (dialing '2#' to get to the 2nd 143 // contact for instance), it doesn't make sense to mess with 144 // the usability of the activity just for this case. 145 146 // These artifacts include: 147 // 1. UI artifacts (checkbox and highlight at the same time) 148 // 2. Allowing the user to edit / create new SIM contacts when 149 // the user is simply trying to retrieve a number into the d 150 // dialer. 151 152 if (mCursorAdapter == null) { 153 mCursorAdapter = newAdapter(); 154 155 setListAdapter(mCursorAdapter); 156 } else { 157 mCursorAdapter.changeCursor(mCursor); 158 } 159 160 if (mInitialSelection >=0 && mInitialSelection < mCursorAdapter.getCount()) { 161 setSelection(mInitialSelection); 162 getListView().setFocusableInTouchMode(true); 163 boolean gotfocus = getListView().requestFocus(); 164 } 165 } 166 newAdapter()167 protected CursorAdapter newAdapter() { 168 SimpleCursorAdapter sca = new SimpleCursorAdapter(this, 169 android.R.layout.simple_list_item_2, 170 mCursor, COLUMN_NAMES, VIEW_NAMES); 171 172 // This code block is for displaying a phone number including "+ country code" correctly 173 // in bidirectional language (b/35180168). 174 // Without this code, e.g. "+0123456789" is wrongly displayed as "0123456789+". 175 sca.setViewBinder(new SimpleCursorAdapter.ViewBinder() { 176 public boolean setViewValue(View view, Cursor cursor, int columnIndex) { 177 view.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); 178 if (columnIndex == NUMBER_COLUMN) { 179 String num = cursor.getString(NUMBER_COLUMN); 180 if (num != null) { 181 BidiFormatter bf = BidiFormatter.getInstance(); 182 num = bf.unicodeWrap(num, TextDirectionHeuristics.LTR, true); 183 } 184 if (view instanceof TextView) { 185 ((TextView) view).setText(num); 186 } 187 return true; 188 } 189 return false; 190 } 191 }); 192 return sca; 193 } 194 displayProgress(boolean loading)195 private void displayProgress(boolean loading) { 196 if (DBG) log("displayProgress: " + loading); 197 198 mEmptyText.setText(loading ? R.string.simContacts_emptyLoading: 199 R.string.simContacts_empty); 200 getWindow().setFeatureInt( 201 Window.FEATURE_INDETERMINATE_PROGRESS, 202 loading ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF); 203 } 204 205 private class QueryHandler extends AsyncQueryHandler { QueryHandler(ContentResolver cr)206 public QueryHandler(ContentResolver cr) { 207 super(cr); 208 } 209 210 @Override onQueryComplete(int token, Object cookie, Cursor c)211 protected void onQueryComplete(int token, Object cookie, Cursor c) { 212 if (DBG) log("onQueryComplete: cursor.count=" + c.getCount()); 213 mCursor = c; 214 setAdapter(); 215 displayProgress(false); 216 217 // Cursor is refreshed and inherited classes may have menu items depending on it. 218 invalidateOptionsMenu(); 219 } 220 221 @Override onInsertComplete(int token, Object cookie, Uri uri)222 protected void onInsertComplete(int token, Object cookie, Uri uri) { 223 if (DBG) log("onInsertComplete: requery"); 224 reQuery(); 225 } 226 227 @Override onUpdateComplete(int token, Object cookie, int result)228 protected void onUpdateComplete(int token, Object cookie, int result) { 229 if (DBG) log("onUpdateComplete: requery"); 230 reQuery(); 231 } 232 233 @Override onDeleteComplete(int token, Object cookie, int result)234 protected void onDeleteComplete(int token, Object cookie, int result) { 235 if (DBG) log("onDeleteComplete: requery"); 236 reQuery(); 237 } 238 } 239 log(String msg)240 protected void log(String msg) { 241 Log.d(TAG, "[ADNList] " + msg); 242 } 243 } 244