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 17 package com.android.contacts.group; 18 19 import android.app.Activity; 20 import android.app.Fragment; 21 import android.app.LoaderManager; 22 import android.app.LoaderManager.LoaderCallbacks; 23 import android.content.Context; 24 import android.content.CursorLoader; 25 import android.content.Intent; 26 import android.content.Loader; 27 import android.database.Cursor; 28 import android.net.Uri; 29 import android.os.Bundle; 30 import android.provider.ContactsContract; 31 import android.provider.Settings; 32 import android.view.LayoutInflater; 33 import android.view.MotionEvent; 34 import android.view.View; 35 import android.view.View.OnClickListener; 36 import android.view.View.OnFocusChangeListener; 37 import android.view.View.OnTouchListener; 38 import android.view.ViewGroup; 39 import android.view.inputmethod.InputMethodManager; 40 import android.widget.AdapterView; 41 import android.widget.AdapterView.OnItemClickListener; 42 import android.widget.ListView; 43 import android.widget.TextView; 44 45 import com.android.contacts.ContactsUtils; 46 import com.android.contacts.GroupListLoader; 47 import com.android.contacts.R; 48 import com.android.contacts.group.GroupBrowseListAdapter.GroupListItemViewCache; 49 import com.android.contacts.widget.AutoScrollListView; 50 51 /** 52 * Fragment to display the list of groups. 53 */ 54 public 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