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.group.GroupBrowseListAdapter.GroupListItemViewCache;
48import com.android.contacts.common.ContactsUtils;
49import com.android.contacts.common.list.AutoScrollListView;
50
51/**
52 * Fragment to display the list of groups.
53 */
54public class GroupBrowseListFragment extends Fragment
55        implements OnFocusChangeListener, OnTouchListener {
56
57    /**
58     * Action callbacks that can be sent by a group list.
59     */
60    public interface OnGroupBrowserActionListener  {
61
62        /**
63         * Opens the specified group for viewing.
64         *
65         * @param groupUri for the group that the user wishes to view.
66         */
67        void onViewGroupAction(Uri groupUri);
68
69    }
70
71    private static final String TAG = "GroupBrowseListFragment";
72
73    private static final int LOADER_GROUPS = 1;
74
75    private Context mContext;
76    private Cursor mGroupListCursor;
77
78    private boolean mSelectionToScreenRequested;
79
80    private static final String EXTRA_KEY_GROUP_URI = "groups.groupUri";
81
82    private View mRootView;
83    private AutoScrollListView mListView;
84    private TextView mEmptyView;
85    private View mAddAccountsView;
86    private View mAddAccountButton;
87
88    private GroupBrowseListAdapter mAdapter;
89    private boolean mSelectionVisible;
90    private Uri mSelectedGroupUri;
91
92    private int mVerticalScrollbarPosition = View.SCROLLBAR_POSITION_RIGHT;
93
94    private OnGroupBrowserActionListener mListener;
95
96    public GroupBrowseListFragment() {
97    }
98
99    @Override
100    public View onCreateView(LayoutInflater inflater, ViewGroup container,
101            Bundle savedInstanceState) {
102        if (savedInstanceState != null) {
103            mSelectedGroupUri = savedInstanceState.getParcelable(EXTRA_KEY_GROUP_URI);
104            if (mSelectedGroupUri != null) {
105                // The selection may be out of screen, if rotated from portrait to landscape,
106                // so ensure it's visible.
107                mSelectionToScreenRequested = true;
108            }
109        }
110
111        mRootView = inflater.inflate(R.layout.group_browse_list_fragment, null);
112        mEmptyView = (TextView)mRootView.findViewById(R.id.empty);
113
114        mAdapter = new GroupBrowseListAdapter(mContext);
115        mAdapter.setSelectionVisible(mSelectionVisible);
116        mAdapter.setSelectedGroup(mSelectedGroupUri);
117
118        mListView = (AutoScrollListView) mRootView.findViewById(R.id.list);
119        mListView.setOnFocusChangeListener(this);
120        mListView.setOnTouchListener(this);
121        mListView.setAdapter(mAdapter);
122        mListView.setOnItemClickListener(new OnItemClickListener() {
123            @Override
124            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
125                GroupListItemViewCache groupListItem = (GroupListItemViewCache) view.getTag();
126                if (groupListItem != null) {
127                    viewGroup(groupListItem.getUri());
128                }
129            }
130        });
131
132        mListView.setEmptyView(mEmptyView);
133        configureVerticalScrollbar();
134
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        mVerticalScrollbarPosition = position;
154        if (mListView != null) {
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