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