Merge "Make toolbar title larger in landscape"
diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 27c9f96..72c6481 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -16,8 +16,8 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.android.contacts"
- android:versionCode="10502"
- android:versionName="1.5.2">
+ android:versionCode="10503"
+ android:versionName="1.5.3">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="23" />
<original-package android:name="com.android.contacts" />
@@ -508,5 +508,11 @@
</provider>
<meta-data android:name="android.nfc.disable_beam_default" android:value="true" />
+
+ <receiver android:name="com.android.contacts.editor.AccountsChangedBroadcastReceiver">
+ <intent-filter>
+ <action android:name="android.accounts.LOGIN_ACCOUNTS_CHANGED"/>
+ </intent-filter>
+ </receiver>
</application>
</manifest>
diff --git a/res/drawable/ic_menu_group_add.xml b/res/drawable/ic_add.xml
similarity index 100%
rename from res/drawable/ic_menu_group_add.xml
rename to res/drawable/ic_add.xml
diff --git a/res/layout/contacts_drawer_activity.xml b/res/layout/contacts_drawer_activity.xml
new file mode 100644
index 0000000..008d4f5
--- /dev/null
+++ b/res/layout/contacts_drawer_activity.xml
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ 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.
+-->
+
+<android.support.v4.widget.DrawerLayout
+ xmlns:android="http://schemas.android.com/apk/res/android"
+ xmlns:app="http://schemas.android.com/apk/res-auto"
+ xmlns:tools="http://schemas.android.com/tools"
+ android:id="@+id/drawer_layout"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:fitsSystemWindows="true"
+ tools:openDrawer="start">
+
+ <!-- To prevent hamburger menu from getting the initial focus. -->
+ <View
+ android:focusable="true"
+ android:focusableInTouchMode="true"
+ android:layout_width="1px"
+ android:layout_height="1px" >
+ <requestFocus/>
+ </View>
+
+ <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/fragment_container"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent">
+
+ <include
+ layout="@layout/people_activity_toolbar"
+ android:id="@+id/toolbar_parent" />
+
+ <FrameLayout
+ android:id="@+id/content_frame"
+ android:layout_width="match_parent"
+ android:layout_height="fill_parent"
+ android:background="?android:attr/windowBackground" />
+ </LinearLayout>
+
+ <android.support.design.widget.NavigationView
+ android:id="@+id/nav_view"
+ android:layout_width="wrap_content"
+ android:layout_height="match_parent"
+ android:layout_gravity="start"
+ android:fitsSystemWindows="true"
+ app:headerLayout="@layout/nav_header_main"
+ app:menu="@menu/activity_main_drawer"/>
+
+</android.support.v4.widget.DrawerLayout>
\ No newline at end of file
diff --git a/res/layout/floating_action_button.xml b/res/layout/floating_action_button.xml
index 294d88b..95c76ae 100644
--- a/res/layout/floating_action_button.xml
+++ b/res/layout/floating_action_button.xml
@@ -33,5 +33,5 @@
android:background="@drawable/floating_action_button"
android:tint="@color/floating_action_button_icon_color"
android:contentDescription="@string/action_menu_add_new_contact_button"
- android:src="@drawable/ic_person_add_24dp"/>
+ android:src="@drawable/ic_add"/>
</FrameLayout>
\ No newline at end of file
diff --git a/res/layout/group_member_suggestion.xml b/res/layout/group_member_suggestion.xml
deleted file mode 100644
index 4fe8d20..0000000
--- a/res/layout/group_member_suggestion.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<?xml version="1.0" encoding="utf-8"?>
-<!--
-/*
-** Copyright 2011, 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.
-*/
--->
-
-<LinearLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- android:layout_width="match_parent"
- android:layout_height="48dip"
- android:orientation="horizontal"
- android:gravity="center_vertical"
- android:background="?android:attr/selectableItemBackground">
-
- <LinearLayout
- android:layout_width="0dip"
- android:layout_height="match_parent"
- android:layout_weight="1"
- android:gravity="center_vertical"
- android:orientation="vertical">
-
- <TextView
- android:id="@+id/text1"
- android:textAppearance="?android:attr/textAppearanceMedium"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="8dip"
- android:paddingStart="8dip"
- android:singleLine="true"
- android:ellipsize="end"/>
-
- <TextView android:id="@+id/text2"
- android:textAppearance="?android:attr/textAppearanceSmall"
- android:textColor="?android:attr/textColorSecondary"
- android:layout_width="wrap_content"
- android:layout_height="wrap_content"
- android:paddingLeft="8dip"
- android:paddingStart="8dip"
- android:singleLine="true"
- android:ellipsize="end" />
-
- </LinearLayout>
-
- <ImageView
- android:id="@+id/icon"
- android:layout_width="48dip"
- android:layout_height="48dip"
- android:cropToPadding="true"
- android:scaleType="centerCrop" />
-
-</LinearLayout>
\ No newline at end of file
diff --git a/res/layout/group_members_activity.xml b/res/layout/group_members_activity.xml
index 387fdd2..5466d2f 100644
--- a/res/layout/group_members_activity.xml
+++ b/res/layout/group_members_activity.xml
@@ -16,13 +16,8 @@
-->
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
- android:id="@+id/fragment_container"
+ android:id="@+id/fragment_container_inner"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
-
- <include
- layout="@layout/people_activity_toolbar"
- android:id="@+id/toolbar_parent" />
-
-</LinearLayout>
\ No newline at end of file
+</LinearLayout>
diff --git a/res/layout/people_activity.xml b/res/layout/people_activity.xml
index fe51c6f..d8b900d 100644
--- a/res/layout/people_activity.xml
+++ b/res/layout/people_activity.xml
@@ -14,69 +14,35 @@
limitations under the License.
-->
-<android.support.v4.widget.DrawerLayout
- xmlns:android="http://schemas.android.com/apk/res/android"
- xmlns:app="http://schemas.android.com/apk/res-auto"
- xmlns:tools="http://schemas.android.com/tools"
- android:id="@+id/drawer_layout"
+<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:id="@+id/list_container"
android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:fitsSystemWindows="true"
- tools:openDrawer="start">
+ android:layout_height="match_parent">
- <!-- To prevent hamburger menu from getting the initial focus. -->
- <View
- android:focusable="true"
- android:focusableInTouchMode="true"
- android:layout_width="1px"
- android:layout_height="1px" >
- <requestFocus/>
- </View>
+ <!--
+ ViewPager for swiping between tabs. We put fragments at runtime.
- <RelativeLayout
- android:id="@+id/list_container"
- android:layout_width="match_parent"
- android:layout_height="match_parent">
-
- <include
- layout="@layout/people_activity_toolbar"
- android:id="@+id/toolbar_parent" />
-
- <!--
- ViewPager for swiping between tabs. We put StrequentContactListFragment,
- DefaultContactBrowseListFragment and GroupBrowseListFragment at runtime.
-
- (Adding them directly as the children of this view is not recommended. ViewPager should
- be treated like a ListView, which doesn't expect children to be added from the layout.)
- -->
- <android.support.v4.view.ViewPager
- android:id="@+id/tab_pager"
- android:layout_height="match_parent"
- android:layout_width="match_parent"
- android:layout_below="@id/toolbar_parent"
- />
-
- <FrameLayout
- android:id="@+id/contacts_unavailable_view"
- android:layout_width="match_parent"
- android:layout_height="match_parent"
- android:layout_below="@id/toolbar_parent"
- android:visibility="gone">
- <FrameLayout
- android:id="@+id/contacts_unavailable_container"
- android:layout_height="match_parent"
- android:layout_width="match_parent" />
- </FrameLayout>
-
- <include layout="@layout/floating_action_button" />
- </RelativeLayout>
-
- <android.support.design.widget.NavigationView
- android:id="@+id/nav_view"
- android:layout_width="wrap_content"
+ (Adding them directly as the children of this view is not recommended. ViewPager should
+ be treated like a ListView, which doesn't expect children to be added from the layout.)
+ -->
+ <android.support.v4.view.ViewPager
+ android:id="@+id/tab_pager"
android:layout_height="match_parent"
- android:layout_gravity="start"
- app:headerLayout="@layout/nav_header_main"
- app:menu="@menu/activity_main_drawer"/>
+ android:layout_width="match_parent"
+ android:layout_below="@id/toolbar_parent"
+ />
-</android.support.v4.widget.DrawerLayout>
\ No newline at end of file
+ <FrameLayout
+ android:id="@+id/contacts_unavailable_view"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:layout_below="@id/toolbar_parent"
+ android:visibility="gone">
+ <FrameLayout
+ android:id="@+id/contacts_unavailable_container"
+ android:layout_height="match_parent"
+ android:layout_width="match_parent" />
+ </FrameLayout>
+
+ <include layout="@layout/floating_action_button" />
+</RelativeLayout>
diff --git a/res/menu/view_group.xml b/res/menu/view_group.xml
index 2fc36be..cf945fd 100644
--- a/res/menu/view_group.xml
+++ b/res/menu/view_group.xml
@@ -24,11 +24,6 @@
contacts:showAsAction="ifRoom" />
<item
- android:id="@+id/menu_search"
- android:icon="@drawable/ic_ab_search"
- android:title="@string/menu_search" />
-
- <item
android:id="@+id/menu_rename_group"
android:title="@string/menu_renameGroup"/>
diff --git a/res/values/styles.xml b/res/values/styles.xml
index fd6f56d..0358caa 100644
--- a/res/values/styles.xml
+++ b/res/values/styles.xml
@@ -134,8 +134,8 @@
<item name="colorPrimary">@color/primary_color</item>
<item name="android:colorAccent">@color/primary_color</item>
<item name="colorAccent">@color/primary_color</item>
- <item name="android:alertDialogTheme">@style/ContactsAlertDialogTheme</item>
- <item name="alertDialogTheme">@style/ContactsAlertDialogTheme</item>
+ <item name="android:alertDialogTheme">@style/ContactsAlertDialogThemeAppCompat</item>
+ <item name="alertDialogTheme">@style/ContactsAlertDialogThemeAppCompat</item>
<item name="list_item_height">?android:attr/listPreferredItemHeight</item>
<item name="activated_background">@drawable/list_item_activated_background</item>
<item name="section_header_background">@drawable/list_title_holo</item>
@@ -173,6 +173,13 @@
<item name="list_item_text_indent">@dimen/contact_browser_list_item_text_indent</item>
<!-- Favorites -->
<item name="favorites_padding_bottom">0dip</item>
+ <item name="drawerArrowStyle">@style/DrawerArrowStyle</item>
+ </style>
+
+ <style name="DrawerArrowStyle" parent="Widget.AppCompat.DrawerArrowToggle">
+ <item name="spinBars">true</item>
+ <item name="color">@android:color/white</item>
+ <item name="android:color">@android:color/white</item>
</style>
<style name="ContactsUnavailableButtonStyle" parent="@style/Widget.AppCompat.Button.Colored">
@@ -377,7 +384,13 @@
<item name="android:fontFamily">sans-serif</item>
</style>
- <style name="ContactsAlertDialogTheme" parent="Theme.AppCompat.Light.Dialog.MinWidth">
+ <!-- Inherit from Theme.Material.Light.Dialog instead of Theme.Material.Light.Dialog.Alert
+ since the Alert dialog is private. They are identical anyway. -->
+ <style name="ContactsAlertDialogTheme" parent="@android:style/Theme.Material.Light.Dialog">
+ <item name="android:colorAccent">@color/primary_color</item>
+ </style>
+
+ <style name="ContactsAlertDialogThemeAppCompat" parent="Theme.AppCompat.Light.Dialog.MinWidth">
<item name="android:colorAccent">@color/primary_color</item>
<item name="colorAccent">@color/primary_color</item>
</style>
diff --git a/src/com/android/contacts/ContactsDrawerActivity.java b/src/com/android/contacts/ContactsDrawerActivity.java
new file mode 100644
index 0000000..62a9258
--- /dev/null
+++ b/src/com/android/contacts/ContactsDrawerActivity.java
@@ -0,0 +1,312 @@
+/*
+ * 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;
+
+import android.app.FragmentManager;
+import android.app.FragmentTransaction;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.os.Bundle;
+import android.support.annotation.LayoutRes;
+import android.support.design.widget.NavigationView;
+import android.support.v4.view.GravityCompat;
+import android.support.v4.widget.DrawerLayout;
+import android.support.v7.app.ActionBarDrawerToggle;
+import android.support.v7.app.AppCompatActivity;
+import android.support.v7.widget.Toolbar;
+import android.telecom.TelecomManager;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.Menu;
+import android.view.MenuItem;
+import android.view.SubMenu;
+import android.view.ViewGroup;
+
+import com.android.contacts.common.ContactsUtils;
+import com.android.contacts.common.compat.BlockedNumberContractCompat;
+import com.android.contacts.common.compat.TelecomManagerUtil;
+import com.android.contacts.common.list.ContactListFilter;
+import com.android.contacts.common.list.ContactListFilterController;
+import com.android.contacts.common.preference.ContactsPreferenceActivity;
+import com.android.contacts.common.util.AccountFilterUtil;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
+import com.android.contacts.common.util.ViewUtil;
+import com.android.contacts.editor.ContactEditorFragment;
+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.AccountFiltersFragment;
+import com.android.contacts.interactions.AccountFiltersFragment.AccountFiltersListener;
+import com.android.contacts.quickcontact.QuickContactActivity;
+import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contactsbind.Assistants;
+import com.android.contactsbind.HelpUtils;
+
+import java.util.List;
+
+/**
+ * A common superclass for Contacts activities with a navigation drawer.
+ */
+public abstract class ContactsDrawerActivity extends AppCompatContactsActivity implements
+ AccountFiltersListener,
+ GroupsListener,
+ NavigationView.OnNavigationItemSelectedListener {
+
+ protected static String TAG = "ContactsDrawerActivity";
+
+ protected static final String GROUPS_TAG = "groups";
+ protected static final String FILTERS_TAG = "filters";
+
+ protected ContactListFilterController mContactListFilterController;
+ protected DrawerLayout mDrawer;
+ protected Toolbar mToolbar;
+ protected NavigationView mNavigationView;
+ protected GroupsFragment mGroupsFragment;
+ protected AccountFiltersFragment mAccountFiltersFragment;
+
+ @Override
+ protected void onCreate(Bundle savedState) {
+ super.onCreate(savedState);
+
+ mContactListFilterController = ContactListFilterController.getInstance(this);
+ mContactListFilterController.checkFilterValidity(false);
+
+ super.setContentView(R.layout.contacts_drawer_activity);
+
+ // Set up the action bar.
+ mToolbar = getView(R.id.toolbar);
+ setSupportActionBar(mToolbar);
+
+ // Add shadow under toolbar.
+ ViewUtil.addRectangularOutlineProvider(findViewById(R.id.toolbar_parent), getResources());
+
+ // Set up hamburger button.
+ mDrawer = (DrawerLayout) findViewById(R.id.drawer_layout);
+ final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, mDrawer, mToolbar,
+ R.string.navigation_drawer_open, R.string.navigation_drawer_close);
+ mDrawer.setDrawerListener(toggle);
+ toggle.syncState();
+
+ // Set up hamburger menu items.
+ mNavigationView = (NavigationView) findViewById(R.id.nav_view);
+ mNavigationView.setNavigationItemSelectedListener(this);
+
+ final Menu menu = mNavigationView.getMenu();
+ final boolean showBlockedNumbers = PhoneCapabilityTester.isPhone(this)
+ && ContactsUtils.FLAG_N_FEATURE
+ && BlockedNumberContractCompat.canCurrentUserBlockNumbers(this);
+
+ if (!showBlockedNumbers) {
+ menu.removeItem(R.id.nav_blocked_numbers);
+ }
+
+ if (Assistants.getDuplicatesActivityIntent(this) == null) {
+ menu.removeItem(R.id.nav_find_duplicates);
+ }
+
+ if (!HelpUtils.isHelpAndFeedbackAvailable()) {
+ menu.removeItem(R.id.nav_help);
+ }
+
+ // Set up fragment manager to load groups and filters.
+ final FragmentManager fragmentManager = getFragmentManager();
+ final FragmentTransaction transaction = fragmentManager.beginTransaction();
+ addGroupsAndFiltersFragments(transaction);
+ transaction.commitAllowingStateLoss();
+ fragmentManager.executePendingTransactions();
+ }
+
+ @Override
+ public void setContentView(@LayoutRes int layoutResID) {
+ final ViewGroup parent = (ViewGroup) findViewById(R.id.content_frame);
+ if (parent != null) {
+ parent.removeAllViews();
+ }
+ LayoutInflater.from(this).inflate(layoutResID, parent);
+ }
+
+ protected void addGroupsAndFiltersFragments(FragmentTransaction transaction) {
+ final FragmentManager fragmentManager = getFragmentManager();
+ mGroupsFragment = (GroupsFragment) fragmentManager.findFragmentByTag(GROUPS_TAG);
+ mAccountFiltersFragment = (AccountFiltersFragment)
+ fragmentManager.findFragmentByTag(FILTERS_TAG);
+
+ if (mGroupsFragment == null && ContactsUtils.areGroupWritableAccountsAvailable(this)) {
+ mGroupsFragment = new GroupsFragment();
+ transaction.add(mGroupsFragment, GROUPS_TAG);
+ }
+
+ if (mAccountFiltersFragment == null) {
+ mAccountFiltersFragment = new AccountFiltersFragment();
+ transaction.add(mAccountFiltersFragment, FILTERS_TAG);
+ }
+
+ if (ContactsUtils.areGroupWritableAccountsAvailable(this) && mGroupsFragment != null) {
+ mGroupsFragment.setListener(this);
+ }
+ mAccountFiltersFragment.setListener(this);
+ }
+
+ @Override
+ public void onGroupsLoaded(List<GroupListItem> groupListItems) {
+ final Menu menu = mNavigationView.getMenu();
+ final MenuItem groupsMenuItem = menu.findItem(R.id.nav_groups);
+ final SubMenu subMenu = groupsMenuItem.getSubMenu();
+ subMenu.removeGroup(R.id.nav_groups_items);
+
+ if (groupListItems != null) {
+ // Add each group
+ for (final GroupListItem groupListItem : groupListItems) {
+ if (GroupUtil.isEmptyFFCGroup(groupListItem)) {
+ continue;
+ }
+ final String title = groupListItem.getTitle();
+ final MenuItem menuItem =
+ subMenu.add(R.id.nav_groups_items, Menu.NONE, Menu.NONE, title);
+ menuItem.setIcon(R.drawable.ic_menu_label);
+ menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ onGroupMenuItemClicked(groupListItem.getGroupId());
+ return true;
+ }
+ });
+ }
+ }
+
+ // Create a menu item in the sub menu to add new groups
+ final MenuItem menuItem = subMenu.add(R.id.nav_groups_items, Menu.NONE, Menu.NONE,
+ getString(R.string.menu_new_group_action_bar));
+ menuItem.setIcon(R.drawable.ic_add);
+ menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ onCreateGroupMenuItemClicked();
+ return true;
+ }
+ });
+ }
+
+ protected void onGroupMenuItemClicked(long groupId) {
+ final Intent intent = GroupUtil.createViewGroupIntent(this, groupId);
+ startActivity(intent);
+ }
+
+ protected void onCreateGroupMenuItemClicked() {
+ startActivity(GroupUtil.createAddGroupIntent(this));
+ mDrawer.closeDrawer(GravityCompat.START);
+ }
+
+ @Override
+ public void onFiltersLoaded(List<ContactListFilter> accountFilterItems) {
+ final Menu menu = mNavigationView.getMenu();
+ final MenuItem filtersMenuItem = menu.findItem(R.id.nav_filters);
+ final SubMenu subMenu = filtersMenuItem.getSubMenu();
+ subMenu.removeGroup(R.id.nav_filters_items);
+
+ if (accountFilterItems == null || accountFilterItems.size() < 2) {
+ return;
+ }
+
+ for (int i = 0; i < accountFilterItems.size(); i++) {
+ final ContactListFilter filter = accountFilterItems.get(i);
+ final String accountName = filter.accountName;
+ final MenuItem menuItem = subMenu.add(R.id.nav_filters_items, Menu.NONE, Menu.NONE,
+ accountName);
+ final Intent intent = new Intent();
+ intent.putExtra(AccountFilterUtil.EXTRA_CONTACT_LIST_FILTER, filter);
+ menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
+ @Override
+ public boolean onMenuItemClick(MenuItem item) {
+ mDrawer.closeDrawer(GravityCompat.START);
+ AccountFilterUtil.handleAccountFilterResult(mContactListFilterController,
+ AppCompatActivity.RESULT_OK, intent);
+ if (shouldFinish()) {
+ finish();
+ }
+ return true;
+ }
+ });
+ menuItem.setIcon(filter.icon);
+ // Get rid of the default memu item overlay and show original account icons.
+ menuItem.getIcon().setColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);
+ }
+ }
+
+ /**
+ * @return true if the child activity should finish after launching another activity.
+ */
+ protected abstract boolean shouldFinish();
+
+ @Override
+ public boolean onNavigationItemSelected(MenuItem item) {
+ final int id = item.getItemId();
+
+ if (id == R.id.nav_settings) {
+ startActivity(createPreferenceIntent());
+ } else if (id == R.id.nav_help) {
+ HelpUtils.launchHelpAndFeedbackForMainScreen(this);
+ } else if (id == R.id.nav_all_contacts) {
+ switchToAllContacts();
+ } else if (id == R.id.nav_blocked_numbers) {
+ final Intent intent = TelecomManagerUtil.createManageBlockedNumbersIntent(
+ (TelecomManager) getSystemService(Context.TELECOM_SERVICE));
+ ImplicitIntentsUtil.startActivityInApp(this, intent);
+ } else if (id == R.id.nav_find_duplicates) {
+ launchFindDuplicates();
+ } else if (item.getIntent() != null) {
+ ImplicitIntentsUtil.startActivityInApp(this, item.getIntent());
+ } else {
+ Log.w(TAG, "Unhandled navigation view item selection");
+ }
+
+ mDrawer.closeDrawer(GravityCompat.START);
+ return true;
+ }
+
+ private Intent createPreferenceIntent() {
+ final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
+ intent.putExtra(ContactsPreferenceActivity.EXTRA_NEW_LOCAL_PROFILE,
+ ContactEditorFragment.INTENT_EXTRA_NEW_LOCAL_PROFILE);
+ intent.putExtra(ContactsPreferenceActivity.EXTRA_MODE_FULLY_EXPANDED,
+ QuickContactActivity.MODE_FULLY_EXPANDED);
+ intent.putExtra(ContactsPreferenceActivity.EXTRA_PREVIOUS_SCREEN_TYPE,
+ QuickContactActivity.EXTRA_PREVIOUS_SCREEN_TYPE);
+ return intent;
+ }
+
+ protected void switchToAllContacts() {
+ final Intent intent = new Intent();
+ final ContactListFilter filter = createAllAccountsFilter();
+ intent.putExtra(AccountFilterUtil.EXTRA_CONTACT_LIST_FILTER, filter);
+ AccountFilterUtil.handleAccountFilterResult(
+ mContactListFilterController, AppCompatActivity.RESULT_OK, intent);
+ }
+
+ protected void launchFindDuplicates() {
+ ImplicitIntentsUtil.startActivityInAppIfPossible(this,
+ Assistants.getDuplicatesActivityIntent(this));
+ }
+
+ protected ContactListFilter createAllAccountsFilter() {
+ return ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ }
+
+}
diff --git a/src/com/android/contacts/GroupListLoader.java b/src/com/android/contacts/GroupListLoader.java
index 5bb240f..4816a5e 100644
--- a/src/com/android/contacts/GroupListLoader.java
+++ b/src/com/android/contacts/GroupListLoader.java
@@ -20,6 +20,8 @@
import android.net.Uri;
import android.provider.ContactsContract.Groups;
+import com.android.contacts.group.GroupUtil;
+
/**
* Group loader for the group list that includes details such as the number of contacts per group
* and number of groups per account. This list is sorted by account type, account name, where the
@@ -56,12 +58,11 @@
private static final Uri GROUP_LIST_URI = Groups.CONTENT_SUMMARY_URI;
public GroupListLoader(Context context) {
- // Sort groups from all accounts alphabettically and in a localized way.
super(context,
GROUP_LIST_URI,
COLUMNS,
DEFAULT_SELECTION,
null,
- Groups.TITLE + " COLLATE LOCALIZED ASC");
+ GroupUtil.getGroupsSortOrder());
}
}
diff --git a/src/com/android/contacts/GroupMemberLoader.java b/src/com/android/contacts/GroupMemberLoader.java
index 9f55848..8bc7d04 100644
--- a/src/com/android/contacts/GroupMemberLoader.java
+++ b/src/com/android/contacts/GroupMemberLoader.java
@@ -52,34 +52,8 @@
public static final int CONTACT_PHOTO_ID = 5;
}
- public static class GroupDetailQuery {
- private static final String[] PROJECTION = new String[] {
- Data.CONTACT_ID, // 0
- Data.PHOTO_URI, // 1
- Data.LOOKUP_KEY, // 2
- Data.DISPLAY_NAME_PRIMARY, // 3
- Data.CONTACT_PRESENCE, // 4
- Data.CONTACT_STATUS, // 5
- };
-
- public static final int CONTACT_ID = 0;
- public static final int CONTACT_PHOTO_URI = 1;
- public static final int CONTACT_LOOKUP_KEY = 2;
- public static final int CONTACT_DISPLAY_NAME_PRIMARY = 3;
- public static final int CONTACT_PRESENCE_STATUS = 4;
- public static final int CONTACT_STATUS = 5;
- }
-
private final long mGroupId;
- /**
- * @return GroupMemberLoader object which can be used in group editor.
- */
- public static GroupMemberLoader constructLoaderForGroupEditorQuery(
- Context context, long groupId) {
- return new GroupMemberLoader(context, groupId, GroupEditorQuery.PROJECTION);
- }
-
private GroupMemberLoader(Context context, long groupId, String[] projection) {
super(context);
mGroupId = groupId;
diff --git a/src/com/android/contacts/GroupMetaDataLoader.java b/src/com/android/contacts/GroupMetaDataLoader.java
index ad9b0f9..8cdca2e 100644
--- a/src/com/android/contacts/GroupMetaDataLoader.java
+++ b/src/com/android/contacts/GroupMetaDataLoader.java
@@ -20,6 +20,8 @@
import android.net.Uri;
import android.provider.ContactsContract.Groups;
+import com.android.contacts.group.GroupUtil;
+
/**
* Group meta-data loader. Loads all groups or just a single group from the
* database (if given a {@link Uri}).
@@ -52,7 +54,7 @@
super(context, ensureIsGroupUri(groupUri), COLUMNS,
Groups.ACCOUNT_TYPE + " NOT NULL AND " + Groups.ACCOUNT_NAME + " NOT NULL AND "
+ Groups.DELETED + "=0",
- null, Groups.TITLE + " COLLATE NOCASE ASC");
+ null, GroupUtil.getGroupsSortOrder());
}
/**
diff --git a/src/com/android/contacts/activities/ActionBarAdapter.java b/src/com/android/contacts/activities/ActionBarAdapter.java
index 7042121..3af9c4b 100644
--- a/src/com/android/contacts/activities/ActionBarAdapter.java
+++ b/src/com/android/contacts/activities/ActionBarAdapter.java
@@ -21,6 +21,7 @@
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
+import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.content.ContextCompat;
@@ -517,9 +518,7 @@
R.color.contextual_selection_bar_status_bar_color);
mActivity.getWindow().setStatusBarColor(cabStatusBarColor);
} else {
- final int normalStatusBarColor = ContextCompat.getColor(
- mActivity, R.color.primary_color_dark);
- mActivity.getWindow().setStatusBarColor(normalStatusBarColor);
+ mActivity.getWindow().setStatusBarColor(Color.TRANSPARENT);
}
}
diff --git a/src/com/android/contacts/activities/ContactEditorBaseActivity.java b/src/com/android/contacts/activities/ContactEditorBaseActivity.java
index ffbeb49..97095f0 100644
--- a/src/com/android/contacts/activities/ContactEditorBaseActivity.java
+++ b/src/com/android/contacts/activities/ContactEditorBaseActivity.java
@@ -16,6 +16,18 @@
package com.android.contacts.activities;
+import android.app.ActionBar;
+import android.app.Dialog;
+import android.content.ContentValues;
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.ContactsContract.Contacts;
+import android.provider.ContactsContract.RawContacts;
+import android.util.Log;
+import android.view.View;
+import android.view.inputmethod.InputMethodManager;
+
import com.android.contacts.ContactSaveService;
import com.android.contacts.ContactsActivity;
import com.android.contacts.R;
@@ -29,18 +41,6 @@
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.util.DialogManager;
-import android.app.ActionBar;
-import android.app.Dialog;
-import android.content.ContentValues;
-import android.content.Intent;
-import android.net.Uri;
-import android.os.Bundle;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.RawContacts;
-import android.util.Log;
-import android.view.View;
-import android.view.inputmethod.InputMethodManager;
-
import java.util.ArrayList;
/**
@@ -68,6 +68,8 @@
public static final String ACTION_SAVE_COMPLETED = "saveCompleted";
public static final int RESULT_CODE_SPLIT = 2;
+ // 3 used for ContactDeletionInteraction.RESULT_CODE_DELETED
+ public static final int RESULT_CODE_EDITED = 4;
protected int mActionBarTitleResId;
diff --git a/src/com/android/contacts/activities/ContactSelectionActivity.java b/src/com/android/contacts/activities/ContactSelectionActivity.java
index 80a4acb..17ba765 100644
--- a/src/com/android/contacts/activities/ContactSelectionActivity.java
+++ b/src/com/android/contacts/activities/ContactSelectionActivity.java
@@ -139,10 +139,9 @@
.inflate(R.layout.custom_action_bar, null);
mSearchView = (SearchView) mSearchViewContainer.findViewById(R.id.search_view);
- // Postal address group member,and legacy pickers don't support search, so just show
+ // Postal address pickers (and legacy pickers) don't support search, so just show
// "HomeAsUp" button and title.
if (mRequest.getActionCode() == ContactsRequest.ACTION_PICK_POSTAL ||
- mRequest.getActionCode() == ContactsRequest.ACTION_PICK_GROUP_MEMBERS ||
mRequest.isLegacyCompatibilityMode()) {
mSearchView.setVisibility(View.GONE);
if (actionBar != null) {
@@ -357,11 +356,16 @@
}
case ContactsRequest.ACTION_PICK_GROUP_MEMBERS: {
- final AccountWithDataSet account = getIntent().getParcelableExtra(
- UiIntentActions.GROUP_ACCOUNT_WITH_DATA_SET);
- final ArrayList<String> rawContactIds = getIntent().getStringArrayListExtra(
- UiIntentActions.GROUP_RAW_CONTACT_IDS);
- mListFragment = GroupMemberPickerFragment.newInstance(account, rawContactIds);
+ final String accountName = getIntent().getStringExtra(
+ UiIntentActions.GROUP_ACCOUNT_NAME);
+ final String accountType = getIntent().getStringExtra(
+ UiIntentActions.GROUP_ACCOUNT_TYPE);
+ final String accountDataSet = getIntent().getStringExtra(
+ UiIntentActions.GROUP_ACCOUNT_DATA_SET);
+ final ArrayList<String> contactIds = getIntent().getStringArrayListExtra(
+ UiIntentActions.GROUP_CONTACT_IDS);
+ mListFragment = GroupMemberPickerFragment.newInstance(
+ accountName, accountType, accountDataSet, contactIds);
break;
}
@@ -483,8 +487,10 @@
private final class GroupMemberPickerListener implements GroupMemberPickerFragment.Listener {
@Override
- public void onGroupMemberClicked(Uri uri) {
- returnPickerResult(uri);
+ public void onGroupMemberClicked(long contactId) {
+ final Intent intent = new Intent();
+ intent.putExtra(UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, contactId);
+ returnPickerResult(intent);
}
}
diff --git a/src/com/android/contacts/activities/GroupMembersActivity.java b/src/com/android/contacts/activities/GroupMembersActivity.java
index f41972e..01999bb 100644
--- a/src/com/android/contacts/activities/GroupMembersActivity.java
+++ b/src/com/android/contacts/activities/GroupMembersActivity.java
@@ -17,27 +17,27 @@
import android.accounts.Account;
import android.app.FragmentManager;
+import android.content.Context;
+import android.app.FragmentTransaction;
import android.app.LoaderManager.LoaderCallbacks;
import android.content.CursorLoader;
import android.content.Intent;
-import android.content.Loader;
import android.database.Cursor;
import android.net.Uri;
+import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Intents;
+import android.provider.ContactsContract.RawContacts;
+import android.support.v4.view.GravityCompat;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
-import android.view.View;
-import android.widget.AdapterView;
-import android.widget.AdapterView.OnItemClickListener;
-import android.widget.AutoCompleteTextView;
import android.widget.Toast;
-import com.android.contacts.AppCompatContactsActivity;
import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsDrawerActivity;
import com.android.contacts.GroupMemberLoader;
import com.android.contacts.GroupMemberLoader.GroupEditorQuery;
import com.android.contacts.R;
@@ -53,9 +53,6 @@
import com.android.contacts.group.GroupMembersListFragment;
import com.android.contacts.group.GroupMetadata;
import com.android.contacts.group.GroupNameEditDialogFragment;
-import com.android.contacts.group.Member;
-import com.android.contacts.group.SuggestedMemberListAdapter;
-import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
import com.android.contacts.interactions.GroupDeletionDialogFragment;
import com.android.contacts.list.ContactsRequest;
import com.android.contacts.list.MultiSelectContactsListFragment;
@@ -68,7 +65,7 @@
/**
* Displays the members of a group and allows the user to edit it.
*/
-public class GroupMembersActivity extends AppCompatContactsActivity implements
+public class GroupMembersActivity extends ContactsDrawerActivity implements
ActionBarAdapter.Listener,
MultiSelectContactsListFragment.OnCheckBoxListActionListener,
SelectAccountDialogFragment.Listener,
@@ -85,8 +82,6 @@
private static final String TAG_SELECT_ACCOUNT_DIALOG = "selectAccountDialog";
private static final String TAG_GROUP_NAME_EDIT_DIALOG = "groupNameEditDialog";
- private static final int LOADER_GROUP_MEMBERS = 0;
-
private static final String ACTION_DELETE_GROUP = "deleteGroup";
private static final String ACTION_CREATE_GROUP = "createGroup";
private static final String ACTION_UPDATE_GROUP = "updateGroup";
@@ -95,35 +90,74 @@
private static final int RESULT_GROUP_ADD_MEMBER = 100;
- /** Loader callbacks for existing group members for the autocomplete text view. */
- private final LoaderCallbacks<Cursor> mGroupMemberCallbacks = new LoaderCallbacks<Cursor>() {
+ /**
+ * Starts an Intent to add the raw contacts for a given contact ID to a group.
+ * Only the raw contacts that belong to the specified account are added.
+ */
+ private static class AddGroupMembersAsyncTask extends AsyncTask<Void, Void, Intent> {
- @Override
- public CursorLoader onCreateLoader(int id, Bundle args) {
- return GroupMemberLoader.constructLoaderForGroupEditorQuery(
- GroupMembersActivity.this, mGroupMetadata.groupId);
+ private final Context mContext;
+ private final long mContactId;
+ private final long mGroupId;
+ private final String mAccountName;
+ private final String mAccountType;
+
+ AddGroupMembersAsyncTask(Context context, long contactId, long groupId, String accountName,
+ String accountType) {
+ mContext = context;
+ mContactId = contactId;
+ mGroupId = groupId;
+ mAccountName = accountName;
+ mAccountType = accountType;
}
@Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
- final List<Member> members = new ArrayList<>();
- data.moveToPosition(-1);
- while (data.moveToNext()) {
- members.add(new Member(
- data.getLong(GroupEditorQuery.RAW_CONTACT_ID),
- data.getString(GroupEditorQuery.CONTACT_LOOKUP_KEY),
- data.getLong(GroupEditorQuery.CONTACT_ID),
- data.getString(GroupEditorQuery.CONTACT_DISPLAY_NAME_PRIMARY),
- data.getString(GroupEditorQuery.CONTACT_PHOTO_URI),
- data.getLong(GroupEditorQuery.CONTACT_PHOTO_ID)));
+ protected Intent doInBackground(Void... params) {
+ final long[] rawContactIdsToAdd = getRawContactIdsToAdd();
+ if (rawContactIdsToAdd.length == 0) {
+ return null;
}
+ return ContactSaveService.createGroupUpdateIntent(
+ mContext, mGroupId, /* newLabel */ null,
+ rawContactIdsToAdd, /* rawContactIdsToRemove */ null,
+ GroupMembersActivity.class, GroupMembersActivity.ACTION_ADD_TO_GROUP);
+ }
- bindAutocompleteGroupMembers(members);
+ // TODO(wjang): prune raw contacts that are already in the group; ContactSaveService will
+ // log a warning if the raw contact is already a member and keep going but it is not ideal.
+ private long[] getRawContactIdsToAdd() {
+ final Uri rawContactUri = RawContacts.CONTENT_URI.buildUpon()
+ .appendQueryParameter(RawContacts.ACCOUNT_NAME, mAccountName)
+ .appendQueryParameter(RawContacts.ACCOUNT_TYPE, mAccountType)
+ .build();
+ final String[] projection = new String[]{RawContacts._ID};
+ final String selection = RawContacts.CONTACT_ID + "=?";
+ final String[] selectionArgs = new String[1];
+ selectionArgs[0] = Long.toString(mContactId);
+ final Cursor cursor = mContext.getContentResolver().query(
+ rawContactUri, projection, selection, selectionArgs, null, null);
+ final long[] rawContactIds = new long[cursor.getCount()];
+ try {
+ int i = 0;
+ while (cursor.moveToNext()) {
+ rawContactIds[i] = cursor.getLong(0);
+ i++;
+ }
+ } finally {
+ cursor.close();
+ }
+ return rawContactIds;
}
@Override
- public void onLoaderReset(Loader<Cursor> loader) {}
- };
+ protected void onPostExecute(Intent intent) {
+ if (intent == null) {
+ Toast.makeText(mContext, R.string.groupSavedErrorToast, Toast.LENGTH_SHORT).show();
+ } else {
+ mContext.startService(intent);
+ }
+ }
+ }
private ActionBarAdapter mActionBarAdapter;
@@ -131,8 +165,6 @@
private GroupMembersListFragment mMembersListFragment;
- private SuggestedMemberListAdapter mAutoCompleteAdapter;
-
private Uri mGroupUri;
private boolean mIsInsertAction;
@@ -158,13 +190,10 @@
setContentView(R.layout.group_members_activity);
// Set up the action bar
- final Toolbar toolbar = getView(R.id.toolbar);
- setSupportActionBar(toolbar);
mActionBarAdapter = new ActionBarAdapter(this, this, getSupportActionBar(),
- /* portraitTabs */ null, /* landscapeTabs */ null, toolbar,
+ /* portraitTabs */ null, /* landscapeTabs */ null, mToolbar,
R.string.enter_contact_name);
mActionBarAdapter.setShowHomeIcon(true);
- mActionBarAdapter.setShowHomeAsUp(true);
// Decide whether to prompt for the account and group name or start loading existing members
if (mIsInsertAction) {
@@ -189,17 +218,14 @@
}
}
} else {
- // Add the members list fragment
final FragmentManager fragmentManager = getFragmentManager();
+ // Add the members list fragment
mMembersListFragment = (GroupMembersListFragment)
fragmentManager.findFragmentByTag(TAG_GROUP_MEMBERS);
if (mMembersListFragment == null) {
mMembersListFragment = GroupMembersListFragment.newInstance(getIntent().getData());
- fragmentManager.beginTransaction()
- .replace(R.id.fragment_container, mMembersListFragment, TAG_GROUP_MEMBERS)
- .commit();
- } else {
- getLoaderManager().initLoader(LOADER_GROUP_MEMBERS, null, mGroupMemberCallbacks);
+ fragmentManager.beginTransaction().replace(R.id.fragment_container_inner,
+ mMembersListFragment, TAG_GROUP_MEMBERS).commitAllowingStateLoss();
}
mMembersListFragment.setListener(this);
if (mGroupMetadata != null && mGroupMetadata.editable) {
@@ -267,9 +293,13 @@
mMembersListFragment = GroupMembersListFragment.newInstance(groupUri);
mMembersListFragment.setListener(this);
- getFragmentManager().beginTransaction()
- .replace(R.id.fragment_container, mMembersListFragment, TAG_GROUP_MEMBERS)
- .commit();
+
+ final FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ addGroupsAndFiltersFragments(transaction);
+ transaction.replace(
+ R.id.fragment_container_inner, mMembersListFragment, TAG_GROUP_MEMBERS)
+ .commitAllowingStateLoss();
+
if (mGroupMetadata != null && mGroupMetadata.editable) {
mMembersListFragment.setCheckBoxListListener(this);
}
@@ -298,6 +328,32 @@
}
@Override
+ protected void onGroupMenuItemClicked(long groupId) {
+ if (mGroupMetadata.groupId != groupId) {
+ super.onGroupMenuItemClicked(groupId);
+ finish();
+ }
+ mDrawer.closeDrawer(GravityCompat.START);
+ }
+
+ @Override
+ protected boolean shouldFinish() {
+ return true;
+ }
+
+ @Override
+ protected void switchToAllContacts() {
+ super.switchToAllContacts();
+ finish();
+ }
+
+ @Override
+ protected void launchFindDuplicates() {
+ super.launchFindDuplicates();
+ finish();
+ }
+
+ @Override
public boolean onCreateOptionsMenu(Menu menu) {
if (mGroupMetadata == null || mGroupMetadata.memberCount < 0) {
// Hide menu options until metadata is fully loaded
@@ -311,25 +367,13 @@
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
final boolean isSelectionMode = mActionBarAdapter.isSelectionMode();
- final boolean isSearchMode = mActionBarAdapter.isSearchMode();
-
final boolean isGroupEditable = mGroupMetadata != null && mGroupMetadata.editable;
final boolean isGroupReadOnly = mGroupMetadata != null && mGroupMetadata.readOnly;
- setVisible(menu, R.id.menu_add,
- isGroupEditable && !isSelectionMode && !isSearchMode);
-
- setVisible(menu, R.id.menu_search,
- isGroupEditable && !isSelectionMode && !isSearchMode);
-
- setVisible(menu, R.id.menu_rename_group,
- isGroupEditable && !isSelectionMode && !isSearchMode);
-
- setVisible(menu, R.id.menu_delete_group,
- !isGroupReadOnly && !isSelectionMode && !isSearchMode);
-
- setVisible(menu, R.id.menu_remove_from_group,
- isGroupEditable && isSelectionMode);
+ setVisible(menu, R.id.menu_add, isGroupEditable && !isSelectionMode);
+ setVisible(menu, R.id.menu_rename_group, isGroupEditable && !isSelectionMode);
+ setVisible(menu, R.id.menu_delete_group, !isGroupReadOnly && !isSelectionMode);
+ setVisible(menu, R.id.menu_remove_from_group, isGroupEditable && isSelectionMode);
return true;
}
@@ -351,19 +395,14 @@
case R.id.menu_add: {
final Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(ContactsContract.Groups.CONTENT_ITEM_TYPE);
- intent.putExtra(UiIntentActions.GROUP_ACCOUNT_WITH_DATA_SET,
- mGroupMetadata.createAccountWithDataSet());
- intent.putExtra(UiIntentActions.GROUP_RAW_CONTACT_IDS,
- getExistingGroupMemberRawContactIds());
+ intent.putExtra(UiIntentActions.GROUP_ACCOUNT_NAME, mGroupMetadata.accountName);
+ intent.putExtra(UiIntentActions.GROUP_ACCOUNT_TYPE, mGroupMetadata.accountType);
+ intent.putExtra(UiIntentActions.GROUP_ACCOUNT_DATA_SET, mGroupMetadata.dataSet);
+ intent.putExtra(UiIntentActions.GROUP_CONTACT_IDS,
+ getExistingGroupMemberContactIds());
startActivityForResult(intent, RESULT_GROUP_ADD_MEMBER);
return true;
}
- case R.id.menu_search: {
- if (mActionBarAdapter != null) {
- mActionBarAdapter.setSearchMode(true);
- }
- return true;
- }
case R.id.menu_rename_group: {
GroupNameEditDialogFragment.showUpdateDialog(
getFragmentManager(), TAG_GROUP_NAME_EDIT_DIALOG, mGroupMetadata.groupName);
@@ -385,15 +424,15 @@
return super.onOptionsItemSelected(item);
}
- private ArrayList<String> getExistingGroupMemberRawContactIds() {
- final ArrayList<String> rawContactIds = new ArrayList<>();
+ private ArrayList<String> getExistingGroupMemberContactIds() {
+ final ArrayList<String> contactIds = new ArrayList<>();
final Cursor cursor = mMembersListFragment.getAdapter().getCursor(/* partition */ 0);
if (cursor != null && cursor.moveToFirst()) {
do {
- rawContactIds.add(cursor.getString(GroupMembersQuery.RAW_CONTACT_ID));
+ contactIds.add(cursor.getString(GroupMembersQuery.CONTACT_ID));
} while (cursor.moveToNext());
}
- return rawContactIds;
+ return contactIds;
}
private void deleteGroup() {
@@ -408,7 +447,6 @@
}
}
- // TODO(wjang): replace this with group events
private void logListEvent() {
Logger.logListEvent(
ListEvent.ActionType.REMOVE_LABEL,
@@ -432,7 +470,12 @@
@Override
public void onBackPressed() {
- if (mIsInsertAction) {
+ if (!isSafeToCommitTransactions()) {
+ return;
+ }
+ if (mDrawer.isDrawerOpen(GravityCompat.START)) {
+ mDrawer.closeDrawer(GravityCompat.START);
+ } else if (mIsInsertAction) {
finish();
} else if (mActionBarAdapter.isSelectionMode()) {
mActionBarAdapter.setSelectionMode(false);
@@ -446,30 +489,14 @@
}
}
-
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == RESULT_GROUP_ADD_MEMBER && resultCode == RESULT_OK && data != null) {
- final Uri rawContactUri = data.getData();
- if (rawContactUri != null) {
- long rawContactId = -1;
- try {
- rawContactId = Long.parseLong(rawContactUri.getLastPathSegment());
- } catch (NumberFormatException ignored) {}
- if (rawContactId < 0) {
- Toast.makeText(this, R.string.groupSavedErrorToast, Toast.LENGTH_SHORT).show();
- Log.w(TAG, "Failed to parse ID from pick group member result uri " +
- rawContactUri);
- return;
- }
- final long[] rawContactIdsToAdd = new long[1];
- rawContactIdsToAdd[0] = rawContactId;
- final Intent intent = ContactSaveService.createGroupUpdateIntent(
- GroupMembersActivity.this, mGroupMetadata.groupId, /* newLabel */ null,
- rawContactIdsToAdd, /* rawContactIdsToRemove */ null,
- GroupMembersActivity.class, GroupMembersActivity.ACTION_ADD_TO_GROUP);
- startService(intent);
- }
+ final long contactId = data.getLongExtra(
+ UiIntentActions.TARGET_CONTACT_ID_EXTRA_KEY, -1);
+ new AddGroupMembersAsyncTask(this, contactId, mGroupMetadata.groupId,
+ mGroupMetadata.accountName, mGroupMetadata.accountType)
+ .execute();
}
}
@@ -512,11 +539,6 @@
@Override
public void onAction(int action) {
switch (action) {
- case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
- mActionBarAdapter.setSearchMode(true);
- invalidateOptionsMenu();
- showFabWithAnimation(/* showFabWithAnimation = */ false);
- break;
case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
if (mMembersListFragment != null) {
mMembersListFragment.displayCheckBoxes(true);
@@ -539,7 +561,7 @@
}
private void showFabWithAnimation(boolean showFab) {
- // TODO(wjang): b/28497108
+ // TODO: b/28497108
}
@Override
@@ -598,63 +620,12 @@
@Override
public void onGroupMetadataLoaded(GroupMetadata groupMetadata) {
mGroupMetadata = groupMetadata;
-
if (!mIsInsertAction) {
getSupportActionBar().setTitle(mGroupMetadata.groupName);
}
-
- bindAutocompleteTextView();
- getLoaderManager().initLoader(LOADER_GROUP_MEMBERS, null, mGroupMemberCallbacks);
-
invalidateOptionsMenu();
}
- private void bindAutocompleteTextView() {
- final AutoCompleteTextView autoCompleteTextView =
- (AutoCompleteTextView) mActionBarAdapter.getSearchView();
- if (autoCompleteTextView == null) return;
- mAutoCompleteAdapter = createAutocompleteAdapter();
- autoCompleteTextView.setAdapter(mAutoCompleteAdapter);
- autoCompleteTextView.setOnItemClickListener(new OnItemClickListener() {
- @Override
- public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
- final SuggestedMember member = (SuggestedMember) view.getTag();
- if (member == null) {
- return;
- }
- final long[] rawContactIdsToAdd = new long[1];
- rawContactIdsToAdd[0] = member.getRawContactId();
- final Intent intent = ContactSaveService.createGroupUpdateIntent(
- GroupMembersActivity.this, mGroupMetadata.groupId, /* newLabel */ null,
- rawContactIdsToAdd, /* rawContactIdsToRemove */ null,
- GroupMembersActivity.class, ACTION_ADD_TO_GROUP);
- startService(intent);
-
- // Update the autocomplete adapter so the contact doesn't get suggested again
- mAutoCompleteAdapter.addNewMember(member.getContactId());
-
- // Clear out the text field
- autoCompleteTextView.setText("");
- }
- });
- }
-
- private SuggestedMemberListAdapter createAutocompleteAdapter() {
- final SuggestedMemberListAdapter adapter = new SuggestedMemberListAdapter(
- this, android.R.layout.simple_dropdown_item_1line);
- adapter.setContentResolver(this.getContentResolver());
- adapter.setAccountType(mGroupMetadata.accountType);
- adapter.setAccountName(mGroupMetadata.accountName);
- adapter.setDataSet(mGroupMetadata.dataSet);
- return adapter;
- }
-
- private void bindAutocompleteGroupMembers(List<Member> members) {
- if (mAutoCompleteAdapter != null) {
- mAutoCompleteAdapter.updateExistingMembersList(members);
- }
- }
-
@Override
public void onGroupMetadataLoadFailed() {
setResultCanceledAndFinish(R.string.groupLoadErrorToast);
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 892e831..d5c5b9b 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -40,7 +40,6 @@
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.support.v4.widget.DrawerLayout;
-import android.support.v7.app.ActionBarDrawerToggle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.telecom.TelecomManager;
@@ -57,8 +56,8 @@
import android.widget.ImageButton;
import android.widget.Toast;
-import com.android.contacts.AppCompatContactsActivity;
import com.android.contacts.ContactSaveService;
+import com.android.contacts.ContactsDrawerActivity;
import com.android.contacts.R;
import com.android.contacts.activities.ActionBarAdapter.TabState;
import com.android.contacts.common.ContactsUtils;
@@ -71,24 +70,24 @@
import com.android.contacts.common.list.ContactListFilter;
import com.android.contacts.common.list.ContactListFilterController;
import com.android.contacts.common.list.DirectoryListLoader;
+import com.android.contacts.common.list.ProviderStatusWatcher;
+import com.android.contacts.common.list.ProviderStatusWatcher.ProviderStatusListener;
import com.android.contacts.common.list.ViewPagerTabs;
import com.android.contacts.common.logging.ListEvent;
import com.android.contacts.common.logging.Logger;
import com.android.contacts.common.logging.ScreenEvent.ScreenType;
-import com.android.contacts.common.preference.ContactsPreferenceActivity;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountWithDataSet;
import com.android.contacts.common.util.AccountFilterUtil;
import com.android.contacts.common.util.Constants;
import com.android.contacts.common.util.ImplicitIntentsUtil;
-import com.android.contacts.common.util.ViewUtil;
import com.android.contacts.common.widget.FloatingActionButtonController;
import com.android.contacts.editor.ContactEditorFragment;
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.AccountFiltersFragment;
-import com.android.contacts.interactions.AccountFiltersFragment.AccountFiltersListener;
import com.android.contacts.interactions.ContactDeletionInteraction;
import com.android.contacts.interactions.ContactMultiDeletionInteraction;
import com.android.contacts.interactions.ContactMultiDeletionInteraction.MultiContactDeleteListener;
@@ -99,8 +98,6 @@
import com.android.contacts.list.MultiSelectContactsListFragment.OnCheckBoxListActionListener;
import com.android.contacts.list.OnContactBrowserActionListener;
import com.android.contacts.list.OnContactsUnavailableActionListener;
-import com.android.contacts.list.ProviderStatusWatcher;
-import com.android.contacts.list.ProviderStatusWatcher.ProviderStatusListener;
import com.android.contacts.quickcontact.QuickContactActivity;
import com.android.contacts.util.DialogManager;
import com.android.contacts.util.PhoneCapabilityTester;
@@ -114,17 +111,14 @@
/**
* Displays a list to browse contacts.
*/
-public class PeopleActivity extends AppCompatContactsActivity implements
+public class PeopleActivity extends ContactsDrawerActivity implements
View.OnCreateContextMenuListener,
View.OnClickListener,
- AccountFiltersListener,
ActionBarAdapter.Listener,
DialogManager.DialogShowingViewActivity,
ContactListFilterController.ContactListFilterListener,
- GroupsListener,
ProviderStatusListener,
- MultiContactDeleteListener,
- NavigationView.OnNavigationItemSelectedListener {
+ MultiContactDeleteListener {
private static final String TAG = "PeopleActivity";
@@ -138,12 +132,11 @@
private ContactsRequest mRequest;
private ActionBarAdapter mActionBarAdapter;
+ private List<AccountWithDataSet> mWritableAccounts;
private FloatingActionButtonController mFloatingActionButtonController;
private View mFloatingActionButtonContainer;
private boolean wasLastFabAnimationScaleIn = false;
- private ContactListFilterController mContactListFilterController;
-
private ContactsUnavailableFragment mContactsUnavailableFragment;
private ProviderStatusWatcher mProviderStatusWatcher;
private Integer mProviderStatus;
@@ -154,8 +147,6 @@
* Showing a list of Contacts. Also used for showing search results in search mode.
*/
private DefaultContactBrowseListFragment mAllFragment;
- private GroupsFragment mGroupsFragment;
- private AccountFiltersFragment mAccountFiltersFragment;
/** ViewPager for swipe */
private ViewPager mTabPager;
@@ -164,8 +155,6 @@
private String[] mTabTitles;
private final TabPagerListener mTabPagerListener = new TabPagerListener();
- private NavigationView mNavigationView;
-
private boolean mEnableDebugMenuOptions;
/**
@@ -207,10 +196,6 @@
return (mProviderStatus != null) && mProviderStatus.equals(ProviderStatus.STATUS_NORMAL);
}
- private boolean areGroupWritableAccountsAvailable() {
- return ContactsUtils.areGroupWritableAccountsAvailable(this);
- }
-
/**
* Initialize fragments that are (or may not be) in the layout.
*
@@ -267,9 +252,6 @@
Log.d(Constants.PERFORMANCE_TAG, "PeopleActivity.onCreate finish");
}
getWindow().setBackgroundDrawable(null);
- if (CompatUtils.isLollipopCompatible()) {
- getWindow().setStatusBarColor(Color.TRANSPARENT);
- }
}
@Override
@@ -336,50 +318,19 @@
mTabPager.setOnPageChangeListener(mTabPagerListener);
// Configure toolbar and toolbar tabs. If in landscape mode, we configure tabs differently.
- final Toolbar toolbar = getView(R.id.toolbar);
- setSupportActionBar(toolbar);
final ViewPagerTabs portraitViewPagerTabs
= (ViewPagerTabs) findViewById(R.id.lists_pager_header);
ViewPagerTabs landscapeViewPagerTabs = null;
if (portraitViewPagerTabs == null) {
landscapeViewPagerTabs = (ViewPagerTabs) getLayoutInflater().inflate(
- R.layout.people_activity_tabs_lands, toolbar, /* attachToRoot = */ false);
+ R.layout.people_activity_tabs_lands, mToolbar, /* attachToRoot = */ false);
mViewPagerTabs = landscapeViewPagerTabs;
} else {
mViewPagerTabs = portraitViewPagerTabs;
}
mViewPagerTabs.setViewPager(mTabPager);
- final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
- final ActionBarDrawerToggle toggle = new ActionBarDrawerToggle(this, drawer, toolbar,
- R.string.navigation_drawer_open, R.string.navigation_drawer_close);
- drawer.setDrawerListener(toggle);
- toggle.syncState();
-
- mNavigationView = (NavigationView) findViewById(R.id.nav_view);
- mNavigationView.setNavigationItemSelectedListener(this);
-
- final Menu menu = mNavigationView.getMenu();
-
- final boolean showBlockedNumbers = PhoneCapabilityTester.isPhone(this)
- && ContactsUtils.FLAG_N_FEATURE
- && BlockedNumberContractCompat.canCurrentUserBlockNumbers(this);
-
- if (!showBlockedNumbers) {
- menu.removeItem(R.id.nav_blocked_numbers);
- }
-
- if (Assistants.getDuplicatesActivityIntent(this) == null) {
- menu.removeItem(R.id.nav_find_duplicates);
- }
-
- if (!HelpUtils.isHelpAndFeedbackAvailable()) {
- menu.removeItem(R.id.nav_help);
- }
-
final String ALL_TAG = "tab-pager-all";
- final String GROUPS_TAG = "groups";
- final String FILTERS_TAG = "filters";
// Create the fragments and add as children of the view pager.
// The pager adapter will only change the visibility; it'll never create/destroy
@@ -389,49 +340,27 @@
// existing.
mAllFragment = (DefaultContactBrowseListFragment)
fragmentManager.findFragmentByTag(ALL_TAG);
- mGroupsFragment = (GroupsFragment)
- fragmentManager.findFragmentByTag(GROUPS_TAG);
- mAccountFiltersFragment = (AccountFiltersFragment)
- fragmentManager.findFragmentByTag(FILTERS_TAG);
if (mAllFragment == null) {
mAllFragment = new DefaultContactBrowseListFragment();
transaction.add(R.id.tab_pager, mAllFragment, ALL_TAG);
-
- if (areGroupWritableAccountsAvailable()) {
- mGroupsFragment = new GroupsFragment();
- transaction.add(mGroupsFragment, GROUPS_TAG);
- }
-
- mAccountFiltersFragment = new AccountFiltersFragment();
- transaction.add(mAccountFiltersFragment, FILTERS_TAG);
}
mAllFragment.setOnContactListActionListener(new ContactBrowserActionListener());
mAllFragment.setCheckBoxListListener(new CheckBoxListListener());
mAllFragment.setListType(ListEvent.ListType.ALL_CONTACTS);
- if (areGroupWritableAccountsAvailable() && mGroupsFragment != null) {
- mGroupsFragment.setListener(this);
- }
-
- mAccountFiltersFragment.setListener(this);
-
// Hide all fragments for now. We adjust visibility when we get onSelectedTabChanged()
// from ActionBarAdapter.
transaction.hide(mAllFragment);
- // Groups fragment has no UI, no need to hide it
transaction.commitAllowingStateLoss();
fragmentManager.executePendingTransactions();
mActionBarAdapter = new ActionBarAdapter(this, this, getSupportActionBar(),
- portraitViewPagerTabs, landscapeViewPagerTabs, toolbar);
+ portraitViewPagerTabs, landscapeViewPagerTabs, mToolbar);
mActionBarAdapter.initialize(savedState, mRequest);
- // Add shadow under toolbar
- ViewUtil.addRectangularOutlineProvider(findViewById(R.id.toolbar_parent), getResources());
-
// Configure floating action button
mFloatingActionButtonContainer = findViewById(R.id.floating_action_button_container);
final ImageButton floatingActionButton
@@ -563,12 +492,25 @@
private void initializeFabVisibility() {
final boolean hideFab = mActionBarAdapter.isSearchMode()
- || mActionBarAdapter.isSelectionMode();
+ || mActionBarAdapter.isSelectionMode()
+ || !shouldShowFabForAccount();
mFloatingActionButtonContainer.setVisibility(hideFab ? View.GONE : View.VISIBLE);
mFloatingActionButtonController.resetIn();
wasLastFabAnimationScaleIn = !hideFab;
}
+ private boolean shouldShowFabForAccount() {
+ return isCurrentAccountFilterWritable()
+ || isAllContactsFilter(mContactListFilterController.getFilter());
+ }
+
+ private boolean isCurrentAccountFilterWritable() {
+ final ContactListFilter currentFilter = mContactListFilterController.getFilter();
+ final AccountWithDataSet accountOfCurrentFilter = new AccountWithDataSet(
+ currentFilter.accountName, currentFilter.accountType, currentFilter.dataSet);
+ return mWritableAccounts != null && mWritableAccounts.contains(accountOfCurrentFilter);
+ }
+
private void showFabWithAnimation(boolean showFab) {
if (mFloatingActionButtonContainer == null) {
return;
@@ -596,6 +538,7 @@
}
setFilterAndUpdateTitle(mContactListFilterController.getFilter());
+ showFabWithAnimation(shouldShowFabForAccount());
invalidateOptionsMenuIfNeeded();
}
@@ -920,67 +863,18 @@
}
@Override
- public void onGroupsLoaded(List<GroupListItem> groupListItems) {
- final Menu menu = mNavigationView.getMenu();
- final MenuItem groupsMenuItem = menu.findItem(R.id.nav_groups);
- final SubMenu subMenu = groupsMenuItem.getSubMenu();
- subMenu.removeGroup(R.id.nav_groups_items);
-
- if (groupListItems != null) {
- // Add each group
- for (GroupListItem groupListItem : groupListItems) {
- if (GroupUtil.isEmptyFFCGroup(groupListItem)) {
- continue;
- }
- final String title = groupListItem.getTitle();
- final MenuItem menuItem =
- subMenu.add(R.id.nav_groups_items, Menu.NONE, Menu.NONE, title);
- menuItem.setIntent(GroupUtil.createViewGroupIntent(
- this, groupListItem.getGroupId()));
- menuItem.setIcon(R.drawable.ic_menu_label);
- }
- }
-
- // Create a menu item in the sub menu to add new groups
- final MenuItem menuItem = subMenu.add(R.id.nav_groups_items, Menu.NONE, Menu.NONE,
- getString(R.string.menu_new_group_action_bar));
- menuItem.setIntent(GroupUtil.createAddGroupIntent(this));
- menuItem.setIcon(R.drawable.ic_menu_group_add);
+ public void onFiltersLoaded(List<ContactListFilter> accountFilterItems) {
+ super.onFiltersLoaded(accountFilterItems);
+ mWritableAccounts =
+ AccountTypeManager.getInstance(this).getAccounts(/* contactWritableOnly */ true);
+ initializeFabVisibility();
}
@Override
- public void onFiltersLoaded(List<ContactListFilter> accountFilterItems) {
- final Menu menu = mNavigationView.getMenu();
- final MenuItem filtersMenuItem = menu.findItem(R.id.nav_filters);
- final SubMenu subMenu = filtersMenuItem.getSubMenu();
- subMenu.removeGroup(R.id.nav_filters_items);
-
- if (accountFilterItems == null || accountFilterItems.size() < 2) {
- return;
- }
-
- for (int i = 0; i < accountFilterItems.size(); i++) {
- final ContactListFilter filter = accountFilterItems.get(i);
- final String accountName = filter.accountName;
- final MenuItem menuItem = subMenu.add(R.id.nav_filters_items, Menu.NONE, Menu.NONE,
- accountName);
- final Intent intent = new Intent();
- intent.putExtra(AccountFilterUtil.EXTRA_CONTACT_LIST_FILTER, filter);
- menuItem.setOnMenuItemClickListener(new MenuItem.OnMenuItemClickListener() {
- @Override
- public boolean onMenuItemClick(MenuItem item) {
- final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
- drawer.closeDrawer(GravityCompat.START);
- mAllFragment.setListType(ListEvent.ListType.ACCOUNT);
- AccountFilterUtil.handleAccountFilterResult(
- mContactListFilterController, AppCompatActivity.RESULT_OK, intent);
- return true;
- }
- });
- menuItem.setIcon(filter.icon);
- // Get rid of the default memu item overlay and show original account icons.
- menuItem.getIcon().setColorFilter(Color.TRANSPARENT, PorterDuff.Mode.SRC_ATOP);
- }
+ protected void onGroupMenuItemClicked(long groupId) {
+ switchToAllContacts();
+ super.onGroupMenuItemClicked(groupId);
+ mDrawer.closeDrawer(GravityCompat.START);
}
@Override
@@ -1057,8 +951,7 @@
if (mAllFragment.isSearchMode()) {
previousScreen = ScreenType.SEARCH;
} else {
- if (mAllFragment.getFilter().filterType ==
- ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS) {
+ if (isAllContactsFilter(mContactListFilterController.getFilter())) {
if (position < mAllFragment.getAdapter().getNumberOfFavorites()) {
previousScreen = ScreenType.FAVORITES;
} else {
@@ -1263,50 +1156,6 @@
return super.onOptionsItemSelected(item);
}
- @SuppressWarnings("StatementWithEmptyBody")
- @Override
- public boolean onNavigationItemSelected(MenuItem item) {
- final int id = item.getItemId();
-
- if (id == R.id.nav_settings) {
- startActivity(createPreferenceIntent());
- } else if (id == R.id.nav_help) {
- HelpUtils.launchHelpAndFeedbackForMainScreen(this);
- } else if (id == R.id.nav_all_contacts) {
- switchToAllContacts();
- } else if (id == R.id.nav_blocked_numbers) {
- final Intent intent = TelecomManagerUtil.createManageBlockedNumbersIntent(
- (TelecomManager) getSystemService(Context.TELECOM_SERVICE));
- if (intent != null) {
- startActivity(intent);
- }
- } else if (id == R.id.nav_find_duplicates) {
- ImplicitIntentsUtil.startActivityInAppIfPossible(this,
- Assistants.getDuplicatesActivityIntent(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);
- drawer.closeDrawer(GravityCompat.START);
- return true;
- }
-
- private Intent createPreferenceIntent() {
- final Intent intent = new Intent(this, ContactsPreferenceActivity.class);
- intent.putExtra(ContactsPreferenceActivity.EXTRA_NEW_LOCAL_PROFILE,
- ContactEditorFragment.INTENT_EXTRA_NEW_LOCAL_PROFILE);
- intent.putExtra(ContactsPreferenceActivity.EXTRA_MODE_FULLY_EXPANDED,
- QuickContactActivity.MODE_FULLY_EXPANDED);
- intent.putExtra(ContactsPreferenceActivity.EXTRA_PREVIOUS_SCREEN_TYPE,
- QuickContactActivity.EXTRA_PREVIOUS_SCREEN_TYPE);
- intent.putExtra(ContactsPreferenceActivity.EXTRA_ARE_CONTACTS_AVAILABLE,
- areContactsAvailable());
- return intent;
- }
-
private void showImportExportDialogFragment(){
ImportExportDialogFragment.show(getFragmentManager(), areContactsAvailable(),
PeopleActivity.class, ImportExportDialogFragment.EXPORT_MODE_ALL_CONTACTS);
@@ -1404,7 +1253,6 @@
/* count */ mAllFragment.getAdapter().getCount(), /* clickedIndex */ -1,
/* numSelected */ mAllFragment.getAdapter().getSelectedContactIds().size());
-
// TODO fix or remove multipicker code
// else if (resultCode == RESULT_CANCELED && mMode == MODE_PICK_MULTIPLE_PHONES) {
// // Finish the activity if the sub activity was canceled as back key is used
@@ -1446,9 +1294,8 @@
return;
}
- final DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
- if (drawer.isDrawerOpen(GravityCompat.START)) {
- drawer.closeDrawer(GravityCompat.START);
+ if (mDrawer.isDrawerOpen(GravityCompat.START)) {
+ mDrawer.closeDrawer(GravityCompat.START);
} else if (mActionBarAdapter.isSelectionMode()) {
mActionBarAdapter.setSelectionMode(false);
mAllFragment.displayCheckBoxes(false);
@@ -1537,6 +1384,9 @@
private void setFilterAndUpdateTitle(ContactListFilter filter, boolean restoreSelectedUri) {
mAllFragment.setFilter(filter, restoreSelectedUri);
+ final int listType = filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS
+ ? ListEvent.ListType.ALL_CONTACTS : ListEvent.ListType.ACCOUNT;
+ mAllFragment.setListType(listType);
if (getSupportActionBar() != null) {
final String actionBarTitle = TextUtils.isEmpty(filter.accountName) ?
getString(R.string.contactsList) : filter.accountName;
@@ -1544,21 +1394,18 @@
}
}
- private void switchToAllContacts() {
- final Intent intent = new Intent();
- final ContactListFilter filter = createAllAccountsFilter();
- intent.putExtra(AccountFilterUtil.EXTRA_CONTACT_LIST_FILTER, filter);
- AccountFilterUtil.handleAccountFilterResult(
- mContactListFilterController, AppCompatActivity.RESULT_OK, intent);
- }
-
- private ContactListFilter createAllAccountsFilter() {
- return ContactListFilter.createFilterWithType(ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
- }
-
// Persist filter only when it's of the type FILTER_TYPE_ALL_ACCOUNTS.
private void persistFilterIfNeeded(ContactListFilter filter) {
- mContactListFilterController.setContactListFilter(filter, /* persistent */
- filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS);
+ mContactListFilterController.setContactListFilter(filter,
+ /* persistent */ isAllContactsFilter(filter));
+ }
+
+ private boolean isAllContactsFilter(ContactListFilter filter) {
+ return filter.filterType == ContactListFilter.FILTER_TYPE_ALL_ACCOUNTS;
+ }
+
+ @Override
+ protected boolean shouldFinish() {
+ return false;
}
}
diff --git a/src/com/android/contacts/compat/ProviderStatusCompat.java b/src/com/android/contacts/compat/ProviderStatusCompat.java
deleted file mode 100644
index 2b5c820..0000000
--- a/src/com/android/contacts/compat/ProviderStatusCompat.java
+++ /dev/null
@@ -1,69 +0,0 @@
-/*
- * Copyright (C) 2015 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.compat;
-
-import android.os.Build;
-import android.provider.ContactsContract.ProviderStatus;
-
-import com.android.contacts.common.compat.CompatUtils;
-import com.android.contacts.common.compat.SdkVersionOverride;
-
-/**
- * This class contains constants from the pre-M version of ContactsContract.ProviderStatus class
- * and also the mappings between pre-M constants and M constants for compatibility purpose,
- * because ProviderStatus class constant names and values changed and the class became visible in
- * API level 23.
- */
-public class ProviderStatusCompat {
- /**
- * Not instantiable.
- */
- private ProviderStatusCompat() {
- }
-
- public static final boolean USE_CURRENT_VERSION = CompatUtils.isMarshmallowCompatible();
-
- public static final int STATUS_EMPTY = USE_CURRENT_VERSION ?
- ProviderStatus.STATUS_EMPTY : ProviderStatusCompat.STATUS_NO_ACCOUNTS_NO_CONTACTS;
-
- public static final int STATUS_BUSY = USE_CURRENT_VERSION ?
- ProviderStatus.STATUS_BUSY : ProviderStatusCompat.STATUS_UPGRADING;
-
- /**
- * Default status of the provider, using the actual constant to guard against errors
- */
- public static final int STATUS_NORMAL = ProviderStatus.STATUS_NORMAL;
-
- /**
- * The following three constants are from pre-M.
- *
- * The status used when the provider is in the process of upgrading. Contacts
- * are temporarily unaccessible.
- */
- private static final int STATUS_UPGRADING = 1;
-
- /**
- * The status used during a locale change.
- */
- public static final int STATUS_CHANGING_LOCALE = 3;
-
- /**
- * The status that indicates that there are no accounts and no contacts
- * on the device.
- */
- private static final int STATUS_NO_ACCOUNTS_NO_CONTACTS = 4;
-}
diff --git a/src/com/android/contacts/editor/AccountsChangedBroadcastReceiver.java b/src/com/android/contacts/editor/AccountsChangedBroadcastReceiver.java
new file mode 100644
index 0000000..55300d5
--- /dev/null
+++ b/src/com/android/contacts/editor/AccountsChangedBroadcastReceiver.java
@@ -0,0 +1,71 @@
+/*
+ * 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.editor;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.text.TextUtils;
+import android.util.Log;
+
+import com.android.contacts.R;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+
+import java.util.List;
+
+/**
+ * This class is to fix the bug that no prompt is seen for multiple accounts while creating new
+ * contacts. By registering a BroadcastReceiver statically, we detect the changes of accounts by
+ * receiving the message "android.accounts.LOGIN_ACCOUNTS_CHANGED". If the BroadcastReceiver gets
+ * this message, it will get the default account from the SharedPreference and compare current
+ * accounts with the default account. At last, it will renew the default account in the
+ * SharedPreference if necessary.
+ */
+public class AccountsChangedBroadcastReceiver extends BroadcastReceiver {
+ final String TAG = "AccountsChanged";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Context appContext = context.getApplicationContext();
+ final ContactEditorUtils contactEditorUtils = ContactEditorUtils.getInstance(appContext);
+ final String defaultAccountKey = appContext.getResources().getString(
+ R.string.contact_editor_default_account_key);
+ final SharedPreferences pref = appContext.getSharedPreferences(
+ appContext.getPackageName(), Context.MODE_PRIVATE);
+ final String defaultAccountString = pref.getString(defaultAccountKey, null);
+
+ if (!TextUtils.isEmpty(defaultAccountString)) {
+ AccountWithDataSet defaultAccount;
+ try {
+ defaultAccount = AccountWithDataSet.unstringify(defaultAccountString);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "Invalid string in SharedPreference", e);
+ contactEditorUtils.saveDefaultAndAllAccounts(null);
+ return;
+ }
+
+ final AccountTypeManager accountTypeManager = AccountTypeManager.getInstance(
+ appContext);
+ final List<AccountWithDataSet> accounts = accountTypeManager.getAccounts(true);
+ // Delete default account pref if it has been deleted.
+ if (accounts == null || accounts.size() < 1 || !accounts.contains(defaultAccount)) {
+ contactEditorUtils.saveDefaultAndAllAccounts(null);
+ }
+ }
+ }
+}
diff --git a/src/com/android/contacts/editor/ContactEditorBaseFragment.java b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
index 4182e1f..c1e5af8 100644
--- a/src/com/android/contacts/editor/ContactEditorBaseFragment.java
+++ b/src/com/android/contacts/editor/ContactEditorBaseFragment.java
@@ -16,35 +16,6 @@
package com.android.contacts.editor;
-import com.android.contacts.common.logging.ScreenEvent.ScreenType;
-import com.google.common.collect.ImmutableList;
-import com.google.common.collect.Lists;
-
-import com.android.contacts.ContactSaveService;
-import com.android.contacts.GroupMetaDataLoader;
-import com.android.contacts.R;
-import com.android.contacts.activities.ContactEditorAccountsChangedActivity;
-import com.android.contacts.activities.ContactEditorBaseActivity;
-import com.android.contacts.activities.ContactEditorBaseActivity.ContactEditor;
-import com.android.contacts.common.model.AccountTypeManager;
-import com.android.contacts.common.model.Contact;
-import com.android.contacts.common.model.ContactLoader;
-import com.android.contacts.common.model.RawContact;
-import com.android.contacts.common.model.RawContactDelta;
-import com.android.contacts.common.model.RawContactDeltaList;
-import com.android.contacts.common.model.RawContactModifier;
-import com.android.contacts.common.model.ValuesDelta;
-import com.android.contacts.common.model.account.AccountType;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.util.ImplicitIntentsUtil;
-import com.android.contacts.common.util.MaterialColorMapUtils;
-import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
-import com.android.contacts.list.UiIntentActions;
-import com.android.contacts.quickcontact.QuickContactActivity;
-import com.android.contacts.util.HelpUtils;
-import com.android.contacts.util.PhoneCapabilityTester;
-import com.android.contacts.util.UiClosables;
-
import android.accounts.Account;
import android.app.Activity;
import android.app.Fragment;
@@ -84,6 +55,34 @@
import android.widget.ListPopupWindow;
import android.widget.Toast;
+import com.android.contacts.ContactSaveService;
+import com.android.contacts.GroupMetaDataLoader;
+import com.android.contacts.R;
+import com.android.contacts.activities.ContactEditorAccountsChangedActivity;
+import com.android.contacts.activities.ContactEditorBaseActivity;
+import com.android.contacts.activities.ContactEditorBaseActivity.ContactEditor;
+import com.android.contacts.common.logging.ScreenEvent.ScreenType;
+import com.android.contacts.common.model.AccountTypeManager;
+import com.android.contacts.common.model.Contact;
+import com.android.contacts.common.model.ContactLoader;
+import com.android.contacts.common.model.RawContact;
+import com.android.contacts.common.model.RawContactDelta;
+import com.android.contacts.common.model.RawContactDeltaList;
+import com.android.contacts.common.model.RawContactModifier;
+import com.android.contacts.common.model.ValuesDelta;
+import com.android.contacts.common.model.account.AccountType;
+import com.android.contacts.common.model.account.AccountWithDataSet;
+import com.android.contacts.common.util.ImplicitIntentsUtil;
+import com.android.contacts.common.util.MaterialColorMapUtils;
+import com.android.contacts.editor.AggregationSuggestionEngine.Suggestion;
+import com.android.contacts.list.UiIntentActions;
+import com.android.contacts.quickcontact.QuickContactActivity;
+import com.android.contacts.util.HelpUtils;
+import com.android.contacts.util.PhoneCapabilityTester;
+import com.android.contacts.util.UiClosables;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.Lists;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -1472,6 +1471,7 @@
QuickContactActivity.MODE_FULLY_EXPANDED);
resultIntent.putExtra(QuickContactActivity.EXTRA_PREVIOUS_SCREEN_TYPE,
ScreenType.EDITOR);
+ resultIntent.putExtra(QuickContactActivity.EXTRA_CONTACT_EDITED, true);
} else {
resultIntent = null;
}
diff --git a/src/com/android/contacts/group/GroupMembersListAdapter.java b/src/com/android/contacts/group/GroupMembersListAdapter.java
index 86e75b1..e84dde7 100644
--- a/src/com/android/contacts/group/GroupMembersListAdapter.java
+++ b/src/com/android/contacts/group/GroupMembersListAdapter.java
@@ -28,6 +28,7 @@
import android.view.ViewGroup;
import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
+import com.android.contacts.common.R;
import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.MultiSelectEntryContactListAdapter;
import com.android.contacts.common.preference.ContactsPreferences;
@@ -71,7 +72,8 @@
public GroupMembersListAdapter(Context context) {
super(context, GroupMembersQuery.RAW_CONTACT_ID);
- mUnknownNameText = context.getText(android.R.string.unknownName);
+ mUnknownNameText = context.getText(R.string.missing_name);
+ setSectionHeaderDisplayEnabled(true);
}
/** Sets the ID of the group whose members will be displayed. */
@@ -93,7 +95,6 @@
.appendQueryParameter(ContactsContract.DIRECTORY_PARAM_KEY,
String.valueOf(Directory.DEFAULT))
.appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX, "true")
- .appendQueryParameter(Contacts.EXTRA_ADDRESS_BOOK_INDEX_COUNTS, "true")
.build());
loader.setSelection(Data.MIMETYPE + "=?" + " AND " + GroupMembership.GROUP_ROW_ID + "=?");
@@ -131,7 +132,6 @@
final ContactListItemView view =
super.newView(context, partition, cursor, position, parent);
view.setUnknownNameText(mUnknownNameText);
- view.setQuickContactEnabled(isQuickContactEnabled());
return view;
}
@@ -140,10 +140,21 @@
super.bindView(v, partition, cursor, position);
final ContactListItemView view = (ContactListItemView) v;
bindViewId(view, cursor, GroupMembersQuery.CONTACT_ID);
+ bindSectionHeaderAndDivider(view, position);
bindName(view, cursor);
bindPhoto(view, cursor);
}
+ protected void bindSectionHeaderAndDivider(ContactListItemView view, int position) {
+ view.setIsSectionHeaderEnabled(isSectionHeaderDisplayEnabled());
+ if (isSectionHeaderDisplayEnabled()) {
+ final Placement placement = getItemPlacementInSection(position);
+ view.setSectionHeader(placement.sectionHeader);
+ } else {
+ view.setSectionHeader(null);
+ }
+ }
+
private void bindName(ContactListItemView view, Cursor cursor) {
view.showDisplayName(cursor, GroupMembersQuery.CONTACT_DISPLAY_NAME,
getContactNameDisplayOrder());
diff --git a/src/com/android/contacts/group/GroupUtil.java b/src/com/android/contacts/group/GroupUtil.java
index 2057a94..2eee35b 100644
--- a/src/com/android/contacts/group/GroupUtil.java
+++ b/src/com/android/contacts/group/GroupUtil.java
@@ -149,4 +149,11 @@
private static boolean isSystemIdFFC(String systemId) {
return !TextUtils.isEmpty(systemId) && FFC_GROUPS.contains(systemId);
}
+
+ /**
+ * Sort groups alphabetically and in a localized way.
+ */
+ public static String getGroupsSortOrder() {
+ return Groups.TITLE + " COLLATE LOCALIZED ASC";
+ }
}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/Member.java b/src/com/android/contacts/group/Member.java
deleted file mode 100644
index ad6f9b2..0000000
--- a/src/com/android/contacts/group/Member.java
+++ /dev/null
@@ -1,127 +0,0 @@
-/*
- * Copyright (C) 2011 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.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
-import android.provider.ContactsContract.Contacts;
-
-import com.google.common.base.Objects;
-
-/** A member of the group currently being displayed to the user. */
-public class Member implements Parcelable {
-
- public static final Parcelable.Creator<Member> CREATOR = new Parcelable.Creator<Member>() {
- @Override
- public Member createFromParcel(Parcel in) {
- return new Member(in);
- }
-
- @Override
- public Member[] newArray(int size) {
- return new Member[size];
- }
- };
-
- private final long mRawContactId;
- private final long mContactId;
- private final Uri mLookupUri;
- private final String mDisplayName;
- private final Uri mPhotoUri;
- private final String mLookupKey;
- private final long mPhotoId;
-
- public Member(long rawContactId, String lookupKey, long contactId, String displayName,
- String photoUri, long photoId) {
- mRawContactId = rawContactId;
- mContactId = contactId;
- mLookupKey = lookupKey;
- mLookupUri = Contacts.getLookupUri(contactId, lookupKey);
- mDisplayName = displayName;
- mPhotoUri = (photoUri != null) ? Uri.parse(photoUri) : null;
- mPhotoId = photoId;
- }
-
- public long getRawContactId() {
- return mRawContactId;
- }
-
- public long getContactId() {
- return mContactId;
- }
-
- public Uri getLookupUri() {
- return mLookupUri;
- }
-
- public String getLookupKey() {
- return mLookupKey;
- }
-
- public String getDisplayName() {
- return mDisplayName;
- }
-
- public Uri getPhotoUri() {
- return mPhotoUri;
- }
-
- public long getPhotoId() {
- return mPhotoId;
- }
-
- @Override
- public boolean equals(Object object) {
- if (object instanceof Member) {
- Member otherMember = (Member) object;
- return Objects.equal(mLookupUri, otherMember.getLookupUri());
- }
- return false;
- }
-
- @Override
- public int hashCode() {
- return mLookupUri == null ? 0 : mLookupUri.hashCode();
- }
-
- @Override
- public int describeContents() {
- return 0;
- }
-
- @Override
- public void writeToParcel(Parcel dest, int flags) {
- dest.writeLong(mRawContactId);
- dest.writeLong(mContactId);
- dest.writeParcelable(mLookupUri, flags);
- dest.writeString(mLookupKey);
- dest.writeString(mDisplayName);
- dest.writeParcelable(mPhotoUri, flags);
- dest.writeLong(mPhotoId);
- }
-
- private Member(Parcel in) {
- mRawContactId = in.readLong();
- mContactId = in.readLong();
- mLookupUri = in.readParcelable(getClass().getClassLoader());
- mLookupKey = in.readString();
- mDisplayName = in.readString();
- mPhotoUri = in.readParcelable(getClass().getClassLoader());
- mPhotoId = in.readLong();
- }
-}
\ No newline at end of file
diff --git a/src/com/android/contacts/group/SuggestedMemberListAdapter.java b/src/com/android/contacts/group/SuggestedMemberListAdapter.java
deleted file mode 100644
index e2756c6..0000000
--- a/src/com/android/contacts/group/SuggestedMemberListAdapter.java
+++ /dev/null
@@ -1,440 +0,0 @@
-/*
- * Copyright (C) 2011 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.ContentResolver;
-import android.content.ContentUris;
-import android.content.Context;
-import android.database.Cursor;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.Paint;
-import android.graphics.PorterDuff;
-import android.graphics.PorterDuffXfermode;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.Email;
-import android.provider.ContactsContract.CommonDataKinds.Phone;
-import android.provider.ContactsContract.CommonDataKinds.Photo;
-import android.provider.ContactsContract.Contacts.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.provider.ContactsContract.RawContactsEntity;
-import android.text.TextUtils;
-import android.view.LayoutInflater;
-import android.view.View;
-import android.view.ViewGroup;
-import android.widget.ArrayAdapter;
-import android.widget.AutoCompleteTextView;
-import android.widget.Filter;
-import android.widget.ImageView;
-import android.widget.TextView;
-
-import com.android.contacts.R;
-import com.android.contacts.common.ContactPhotoManager;
-import com.android.contacts.group.SuggestedMemberListAdapter.SuggestedMember;
-
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.HashMap;
-import java.util.List;
-
-/**
- * This adapter provides suggested contacts that can be added to a group for an
- * {@link AutoCompleteTextView} within the group editor.
- */
-public class SuggestedMemberListAdapter extends ArrayAdapter<SuggestedMember> {
-
- private static final String[] PROJECTION_FILTERED_MEMBERS = new String[] {
- RawContacts._ID, // 0
- RawContacts.CONTACT_ID, // 1
- RawContacts.DISPLAY_NAME_PRIMARY // 2
- };
-
- private static final int RAW_CONTACT_ID_COLUMN_INDEX = 0;
- private static final int CONTACT_ID_COLUMN_INDEX = 1;
- private static final int DISPLAY_NAME_PRIMARY_COLUMN_INDEX = 2;
-
- private static final String[] PROJECTION_MEMBER_DATA = new String[] {
- RawContacts._ID, // 0
- RawContacts.CONTACT_ID, // 1
- Data.MIMETYPE, // 2
- Data.DATA1, // 3
- Photo.PHOTO, // 4
- };
-
- private static final int ID_INDEX = 0;
- private static final int MIMETYPE_COLUMN_INDEX = 2;
- private static final int DATA_COLUMN_INDEX = 3;
- private static final int PHOTO_COLUMN_INDEX = 4;
-
- private Filter mFilter;
- private ContentResolver mContentResolver;
- private LayoutInflater mInflater;
- private ContactPhotoManager mPhotoManager;
-
- private String mAccountType;
- private String mAccountName;
- private String mDataSet;
-
- // TODO: Make this a Map for better performance when we check if a new contact is in the list
- // or not
- private final List<Long> mExistingMemberContactIds = new ArrayList<Long>();
-
- private static final int SUGGESTIONS_LIMIT = 5;
-
- public SuggestedMemberListAdapter(Context context, int textViewResourceId) {
- super(context, textViewResourceId);
- mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
- mPhotoManager = ContactPhotoManager.getInstance(context);
- }
-
- public void setAccountType(String accountType) {
- mAccountType = accountType;
- }
-
- public void setAccountName(String accountName) {
- mAccountName = accountName;
- }
-
- public void setDataSet(String dataSet) {
- mDataSet = dataSet;
- }
-
- public void setContentResolver(ContentResolver resolver) {
- mContentResolver = resolver;
- }
-
- public void updateExistingMembersList(List<Member> list) {
- mExistingMemberContactIds.clear();
- for (Member member : list) {
- mExistingMemberContactIds.add(member.getContactId());
- }
- }
-
- public void addNewMember(long contactId) {
- mExistingMemberContactIds.add(contactId);
- }
-
- public void removeMember(long contactId) {
- if (mExistingMemberContactIds.contains(contactId)) {
- mExistingMemberContactIds.remove(contactId);
- }
- }
-
- @Override
- public View getView(int position, View convertView, ViewGroup parent) {
- View result = convertView;
- if (result == null) {
- result = mInflater.inflate(R.layout.group_member_suggestion, parent, false);
- }
- // TODO: Use a viewholder
- SuggestedMember member = getItem(position);
- TextView text1 = (TextView) result.findViewById(R.id.text1);
- TextView text2 = (TextView) result.findViewById(R.id.text2);
- ImageView icon = (ImageView) result.findViewById(R.id.icon);
- text1.setText(member.getDisplayName());
- if (member.hasExtraInfo()) {
- text2.setText(member.getExtraInfo());
- } else {
- text2.setVisibility(View.GONE);
- }
- byte[] byteArray = member.getPhotoByteArray();
- if (byteArray == null) {
- final Uri contactLookupUri = RawContacts.getContactLookupUri(mContentResolver,
- ContentUris.withAppendedId(RawContacts.CONTENT_URI, member.getContactId()));
- final String imageRequestIdentifier = contactLookupUri == null
- ? null : contactLookupUri.toString();
- GroupUtil.bindPhoto(mPhotoManager, icon, member.getPhotoId(),
- /* photoUri */ null, member.getDisplayName(), imageRequestIdentifier);
- } else {
- Bitmap bitmap = BitmapFactory.decodeByteArray(byteArray, 0, byteArray.length);
- icon.setImageBitmap(frameBitmapInCircle(bitmap));
- }
- result.setTag(member);
- return result;
- }
-
- private static Bitmap frameBitmapInCircle(Bitmap input) {
- // Crop the image if not squared.
- final int targetDiameter = input.getWidth();
- final Bitmap scaled = Bitmap.createScaledBitmap(
- input, targetDiameter, targetDiameter, /* filter */ false);
- int inputWidth = scaled.getWidth();
- int inputHeight = scaled.getHeight();
- int targetX, targetY, targetSize;
- if (inputWidth >= inputHeight) {
- targetX = inputWidth / 2 - inputHeight / 2;
- targetY = 0;
- targetSize = inputHeight;
- } else {
- targetX = 0;
- targetY = inputHeight / 2 - inputWidth / 2;
- targetSize = inputWidth;
- }
-
- // Create an output bitmap and a canvas to draw on it.
- final Bitmap output = Bitmap.createBitmap(targetSize, targetSize, Bitmap.Config.ARGB_8888);
- final Canvas canvas = new Canvas(output);
-
- // Create a black paint to draw the mask.
- final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);
- paint.setColor(Color.BLACK);
-
- // Draw a circle.
- canvas.drawCircle(targetDiameter / 2, targetDiameter / 2, targetDiameter / 2, paint);
-
- // Replace the black parts of the mask with the input image.
- paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
- canvas.drawBitmap(scaled, targetX /* left */, targetY /* top */, paint);
-
- return output;
- }
-
- @Override
- public Filter getFilter() {
- if (mFilter == null) {
- mFilter = new SuggestedMemberFilter();
- }
- return mFilter;
- }
-
- /**
- * This filter queries for raw contacts that match the given account name and account type,
- * as well as the search query.
- */
- public class SuggestedMemberFilter extends Filter {
-
- @Override
- protected FilterResults performFiltering(CharSequence prefix) {
- FilterResults results = new FilterResults();
- if (mContentResolver == null || TextUtils.isEmpty(prefix)) {
- return results;
- }
-
- // Create a list to store the suggested contacts (which will be alphabetically ordered),
- // but also keep a map of raw contact IDs to {@link SuggestedMember}s to make it easier
- // to add supplementary data to the contact (photo, phone, email) to the members based
- // on raw contact IDs after the second query is completed.
- List<SuggestedMember> suggestionsList = new ArrayList<SuggestedMember>();
- HashMap<Long, SuggestedMember> suggestionsMap = new HashMap<Long, SuggestedMember>();
-
- // First query for all the raw contacts that match the given search query
- // and have the same account name and type as specified in this adapter
- String searchQuery = prefix.toString() + "%";
- String accountClause = RawContacts.ACCOUNT_NAME + "=? AND " +
- RawContacts.ACCOUNT_TYPE + "=?";
- String[] args;
- if (mDataSet == null) {
- accountClause += " AND " + RawContacts.DATA_SET + " IS NULL";
- args = new String[] {mAccountName, mAccountType, searchQuery, searchQuery};
- } else {
- accountClause += " AND " + RawContacts.DATA_SET + "=?";
- args = new String[] {
- mAccountName, mAccountType, mDataSet, searchQuery, searchQuery
- };
- }
-
- Cursor cursor = mContentResolver.query(
- RawContacts.CONTENT_URI, PROJECTION_FILTERED_MEMBERS,
- accountClause + " AND (" +
- RawContacts.DISPLAY_NAME_PRIMARY + " LIKE ? OR " +
- RawContacts.DISPLAY_NAME_ALTERNATIVE + " LIKE ? )",
- args, RawContacts.DISPLAY_NAME_PRIMARY + " COLLATE LOCALIZED ASC");
-
- if (cursor == null) {
- return results;
- }
-
- // Read back the results from the cursor and filter out existing group members.
- // For valid suggestions, add them to the hash map of suggested members.
- try {
- cursor.moveToPosition(-1);
- while (cursor.moveToNext() && suggestionsMap.keySet().size() < SUGGESTIONS_LIMIT) {
- long rawContactId = cursor.getLong(RAW_CONTACT_ID_COLUMN_INDEX);
- long contactId = cursor.getLong(CONTACT_ID_COLUMN_INDEX);
- // Filter out contacts that have already been added to this group
- if (mExistingMemberContactIds.contains(contactId)) {
- continue;
- }
- // Otherwise, add the contact as a suggested new group member
- String displayName = cursor.getString(DISPLAY_NAME_PRIMARY_COLUMN_INDEX);
- SuggestedMember member = new SuggestedMember(rawContactId, displayName,
- contactId);
- // Store the member in the list of suggestions and add it to the hash map too.
- suggestionsList.add(member);
- suggestionsMap.put(rawContactId, member);
- }
- } finally {
- cursor.close();
- }
-
- int numSuggestions = suggestionsMap.keySet().size();
- if (numSuggestions == 0) {
- return results;
- }
-
- // Create a part of the selection string for the next query with the pattern (?, ?, ?)
- // where the number of comma-separated question marks represent the number of raw
- // contact IDs found in the previous query (while respective the SUGGESTION_LIMIT)
- final StringBuilder rawContactIdSelectionBuilder = new StringBuilder();
- final String[] questionMarks = new String[numSuggestions];
- Arrays.fill(questionMarks, "?");
- rawContactIdSelectionBuilder.append(RawContacts._ID + " IN (")
- .append(TextUtils.join(",", questionMarks))
- .append(")");
-
- // Construct the selection args based on the raw contact IDs we're interested in
- // (as well as the photo, email, and phone mimetypes)
- List<String> selectionArgs = new ArrayList<String>();
- selectionArgs.add(Photo.CONTENT_ITEM_TYPE);
- selectionArgs.add(Email.CONTENT_ITEM_TYPE);
- selectionArgs.add(Phone.CONTENT_ITEM_TYPE);
- for (Long rawContactId : suggestionsMap.keySet()) {
- selectionArgs.add(String.valueOf(rawContactId));
- }
-
- // Perform a second query to retrieve a photo and possibly a phone number or email
- // address for the suggested contact
- Cursor memberDataCursor = mContentResolver.query(
- RawContactsEntity.CONTENT_URI, PROJECTION_MEMBER_DATA,
- "(" + Data.MIMETYPE + "=? OR " + Data.MIMETYPE + "=? OR " + Data.MIMETYPE +
- "=?) AND " + rawContactIdSelectionBuilder.toString(),
- selectionArgs.toArray(new String[0]), null);
-
- if (memberDataCursor != null) {
- try {
- memberDataCursor.moveToPosition(-1);
- while (memberDataCursor.moveToNext()) {
- long rawContactId = memberDataCursor.getLong(RAW_CONTACT_ID_COLUMN_INDEX);
- SuggestedMember member = suggestionsMap.get(rawContactId);
- if (member == null) {
- continue;
- }
- String mimetype = memberDataCursor.getString(MIMETYPE_COLUMN_INDEX);
- if (Photo.CONTENT_ITEM_TYPE.equals(mimetype)) {
- // Set photo
- member.setPhotoId(memberDataCursor.getLong(ID_INDEX));
- byte[] bitmapArray = memberDataCursor.getBlob(PHOTO_COLUMN_INDEX);
- member.setPhotoByteArray(bitmapArray);
- } else if (Email.CONTENT_ITEM_TYPE.equals(mimetype) ||
- Phone.CONTENT_ITEM_TYPE.equals(mimetype)) {
- // Set at most 1 extra piece of contact info that can be a phone number or
- // email
- if (!member.hasExtraInfo()) {
- String info = memberDataCursor.getString(DATA_COLUMN_INDEX);
- member.setExtraInfo(info);
- }
- }
- }
- } finally {
- memberDataCursor.close();
- }
- }
- results.values = suggestionsList;
- return results;
- }
-
- @Override
- protected void publishResults(CharSequence constraint, FilterResults results) {
- @SuppressWarnings("unchecked")
- List<SuggestedMember> suggestionsList = (List<SuggestedMember>) results.values;
- if (suggestionsList == null) {
- return;
- }
-
- // Clear out the existing suggestions in this adapter
- clear();
-
- // Add all the suggested members to this adapter
- for (SuggestedMember member : suggestionsList) {
- add(member);
- }
-
- notifyDataSetChanged();
- }
- }
-
- /**
- * This represents a single contact that is a suggestion for the user to add to a group.
- */
- // TODO: Merge this with the {@link GroupEditorFragment} Member class once we can find the
- // lookup URI for this contact using the autocomplete filter queries
- public class SuggestedMember {
-
- private long mRawContactId;
- private long mContactId;
- private String mDisplayName;
-
- private String mExtraInfo;
- private byte[] mPhoto;
- private long mPhotoId;
-
- public SuggestedMember(long rawContactId, String displayName, long contactId) {
- mRawContactId = rawContactId;
- mDisplayName = displayName;
- mContactId = contactId;
- }
-
- public String getDisplayName() {
- return mDisplayName;
- }
-
- public String getExtraInfo() {
- return mExtraInfo;
- }
-
- public long getRawContactId() {
- return mRawContactId;
- }
-
- public long getContactId() {
- return mContactId;
- }
-
- public byte[] getPhotoByteArray() {
- return mPhoto;
- }
-
- public long getPhotoId() {
- return mPhotoId;
- }
-
- public boolean hasExtraInfo() {
- return mExtraInfo != null;
- }
-
- /**
- * Set a phone number or email to distinguish this contact
- */
- public void setExtraInfo(String info) {
- mExtraInfo = info;
- }
-
- public void setPhotoByteArray(byte[] photo) {
- mPhoto = photo;
- }
-
- public void setPhotoId(long photoId) {
- mPhotoId = photoId;
- }
-
- @Override
- public String toString() {
- return getDisplayName();
- }
- }
-}
diff --git a/src/com/android/contacts/interactions/AccountFiltersFragment.java b/src/com/android/contacts/interactions/AccountFiltersFragment.java
index 44a6edf..7836c19 100644
--- a/src/com/android/contacts/interactions/AccountFiltersFragment.java
+++ b/src/com/android/contacts/interactions/AccountFiltersFragment.java
@@ -41,7 +41,7 @@
/**
* Invoked after account filters have been loaded.
*/
- void onFiltersLoaded(List<ContactListFilter> groupListItems);
+ void onFiltersLoaded(List<ContactListFilter> accountFilterItems);
}
private final LoaderManager.LoaderCallbacks<List<ContactListFilter>> mFiltersLoaderListener =
diff --git a/src/com/android/contacts/list/ContactsIntentResolver.java b/src/com/android/contacts/list/ContactsIntentResolver.java
index ded72c2..39eaeba 100644
--- a/src/com/android/contacts/list/ContactsIntentResolver.java
+++ b/src/com/android/contacts/list/ContactsIntentResolver.java
@@ -16,6 +16,7 @@
package com.android.contacts.list;
+import android.accounts.Account;
import android.app.Activity;
import android.app.SearchManager;
import android.content.Intent;
@@ -94,10 +95,12 @@
request.setActionCode(ContactsRequest.ACTION_PICK_EMAIL);
} else if (Groups.CONTENT_ITEM_TYPE.equals(resolvedType)) {
request.setActionCode(ContactsRequest.ACTION_PICK_GROUP_MEMBERS);
- request.setAccountWithDataSet(intent.<AccountWithDataSet> getParcelableExtra(
- UiIntentActions.GROUP_ACCOUNT_WITH_DATA_SET));
+ request.setAccountWithDataSet(new AccountWithDataSet(
+ intent.getStringExtra(UiIntentActions.GROUP_ACCOUNT_NAME),
+ intent.getStringExtra(UiIntentActions.GROUP_ACCOUNT_TYPE),
+ intent.getStringExtra(UiIntentActions.GROUP_ACCOUNT_DATA_SET)));
request.setRawContactIds(intent.getStringArrayListExtra(
- UiIntentActions.GROUP_RAW_CONTACT_IDS));
+ UiIntentActions.GROUP_CONTACT_IDS));
}
} else if (Intent.ACTION_CREATE_SHORTCUT.equals(action)) {
String component = intent.getComponent().getClassName();
diff --git a/src/com/android/contacts/list/ContactsRequest.java b/src/com/android/contacts/list/ContactsRequest.java
index a686752..615fac6 100644
--- a/src/com/android/contacts/list/ContactsRequest.java
+++ b/src/com/android/contacts/list/ContactsRequest.java
@@ -16,10 +16,7 @@
package com.android.contacts.list;
-import android.content.Intent;
import android.net.Uri;
-import android.os.Parcel;
-import android.os.Parcelable;
import com.android.contacts.common.model.account.AccountWithDataSet;
diff --git a/src/com/android/contacts/list/ContactsUnavailableFragment.java b/src/com/android/contacts/list/ContactsUnavailableFragment.java
index 5c62e5c..99a5b7e 100644
--- a/src/com/android/contacts/list/ContactsUnavailableFragment.java
+++ b/src/com/android/contacts/list/ContactsUnavailableFragment.java
@@ -33,7 +33,7 @@
import com.android.contacts.R;
import com.android.contacts.activities.ActionBarAdapter.TabState;
-import com.android.contacts.compat.ProviderStatusCompat;
+import com.android.contacts.common.compat.ProviderStatusCompat;
/**
* Fragment shown when contacts are unavailable. It contains provider status
diff --git a/src/com/android/contacts/list/GroupMemberPickListAdapter.java b/src/com/android/contacts/list/GroupMemberPickListAdapter.java
deleted file mode 100644
index d25aafd..0000000
--- a/src/com/android/contacts/list/GroupMemberPickListAdapter.java
+++ /dev/null
@@ -1,182 +0,0 @@
-/*
- * 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.list;
-
-import android.content.Context;
-import android.content.CursorLoader;
-import android.database.Cursor;
-import android.net.Uri;
-import android.provider.ContactsContract.CommonDataKinds.GroupMembership;
-import android.provider.ContactsContract.Data;
-import android.provider.ContactsContract.RawContacts;
-import android.text.TextUtils;
-import android.view.View;
-import android.view.ViewGroup;
-
-import com.android.contacts.common.ContactPhotoManager.DefaultImageRequest;
-import com.android.contacts.common.list.ContactEntryListAdapter;
-import com.android.contacts.common.list.ContactListItemView;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.preference.ContactsPreferences;
-
-import java.util.ArrayList;
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * Adapter for raw contacts owned by an account that are not already members of a given group.
- */
-public class GroupMemberPickListAdapter extends ContactEntryListAdapter {
-
- static class GroupMembersQuery {
-
- private static final String[] PROJECTION_PRIMARY = new String[] {
- Data.RAW_CONTACT_ID, // 0
- Data.CONTACT_ID, // 1
- Data.DISPLAY_NAME_PRIMARY, // 2
- // Dummy columns overwritten by the cursor wrapper
- Data.PHOTO_ID, // 3
- Data.LOOKUP_KEY // 4
- };
-
- private static final String[] PROJECTION_ALTERNATIVE = new String[] {
- Data.RAW_CONTACT_ID, // 0
- Data.CONTACT_ID, // 1
- Data.DISPLAY_NAME_ALTERNATIVE, // 2
- // Dummy columns overwritten by the cursor wrapper
- Data.PHOTO_ID, // 3
- Data.LOOKUP_KEY // 4
- };
-
- static final int RAW_CONTACT_ID = 0;
- static final int CONTACT_ID = 1;
- static final int CONTACT_DISPLAY_NAME = 2;
- // Provided by the cursor wrapper.
- static final int CONTACT_PHOTO_ID = 3;
- // Provided by the cursor wrapper.
- static final int CONTACT_LOOKUP_KEY = 4;
-
- private GroupMembersQuery() {
- }
- }
-
- private AccountWithDataSet mAccount;
- private final Set<String> mRawContactIds = new HashSet<>();
-
- private final CharSequence mUnknownNameText;
-
- public GroupMemberPickListAdapter(Context context) {
- super(context);
- mUnknownNameText = context.getText(android.R.string.unknownName);
- }
-
- public GroupMemberPickListAdapter setAccount(AccountWithDataSet account) {
- mAccount = account;
- return this;
- }
-
- public GroupMemberPickListAdapter setRawContactIds(ArrayList<String> rawContactIds) {
- mRawContactIds.clear();
- mRawContactIds.addAll(rawContactIds);
- return this;
- }
-
- @Override
- public String getContactDisplayName(int position) {
- final Cursor cursor = (Cursor) getItem(position);
- return cursor.getString(GroupMembersQuery.CONTACT_DISPLAY_NAME);
- }
-
- @Override
- public void configureLoader(CursorLoader loader, long directoryId) {
- final Uri uri = Data.CONTENT_URI.buildUpon()
- .appendQueryParameter(Data.VISIBLE_CONTACTS_ONLY, "true")
- .build();
- loader.setUri(uri);
- loader.setProjection(
- getContactNameDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY
- ? GroupMembersQuery.PROJECTION_PRIMARY
- : GroupMembersQuery.PROJECTION_ALTERNATIVE);
- loader.setSelection(getSelection());
- loader.setSelectionArgs(getSelectionArgs());
- loader.setSortOrder(getSortOrder() == ContactsPreferences.SORT_ORDER_PRIMARY
- ? Data.SORT_KEY_PRIMARY : Data.SORT_KEY_ALTERNATIVE
- + " COLLATE LOCALIZED ASC");
- }
-
- private String getSelection() {
- // Select raw contacts by account
- String result = RawContacts.ACCOUNT_NAME + "=? AND " + RawContacts.ACCOUNT_TYPE + "=? AND "
- + Data.MIMETYPE + "=? AND ";
- if (TextUtils.isEmpty(mAccount.dataSet)) {
- result += Data.DATA_SET + " IS NULL";
- } else {
- result += Data.DATA_SET + "=?";
- }
- return result;
- }
-
- private String[] getSelectionArgs() {
- final ArrayList<String> result = new ArrayList<>();
- result.add(mAccount.name);
- result.add(mAccount.type);
- result.add(GroupMembership.CONTENT_ITEM_TYPE);
- if (!TextUtils.isEmpty(mAccount.dataSet)) result.add(mAccount.dataSet);
- return result.toArray(new String[0]);
- }
-
- public Uri getRawContactUri(int position) {
- final Cursor cursor = (Cursor) getItem(position);
- final long rawContactId = cursor.getLong(GroupMembersQuery.RAW_CONTACT_ID);
- return Data.CONTENT_URI.buildUpon()
- .appendPath(Long.toString(rawContactId))
- .build();
- }
-
- @Override
- protected ContactListItemView newView(Context context, int partition, Cursor cursor,
- int position, ViewGroup parent) {
- final ContactListItemView view =
- super.newView(context, partition, cursor, position, parent);
- view.setUnknownNameText(mUnknownNameText);
- return view;
- }
-
- @Override
- protected void bindView(View v, int partition, Cursor cursor, int position) {
- super.bindView(v, partition, cursor, position);
- final ContactListItemView view = (ContactListItemView) v;
- bindName(view, cursor);
- bindViewId(view, cursor, GroupMembersQuery.RAW_CONTACT_ID);
- bindPhoto(view, cursor);
- }
-
- private void bindName(ContactListItemView view, Cursor cursor) {
- view.showDisplayName(cursor, GroupMembersQuery.CONTACT_DISPLAY_NAME,
- getContactNameDisplayOrder());
- }
-
- private void bindPhoto(final ContactListItemView view, Cursor cursor) {
- final long photoId = cursor.isNull(GroupMembersQuery.CONTACT_PHOTO_ID)
- ? 0 : cursor.getLong(GroupMembersQuery.CONTACT_PHOTO_ID);
- final DefaultImageRequest imageRequest = photoId == 0
- ? getDefaultImageRequestFromCursor(cursor, GroupMembersQuery.CONTACT_DISPLAY_NAME,
- GroupMembersQuery.CONTACT_LOOKUP_KEY)
- : null;
- getPhotoLoader().loadThumbnail(view.getPhotoView(), photoId, false, getCircularPhotos(),
- imageRequest);
- }
-}
diff --git a/src/com/android/contacts/list/GroupMemberPickerFragment.java b/src/com/android/contacts/list/GroupMemberPickerFragment.java
index edd5739..605758f 100644
--- a/src/com/android/contacts/list/GroupMemberPickerFragment.java
+++ b/src/com/android/contacts/list/GroupMemberPickerFragment.java
@@ -15,54 +15,46 @@
*/
package com.android.contacts.list;
-import android.app.LoaderManager.LoaderCallbacks;
-import android.content.CursorLoader;
import android.content.Loader;
import android.database.Cursor;
import android.database.CursorWrapper;
-import android.net.Uri;
import android.os.Bundle;
-import android.provider.ContactsContract.Contacts;
-import android.provider.ContactsContract.Data;
import android.util.Log;
-import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.android.contacts.common.R;
import com.android.contacts.common.list.ContactEntryListFragment;
-import com.android.contacts.common.model.account.AccountWithDataSet;
-import com.android.contacts.common.preference.ContactsPreferences;
-import com.android.contacts.list.GroupMemberPickListAdapter.GroupMembersQuery;
+import com.android.contacts.common.list.ContactListAdapter.ContactQuery;
+import com.android.contacts.common.list.ContactListFilter;
+import com.android.contacts.common.list.DefaultContactListAdapter;
import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
/**
* Fragment containing raw contacts for a specified account that are not already in a group.
*/
public class GroupMemberPickerFragment extends
- ContactEntryListFragment<GroupMemberPickListAdapter> {
+ ContactEntryListFragment<DefaultContactListAdapter> {
public static final String TAG = "GroupMemberPicker";
- private static final String KEY_ACCOUNT = "account";
+ private static final String KEY_ACCOUNT_NAME = "accountName";
+ private static final String KEY_ACCOUNT_TYPE = "accountType";
+ private static final String KEY_ACCOUNT_DATA_SET = "accountDataSet";
private static final String KEY_RAW_CONTACT_IDS = "rawContactIds";
- private static final String ARG_ACCOUNT = "account";
+ private static final String ARG_ACCOUNT_NAME = "accountName";
+ private static final String ARG_ACCOUNT_TYPE = "accountType";
+ private static final String ARG_ACCOUNT_DATA_SET = "accountDataSet";
private static final String ARG_RAW_CONTACT_IDS = "rawContactIds";
- private static final int LOADER_CONTACT_ENTITY = 0;
-
/** Callbacks for host of {@link GroupMemberPickerFragment}. */
public interface Listener {
/** Invoked when a potential group member is selected. */
- void onGroupMemberClicked(Uri uri);
+ void onGroupMemberClicked(long contactId);
}
/**
@@ -82,23 +74,14 @@
mIndex = new int[mCount];
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "FilterCursorWrapper starting size cursor=" + mCount + " photosMap="
- + (mContactPhotosMap == null ? 0 : mContactPhotosMap.size()));
+ Log.v(TAG, "FilterCursorWrapper starting cursor size is " + mCount);
}
- final Set<String> uniqueRawContactIds = new HashSet<String>();
-
for (int i = 0; i < mCount; i++) {
super.moveToPosition(i);
- final String rawContactId = getString(GroupMembersQuery.RAW_CONTACT_ID);
- if (!mRawContactIds.contains(rawContactId)
- && !uniqueRawContactIds.contains(rawContactId)) {
+ final String contactId = getString(ContactQuery.CONTACT_ID);
+ if (!mRawContactIds.contains(contactId)) {
mIndex[mPos++] = i;
- uniqueRawContactIds.add(rawContactId);
- }
- if (mRawContactIds.contains(rawContactId) && mContactPhotosMap != null) {
- final long contactId = getLong(GroupMembersQuery.CONTACT_ID);
- mContactPhotosMap.remove(contactId);
}
}
mCount = mPos;
@@ -106,82 +89,11 @@
super.moveToFirst();
if (Log.isLoggable(TAG, Log.VERBOSE)) {
- Log.v(TAG, "FilterCursorWrapper ending size cursor=" + mCount + " photosMap="
- + (mContactPhotosMap == null ? 0 : mContactPhotosMap.size()));
+ Log.v(TAG, "FilterCursorWrapper ending cursor size is" + mCount);
}
}
@Override
- public int getColumnIndex(String columnName) {
- final int index = getColumnIndexForContactColumn(columnName);
- return index < 0 ? super.getColumnIndex(columnName) : index;
- }
-
- @Override
- public int getColumnIndexOrThrow(String columnName) {
- final int index = getColumnIndexForContactColumn(columnName);
- return index < 0 ? super.getColumnIndexOrThrow(columnName) : index;
- }
-
- private int getColumnIndexForContactColumn(String columnName) {
- if (Data.PHOTO_ID.equals(columnName)) {
- return GroupMembersQuery.CONTACT_PHOTO_ID;
- }
- if (Data.LOOKUP_KEY.equals(columnName)) {
- return GroupMembersQuery.CONTACT_LOOKUP_KEY;
- }
- return -1;
- }
-
- @Override
- public String[] getColumnNames() {
- final String displayNameColumnName =
- getContactNameDisplayOrder() == ContactsPreferences.DISPLAY_ORDER_PRIMARY
- ? Data.DISPLAY_NAME_PRIMARY
- : Data.DISPLAY_NAME_ALTERNATIVE;
- return new String[] {
- Data._ID,
- Data.CONTACT_ID,
- displayNameColumnName,
- Data.PHOTO_ID,
- Data.LOOKUP_KEY,
- };
- }
-
- @Override
- public String getString(int columnIndex) {
- if (columnIndex == GroupMembersQuery.CONTACT_LOOKUP_KEY) {
- if (columnIndex == GroupMembersQuery.CONTACT_PHOTO_ID) {
- final long contactId = getLong(GroupMembersQuery.CONTACT_ID);
- final Pair<Long,String> pair = getContactPhotoPair(contactId);
- if (pair != null) {
- return pair.second;
- }
- }
- return null;
- }
- return super.getString(columnIndex);
- }
-
- @Override
- public long getLong(int columnIndex) {
- if (columnIndex == GroupMembersQuery.CONTACT_PHOTO_ID) {
- final long contactId = getLong(GroupMembersQuery.CONTACT_ID);
- final Pair<Long,String> pair = getContactPhotoPair(contactId);
- if (pair != null) {
- return pair.first;
- }
- return 0;
- }
- return super.getLong(columnIndex);
- }
-
- private Pair<Long,String> getContactPhotoPair(long contactId) {
- return mContactPhotosMap != null && mContactPhotosMap.containsKey(contactId)
- ? mContactPhotosMap.get(contactId) : null;
- }
-
- @Override
public boolean move(int offset) {
return moveToPosition(mPos + offset);
}
@@ -223,49 +135,19 @@
}
}
- private final LoaderCallbacks<Cursor> mContactsEntityCallbacks = new LoaderCallbacks<Cursor>() {
-
- private final String[] PROJECTION = new String[] {
- Data.CONTACT_ID,
- Data.PHOTO_ID,
- Data.LOOKUP_KEY
- };
-
- @Override
- public CursorLoader onCreateLoader(int id, Bundle args) {
- final CursorLoader loader = new CursorLoader(getActivity());
- loader.setUri(Data.CONTENT_URI);
- loader.setProjection(PROJECTION);
- return loader;
- }
- @Override
- public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
- mContactPhotosMap = new HashMap<>();
- while (cursor.moveToNext()) {
- final long contactId = cursor.getLong(0);
- final Pair<Long, String> pair =
- new Pair(cursor.getLong(1), cursor.getString(2));
- mContactPhotosMap.put(contactId, pair);
- }
- GroupMemberPickerFragment.super.startLoading();
- }
-
- @Override
- public void onLoaderReset(Loader<Cursor> loader) {}
- };
-
- private AccountWithDataSet mAccount;
+ private String mAccountName;
+ private String mAccountType;
+ private String mAccountDataSet;
private ArrayList<String> mRawContactIds;
- // Contact ID longs to Pairs of photo ID and contact lookup keys
- private Map<Long, Pair<Long,String>> mContactPhotosMap;
-
private Listener mListener;
- public static GroupMemberPickerFragment newInstance(AccountWithDataSet account,
- ArrayList<String> rawContactids) {
+ public static GroupMemberPickerFragment newInstance(String accountName, String accountType,
+ String accountDataSet, ArrayList<String> rawContactIds) {
final Bundle args = new Bundle();
- args.putParcelable(ARG_ACCOUNT, account);
- args.putStringArrayList(ARG_RAW_CONTACT_IDS, rawContactids);
+ args.putString(ARG_ACCOUNT_NAME, accountName);
+ args.putString(ARG_ACCOUNT_TYPE, accountType);
+ args.putString(ARG_ACCOUNT_DATA_SET, accountDataSet);
+ args.putStringArrayList(ARG_RAW_CONTACT_IDS, rawContactIds);
final GroupMemberPickerFragment fragment = new GroupMemberPickerFragment();
fragment.setArguments(args);
@@ -273,40 +155,38 @@
}
public GroupMemberPickerFragment() {
- setQuickContactEnabled(false);
setPhotoLoaderEnabled(true);
+ setSectionHeaderDisplayEnabled(false);
setVisibleScrollbarEnabled(true);
+
setHasOptionsMenu(true);
}
@Override
public void onCreate(Bundle savedState) {
- super.onCreate(savedState);
if (savedState == null) {
- mAccount = getArguments().getParcelable(ARG_ACCOUNT);
+ mAccountName = getArguments().getString(ARG_ACCOUNT_NAME);
+ mAccountType = getArguments().getString(ARG_ACCOUNT_TYPE);
+ mAccountDataSet = getArguments().getString(ARG_ACCOUNT_DATA_SET);
mRawContactIds = getArguments().getStringArrayList(ARG_RAW_CONTACT_IDS);
} else {
- mAccount = savedState.getParcelable(KEY_ACCOUNT);
+ mAccountName = savedState.getString(KEY_ACCOUNT_NAME);
+ mAccountType = savedState.getString(KEY_ACCOUNT_TYPE);
+ mAccountDataSet = savedState.getString(KEY_ACCOUNT_DATA_SET);
mRawContactIds = savedState.getStringArrayList(KEY_RAW_CONTACT_IDS);
}
+ super.onCreate(savedState);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
- outState.putParcelable(KEY_ACCOUNT, mAccount);
+ outState.putString(KEY_ACCOUNT_NAME, mAccountName);
+ outState.putString(KEY_ACCOUNT_TYPE, mAccountType);
+ outState.putString(KEY_ACCOUNT_DATA_SET, mAccountDataSet);
outState.putStringArrayList(KEY_RAW_CONTACT_IDS, mRawContactIds);
}
- @Override
- protected void startLoading() {
- if (mContactPhotosMap == null) {
- getLoaderManager().restartLoader(LOADER_CONTACT_ENTITY, null, mContactsEntityCallbacks);
- } else {
- super.startLoading();
- }
- }
-
public void setListener(Listener listener) {
mListener = listener;
}
@@ -324,24 +204,22 @@
}
@Override
- protected GroupMemberPickListAdapter createListAdapter() {
- final GroupMemberPickListAdapter adapter = new GroupMemberPickListAdapter(getActivity());
+ protected DefaultContactListAdapter createListAdapter() {
+ final DefaultContactListAdapter adapter = new DefaultContactListAdapter(getActivity());
+ adapter.setFilter(ContactListFilter.createGroupMembersFilter(
+ mAccountType, mAccountName, mAccountDataSet));
+ adapter.setSectionHeaderDisplayEnabled(true);
adapter.setDisplayPhotos(true);
return adapter;
}
@Override
- protected void configureAdapter() {
- super.configureAdapter();
- getAdapter().setAccount(mAccount);
- getAdapter().setRawContactIds(mRawContactIds);
- getAdapter().setEmptyListEnabled(true);
- }
-
- @Override
protected void onItemClick(int position, long id) {
if (mListener != null) {
- mListener.onGroupMemberClicked(getAdapter().getRawContactUri(position));
+ final long contactId = getAdapter().getContactId(position);
+ if (contactId > 0) {
+ mListener.onGroupMemberClicked(contactId);
+ }
}
}
}
diff --git a/src/com/android/contacts/list/GroupMemberTileAdapter.java b/src/com/android/contacts/list/GroupMemberTileAdapter.java
deleted file mode 100644
index 15e67f1..0000000
--- a/src/com/android/contacts/list/GroupMemberTileAdapter.java
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * Copyright (C) 2012 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.list;
-
-import android.content.Context;
-import android.database.Cursor;
-
-import com.android.contacts.GroupMemberLoader;
-import com.android.contacts.common.list.ContactEntry;
-import com.android.contacts.common.list.ContactTileAdapter;
-import com.android.contacts.common.list.ContactTileView;
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * Tile adapter for groups.
- */
-public class GroupMemberTileAdapter extends ContactTileAdapter {
-
- public GroupMemberTileAdapter(Context context, ContactTileView.Listener listener, int numCols) {
- super(context, listener, numCols, DisplayType.GROUP_MEMBERS);
- }
-
- @Override
- protected void bindColumnIndices() {
- mIdIndex = GroupMemberLoader.GroupDetailQuery.CONTACT_ID;
- mLookupIndex = GroupMemberLoader.GroupDetailQuery.CONTACT_LOOKUP_KEY;
- mPhotoUriIndex = GroupMemberLoader.GroupDetailQuery.CONTACT_PHOTO_URI;
- mNameIndex = GroupMemberLoader.GroupDetailQuery.CONTACT_DISPLAY_NAME_PRIMARY;
- mPresenceIndex = GroupMemberLoader.GroupDetailQuery.CONTACT_PRESENCE_STATUS;
- mStatusIndex = GroupMemberLoader.GroupDetailQuery.CONTACT_STATUS;
- }
-
- @Override
- protected void saveNumFrequentsFromCursor(Cursor cursor) {
- mNumFrequents = 0;
- }
-
- @Override
- public int getItemViewType(int position) {
- return ViewTypes.STARRED;
- }
-
- @Override
- protected int getDividerPosition(Cursor cursor) {
- // No divider
- return -1;
- }
-
- @Override
- public int getCount() {
- if (mContactCursor == null || mContactCursor.isClosed()) {
- return 0;
- }
-
- return getRowCount(mContactCursor.getCount());
- }
-
- @Override
- public ArrayList<ContactEntry> getItem(int position) {
- final ArrayList<ContactEntry> resultList = Lists.newArrayListWithCapacity(mColumnCount);
- int contactIndex = position * mColumnCount;
-
- for (int columnCounter = 0; columnCounter < mColumnCount; columnCounter++) {
- resultList.add(createContactEntryFromCursor(mContactCursor, contactIndex));
- contactIndex++;
- }
- return resultList;
- }
-}
diff --git a/src/com/android/contacts/list/ProviderStatusWatcher.java b/src/com/android/contacts/list/ProviderStatusWatcher.java
deleted file mode 100644
index 51f776b..0000000
--- a/src/com/android/contacts/list/ProviderStatusWatcher.java
+++ /dev/null
@@ -1,274 +0,0 @@
-/*
- * Copyright (C) 2012 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.list;
-
-import android.content.ContentValues;
-import android.content.Context;
-import android.database.ContentObserver;
-import android.database.Cursor;
-import android.net.Uri;
-import android.os.AsyncTask;
-import android.os.Handler;
-import android.provider.ContactsContract.ProviderStatus;
-import android.util.Log;
-
-import com.android.contacts.compat.ProviderStatusCompat;
-
-import com.google.common.collect.Lists;
-
-import java.util.ArrayList;
-
-/**
- * A singleton that keeps track of the last known provider status.
- *
- * All methods must be called on the UI thread unless noted otherwise.
- *
- * All members must be set on the UI thread unless noted otherwise.
- */
-public class ProviderStatusWatcher extends ContentObserver {
- private static final String TAG = "ProviderStatusWatcher";
- private static final boolean DEBUG = false;
-
- /**
- * Callback interface invoked when the provider status changes.
- */
- public interface ProviderStatusListener {
- public void onProviderStatusChange();
- }
-
- private static final String[] PROJECTION = new String[] {
- ProviderStatus.STATUS
- };
-
- /**
- * We'll wait for this amount of time on the UI thread if the load hasn't finished.
- */
- private static final int LOAD_WAIT_TIMEOUT_MS = 1000;
-
- private static ProviderStatusWatcher sInstance;
-
- private final Context mContext;
- private final Handler mHandler = new Handler();
-
- private final Object mSignal = new Object();
-
- private int mStartRequestedCount;
-
- private LoaderTask mLoaderTask;
-
- /** Last known provider status. This can be changed on a worker thread.
- * See {@link ProviderStatus#STATUS} */
- private Integer mProviderStatus;
-
- private final ArrayList<ProviderStatusListener> mListeners = Lists.newArrayList();
-
- private final Runnable mStartLoadingRunnable = new Runnable() {
- @Override
- public void run() {
- startLoading();
- }
- };
-
- /**
- * Returns the singleton instance.
- */
- public synchronized static ProviderStatusWatcher getInstance(Context context) {
- if (sInstance == null) {
- sInstance = new ProviderStatusWatcher(context);
- }
- return sInstance;
- }
-
- private ProviderStatusWatcher(Context context) {
- super(null);
- mContext = context;
- }
-
- /** Add a listener. */
- public void addListener(ProviderStatusListener listener) {
- mListeners.add(listener);
- }
-
- /** Remove a listener */
- public void removeListener(ProviderStatusListener listener) {
- mListeners.remove(listener);
- }
-
- private void notifyListeners() {
- if (DEBUG) {
- Log.d(TAG, "notifyListeners: " + mListeners.size());
- }
- if (isStarted()) {
- for (ProviderStatusListener listener : mListeners) {
- listener.onProviderStatusChange();
- }
- }
- }
-
- private boolean isStarted() {
- return mStartRequestedCount > 0;
- }
-
- /**
- * Starts watching the provider status. {@link #start()} and {@link #stop()} calls can be
- * nested.
- */
- public void start() {
- if (++mStartRequestedCount == 1) {
- mContext.getContentResolver()
- .registerContentObserver(ProviderStatus.CONTENT_URI, false, this);
- startLoading();
-
- if (DEBUG) {
- Log.d(TAG, "Start observing");
- }
- }
- }
-
- /**
- * Stops watching the provider status.
- */
- public void stop() {
- if (!isStarted()) {
- Log.e(TAG, "Already stopped");
- return;
- }
- if (--mStartRequestedCount == 0) {
-
- mHandler.removeCallbacks(mStartLoadingRunnable);
-
- mContext.getContentResolver().unregisterContentObserver(this);
- if (DEBUG) {
- Log.d(TAG, "Stop observing");
- }
- }
- }
-
- /**
- * @return last known provider status.
- *
- * If this method is called when we haven't started the status query or the query is still in
- * progress, it will start a query in a worker thread if necessary, and *wait for the result*.
- *
- * This means this method is essentially a blocking {@link ProviderStatus#CONTENT_URI} query.
- * This URI is not backed by the file system, so is usually fast enough to perform on the main
- * thread, but in extreme cases (when the system takes a while to bring up the contacts
- * provider?) this may still cause ANRs.
- *
- * In order to avoid that, if we can't load the status within {@link #LOAD_WAIT_TIMEOUT_MS},
- * we'll give up and just returns {@link ProviderStatusCompat#STATUS_BUSY} in order to unblock
- * the UI thread. The actual result will be delivered later via {@link ProviderStatusListener}.
- * (If {@link ProviderStatusCompat#STATUS_BUSY} is returned, the app (should) shows an according
- * message, like "contacts are being updated".)
- */
- public int getProviderStatus() {
- waitForLoaded();
-
- if (mProviderStatus == null) {
- return ProviderStatusCompat.STATUS_BUSY;
- }
-
- return mProviderStatus;
- }
-
- private void waitForLoaded() {
- if (mProviderStatus == null) {
- if (mLoaderTask == null) {
- // For some reason the loader couldn't load the status. Let's start it again.
- startLoading();
- }
- synchronized (mSignal) {
- try {
- mSignal.wait(LOAD_WAIT_TIMEOUT_MS);
- } catch (InterruptedException ignore) {
- }
- }
- }
- }
-
- private void startLoading() {
- if (mLoaderTask != null) {
- return; // Task already running.
- }
-
- if (DEBUG) {
- Log.d(TAG, "Start loading");
- }
-
- mLoaderTask = new LoaderTask();
- mLoaderTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR);
- }
-
- private class LoaderTask extends AsyncTask<Void, Void, Boolean> {
- @Override
- protected Boolean doInBackground(Void... params) {
- try {
- Cursor cursor = mContext.getContentResolver().query(ProviderStatus.CONTENT_URI,
- PROJECTION, null, null, null);
- if (cursor != null) {
- try {
- if (cursor.moveToFirst()) {
- // Note here we can't just say "Status", as AsyncTask has the "Status"
- // enum too.
- mProviderStatus = cursor.getInt(0);
- return true;
- }
- } finally {
- cursor.close();
- }
- }
- return false;
- } finally {
- synchronized (mSignal) {
- mSignal.notifyAll();
- }
- }
- }
-
- @Override
- protected void onCancelled(Boolean result) {
- cleanUp();
- }
-
- @Override
- protected void onPostExecute(Boolean loaded) {
- cleanUp();
- if (loaded != null && loaded) {
- notifyListeners();
- }
- }
-
- private void cleanUp() {
- mLoaderTask = null;
- }
- }
-
- /**
- * Called when provider status may has changed.
- *
- * This method will be called on a worker thread by the framework.
- */
- @Override
- public void onChange(boolean selfChange, Uri uri) {
- if (!ProviderStatus.CONTENT_URI.equals(uri)) return;
-
- // Provider status change is rare, so okay to log.
- Log.i(TAG, "Provider status changed.");
-
- mHandler.removeCallbacks(mStartLoadingRunnable); // Remove one in the queue, if any.
- mHandler.post(mStartLoadingRunnable);
- }
-}
diff --git a/src/com/android/contacts/list/UiIntentActions.java b/src/com/android/contacts/list/UiIntentActions.java
index 31e5ade..9552942 100644
--- a/src/com/android/contacts/list/UiIntentActions.java
+++ b/src/com/android/contacts/list/UiIntentActions.java
@@ -40,16 +40,28 @@
public static final String GROUP_NAME_EXTRA_KEY = "com.android.contacts.extra.GROUP";
/**
- * The account used to filter potential raw contact groups members.
+ * The account name used to filter potential new group members.
*/
- public static final String GROUP_ACCOUNT_WITH_DATA_SET =
- "com.android.contacts.extra.GROUP_ACCOUNT_WITH_DATA_SET";
+ public static final String GROUP_ACCOUNT_NAME =
+ "com.android.contacts.extra.GROUP_ACCOUNT_NAME";
/**
- * The raw contact IDs for existing group members.
+ * The account type used to filter potential new group members.
*/
- public static final String GROUP_RAW_CONTACT_IDS =
- "com.android.contacts.extra.GROUP_RAW_CONTACT_IDS";
+ public static final String GROUP_ACCOUNT_TYPE =
+ "com.android.contacts.extra.GROUP_ACCOUNT_TYPE";
+
+ /**
+ * The account data set used to filter potential new group members.
+ */
+ public static final String GROUP_ACCOUNT_DATA_SET =
+ "com.android.contacts.extra.GROUP_ACCOUNT_DATA_SET";
+
+ /**
+ * The contact IDs for existing group members.
+ */
+ public static final String GROUP_CONTACT_IDS =
+ "com.android.contacts.extra.GROUP_CONTACT_IDS";
/**
* The action for the all contacts list tab.
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 76e8605..64db298 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -209,6 +209,9 @@
/** Used to pass the screen where the user came before launching this Activity. */
public static final String EXTRA_PREVIOUS_SCREEN_TYPE = "previous_screen_type";
+ /** Used to tell the QuickContact that the previous contact was edited, so it can return an
+ * activity result back to the original Activity that launched it. */
+ public static final String EXTRA_CONTACT_EDITED = "contact_edited";
private static final String TAG = "QuickContact";
@@ -1209,6 +1212,9 @@
return;
}
Uri lookupUri = intent.getData();
+ if (intent.getBooleanExtra(EXTRA_CONTACT_EDITED, false)) {
+ setResult(ContactEditorBaseActivity.RESULT_CODE_EDITED);
+ }
// Check to see whether it comes from the old version.
if (lookupUri != null && LEGACY_AUTHORITY.equals(lookupUri.getAuthority())) {