• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2010 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 package com.android.contacts.common.list;
17 
18 import android.content.Intent;
19 import android.content.Loader;
20 import android.database.Cursor;
21 import android.net.Uri;
22 import android.os.Bundle;
23 import android.text.TextUtils;
24 import android.util.Log;
25 import android.view.LayoutInflater;
26 import android.view.MenuItem;
27 import android.view.View;
28 import android.view.View.OnClickListener;
29 import android.view.ViewGroup;
30 
31 import com.android.contacts.common.R;
32 import com.android.contacts.common.list.ShortcutIntentBuilder.OnShortcutIntentCreatedListener;
33 import com.android.contacts.common.util.AccountFilterUtil;
34 import com.android.contacts.commonbind.analytics.AnalyticsUtil;
35 
36 import org.json.JSONArray;
37 import org.json.JSONException;
38 import org.json.JSONObject;
39 
40 /**
41  * Fragment containing a phone number list for picking.
42  */
43 public class PhoneNumberPickerFragment extends ContactEntryListFragment<ContactEntryListAdapter>
44         implements OnShortcutIntentCreatedListener {
45     private static final String TAG = PhoneNumberPickerFragment.class.getSimpleName();
46 
47     private static final int REQUEST_CODE_ACCOUNT_FILTER = 1;
48 
49     private static final String KEY_SHORTCUT_ACTION = "shortcutAction";
50 
51     private OnPhoneNumberPickerActionListener mListener;
52     private String mShortcutAction;
53 
54     private ContactListFilter mFilter;
55 
56     private View mAccountFilterHeader;
57     /**
58      * Lives as ListView's header and is shown when {@link #mAccountFilterHeader} is set
59      * to View.GONE.
60      */
61     private View mPaddingView;
62 
63     private static final String KEY_FILTER = "filter";
64 
65     /** true if the loader has started at least once. */
66     private boolean mLoaderStarted;
67 
68     private boolean mUseCallableUri;
69 
70     private ContactListItemView.PhotoPosition mPhotoPosition =
71             ContactListItemView.getDefaultPhotoPosition(false /* normal/non opposite */);
72 
73     private class FilterHeaderClickListener implements OnClickListener {
74         @Override
onClick(View view)75         public void onClick(View view) {
76             AccountFilterUtil.startAccountFilterActivityForResult(
77                     PhoneNumberPickerFragment.this,
78                     REQUEST_CODE_ACCOUNT_FILTER,
79                     mFilter);
80         }
81     }
82     private OnClickListener mFilterHeaderClickListener = new FilterHeaderClickListener();
83 
PhoneNumberPickerFragment()84     public PhoneNumberPickerFragment() {
85         setQuickContactEnabled(false);
86         setPhotoLoaderEnabled(true);
87         setSectionHeaderDisplayEnabled(true);
88         setDirectorySearchMode(DirectoryListLoader.SEARCH_MODE_NONE);
89 
90         // Show nothing instead of letting caller Activity show something.
91         setHasOptionsMenu(true);
92     }
93 
setDirectorySearchEnabled(boolean flag)94     public void setDirectorySearchEnabled(boolean flag) {
95         setDirectorySearchMode(flag ? DirectoryListLoader.SEARCH_MODE_DEFAULT
96                 : DirectoryListLoader.SEARCH_MODE_NONE);
97     }
98 
setOnPhoneNumberPickerActionListener(OnPhoneNumberPickerActionListener listener)99     public void setOnPhoneNumberPickerActionListener(OnPhoneNumberPickerActionListener listener) {
100         this.mListener = listener;
101     }
102 
getOnPhoneNumberPickerListener()103     public OnPhoneNumberPickerActionListener getOnPhoneNumberPickerListener() {
104         return mListener;
105     }
106 
107     @Override
onCreateView(LayoutInflater inflater, ViewGroup container)108     protected void onCreateView(LayoutInflater inflater, ViewGroup container) {
109         super.onCreateView(inflater, container);
110 
111         View paddingView = inflater.inflate(R.layout.contact_detail_list_padding, null, false);
112         mPaddingView = paddingView.findViewById(R.id.contact_detail_list_padding);
113         getListView().addHeaderView(paddingView);
114 
115         mAccountFilterHeader = getView().findViewById(R.id.account_filter_header_container);
116         mAccountFilterHeader.setOnClickListener(mFilterHeaderClickListener);
117         updateFilterHeaderView();
118 
119         setVisibleScrollbarEnabled(getVisibleScrollbarEnabled());
120     }
121 
getVisibleScrollbarEnabled()122     protected boolean getVisibleScrollbarEnabled() {
123         return true;
124     }
125 
126     @Override
setSearchMode(boolean flag)127     protected void setSearchMode(boolean flag) {
128         super.setSearchMode(flag);
129         updateFilterHeaderView();
130     }
131 
updateFilterHeaderView()132     private void updateFilterHeaderView() {
133         final ContactListFilter filter = getFilter();
134         if (mAccountFilterHeader == null || filter == null) {
135             return;
136         }
137         final boolean shouldShowHeader =
138                 !isSearchMode() &&
139                 AccountFilterUtil.updateAccountFilterTitleForPhone(
140                         mAccountFilterHeader, filter, false);
141         if (shouldShowHeader) {
142             mPaddingView.setVisibility(View.GONE);
143             mAccountFilterHeader.setVisibility(View.VISIBLE);
144         } else {
145             mPaddingView.setVisibility(View.VISIBLE);
146             mAccountFilterHeader.setVisibility(View.GONE);
147         }
148     }
149 
150     @Override
restoreSavedState(Bundle savedState)151     public void restoreSavedState(Bundle savedState) {
152         super.restoreSavedState(savedState);
153 
154         if (savedState == null) {
155             return;
156         }
157 
158         mFilter = savedState.getParcelable(KEY_FILTER);
159         mShortcutAction = savedState.getString(KEY_SHORTCUT_ACTION);
160     }
161 
162     @Override
onSaveInstanceState(Bundle outState)163     public void onSaveInstanceState(Bundle outState) {
164         super.onSaveInstanceState(outState);
165         outState.putParcelable(KEY_FILTER, mFilter);
166         outState.putString(KEY_SHORTCUT_ACTION, mShortcutAction);
167     }
168 
169     @Override
onOptionsItemSelected(MenuItem item)170     public boolean onOptionsItemSelected(MenuItem item) {
171         final int itemId = item.getItemId();
172         if (itemId == android.R.id.home) {  // See ActionBar#setDisplayHomeAsUpEnabled()
173             if (mListener != null) {
174                 mListener.onHomeInActionBarSelected();
175             }
176             return true;
177         }
178         return super.onOptionsItemSelected(item);
179     }
180 
181     /**
182      * @param shortcutAction either {@link Intent#ACTION_CALL} or
183      *            {@link Intent#ACTION_SENDTO} or null.
184      */
setShortcutAction(String shortcutAction)185     public void setShortcutAction(String shortcutAction) {
186         this.mShortcutAction = shortcutAction;
187     }
188 
189     @Override
onItemClick(int position, long id)190     protected void onItemClick(int position, long id) {
191         final Uri phoneUri = getPhoneUri(position);
192 
193         if (phoneUri != null) {
194             pickPhoneNumber(phoneUri);
195         } else {
196             final String number = getPhoneNumber(position);
197             if (!TextUtils.isEmpty(number)) {
198                 cacheContactInfo(position);
199                 mListener.onCallNumberDirectly(number);
200             } else {
201                 Log.w(TAG, "Item at " + position + " was clicked before"
202                         + " adapter is ready. Ignoring");
203             }
204         }
205 
206         // Get the lookup key and track any analytics
207         final String lookupKey = getLookupKey(position);
208         if (!TextUtils.isEmpty(lookupKey)) {
209             maybeTrackAnalytics(lookupKey);
210         }
211     }
212 
cacheContactInfo(int position)213     protected void cacheContactInfo(int position) {
214         // Not implemented. Hook for child classes
215     }
216 
getPhoneNumber(int position)217     protected String getPhoneNumber(int position) {
218         final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
219         return adapter.getPhoneNumber(position);
220     }
221 
getPhoneUri(int position)222     protected Uri getPhoneUri(int position) {
223         final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
224         return adapter.getDataUri(position);
225     }
226 
getLookupKey(int position)227     protected String getLookupKey(int position) {
228         final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
229         return adapter.getLookupKey(position);
230     }
231 
232     @Override
startLoading()233     protected void startLoading() {
234         mLoaderStarted = true;
235         super.startLoading();
236     }
237 
238     @Override
onLoadFinished(Loader<Cursor> loader, Cursor data)239     public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
240         super.onLoadFinished(loader, data);
241 
242         // disable scroll bar if there is no data
243         setVisibleScrollbarEnabled(data != null && data.getCount() > 0);
244     }
245 
setUseCallableUri(boolean useCallableUri)246     public void setUseCallableUri(boolean useCallableUri) {
247         mUseCallableUri = useCallableUri;
248     }
249 
usesCallableUri()250     public boolean usesCallableUri() {
251         return mUseCallableUri;
252     }
253 
254     @Override
createListAdapter()255     protected ContactEntryListAdapter createListAdapter() {
256         PhoneNumberListAdapter adapter = new PhoneNumberListAdapter(getActivity());
257         adapter.setDisplayPhotos(true);
258         adapter.setUseCallableUri(mUseCallableUri);
259         return adapter;
260     }
261 
262     @Override
configureAdapter()263     protected void configureAdapter() {
264         super.configureAdapter();
265 
266         final ContactEntryListAdapter adapter = getAdapter();
267         if (adapter == null) {
268             return;
269         }
270 
271         if (!isSearchMode() && mFilter != null) {
272             adapter.setFilter(mFilter);
273         }
274 
275         setPhotoPosition(adapter);
276     }
277 
setPhotoPosition(ContactEntryListAdapter adapter)278     protected void setPhotoPosition(ContactEntryListAdapter adapter) {
279         ((PhoneNumberListAdapter) adapter).setPhotoPosition(mPhotoPosition);
280     }
281 
282     @Override
inflateView(LayoutInflater inflater, ViewGroup container)283     protected View inflateView(LayoutInflater inflater, ViewGroup container) {
284         return inflater.inflate(R.layout.contact_list_content, null);
285     }
286 
pickPhoneNumber(Uri uri)287     public void pickPhoneNumber(Uri uri) {
288         if (mShortcutAction == null) {
289             mListener.onPickPhoneNumberAction(uri);
290         } else {
291             startPhoneNumberShortcutIntent(uri);
292         }
293     }
294 
startPhoneNumberShortcutIntent(Uri uri)295     protected void startPhoneNumberShortcutIntent(Uri uri) {
296         ShortcutIntentBuilder builder = new ShortcutIntentBuilder(getActivity(), this);
297         builder.createPhoneNumberShortcutIntent(uri, mShortcutAction);
298     }
299 
onShortcutIntentCreated(Uri uri, Intent shortcutIntent)300     public void onShortcutIntentCreated(Uri uri, Intent shortcutIntent) {
301         mListener.onShortcutIntentCreated(shortcutIntent);
302     }
303 
304     @Override
onPickerResult(Intent data)305     public void onPickerResult(Intent data) {
306         mListener.onPickPhoneNumberAction(data.getData());
307     }
308 
309     @Override
onActivityResult(int requestCode, int resultCode, Intent data)310     public void onActivityResult(int requestCode, int resultCode, Intent data) {
311         if (requestCode == REQUEST_CODE_ACCOUNT_FILTER) {
312             if (getActivity() != null) {
313                 AccountFilterUtil.handleAccountFilterResult(
314                         ContactListFilterController.getInstance(getActivity()), resultCode, data);
315             } else {
316                 Log.e(TAG, "getActivity() returns null during Fragment#onActivityResult()");
317             }
318         }
319     }
320 
getFilter()321     public ContactListFilter getFilter() {
322         return mFilter;
323     }
324 
setFilter(ContactListFilter filter)325     public void setFilter(ContactListFilter filter) {
326         if ((mFilter == null && filter == null) ||
327                 (mFilter != null && mFilter.equals(filter))) {
328             return;
329         }
330 
331         mFilter = filter;
332         if (mLoaderStarted) {
333             reloadData();
334         }
335         updateFilterHeaderView();
336     }
337 
setPhotoPosition(ContactListItemView.PhotoPosition photoPosition)338     public void setPhotoPosition(ContactListItemView.PhotoPosition photoPosition) {
339         mPhotoPosition = photoPosition;
340 
341         final PhoneNumberListAdapter adapter = (PhoneNumberListAdapter) getAdapter();
342         if (adapter != null) {
343             adapter.setPhotoPosition(photoPosition);
344         }
345     }
346 
347     /**
348      * Where a lookup key contains analytic event information, logs the associated analytics event.
349      *
350      * @param lookupKey The lookup key JSON object.
351      */
maybeTrackAnalytics(String lookupKey)352     private void maybeTrackAnalytics(String lookupKey) {
353         try {
354             JSONObject json = new JSONObject(lookupKey);
355 
356             String analyticsCategory = json.getString(
357                     PhoneNumberListAdapter.PhoneQuery.ANALYTICS_CATEGORY);
358             String analyticsAction = json.getString(
359                     PhoneNumberListAdapter.PhoneQuery.ANALYTICS_ACTION);
360             String analyticsValue = json.getString(
361                     PhoneNumberListAdapter.PhoneQuery.ANALYTICS_VALUE);
362 
363             if (TextUtils.isEmpty(analyticsCategory) || TextUtils.isEmpty(analyticsAction) ||
364                     TextUtils.isEmpty(analyticsValue)) {
365                 return;
366             }
367 
368             // Assume that the analytic value being tracked could be a float value, but just cast
369             // to a long so that the analytic server can handle it.
370             long value;
371             try {
372                 float floatValue = Float.parseFloat(analyticsValue);
373                 value = (long) floatValue;
374             } catch (NumberFormatException nfe) {
375                 return;
376             }
377 
378             AnalyticsUtil.sendEvent(getActivity().getApplication(), analyticsCategory,
379                     analyticsAction, "" /* label */, value);
380         } catch (JSONException e) {
381             // Not an error; just a lookup key that doesn't have the right information.
382         }
383     }
384 }
385