/* * Copyright (C) 2007 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.phone; import static android.view.Window.PROGRESS_VISIBILITY_OFF; import static android.view.Window.PROGRESS_VISIBILITY_ON; import android.app.ListActivity; import android.content.AsyncQueryHandler; import android.content.ContentResolver; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.text.BidiFormatter; import android.text.TextDirectionHeuristics; import android.util.Log; import android.view.View; import android.view.Window; import android.widget.CursorAdapter; import android.widget.SimpleCursorAdapter; import android.widget.TextView; /** * Abbreviated Dial Numbers (ADN) list activity for the Phone app. By default, this class will show * you all Service Dialing Numbers (SDN) that are supported by a service provider. SDNs are a form * of speed dial for accessing service provider contacts like "#MIN" for getting user minutes. * To see this class in use, trigger the radio info screen by dialing *#*#INFO#*#* and open the * menu. * This class can also be used as a base class for simple contact lists that can be represented with * only labels and numbers. */ public class ADNList extends ListActivity { protected static final String TAG = "ADNList"; protected static final boolean DBG = false; private static final String[] COLUMN_NAMES = new String[] { "name", "number", "emails" }; protected static final int NAME_COLUMN = 0; protected static final int NUMBER_COLUMN = 1; protected static final int EMAILS_COLUMN = 2; private static final int[] VIEW_NAMES = new int[] { android.R.id.text1, android.R.id.text2 }; protected static final int QUERY_TOKEN = 0; protected static final int INSERT_TOKEN = 1; protected static final int UPDATE_TOKEN = 2; protected static final int DELETE_TOKEN = 3; protected QueryHandler mQueryHandler; protected CursorAdapter mCursorAdapter; protected Cursor mCursor = null; private TextView mEmptyText; protected int mInitialSelection = -1; @Override protected void onCreate(Bundle icicle) { super.onCreate(icicle); getWindow().requestFeature(Window.FEATURE_INDETERMINATE_PROGRESS); setContentView(R.layout.adn_list); mEmptyText = (TextView) findViewById(android.R.id.empty); mQueryHandler = new QueryHandler(getContentResolver()); } @Override protected void onResume() { super.onResume(); query(); } @Override protected void onStop() { super.onStop(); if (mCursor != null) { mCursor.deactivate(); } } protected Uri resolveIntent() { Intent intent = getIntent(); if (intent.getData() == null) { intent.setData(Uri.parse("content://icc/adn")); } return intent.getData(); } private void query() { Uri uri = resolveIntent(); if (DBG) log("query: starting an async query"); mQueryHandler.startQuery(QUERY_TOKEN, null, uri, COLUMN_NAMES, null, null, null); displayProgress(true); } private void reQuery() { query(); } private void setAdapter() { // NOTE: // As it it written, the positioning code below is NOT working. // However, this current non-working state is in compliance with // the UI paradigm, so we can't really do much to change it. // In the future, if we wish to get this "positioning" correct, // we'll need to do the following: // 1. Change the layout to in the cursor adapter to: // android.R.layout.simple_list_item_checked // 2. replace the selection / focus code with: // getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); // getListView().setItemChecked(mInitialSelection, true); // Since the positioning is really only useful for the dialer's // SpecialCharSequence case (dialing '2#' to get to the 2nd // contact for instance), it doesn't make sense to mess with // the usability of the activity just for this case. // These artifacts include: // 1. UI artifacts (checkbox and highlight at the same time) // 2. Allowing the user to edit / create new SIM contacts when // the user is simply trying to retrieve a number into the d // dialer. if (mCursorAdapter == null) { mCursorAdapter = newAdapter(); setListAdapter(mCursorAdapter); } else { mCursorAdapter.changeCursor(mCursor); } if (mInitialSelection >=0 && mInitialSelection < mCursorAdapter.getCount()) { setSelection(mInitialSelection); getListView().setFocusableInTouchMode(true); boolean gotfocus = getListView().requestFocus(); } } protected CursorAdapter newAdapter() { SimpleCursorAdapter sca = new SimpleCursorAdapter(this, android.R.layout.simple_list_item_2, mCursor, COLUMN_NAMES, VIEW_NAMES); // This code block is for displaying a phone number including "+ country code" correctly // in bidirectional language (b/35180168). // Without this code, e.g. "+0123456789" is wrongly displayed as "0123456789+". sca.setViewBinder(new SimpleCursorAdapter.ViewBinder() { public boolean setViewValue(View view, Cursor cursor, int columnIndex) { view.setTextAlignment(View.TEXT_ALIGNMENT_VIEW_START); if (columnIndex == NUMBER_COLUMN) { String num = cursor.getString(NUMBER_COLUMN); if (num != null) { BidiFormatter bf = BidiFormatter.getInstance(); num = bf.unicodeWrap(num, TextDirectionHeuristics.LTR, true); } if (view instanceof TextView) { ((TextView) view).setText(num); } return true; } return false; } }); return sca; } private void displayProgress(boolean loading) { if (DBG) log("displayProgress: " + loading); mEmptyText.setText(loading ? R.string.simContacts_emptyLoading: R.string.simContacts_empty); getWindow().setFeatureInt( Window.FEATURE_INDETERMINATE_PROGRESS, loading ? PROGRESS_VISIBILITY_ON : PROGRESS_VISIBILITY_OFF); } private class QueryHandler extends AsyncQueryHandler { public QueryHandler(ContentResolver cr) { super(cr); } @Override protected void onQueryComplete(int token, Object cookie, Cursor c) { if (DBG) log("onQueryComplete: cursor.count=" + c.getCount()); mCursor = c; setAdapter(); displayProgress(false); // Cursor is refreshed and inherited classes may have menu items depending on it. invalidateOptionsMenu(); } @Override protected void onInsertComplete(int token, Object cookie, Uri uri) { if (DBG) log("onInsertComplete: requery"); reQuery(); } @Override protected void onUpdateComplete(int token, Object cookie, int result) { if (DBG) log("onUpdateComplete: requery"); reQuery(); } @Override protected void onDeleteComplete(int token, Object cookie, int result) { if (DBG) log("onDeleteComplete: requery"); reQuery(); } } protected void log(String msg) { Log.d(TAG, "[ADNList] " + msg); } }