• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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