1/*
2 * Copyright (C) 2011 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
17package com.android.contacts.group;
18
19import android.app.Activity;
20import android.app.Fragment;
21import android.app.LoaderManager;
22import android.app.LoaderManager.LoaderCallbacks;
23import android.content.Context;
24import android.content.CursorLoader;
25import android.content.Intent;
26import android.content.Loader;
27import android.database.Cursor;
28import android.net.Uri;
29import android.os.Bundle;
30import android.provider.ContactsContract;
31import android.provider.Settings;
32import android.view.LayoutInflater;
33import android.view.MotionEvent;
34import android.view.View;
35import android.view.View.OnClickListener;
36import android.view.View.OnFocusChangeListener;
37import android.view.View.OnTouchListener;
38import android.view.ViewGroup;
39import android.view.inputmethod.InputMethodManager;
40import android.widget.AdapterView;
41import android.widget.AdapterView.OnItemClickListener;
42import android.widget.ListView;
43import android.widget.TextView;
44
45import com.android.contacts.GroupListLoader;
46import com.android.contacts.R;
47import com.android.contacts.common.util.ImplicitIntentsUtil;
48import com.android.contacts.group.GroupBrowseListAdapter.GroupListItemViewCache;
49import com.android.contacts.common.ContactsUtils;
50import com.android.contacts.common.list.AutoScrollListView;
51
52/**
53 * Fragment to display the list of groups.
54 */
55public class GroupBrowseListFragment extends Fragment
56        implements OnFocusChangeListener, OnTouchListener {
57
58    /**
59     * Action callbacks that can be sent by a group list.
60     */
61    public interface OnGroupBrowserActionListener  {
62
63        /**
64         * Opens the specified group for viewing.
65         *
66         * @param groupUri for the group that the user wishes to view.
67         */
68        void onViewGroupAction(Uri groupUri);
69
70    }
71
72    private static final String TAG = "GroupBrowseListFragment";
73
74    private static final int LOADER_GROUPS = 1;
75
76    private Context mContext;
77    private Cursor mGroupListCursor;
78
79    private boolean mSelectionToScreenRequested;
80
81    private static final String EXTRA_KEY_GROUP_URI = "groups.groupUri";
82
83    private View mRootView;
84    private AutoScrollListView mListView;
85    private TextView mEmptyView;
86    private View mAddAccountsView;
87    private View mAddAccountButton;
88
89    private GroupBrowseListAdapter mAdapter;
90    private boolean mSelectionVisible;
91    private Uri mSelectedGroupUri;
92
93    private int mVerticalScrollbarPosition = View.SCROLLBAR_POSITION_RIGHT;
94
95    private OnGroupBrowserActionListener mListener;
96
97    public GroupBrowseListFragment() {
98    }
99
100    @Override
101    public View onCreateView(LayoutInflater inflater, ViewGroup container,
102            Bundle savedInstanceState) {
103        if (savedInstanceState != null) {
104            mSelectedGroupUri = savedInstanceState.getParcelable(EXTRA_KEY_GROUP_URI);
105            if (mSelectedGroupUri != null) {
106                // The selection may be out of screen, if rotated from portrait to landscape,
107                // so ensure it's visible.
108                mSelectionToScreenRequested = true;
109            }
110        }
111
112        mRootView = inflater.inflate(R.layout.group_browse_list_fragment, null);
113        mEmptyView = (TextView)mRootView.findViewById(R.id.empty);
114
115        mAdapter = new GroupBrowseListAdapter(mContext);
116        mAdapter.setSelectionVisible(mSelectionVisible);
117        mAdapter.setSelectedGroup(mSelectedGroupUri);
118
119        mListView = (AutoScrollListView) mRootView.findViewById(R.id.list);
120        mListView.setOnFocusChangeListener(this);
121        mListView.setOnTouchListener(this);
122        mListView.setAdapter(mAdapter);
123        mListView.setOnItemClickListener(new OnItemClickListener() {
124            @Override
125            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
126                GroupListItemViewCache groupListItem = (GroupListItemViewCache) view.getTag();
127                if (groupListItem != null) {
128                    viewGroup(groupListItem.getUri());
129                }
130            }
131        });
132
133        mListView.setEmptyView(mEmptyView);
134        configureVerticalScrollbar();
135
136        mAddAccountsView = mRootView.findViewById(R.id.add_accounts);
137        mAddAccountButton = mRootView.findViewById(R.id.add_account_button);
138        mAddAccountButton.setOnClickListener(new OnClickListener() {
139            @Override
140            public void onClick(View v) {
141                Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
142                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
143                intent.putExtra(Settings.EXTRA_AUTHORITIES,
144                        new String[] { ContactsContract.AUTHORITY });
145                ImplicitIntentsUtil.startActivityOutsideApp(getActivity(), intent);
146            }
147        });
148        setAddAccountsVisibility(!ContactsUtils.areGroupWritableAccountsAvailable(mContext));
149
150        return mRootView;
151    }
152
153    public void setVerticalScrollbarPosition(int position) {
154        mVerticalScrollbarPosition = position;
155        if (mListView != null) {
156            configureVerticalScrollbar();
157        }
158    }
159
160    private void configureVerticalScrollbar() {
161        mListView.setVerticalScrollbarPosition(mVerticalScrollbarPosition);
162        mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
163        int leftPadding = 0;
164        int rightPadding = 0;
165        if (mVerticalScrollbarPosition == View.SCROLLBAR_POSITION_LEFT) {
166            leftPadding = mContext.getResources().getDimensionPixelOffset(
167                    R.dimen.list_visible_scrollbar_padding);
168        } else {
169            rightPadding = mContext.getResources().getDimensionPixelOffset(
170                    R.dimen.list_visible_scrollbar_padding);
171        }
172        mListView.setPadding(leftPadding, mListView.getPaddingTop(),
173                rightPadding, mListView.getPaddingBottom());
174    }
175
176    @Override
177    public void onAttach(Activity activity) {
178        super.onAttach(activity);
179        mContext = activity;
180    }
181
182    @Override
183    public void onDetach() {
184        super.onDetach();
185        mContext = null;
186    }
187
188    @Override
189    public void onStart() {
190        getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener);
191        super.onStart();
192    }
193
194    /**
195     * The listener for the group meta data loader for all groups.
196     */
197    private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
198            new LoaderCallbacks<Cursor>() {
199
200        @Override
201        public CursorLoader onCreateLoader(int id, Bundle args) {
202            mEmptyView.setText(null);
203            return new GroupListLoader(mContext);
204        }
205
206        @Override
207        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
208            mGroupListCursor = data;
209            bindGroupList();
210        }
211
212        public void onLoaderReset(Loader<Cursor> loader) {
213        }
214    };
215
216    private void bindGroupList() {
217        mEmptyView.setText(R.string.noGroups);
218        setAddAccountsVisibility(!ContactsUtils.areGroupWritableAccountsAvailable(mContext));
219        if (mGroupListCursor == null) {
220            return;
221        }
222        mAdapter.setCursor(mGroupListCursor);
223
224        if (mSelectionToScreenRequested) {
225            mSelectionToScreenRequested = false;
226            requestSelectionToScreen();
227        }
228
229        mSelectedGroupUri = mAdapter.getSelectedGroup();
230        if (mSelectionVisible && mSelectedGroupUri != null) {
231            viewGroup(mSelectedGroupUri);
232        }
233    }
234
235    public void setListener(OnGroupBrowserActionListener listener) {
236        mListener = listener;
237    }
238
239    public void setSelectionVisible(boolean flag) {
240        mSelectionVisible = flag;
241        if (mAdapter != null) {
242            mAdapter.setSelectionVisible(mSelectionVisible);
243        }
244    }
245
246    private void setSelectedGroup(Uri groupUri) {
247        mSelectedGroupUri = groupUri;
248        mAdapter.setSelectedGroup(groupUri);
249        mListView.invalidateViews();
250    }
251
252    private void viewGroup(Uri groupUri) {
253        setSelectedGroup(groupUri);
254        if (mListener != null) mListener.onViewGroupAction(groupUri);
255    }
256
257    public void setSelectedUri(Uri groupUri) {
258        viewGroup(groupUri);
259        mSelectionToScreenRequested = true;
260    }
261
262    protected void requestSelectionToScreen() {
263        if (!mSelectionVisible) {
264            return; // If selection isn't visible we don't care.
265        }
266        int selectedPosition = mAdapter.getSelectedGroupPosition();
267        if (selectedPosition != -1) {
268            mListView.requestPositionToScreen(selectedPosition,
269                    true /* smooth scroll requested */);
270        }
271    }
272
273    private void hideSoftKeyboard() {
274        if (mContext == null) {
275            return;
276        }
277        // Hide soft keyboard, if visible
278        InputMethodManager inputMethodManager = (InputMethodManager)
279                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
280        inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
281    }
282
283    /**
284     * Dismisses the soft keyboard when the list takes focus.
285     */
286    @Override
287    public void onFocusChange(View view, boolean hasFocus) {
288        if (view == mListView && hasFocus) {
289            hideSoftKeyboard();
290        }
291    }
292
293    /**
294     * Dismisses the soft keyboard when the list is touched.
295     */
296    @Override
297    public boolean onTouch(View view, MotionEvent event) {
298        if (view == mListView) {
299            hideSoftKeyboard();
300        }
301        return false;
302    }
303
304    @Override
305    public void onSaveInstanceState(Bundle outState) {
306        super.onSaveInstanceState(outState);
307        outState.putParcelable(EXTRA_KEY_GROUP_URI, mSelectedGroupUri);
308    }
309
310    public void setAddAccountsVisibility(boolean visible) {
311        if (mAddAccountsView != null) {
312            mAddAccountsView.setVisibility(visible ? View.VISIBLE : View.GONE);
313        }
314    }
315}
316