Log when a search result is selected or search is abandoned (1/3)
* Populate a SearchState from the MultiSelectContactEntryListAdapter
to with information about the number of results and partitions
displayed to the user. If a selection was made, record additional
details.
Bug 26697731
Change-Id: I96de87ea1d297045421604ee0cd13c51c6c13dc4
diff --git a/src/com/android/contacts/activities/PeopleActivity.java b/src/com/android/contacts/activities/PeopleActivity.java
index 9806c8c..f0ad19d 100644
--- a/src/com/android/contacts/activities/PeopleActivity.java
+++ b/src/com/android/contacts/activities/PeopleActivity.java
@@ -51,7 +51,6 @@
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
-import android.view.Window;
import android.widget.ImageButton;
import android.widget.Toast;
@@ -87,6 +86,8 @@
import com.android.contacts.list.ProviderStatusWatcher;
import com.android.contacts.list.ProviderStatusWatcher.ProviderStatusListener;
import com.android.contacts.common.list.ViewPagerTabs;
+import com.android.contacts.common.logging.Logger;
+import com.android.contacts.common.logging.ScreenEvent;
import com.android.contacts.preference.ContactsPreferenceActivity;
import com.android.contacts.common.util.AccountFilterUtil;
import com.android.contacts.common.util.ViewUtil;
@@ -564,13 +565,14 @@
switch (action) {
case ActionBarAdapter.Listener.Action.START_SELECTION_MODE:
mAllFragment.displayCheckBoxes(true);
- // Fall through:
+ startSearchOrSelectionMode();
+ break;
case ActionBarAdapter.Listener.Action.START_SEARCH_MODE:
- // Tell the fragments that we're in the search mode or selection mode
- configureFragments(false /* from request */);
- updateFragmentsVisibility();
- invalidateOptionsMenu();
- showFabWithAnimation(/* showFabWithAnimation = */ false);
+ if (!mIsRecreatedInstance) {
+ Logger.getInstance().logScreenView(
+ ScreenEvent.SEARCH, this, ScreenEvent.TAG_SEARCH);
+ }
+ startSearchOrSelectionMode();
break;
case ActionBarAdapter.Listener.Action.BEGIN_STOPPING_SEARCH_AND_SELECTION_MODE:
showFabWithAnimation(/* showFabWithAnimation = */ true);
@@ -592,6 +594,13 @@
}
}
+ private void startSearchOrSelectionMode() {
+ configureFragments(false /* from request */);
+ updateFragmentsVisibility();
+ invalidateOptionsMenu();
+ showFabWithAnimation(/* showFabWithAnimation = */ false);
+ }
+
@Override
public void onSelectedTabChanged() {
updateFragmentsVisibility();
@@ -1315,6 +1324,7 @@
intent.putExtra(Intent.EXTRA_STREAM, uri);
ImplicitIntentsUtil.startActivityOutsideApp(this, intent);
}
+
private void joinSelectedContacts() {
JoinContactsDialogFragment.start(this, mAllFragment.getSelectedContactIds());
}
@@ -1396,6 +1406,14 @@
mAllFragment.displayCheckBoxes(false);
} else if (mActionBarAdapter.isSearchMode()) {
mActionBarAdapter.setSearchMode(false);
+
+ if (mAllFragment.wasSearchResultClicked()) {
+ mAllFragment.resetSearchResultClicked();
+ } else {
+ Logger.getInstance().logScreenView(
+ ScreenEvent.SEARCH_EXIT, this, ScreenEvent.TAG_SEARCH_EXIT);
+ Logger.getInstance().logSearchEventImpl(mAllFragment.createSearchState());
+ }
} else {
super.onBackPressed();
}
diff --git a/src/com/android/contacts/list/MultiSelectContactsListFragment.java b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
index 4d2eae8..1c5d7e7 100644
--- a/src/com/android/contacts/list/MultiSelectContactsListFragment.java
+++ b/src/com/android/contacts/list/MultiSelectContactsListFragment.java
@@ -19,14 +19,19 @@
import com.android.contacts.common.list.ContactListAdapter;
import com.android.contacts.common.list.ContactListItemView;
import com.android.contacts.common.list.DefaultContactListAdapter;
+import com.android.contacts.common.logging.SearchState;
import com.android.contacts.list.MultiSelectEntryContactListAdapter.SelectedContactsListener;
+import com.android.contacts.common.logging.Logger;
+import android.database.Cursor;
import android.net.Uri;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.text.TextUtils;
import android.view.accessibility.AccessibilityEvent;
+import java.util.ArrayList;
+import java.util.List;
import java.util.TreeSet;
/**
@@ -44,12 +49,31 @@
private static final String EXTRA_KEY_SELECTED_CONTACTS = "selected_contacts";
+ private static final String KEY_SEARCH_RESULT_CLICKED = "search_result_clicked";
+
private OnCheckBoxListActionListener mCheckBoxListListener;
+ private boolean mSearchResultClicked;
public void setCheckBoxListListener(OnCheckBoxListActionListener checkBoxListListener) {
mCheckBoxListListener = checkBoxListListener;
}
+ /**
+ * Whether a search result was clicked by the user. Tracked so that we can distinguish
+ * between exiting the search mode after a result was clicked from existing w/o clicking
+ * any search result.
+ */
+ public boolean wasSearchResultClicked() {
+ return mSearchResultClicked;
+ }
+
+ /**
+ * Resets whether a search result was clicked by the user to false.
+ */
+ public void resetSearchResultClicked() {
+ mSearchResultClicked = false;
+ }
+
@Override
public void onSelectedContactsChanged() {
if (mCheckBoxListListener != null) {
@@ -77,6 +101,7 @@
if (mCheckBoxListListener != null) {
mCheckBoxListListener.onSelectedContactIdsChanged();
}
+ mSearchResultClicked = savedInstanceState.getBoolean(KEY_SEARCH_RESULT_CLICKED);
}
}
@@ -100,6 +125,7 @@
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putSerializable(EXTRA_KEY_SELECTED_CONTACTS, getSelectedContactIds());
+ outState.putBoolean(KEY_SEARCH_RESULT_CLICKED, mSearchResultClicked);
}
public void displayCheckBoxes(boolean displayCheckBoxes) {
@@ -157,6 +183,10 @@
getAdapter().toggleSelectionOfContactId(Long.valueOf(contactId));
}
} else {
+ mSearchResultClicked = true;
+ Logger.getInstance().logSearchEventImpl(
+ createSearchStateForSearchResultClick(position));
+
super.onItemClick(position, id);
}
if (mCheckBoxListListener != null && getAdapter().getSelectedContactIds().size() == 0) {
@@ -164,6 +194,73 @@
}
}
+ /**
+ * Returns the state of the search results currently presented to the user.
+ */
+ public SearchState createSearchState() {
+ return createSearchState(/* selectedPosition */ -1);
+ }
+
+ /**
+ * Returns the state of the search results presented to the user
+ * at the time the result in the given position was clicked.
+ */
+ public SearchState createSearchStateForSearchResultClick(int selectedPosition) {
+ return createSearchState(selectedPosition);
+ }
+
+ private SearchState createSearchState(int selectedPosition) {
+ final MultiSelectEntryContactListAdapter adapter = getAdapter();
+ if (adapter == null) {
+ return null;
+ }
+ final SearchState searchState = new SearchState();
+ searchState.queryLength = adapter.getQueryString() == null
+ ? 0 : adapter.getQueryString().length();
+ searchState.numPartitions = adapter.getPartitionCount();
+
+ // Set the number of results displayed to the user. Note that the adapter.getCount(),
+ // value does not always match the number of results actually displayed to the user,
+ // which is why we calculate it manually.
+ final List<Integer> numResultsInEachPartition = new ArrayList<>();
+ for (int i = 0; i < adapter.getPartitionCount(); i++) {
+ final Cursor cursor = adapter.getCursor(i);
+ if (cursor == null || cursor.isClosed()) {
+ // Something went wrong, abort.
+ numResultsInEachPartition.clear();
+ break;
+ }
+ numResultsInEachPartition.add(cursor.getCount());
+ }
+ if (!numResultsInEachPartition.isEmpty()) {
+ int numResults = 0;
+ for (int i = 0; i < numResultsInEachPartition.size(); i++) {
+ numResults += numResultsInEachPartition.get(i);
+ }
+ searchState.numResults = numResults;
+ }
+
+ // If a selection was made, set additional search state
+ if (selectedPosition >= 0) {
+ searchState.selectedPartition = adapter.getPartitionForPosition(selectedPosition);
+ searchState.selectedIndexInPartition = adapter.getOffsetInPartition(selectedPosition);
+ final Cursor cursor = adapter.getCursor(searchState.selectedPartition);
+ searchState.numResultsInSelectedPartition =
+ cursor == null || cursor.isClosed() ? -1 : cursor.getCount();
+
+ // Calculate the index across all partitions
+ if (!numResultsInEachPartition.isEmpty()) {
+ int selectedIndex = 0;
+ for (int i = 0; i < searchState.selectedPartition; i++) {
+ selectedIndex += numResultsInEachPartition.get(i);
+ }
+ selectedIndex += searchState.selectedIndexInPartition;
+ searchState.selectedIndex = selectedIndex;
+ }
+ }
+ return searchState;
+ }
+
@Override
protected ContactListAdapter createListAdapter() {
DefaultContactListAdapter adapter = new MultiSelectEntryContactListAdapter(getContext());