Add an invite button in the new conversation screen.

master
Alan Evans 2019-08-26 19:49:16 +01:00
parent af42d5b671
commit 85c9a9050a
14 changed files with 550 additions and 42 deletions

View File

@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="@color/core_grey_75"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M20,4L4,4c-1.1,0 -1.99,0.9 -1.99,2L2,18c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM20,18L4,18L4,8l8,5 8,-5v10zM12,11L4,6h16l-8,5z"/>
</vector>

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="#eeefef" />
</shape>
</item>
<item
android:bottom="12dp"
android:drawable="@drawable/ic_invite_24dp"
android:left="12dp"
android:right="12dp"
android:top="12dp" />
</layer-list>

View File

@ -0,0 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
style="@style/Signal.Text.Body"
android:layout_width="match_parent"
android:layout_height="@dimen/contact_selection_item_height"
android:drawableStart="@drawable/ic_invite_circle"
android:drawablePadding="16dp"
android:ellipsize="marquee"
android:fontFamily="sans-serif-medium"
android:gravity="center_vertical|start"
android:labelFor="@id/action_icon"
android:paddingStart="@dimen/selection_item_header_width"
android:singleLine="true"
android:text="@string/contact_selection_activity__invite_to_signal"
android:textAlignment="viewStart"
tools:ignore="RtlSymmetry" />

View File

@ -933,6 +933,7 @@
<!-- contact_selection_activity -->
<string name="contact_selection_activity__enter_name_or_number">Enter name or number</string>
<string name="contact_selection_activity__invite_to_signal">Invite to Signal</string>
<!-- contact_filter_toolbar -->
<string name="contact_filter_toolbar__clear_entered_text_description">Clear entered text</string>

View File

@ -19,16 +19,10 @@ package org.thoughtcrime.securesms;
import android.Manifest;
import android.annotation.SuppressLint;
import android.content.Context;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@ -37,6 +31,15 @@ import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.loader.app.LoaderManager;
import androidx.loader.content.Loader;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.pnikosis.materialishprogress.ProgressWheel;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
@ -44,7 +47,6 @@ import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
import org.thoughtcrime.securesms.contacts.ContactSelectionListItem;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader.DisplayMode;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
@ -52,6 +54,8 @@ import org.thoughtcrime.securesms.util.DirectoryHelper;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.thoughtcrime.securesms.util.TextSecurePreferences;
import org.thoughtcrime.securesms.util.ViewUtil;
import org.thoughtcrime.securesms.util.adapter.FixedViewsAdapter;
import org.thoughtcrime.securesms.util.adapter.RecyclerViewConcatenateAdapterStickyHeader;
import java.io.IOException;
import java.util.LinkedList;
@ -64,28 +68,41 @@ import java.util.Set;
* @author Moxie Marlinspike
*
*/
public class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
public final class ContactSelectionListFragment extends Fragment
implements LoaderManager.LoaderCallbacks<Cursor>
{
@SuppressWarnings("unused")
private static final String TAG = ContactSelectionListFragment.class.getSimpleName();
private static final String TAG = Log.tag(ContactSelectionListFragment.class);
public static final String DISPLAY_MODE = "display_mode";
public static final String MULTI_SELECT = "multi_select";
public static final String REFRESHABLE = "refreshable";
public static final String RECENTS = "recents";
private TextView emptyText;
private Set<String> selectedContacts;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private TextView emptyText;
private Set<String> selectedContacts;
private OnContactSelectedListener onContactSelectedListener;
private SwipeRefreshLayout swipeRefresh;
private View showContactsLayout;
private Button showContactsButton;
private TextView showContactsDescription;
private ProgressWheel showContactsProgress;
private String cursorFilter;
private RecyclerView recyclerView;
private RecyclerViewFastScroller fastScroller;
private ContactSelectionListAdapter cursorRecyclerViewAdapter;
@Nullable private FixedViewsAdapter footerAdapter;
@Nullable private InviteCallback inviteCallback;
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
if (context instanceof InviteCallback) {
inviteCallback = (InviteCallback) context;
}
}
@Override
public void onActivityCreated(Bundle icicle) {
@ -158,14 +175,31 @@ public class ContactSelectionListFragment extends Fragment
}
private void initializeCursor() {
ContactSelectionListAdapter adapter = new ContactSelectionListAdapter(getActivity(),
GlideApp.with(this),
null,
new ListClickListener(),
isMulti());
selectedContacts = adapter.getSelectedContacts();
recyclerView.setAdapter(adapter);
recyclerView.addItemDecoration(new StickyHeaderDecoration(adapter, true, true));
cursorRecyclerViewAdapter = new ContactSelectionListAdapter(requireContext(),
GlideApp.with(this),
null,
new ListClickListener(),
isMulti());
selectedContacts = cursorRecyclerViewAdapter.getSelectedContacts();
RecyclerViewConcatenateAdapterStickyHeader concatenateAdapter = new RecyclerViewConcatenateAdapterStickyHeader();
concatenateAdapter.addAdapter(cursorRecyclerViewAdapter);
if (inviteCallback != null) {
footerAdapter = new FixedViewsAdapter(createInviteActionView(inviteCallback));
footerAdapter.hide();
concatenateAdapter.addAdapter(footerAdapter);
}
recyclerView.setAdapter(concatenateAdapter);
recyclerView.addItemDecoration(new StickyHeaderDecoration(concatenateAdapter, true, true));
}
private View createInviteActionView(@NonNull InviteCallback inviteCallback) {
View view = LayoutInflater.from(requireContext())
.inflate(R.layout.contact_selection_invite_action_item, (ViewGroup) requireView(), false);
view.setOnClickListener(v -> inviteCallback.onInvite());
return view;
}
private void initializeNoContactsPermission() {
@ -192,7 +226,7 @@ public class ContactSelectionListFragment extends Fragment
public void setQueryFilter(String filter) {
this.cursorFilter = filter;
this.getLoaderManager().restartLoader(0, null, this);
LoaderManager.getInstance(this).restartLoader(0, null, this);
}
public void resetQueryFilter() {
@ -224,9 +258,14 @@ public class ContactSelectionListFragment extends Fragment
swipeRefresh.setVisibility(View.VISIBLE);
showContactsLayout.setVisibility(View.GONE);
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(data);
cursorRecyclerViewAdapter.changeCursor(data);
if (footerAdapter != null) {
footerAdapter.show();
}
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
boolean useFastScroller = (recyclerView.getAdapter().getItemCount() > 20);
boolean useFastScroller = data.getCount() > 20;
recyclerView.setVerticalScrollBarEnabled(!useFastScroller);
if (useFastScroller) {
fastScroller.setVisibility(View.VISIBLE);
@ -239,7 +278,7 @@ public class ContactSelectionListFragment extends Fragment
@Override
public void onLoaderReset(@NonNull Loader<Cursor> loader) {
((CursorRecyclerViewAdapter) recyclerView.getAdapter()).changeCursor(null);
cursorRecyclerViewAdapter.changeCursor(null);
fastScroller.setVisibility(View.GONE);
}
@ -309,4 +348,7 @@ public class ContactSelectionListFragment extends Fragment
void onContactDeselected(String number);
}
public interface InviteCallback {
void onInvite();
}
}

View File

@ -93,7 +93,7 @@ public class MediaDocumentsAdapter extends CursorRecyclerViewAdapter<ViewHolder>
}
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.media_overview_document_item_header, parent, false));
}

View File

@ -35,7 +35,9 @@ import org.thoughtcrime.securesms.recipients.Recipient;
* @author Moxie Marlinspike
*
*/
public class NewConversationActivity extends ContactSelectionActivity {
public class NewConversationActivity extends ContactSelectionActivity
implements ContactSelectionListFragment.InviteCallback
{
@SuppressWarnings("unused")
private static final String TAG = NewConversationActivity.class.getSimpleName();
@ -99,4 +101,9 @@ public class NewConversationActivity extends ContactSelectionActivity {
super.onPrepareOptionsMenu(menu);
return true;
}
@Override
public void onInvite() {
handleInvite();
}
}

View File

@ -37,6 +37,7 @@ import org.thoughtcrime.securesms.components.RecyclerViewFastScroller.FastScroll
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.HeaderViewHolder;
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.logging.Log;
import org.thoughtcrime.securesms.mms.GlideRequests;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration.StickyHeaderAdapter;
import org.thoughtcrime.securesms.util.Util;
@ -53,7 +54,8 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
implements FastScrollAdapter,
StickyHeaderAdapter<HeaderViewHolder>
{
private final static String TAG = ContactSelectionListAdapter.class.getSimpleName();
@SuppressWarnings("unused")
private final static String TAG = Log.tag(ContactSelectionListAdapter.class);
private static final int VIEW_TYPE_CONTACT = 0;
private static final int VIEW_TYPE_DIVIDER = 1;
@ -198,7 +200,7 @@ public class ContactSelectionListAdapter extends CursorRecyclerViewAdapter<ViewH
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.contact_selection_recyclerview_header, parent, false));
}

View File

@ -414,7 +414,7 @@ public class ConversationAdapter <V extends View & BindableConversationItem>
}
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
return new HeaderViewHolder(LayoutInflater.from(getContext()).inflate(R.layout.conversation_item_header, parent, false));
}

View File

@ -94,7 +94,7 @@ class SearchListAdapter extends RecyclerView.Adapter<SearchListAdapter.Search
}
@Override
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent) {
public HeaderViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
return new HeaderViewHolder(LayoutInflater.from(parent.getContext())
.inflate(R.layout.contact_selection_list_divider, parent, false));
}

View File

@ -88,7 +88,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
if (headerHolder == null) {
if (key != StickyHeaderAdapter.NO_HEADER_ID) {
headerHolder = adapter.onCreateHeaderViewHolder(parent );
headerHolder = adapter.onCreateHeaderViewHolder(parent, position);
//noinspection unchecked
adapter.onBindHeaderViewHolder(headerHolder, position);
}
@ -221,7 +221,7 @@ public class StickyHeaderDecoration extends RecyclerView.ItemDecoration {
* @param position position in the adapter
* @return a view holder for the created view
*/
T onCreateHeaderViewHolder(ViewGroup parent);
T onCreateHeaderViewHolder(ViewGroup parent, int position);
/**
* Updates the header view to reflect the header data for the given position.

View File

@ -0,0 +1,67 @@
package org.thoughtcrime.securesms.util.adapter;
import android.view.View;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.Arrays;
import java.util.List;
public final class FixedViewsAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<View> viewList;
private boolean hidden;
public FixedViewsAdapter(@NonNull View... viewList) {
this.viewList = Arrays.asList(viewList);
}
@Override
public int getItemCount() {
return hidden ? 0 : viewList.size();
}
/**
* @return View type is the index.
*/
@Override
public int getItemViewType(int position) {
return position;
}
/**
* @param viewType The index in the list of views.
*/
@Override
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
return new RecyclerView.ViewHolder(viewList.get(viewType)) {
};
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
}
@Override
public long getItemId(int position) {
return position;
}
public void hide() {
setHidden(true);
}
public void show() {
setHidden(false);
}
private void setHidden(boolean hidden) {
if (this.hidden != hidden) {
this.hidden = hidden;
notifyDataSetChanged();
}
}
}

View File

@ -0,0 +1,292 @@
/*
* Copyright (C) 2017 Martijn van der Woude
*
* 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.
*
* Original source: https://github.com/martijnvdwoude/recycler-view-merge-adapter
*
* This file has been modified by Signal.
*/
package org.thoughtcrime.securesms.util.adapter;
import android.util.LongSparseArray;
import android.util.SparseIntArray;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import java.util.LinkedList;
import java.util.List;
public class RecyclerViewConcatenateAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final List<ChildAdapter> adapters = new LinkedList<>();
private long nextUnassignedItemId;
/**
* Map of global view type to local adapter.
* <p>
* Not the same as {@link #adapters}, it may have duplicates and may be in a different order.
*/
private final List<ChildAdapter> viewTypes = new LinkedList<>();
/** Observes a single sub adapter and maps the positions on the events to global positions. */
private static class AdapterDataObserver extends RecyclerView.AdapterDataObserver {
private final RecyclerViewConcatenateAdapter mergeAdapter;
private final RecyclerView.Adapter<RecyclerView.ViewHolder> adapter;
AdapterDataObserver(RecyclerViewConcatenateAdapter mergeAdapter, RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
this.mergeAdapter = mergeAdapter;
this.adapter = adapter;
}
@Override
public void onChanged() {
mergeAdapter.notifyDataSetChanged();
}
@Override
public void onItemRangeChanged(int positionStart, int itemCount) {
int subAdapterOffset = mergeAdapter.getSubAdapterFirstGlobalPosition(adapter);
mergeAdapter.notifyItemRangeChanged(subAdapterOffset + positionStart, itemCount);
}
@Override
public void onItemRangeInserted(int positionStart, int itemCount) {
int subAdapterOffset = mergeAdapter.getSubAdapterFirstGlobalPosition(adapter);
mergeAdapter.notifyItemRangeInserted(subAdapterOffset + positionStart, itemCount);
}
@Override
public void onItemRangeRemoved(int positionStart, int itemCount) {
int subAdapterOffset = mergeAdapter.getSubAdapterFirstGlobalPosition(adapter);
mergeAdapter.notifyItemRangeRemoved(subAdapterOffset + positionStart, itemCount);
}
}
private static class ChildAdapter {
final RecyclerView.Adapter<RecyclerView.ViewHolder> adapter;
/** Map of global view types to local view types */
private final SparseIntArray globalViewTypesMap = new SparseIntArray();
/** Map of local view types to global view types */
private final SparseIntArray localViewTypesMap = new SparseIntArray();
private final AdapterDataObserver adapterDataObserver;
/** Map of local ids to global ids. */
private final LongSparseArray<Long> localItemIdMap = new LongSparseArray<>();
ChildAdapter(@NonNull RecyclerView.Adapter<RecyclerView.ViewHolder> adapter, @NonNull AdapterDataObserver adapterDataObserver) {
this.adapter = adapter;
this.adapterDataObserver = adapterDataObserver;
this.adapter.registerAdapterDataObserver(this.adapterDataObserver);
}
int getGlobalItemViewType(int localPosition, int defaultValue) {
int localViewType = adapter.getItemViewType(localPosition);
int globalViewType = localViewTypesMap.get(localViewType, defaultValue);
if (globalViewType == defaultValue) {
globalViewTypesMap.append(globalViewType, localViewType);
localViewTypesMap.append(localViewType, globalViewType);
}
return globalViewType;
}
long getGlobalItemId(int localPosition, long defaultGlobalValue) {
final long localItemId = adapter.getItemId(localPosition);
if (RecyclerView.NO_ID == localItemId) {
return RecyclerView.NO_ID;
}
final Long globalItemId = localItemIdMap.get(localItemId);
if (globalItemId == null) {
localItemIdMap.put(localItemId, defaultGlobalValue);
return defaultGlobalValue;
}
return globalItemId;
}
void unregister() {
adapter.unregisterAdapterDataObserver(adapterDataObserver);
}
RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int globalViewType) {
int localViewType = globalViewTypesMap.get(globalViewType);
return adapter.onCreateViewHolder(viewGroup, localViewType);
}
}
static class ChildAdapterPositionPair {
final ChildAdapter childAdapter;
final int localPosition;
ChildAdapterPositionPair(@NonNull ChildAdapter adapter, int position) {
childAdapter = adapter;
localPosition = position;
}
RecyclerView.Adapter<RecyclerView.ViewHolder> getAdapter() {
return childAdapter.adapter;
}
}
/**
* @param adapter Append an adapter to the list of adapters.
*/
public void addAdapter(@NonNull RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
addAdapter(adapters.size(), adapter);
}
/**
* @param index The index at which to add an adapter to the list of adapters.
* @param adapter The adapter to add.
*/
public void addAdapter(int index, @NonNull RecyclerView.Adapter<RecyclerView.ViewHolder> adapter) {
AdapterDataObserver adapterDataObserver = new AdapterDataObserver(this, adapter);
adapters.add(index, new ChildAdapter(adapter, adapterDataObserver));
notifyDataSetChanged();
}
/**
* Clear all adapters from the list of adapters.
*/
public void clearAdapters() {
for (ChildAdapter childAdapter : adapters) {
childAdapter.unregister();
}
adapters.clear();
notifyDataSetChanged();
}
/**
* Return a childAdapterPositionPair object for a given global position.
*
* @param globalPosition The global position in the entire set of items.
* @return A childAdapterPositionPair object containing a reference to the adapter and the local
* position in that adapter that corresponds to the given global position.
*/
@NonNull
ChildAdapterPositionPair getLocalPosition(final int globalPosition) {
int count = 0;
for (ChildAdapter childAdapter : adapters) {
int newCount = count + childAdapter.adapter.getItemCount();
if (globalPosition < newCount) {
return new ChildAdapterPositionPair(childAdapter, globalPosition - count);
}
count = newCount;
}
throw new AssertionError("Position out of range");
}
@Override
@NonNull
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int viewType) {
ChildAdapter childAdapter = viewTypes.get(viewType);
if (childAdapter == null) {
throw new AssertionError("Unknown view type");
}
return childAdapter.onCreateViewHolder(viewGroup, viewType);
}
/**
* Return the first global position in the entire set of items for a given adapter.
*
* @param adapter The adapter for which to the return the first global position.
* @return The first global position for the given adapter, or -1 if no such position could be found.
*/
private int getSubAdapterFirstGlobalPosition(@NonNull RecyclerView.Adapter adapter) {
int count = 0;
for (ChildAdapter childAdapterWrapper : adapters) {
RecyclerView.Adapter childAdapter = childAdapterWrapper.adapter;
if (childAdapter == adapter) {
return count;
}
count += childAdapter.getItemCount();
}
throw new AssertionError("Adapter not found in list of adapters");
}
@Override
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
ChildAdapterPositionPair childAdapterPositionPair = getLocalPosition(position);
RecyclerView.Adapter adapter = childAdapterPositionPair.getAdapter();
//noinspection unchecked
adapter.onBindViewHolder(viewHolder, childAdapterPositionPair.localPosition);
}
@Override
public int getItemViewType(int position) {
int nextUnassignedViewType = viewTypes.size();
ChildAdapterPositionPair localPosition = getLocalPosition(position);
int viewType = localPosition.childAdapter.getGlobalItemViewType(localPosition.localPosition, nextUnassignedViewType);
if (viewType == nextUnassignedViewType) {
viewTypes.add(viewType, localPosition.childAdapter);
}
return viewType;
}
@Override
public long getItemId(int position) {
ChildAdapterPositionPair localPosition = getLocalPosition(position);
long itemId = localPosition.childAdapter.getGlobalItemId(localPosition.localPosition, nextUnassignedItemId);
if (itemId == nextUnassignedItemId) {
nextUnassignedItemId++;
}
return itemId;
}
@Override
public int getItemCount() {
int count = 0;
for (ChildAdapter adapter : adapters) {
count += adapter.adapter.getItemCount();
}
return count;
}
}

View File

@ -0,0 +1,61 @@
package org.thoughtcrime.securesms.util.adapter;
import android.view.ViewGroup;
import androidx.recyclerview.widget.RecyclerView;
import org.thoughtcrime.securesms.components.RecyclerViewFastScroller;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
import org.whispersystems.libsignal.util.Pair;
import org.whispersystems.libsignal.util.guava.Optional;
public final class RecyclerViewConcatenateAdapterStickyHeader extends RecyclerViewConcatenateAdapter
implements StickyHeaderDecoration.StickyHeaderAdapter,
RecyclerViewFastScroller.FastScrollAdapter
{
@Override
public long getHeaderId(int position) {
return getForPosition(position).transform(p -> p.first().getHeaderId(p.second())).or(-1L);
}
@Override
public RecyclerView.ViewHolder onCreateHeaderViewHolder(ViewGroup parent, int position) {
return getForPosition(position).transform(p -> p.first().onCreateHeaderViewHolder(parent, p.second())).orNull();
}
@Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
Optional<Pair<StickyHeaderDecoration.StickyHeaderAdapter, Integer>> forPosition = getForPosition(position);
if (forPosition.isPresent()) {
Pair<StickyHeaderDecoration.StickyHeaderAdapter, Integer> stickyHeaderAdapterIntegerPair = forPosition.get();
//noinspection unchecked
stickyHeaderAdapterIntegerPair.first().onBindHeaderViewHolder(viewHolder, stickyHeaderAdapterIntegerPair.second());
}
}
@Override
public CharSequence getBubbleText(int position) {
Optional<Pair<StickyHeaderDecoration.StickyHeaderAdapter, Integer>> forPosition = getForPosition(position);
return forPosition.transform(a -> {
if (a.first() instanceof RecyclerViewFastScroller.FastScrollAdapter) {
return ((RecyclerViewFastScroller.FastScrollAdapter) a.first()).getBubbleText(a.second());
} else {
return "";
}
}).or("");
}
private Optional<Pair<StickyHeaderDecoration.StickyHeaderAdapter, Integer>> getForPosition(int position) {
ChildAdapterPositionPair localAdapterPosition = getLocalPosition(position);
RecyclerView.Adapter<? extends RecyclerView.ViewHolder> adapter = localAdapterPosition.getAdapter();
if (adapter instanceof StickyHeaderDecoration.StickyHeaderAdapter) {
StickyHeaderDecoration.StickyHeaderAdapter sticky = (StickyHeaderDecoration.StickyHeaderAdapter) adapter;
return Optional.of(new Pair<>(sticky, localAdapterPosition.localPosition));
}
return Optional.absent();
}
}