Messaging: Looply search hub with frequent contacts and nav inset
Redesign Search around frequent contacts, category counts, and recent chips, and keep the Messages/Automation pill on the left guideline without moving compose. Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
+19
@@ -138,6 +138,7 @@ public class ConversationListFragment extends Fragment implements ConversationLi
|
||||
private TextView mHomeEditButton;
|
||||
private ImageButton mHomePinnedDoneButton;
|
||||
private boolean mHomePinnedEditMode;
|
||||
private View mHomeNavDock;
|
||||
private View mHomeNavMessages;
|
||||
private View mHomeNavAutomate;
|
||||
private View mHomeNavTabGap;
|
||||
@@ -389,6 +390,7 @@ public class ConversationListFragment extends Fragment implements ConversationLi
|
||||
mHomeMessagesTop = rootView.findViewById(R.id.home_messages_top);
|
||||
mHomeTitle = rootView.findViewById(R.id.home_title);
|
||||
mHomeAutomationPane = rootView.findViewById(R.id.home_automation_pane);
|
||||
mHomeNavDock = rootView.findViewById(R.id.home_nav_dock);
|
||||
mHomeNavMessages = rootView.findViewById(R.id.home_nav_messages);
|
||||
bindHomeNavTab(mHomeNavMessages, R.drawable.ic_home_nav_messages,
|
||||
R.string.home_nav_messages);
|
||||
@@ -453,6 +455,23 @@ public class ConversationListFragment extends Fragment implements ConversationLi
|
||||
}
|
||||
applyHomeNavTabActiveSize(mHomeNavMessages, mHomeNavMessagesNaturalWidth);
|
||||
applyHomeNavTabActiveSize(mHomeNavAutomate, mHomeNavAutomateNaturalWidth);
|
||||
if (mHomeNavDock != null) {
|
||||
mHomeNavDock.post(this::alignHomeNavDockToGuideline);
|
||||
}
|
||||
}
|
||||
|
||||
/** Slide only the Messages/Automation pill to the 20dp screen guideline; compose stays put. */
|
||||
private void alignHomeNavDockToGuideline() {
|
||||
if (mHomeNavDock == null || mRootView == null || mHomeNavDock.getWidth() <= 0) {
|
||||
return;
|
||||
}
|
||||
final int guideline = getResources().getDimensionPixelSize(R.dimen.looply_screen_padding);
|
||||
mHomeNavDock.setTranslationX(0f);
|
||||
final int[] dockLoc = new int[2];
|
||||
final int[] rootLoc = new int[2];
|
||||
mHomeNavDock.getLocationOnScreen(dockLoc);
|
||||
mRootView.getLocationOnScreen(rootLoc);
|
||||
mHomeNavDock.setTranslationX(guideline - (dockLoc[0] - rootLoc[0]));
|
||||
}
|
||||
|
||||
private void applyHomeNavTabActiveSize(final View tab, final int natural) {
|
||||
|
||||
@@ -16,29 +16,58 @@
|
||||
|
||||
package com.android.messaging.ui.home;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.content.res.ColorStateList;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.speech.RecognizerIntent;
|
||||
import android.text.Editable;
|
||||
import android.text.TextUtils;
|
||||
import android.text.TextWatcher;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.IdRes;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.core.content.ContextCompat;
|
||||
|
||||
import com.android.messaging.R;
|
||||
import com.android.messaging.datamodel.DatabaseHelper.ConversationColumns;
|
||||
import com.android.messaging.datamodel.MessagingContentProvider;
|
||||
import com.android.messaging.datamodel.data.ConversationListItemData;
|
||||
import com.android.messaging.ui.BugleActionBarActivity;
|
||||
import com.android.messaging.ui.ContactIconView;
|
||||
import com.android.messaging.ui.UIIntents;
|
||||
import com.android.messaging.util.AvatarUriUtil;
|
||||
import com.android.messaging.util.ImeUtil;
|
||||
import com.android.messaging.util.UriUtil;
|
||||
import com.google.android.material.chip.Chip;
|
||||
import com.google.android.material.chip.ChipGroup;
|
||||
|
||||
/** Home-style message search entry point with quick filter tiles. */
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/** Home-style message search hub with frequent contacts, categories, and recent queries. */
|
||||
public class HomeSearchActivity extends BugleActionBarActivity {
|
||||
|
||||
private static final int FREQUENT_LIMIT = 4;
|
||||
private static final int VOICE_SEARCH_REQUEST = 71;
|
||||
|
||||
private EditText mSearchField;
|
||||
private ImageButton mClearButton;
|
||||
private ImageButton mVoiceButton;
|
||||
private LinearLayout mFrequentSection;
|
||||
private LinearLayout mFrequentRow;
|
||||
private View mRecentSection;
|
||||
private ChipGroup mRecentGroup;
|
||||
|
||||
@Override
|
||||
protected void onCreate(final Bundle savedInstanceState) {
|
||||
@@ -49,12 +78,40 @@ public class HomeSearchActivity extends BugleActionBarActivity {
|
||||
}
|
||||
|
||||
HomeSubHeader.bind(this, R.string.home_search_title, v -> finish());
|
||||
HomeSubHeader.bindAction(this, R.string.home_search_reset, v -> resetSearch());
|
||||
|
||||
mSearchField = findViewById(R.id.home_search_field);
|
||||
mClearButton = findViewById(R.id.home_search_clear);
|
||||
mVoiceButton = findViewById(R.id.home_search_voice);
|
||||
mFrequentSection = findViewById(R.id.home_search_frequent_section);
|
||||
mFrequentRow = findViewById(R.id.home_search_frequent_row);
|
||||
mRecentSection = findViewById(R.id.home_search_recent_section);
|
||||
mRecentGroup = findViewById(R.id.home_search_recent_group);
|
||||
|
||||
if (mClearButton != null) {
|
||||
mClearButton.setOnClickListener(v -> mSearchField.setText(""));
|
||||
}
|
||||
if (mVoiceButton != null) {
|
||||
mVoiceButton.setOnClickListener(v -> startVoiceSearch());
|
||||
}
|
||||
final View seeAll = findViewById(R.id.home_search_frequent_see_all);
|
||||
if (seeAll != null) {
|
||||
seeAll.setOnClickListener(v -> HomeToast.show(this,
|
||||
getString(R.string.home_search_coming_soon,
|
||||
getString(R.string.home_search_frequent_title))));
|
||||
}
|
||||
final View clearHistory = findViewById(R.id.home_search_clear_history);
|
||||
if (clearHistory != null) {
|
||||
clearHistory.setOnClickListener(v -> {
|
||||
HomeSearchHistoryStore.clear(this);
|
||||
bindRecentSearches();
|
||||
});
|
||||
}
|
||||
final TextView categoryCount = findViewById(R.id.home_search_categories_count);
|
||||
if (categoryCount != null) {
|
||||
categoryCount.setText(getString(R.string.home_search_categories_count, 8));
|
||||
}
|
||||
|
||||
if (mSearchField != null) {
|
||||
mSearchField.addTextChangedListener(new TextWatcher() {
|
||||
@Override
|
||||
@@ -69,7 +126,7 @@ public class HomeSearchActivity extends BugleActionBarActivity {
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(final Editable s) {
|
||||
updateClearButton();
|
||||
updateFieldActions();
|
||||
}
|
||||
});
|
||||
mSearchField.setOnEditorActionListener((v, actionId, event) -> {
|
||||
@@ -79,28 +136,31 @@ public class HomeSearchActivity extends BugleActionBarActivity {
|
||||
mSearchField.requestFocus();
|
||||
mSearchField.post(() -> ImeUtil.get().showImeKeyboard(this, mSearchField));
|
||||
}
|
||||
updateClearButton();
|
||||
updateFieldActions();
|
||||
|
||||
bindFilter(R.id.home_search_filter_unread, R.drawable.ic_home_menu_unread,
|
||||
R.string.home_search_filter_unread);
|
||||
R.string.home_search_filter_unread, 0);
|
||||
bindFilter(R.id.home_search_filter_saved, R.drawable.ic_home_menu_person,
|
||||
R.string.home_search_filter_saved);
|
||||
R.string.home_search_filter_saved, 0);
|
||||
bindFilter(R.id.home_search_filter_unknown, R.drawable.ic_home_menu_spam,
|
||||
R.string.home_search_filter_unknown);
|
||||
R.string.home_search_filter_unknown, 0);
|
||||
bindFilter(R.id.home_search_filter_starred, R.drawable.ic_home_star_line,
|
||||
R.string.home_search_filter_starred);
|
||||
R.string.home_search_filter_starred, StarredMessagesStore.count(this));
|
||||
bindFilter(R.id.home_search_filter_images, R.drawable.ic_home_image_line,
|
||||
R.string.home_search_filter_images);
|
||||
R.string.home_search_filter_images, 0);
|
||||
bindFilter(R.id.home_search_filter_videos, R.drawable.ic_home_video_line,
|
||||
R.string.home_search_filter_videos);
|
||||
R.string.home_search_filter_videos, 0);
|
||||
bindFilter(R.id.home_search_filter_places, R.drawable.ic_home_place_line,
|
||||
R.string.home_search_filter_places);
|
||||
R.string.home_search_filter_places, 0);
|
||||
bindFilter(R.id.home_search_filter_links, R.drawable.ic_home_link_line,
|
||||
R.string.home_search_filter_links);
|
||||
R.string.home_search_filter_links, 0);
|
||||
|
||||
bindRecentSearches();
|
||||
loadConversationStats();
|
||||
}
|
||||
|
||||
private void bindFilter(@IdRes final int tileId, @DrawableRes final int iconRes,
|
||||
@StringRes final int labelRes) {
|
||||
@StringRes final int labelRes, final int count) {
|
||||
final View tile = findViewById(tileId);
|
||||
if (tile == null) {
|
||||
return;
|
||||
@@ -113,25 +173,232 @@ public class HomeSearchActivity extends BugleActionBarActivity {
|
||||
if (label != null) {
|
||||
label.setText(labelRes);
|
||||
}
|
||||
setFilterCount(tile, count);
|
||||
tile.setOnClickListener(v -> HomeToast.show(this,
|
||||
getString(R.string.home_search_coming_soon, getString(labelRes))));
|
||||
}
|
||||
|
||||
private void updateClearButton() {
|
||||
if (mClearButton == null || mSearchField == null) {
|
||||
private void setFilterCount(final View tile, final int count) {
|
||||
final TextView countView = tile.findViewById(R.id.home_search_filter_count);
|
||||
if (countView == null) {
|
||||
return;
|
||||
}
|
||||
mClearButton.setVisibility(
|
||||
TextUtils.isEmpty(mSearchField.getText()) ? View.GONE : View.VISIBLE);
|
||||
countView.setText(String.valueOf(Math.max(0, count)));
|
||||
countView.setTextColor(ContextCompat.getColor(this,
|
||||
count > 0 ? R.color.home_accent : R.color.home_text_secondary));
|
||||
}
|
||||
|
||||
private void loadConversationStats() {
|
||||
new Thread(() -> {
|
||||
int unread = 0;
|
||||
int saved = 0;
|
||||
int unknown = 0;
|
||||
final List<ConversationListItemData> frequent = new ArrayList<>();
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = getContentResolver().query(
|
||||
MessagingContentProvider.CONVERSATIONS_URI, null, null, null,
|
||||
ConversationColumns.SORT_TIMESTAMP + " DESC");
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
final ConversationListItemData item = new ConversationListItemData();
|
||||
item.bind(cursor);
|
||||
if (item.getIsArchived()) {
|
||||
continue;
|
||||
}
|
||||
if (!item.getIsRead()) {
|
||||
unread++;
|
||||
}
|
||||
if (!item.getIsGroup()) {
|
||||
if (item.getParticipantContactId() > 0) {
|
||||
saved++;
|
||||
} else {
|
||||
unknown++;
|
||||
}
|
||||
}
|
||||
if (frequent.size() < FREQUENT_LIMIT) {
|
||||
frequent.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (final Exception ignored) {
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
final int unreadCount = unread;
|
||||
final int savedCount = saved;
|
||||
final int unknownCount = unknown;
|
||||
runOnUiThread(() -> {
|
||||
if (isFinishing()) {
|
||||
return;
|
||||
}
|
||||
applyFilterCount(R.id.home_search_filter_unread, unreadCount);
|
||||
applyFilterCount(R.id.home_search_filter_saved, savedCount);
|
||||
applyFilterCount(R.id.home_search_filter_unknown, unknownCount);
|
||||
bindFrequentContacts(frequent);
|
||||
});
|
||||
}, "home-search-stats").start();
|
||||
}
|
||||
|
||||
private void applyFilterCount(@IdRes final int tileId, final int count) {
|
||||
final View tile = findViewById(tileId);
|
||||
if (tile != null) {
|
||||
setFilterCount(tile, count);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindFrequentContacts(final List<ConversationListItemData> items) {
|
||||
if (mFrequentSection == null || mFrequentRow == null) {
|
||||
return;
|
||||
}
|
||||
mFrequentRow.removeAllViews();
|
||||
if (items == null || items.isEmpty()) {
|
||||
mFrequentSection.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
mFrequentSection.setVisibility(View.VISIBLE);
|
||||
final LayoutInflater inflater = getLayoutInflater();
|
||||
for (final ConversationListItemData item : items) {
|
||||
final View cell = inflater.inflate(R.layout.home_search_frequent_item, mFrequentRow,
|
||||
false);
|
||||
final ContactIconView avatar = cell.findViewById(R.id.home_search_frequent_avatar);
|
||||
final TextView name = cell.findViewById(R.id.home_search_frequent_name);
|
||||
if (avatar != null) {
|
||||
avatar.setImageResourceUri(frequentAvatarUri(item),
|
||||
item.getParticipantContactId(),
|
||||
item.getParticipantLookupKey(),
|
||||
item.getOtherParticipantNormalizedDestination());
|
||||
avatar.setImageClickHandlerDisabled(true);
|
||||
}
|
||||
if (name != null) {
|
||||
name.setText(item.getName());
|
||||
}
|
||||
cell.setOnClickListener(v -> UIIntents.get().launchConversationActivity(
|
||||
this, item.getConversationId(), null));
|
||||
mFrequentRow.addView(cell);
|
||||
}
|
||||
}
|
||||
|
||||
private void bindRecentSearches() {
|
||||
if (mRecentSection == null || mRecentGroup == null) {
|
||||
return;
|
||||
}
|
||||
mRecentGroup.removeAllViews();
|
||||
final List<String> items = HomeSearchHistoryStore.list(this);
|
||||
if (items.isEmpty()) {
|
||||
mRecentSection.setVisibility(View.GONE);
|
||||
return;
|
||||
}
|
||||
mRecentSection.setVisibility(View.VISIBLE);
|
||||
final int bg = ContextCompat.getColor(this, R.color.home_row_bg);
|
||||
final int stroke = ContextCompat.getColor(this, R.color.home_divider);
|
||||
final int text = ContextCompat.getColor(this, R.color.home_text_primary);
|
||||
final int close = ContextCompat.getColor(this, R.color.home_text_secondary);
|
||||
for (final String query : items) {
|
||||
final Chip chip = new Chip(this, null, com.google.android.material.R.attr.chipStyle);
|
||||
chip.setText(query);
|
||||
chip.setCheckable(false);
|
||||
chip.setClickable(true);
|
||||
chip.setCloseIconVisible(true);
|
||||
chip.setChipBackgroundColor(ColorStateList.valueOf(bg));
|
||||
chip.setChipStrokeColor(ColorStateList.valueOf(stroke));
|
||||
chip.setChipStrokeWidth(getResources().getDisplayMetrics().density);
|
||||
chip.setChipCornerRadius(999f);
|
||||
chip.setTextColor(text);
|
||||
chip.setCloseIconTint(ColorStateList.valueOf(close));
|
||||
chip.setEnsureMinTouchTargetSize(false);
|
||||
chip.setOnClickListener(v -> {
|
||||
mSearchField.setText(query);
|
||||
mSearchField.setSelection(query.length());
|
||||
submitSearch();
|
||||
});
|
||||
chip.setOnCloseIconClickListener(v -> {
|
||||
HomeSearchHistoryStore.remove(this, query);
|
||||
bindRecentSearches();
|
||||
});
|
||||
mRecentGroup.addView(chip);
|
||||
}
|
||||
}
|
||||
|
||||
private static Uri frequentAvatarUri(final ConversationListItemData item) {
|
||||
Uri stored = null;
|
||||
if (!TextUtils.isEmpty(item.getIcon())) {
|
||||
try {
|
||||
stored = Uri.parse(item.getIcon());
|
||||
} catch (final RuntimeException ignored) {
|
||||
stored = null;
|
||||
}
|
||||
}
|
||||
if (stored != null && AvatarUriUtil.isAvatarUri(stored)) {
|
||||
return stored;
|
||||
}
|
||||
final Uri photo = (stored != null && UriUtil.isLocalResourceUri(stored)) ? stored : null;
|
||||
return AvatarUriUtil.createAvatarUri(photo, item.getName(),
|
||||
item.getOtherParticipantNormalizedDestination(),
|
||||
item.getParticipantLookupKey());
|
||||
}
|
||||
|
||||
private void updateFieldActions() {
|
||||
final boolean empty = mSearchField == null || TextUtils.isEmpty(mSearchField.getText());
|
||||
if (mClearButton != null) {
|
||||
mClearButton.setVisibility(empty ? View.GONE : View.VISIBLE);
|
||||
}
|
||||
if (mVoiceButton != null) {
|
||||
mVoiceButton.setVisibility(empty ? View.VISIBLE : View.GONE);
|
||||
}
|
||||
}
|
||||
|
||||
private void submitSearch() {
|
||||
if (mSearchField == null || TextUtils.isEmpty(mSearchField.getText())) {
|
||||
return;
|
||||
}
|
||||
final String query = mSearchField.getText().toString().trim();
|
||||
HomeSearchHistoryStore.add(this, query);
|
||||
bindRecentSearches();
|
||||
ImeUtil.get().hideImeKeyboard(this, mSearchField);
|
||||
HomeToast.show(this, getString(R.string.home_search_coming_soon, query));
|
||||
}
|
||||
|
||||
private void resetSearch() {
|
||||
if (mSearchField != null) {
|
||||
mSearchField.setText("");
|
||||
}
|
||||
HomeSearchHistoryStore.clear(this);
|
||||
bindRecentSearches();
|
||||
updateFieldActions();
|
||||
}
|
||||
|
||||
private void startVoiceSearch() {
|
||||
final Intent intent = new Intent(RecognizerIntent.ACTION_RECOGNIZE_SPEECH);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_LANGUAGE_MODEL,
|
||||
RecognizerIntent.LANGUAGE_MODEL_FREE_FORM);
|
||||
intent.putExtra(RecognizerIntent.EXTRA_PROMPT, getString(R.string.home_search_field_hint));
|
||||
try {
|
||||
startActivityForResult(intent, VOICE_SEARCH_REQUEST);
|
||||
} catch (final Exception ignored) {
|
||||
HomeToast.show(this, getString(R.string.home_search_coming_soon,
|
||||
mSearchField.getText().toString()));
|
||||
getString(R.string.home_search_voice)));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(final int requestCode, final int resultCode,
|
||||
@Nullable final Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
if (requestCode != VOICE_SEARCH_REQUEST || resultCode != RESULT_OK || data == null
|
||||
|| mSearchField == null) {
|
||||
return;
|
||||
}
|
||||
final ArrayList<String> results =
|
||||
data.getStringArrayListExtra(RecognizerIntent.EXTRA_RESULTS);
|
||||
if (results == null || results.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
mSearchField.setText(results.get(0));
|
||||
mSearchField.setSelection(mSearchField.getText().length());
|
||||
submitSearch();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.android.messaging.ui.home;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.text.TextUtils;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/** Recent search queries for the home search hub. */
|
||||
public final class HomeSearchHistoryStore {
|
||||
|
||||
private static final String PREFS_NAME = "home_search_history";
|
||||
private static final String KEY_QUERIES = "queries";
|
||||
private static final String SEPARATOR = "\n";
|
||||
private static final int MAX_ITEMS = 10;
|
||||
|
||||
private HomeSearchHistoryStore() {
|
||||
}
|
||||
|
||||
public static List<String> list(final Context context) {
|
||||
final String raw = prefs(context).getString(KEY_QUERIES, "");
|
||||
if (TextUtils.isEmpty(raw)) {
|
||||
return Collections.emptyList();
|
||||
}
|
||||
final List<String> items = new ArrayList<>();
|
||||
for (final String part : raw.split(SEPARATOR, -1)) {
|
||||
if (!TextUtils.isEmpty(part)) {
|
||||
items.add(part);
|
||||
}
|
||||
}
|
||||
return items;
|
||||
}
|
||||
|
||||
public static void add(final Context context, final String query) {
|
||||
final String trimmed = query == null ? "" : query.trim();
|
||||
if (TextUtils.isEmpty(trimmed)) {
|
||||
return;
|
||||
}
|
||||
final List<String> items = new ArrayList<>(list(context));
|
||||
items.remove(trimmed);
|
||||
items.add(0, trimmed);
|
||||
while (items.size() > MAX_ITEMS) {
|
||||
items.remove(items.size() - 1);
|
||||
}
|
||||
save(context, items);
|
||||
}
|
||||
|
||||
public static void remove(final Context context, final String query) {
|
||||
final List<String> items = new ArrayList<>(list(context));
|
||||
if (items.remove(query)) {
|
||||
save(context, items);
|
||||
}
|
||||
}
|
||||
|
||||
public static void clear(final Context context) {
|
||||
prefs(context).edit().remove(KEY_QUERIES).apply();
|
||||
}
|
||||
|
||||
private static void save(final Context context, final List<String> items) {
|
||||
prefs(context).edit().putString(KEY_QUERIES, TextUtils.join(SEPARATOR, items)).apply();
|
||||
}
|
||||
|
||||
private static SharedPreferences prefs(final Context context) {
|
||||
return context.getApplicationContext().getSharedPreferences(PREFS_NAME,
|
||||
Context.MODE_PRIVATE);
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,17 @@ public final class HomeSubHeader {
|
||||
}
|
||||
}
|
||||
|
||||
public static void bindAction(final Activity activity, @StringRes final int actionResId,
|
||||
final View.OnClickListener onAction) {
|
||||
final TextView action = activity.findViewById(R.id.home_sub_header_action);
|
||||
if (action == null) {
|
||||
return;
|
||||
}
|
||||
action.setText(actionResId);
|
||||
action.setVisibility(View.VISIBLE);
|
||||
action.setOnClickListener(onAction);
|
||||
}
|
||||
|
||||
public static void bindBackOnly(final Activity activity,
|
||||
final View.OnClickListener onBack) {
|
||||
final ImageButton back = activity.findViewById(R.id.home_sub_header_back);
|
||||
|
||||
@@ -32,6 +32,10 @@ public final class StarredMessagesStore {
|
||||
private StarredMessagesStore() {
|
||||
}
|
||||
|
||||
public static int count(final Context context) {
|
||||
return ids(context).size();
|
||||
}
|
||||
|
||||
public static boolean isStarred(final Context context, final String messageId) {
|
||||
if (TextUtils.isEmpty(messageId)) {
|
||||
return false;
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<ripple xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:color="@color/home_surface_elevated">
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<corners android:radius="@dimen/looply_radius_xl" />
|
||||
<solid android:color="@color/home_row_bg" />
|
||||
</shape>
|
||||
</item>
|
||||
</ripple>
|
||||
@@ -277,6 +277,15 @@
|
||||
android:text="@string/home_search_field_hint"
|
||||
android:textColor="@color/home_search_hint"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/home_search_bar_mic"
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:contentDescription="@string/home_search_voice"
|
||||
android:scaleType="fitCenter"
|
||||
android:src="@drawable/ic_home_mic"
|
||||
android:tint="@color/home_text_secondary" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -9,17 +9,32 @@
|
||||
|
||||
<include layout="@layout/home_sub_header" />
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0dp"
|
||||
android:layout_weight="1"
|
||||
android:clipToPadding="false"
|
||||
android:overScrollMode="ifContentScrolls"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="@dimen/looply_screen_padding"
|
||||
android:paddingTop="@dimen/looply_space_sm"
|
||||
android:paddingEnd="@dimen/looply_screen_padding"
|
||||
android:paddingBottom="@dimen/looply_space_xxl">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/home_search_field_height"
|
||||
android:layout_marginStart="@dimen/looply_space_md"
|
||||
android:layout_marginTop="@dimen/looply_space_sm"
|
||||
android:layout_marginEnd="@dimen/looply_space_md"
|
||||
android:background="@drawable/home_bg_search_field_inner"
|
||||
android:elevation="2dp"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/looply_space_md"
|
||||
android:paddingEnd="@dimen/looply_space_md">
|
||||
android:paddingEnd="@dimen/looply_space_sm">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="18dp"
|
||||
@@ -33,8 +48,8 @@
|
||||
android:id="@+id/home_search_field"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:layout_marginStart="@dimen/looply_space_sm"
|
||||
android:layout_weight="1"
|
||||
android:background="@android:color/transparent"
|
||||
android:gravity="center_vertical|start"
|
||||
android:hint="@string/home_search_field_hint"
|
||||
@@ -44,28 +59,108 @@
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/home_text_primary"
|
||||
android:textColorHint="@color/home_search_hint"
|
||||
android:textSize="16sp" />
|
||||
android:textSize="15sp" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/home_search_clear"
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/home_search_clear"
|
||||
android:padding="8dp"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_home_close"
|
||||
android:tint="@color/home_text_secondary"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/home_search_voice"
|
||||
android:layout_width="36dp"
|
||||
android:layout_height="36dp"
|
||||
android:background="@android:color/transparent"
|
||||
android:contentDescription="@string/home_search_voice"
|
||||
android:padding="6dp"
|
||||
android:scaleType="centerInside"
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_home_close"
|
||||
app:tint="@color/home_text_secondary" />
|
||||
android:src="@drawable/ic_home_mic"
|
||||
android:tint="@color/home_text_secondary"
|
||||
android:visibility="visible" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_search_frequent_section"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/looply_space_xl"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:includeFontPadding="false"
|
||||
android:text="@string/home_search_frequent_title"
|
||||
android:textColor="@color/home_text_primary"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_search_frequent_see_all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:includeFontPadding="false"
|
||||
android:padding="@dimen/looply_space_xs"
|
||||
android:text="@string/home_search_see_all"
|
||||
android:textColor="@color/home_accent"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_search_frequent_row"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/looply_space_md"
|
||||
android:orientation="horizontal" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/looply_space_xl"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:includeFontPadding="false"
|
||||
android:text="@string/home_search_categories_title"
|
||||
android:textColor="@color/home_text_primary"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_search_categories_count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@color/home_text_secondary"
|
||||
android:textSize="13sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_search_filters"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/looply_space_md"
|
||||
android:layout_marginTop="@dimen/looply_space_md"
|
||||
android:layout_marginEnd="@dimen/looply_space_md"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
@@ -155,4 +250,51 @@
|
||||
android:layout_weight="1" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/home_search_recent_section"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/looply_space_xl"
|
||||
android:orientation="vertical"
|
||||
android:visibility="gone">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:includeFontPadding="false"
|
||||
android:text="@string/home_search_recent_title"
|
||||
android:textColor="@color/home_text_primary"
|
||||
android:textSize="15sp"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_search_clear_history"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:includeFontPadding="false"
|
||||
android:padding="@dimen/looply_space_xs"
|
||||
android:text="@string/home_search_clear_history"
|
||||
android:textColor="@color/home_accent"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
<com.google.android.material.chip.ChipGroup
|
||||
android:id="@+id/home_search_recent_group"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/looply_space_md"
|
||||
app:chipSpacingHorizontal="8dp"
|
||||
app:chipSpacingVertical="8dp" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</ScrollView>
|
||||
</LinearLayout>
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- One filter tile in the message search grid (icon + label). -->
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/home_search_filter_height"
|
||||
android:background="@drawable/home_bg_sheet_action"
|
||||
android:background="@drawable/home_bg_search_card"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:paddingStart="@dimen/looply_space_lg"
|
||||
android:paddingEnd="@dimen/looply_space_md">
|
||||
android:paddingTop="@dimen/looply_space_md"
|
||||
android:paddingEnd="@dimen/looply_space_lg"
|
||||
android:paddingBottom="@dimen/looply_space_md">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/home_search_filter_icon"
|
||||
android:layout_width="@dimen/home_search_filter_icon_size"
|
||||
android:layout_height="@dimen/home_search_filter_icon_size"
|
||||
android:layout_gravity="top"
|
||||
android:layout_marginTop="2dp"
|
||||
android:contentDescription="@null"
|
||||
android:scaleType="fitCenter"
|
||||
android:tint="@color/home_text_primary" />
|
||||
@@ -27,7 +30,17 @@
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="1"
|
||||
android:maxLines="2"
|
||||
android:textColor="@color/home_text_primary"
|
||||
android:textSize="15sp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_search_filter_count"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="bottom"
|
||||
android:layout_marginStart="@dimen/looply_space_sm"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@color/home_text_secondary"
|
||||
android:textSize="14sp" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:clickable="true"
|
||||
android:focusable="true"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical"
|
||||
android:paddingStart="2dp"
|
||||
android:paddingEnd="2dp">
|
||||
|
||||
<com.android.messaging.ui.ContactIconView
|
||||
android:id="@+id/home_search_frequent_avatar"
|
||||
android:layout_width="@dimen/home_search_frequent_avatar"
|
||||
android:layout_height="@dimen/home_search_frequent_avatar"
|
||||
android:contentDescription="@null"
|
||||
app:contactIconSize="normal" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_search_frequent_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="6dp"
|
||||
android:ellipsize="end"
|
||||
android:gravity="center_horizontal"
|
||||
android:includeFontPadding="false"
|
||||
android:maxLines="2"
|
||||
android:textColor="@color/home_text_primary"
|
||||
android:textSize="12sp" />
|
||||
</LinearLayout>
|
||||
@@ -41,4 +41,18 @@
|
||||
android:visibility="gone"
|
||||
app:srcCompat="@drawable/ic_looply_more"
|
||||
app:tint="@color/home_text_primary" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/home_sub_header_action"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?attr/selectableItemBackgroundBorderless"
|
||||
android:includeFontPadding="false"
|
||||
android:paddingStart="@dimen/looply_space_sm"
|
||||
android:paddingTop="@dimen/looply_space_sm"
|
||||
android:paddingEnd="@dimen/looply_space_xs"
|
||||
android:paddingBottom="@dimen/looply_space_sm"
|
||||
android:textColor="@color/home_accent"
|
||||
android:textSize="16sp"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
@@ -106,9 +106,10 @@
|
||||
<dimen name="home_nav_item_padding">8dp</dimen>
|
||||
<dimen name="home_nav_dock_padding_bottom">14dp</dimen>
|
||||
<dimen name="home_alert_dialog_horizontal_margin">28dp</dimen>
|
||||
<dimen name="home_search_filter_height">56dp</dimen>
|
||||
<dimen name="home_search_filter_height">88dp</dimen>
|
||||
<dimen name="home_search_filter_icon_size">22dp</dimen>
|
||||
<dimen name="home_search_field_height">52dp</dimen>
|
||||
<dimen name="home_search_field_height">48dp</dimen>
|
||||
<dimen name="home_search_frequent_avatar">48dp</dimen>
|
||||
<dimen name="home_compose_field_height">52dp</dimen>
|
||||
<dimen name="home_compose_group_button_height">52dp</dimen>
|
||||
<dimen name="home_selection_dock_height">55dp</dimen>
|
||||
|
||||
@@ -155,8 +155,16 @@
|
||||
|
||||
<!-- Message search screen -->
|
||||
<string name="home_search_title" translatable="false">Search</string>
|
||||
<string name="home_search_field_hint" translatable="false">Search messages</string>
|
||||
<string name="home_search_field_hint" translatable="false">Search messages or contacts</string>
|
||||
<string name="home_search_clear" translatable="false">Clear</string>
|
||||
<string name="home_search_reset" translatable="false">Reset</string>
|
||||
<string name="home_search_voice" translatable="false">Voice search</string>
|
||||
<string name="home_search_frequent_title" translatable="false">Frequent contacts</string>
|
||||
<string name="home_search_see_all" translatable="false">See all</string>
|
||||
<string name="home_search_categories_title" translatable="false">Categories</string>
|
||||
<string name="home_search_categories_count" translatable="false">%1$d items</string>
|
||||
<string name="home_search_recent_title" translatable="false">Recent searches</string>
|
||||
<string name="home_search_clear_history" translatable="false">Clear history</string>
|
||||
<string name="home_search_filter_unread" translatable="false">Unread</string>
|
||||
<string name="home_search_filter_saved" translatable="false">Saved contacts</string>
|
||||
<string name="home_search_filter_unknown" translatable="false">Unknown contacts</string>
|
||||
|
||||
Reference in New Issue
Block a user