diff options
author | Walter Jang <wjang@google.com> | 2016-04-05 09:29:56 -0700 |
---|---|---|
committer | Walter Jang <wjang@google.com> | 2016-04-05 13:32:28 -0700 |
commit | 758f56580b973894213569b61b1ae23b293071c2 (patch) | |
tree | 5260b0b91547a3c578bdafac9ffa8736535e8b6e | |
parent | f568b30b5a4b009e10a8b97117f5c720bbfab43d (diff) | |
download | packages_apps_Contacts-758f56580b973894213569b61b1ae23b293071c2.tar.gz packages_apps_Contacts-758f56580b973894213569b61b1ae23b293071c2.tar.bz2 packages_apps_Contacts-758f56580b973894213569b61b1ae23b293071c2.zip |
Add groups to the side navigation bar
Just launch the old group details Activity for now.
Bug 18641067
Change-Id: I213c88213240d5281edfeda1bc5da9180506520b
-rw-r--r-- | res/values/ids.xml | 3 | ||||
-rw-r--r-- | res/values/strings.xml | 5 | ||||
-rw-r--r-- | src/com/android/contacts/activities/PeopleActivity.java | 69 | ||||
-rw-r--r-- | src/com/android/contacts/group/GroupBrowseListAdapter.java | 40 | ||||
-rw-r--r-- | src/com/android/contacts/group/GroupUtil.java | 82 | ||||
-rw-r--r-- | src/com/android/contacts/group/GroupsFragment.java | 103 |
6 files changed, 250 insertions, 52 deletions
diff --git a/res/values/ids.xml b/res/values/ids.xml index 7f6a51fcc..efc45fc65 100644 --- a/res/values/ids.xml +++ b/res/values/ids.xml @@ -43,4 +43,7 @@ <!-- An ID to be used for contents of a custom dialog so that its state be preserved --> <item type="id" name="custom_dialog_content" /> + + <!-- Menu group ID for the contact groups --> + <item type="id" name="menu_groups" /> </resources> diff --git a/res/values/strings.xml b/res/values/strings.xml index 3bf17376d..b926129b5 100644 --- a/res/values/strings.xml +++ b/res/values/strings.xml @@ -294,6 +294,11 @@ <!-- The text displayed when the contacts list is empty while displaying all contacts [CHAR LIMIT=NONE] --> <string name="noContacts">No contacts</string> + <!-- Group name menu item title when there is at least one group member. [CHAR LIMIT=NONE] --> + <string name="group_name_menu_item"> + <xliff:g id="group_name">%s</xliff:g> (<xliff:g id="count">%d</xliff:g>) + </string> + <!-- The text displayed when the groups list is empty while displaying all groups [CHAR LIMIT=NONE] --> <string name="noGroups">No groups.</string> diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java index 66a29deab..c3d3277df 100644 --- a/src/com/android/contacts/activities/PeopleActivity.java +++ b/src/com/android/contacts/activities/PeopleActivity.java @@ -36,9 +36,9 @@ import android.provider.ContactsContract.QuickContact; import android.provider.Settings; import android.support.design.widget.NavigationView; import android.support.v13.app.FragmentPagerAdapter; +import android.support.v4.view.GravityCompat; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; -import android.support.v4.view.GravityCompat; import android.support.v4.widget.DrawerLayout; import android.support.v7.app.ActionBarDrawerToggle; import android.support.v7.widget.Toolbar; @@ -55,14 +55,13 @@ import android.view.ViewGroup; import android.widget.ImageButton; import android.widget.Toast; - import com.android.contacts.AppCompatContactsActivity; import com.android.contacts.R; import com.android.contacts.activities.ActionBarAdapter.TabState; import com.android.contacts.common.ContactsUtils; import com.android.contacts.common.activity.RequestPermissionsActivity; -import com.android.contacts.common.compat.TelecomManagerUtil; import com.android.contacts.common.compat.BlockedNumberContractCompat; +import com.android.contacts.common.compat.TelecomManagerUtil; import com.android.contacts.common.dialog.ClearFrequentsDialog; import com.android.contacts.common.interactions.ImportExportDialogFragment; import com.android.contacts.common.list.ContactEntryListFragment; @@ -81,6 +80,10 @@ import com.android.contacts.common.util.ViewUtil; import com.android.contacts.common.widget.FloatingActionButtonController; import com.android.contacts.commonbind.ObjectFactory; import com.android.contacts.editor.EditorIntents; +import com.android.contacts.group.GroupListItem; +import com.android.contacts.group.GroupUtil; +import com.android.contacts.group.GroupsFragment; +import com.android.contacts.group.GroupsFragment.GroupsListener; import com.android.contacts.interactions.ContactDeletionInteraction; import com.android.contacts.interactions.ContactMultiDeletionInteraction; import com.android.contacts.interactions.ContactMultiDeletionInteraction.MultiContactDeleteListener; @@ -114,6 +117,7 @@ public class PeopleActivity extends AppCompatContactsActivity implements ActionBarAdapter.Listener, DialogManager.DialogShowingViewActivity, ContactListFilterController.ContactListFilterListener, + GroupsListener, ProviderStatusListener, MultiContactDeleteListener, JoinContactsListener, @@ -152,6 +156,7 @@ public class PeopleActivity extends AppCompatContactsActivity implements */ private MultiSelectContactsListFragment mAllFragment; private ContactTileListFragment mFavoritesFragment; + private GroupsFragment mGroupsFragment; /** ViewPager for swipe */ private ViewPager mTabPager; @@ -160,6 +165,8 @@ public class PeopleActivity extends AppCompatContactsActivity implements private String[] mTabTitles; private final TabPagerListener mTabPagerListener = new TabPagerListener(); + private NavigationView mNavigationView; + private boolean mEnableDebugMenuOptions; /** @@ -340,10 +347,10 @@ public class PeopleActivity extends AppCompatContactsActivity implements drawer.setDrawerListener(toggle); toggle.syncState(); - final NavigationView navigationView = (NavigationView) findViewById(R.id.nav_view); - navigationView.setNavigationItemSelectedListener(this); + mNavigationView = (NavigationView) findViewById(R.id.nav_view); + mNavigationView.setNavigationItemSelectedListener(this); - final Menu menu = navigationView.getMenu(); + final Menu menu = mNavigationView.getMenu(); if (HelpUtils.isHelpAndFeedbackAvailable()) { final MenuItem menuItem = menu.add(/* groupId */ R.id.misc, /* itemId */ R.id.nav_help, /* order */ Menu.NONE, /* titleRes */ R.string.menu_help); @@ -352,6 +359,7 @@ public class PeopleActivity extends AppCompatContactsActivity implements final String FAVORITE_TAG = "tab-pager-favorite"; final String ALL_TAG = "tab-pager-all"; + final String GROUPS_TAG = "groups"; // Create the fragments and add as children of the view pager. // The pager adapter will only change the visibility; it'll never create/destroy @@ -363,13 +371,20 @@ public class PeopleActivity extends AppCompatContactsActivity implements fragmentManager.findFragmentByTag(FAVORITE_TAG); mAllFragment = (MultiSelectContactsListFragment) fragmentManager.findFragmentByTag(ALL_TAG); + mGroupsFragment = (GroupsFragment) + fragmentManager.findFragmentByTag(GROUPS_TAG); if (mFavoritesFragment == null) { mFavoritesFragment = new ContactTileListFragment(); - mAllFragment = new MultiSelectContactsListFragment(); - transaction.add(R.id.tab_pager, mFavoritesFragment, FAVORITE_TAG); + + mAllFragment = new MultiSelectContactsListFragment(); transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG); + + if (areGroupWritableAccountsAvailable()) { + mGroupsFragment = new GroupsFragment(); + transaction.add(mGroupsFragment, GROUPS_TAG); + } } mFavoritesFragment.setListener(mFavoritesFragmentListener); @@ -377,10 +392,15 @@ public class PeopleActivity extends AppCompatContactsActivity implements mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener()); mAllFragment.setCheckBoxListListener(new CheckBoxListListener()); + if (areGroupWritableAccountsAvailable()) { + mGroupsFragment.setListener(this); + } + // Hide all fragments for now. We adjust visibility when we get onSelectedTabChanged() // from ActionBarAdapter. transaction.hide(mFavoritesFragment); transaction.hide(mAllFragment); + // Groups fragment has no UI, no need to hide it transaction.commitAllowingStateLoss(); fragmentManager.executePendingTransactions(); @@ -893,6 +913,26 @@ public class PeopleActivity extends AppCompatContactsActivity implements } @Override + public void onGroupsLoaded(List<GroupListItem> groupListItems) { + final Menu menu = mNavigationView.getMenu(); + menu.removeGroup(R.id.menu_groups); + if (groupListItems == null || groupListItems.isEmpty()) { + return; + } + for (GroupListItem groupListItem : groupListItems) { + if (groupListItem.isFirstGroupInAccount()) { + menu.addSubMenu(groupListItem.getAccountName()); + } + final String title = groupListItem.getMemberCount() == 0 ? groupListItem.getTitle() + : getString(R.string.group_name_menu_item, groupListItem.getTitle(), + groupListItem.getMemberCount()); + final MenuItem menuItem = + menu.add(R.id.menu_groups, Menu.NONE, Menu.CATEGORY_SYSTEM, title); + menuItem.setIntent(GroupUtil.createViewGroupIntent(this, groupListItem.getGroupId())); + } + } + + @Override public void onProviderStatusChange() { updateViewConfiguration(false); } @@ -1164,13 +1204,6 @@ public class PeopleActivity extends AppCompatContactsActivity implements } } - private void makeMenuItemEnabled(Menu menu, int itemId, boolean visible) { - final MenuItem item = menu.findItem(itemId); - if (item != null) { - item.setEnabled(visible); - } - } - @Override public boolean onOptionsItemSelected(MenuItem item) { if (mDisableOptionItemSelected) { @@ -1242,7 +1275,7 @@ public class PeopleActivity extends AppCompatContactsActivity implements return true; } } - return false; + return super.onOptionsItemSelected(item); } @SuppressWarnings("StatementWithEmptyBody") @@ -1254,6 +1287,10 @@ public class PeopleActivity extends AppCompatContactsActivity implements startActivity(new Intent(this, ContactsPreferenceActivity.class)); } else if (id == R.id.nav_help) { HelpUtils.launchHelpAndFeedbackForMainScreen(this); + } else if (item.getIntent() != null) { + ImplicitIntentsUtil.startActivityInApp(this, item.getIntent()); + } else { + Log.w(TAG, "Unhandled navigation view item selection"); } final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout); diff --git a/src/com/android/contacts/group/GroupBrowseListAdapter.java b/src/com/android/contacts/group/GroupBrowseListAdapter.java index 48751e72d..71b235cea 100644 --- a/src/com/android/contacts/group/GroupBrowseListAdapter.java +++ b/src/com/android/contacts/group/GroupBrowseListAdapter.java @@ -61,7 +61,7 @@ public class GroupBrowseListAdapter extends BaseAdapter { if (mSelectedGroupUri == null && cursor != null && cursor.getCount() > 0) { GroupListItem firstItem = getItem(0); long groupId = (firstItem == null) ? 0 : firstItem.getGroupId(); - mSelectedGroupUri = getGroupUriFromId(groupId); + mSelectedGroupUri = GroupUtil.getGroupUriFromId(groupId); } notifyDataSetChanged(); @@ -76,7 +76,7 @@ public class GroupBrowseListAdapter extends BaseAdapter { mCursor.moveToPosition(-1); while (mCursor.moveToNext()) { long groupId = mCursor.getLong(GroupListLoader.GROUP_ID); - Uri uri = getGroupUriFromId(groupId); + Uri uri = GroupUtil.getGroupUriFromId(groupId); if (mSelectedGroupUri.equals(uri)) { return index; } @@ -113,35 +113,7 @@ public class GroupBrowseListAdapter extends BaseAdapter { @Override public GroupListItem getItem(int position) { - if (mCursor == null || mCursor.isClosed() || !mCursor.moveToPosition(position)) { - return null; - } - String accountName = mCursor.getString(GroupListLoader.ACCOUNT_NAME); - String accountType = mCursor.getString(GroupListLoader.ACCOUNT_TYPE); - String dataSet = mCursor.getString(GroupListLoader.DATA_SET); - long groupId = mCursor.getLong(GroupListLoader.GROUP_ID); - String title = mCursor.getString(GroupListLoader.TITLE); - int memberCount = mCursor.getInt(GroupListLoader.MEMBER_COUNT); - - // Figure out if this is the first group for this account name / account type pair by - // checking the previous entry. This is to determine whether or not we need to display an - // account header in this item. - int previousIndex = position - 1; - boolean isFirstGroupInAccount = true; - if (previousIndex >= 0 && mCursor.moveToPosition(previousIndex)) { - String previousGroupAccountName = mCursor.getString(GroupListLoader.ACCOUNT_NAME); - String previousGroupAccountType = mCursor.getString(GroupListLoader.ACCOUNT_TYPE); - String previousGroupDataSet = mCursor.getString(GroupListLoader.DATA_SET); - - if (accountName.equals(previousGroupAccountName) && - accountType.equals(previousGroupAccountType) && - Objects.equal(dataSet, previousGroupDataSet)) { - isFirstGroupInAccount = false; - } - } - - return new GroupListItem(accountName, accountType, dataSet, groupId, title, - isFirstGroupInAccount, memberCount); + return GroupUtil.getGroupListItem(mCursor, position); } @Override @@ -180,7 +152,7 @@ public class GroupBrowseListAdapter extends BaseAdapter { } // Bind the group data - Uri groupUri = getGroupUriFromId(entry.getGroupId()); + Uri groupUri = GroupUtil.getGroupUriFromId(entry.getGroupId()); String memberCountString = mContext.getResources().getQuantityString( R.plurals.group_list_num_contacts_in_group, entry.getMemberCount(), entry.getMemberCount()); @@ -201,10 +173,6 @@ public class GroupBrowseListAdapter extends BaseAdapter { viewCache.accountName.setText(entry.getAccountName()); } - private static Uri getGroupUriFromId(long groupId) { - return ContentUris.withAppendedId(Groups.CONTENT_URI, groupId); - } - /** * Cache of the children views of a contact detail entry represented by a * {@link GroupListItem} diff --git a/src/com/android/contacts/group/GroupUtil.java b/src/com/android/contacts/group/GroupUtil.java new file mode 100644 index 000000000..f5b7db11f --- /dev/null +++ b/src/com/android/contacts/group/GroupUtil.java @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.group; + +import android.content.ContentUris; +import android.content.Context; +import android.content.Intent; +import android.database.Cursor; +import android.net.Uri; +import android.provider.ContactsContract.Groups; + +import com.android.contacts.GroupListLoader; +import com.android.contacts.activities.GroupDetailActivity; +import com.google.common.base.Objects; + +/** + * Group utility methods. + */ +public final class GroupUtil { + + private GroupUtil() { + } + + /** Returns a {@link GroupListItem} read from the given cursor and position. */ + static GroupListItem getGroupListItem(Cursor cursor, int position) { + if (cursor == null || cursor.isClosed() || !cursor.moveToPosition(position)) { + return null; + } + String accountName = cursor.getString(GroupListLoader.ACCOUNT_NAME); + String accountType = cursor.getString(GroupListLoader.ACCOUNT_TYPE); + String dataSet = cursor.getString(GroupListLoader.DATA_SET); + long groupId = cursor.getLong(GroupListLoader.GROUP_ID); + String title = cursor.getString(GroupListLoader.TITLE); + int memberCount = cursor.getInt(GroupListLoader.MEMBER_COUNT); + + // Figure out if this is the first group for this account name / account type pair by + // checking the previous entry. This is to determine whether or not we need to display an + // account header in this item. + int previousIndex = position - 1; + boolean isFirstGroupInAccount = true; + if (previousIndex >= 0 && cursor.moveToPosition(previousIndex)) { + String previousGroupAccountName = cursor.getString(GroupListLoader.ACCOUNT_NAME); + String previousGroupAccountType = cursor.getString(GroupListLoader.ACCOUNT_TYPE); + String previousGroupDataSet = cursor.getString(GroupListLoader.DATA_SET); + + if (accountName.equals(previousGroupAccountName) && + accountType.equals(previousGroupAccountType) && + Objects.equal(dataSet, previousGroupDataSet)) { + isFirstGroupInAccount = false; + } + } + + return new GroupListItem(accountName, accountType, dataSet, groupId, title, + isFirstGroupInAccount, memberCount); + } + + /** Returns an Intent to view the details of the group identified by the given Uri. */ + public static Intent createViewGroupIntent(Context context, long groupId) { + final Intent intent = new Intent(context, GroupDetailActivity.class); + intent.setData(getGroupUriFromId(groupId)); + return intent; + } + + /** TODO: Make it private after {@link GroupBrowseListAdapter} is removed. */ + static Uri getGroupUriFromId(long groupId) { + return ContentUris.withAppendedId(Groups.CONTENT_URI, groupId); + } +}
\ No newline at end of file diff --git a/src/com/android/contacts/group/GroupsFragment.java b/src/com/android/contacts/group/GroupsFragment.java new file mode 100644 index 000000000..15529c7bb --- /dev/null +++ b/src/com/android/contacts/group/GroupsFragment.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2016 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.contacts.group; + +import android.app.Fragment; +import android.app.LoaderManager; +import android.content.Context; +import android.content.CursorLoader; +import android.content.Loader; +import android.database.Cursor; +import android.os.Bundle; + +import com.android.contacts.GroupListLoader; + +import java.util.ArrayList; +import java.util.List; + +/** + * Loads groups and group metadata for all accounts. + */ +public final class GroupsFragment extends Fragment { + + private static final int LOADER_GROUPS = 1; + + /** + * Callbacks for hosts of the {@link GroupsFragment}. + */ + public interface GroupsListener { + + /** + * Invoked after groups and group metadata have been loaded. + */ + void onGroupsLoaded(List<GroupListItem> groupListItems); + } + + /** + * Group meta data loader listener. + */ + private final LoaderManager.LoaderCallbacks<Cursor> mGroupLoaderListener = + new LoaderManager.LoaderCallbacks<Cursor>() { + + @Override + public CursorLoader onCreateLoader(int id, Bundle args) { + return new GroupListLoader(mContext); + } + + @Override + public void onLoadFinished(Loader<Cursor> loader, Cursor data) { + mGroupListItems.clear(); + for (int i = 0; i < data.getCount(); i++) { + if (data.moveToNext()) { + mGroupListItems.add(GroupUtil.getGroupListItem(data, i)); + } + } + if (mListener != null) { + mListener.onGroupsLoaded(mGroupListItems); + } + } + + public void onLoaderReset(Loader<Cursor> loader) { + } + }; + + private Context mContext; + private List<GroupListItem> mGroupListItems = new ArrayList<>(); + private GroupsListener mListener; + + @Override + public void onAttach(Context context) { + super.onAttach(context); + mContext = context; + } + + @Override + public void onDetach() { + super.onDetach(); + mContext = null; + } + + @Override + public void onStart() { + getLoaderManager().initLoader(LOADER_GROUPS, null, mGroupLoaderListener); + super.onStart(); + } + + public void setListener(GroupsListener listener) { + mListener = listener; + } +} |