Home | History | Annotate | Download | only in phone
      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
     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
     92     protected void onResume() {
     93         super.onResume();
     94         query();
     95     }
     96 
     97     @Override
     98     protected void onStop() {
     99         super.onStop();
    100         if (mCursor != null) {
    101             mCursor.deactivate();
    102         }
    103     }
    104 
    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 
    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 
    122     private void reQuery() {
    123         query();
    124     }
    125 
    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 
    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 
    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 {
    205         public QueryHandler(ContentResolver cr) {
    206             super(cr);
    207         }
    208 
    209         @Override
    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
    221         protected void onInsertComplete(int token, Object cookie, Uri uri) {
    222             if (DBG) log("onInsertComplete: requery");
    223             reQuery();
    224         }
    225 
    226         @Override
    227         protected void onUpdateComplete(int token, Object cookie, int result) {
    228             if (DBG) log("onUpdateComplete: requery");
    229             reQuery();
    230         }
    231 
    232         @Override
    233         protected void onDeleteComplete(int token, Object cookie, int result) {
    234             if (DBG) log("onDeleteComplete: requery");
    235             reQuery();
    236         }
    237     }
    238 
    239     protected void log(String msg) {
    240         Log.d(TAG, "[ADNList] " + msg);
    241     }
    242 }
    243