diff options
Diffstat (limited to 'java/com/android/dialer/searchfragment')
12 files changed, 0 insertions, 1512 deletions
diff --git a/java/com/android/dialer/searchfragment/AndroidManifest.xml b/java/com/android/dialer/searchfragment/AndroidManifest.xml deleted file mode 100644 index 88ce67c13..000000000 --- a/java/com/android/dialer/searchfragment/AndroidManifest.xml +++ /dev/null @@ -1,16 +0,0 @@ -<!-- - ~ 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"/>
\ No newline at end of file diff --git a/java/com/android/dialer/searchfragment/NewSearchFragment.java b/java/com/android/dialer/searchfragment/NewSearchFragment.java deleted file mode 100644 index e7283d0ad..000000000 --- a/java/com/android/dialer/searchfragment/NewSearchFragment.java +++ /dev/null @@ -1,83 +0,0 @@ -/* - * 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; - -import android.app.Fragment; -import android.app.LoaderManager.LoaderCallbacks; -import android.content.Loader; -import android.database.Cursor; -import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v7.widget.LinearLayoutManager; -import android.support.v7.widget.RecyclerView; -import android.view.LayoutInflater; -import android.view.View; -import android.view.ViewGroup; - -/** Fragment used for searching contacts. */ -public final class NewSearchFragment extends Fragment implements LoaderCallbacks<Cursor> { - - private RecyclerView recyclerView; - private SearchAdapter adapter; - private String query; - - @Nullable - @Override - public View onCreateView( - LayoutInflater inflater, @Nullable ViewGroup parent, @Nullable Bundle bundle) { - getLoaderManager().initLoader(0, null, this); - View view = inflater.inflate(R.layout.fragment_search, parent, false); - recyclerView = view.findViewById(R.id.recycler_view); - recyclerView.setLayoutManager(new LinearLayoutManager(getContext())); - - getLoaderManager().initLoader(0, null, this); - return view; - } - - @Override - public Loader<Cursor> onCreateLoader(int id, Bundle bundle) { - // TODO add more loaders - return new SearchContactsCursorLoader(getContext()); - } - - @Override - public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) { - if (adapter == null) { - adapter = new SearchAdapter(getContext()); - } - if (loader instanceof SearchContactsCursorLoader) { - adapter.setContactsCursor(new SearchContactCursor(cursor, query)); - } - recyclerView.setAdapter(adapter); - } - - @Override - public void onLoaderReset(Loader<Cursor> loader) { - if (adapter != null) { - adapter.clear(); - adapter = null; - } - recyclerView.setAdapter(null); - } - - public void setQuery(String query) { - this.query = query; - if (adapter != null) { - adapter.setQuery(query); - } - } -} diff --git a/java/com/android/dialer/searchfragment/QueryUtil.java b/java/com/android/dialer/searchfragment/QueryUtil.java deleted file mode 100644 index a3f44ab83..000000000 --- a/java/com/android/dialer/searchfragment/QueryUtil.java +++ /dev/null @@ -1,269 +0,0 @@ -/* - * 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; - -import android.graphics.Typeface; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.telephony.PhoneNumberUtils; -import android.text.SpannableString; -import android.text.Spanned; -import android.text.TextUtils; -import android.text.style.StyleSpan; -import java.util.regex.Pattern; - -/** Contains utility methods for comparing and filtering strings with search queries. */ -final class QueryUtil { - - /** Matches strings with "-", "(", ")", 2-9 of at least length one. */ - static final Pattern T9_PATTERN = Pattern.compile("[\\-()2-9]+"); - - /** - * Compares a name and query and returns a {@link CharSequence} with bolded characters. - * - * <p>Some example: - * - * <ul> - * <li>"query" would bold "John [query] Smith" - * <li>"222" would bold "[AAA] Mom" - * <li>"222" would bold "[A]llen [A]lex [A]aron" - * </ul> - * - * @param query containing any characters - * @param name of a contact/string that query will compare to - * @return name with query bolded if query can be found in the name. - */ - static CharSequence getNameWithQueryBolded(@Nullable String query, @NonNull String name) { - if (TextUtils.isEmpty(query)) { - return name; - } - - int index = -1; - int numberOfBoldedCharacters = 0; - - if (nameMatchesT9Query(query, name)) { - // Bold the characters that match the t9 query - index = indexOfQueryNonDigitsIgnored(query, getT9Representation(name)); - if (index == -1) { - return getNameWithInitialsBolded(query, name); - } - numberOfBoldedCharacters = query.length(); - - for (int i = 0; i < query.length(); i++) { - char c = query.charAt(i); - if (!Character.isDigit(c)) { - numberOfBoldedCharacters--; - } - } - - for (int i = 0; i < index + numberOfBoldedCharacters; i++) { - if (!Character.isLetterOrDigit(name.charAt(i))) { - if (i < index) { - index++; - } else { - numberOfBoldedCharacters++; - } - } - } - } - - if (index == -1) { - // Bold the query as an exact match in the name - index = name.toLowerCase().indexOf(query); - numberOfBoldedCharacters = query.length(); - } - - return index == -1 ? name : getBoldedString(name, index, numberOfBoldedCharacters); - } - - private static CharSequence getNameWithInitialsBolded(String query, String name) { - SpannableString boldedInitials = new SpannableString(name); - name = name.toLowerCase(); - int initialsBolded = 0; - int nameIndex = -1; - - while (++nameIndex < name.length() && initialsBolded < query.length()) { - if ((nameIndex == 0 || name.charAt(nameIndex - 1) == ' ') - && getDigit(name.charAt(nameIndex)) == query.charAt(initialsBolded)) { - boldedInitials.setSpan( - new StyleSpan(Typeface.BOLD), - nameIndex, - nameIndex + 1, - Spanned.SPAN_INCLUSIVE_INCLUSIVE); - initialsBolded++; - } - } - return boldedInitials; - } - - /** - * Compares a number and a query and returns a {@link CharSequence} with bolded characters. - * - * <ul> - * <li>"123" would bold "(650)34[1-23]24" - * <li>"123" would bold "+1([123])111-2222 - * </ul> - * - * @param query containing only numbers and phone number related characters "(", ")", "-", "+" - * @param number phone number of a contact that the query will compare to. - * @return number with query bolded if query can be found in the number. - */ - static CharSequence getNumberWithQueryBolded(@Nullable String query, @NonNull String number) { - if (TextUtils.isEmpty(query) || !numberMatchesNumberQuery(query, number)) { - return number; - } - - int index = indexOfQueryNonDigitsIgnored(query, number); - int boldedCharacters = query.length(); - - for (char c : query.toCharArray()) { - if (!Character.isDigit(c)) { - boldedCharacters--; - } - } - - for (int i = 0; i < index + boldedCharacters; i++) { - if (!Character.isDigit(number.charAt(i))) { - if (i <= index) { - index++; - } else { - boldedCharacters++; - } - } - } - return getBoldedString(number, index, boldedCharacters); - } - - private static SpannableString getBoldedString(String s, int index, int numBolded) { - SpannableString span = new SpannableString(s); - span.setSpan( - new StyleSpan(Typeface.BOLD), index, index + numBolded, Spanned.SPAN_INCLUSIVE_EXCLUSIVE); - return span; - } - - /** - * @return true if the query is of T9 format and the name's T9 representation belongs to the - * query; false otherwise. - */ - static boolean nameMatchesT9Query(String query, String name) { - if (!T9_PATTERN.matcher(query).matches()) { - return false; - } - - // Substring - if (indexOfQueryNonDigitsIgnored(query, getT9Representation(name)) != -1) { - return true; - } - - // Check matches initials - // TODO investigate faster implementation - query = digitsOnly(query); - int queryIndex = 0; - - String[] names = name.toLowerCase().split("\\s"); - for (int i = 0; i < names.length && queryIndex < query.length(); i++) { - if (TextUtils.isEmpty(names[i])) { - continue; - } - - if (getDigit(names[i].charAt(0)) == query.charAt(queryIndex)) { - queryIndex++; - } - } - - return queryIndex == query.length(); - } - - /** @return true if the number belongs to the query. */ - static boolean numberMatchesNumberQuery(String query, String number) { - return PhoneNumberUtils.isGlobalPhoneNumber(query) - && indexOfQueryNonDigitsIgnored(query, number) != -1; - } - - /** - * Checks if query is contained in number while ignoring all characters in both that are not - * digits (i.e. {@link Character#isDigit(char)} returns false). - * - * @return index where query is found with all non-digits removed, -1 if it's not found. - */ - private static int indexOfQueryNonDigitsIgnored(@NonNull String query, @NonNull String number) { - return digitsOnly(number).indexOf(digitsOnly(query)); - } - - // Returns string with letters replaced with their T9 representation. - private static String getT9Representation(String s) { - StringBuilder builder = new StringBuilder(s.length()); - for (char c : s.toLowerCase().toCharArray()) { - builder.append(getDigit(c)); - } - return builder.toString(); - } - - /** @return String s with only digits recognized by Character#isDigit() remaining */ - static String digitsOnly(String s) { - StringBuilder sb = new StringBuilder(); - for (int i = 0; i < s.length(); i++) { - char c = s.charAt(i); - if (Character.isDigit(c)) { - sb.append(c); - } - } - return sb.toString(); - } - - // Returns the T9 representation of a lower case character, otherwise returns the character. - private static char getDigit(char c) { - switch (c) { - case 'a': - case 'b': - case 'c': - return '2'; - case 'd': - case 'e': - case 'f': - return '3'; - case 'g': - case 'h': - case 'i': - return '4'; - case 'j': - case 'k': - case 'l': - return '5'; - case 'm': - case 'n': - case 'o': - return '6'; - case 'p': - case 'q': - case 'r': - case 's': - return '7'; - case 't': - case 'u': - case 'v': - return '8'; - case 'w': - case 'x': - case 'y': - case 'z': - return '9'; - default: - return c; - } - } -} diff --git a/java/com/android/dialer/searchfragment/README.md b/java/com/android/dialer/searchfragment/README.md deleted file mode 100644 index b3b9135e5..000000000 --- a/java/com/android/dialer/searchfragment/README.md +++ /dev/null @@ -1,62 +0,0 @@ -# Dialer Search Ui - -searchfragment/ contains all code relevant to loading, rendering and filtering -search results in both dialpad search and regular search. - -## Loading - -### On Device Contacts - -On device contacts loading happens in SearchContactsCursorLoader. It is used in -conjunction with NewSearchFragment and Loader Callbacks to return a cursor from -cp2 containing all of the relevant info needed to rendering. - -### Business Search - -// TODO(calderwoodra) - -### Google Directory Search - -// TODO(calderwoodra) - -## Rendering - -NewSearchFragment, SearchAdapter, SearchContactViewHolder and -SearchCursorManager are used to render contact information. The fragment's -recyclerview, adapter and viewholder work as expected like a normal recyclerview -paradigm. - -The are three things to note about rendering: - -* There are three data sources rendered: On device contacts, business search - results and google directory results. -* SearchContactsCursorLoader returns its cursor from cp2 and we filter/wrap it - with SearchContactCursor to render useful results (see below). -* SearchCursorManager is used to coalesce all three data sources to help with - determining row count, row type and returning the correct data source for - each position. - -## Filtering - -On device contacts are filtered using SearchContactCursor. We wrap the cursor -returned from SearchContactsCursorLoader in NewSearchFragment#onLoadFinished in -order to abstract away the filtering logic from the recyclerview adapter and -viewholders. - -SearchContactCursor applies filtering in SearchContactCursor#filter to remove -duplicate phone numbers returned from cp2 and phone numbers that do not match -the given search query. - -Filtering methods used are: - -* T9/dialpad search methods - * Initial match (957 matches [W]illiam [J]ohn [S]mith) - * Number + name match (1800946 matches [1800-Win]-A-Prize) -* Numeric/dialpad search methods - * Simple number match (510333 matches [510-333]-7596) - * Country-code agnostic matching for E164 normalized numbers (9177 matches - +65[9177]6930) - * Country-code agnostic matching (510333 matches 1-[510-333]-7596) - * Area-code agnostic matching (333 matches 510-[333]-7596) -* Name/keyboard search methods: - * Simple name match (564 matches [Joh]n) diff --git a/java/com/android/dialer/searchfragment/SearchAdapter.java b/java/com/android/dialer/searchfragment/SearchAdapter.java deleted file mode 100644 index 8f5241557..000000000 --- a/java/com/android/dialer/searchfragment/SearchAdapter.java +++ /dev/null @@ -1,84 +0,0 @@ -/* - * 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; - -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.SearchCursorManager.RowType; - -/** RecyclerView adapter for {@link NewSearchFragment}. */ -class SearchAdapter extends RecyclerView.Adapter<ViewHolder> { - - private final SearchCursorManager searchCursorManager; - private final Context context; - - private String query; - - SearchAdapter(Context context) { - searchCursorManager = new SearchCursorManager(); - this.context = context; - } - - // TODO: fill in the rest of the view holders. - @Override - public ViewHolder onCreateViewHolder(ViewGroup root, int position) { - @RowType int rowType = searchCursorManager.getRowType(position); - switch (rowType) { - case RowType.CONTACT_ROW: - return new SearchContactViewHolder( - LayoutInflater.from(context).inflate(R.layout.search_contact_row, root, false)); - case RowType.DIRECTORY_HEADER: - case RowType.DIRECTORY_ROW: - case RowType.INVALID: - case RowType.NEARBY_PLACES_HEADER: - case RowType.NEARBY_PLACES_ROW: - return null; - default: - throw Assert.createIllegalStateFailException("Invalid RowType: " + rowType); - } - } - - @Override - public void onBindViewHolder(ViewHolder holder, int position) { - Cursor cursor = searchCursorManager.getCursor(position); - ((SearchContactViewHolder) holder).bind(cursor, query); - } - - void setContactsCursor(Cursor cursor) { - searchCursorManager.setContactsCursor(cursor); - } - - void clear() { - searchCursorManager.clear(); - } - - @Override - public int getItemCount() { - return searchCursorManager.getCount(); - } - - public void setQuery(String query) { - this.query = query; - searchCursorManager.setQuery(query); - notifyDataSetChanged(); - } -} diff --git a/java/com/android/dialer/searchfragment/SearchContactCursor.java b/java/com/android/dialer/searchfragment/SearchContactCursor.java deleted file mode 100644 index 5006e1a60..000000000 --- a/java/com/android/dialer/searchfragment/SearchContactCursor.java +++ /dev/null @@ -1,390 +0,0 @@ -/* - * 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; - -import android.content.ContentResolver; -import android.database.CharArrayBuffer; -import android.database.ContentObserver; -import android.database.Cursor; -import android.database.DataSetObserver; -import android.net.Uri; -import android.os.Bundle; -import android.support.annotation.IntDef; -import android.support.annotation.Nullable; -import android.text.TextUtils; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.util.ArrayList; -import java.util.List; - -/** - * Wrapper for a cursor returned by {@link SearchContactsCursorLoader}. - * - * <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)}. - */ -final class SearchContactCursor implements Cursor { - - private final Cursor cursor; - // List of cursor ids that are valid for displaying after filtering. - private final List<Integer> queryFilteredPositions = new ArrayList<>(); - - private int currentPosition = 0; - - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - Qualification.NUMBERS_ARE_NOT_DUPLICATES, - Qualification.NEW_NUMBER_IS_MORE_QUALIFIED, - Qualification.CURRENT_MORE_QUALIFIED - }) - private @interface Qualification { - /** Numbers are not duplicates (i.e. neither is more qualified than the other). */ - int NUMBERS_ARE_NOT_DUPLICATES = 0; - /** Number are duplicates and new number is more qualified than the existing number. */ - int NEW_NUMBER_IS_MORE_QUALIFIED = 1; - /** Numbers are duplicates but current/existing number is more qualified than new number. */ - int CURRENT_MORE_QUALIFIED = 2; - } - - /** - * @param cursor with projection {@link SearchContactsCursorLoader#PHONE_PROJECTION}. - * @param query to filter cursor results. - */ - SearchContactCursor(Cursor cursor, @Nullable String query) { - // TODO investigate copying this into a MatrixCursor and holding in memory - this.cursor = cursor; - filter(query); - } - - /** - * Filters out contacts that do not match the query. - * - * <p>The query can have at least 1 of 3 forms: - * - * <ul> - * <li>A phone number - * <li>A T9 representation of a name (matches {@link QueryUtil#T9_PATTERN}). - * <li>A name - * </ul> - * - * <p>A contact is considered a match if: - * - * <ul> - * <li>Its phone number contains the phone number query - * <li>Its name represented in T9 contains the T9 query - * <li>Its name contains the query - * </ul> - */ - void filter(@Nullable String query) { - if (query == null) { - query = ""; - } - queryFilteredPositions.clear(); - - // On some devices, contacts have multiple rows with identical phone numbers. These numbers are - // considered duplicates. Since the order might not be guaranteed, we compare all of the numbers - // and hold onto the most qualified one as the one we want to display to the user. - // See #getQualification for details on how qualification is determined. - int previousMostQualifiedPosition = 0; - String previousName = ""; - String previousMostQualifiedNumber = ""; - - query = query.toLowerCase(); - cursor.moveToPosition(-1); - - while (cursor.moveToNext()) { - int position = cursor.getPosition(); - String currentNumber = cursor.getString(SearchContactsCursorLoader.PHONE_NUMBER); - String currentName = cursor.getString(SearchContactsCursorLoader.PHONE_DISPLAY_NAME); - - if (!previousName.equals(currentName)) { - previousName = currentName; - previousMostQualifiedNumber = currentNumber; - previousMostQualifiedPosition = position; - } else { - // Since the contact name is the same, check if this number is a duplicate - switch (getQualification(currentNumber, previousMostQualifiedNumber)) { - case Qualification.CURRENT_MORE_QUALIFIED: - // Number is a less qualified duplicate, ignore it. - continue; - case Qualification.NEW_NUMBER_IS_MORE_QUALIFIED: - // If number wasn't filtered out before, remove it and add it's more qualified version. - if (queryFilteredPositions.contains(previousMostQualifiedPosition)) { - queryFilteredPositions.remove(previousMostQualifiedPosition); - queryFilteredPositions.add(position); - } - previousMostQualifiedNumber = currentNumber; - previousMostQualifiedPosition = position; - continue; - case Qualification.NUMBERS_ARE_NOT_DUPLICATES: - default: - previousMostQualifiedNumber = currentNumber; - previousMostQualifiedPosition = position; - } - } - - if (TextUtils.isEmpty(query) - || QueryUtil.nameMatchesT9Query(query, previousName) - || QueryUtil.numberMatchesNumberQuery(query, previousMostQualifiedNumber) - || previousName.contains(query)) { - queryFilteredPositions.add(previousMostQualifiedPosition); - } - } - currentPosition = 0; - cursor.moveToFirst(); - } - - /** - * @param number that may or may not be more qualified than the existing most qualified number - * @param mostQualifiedNumber currently most qualified number associated with same contact - * @return {@link Qualification} where the more qualified number is the number with the most - * digits. If the digits are the same, the number with the most formatting is more qualified. - */ - private @Qualification int getQualification(String number, String mostQualifiedNumber) { - // Ignore formatting - String numberDigits = QueryUtil.digitsOnly(number); - String qualifiedNumberDigits = QueryUtil.digitsOnly(mostQualifiedNumber); - - // If the numbers are identical, return version with more formatting - if (qualifiedNumberDigits.equals(numberDigits)) { - if (mostQualifiedNumber.length() >= number.length()) { - return Qualification.CURRENT_MORE_QUALIFIED; - } else { - return Qualification.NEW_NUMBER_IS_MORE_QUALIFIED; - } - } - - // If one number is a suffix of another, then return the longer one. - // If they are equal, then return the current most qualified number. - if (qualifiedNumberDigits.endsWith(numberDigits)) { - return Qualification.CURRENT_MORE_QUALIFIED; - } - if (numberDigits.endsWith(qualifiedNumberDigits)) { - return Qualification.NEW_NUMBER_IS_MORE_QUALIFIED; - } - return Qualification.NUMBERS_ARE_NOT_DUPLICATES; - } - - @Override - public boolean moveToPosition(int position) { - currentPosition = position; - return currentPosition < getCount() - && cursor.moveToPosition(queryFilteredPositions.get(currentPosition)); - } - - @Override - public boolean move(int offset) { - currentPosition += offset; - return moveToPosition(currentPosition); - } - - @Override - public int getCount() { - return queryFilteredPositions.size(); - } - - @Override - public boolean isFirst() { - return currentPosition == 0; - } - - @Override - public boolean isLast() { - return currentPosition == getCount() - 1; - } - - @Override - public int getPosition() { - return currentPosition; - } - - @Override - public boolean moveToFirst() { - return moveToPosition(0); - } - - @Override - public boolean moveToLast() { - return moveToPosition(getCount() - 1); - } - - @Override - public boolean moveToNext() { - return moveToPosition(++currentPosition); - } - - @Override - public boolean moveToPrevious() { - return moveToPosition(--currentPosition); - } - - // Methods below simply call the corresponding method in cursor. - @Override - public boolean isBeforeFirst() { - return cursor.isBeforeFirst(); - } - - @Override - public boolean isAfterLast() { - return cursor.isAfterLast(); - } - - @Override - public int getColumnIndex(String columnName) { - return cursor.getColumnIndex(columnName); - } - - @Override - public int getColumnIndexOrThrow(String columnName) { - return cursor.getColumnIndexOrThrow(columnName); - } - - @Override - public String getColumnName(int columnIndex) { - return cursor.getColumnName(columnIndex); - } - - @Override - public String[] getColumnNames() { - return cursor.getColumnNames(); - } - - @Override - public int getColumnCount() { - return cursor.getColumnCount(); - } - - @Override - public byte[] getBlob(int columnIndex) { - return cursor.getBlob(columnIndex); - } - - @Override - public String getString(int columnIndex) { - return cursor.getString(columnIndex); - } - - @Override - public void copyStringToBuffer(int columnIndex, CharArrayBuffer buffer) { - cursor.copyStringToBuffer(columnIndex, buffer); - } - - @Override - public short getShort(int columnIndex) { - return cursor.getShort(columnIndex); - } - - @Override - public int getInt(int columnIndex) { - return cursor.getInt(columnIndex); - } - - @Override - public long getLong(int columnIndex) { - return cursor.getLong(columnIndex); - } - - @Override - public float getFloat(int columnIndex) { - return cursor.getFloat(columnIndex); - } - - @Override - public double getDouble(int columnIndex) { - return cursor.getDouble(columnIndex); - } - - @Override - public int getType(int columnIndex) { - return cursor.getType(columnIndex); - } - - @Override - public boolean isNull(int columnIndex) { - return cursor.isNull(columnIndex); - } - - @Override - public void deactivate() { - cursor.deactivate(); - } - - @Override - public boolean requery() { - return cursor.requery(); - } - - @Override - public void close() { - cursor.close(); - } - - @Override - public boolean isClosed() { - return cursor.isClosed(); - } - - @Override - public void registerContentObserver(ContentObserver observer) { - cursor.registerContentObserver(observer); - } - - @Override - public void unregisterContentObserver(ContentObserver observer) { - cursor.unregisterContentObserver(observer); - } - - @Override - public void registerDataSetObserver(DataSetObserver observer) { - cursor.registerDataSetObserver(observer); - } - - @Override - public void unregisterDataSetObserver(DataSetObserver observer) { - cursor.unregisterDataSetObserver(observer); - } - - @Override - public void setNotificationUri(ContentResolver cr, Uri uri) { - cursor.setNotificationUri(cr, uri); - } - - @Override - public Uri getNotificationUri() { - return cursor.getNotificationUri(); - } - - @Override - public boolean getWantsAllOnMoveCalls() { - return cursor.getWantsAllOnMoveCalls(); - } - - @Override - public void setExtras(Bundle extras) { - cursor.setExtras(extras); - } - - @Override - public Bundle getExtras() { - return cursor.getExtras(); - } - - @Override - public Bundle respond(Bundle extras) { - return cursor.respond(extras); - } -} diff --git a/java/com/android/dialer/searchfragment/SearchContactViewHolder.java b/java/com/android/dialer/searchfragment/SearchContactViewHolder.java deleted file mode 100644 index 4ac6af33e..000000000 --- a/java/com/android/dialer/searchfragment/SearchContactViewHolder.java +++ /dev/null @@ -1,203 +0,0 @@ -/* - * 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; - -import android.content.Context; -import android.content.res.Resources; -import android.database.Cursor; -import android.net.Uri; -import android.provider.ContactsContract.CommonDataKinds.Phone; -import android.provider.ContactsContract.Contacts; -import android.support.annotation.IntDef; -import android.support.v7.widget.RecyclerView.ViewHolder; -import android.text.TextUtils; -import android.view.View; -import android.view.View.OnClickListener; -import android.widget.ImageView; -import android.widget.QuickContactBadge; -import android.widget.TextView; -import com.android.contacts.common.ContactPhotoManager; -import com.android.contacts.common.lettertiles.LetterTileDrawable; -import com.android.dialer.callintent.CallInitiationType.Type; -import com.android.dialer.callintent.CallIntentBuilder; -import com.android.dialer.common.Assert; -import com.android.dialer.telecom.TelecomUtil; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** ViewHolder for a contact row in {@link NewSearchFragment}. */ -final class SearchContactViewHolder extends ViewHolder implements OnClickListener { - - /** IntDef for the different types of actions that can be shown. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - SearchContactViewHolder.CallToAction.NONE, - SearchContactViewHolder.CallToAction.VIDEO_CALL, - SearchContactViewHolder.CallToAction.SHARE_AND_CALL - }) - @interface CallToAction { - int NONE = 0; - int VIDEO_CALL = 1; - int SHARE_AND_CALL = 2; - } - - private final QuickContactBadge photo; - private final TextView nameOrNumberView; - private final TextView numberView; - private final ImageView callToActionView; - private final Context context; - - private String number; - private @CallToAction int currentAction; - - SearchContactViewHolder(View view) { - super(view); - view.setOnClickListener(this); - photo = view.findViewById(R.id.photo); - nameOrNumberView = view.findViewById(R.id.primary); - numberView = view.findViewById(R.id.secondary); - callToActionView = view.findViewById(R.id.call_to_action); - context = view.getContext(); - } - - /** - * Binds the ViewHolder with a cursor from {@link SearchContactsCursorLoader} with the data found - * at the cursors set position. - */ - void bind(Cursor cursor, String query) { - number = cursor.getString(SearchContactsCursorLoader.PHONE_NUMBER); - String name = cursor.getString(SearchContactsCursorLoader.PHONE_DISPLAY_NAME); - String label = getLabel(context.getResources(), cursor); - String secondaryInfo = - TextUtils.isEmpty(label) - ? number - : context.getString(R.string.call_subject_type_and_number, label, number); - - nameOrNumberView.setText(QueryUtil.getNameWithQueryBolded(query, name)); - numberView.setText(QueryUtil.getNumberWithQueryBolded(query, secondaryInfo)); - setCallToAction(cursor); - - if (shouldShowPhoto(cursor, name)) { - nameOrNumberView.setVisibility(View.VISIBLE); - photo.setVisibility(View.VISIBLE); - photo.setContentDescription(context.getString(R.string.description_quick_contact_for, name)); - String photoUri = cursor.getString(SearchContactsCursorLoader.PHONE_PHOTO_URI); - ContactPhotoManager.getInstance(context) - .loadDialerThumbnailOrPhoto( - photo, - getContactUri(cursor), - cursor.getLong(SearchContactsCursorLoader.PHONE_PHOTO_ID), - photoUri == null ? null : Uri.parse(photoUri), - name, - LetterTileDrawable.TYPE_DEFAULT); - } else { - nameOrNumberView.setVisibility(View.GONE); - photo.setVisibility(View.INVISIBLE); - } - } - - private boolean shouldShowPhoto(Cursor cursor, String currentName) { - int currentPosition = cursor.getPosition(); - if (currentPosition == 0) { - return true; - } else { - cursor.moveToPosition(currentPosition - 1); - String previousName = cursor.getString(SearchContactsCursorLoader.PHONE_DISPLAY_NAME); - cursor.moveToPosition(currentPosition); - return !currentName.equals(previousName); - } - } - - private static Uri getContactUri(Cursor cursor) { - long contactId = cursor.getLong(SearchContactsCursorLoader.PHONE_ID); - String lookupKey = cursor.getString(SearchContactsCursorLoader.PHONE_LOOKUP_KEY); - return Contacts.getLookupUri(contactId, lookupKey); - } - - // TODO: handle CNAP and cequint types. - // TODO: unify this into a utility method with CallLogAdapter#getNumberType - private static String getLabel(Resources resources, Cursor cursor) { - int numberType = cursor.getInt(SearchContactsCursorLoader.PHONE_TYPE); - String numberLabel = cursor.getString(SearchContactsCursorLoader.PHONE_LABEL); - - // Returns empty label instead of "custom" if the custom label is empty. - if (numberType == Phone.TYPE_CUSTOM && TextUtils.isEmpty(numberLabel)) { - return ""; - } - return (String) Phone.getTypeLabel(resources, numberType, numberLabel); - } - - private void setCallToAction(Cursor cursor) { - currentAction = getCallToAction(cursor); - switch (currentAction) { - case CallToAction.NONE: - callToActionView.setVisibility(View.GONE); - callToActionView.setOnClickListener(null); - break; - case CallToAction.SHARE_AND_CALL: - callToActionView.setVisibility(View.VISIBLE); - callToActionView.setImageDrawable(context.getDrawable(R.drawable.ic_phone_attach)); - callToActionView.setOnClickListener(this); - break; - case CallToAction.VIDEO_CALL: - callToActionView.setVisibility(View.VISIBLE); - callToActionView.setImageDrawable( - context.getDrawable(R.drawable.quantum_ic_videocam_white_24)); - callToActionView.setOnClickListener(this); - break; - default: - throw Assert.createIllegalStateFailException( - "Invalid Call to action type: " + currentAction); - } - } - - private static @CallToAction int getCallToAction(Cursor cursor) { - int carrierPresence = cursor.getInt(SearchContactsCursorLoader.PHONE_CARRIER_PRESENCE); - if ((carrierPresence & Phone.CARRIER_PRESENCE_VT_CAPABLE) == 1) { - return CallToAction.VIDEO_CALL; - } - - // TODO: enriched calling - return CallToAction.NONE; - } - - @Override - public void onClick(View view) { - if (view == callToActionView) { - switch (currentAction) { - case CallToAction.SHARE_AND_CALL: - callToActionView.setVisibility(View.VISIBLE); - callToActionView.setImageDrawable(context.getDrawable(R.drawable.ic_phone_attach)); - // TODO: open call composer. - break; - case CallToAction.VIDEO_CALL: - callToActionView.setVisibility(View.VISIBLE); - callToActionView.setImageDrawable( - context.getDrawable(R.drawable.quantum_ic_videocam_white_24)); - // TODO: place a video call - break; - case CallToAction.NONE: - default: - throw Assert.createIllegalStateFailException( - "Invalid Call to action type: " + currentAction); - } - } else { - // TODO: set the correct call initiation type. - TelecomUtil.placeCall(context, new CallIntentBuilder(number, Type.REGULAR_SEARCH).build()); - } - } -} diff --git a/java/com/android/dialer/searchfragment/SearchContactsCursorLoader.java b/java/com/android/dialer/searchfragment/SearchContactsCursorLoader.java deleted file mode 100644 index 309dfffd9..000000000 --- a/java/com/android/dialer/searchfragment/SearchContactsCursorLoader.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * 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; - -import android.content.Context; -import android.content.CursorLoader; -import android.provider.ContactsContract.CommonDataKinds.Phone; - -/** Cursor Loader for {@link NewSearchFragment}. */ -final class SearchContactsCursorLoader extends CursorLoader { - - public static final int PHONE_ID = 0; - public static final int PHONE_TYPE = 1; - public static final int PHONE_LABEL = 2; - public static final int PHONE_NUMBER = 3; - public static final int PHONE_DISPLAY_NAME = 4; - public static final int PHONE_PHOTO_ID = 5; - public static final int PHONE_PHOTO_URI = 6; - public static final int PHONE_LOOKUP_KEY = 7; - public static final int PHONE_CARRIER_PRESENCE = 8; - - @SuppressWarnings("unused") - public static final int PHONE_SOFT_KEY = 9; - - static final String[] PHONE_PROJECTION = - new String[] { - Phone._ID, // 0 - Phone.TYPE, // 1 - Phone.LABEL, // 2 - Phone.NUMBER, // 3 - Phone.DISPLAY_NAME_PRIMARY, // 4 - Phone.PHOTO_ID, // 5 - Phone.PHOTO_THUMBNAIL_URI, // 6 - Phone.LOOKUP_KEY, // 7 - Phone.CARRIER_PRESENCE, // 8 - Phone.SORT_KEY_PRIMARY // 9 - }; - - SearchContactsCursorLoader(Context context) { - super( - context, Phone.CONTENT_URI, PHONE_PROJECTION, null, null, Phone.SORT_KEY_PRIMARY + " ASC"); - } -} diff --git a/java/com/android/dialer/searchfragment/SearchCursorManager.java b/java/com/android/dialer/searchfragment/SearchCursorManager.java deleted file mode 100644 index 64c4fc170..000000000 --- a/java/com/android/dialer/searchfragment/SearchCursorManager.java +++ /dev/null @@ -1,229 +0,0 @@ -/* - * 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; - -import android.database.Cursor; -import android.support.annotation.IntDef; -import com.android.dialer.common.Assert; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; - -/** - * Manages all of the cursors needed for {@link SearchAdapter}. - * - * <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)} - * </ul> - * - * <p>The key purpose of this class is to compose three aforementioned cursors together to function - * as one cursor. The key methods needed to utilize this class as a cursor are: - * - * <ul> - * <li>{@link #getCursor(int)} - * <li>{@link #getCount()} - * <li>{@link #getRowType(int)} - * </ul> - */ -final class SearchCursorManager { - - /** IntDef for the different types of rows that can be shown when searching. */ - @Retention(RetentionPolicy.SOURCE) - @IntDef({ - SearchCursorManager.RowType.INVALID, - SearchCursorManager.RowType.CONTACT_ROW, - SearchCursorManager.RowType.NEARBY_PLACES_HEADER, - SearchCursorManager.RowType.NEARBY_PLACES_ROW, - SearchCursorManager.RowType.DIRECTORY_HEADER, - SearchCursorManager.RowType.DIRECTORY_ROW - }) - @interface RowType { - int INVALID = 0; - /** A row containing contact information for contacts stored locally on device. */ - int CONTACT_ROW = 1; - /** Header to mark the end of contact rows and start of nearby places rows. */ - int NEARBY_PLACES_HEADER = 2; - /** A row containing nearby places information/search results. */ - int NEARBY_PLACES_ROW = 3; - /** Header to mark the end of the previous row set and start of directory rows. */ - int DIRECTORY_HEADER = 4; - /** A row containing contact information for contacts stored externally in corp directories. */ - int DIRECTORY_ROW = 5; - } - - private Cursor contactsCursor = null; - private Cursor nearbyPlacesCursor = null; - private Cursor corpDirectoryCursor = null; - - void setContactsCursor(Cursor cursor) { - if (cursor != null && cursor.getCount() > 0) { - contactsCursor = cursor; - } else { - contactsCursor = null; - } - } - - void setNearbyPlacesCursor(Cursor cursor) { - if (cursor != null && cursor.getCount() > 0) { - nearbyPlacesCursor = cursor; - } else { - nearbyPlacesCursor = null; - } - } - - void setCorpDirectoryCursor(Cursor cursor) { - if (cursor != null && cursor.getCount() > 0) { - corpDirectoryCursor = cursor; - } else { - corpDirectoryCursor = null; - } - } - - void setQuery(String query) { - if (contactsCursor != null) { - ((SearchContactCursor) contactsCursor).filter(query); - } - } - - /** @return the sum of counts of all cursors, including headers. */ - int getCount() { - int count = 0; - if (contactsCursor != null) { - count += contactsCursor.getCount(); - } - - if (nearbyPlacesCursor != null) { - count++; // header - count += nearbyPlacesCursor.getCount(); - } - - if (corpDirectoryCursor != null) { - count++; // header - count += corpDirectoryCursor.getCount(); - } - - return count; - } - - @RowType - int getRowType(int position) { - if (contactsCursor != null) { - position -= contactsCursor.getCount(); - - if (position < 0) { - return SearchCursorManager.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 (corpDirectoryCursor != null) { - if (position == 0) { - return SearchCursorManager.RowType.DIRECTORY_HEADER; - } else { - position--; // header - } - - position -= corpDirectoryCursor.getCount(); - - if (position < 0) { - return SearchCursorManager.RowType.DIRECTORY_ROW; - } - } - - throw Assert.createIllegalStateFailException("No valid row type."); - } - - /** - * Gets cursor corresponding to position in coelesced 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 - * @return Cursor moved to position specific to passed in position. - */ - Cursor getCursor(int position) { - if (contactsCursor != null) { - int count = contactsCursor.getCount(); - - if (position - count < 0) { - contactsCursor.moveToPosition(position); - return contactsCursor; - } - position -= count; - } - - 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) { - nearbyPlacesCursor.moveToPosition(position); - return nearbyPlacesCursor; - } - position -= count; - } - - 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) { - corpDirectoryCursor.moveToPosition(position); - return corpDirectoryCursor; - } - position -= count; - } - - throw Assert.createIllegalStateFailException("No valid cursor."); - } - - /** removes all cursors. */ - void clear() { - if (contactsCursor != null) { - contactsCursor.close(); - contactsCursor = null; - } - - if (nearbyPlacesCursor != null) { - nearbyPlacesCursor.close(); - nearbyPlacesCursor = null; - } - - if (corpDirectoryCursor != null) { - corpDirectoryCursor.close(); - corpDirectoryCursor = null; - } - } -} diff --git a/java/com/android/dialer/searchfragment/res/layout/fragment_search.xml b/java/com/android/dialer/searchfragment/res/layout/fragment_search.xml deleted file mode 100644 index 06f234889..000000000 --- a/java/com/android/dialer/searchfragment/res/layout/fragment_search.xml +++ /dev/null @@ -1,21 +0,0 @@ -<?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 - --> -<android.support.v7.widget.RecyclerView - xmlns:android="http://schemas.android.com/apk/res/android" - android:id="@+id/recycler_view" - android:layout_width="match_parent" - android:layout_height="match_parent"/> diff --git a/java/com/android/dialer/searchfragment/res/layout/search_contact_row.xml b/java/com/android/dialer/searchfragment/res/layout/search_contact_row.xml deleted file mode 100644 index efeca0e9d..000000000 --- a/java/com/android/dialer/searchfragment/res/layout/search_contact_row.xml +++ /dev/null @@ -1,75 +0,0 @@ -<?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 - --> -<RelativeLayout - xmlns:android="http://schemas.android.com/apk/res/android" - android:orientation="horizontal" - android:layout_width="match_parent" - android:layout_height="@dimen/search_row_height" - android:paddingStart="16dp" - android:paddingEnd="16dp" - android:background="?android:attr/selectableItemBackground"> - - <QuickContactBadge - android:id="@+id/photo" - android:layout_width="@dimen/search_row_height" - android:layout_height="@dimen/search_row_height" - android:padding="@dimen/search_photo_padding" - android:clickable="false"/> - - <LinearLayout - android:orientation="vertical" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:layout_toEndOf="@+id/photo" - android:layout_toStartOf="@+id/call_to_action" - android:layout_centerVertical="true"> - - <TextView - android:id="@+id/primary" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="@dimen/search_text_padding_start" - android:gravity="center_vertical|start" - android:textSize="@dimen/new_search_text_size" - android:textColor="@color/dialer_primary_text_color" - android:maxLines="1" - android:ellipsize="end" - android:fontFamily="sans-serif"/> - - <TextView - android:id="@+id/secondary" - android:layout_width="wrap_content" - android:layout_height="wrap_content" - android:paddingStart="@dimen/search_text_padding_start" - android:gravity="center_vertical|start" - android:textSize="@dimen/new_search_text_size" - android:textColor="@color/dialer_primary_text_color" - android:maxLines="1" - android:ellipsize="end" - android:fontFamily="sans-serif"/> - </LinearLayout> - - <ImageView - android:id="@+id/call_to_action" - android:layout_width="@dimen/search_row_height" - android:layout_height="@dimen/search_row_height" - android:layout_alignParentEnd="true" - android:padding="@dimen/call_to_action_padding" - android:tint="@color/secondary_text_color" - android:visibility="gone" - android:scaleType="center"/> -</RelativeLayout>
\ No newline at end of file diff --git a/java/com/android/dialer/searchfragment/res/values/dimens.xml b/java/com/android/dialer/searchfragment/res/values/dimens.xml deleted file mode 100644 index d5459ddb3..000000000 --- a/java/com/android/dialer/searchfragment/res/values/dimens.xml +++ /dev/null @@ -1,23 +0,0 @@ -<?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> - <dimen name="search_row_height">56dp</dimen> - <dimen name="search_photo_padding">8dp</dimen> - <dimen name="call_to_action_padding">8dp</dimen> - <dimen name="search_text_padding_start">16dp</dimen> - <dimen name="new_search_text_size">16sp</dimen> -</resources>
\ No newline at end of file |