enable multiple hashtags in one hashtag tab (#1790)

* enable multiple hashtags in one hashtag tab

* add comment explaining the code in TabAdapter

* delete unused drawables

* add padding to EditText in dialog
This commit is contained in:
Konrad Pozniak 2020-05-15 22:10:29 +02:00 committed by Alibek Omarov
parent 59d36fcbc9
commit 83600ee92d
10 changed files with 105 additions and 62 deletions

View File

@ -45,7 +45,7 @@ fun createTabDataFromId(id: String, arguments: List<String> = emptyList()): TabD
LOCAL -> TabData(LOCAL, R.string.title_public_local, R.drawable.ic_local_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_LOCAL) }) LOCAL -> TabData(LOCAL, R.string.title_public_local, R.drawable.ic_local_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_LOCAL) })
FEDERATED -> TabData(FEDERATED, R.string.title_public_federated, R.drawable.ic_public_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_FEDERATED) }) FEDERATED -> TabData(FEDERATED, R.string.title_public_federated, R.drawable.ic_public_24dp, { TimelineFragment.newInstance(TimelineFragment.Kind.PUBLIC_FEDERATED) })
DIRECT -> TabData(DIRECT, R.string.title_direct_messages, R.drawable.ic_reblog_direct_24dp, { ConversationsFragment.newInstance() }) DIRECT -> TabData(DIRECT, R.string.title_direct_messages, R.drawable.ic_reblog_direct_24dp, { ConversationsFragment.newInstance() })
HASHTAG -> TabData(HASHTAG, R.string.hashtag, R.drawable.ic_hashtag, { args -> TimelineFragment.newInstance(TimelineFragment.Kind.TAG, args.getOrNull(0).orEmpty()) }, arguments) HASHTAG -> TabData(HASHTAG, R.string.hashtags, R.drawable.ic_hashtag, { args -> TimelineFragment.newHashtagInstance(args) }, arguments)
LIST -> TabData(LIST, R.string.list, R.drawable.ic_list, { args -> TimelineFragment.newInstance(TimelineFragment.Kind.LIST, args.getOrNull(0).orEmpty()) }, arguments) LIST -> TabData(LIST, R.string.list, R.drawable.ic_list, { args -> TimelineFragment.newInstance(TimelineFragment.Kind.LIST, args.getOrNull(0).orEmpty()) }, arguments)
else -> throw IllegalArgumentException("unknown tab type") else -> throw IllegalArgumentException("unknown tab type")
} }

View File

@ -18,13 +18,16 @@ package com.keylesspalace.tusky
import android.os.Bundle import android.os.Bundle
import android.util.Log import android.util.Log
import android.view.MenuItem import android.view.MenuItem
import android.widget.FrameLayout
import androidx.appcompat.app.AlertDialog import androidx.appcompat.app.AlertDialog
import androidx.appcompat.widget.AppCompatEditText import androidx.appcompat.widget.AppCompatEditText
import androidx.core.view.updatePadding
import androidx.lifecycle.Lifecycle import androidx.lifecycle.Lifecycle
import androidx.recyclerview.widget.DividerItemDecoration import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.ItemTouchHelper import androidx.recyclerview.widget.ItemTouchHelper
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import at.connyduck.sparkbutton.helpers.Utils
import com.keylesspalace.tusky.adapter.ItemInteractionListener import com.keylesspalace.tusky.adapter.ItemInteractionListener
import com.keylesspalace.tusky.adapter.ListSelectionAdapter import com.keylesspalace.tusky.adapter.ListSelectionAdapter
import com.keylesspalace.tusky.adapter.TabAdapter import com.keylesspalace.tusky.adapter.TabAdapter
@ -150,7 +153,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
actionButton.isExpanded = false actionButton.isExpanded = false
if (tab.id == HASHTAG) { if (tab.id == HASHTAG) {
showEditHashtagDialog() showAddHashtagDialog()
return return
} }
@ -173,19 +176,32 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
} }
override fun onActionChipClicked(tab: TabData) { override fun onActionChipClicked(tab: TabData) {
showEditHashtagDialog(tab) showAddHashtagDialog(tab)
} }
private fun showEditHashtagDialog(tab: TabData? = null) { override fun onChipClicked(tab: TabData, chipPosition: Int) {
val newArguments = tab.arguments.filterIndexed { i, _ -> i != chipPosition }
val newTab = tab.copy(arguments = newArguments)
val position = currentTabs.indexOf(tab)
currentTabs[position] = newTab
currentTabsAdapter.notifyItemChanged(position)
}
private fun showAddHashtagDialog(tab: TabData? = null) {
val frameLayout = FrameLayout(this)
val padding = Utils.dpToPx(this, 8)
frameLayout.updatePadding(left = padding, right = padding)
val editText = AppCompatEditText(this) val editText = AppCompatEditText(this)
editText.setHint(R.string.edit_hashtag_hint) editText.setHint(R.string.edit_hashtag_hint)
editText.setText("") editText.setText("")
editText.append(tab?.arguments?.first().orEmpty()) frameLayout.addView(editText)
val dialog = AlertDialog.Builder(this) val dialog = AlertDialog.Builder(this)
.setTitle(R.string.edit_hashtag_title) .setTitle(R.string.add_hashtag_title)
.setView(editText) .setView(frameLayout)
.setNegativeButton(android.R.string.cancel, null) .setNegativeButton(android.R.string.cancel, null)
.setPositiveButton(R.string.action_save) { _, _ -> .setPositiveButton(R.string.action_save) { _, _ ->
val input = editText.text.toString().trim() val input = editText.text.toString().trim()
@ -194,7 +210,7 @@ class TabPreferenceActivity : BaseActivity(), Injectable, ItemInteractionListene
currentTabs.add(newTab) currentTabs.add(newTab)
currentTabsAdapter.notifyItemInserted(currentTabs.size - 1) currentTabsAdapter.notifyItemInserted(currentTabs.size - 1)
} else { } else {
val newTab = tab.copy(arguments = listOf(input)) val newTab = tab.copy(arguments = tab.arguments + input)
val position = currentTabs.indexOf(tab) val position = currentTabs.indexOf(tab)
currentTabs[position] = newTab currentTabs[position] = newTab

View File

@ -28,6 +28,8 @@ import androidx.fragment.app.FragmentTransaction;
import com.keylesspalace.tusky.fragment.TimelineFragment; import com.keylesspalace.tusky.fragment.TimelineFragment;
import java.util.Collections;
import javax.inject.Inject; import javax.inject.Inject;
import dagger.android.AndroidInjector; import dagger.android.AndroidInjector;
@ -65,7 +67,7 @@ public class ViewTagActivity extends BottomSheetActivity implements HasAndroidIn
} }
FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction(); FragmentTransaction fragmentTransaction = getSupportFragmentManager().beginTransaction();
Fragment fragment = TimelineFragment.newInstance(TimelineFragment.Kind.TAG, hashtag); Fragment fragment = TimelineFragment.newHashtagInstance(Collections.singletonList(hashtag));
fragmentTransaction.replace(R.id.fragment_container, fragment); fragmentTransaction.replace(R.id.fragment_container, fragment);
fragmentTransaction.commit(); fragmentTransaction.commit();
} }

View File

@ -19,7 +19,9 @@ import android.view.LayoutInflater
import android.view.MotionEvent import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import androidx.core.view.size
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import com.google.android.material.chip.Chip
import com.keylesspalace.tusky.HASHTAG import com.keylesspalace.tusky.HASHTAG
import com.keylesspalace.tusky.LIST import com.keylesspalace.tusky.LIST
import com.keylesspalace.tusky.R import com.keylesspalace.tusky.R
@ -29,13 +31,13 @@ import com.keylesspalace.tusky.util.hide
import com.keylesspalace.tusky.util.show import com.keylesspalace.tusky.util.show
import kotlinx.android.synthetic.main.item_tab_preference.view.* import kotlinx.android.synthetic.main.item_tab_preference.view.*
interface ItemInteractionListener { interface ItemInteractionListener {
fun onTabAdded(tab: TabData) fun onTabAdded(tab: TabData)
fun onTabRemoved(position: Int) fun onTabRemoved(position: Int)
fun onStartDelete(viewHolder: RecyclerView.ViewHolder) fun onStartDelete(viewHolder: RecyclerView.ViewHolder)
fun onStartDrag(viewHolder: RecyclerView.ViewHolder) fun onStartDrag(viewHolder: RecyclerView.ViewHolder)
fun onActionChipClicked(tab: TabData) fun onActionChipClicked(tab: TabData)
fun onChipClicked(tab: TabData, chipPosition: Int)
} }
class TabAdapter(private var data: List<TabData>, class TabAdapter(private var data: List<TabData>,
@ -86,9 +88,9 @@ class TabAdapter(private var data: List<TabData>,
if (holder.itemView.removeButton != null) { if (holder.itemView.removeButton != null) {
holder.itemView.removeButton.isEnabled = removeButtonEnabled holder.itemView.removeButton.isEnabled = removeButtonEnabled
ThemeUtils.setDrawableTint( ThemeUtils.setDrawableTint(
holder.itemView.context, holder.itemView.context,
holder.itemView.removeButton.drawable, holder.itemView.removeButton.drawable,
(if (removeButtonEnabled) android.R.attr.textColorTertiary else R.attr.textColorDisabled) (if (removeButtonEnabled) android.R.attr.textColorTertiary else R.attr.textColorDisabled)
) )
} }
@ -96,11 +98,38 @@ class TabAdapter(private var data: List<TabData>,
if (data[position].id == HASHTAG) { if (data[position].id == HASHTAG) {
holder.itemView.chipGroup.show() holder.itemView.chipGroup.show()
holder.itemView.actionChip.text = data[position].arguments[0]
holder.itemView.actionChip.setChipIconResource(R.drawable.ic_edit_chip) /*
* The chip group will always contain the actionChip (it is defined in the xml layout).
* The other dynamic chips are inserted in front of the actionChip.
* This code tries to reuse already added chips to reduce the number of Views created.
*/
data[position].arguments.forEachIndexed { i, arg ->
val chip = holder.itemView.chipGroup.getChildAt(i).takeUnless { it.id == R.id.actionChip } as Chip?
?: Chip(context).apply {
text = arg
holder.itemView.chipGroup.addView(this, holder.itemView.chipGroup.size - 1)
}
chip.text = arg
if(data[position].arguments.size <= 1) {
chip.chipIcon = null
chip.setOnClickListener(null)
} else {
val cancelIcon = ThemeUtils.getTintedDrawable(context, R.drawable.ic_cancel_24dp, android.R.attr.textColorPrimary)
chip.chipIcon = cancelIcon
chip.setOnClickListener {
listener.onChipClicked(data[position], i)
}
}
}
while(holder.itemView.chipGroup.size - 1 > data[position].arguments.size) {
holder.itemView.chipGroup.removeViewAt(data[position].arguments.size - 1)
}
holder.itemView.actionChip.chipIcon = context.getDrawable(R.drawable.ic_edit_chip)
holder.itemView.actionChip.setOnClickListener { holder.itemView.actionChip.setOnClickListener {
listener.onActionChipClicked(data[position]) listener.onActionChipClicked(data[position])
} }

View File

@ -75,11 +75,11 @@ import com.keylesspalace.tusky.view.EndlessOnScrollListener;
import com.keylesspalace.tusky.viewdata.StatusViewData; import com.keylesspalace.tusky.viewdata.StatusViewData;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.ListIterator; import java.util.ListIterator;
import java.util.Objects;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import javax.inject.Inject; import javax.inject.Inject;
@ -103,7 +103,8 @@ public class TimelineFragment extends SFragment implements
Injectable, ReselectableFragment, RefreshableFragment { Injectable, ReselectableFragment, RefreshableFragment {
private static final String TAG = "TimelineF"; // logging tag private static final String TAG = "TimelineF"; // logging tag
private static final String KIND_ARG = "kind"; private static final String KIND_ARG = "kind";
private static final String HASHTAG_OR_ID_ARG = "hashtag_or_id"; private static final String ID_ARG = "id";
private static final String HASHTAGS_ARG = "hastags";
private static final String ARG_ENABLE_SWIPE_TO_REFRESH = "arg.enable.swipe.to.refresh"; private static final String ARG_ENABLE_SWIPE_TO_REFRESH = "arg.enable.swipe.to.refresh";
private static final int LOAD_AT_ONCE = 30; private static final int LOAD_AT_ONCE = 30;
@ -147,7 +148,8 @@ public class TimelineFragment extends SFragment implements
private TimelineAdapter adapter; private TimelineAdapter adapter;
private Kind kind; private Kind kind;
private String hashtagOrId; private String id;
private List<String> tags;
private LinearLayoutManager layoutManager; private LinearLayoutManager layoutManager;
private EndlessOnScrollListener scrollListener; private EndlessOnScrollListener scrollListener;
private boolean filterRemoveReplies; private boolean filterRemoveReplies;
@ -188,25 +190,37 @@ public class TimelineFragment extends SFragment implements
public static TimelineFragment newInstance(Kind kind, @Nullable String hashtagOrId, boolean enableSwipeToRefresh) { public static TimelineFragment newInstance(Kind kind, @Nullable String hashtagOrId, boolean enableSwipeToRefresh) {
TimelineFragment fragment = new TimelineFragment(); TimelineFragment fragment = new TimelineFragment();
Bundle arguments = new Bundle(); Bundle arguments = new Bundle(3);
arguments.putString(KIND_ARG, kind.name()); arguments.putString(KIND_ARG, kind.name());
arguments.putString(HASHTAG_OR_ID_ARG, hashtagOrId); arguments.putString(ID_ARG, hashtagOrId);
arguments.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, enableSwipeToRefresh); arguments.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, enableSwipeToRefresh);
fragment.setArguments(arguments); fragment.setArguments(arguments);
return fragment; return fragment;
} }
public static TimelineFragment newHashtagInstance(@NonNull List<String> hashtags) {
TimelineFragment fragment = new TimelineFragment();
Bundle arguments = new Bundle(3);
arguments.putString(KIND_ARG, Kind.TAG.name());
arguments.putStringArrayList(HASHTAGS_ARG, new ArrayList<>(hashtags));
arguments.putBoolean(ARG_ENABLE_SWIPE_TO_REFRESH, true);
fragment.setArguments(arguments);
return fragment;
}
@Override @Override
public void onCreate(@Nullable Bundle savedInstanceState) { public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
Bundle arguments = Objects.requireNonNull(getArguments()); Bundle arguments = Objects.requireNonNull(getArguments());
kind = Kind.valueOf(arguments.getString(KIND_ARG)); kind = Kind.valueOf(arguments.getString(KIND_ARG));
if (kind == Kind.TAG if (kind == Kind.USER
|| kind == Kind.USER
|| kind == Kind.USER_PINNED || kind == Kind.USER_PINNED
|| kind == Kind.USER_WITH_REPLIES || kind == Kind.USER_WITH_REPLIES
|| kind == Kind.LIST) { || kind == Kind.LIST) {
hashtagOrId = arguments.getString(HASHTAG_OR_ID_ARG); id = arguments.getString(ID_ARG);
}
if(kind == Kind.TAG) {
tags = arguments.getStringArrayList(HASHTAGS_ARG);
} }
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
@ -828,7 +842,7 @@ public class TimelineFragment extends SFragment implements
@Override @Override
public void onViewTag(String tag) { public void onViewTag(String tag) {
if (kind == Kind.TAG && hashtagOrId.equals(tag)) { if (kind == Kind.TAG && tags.size() == 1 && tags.contains(tag)) {
// If already viewing a tag page, then ignore any request to view that tag again. // If already viewing a tag page, then ignore any request to view that tag again.
return; return;
} }
@ -837,7 +851,7 @@ public class TimelineFragment extends SFragment implements
@Override @Override
public void onViewAccount(String id) { public void onViewAccount(String id) {
if ((kind == Kind.USER || kind == Kind.USER_WITH_REPLIES) && hashtagOrId.equals(id)) { if ((kind == Kind.USER || kind == Kind.USER_WITH_REPLIES) && this.id.equals(id)) {
/* If already viewing an account page, then any requests to view that account page /* If already viewing an account page, then any requests to view that account page
* should be ignored. */ * should be ignored. */
return; return;
@ -986,8 +1000,7 @@ public class TimelineFragment extends SFragment implements
} }
} }
private Call<List<Status>> getFetchCallByTimelineType(Kind kind, String tagOrId, String fromId, private Call<List<Status>> getFetchCallByTimelineType(String fromId, String uptoId) {
String uptoId) {
MastodonApi api = mastodonApi; MastodonApi api = mastodonApi;
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity()); SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getActivity());
boolean withMuted = !preferences.getBoolean("hideMutedUsers", false); boolean withMuted = !preferences.getBoolean("hideMutedUsers", false);
@ -1000,19 +1013,21 @@ public class TimelineFragment extends SFragment implements
case PUBLIC_LOCAL: case PUBLIC_LOCAL:
return api.publicTimeline(true, fromId, uptoId, LOAD_AT_ONCE, withMuted); return api.publicTimeline(true, fromId, uptoId, LOAD_AT_ONCE, withMuted);
case TAG: case TAG:
return api.hashtagTimeline(tagOrId, null, fromId, uptoId, LOAD_AT_ONCE, withMuted); String firstHashtag = tags.get(0);
List<String> additionalHashtags = tags.subList(1, tags.size());
return api.hashtagTimeline(firstHashtag, additionalHashtags, null, fromId, uptoId, LOAD_AT_ONCE, withMuted);
case USER: case USER:
return api.accountStatuses(tagOrId, fromId, uptoId, LOAD_AT_ONCE, true, null, null, withMuted); return api.accountStatuses(id, fromId, uptoId, LOAD_AT_ONCE, true, null, null, withMuted);
case USER_PINNED: case USER_PINNED:
return api.accountStatuses(tagOrId, fromId, uptoId, LOAD_AT_ONCE, null, null, true, withMuted); return api.accountStatuses(id, fromId, uptoId, LOAD_AT_ONCE, null, null, true, withMuted);
case USER_WITH_REPLIES: case USER_WITH_REPLIES:
return api.accountStatuses(tagOrId, fromId, uptoId, LOAD_AT_ONCE, null, null, null, withMuted); return api.accountStatuses(id, fromId, uptoId, LOAD_AT_ONCE, null, null, null, withMuted);
case FAVOURITES: case FAVOURITES:
return api.favourites(fromId, uptoId, LOAD_AT_ONCE, withMuted); return api.favourites(fromId, uptoId, LOAD_AT_ONCE, withMuted);
case BOOKMARKS: case BOOKMARKS:
return api.bookmarks(fromId, uptoId, LOAD_AT_ONCE, withMuted); return api.bookmarks(fromId, uptoId, LOAD_AT_ONCE, withMuted);
case LIST: case LIST:
return api.listTimeline(tagOrId, fromId, uptoId, LOAD_AT_ONCE, withMuted); return api.listTimeline(id, fromId, uptoId, LOAD_AT_ONCE, withMuted);
} }
} }
@ -1054,7 +1069,7 @@ public class TimelineFragment extends SFragment implements
} }
}; };
Call<List<Status>> listCall = getFetchCallByTimelineType(kind, hashtagOrId, maxId, sinceId); Call<List<Status>> listCall = getFetchCallByTimelineType(maxId, sinceId);
callList.add(listCall); callList.add(listCall);
listCall.enqueue(callback); listCall.enqueue(callback);
} }
@ -1333,7 +1348,7 @@ public class TimelineFragment extends SFragment implements
break; break;
case USER: case USER:
case USER_WITH_REPLIES: case USER_WITH_REPLIES:
if (status.getAccount().getId().equals(hashtagOrId)) { if (status.getAccount().getId().equals(id)) {
break; break;
} else { } else {
return; return;

View File

@ -79,6 +79,7 @@ interface MastodonApi {
@GET("api/v1/timelines/tag/{hashtag}") @GET("api/v1/timelines/tag/{hashtag}")
fun hashtagTimeline( fun hashtagTimeline(
@Path("hashtag") hashtag: String, @Path("hashtag") hashtag: String,
@Query("any[]") any: List<String>?,
@Query("local") local: Boolean?, @Query("local") local: Boolean?,
@Query("max_id") maxId: String?, @Query("max_id") maxId: String?,
@Query("since_id") sinceId: String?, @Query("since_id") sinceId: String?,

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="24dp"
android:layout_height="24dp">
<background android:drawable="@color/tusky_blue" />
<foreground>
<inset
android:drawable="@drawable/ic_create_24dp"
android:inset="30%" />
</foreground>
</adaptive-icon>

View File

@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="?android:attr/textColorPrimary"
android:pathData="m4.5088,16.3703v3.1209H7.6297L16.8342,10.2866 13.7134,7.1658ZM19.2477,7.8732c0.3246,-0.3246 0.3246,-0.8489 0,-1.1735l-1.9474,-1.9474c-0.3246,-0.3246 -0.8489,-0.3246 -1.1735,0l-1.523,1.523 3.1209,3.1209z" />
</vector>

View File

@ -26,10 +26,10 @@
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="8dp" android:layout_marginStart="8dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:layout_weight="1" android:layout_weight="1"
android:drawablePadding="12dp" android:drawablePadding="12dp"
android:paddingTop="8dp"
android:paddingBottom="8dp"
android:textColor="?android:attr/textColorSecondary" android:textColor="?android:attr/textColorSecondary"
android:textSize="?attr/status_text_large" android:textSize="?attr/status_text_large"
app:layout_constraintBottom_toTopOf="@id/chipGroup" app:layout_constraintBottom_toTopOf="@id/chipGroup"
@ -57,9 +57,7 @@
android:id="@+id/chipGroup" android:id="@+id/chipGroup"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginBottom="8dp" android:layout_marginBottom="8dp"
android:paddingTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"> app:layout_constraintBottom_toBottomOf="parent">
<com.google.android.material.chip.Chip <com.google.android.material.chip.Chip
@ -68,7 +66,9 @@
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:checkable="false" android:checkable="false"
tools:text="add hashtag" /> android:text="@string/add_hashtag_title"
app:chipIcon="@drawable/ic_plus_24dp"
app:chipSurfaceColor="@color/tusky_blue" />
</com.google.android.material.chip.ChipGroup> </com.google.android.material.chip.ChipGroup>

View File

@ -472,9 +472,9 @@
<string name="hint_list_name">List name</string> <string name="hint_list_name">List name</string>
<string name="edit_hashtag_title">Edit hashtag</string> <string name="add_hashtag_title">Add hashtag</string>
<string name="edit_hashtag_hint">Hashtag without #</string> <string name="edit_hashtag_hint">Hashtag without #</string>
<string name="hashtag">Hashtag</string> <string name="hashtags">Hashtags</string>
<string name="select_list_title">Select list</string> <string name="select_list_title">Select list</string>
<string name="list">List</string> <string name="list">List</string>
<string name="notifications_clear">Clear</string> <string name="notifications_clear">Clear</string>