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.ContactsUtils;
46import com.android.contacts.GroupListLoader;
47import com.android.contacts.R;
48import com.android.contacts.group.GroupBrowseListAdapter.GroupListItemViewCache;
49import com.android.contacts.widget.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        mAddAccountsView = mRootView.findViewById(R.id.add_accounts);
134        mAddAccountButton = mRootView.findViewById(R.id.add_account_button);
135        mAddAccountButton.setOnClickListener(new OnClickListener() {
136            @Override
137            public void onClick(View v) {
138                Intent intent = new Intent(Settings.ACTION_ADD_ACCOUNT);
139                intent.setFlags(Intent.FLAG_ACTIVITY_CLEAR_WHEN_TASK_RESET);
140                intent.putExtra(Settings.EXTRA_AUTHORITIES,
141                        new String[] { ContactsContract.AUTHORITY });
142                startActivity(intent);
143            }
144        });
145        setAddAccountsVisibility(!ContactsUtils.areGroupWritableAccountsAvailable(mContext));
146
147        return mRootView;
148    }
149
150    public void setVerticalScrollbarPosition(int position) {
151        if (mVerticalScrollbarPosition != position) {
152            mVerticalScrollbarPosition = position;
153            configureVerticalScrollbar();
154        }
155    }
156
157    private void configureVerticalScrollbar() {
158        mListView.setVerticalScrollbarPosition(mVerticalScrollbarPosition);
159        mListView.setScrollBarStyle(ListView.SCROLLBARS_OUTSIDE_OVERLAY);
160        int leftPadding = 0;
161        int rightPadding = 0;
162        if (mVerticalScrollbarPosition == View.SCROLLBAR_POSITION_LEFT) {
163            leftPadding = mContext.getResources().getDimensionPixelOffset(
164                    R.dimen.list_visible_scrollbar_padding);
165        } else {
166            rightPadding = mContext.getResources().getDimensionPixelOffset(
167                    R.dimen.list_visible_scrollbar_padding);
168        }
169        mListView.setPadding(leftPadding, mListView.getPaddingTop(),
170                rightPadding, mListView.getPaddingBottom());
171    }
172
173    @Override
174    public void onAttach(Activity activity) {
175        super.onAttach(activity);
176        mContext = activity;
177    }
178
179    @Override
180    public void onDetach() {
181        super.onDetach();
182        mContext = null;
183    }
184
185    @Override
186    public void onStart() {
187        getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener);
188        super.onStart();
189    }
190
191    /**
192     * The listener for the group meta data loader for all groups.
193     */
194    private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener =
195            new LoaderCallbacks<Cursor>() {
196
197        @Override
198        public CursorLoader onCreateLoader(int id, Bundle args) {
199            mEmptyView.setText(null);
200            return new GroupListLoader(mContext);
201        }
202
203        @Override
204        public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
205            mGroupListCursor = data;
206            bindGroupList();
207        }
208
209        public void onLoaderReset(Loader<Cursor> loader) {
210        }
211    };
212
213    private void bindGroupList() {
214        mEmptyView.setText(R.string.noGroups);
215        setAddAccountsVisibility(!ContactsUtils.areGroupWritableAccountsAvailable(mContext));
216        if (mGroupListCursor == null) {
217            return;
218        }
219        mAdapter.setCursor(mGroupListCursor);
220
221        if (mSelectionToScreenRequested) {
222            mSelectionToScreenRequested = false;
223            requestSelectionToScreen();
224        }
225
226        mSelectedGroupUri = mAdapter.getSelectedGroup();
227        if (mSelectionVisible && mSelectedGroupUri != null) {
228            viewGroup(mSelectedGroupUri);
229        }
230    }
231
232    public void setListener(OnGroupBrowserActionListener listener) {
233        mListener = listener;
234    }
235
236    public void setSelectionVisible(boolean flag) {
237        mSelectionVisible = flag;
238        if (mAdapter != null) {
239            mAdapter.setSelectionVisible(mSelectionVisible);
240        }
241    }
242
243    private void setSelectedGroup(Uri groupUri) {
244        mSelectedGroupUri = groupUri;
245        mAdapter.setSelectedGroup(groupUri);
246        mListView.invalidateViews();
247    }
248
249    private void viewGroup(Uri groupUri) {
250        setSelectedGroup(groupUri);
251        if (mListener != null) mListener.onViewGroupAction(groupUri);
252    }
253
254    public void setSelectedUri(Uri groupUri) {
255        viewGroup(groupUri);
256        mSelectionToScreenRequested = true;
257    }
258
259    protected void requestSelectionToScreen() {
260        if (!mSelectionVisible) {
261            return; // If selection isn't visible we don't care.
262        }
263        int selectedPosition = mAdapter.getSelectedGroupPosition();
264        if (selectedPosition != -1) {
265            mListView.requestPositionToScreen(selectedPosition,
266                    true /* smooth scroll requested */);
267        }
268    }
269
270    private void hideSoftKeyboard() {
271        if (mContext == null) {
272            return;
273        }
274        // Hide soft keyboard, if visible
275        InputMethodManager inputMethodManager = (InputMethodManager)
276                mContext.getSystemService(Context.INPUT_METHOD_SERVICE);
277        inputMethodManager.hideSoftInputFromWindow(mListView.getWindowToken(), 0);
278    }
279
280    /**
281     * Dismisses the soft keyboard when the list takes focus.
282     */
283    @Override
284    public void onFocusChange(View view, boolean hasFocus) {
285        if (view == mListView && hasFocus) {
286            hideSoftKeyboard();
287        }
288    }
289
290    /**
291     * Dismisses the soft keyboard when the list is touched.
292     */
293    @Override
294    public boolean onTouch(View view, MotionEvent event) {
295        if (view == mListView) {
296            hideSoftKeyboard();
297        }
298        return false;
299    }
300
301    @Override
302    public void onSaveInstanceState(Bundle outState) {
303        super.onSaveInstanceState(outState);
304        outState.putParcelable(EXTRA_KEY_GROUP_URI, mSelectedGroupUri);
305    }
306
307    public void setAddAccountsVisibility(boolean visible) {
308        if (mAddAccountsView != null) {
309            mAddAccountsView.setVisibility(visible ? View.VISIBLE : View.GONE);
310        }
311    }
312}
313