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.GroupListLoader; 46 import com.android.contacts.R; 47 import com.android.contacts.group.GroupBrowseListAdapter.GroupListItemViewCache; 48 import com.android.contacts.common.ContactsUtils; 49 import com.android.contacts.common.list.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 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