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