mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-17 13:19:52 +01:00
Migrate GroupListController to a ViewModel
Use ListAdapter to calculate list diffs on a background thread
This commit is contained in:
@@ -31,6 +31,7 @@ import org.briarproject.briar.android.account.LockManagerImpl;
|
|||||||
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
|
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
|
||||||
import org.briarproject.briar.android.login.LoginModule;
|
import org.briarproject.briar.android.login.LoginModule;
|
||||||
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
import org.briarproject.briar.android.navdrawer.NavDrawerModule;
|
||||||
|
import org.briarproject.briar.android.privategroup.list.GroupListModule;
|
||||||
import org.briarproject.briar.android.reporting.DevReportModule;
|
import org.briarproject.briar.android.reporting.DevReportModule;
|
||||||
import org.briarproject.briar.android.viewmodel.ViewModelModule;
|
import org.briarproject.briar.android.viewmodel.ViewModelModule;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
@@ -65,7 +66,9 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
|
|||||||
LoginModule.class,
|
LoginModule.class,
|
||||||
NavDrawerModule.class,
|
NavDrawerModule.class,
|
||||||
ViewModelModule.class,
|
ViewModelModule.class,
|
||||||
DevReportModule.class
|
DevReportModule.class,
|
||||||
|
// below need to be within same scope as ViewModelProvider.Factory
|
||||||
|
GroupListModule.class,
|
||||||
})
|
})
|
||||||
public class AppModule {
|
public class AppModule {
|
||||||
|
|
||||||
|
|||||||
@@ -60,7 +60,6 @@ import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment;
|
|||||||
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
|
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
|
||||||
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationModule;
|
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationModule;
|
||||||
import org.briarproject.briar.android.privategroup.list.GroupListFragment;
|
import org.briarproject.briar.android.privategroup.list.GroupListFragment;
|
||||||
import org.briarproject.briar.android.privategroup.list.GroupListModule;
|
|
||||||
import org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity;
|
import org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity;
|
||||||
import org.briarproject.briar.android.privategroup.memberlist.GroupMemberModule;
|
import org.briarproject.briar.android.privategroup.memberlist.GroupMemberModule;
|
||||||
import org.briarproject.briar.android.privategroup.reveal.GroupRevealModule;
|
import org.briarproject.briar.android.privategroup.reveal.GroupRevealModule;
|
||||||
@@ -94,7 +93,6 @@ import dagger.Component;
|
|||||||
ForumModule.class,
|
ForumModule.class,
|
||||||
GroupInvitationModule.class,
|
GroupInvitationModule.class,
|
||||||
GroupConversationModule.class,
|
GroupConversationModule.class,
|
||||||
GroupListModule.class,
|
|
||||||
GroupMemberModule.class,
|
GroupMemberModule.class,
|
||||||
GroupRevealModule.class,
|
GroupRevealModule.class,
|
||||||
SharingModule.class
|
SharingModule.class
|
||||||
|
|||||||
@@ -8,9 +8,11 @@ import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
|||||||
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
||||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||||
|
|
||||||
|
import androidx.annotation.Nullable;
|
||||||
|
|
||||||
// This class is not thread-safe
|
// This class is not thread-safe
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
class GroupItem {
|
class GroupItem implements Comparable<GroupItem> {
|
||||||
|
|
||||||
private final PrivateGroup privateGroup;
|
private final PrivateGroup privateGroup;
|
||||||
private final AuthorInfo authorInfo;
|
private final AuthorInfo authorInfo;
|
||||||
@@ -28,6 +30,15 @@ class GroupItem {
|
|||||||
this.dissolved = dissolved;
|
this.dissolved = dissolved;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
GroupItem(GroupItem item) {
|
||||||
|
this.privateGroup = item.privateGroup;
|
||||||
|
this.authorInfo = item.authorInfo;
|
||||||
|
this.messageCount = item.messageCount;
|
||||||
|
this.unreadCount = item.unreadCount;
|
||||||
|
this.timestamp = item.timestamp;
|
||||||
|
this.dissolved = item.dissolved;
|
||||||
|
}
|
||||||
|
|
||||||
void addMessageHeader(GroupMessageHeader header) {
|
void addMessageHeader(GroupMessageHeader header) {
|
||||||
messageCount++;
|
messageCount++;
|
||||||
if (header.getTimestamp() > timestamp) {
|
if (header.getTimestamp() > timestamp) {
|
||||||
@@ -38,10 +49,6 @@ class GroupItem {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
PrivateGroup getPrivateGroup() {
|
|
||||||
return privateGroup;
|
|
||||||
}
|
|
||||||
|
|
||||||
GroupId getId() {
|
GroupId getId() {
|
||||||
return privateGroup.getId();
|
return privateGroup.getId();
|
||||||
}
|
}
|
||||||
@@ -82,4 +89,22 @@ class GroupItem {
|
|||||||
dissolved = true;
|
dissolved = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(@Nullable Object o) {
|
||||||
|
return o instanceof GroupItem &&
|
||||||
|
getId().equals(((GroupItem) o).getId());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(GroupItem o) {
|
||||||
|
if (this == o) return 0;
|
||||||
|
// The group with the latest message comes first
|
||||||
|
long aTime = getTimestamp(), bTime = o.getTimestamp();
|
||||||
|
if (aTime > bTime) return -1;
|
||||||
|
if (aTime < bTime) return 1;
|
||||||
|
// Break ties by group name
|
||||||
|
String aName = getName();
|
||||||
|
String bName = o.getName();
|
||||||
|
return String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,81 +1,52 @@
|
|||||||
package org.briarproject.briar.android.privategroup.list;
|
package org.briarproject.briar.android.privategroup.list;
|
||||||
|
|
||||||
import android.content.Context;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
|
import org.briarproject.briar.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
|
||||||
import org.briarproject.briar.android.util.BriarAdapter;
|
|
||||||
|
|
||||||
import static androidx.recyclerview.widget.SortedList.INVALID_POSITION;
|
import androidx.recyclerview.widget.DiffUtil.ItemCallback;
|
||||||
|
import androidx.recyclerview.widget.ListAdapter;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class GroupListAdapter extends BriarAdapter<GroupItem, GroupViewHolder> {
|
class GroupListAdapter extends ListAdapter<GroupItem, GroupViewHolder> {
|
||||||
|
|
||||||
private final OnGroupRemoveClickListener listener;
|
private final OnGroupRemoveClickListener listener;
|
||||||
|
|
||||||
GroupListAdapter(Context ctx, OnGroupRemoveClickListener listener) {
|
GroupListAdapter(OnGroupRemoveClickListener listener) {
|
||||||
super(ctx, GroupItem.class);
|
super(new GroupItemCallback());
|
||||||
this.listener = listener;
|
this.listener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GroupViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
public GroupViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
View v = LayoutInflater.from(ctx).inflate(
|
View v = LayoutInflater.from(parent.getContext())
|
||||||
R.layout.list_item_group, parent, false);
|
.inflate(R.layout.list_item_group, parent, false);
|
||||||
return new GroupViewHolder(v);
|
return new GroupViewHolder(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBindViewHolder(GroupViewHolder ui, int position) {
|
public void onBindViewHolder(GroupViewHolder ui, int position) {
|
||||||
ui.bindView(ctx, items.get(position), listener);
|
ui.bindView(getItem(position), listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private static class GroupItemCallback extends ItemCallback<GroupItem> {
|
||||||
public int compare(GroupItem a, GroupItem b) {
|
@Override
|
||||||
if (a == b) return 0;
|
public boolean areItemsTheSame(GroupItem a, GroupItem b) {
|
||||||
// The group with the latest message comes first
|
return a.equals(b);
|
||||||
long aTime = a.getTimestamp(), bTime = b.getTimestamp();
|
|
||||||
if (aTime > bTime) return -1;
|
|
||||||
if (aTime < bTime) return 1;
|
|
||||||
// Break ties by group name
|
|
||||||
String aName = a.getName();
|
|
||||||
String bName = b.getName();
|
|
||||||
return String.CASE_INSENSITIVE_ORDER.compare(aName, bName);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areContentsTheSame(GroupItem a, GroupItem b) {
|
|
||||||
return a.getMessageCount() == b.getMessageCount() &&
|
|
||||||
a.getTimestamp() == b.getTimestamp() &&
|
|
||||||
a.getUnreadCount() == b.getUnreadCount() &&
|
|
||||||
a.isDissolved() == b.isDissolved();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean areItemsTheSame(GroupItem a, GroupItem b) {
|
|
||||||
return a.getId().equals(b.getId());
|
|
||||||
}
|
|
||||||
|
|
||||||
int findItemPosition(GroupId g) {
|
|
||||||
for (int i = 0; i < items.size(); i++) {
|
|
||||||
GroupItem item = items.get(i);
|
|
||||||
if (item.getId().equals(g)) {
|
|
||||||
return i;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return INVALID_POSITION;
|
|
||||||
}
|
|
||||||
|
|
||||||
void removeItem(GroupId groupId) {
|
@Override
|
||||||
int pos = findItemPosition(groupId);
|
public boolean areContentsTheSame(GroupItem a, GroupItem b) {
|
||||||
if (pos != INVALID_POSITION) items.removeItemAt(pos);
|
return a.getMessageCount() == b.getMessageCount() &&
|
||||||
|
a.getTimestamp() == b.getTimestamp() &&
|
||||||
|
a.getUnreadCount() == b.getUnreadCount() &&
|
||||||
|
a.isDissolved() == b.isDissolved();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,60 +0,0 @@
|
|||||||
package org.briarproject.briar.android.privategroup.list;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
|
||||||
import org.briarproject.briar.android.controller.DbController;
|
|
||||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
|
||||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
|
||||||
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import androidx.annotation.UiThread;
|
|
||||||
|
|
||||||
@NotNullByDefault
|
|
||||||
interface GroupListController extends DbController {
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The listener must be set right after the controller was injected
|
|
||||||
*/
|
|
||||||
@UiThread
|
|
||||||
void setGroupListListener(GroupListListener listener);
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void unsetGroupListListener(GroupListListener listener);
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void onStart();
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void onStop();
|
|
||||||
|
|
||||||
void loadGroups(
|
|
||||||
ResultExceptionHandler<Collection<GroupItem>, DbException> result);
|
|
||||||
|
|
||||||
void removeGroup(GroupId g, ExceptionHandler<DbException> result);
|
|
||||||
|
|
||||||
void loadAvailableGroups(
|
|
||||||
ResultExceptionHandler<Integer, DbException> result);
|
|
||||||
|
|
||||||
interface GroupListListener {
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void onGroupMessageAdded(GroupMessageHeader header);
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void onGroupInvitationReceived();
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void onGroupAdded(GroupId groupId);
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void onGroupRemoved(GroupId groupId);
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void onGroupDissolved(GroupId groupId);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -15,54 +15,50 @@ import com.google.android.material.snackbar.Snackbar;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.controller.handler.UiExceptionHandler;
|
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
|
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
|
||||||
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
|
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
|
||||||
import org.briarproject.briar.android.privategroup.list.GroupListController.GroupListListener;
|
|
||||||
import org.briarproject.briar.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
|
import org.briarproject.briar.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
|
||||||
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
import org.briarproject.briar.android.util.BriarSnackbarBuilder;
|
||||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.UiThread;
|
import androidx.annotation.UiThread;
|
||||||
|
import androidx.lifecycle.ViewModelProvider;
|
||||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||||
|
|
||||||
import static com.google.android.material.snackbar.Snackbar.LENGTH_INDEFINITE;
|
import static com.google.android.material.snackbar.Snackbar.LENGTH_INDEFINITE;
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class GroupListFragment extends BaseFragment implements
|
public class GroupListFragment extends BaseFragment implements
|
||||||
GroupListListener, OnGroupRemoveClickListener, OnClickListener {
|
OnGroupRemoveClickListener, OnClickListener {
|
||||||
|
|
||||||
public final static String TAG = GroupListFragment.class.getName();
|
public final static String TAG = GroupListFragment.class.getName();
|
||||||
private static final Logger LOG = Logger.getLogger(TAG);
|
|
||||||
|
|
||||||
public static GroupListFragment newInstance() {
|
public static GroupListFragment newInstance() {
|
||||||
return new GroupListFragment();
|
return new GroupListFragment();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GroupListController controller;
|
ViewModelProvider.Factory viewModelFactory;
|
||||||
|
|
||||||
|
private GroupListViewModel viewModel;
|
||||||
private BriarRecyclerView list;
|
private BriarRecyclerView list;
|
||||||
private GroupListAdapter adapter;
|
private GroupListAdapter adapter;
|
||||||
private Snackbar snackbar;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectFragment(ActivityComponent component) {
|
public void injectFragment(ActivityComponent component) {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
controller.setGroupListListener(this);
|
viewModel = new ViewModelProvider(this, viewModelFactory)
|
||||||
|
.get(GroupListViewModel.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
@@ -75,17 +71,35 @@ public class GroupListFragment extends BaseFragment implements
|
|||||||
|
|
||||||
View v = inflater.inflate(R.layout.list, container, false);
|
View v = inflater.inflate(R.layout.list, container, false);
|
||||||
|
|
||||||
adapter = new GroupListAdapter(getActivity(), this);
|
adapter = new GroupListAdapter(this);
|
||||||
list = v.findViewById(R.id.list);
|
list = v.findViewById(R.id.list);
|
||||||
list.setEmptyImage(R.drawable.ic_empty_state_group_list);
|
list.setEmptyImage(R.drawable.ic_empty_state_group_list);
|
||||||
list.setEmptyText(R.string.groups_list_empty);
|
list.setEmptyText(R.string.groups_list_empty);
|
||||||
list.setEmptyAction(R.string.groups_list_empty_action);
|
list.setEmptyAction(R.string.groups_list_empty_action);
|
||||||
list.setLayoutManager(new LinearLayoutManager(getContext()));
|
list.setLayoutManager(new LinearLayoutManager(getContext()));
|
||||||
list.setAdapter(adapter);
|
list.setAdapter(adapter);
|
||||||
|
viewModel.getGroupItems().observe(getViewLifecycleOwner(), result -> {
|
||||||
|
List<GroupItem> items = result.getResultOrNull();
|
||||||
|
if (items == null && result.getException() instanceof DbException) {
|
||||||
|
handleDbException((DbException) result.getException());
|
||||||
|
} else {
|
||||||
|
adapter.submitList(items);
|
||||||
|
if (requireNonNull(items).size() == 0) list.showData();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
snackbar = new BriarSnackbarBuilder()
|
Snackbar snackbar = new BriarSnackbarBuilder()
|
||||||
.setAction(R.string.show, this)
|
.setAction(R.string.show, this)
|
||||||
.make(list, "", LENGTH_INDEFINITE);
|
.make(list, "", LENGTH_INDEFINITE);
|
||||||
|
viewModel.getNumInvitations().observe(getViewLifecycleOwner(), num -> {
|
||||||
|
if (num == 0) {
|
||||||
|
snackbar.dismiss();
|
||||||
|
} else {
|
||||||
|
snackbar.setText(getResources().getQuantityString(
|
||||||
|
R.plurals.groups_invitations_open, num, num));
|
||||||
|
if (!snackbar.isShownOrQueued()) snackbar.show();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
@@ -93,25 +107,22 @@ public class GroupListFragment extends BaseFragment implements
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
controller.onStart();
|
// TODO should we block all group message notifications as well?
|
||||||
|
viewModel.clearAllGroupMessageNotifications();
|
||||||
|
// The attributes and sorting of the groups may have changed while we
|
||||||
|
// were stopped and we have no way finding out about them, so re-load
|
||||||
|
// e.g. less unread messages in a group after viewing it.
|
||||||
|
viewModel.loadGroups();
|
||||||
|
// The number of invitations might have changed while we were stopped
|
||||||
|
// e.g. because of accepting an invitation which does not trigger event
|
||||||
|
viewModel.loadNumInvitations();
|
||||||
list.startPeriodicUpdate();
|
list.startPeriodicUpdate();
|
||||||
loadGroups();
|
|
||||||
loadAvailableGroups();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
controller.onStop();
|
|
||||||
list.stopPeriodicUpdate();
|
list.stopPeriodicUpdate();
|
||||||
adapter.clear();
|
|
||||||
list.showProgressBar();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onDestroy() {
|
|
||||||
super.onDestroy();
|
|
||||||
controller.unsetGroupListListener(this);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -122,68 +133,18 @@ public class GroupListFragment extends BaseFragment implements
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(MenuItem item) {
|
public boolean onOptionsItemSelected(MenuItem item) {
|
||||||
switch (item.getItemId()) {
|
if (item.getItemId() == R.id.action_add_group) {
|
||||||
case R.id.action_add_group:
|
Intent i = new Intent(getContext(), CreateGroupActivity.class);
|
||||||
Intent i = new Intent(getContext(), CreateGroupActivity.class);
|
startActivity(i);
|
||||||
startActivity(i);
|
return true;
|
||||||
return true;
|
|
||||||
default:
|
|
||||||
return super.onOptionsItemSelected(item);
|
|
||||||
}
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@Override
|
@Override
|
||||||
public void onGroupRemoveClick(GroupItem item) {
|
public void onGroupRemoveClick(GroupItem item) {
|
||||||
controller.removeGroup(item.getId(),
|
viewModel.removeGroup(item.getId());
|
||||||
new UiExceptionHandler<DbException>(this) {
|
|
||||||
// result handled by GroupRemovedEvent and onGroupRemoved()
|
|
||||||
@Override
|
|
||||||
public void onExceptionUi(DbException exception) {
|
|
||||||
handleDbException(exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
@Override
|
|
||||||
public void onGroupMessageAdded(GroupMessageHeader header) {
|
|
||||||
adapter.incrementRevision();
|
|
||||||
int position = adapter.findItemPosition(header.getGroupId());
|
|
||||||
GroupItem item = adapter.getItemAt(position);
|
|
||||||
if (item != null) {
|
|
||||||
item.addMessageHeader(header);
|
|
||||||
adapter.updateItemAt(position, item);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGroupInvitationReceived() {
|
|
||||||
loadAvailableGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
@Override
|
|
||||||
public void onGroupAdded(GroupId groupId) {
|
|
||||||
loadGroups();
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
|
||||||
@Override
|
|
||||||
public void onGroupRemoved(GroupId groupId) {
|
|
||||||
adapter.incrementRevision();
|
|
||||||
adapter.removeItem(groupId);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onGroupDissolved(GroupId groupId) {
|
|
||||||
adapter.incrementRevision();
|
|
||||||
int position = adapter.findItemPosition(groupId);
|
|
||||||
GroupItem item = adapter.getItemAt(position);
|
|
||||||
if (item != null) {
|
|
||||||
item.setDissolved();
|
|
||||||
adapter.updateItemAt(position, item);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -191,57 +152,13 @@ public class GroupListFragment extends BaseFragment implements
|
|||||||
return TAG;
|
return TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadGroups() {
|
|
||||||
int revision = adapter.getRevision();
|
|
||||||
controller.loadGroups(
|
|
||||||
new UiResultExceptionHandler<Collection<GroupItem>, DbException>(
|
|
||||||
this) {
|
|
||||||
@Override
|
|
||||||
public void onResultUi(Collection<GroupItem> groups) {
|
|
||||||
if (revision == adapter.getRevision()) {
|
|
||||||
adapter.incrementRevision();
|
|
||||||
if (groups.isEmpty()) list.showData();
|
|
||||||
else adapter.replaceAll(groups);
|
|
||||||
} else {
|
|
||||||
LOG.info("Concurrent update, reloading");
|
|
||||||
loadGroups();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExceptionUi(DbException exception) {
|
|
||||||
handleDbException(exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
private void loadAvailableGroups() {
|
|
||||||
controller.loadAvailableGroups(
|
|
||||||
new UiResultExceptionHandler<Integer, DbException>(this) {
|
|
||||||
@Override
|
|
||||||
public void onResultUi(Integer num) {
|
|
||||||
if (num == 0) {
|
|
||||||
snackbar.dismiss();
|
|
||||||
} else {
|
|
||||||
snackbar.setText(getResources().getQuantityString(
|
|
||||||
R.plurals.groups_invitations_open, num,
|
|
||||||
num));
|
|
||||||
if (!snackbar.isShownOrQueued()) snackbar.show();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExceptionUi(DbException exception) {
|
|
||||||
handleDbException(exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method is handling the available groups snackbar action
|
* This method is handling the available groups snackbar action
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
|
// The snackbar dismisses itself when this is called
|
||||||
|
// and does not come back until the fragment gets recreated.
|
||||||
Intent i = new Intent(getContext(), GroupInvitationActivity.class);
|
Intent i = new Intent(getContext(), GroupInvitationActivity.class);
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,17 +1,18 @@
|
|||||||
package org.briarproject.briar.android.privategroup.list;
|
package org.briarproject.briar.android.privategroup.list;
|
||||||
|
|
||||||
import org.briarproject.briar.android.activity.ActivityScope;
|
import org.briarproject.briar.android.viewmodel.ViewModelKey;
|
||||||
|
|
||||||
|
import androidx.lifecycle.ViewModel;
|
||||||
|
import dagger.Binds;
|
||||||
import dagger.Module;
|
import dagger.Module;
|
||||||
import dagger.Provides;
|
import dagger.multibindings.IntoMap;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class GroupListModule {
|
public abstract class GroupListModule {
|
||||||
|
|
||||||
@ActivityScope
|
@Binds
|
||||||
@Provides
|
@IntoMap
|
||||||
GroupListController provideGroupListController(
|
@ViewModelKey(GroupListViewModel.class)
|
||||||
GroupListControllerImpl groupListController) {
|
abstract ViewModel bindGroupListViewModel(
|
||||||
return groupListController;
|
GroupListViewModel groupListViewModel);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.briarproject.briar.android.privategroup.list;
|
package org.briarproject.briar.android.privategroup.list;
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactManager;
|
import org.briarproject.bramble.api.contact.ContactManager;
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -18,11 +20,11 @@ import org.briarproject.bramble.api.sync.ClientId;
|
|||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.bramble.api.sync.event.GroupAddedEvent;
|
import org.briarproject.bramble.api.sync.event.GroupAddedEvent;
|
||||||
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
|
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
|
||||||
import org.briarproject.briar.android.controller.DbControllerImpl;
|
import org.briarproject.briar.android.viewmodel.DbViewModel;
|
||||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
import org.briarproject.briar.android.viewmodel.LiveResult;
|
||||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||||
|
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
||||||
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
import org.briarproject.briar.api.privategroup.PrivateGroup;
|
||||||
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
import org.briarproject.briar.api.privategroup.PrivateGroupManager;
|
||||||
import org.briarproject.briar.api.privategroup.event.GroupDissolvedEvent;
|
import org.briarproject.briar.api.privategroup.event.GroupDissolvedEvent;
|
||||||
@@ -32,6 +34,7 @@ import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
@@ -40,10 +43,13 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import androidx.annotation.CallSuper;
|
import androidx.annotation.UiThread;
|
||||||
import androidx.annotation.Nullable;
|
import androidx.lifecycle.LiveData;
|
||||||
|
import androidx.lifecycle.MutableLiveData;
|
||||||
|
|
||||||
|
import static java.util.Objects.requireNonNull;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static java.util.logging.Logger.getLogger;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
import static org.briarproject.bramble.util.LogUtils.logDuration;
|
||||||
import static org.briarproject.bramble.util.LogUtils.logException;
|
import static org.briarproject.bramble.util.LogUtils.logException;
|
||||||
import static org.briarproject.bramble.util.LogUtils.now;
|
import static org.briarproject.bramble.util.LogUtils.now;
|
||||||
@@ -51,113 +57,86 @@ import static org.briarproject.briar.api.privategroup.PrivateGroupManager.CLIENT
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
class GroupListControllerImpl extends DbControllerImpl
|
class GroupListViewModel extends DbViewModel implements EventListener {
|
||||||
implements GroupListController, EventListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(GroupListControllerImpl.class.getName());
|
getLogger(GroupListViewModel.class.getName());
|
||||||
|
|
||||||
private final TransactionManager db;
|
|
||||||
private final PrivateGroupManager groupManager;
|
private final PrivateGroupManager groupManager;
|
||||||
private final GroupInvitationManager groupInvitationManager;
|
private final GroupInvitationManager groupInvitationManager;
|
||||||
private final ContactManager contactManager;
|
private final ContactManager contactManager;
|
||||||
private final AndroidNotificationManager notificationManager;
|
private final AndroidNotificationManager notificationManager;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
|
|
||||||
// UI thread
|
private final MutableLiveData<LiveResult<List<GroupItem>>> groupItems =
|
||||||
@Nullable
|
new MutableLiveData<>();
|
||||||
private GroupListListener listener;
|
private final MutableLiveData<Integer> numInvitations =
|
||||||
|
new MutableLiveData<>();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
GroupListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
GroupListViewModel(Application application,
|
||||||
|
@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager,
|
||||||
TransactionManager db,
|
TransactionManager db,
|
||||||
PrivateGroupManager groupManager,
|
PrivateGroupManager groupManager,
|
||||||
GroupInvitationManager groupInvitationManager,
|
GroupInvitationManager groupInvitationManager,
|
||||||
ContactManager contactManager,
|
ContactManager contactManager,
|
||||||
AndroidNotificationManager notificationManager, EventBus eventBus) {
|
AndroidNotificationManager notificationManager, EventBus eventBus) {
|
||||||
super(dbExecutor, lifecycleManager);
|
super(application, dbExecutor, lifecycleManager, db);
|
||||||
this.db = db;
|
|
||||||
this.groupManager = groupManager;
|
this.groupManager = groupManager;
|
||||||
this.groupInvitationManager = groupInvitationManager;
|
this.groupInvitationManager = groupInvitationManager;
|
||||||
this.contactManager = contactManager;
|
this.contactManager = contactManager;
|
||||||
this.notificationManager = notificationManager;
|
this.notificationManager = notificationManager;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
|
this.eventBus.addListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setGroupListListener(GroupListListener listener) {
|
protected void onCleared() {
|
||||||
this.listener = listener;
|
super.onCleared();
|
||||||
|
eventBus.removeListener(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void clearAllGroupMessageNotifications() {
|
||||||
public void unsetGroupListListener(GroupListListener listener) {
|
|
||||||
if (this.listener == listener) this.listener = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@CallSuper
|
|
||||||
public void onStart() {
|
|
||||||
if (listener == null) throw new IllegalStateException();
|
|
||||||
eventBus.addListener(this);
|
|
||||||
notificationManager.clearAllGroupMessageNotifications();
|
notificationManager.clearAllGroupMessageNotifications();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@CallSuper
|
|
||||||
public void onStop() {
|
|
||||||
eventBus.removeListener(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@CallSuper
|
|
||||||
public void eventOccurred(Event e) {
|
public void eventOccurred(Event e) {
|
||||||
if (listener == null) throw new IllegalStateException();
|
|
||||||
if (e instanceof GroupMessageAddedEvent) {
|
if (e instanceof GroupMessageAddedEvent) {
|
||||||
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
GroupMessageAddedEvent g = (GroupMessageAddedEvent) e;
|
||||||
LOG.info("Private group message added");
|
LOG.info("Private group message added");
|
||||||
listener.onGroupMessageAdded(g.getHeader());
|
onGroupMessageAdded(g.getHeader());
|
||||||
} else if (e instanceof GroupInvitationRequestReceivedEvent) {
|
} else if (e instanceof GroupInvitationRequestReceivedEvent) {
|
||||||
LOG.info("Private group invitation received");
|
LOG.info("Private group invitation received");
|
||||||
listener.onGroupInvitationReceived();
|
loadNumInvitations();
|
||||||
} else if (e instanceof GroupAddedEvent) {
|
} else if (e instanceof GroupAddedEvent) {
|
||||||
GroupAddedEvent g = (GroupAddedEvent) e;
|
GroupAddedEvent g = (GroupAddedEvent) e;
|
||||||
ClientId id = g.getGroup().getClientId();
|
ClientId id = g.getGroup().getClientId();
|
||||||
if (id.equals(CLIENT_ID)) {
|
if (id.equals(CLIENT_ID)) {
|
||||||
LOG.info("Private group added");
|
LOG.info("Private group added");
|
||||||
listener.onGroupAdded(g.getGroup().getId());
|
loadGroups();
|
||||||
}
|
}
|
||||||
} else if (e instanceof GroupRemovedEvent) {
|
} else if (e instanceof GroupRemovedEvent) {
|
||||||
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
GroupRemovedEvent g = (GroupRemovedEvent) e;
|
||||||
ClientId id = g.getGroup().getClientId();
|
ClientId id = g.getGroup().getClientId();
|
||||||
if (id.equals(CLIENT_ID)) {
|
if (id.equals(CLIENT_ID)) {
|
||||||
LOG.info("Private group removed");
|
LOG.info("Private group removed");
|
||||||
listener.onGroupRemoved(g.getGroup().getId());
|
onGroupRemoved(g.getGroup().getId());
|
||||||
}
|
}
|
||||||
} else if (e instanceof GroupDissolvedEvent) {
|
} else if (e instanceof GroupDissolvedEvent) {
|
||||||
GroupDissolvedEvent g = (GroupDissolvedEvent) e;
|
GroupDissolvedEvent g = (GroupDissolvedEvent) e;
|
||||||
LOG.info("Private group dissolved");
|
LOG.info("Private group dissolved");
|
||||||
listener.onGroupDissolved(g.getGroupId());
|
onGroupDissolved(g.getGroupId());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void loadGroups() {
|
||||||
public void loadGroups(
|
loadList(this::loadGroups, groupItems::setValue);
|
||||||
ResultExceptionHandler<Collection<GroupItem>, DbException> handler) {
|
|
||||||
runOnDbThread(() -> {
|
|
||||||
try {
|
|
||||||
db.transaction(true, txn -> loadGroups(txn, handler));
|
|
||||||
} catch (DbException e) {
|
|
||||||
logException(LOG, WARNING, e);
|
|
||||||
handler.onException(e);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
private void loadGroups(Transaction txn,
|
private List<GroupItem> loadGroups(Transaction txn) throws DbException {
|
||||||
ResultExceptionHandler<Collection<GroupItem>, DbException> handler)
|
|
||||||
throws DbException {
|
|
||||||
long start = now();
|
long start = now();
|
||||||
Collection<PrivateGroup> groups = groupManager.getPrivateGroups(txn);
|
Collection<PrivateGroup> groups = groupManager.getPrivateGroups(txn);
|
||||||
List<GroupItem> items = new ArrayList<>(groups.size());
|
List<GroupItem> items = new ArrayList<>(groups.size());
|
||||||
@@ -168,7 +147,7 @@ class GroupListControllerImpl extends DbControllerImpl
|
|||||||
AuthorId authorId = g.getCreator().getId();
|
AuthorId authorId = g.getCreator().getId();
|
||||||
AuthorInfo authorInfo;
|
AuthorInfo authorInfo;
|
||||||
if (authorInfos.containsKey(authorId)) {
|
if (authorInfos.containsKey(authorId)) {
|
||||||
authorInfo = authorInfos.get(authorId);
|
authorInfo = requireNonNull(authorInfos.get(authorId));
|
||||||
} else {
|
} else {
|
||||||
authorInfo = contactManager.getAuthorInfo(txn, authorId);
|
authorInfo = contactManager.getAuthorInfo(txn, authorId);
|
||||||
authorInfos.put(authorId, authorInfo);
|
authorInfos.put(authorId, authorInfo);
|
||||||
@@ -180,12 +159,49 @@ class GroupListControllerImpl extends DbControllerImpl
|
|||||||
// Continue
|
// Continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Collections.sort(items);
|
||||||
logDuration(LOG, "Loading groups", start);
|
logDuration(LOG, "Loading groups", start);
|
||||||
handler.onResult(items);
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@UiThread
|
||||||
public void removeGroup(GroupId g, ExceptionHandler<DbException> handler) {
|
private void onGroupMessageAdded(GroupMessageHeader header) {
|
||||||
|
GroupId g = header.getGroupId();
|
||||||
|
List<GroupItem> list = updateListItem(groupItems,
|
||||||
|
itemToTest -> itemToTest.getId().equals(g),
|
||||||
|
itemToUpdate -> {
|
||||||
|
GroupItem newItem = new GroupItem(itemToUpdate);
|
||||||
|
newItem.addMessageHeader(header);
|
||||||
|
return newItem;
|
||||||
|
});
|
||||||
|
if (list == null) return;
|
||||||
|
// re-sort as the order of items may have changed
|
||||||
|
Collections.sort(list);
|
||||||
|
groupItems.setValue(new LiveResult<>(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void onGroupDissolved(GroupId groupId) {
|
||||||
|
List<GroupItem> list = updateListItem(groupItems,
|
||||||
|
itemToTest -> itemToTest.getId().equals(groupId),
|
||||||
|
itemToUpdate -> {
|
||||||
|
GroupItem newItem = new GroupItem(itemToUpdate);
|
||||||
|
newItem.setDissolved();
|
||||||
|
return newItem;
|
||||||
|
});
|
||||||
|
if (list == null) return;
|
||||||
|
groupItems.setValue(new LiveResult<>(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
@UiThread
|
||||||
|
private void onGroupRemoved(GroupId groupId) {
|
||||||
|
List<GroupItem> list =
|
||||||
|
removeListItem(groupItems, i -> i.getId().equals(groupId));
|
||||||
|
if (list == null) return;
|
||||||
|
groupItems.setValue(new LiveResult<>(list));
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeGroup(GroupId g) {
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
long start = now();
|
long start = now();
|
||||||
@@ -193,23 +209,26 @@ class GroupListControllerImpl extends DbControllerImpl
|
|||||||
logDuration(LOG, "Removing group", start);
|
logDuration(LOG, "Removing group", start);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
handler.onException(e);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
void loadNumInvitations() {
|
||||||
public void loadAvailableGroups(
|
|
||||||
ResultExceptionHandler<Integer, DbException> handler) {
|
|
||||||
runOnDbThread(() -> {
|
runOnDbThread(() -> {
|
||||||
try {
|
try {
|
||||||
handler.onResult(
|
int i = groupInvitationManager.getInvitations().size();
|
||||||
groupInvitationManager.getInvitations().size());
|
numInvitations.postValue(i);
|
||||||
} catch (DbException e) {
|
} catch (DbException e) {
|
||||||
logException(LOG, WARNING, e);
|
logException(LOG, WARNING, e);
|
||||||
handler.onException(e);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LiveData<LiveResult<List<GroupItem>>> getGroupItems() {
|
||||||
|
return groupItems;
|
||||||
|
}
|
||||||
|
|
||||||
|
LiveData<Integer> getNumInvitations() {
|
||||||
|
return numInvitations;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,7 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
private final static float ALPHA = 0.42f;
|
private final static float ALPHA = 0.42f;
|
||||||
|
|
||||||
|
private final Context ctx;
|
||||||
private final ViewGroup layout;
|
private final ViewGroup layout;
|
||||||
private final TextAvatarView avatar;
|
private final TextAvatarView avatar;
|
||||||
private final TextView name;
|
private final TextView name;
|
||||||
@@ -40,7 +41,7 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
|
|||||||
|
|
||||||
GroupViewHolder(View v) {
|
GroupViewHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
|
ctx = v.getContext();
|
||||||
layout = (ViewGroup) v;
|
layout = (ViewGroup) v;
|
||||||
avatar = v.findViewById(R.id.avatarView);
|
avatar = v.findViewById(R.id.avatarView);
|
||||||
name = v.findViewById(R.id.nameView);
|
name = v.findViewById(R.id.nameView);
|
||||||
@@ -51,8 +52,7 @@ class GroupViewHolder extends RecyclerView.ViewHolder {
|
|||||||
remove = v.findViewById(R.id.removeButton);
|
remove = v.findViewById(R.id.removeButton);
|
||||||
}
|
}
|
||||||
|
|
||||||
void bindView(Context ctx, GroupItem group,
|
void bindView(GroupItem group, OnGroupRemoveClickListener listener) {
|
||||||
OnGroupRemoveClickListener listener) {
|
|
||||||
// Avatar
|
// Avatar
|
||||||
avatar.setText(group.getName().substring(0, 1));
|
avatar.setText(group.getName().substring(0, 1));
|
||||||
avatar.setBackgroundBytes(group.getId().getBytes());
|
avatar.setBackgroundBytes(group.getId().getBytes());
|
||||||
|
|||||||
Reference in New Issue
Block a user