diff options
Diffstat (limited to 'java')
14 files changed, 338 insertions, 104 deletions
diff --git a/java/com/android/dialer/searchfragment/common/SearchCursor.java b/java/com/android/dialer/searchfragment/common/SearchCursor.java new file mode 100644 index 000000000..368ee09d6 --- /dev/null +++ b/java/com/android/dialer/searchfragment/common/SearchCursor.java @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2017 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.dialer.searchfragment.common; + +import android.database.Cursor; +import android.support.annotation.NonNull; + +/** Base cursor interface needed for all cursors used in search. */ +public interface SearchCursor extends Cursor { + + String[] HEADER_PROJECTION = {"header_text"}; + + int HEADER_TEXT_POSITION = 0; + + /** Returns true if the current cursor position is a header */ + boolean isHeader(); + + /** + * Notifies the cursor that the query has updated. + * + * @return true if the data set has changed. + */ + boolean updateQuery(@NonNull String query); +} diff --git a/java/com/android/dialer/searchfragment/cp2/AndroidManifest.xml b/java/com/android/dialer/searchfragment/cp2/AndroidManifest.xml new file mode 100644 index 000000000..8d2efca40 --- /dev/null +++ b/java/com/android/dialer/searchfragment/cp2/AndroidManifest.xml @@ -0,0 +1,16 @@ +<!-- + ~ Copyright (C) 2017 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 + --> +<manifest package="com.android.dialer.searchfragment.cp2"/>
\ No newline at end of file diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactCursor.java b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java index 05e98cc84..d5fcfba4f 100644 --- a/java/com/android/dialer/searchfragment/cp2/SearchContactCursor.java +++ b/java/com/android/dialer/searchfragment/cp2/ContactFilterCursor.java @@ -34,12 +34,12 @@ import java.util.ArrayList; import java.util.List; /** - * Wrapper for a cursor returned by {@link SearchContactsCursorLoader}. + * Wrapper for a cursor containing all on device contacts. * * <p>This cursor removes duplicate phone numbers associated with the same contact and can filter * contacts based on a query by calling {@link #filter(String)}. */ -public final class SearchContactCursor implements Cursor { +final class ContactFilterCursor implements Cursor { private final Cursor cursor; // List of cursor ids that are valid for displaying after filtering. @@ -66,7 +66,7 @@ public final class SearchContactCursor implements Cursor { * @param cursor with projection {@link Projections#PHONE_PROJECTION}. * @param query to filter cursor results. */ - public SearchContactCursor(Cursor cursor, @Nullable String query) { + ContactFilterCursor(Cursor cursor, @Nullable String query) { // TODO(calderwoodra) investigate copying this into a MatrixCursor and holding in memory this.cursor = cursor; filter(query); diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java index 4b5cab901..2bd9cdd8a 100644 --- a/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java +++ b/java/com/android/dialer/searchfragment/cp2/SearchContactViewHolder.java @@ -38,6 +38,7 @@ import com.android.dialer.lettertile.LetterTileDrawable; import com.android.dialer.searchfragment.common.Projections; import com.android.dialer.searchfragment.common.QueryBoldingUtil; import com.android.dialer.searchfragment.common.R; +import com.android.dialer.searchfragment.common.SearchCursor; import com.android.dialer.telecom.TelecomUtil; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -77,7 +78,7 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick * Binds the ViewHolder with a cursor from {@link SearchContactsCursorLoader} with the data found * at the cursors set position. */ - public void bind(Cursor cursor, String query) { + public void bind(SearchCursor cursor, String query) { number = cursor.getString(Projections.PHONE_NUMBER); String name = cursor.getString(Projections.PHONE_DISPLAY_NAME); String label = getLabel(context.getResources(), cursor); @@ -109,17 +110,18 @@ public final class SearchContactViewHolder extends ViewHolder implements OnClick } } - private boolean shouldShowPhoto(Cursor cursor) { + private boolean shouldShowPhoto(SearchCursor cursor) { int currentPosition = cursor.getPosition(); - if (currentPosition == 0) { - return true; - } else { - String currentLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY); - cursor.moveToPosition(currentPosition - 1); + String currentLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY); + cursor.moveToPosition(currentPosition - 1); + + if (!cursor.isHeader() && !cursor.isBeforeFirst()) { String previousLookupKey = cursor.getString(Projections.PHONE_LOOKUP_KEY); cursor.moveToPosition(currentPosition); return !currentLookupKey.equals(previousLookupKey); } + cursor.moveToPosition(currentPosition); + return true; } private static Uri getContactUri(Cursor cursor) { diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java new file mode 100644 index 000000000..18c9ecd7f --- /dev/null +++ b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursor.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 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.dialer.searchfragment.cp2; + +import android.content.Context; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.database.MergeCursor; +import android.support.annotation.Nullable; +import com.android.dialer.searchfragment.common.SearchCursor; + +/** + * {@link SearchCursor} implementation for displaying on device contacts. + * + * <p>Inserts header "All Contacts" at position 0. + */ +final class SearchContactsCursor extends MergeCursor implements SearchCursor { + + private final ContactFilterCursor contactFilterCursor; + + public static SearchContactsCursor newInstnace( + Context context, ContactFilterCursor contactFilterCursor) { + MatrixCursor headerCursor = new MatrixCursor(HEADER_PROJECTION); + headerCursor.addRow(new String[] {context.getString(R.string.all_contacts)}); + return new SearchContactsCursor(new Cursor[] {headerCursor, contactFilterCursor}); + } + + private SearchContactsCursor(Cursor[] cursors) { + super(cursors); + contactFilterCursor = (ContactFilterCursor) cursors[1]; + } + + @Override + public boolean isHeader() { + return isFirst(); + } + + @Override + public boolean updateQuery(@Nullable String query) { + contactFilterCursor.filter(query); + return true; + } + + @Override + public int getCount() { + // If we don't have any contents, we don't want to show the header + int count = contactFilterCursor.getCount(); + return count == 0 ? 0 : count + 1; + } +} diff --git a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java index c72f28b25..d75a66122 100644 --- a/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java +++ b/java/com/android/dialer/searchfragment/cp2/SearchContactsCursorLoader.java @@ -37,6 +37,11 @@ public final class SearchContactsCursorLoader extends CursorLoader { @Override public Cursor loadInBackground() { - return new SearchContactCursor(super.loadInBackground(), null); + // All contacts + Cursor cursor = super.loadInBackground(); + // Filtering logic + ContactFilterCursor contactFilterCursor = new ContactFilterCursor(cursor, null); + // Header logic + return SearchContactsCursor.newInstnace(getContext(), contactFilterCursor); } } diff --git a/java/com/android/dialer/searchfragment/cp2/res/values/strings.xml b/java/com/android/dialer/searchfragment/cp2/res/values/strings.xml new file mode 100644 index 000000000..5462dc926 --- /dev/null +++ b/java/com/android/dialer/searchfragment/cp2/res/values/strings.xml @@ -0,0 +1,20 @@ +<?xml version="1.0" encoding="utf-8"?> +<!-- + ~ Copyright (C) 2017 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 + --> +<resources> + <!-- Label for a list of all contacts on device. [CHAR LIMIT=30]--> + <string name="all_contacts">All contacts</string> +</resources>
\ No newline at end of file diff --git a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java index 70358bb16..1a489513c 100644 --- a/java/com/android/dialer/searchfragment/list/NewSearchFragment.java +++ b/java/com/android/dialer/searchfragment/list/NewSearchFragment.java @@ -34,8 +34,10 @@ import android.view.ViewGroup; import android.view.animation.Interpolator; import com.android.contacts.common.extensions.PhoneDirectoryExtenderAccessor; import com.android.dialer.animation.AnimUtils; +import com.android.dialer.common.Assert; import com.android.dialer.common.LogUtil; import com.android.dialer.common.concurrent.ThreadUtil; +import com.android.dialer.searchfragment.common.SearchCursor; import com.android.dialer.searchfragment.cp2.SearchContactsCursorLoader; import com.android.dialer.searchfragment.nearbyplaces.NearbyPlacesCursorLoader; import com.android.dialer.util.PermissionsUtil; @@ -72,7 +74,7 @@ public final class NewSearchFragment extends Fragment public View onCreateView( LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle bundle) { View view = inflater.inflate(R.layout.fragment_search, parent, false); - adapter = new SearchAdapter(getContext()); + adapter = new SearchAdapter(getContext(), new SearchCursorManager()); emptyContentView = view.findViewById(R.id.empty_view); recyclerView = view.findViewById(R.id.recycler_view); recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); @@ -113,10 +115,14 @@ public final class NewSearchFragment extends Fragment @Override public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { + if (!(cursor instanceof SearchCursor)) { + throw Assert.createIllegalStateFailException("Cursors must implement SearchCursor"); + } + if (loader instanceof SearchContactsCursorLoader) { - adapter.setContactsCursor(cursor); + adapter.setContactsCursor((SearchCursor) cursor); } else if (loader instanceof NearbyPlacesCursorLoader) { - adapter.setNearbyPlacesCursor(cursor); + adapter.setNearbyPlacesCursor((SearchCursor) cursor); } else { throw new IllegalStateException("Invalid loader: " + loader); } diff --git a/java/com/android/dialer/searchfragment/list/SearchAdapter.java b/java/com/android/dialer/searchfragment/list/SearchAdapter.java index faa80fe85..c8588fc7d 100644 --- a/java/com/android/dialer/searchfragment/list/SearchAdapter.java +++ b/java/com/android/dialer/searchfragment/list/SearchAdapter.java @@ -17,12 +17,12 @@ package com.android.dialer.searchfragment.list; import android.content.Context; -import android.database.Cursor; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.LayoutInflater; import android.view.ViewGroup; import com.android.dialer.common.Assert; +import com.android.dialer.searchfragment.common.SearchCursor; import com.android.dialer.searchfragment.cp2.SearchContactViewHolder; import com.android.dialer.searchfragment.list.SearchCursorManager.RowType; import com.android.dialer.searchfragment.nearbyplaces.NearbyPlaceViewHolder; @@ -35,9 +35,9 @@ class SearchAdapter extends RecyclerView.Adapter<ViewHolder> { private String query; - SearchAdapter(Context context) { - searchCursorManager = new SearchCursorManager(); + SearchAdapter(Context context, SearchCursorManager searchCursorManager) { this.context = context; + this.searchCursorManager = searchCursorManager; } @Override @@ -49,6 +49,7 @@ class SearchAdapter extends RecyclerView.Adapter<ViewHolder> { case RowType.NEARBY_PLACES_ROW: return new NearbyPlaceViewHolder( LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false)); + case RowType.CONTACT_HEADER: case RowType.DIRECTORY_HEADER: case RowType.NEARBY_PLACES_HEADER: return new HeaderViewHolder( @@ -68,20 +69,17 @@ class SearchAdapter extends RecyclerView.Adapter<ViewHolder> { @Override public void onBindViewHolder(ViewHolder holder, int position) { if (holder instanceof SearchContactViewHolder) { - Cursor cursor = searchCursorManager.getCursor(position); - ((SearchContactViewHolder) holder).bind(cursor, query); + ((SearchContactViewHolder) holder).bind(searchCursorManager.getCursor(position), query); } else if (holder instanceof NearbyPlaceViewHolder) { - Cursor cursor = searchCursorManager.getCursor(position); - ((NearbyPlaceViewHolder) holder).bind(cursor, query); + ((NearbyPlaceViewHolder) holder).bind(searchCursorManager.getCursor(position), query); } else if (holder instanceof HeaderViewHolder) { - String header = context.getString(searchCursorManager.getHeaderText(position)); - ((HeaderViewHolder) holder).setHeader(header); + ((HeaderViewHolder) holder).setHeader(searchCursorManager.getHeaderText(position)); } else { throw Assert.createIllegalStateFailException("Invalid ViewHolder: " + holder); } } - void setContactsCursor(Cursor cursor) { + void setContactsCursor(SearchCursor cursor) { searchCursorManager.setContactsCursor(cursor); notifyDataSetChanged(); } @@ -97,11 +95,12 @@ class SearchAdapter extends RecyclerView.Adapter<ViewHolder> { public void setQuery(String query) { this.query = query; - searchCursorManager.setQuery(query); - notifyDataSetChanged(); + if (searchCursorManager.setQuery(query)) { + notifyDataSetChanged(); + } } - public void setNearbyPlacesCursor(Cursor nearbyPlacesCursor) { + public void setNearbyPlacesCursor(SearchCursor nearbyPlacesCursor) { searchCursorManager.setNearbyPlacesCursor(nearbyPlacesCursor); notifyDataSetChanged(); } diff --git a/java/com/android/dialer/searchfragment/list/SearchCursorManager.java b/java/com/android/dialer/searchfragment/list/SearchCursorManager.java index 45d66aab8..68f770af9 100644 --- a/java/com/android/dialer/searchfragment/list/SearchCursorManager.java +++ b/java/com/android/dialer/searchfragment/list/SearchCursorManager.java @@ -16,11 +16,9 @@ package com.android.dialer.searchfragment.list; -import android.database.Cursor; import android.support.annotation.IntDef; -import android.support.annotation.StringRes; import com.android.dialer.common.Assert; -import com.android.dialer.searchfragment.cp2.SearchContactCursor; +import com.android.dialer.searchfragment.common.SearchCursor; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @@ -30,9 +28,9 @@ import java.lang.annotation.RetentionPolicy; * <p>This class accepts three cursors: * * <ul> - * <li>A contacts cursor {@link #setContactsCursor(Cursor)} - * <li>A google search results cursor {@link #setNearbyPlacesCursor(Cursor)} - * <li>A work directory cursor {@link #setCorpDirectoryCursor(Cursor)} + * <li>A contacts cursor {@link #setContactsCursor(SearchCursor)} + * <li>A google search results cursor {@link #setNearbyPlacesCursor(SearchCursor)} + * <li>A work directory cursor {@link #setCorpDirectoryCursor(SearchCursor)} * </ul> * * <p>The key purpose of this class is to compose three aforementioned cursors together to function @@ -50,6 +48,7 @@ final class SearchCursorManager { @Retention(RetentionPolicy.SOURCE) @IntDef({ SearchCursorManager.RowType.INVALID, + SearchCursorManager.RowType.CONTACT_HEADER, SearchCursorManager.RowType.CONTACT_ROW, SearchCursorManager.RowType.NEARBY_PLACES_HEADER, SearchCursorManager.RowType.NEARBY_PLACES_ROW, @@ -58,23 +57,25 @@ final class SearchCursorManager { }) @interface RowType { int INVALID = 0; + /** Header to mark the start of contact rows. */ + int CONTACT_HEADER = 1; /** A row containing contact information for contacts stored locally on device. */ - int CONTACT_ROW = 1; + int CONTACT_ROW = 2; /** Header to mark the end of contact rows and start of nearby places rows. */ - int NEARBY_PLACES_HEADER = 2; + int NEARBY_PLACES_HEADER = 3; /** A row containing nearby places information/search results. */ - int NEARBY_PLACES_ROW = 3; + int NEARBY_PLACES_ROW = 4; /** Header to mark the end of the previous row set and start of directory rows. */ - int DIRECTORY_HEADER = 4; + int DIRECTORY_HEADER = 5; /** A row containing contact information for contacts stored externally in corp directories. */ - int DIRECTORY_ROW = 5; + int DIRECTORY_ROW = 6; } - private Cursor contactsCursor = null; - private Cursor nearbyPlacesCursor = null; - private Cursor corpDirectoryCursor = null; + private SearchCursor contactsCursor = null; + private SearchCursor nearbyPlacesCursor = null; + private SearchCursor corpDirectoryCursor = null; - void setContactsCursor(Cursor cursor) { + void setContactsCursor(SearchCursor cursor) { if (cursor == contactsCursor) { return; } @@ -90,7 +91,7 @@ final class SearchCursorManager { } } - void setNearbyPlacesCursor(Cursor cursor) { + void setNearbyPlacesCursor(SearchCursor cursor) { if (cursor == nearbyPlacesCursor) { return; } @@ -106,7 +107,7 @@ final class SearchCursorManager { } } - void setCorpDirectoryCursor(Cursor cursor) { + void setCorpDirectoryCursor(SearchCursor cursor) { if (cursor == corpDirectoryCursor) { return; } @@ -122,14 +123,23 @@ final class SearchCursorManager { } } - void setQuery(String query) { + boolean setQuery(String query) { + boolean updated = false; if (contactsCursor != null) { - // TODO(calderwoodra): abstract this - ((SearchContactCursor) contactsCursor).filter(query); + updated = contactsCursor.updateQuery(query); } + + if (nearbyPlacesCursor != null) { + updated |= nearbyPlacesCursor.updateQuery(query); + } + + if (corpDirectoryCursor != null) { + updated |= corpDirectoryCursor.updateQuery(query); + } + return updated; } - /** @return the sum of counts of all cursors, including headers. */ + /** Returns the sum of counts of all cursors, including headers. */ int getCount() { int count = 0; if (contactsCursor != null) { @@ -137,12 +147,10 @@ final class SearchCursorManager { } if (nearbyPlacesCursor != null) { - count++; // header count += nearbyPlacesCursor.getCount(); } if (corpDirectoryCursor != null) { - count++; // header count += corpDirectoryCursor.getCount(); } @@ -151,54 +159,30 @@ final class SearchCursorManager { @RowType int getRowType(int position) { - if (contactsCursor != null) { - position -= contactsCursor.getCount(); - - if (position < 0) { - return SearchCursorManager.RowType.CONTACT_ROW; - } + SearchCursor cursor = getCursor(position); + if (cursor == contactsCursor) { + return cursor.isHeader() ? RowType.CONTACT_HEADER : RowType.CONTACT_ROW; } - if (nearbyPlacesCursor != null) { - if (position == 0) { - return SearchCursorManager.RowType.NEARBY_PLACES_HEADER; - } else { - position--; // header - } - - position -= nearbyPlacesCursor.getCount(); - - if (position < 0) { - return SearchCursorManager.RowType.NEARBY_PLACES_ROW; - } + if (cursor == nearbyPlacesCursor) { + return cursor.isHeader() ? RowType.NEARBY_PLACES_HEADER : RowType.NEARBY_PLACES_ROW; } - if (corpDirectoryCursor != null) { - if (position == 0) { - return SearchCursorManager.RowType.DIRECTORY_HEADER; - } else { - position--; // header - } - - position -= corpDirectoryCursor.getCount(); - - if (position < 0) { - return SearchCursorManager.RowType.DIRECTORY_ROW; - } + if (cursor == corpDirectoryCursor) { + return cursor.isHeader() ? RowType.DIRECTORY_HEADER : RowType.DIRECTORY_ROW; } - throw Assert.createIllegalStateFailException("No valid row type."); } /** - * Gets cursor corresponding to position in coelesced list of search cursors. Callers should + * Gets cursor corresponding to position in coalesced list of search cursors. Callers should * ensure that {@link #getRowType(int)} doesn't correspond to header position, otherwise an * exception will be thrown. * - * @param position in coalecsed list of search cursors + * @param position in coalesced list of search cursors * @return Cursor moved to position specific to passed in position. */ - Cursor getCursor(int position) { + SearchCursor getCursor(int position) { if (contactsCursor != null) { int count = contactsCursor.getCount(); @@ -210,8 +194,6 @@ final class SearchCursorManager { } if (nearbyPlacesCursor != null) { - Assert.checkArgument(position != 0, "No valid cursor, position is nearby places header."); - position--; // header int count = nearbyPlacesCursor.getCount(); if (position - count < 0) { @@ -222,8 +204,6 @@ final class SearchCursorManager { } if (corpDirectoryCursor != null) { - Assert.checkArgument(position != 0, "No valid cursor, position is directory search header."); - position--; // header int count = corpDirectoryCursor.getCount(); if (position - count < 0) { @@ -236,21 +216,8 @@ final class SearchCursorManager { throw Assert.createIllegalStateFailException("No valid cursor."); } - @StringRes - int getHeaderText(int position) { - @RowType int rowType = getRowType(position); - switch (rowType) { - case RowType.NEARBY_PLACES_HEADER: - return R.string.nearby_places; - case RowType.DIRECTORY_HEADER: // TODO(calderwoodra) - case RowType.DIRECTORY_ROW: - case RowType.CONTACT_ROW: - case RowType.NEARBY_PLACES_ROW: - case RowType.INVALID: - default: - throw Assert.createIllegalStateFailException( - "Invalid row type, position " + position + " is rowtype " + rowType); - } + String getHeaderText(int position) { + return getCursor(position).getString(SearchCursor.HEADER_TEXT_POSITION); } /** removes all cursors. */ diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/AndroidManifest.xml b/java/com/android/dialer/searchfragment/nearbyplaces/AndroidManifest.xml index 178cd83c3..52fb08671 100644 --- a/java/com/android/dialer/searchfragment/nearbyplaces/AndroidManifest.xml +++ b/java/com/android/dialer/searchfragment/nearbyplaces/AndroidManifest.xml @@ -13,4 +13,4 @@ ~ See the License for the specific language governing permissions and ~ limitations under the License --> -<manifest package="com.android.dialer.searchfragment.common"/>
\ No newline at end of file +<manifest package="com.android.dialer.searchfragment.nearbyplaces"/>
\ No newline at end of file diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java new file mode 100644 index 000000000..a4142a41d --- /dev/null +++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursor.java @@ -0,0 +1,64 @@ +/* + * Copyright (C) 2017 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.dialer.searchfragment.nearbyplaces; + +import android.content.Context; +import android.database.Cursor; +import android.database.MatrixCursor; +import android.database.MergeCursor; +import android.support.annotation.Nullable; +import com.android.dialer.searchfragment.common.SearchCursor; + +/** {@link SearchCursor} implementation for displaying on nearby places. */ +final class NearbyPlacesCursor extends MergeCursor implements SearchCursor { + + private final Cursor nearbyPlacesCursor; + + public static NearbyPlacesCursor newInstnace(Context context, Cursor nearbyPlacesCursor) { + MatrixCursor headerCursor = new MatrixCursor(HEADER_PROJECTION); + headerCursor.addRow(new String[] {context.getString(R.string.nearby_places)}); + return new NearbyPlacesCursor(new Cursor[] {headerCursor, nearbyPlacesCursor}); + } + + private NearbyPlacesCursor(Cursor[] cursors) { + super(cursors); + nearbyPlacesCursor = cursors[1]; + } + + @Override + public boolean isHeader() { + return isFirst(); + } + + @Override + public boolean updateQuery(@Nullable String query) { + // When the query changes, a new network request is made for nearby places. Meaning this cursor + // will be closed and another created, so return false. + return false; + } + + @Override + public int getCount() { + // If we don't have any contents, we don't want to show the header + if (nearbyPlacesCursor == null || nearbyPlacesCursor.isClosed()) { + return 0; + } + + int count = nearbyPlacesCursor.getCount(); + return count == 0 ? 0 : count + 1; + } +} diff --git a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java index 9f3193e92..6807a6e6b 100644 --- a/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java +++ b/java/com/android/dialer/searchfragment/nearbyplaces/NearbyPlacesCursorLoader.java @@ -18,6 +18,7 @@ package com.android.dialer.searchfragment.nearbyplaces; import android.content.Context; import android.content.CursorLoader; +import android.database.Cursor; import android.net.Uri; import android.provider.ContactsContract; import com.android.contacts.common.extensions.PhoneDirectoryExtenderAccessor; @@ -32,6 +33,11 @@ public final class NearbyPlacesCursorLoader extends CursorLoader { super(context, getContentUri(context, query), Projections.PHONE_PROJECTION, null, null, null); } + @Override + public Cursor loadInBackground() { + return NearbyPlacesCursor.newInstnace(getContext(), super.loadInBackground()); + } + private static Uri getContentUri(Context context, String query) { return PhoneDirectoryExtenderAccessor.get(context) .getContentUri() diff --git a/java/com/android/dialer/searchfragment/testing/TestSearchCursor.java b/java/com/android/dialer/searchfragment/testing/TestSearchCursor.java new file mode 100644 index 000000000..9a0b95789 --- /dev/null +++ b/java/com/android/dialer/searchfragment/testing/TestSearchCursor.java @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2017 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.dialer.searchfragment.testing; + +import android.database.Cursor; +import android.database.MatrixCursor; +import android.database.MergeCursor; +import android.support.annotation.Nullable; +import com.android.dialer.searchfragment.common.SearchCursor; + +/** {@link SearchCursor} implementation useful for testing with a header inserted at position 0. */ +public final class TestSearchCursor extends MergeCursor implements SearchCursor { + + public static TestSearchCursor newInstance(Cursor cursor, String header) { + MatrixCursor headerRow = new MatrixCursor(HEADER_PROJECTION); + headerRow.addRow(new String[] {header}); + return new TestSearchCursor(new Cursor[] {headerRow, cursor}); + } + + private TestSearchCursor(Cursor[] cursors) { + super(cursors); + } + + @Override + public boolean isHeader() { + return isFirst(); + } + + @Override + public boolean updateQuery(@Nullable String query) { + return false; + } +} |