1 /*
2  * Copyright (C) 2013 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.dialer.list;
17 
18 import android.app.Activity;
19 import android.content.Intent;
20 import android.content.res.Resources;
21 import android.os.Bundle;
22 import android.text.TextUtils;
23 import android.view.View;
24 import android.view.animation.Interpolator;
25 import android.widget.AbsListView;
26 import android.widget.AbsListView.OnScrollListener;
27 import android.widget.ListView;
28 
29 import com.android.contacts.common.list.ContactEntryListAdapter;
30 import com.android.contacts.common.list.ContactListItemView;
31 import com.android.contacts.common.list.OnPhoneNumberPickerActionListener;
32 import com.android.contacts.common.list.PhoneNumberPickerFragment;
33 import com.android.contacts.common.util.ViewUtil;
34 import com.android.contacts.commonbind.analytics.AnalyticsUtil;
35 import com.android.dialer.DialtactsActivity;
36 import com.android.dialer.R;
37 import com.android.dialer.util.DialerUtils;
38 import com.android.phone.common.animation.AnimUtils;
39 
40 public class SearchFragment extends PhoneNumberPickerFragment {
41 
42     private OnListFragmentScrolledListener mActivityScrollListener;
43 
44     /*
45      * Stores the untouched user-entered string that is used to populate the add to contacts
46      * intent.
47      */
48     private String mAddToContactNumber;
49     private int mActionBarHeight;
50     private int mShadowHeight;
51     private int mPaddingTop;
52     private int mShowDialpadDuration;
53     private int mHideDialpadDuration;
54 
55     private HostInterface mActivity;
56 
57     public interface HostInterface {
isActionBarShowing()58         public boolean isActionBarShowing();
isDialpadShown()59         public boolean isDialpadShown();
getActionBarHideOffset()60         public int getActionBarHideOffset();
getActionBarHeight()61         public int getActionBarHeight();
62     }
63 
64     @Override
onAttach(Activity activity)65     public void onAttach(Activity activity) {
66         super.onAttach(activity);
67 
68         setQuickContactEnabled(true);
69         setAdjustSelectionBoundsEnabled(false);
70         setDarkTheme(false);
71         setPhotoPosition(ContactListItemView.getDefaultPhotoPosition(false /* opposite */));
72         setUseCallableUri(true);
73 
74         try {
75             mActivityScrollListener = (OnListFragmentScrolledListener) activity;
76         } catch (ClassCastException e) {
77             throw new ClassCastException(activity.toString()
78                     + " must implement OnListFragmentScrolledListener");
79         }
80     }
81 
82     @Override
onStart()83     public void onStart() {
84         super.onStart();
85         if (isSearchMode()) {
86             getAdapter().setHasHeader(0, false);
87         }
88 
89         mActivity = (HostInterface) getActivity();
90 
91         final Resources res = getResources();
92         mActionBarHeight = mActivity.getActionBarHeight();
93         mShadowHeight  = res.getDrawable(R.drawable.search_shadow).getIntrinsicHeight();
94         mPaddingTop = res.getDimensionPixelSize(R.dimen.search_list_padding_top);
95         mShowDialpadDuration = res.getInteger(R.integer.dialpad_slide_in_duration);
96         mHideDialpadDuration = res.getInteger(R.integer.dialpad_slide_out_duration);
97 
98         final View parentView = getView();
99 
100         final ListView listView = getListView();
101 
102         listView.setBackgroundColor(res.getColor(R.color.background_dialer_results));
103         listView.setClipToPadding(false);
104         setVisibleScrollbarEnabled(false);
105         listView.setOnScrollListener(new OnScrollListener() {
106             @Override
107             public void onScrollStateChanged(AbsListView view, int scrollState) {
108                 mActivityScrollListener.onListFragmentScrollStateChange(scrollState);
109             }
110 
111             @Override
112             public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
113                     int totalItemCount) {
114             }
115         });
116 
117         updatePosition(false /* animate */);
118     }
119 
120     @Override
onViewCreated(View view, Bundle savedInstanceState)121     public void onViewCreated(View view, Bundle savedInstanceState) {
122         super.onViewCreated(view, savedInstanceState);
123         ViewUtil.addBottomPaddingToListViewForFab(getListView(), getResources());
124     }
125 
126     @Override
setSearchMode(boolean flag)127     protected void setSearchMode(boolean flag) {
128         super.setSearchMode(flag);
129         // This hides the "All contacts with phone numbers" header in the search fragment
130         final ContactEntryListAdapter adapter = getAdapter();
131         if (adapter != null) {
132             adapter.setHasHeader(0, false);
133         }
134     }
135 
setAddToContactNumber(String addToContactNumber)136     public void setAddToContactNumber(String addToContactNumber) {
137         mAddToContactNumber = addToContactNumber;
138     }
139 
140     @Override
createListAdapter()141     protected ContactEntryListAdapter createListAdapter() {
142         DialerPhoneNumberListAdapter adapter = new DialerPhoneNumberListAdapter(getActivity());
143         adapter.setDisplayPhotos(true);
144         adapter.setUseCallableUri(super.usesCallableUri());
145         return adapter;
146     }
147 
148     @Override
onItemClick(int position, long id)149     protected void onItemClick(int position, long id) {
150         final DialerPhoneNumberListAdapter adapter = (DialerPhoneNumberListAdapter) getAdapter();
151         final int shortcutType = adapter.getShortcutTypeFromPosition(position);
152         final OnPhoneNumberPickerActionListener listener;
153 
154         switch (shortcutType) {
155             case DialerPhoneNumberListAdapter.SHORTCUT_INVALID:
156                 super.onItemClick(position, id);
157                 break;
158             case DialerPhoneNumberListAdapter.SHORTCUT_DIRECT_CALL:
159                 listener = getOnPhoneNumberPickerListener();
160                 if (listener != null) {
161                     listener.onCallNumberDirectly(getQueryString());
162                 }
163                 break;
164             case DialerPhoneNumberListAdapter.SHORTCUT_ADD_NUMBER_TO_CONTACTS:
165                 final String number = TextUtils.isEmpty(mAddToContactNumber) ?
166                         adapter.getFormattedQueryString() : mAddToContactNumber;
167                 final Intent intent = DialtactsActivity.getAddNumberToContactIntent(number);
168                 DialerUtils.startActivityWithErrorToast(getActivity(), intent,
169                         R.string.add_contact_not_available);
170                 break;
171             case DialerPhoneNumberListAdapter.SHORTCUT_MAKE_VIDEO_CALL:
172                 listener = getOnPhoneNumberPickerListener();
173                 if (listener != null) {
174                     listener.onCallNumberDirectly(getQueryString(), true /* isVideoCall */);
175                 }
176                 break;
177         }
178     }
179 
180     /**
181      * Updates the position and padding of the search fragment, depending on whether the dialpad is
182      * shown. This can be optionally animated.
183      * @param animate
184      */
updatePosition(boolean animate)185     public void updatePosition(boolean animate) {
186         // Use negative shadow height instead of 0 to account for the 9-patch's shadow.
187         int startTranslationValue =
188                 mActivity.isDialpadShown() ? mActionBarHeight - mShadowHeight: -mShadowHeight;
189         int endTranslationValue = 0;
190         // Prevents ListView from being translated down after a rotation when the ActionBar is up.
191         if (animate || mActivity.isActionBarShowing()) {
192             endTranslationValue =
193                     mActivity.isDialpadShown() ? 0 : mActionBarHeight -mShadowHeight;
194         }
195         if (animate) {
196             Interpolator interpolator =
197                     mActivity.isDialpadShown() ? AnimUtils.EASE_IN : AnimUtils.EASE_OUT ;
198             int duration =
199                     mActivity.isDialpadShown() ? mShowDialpadDuration : mHideDialpadDuration;
200             getView().setTranslationY(startTranslationValue);
201             getView().animate()
202                     .translationY(endTranslationValue)
203                     .setInterpolator(interpolator)
204                     .setDuration(duration);
205         } else {
206             getView().setTranslationY(endTranslationValue);
207         }
208 
209         // There is padding which should only be applied when the dialpad is not shown.
210         int paddingTop = mActivity.isDialpadShown() ? 0 : mPaddingTop;
211         final ListView listView = getListView();
212         listView.setPaddingRelative(
213                 listView.getPaddingStart(),
214                 paddingTop,
215                 listView.getPaddingEnd(),
216                 listView.getPaddingBottom());
217     }
218 }
219