Private Group List

parent 3ea36bbd
......@@ -32,7 +32,7 @@ dependencies {
}
compile 'info.guardianproject.panic:panic:0.5'
compile 'info.guardianproject.trustedintents:trustedintents:0.2'
compile 'de.hdodenhof:circleimageview:2.0.0'
compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.google.zxing:core:3.2.1'
apt 'com.google.dagger:dagger-compiler:2.0.2'
provided 'javax.annotation:jsr250-api:1.0'
......@@ -53,7 +53,7 @@ dependencyVerification {
'ch.acra:acra:afd5b28934d5166b55f261c85685ad59e8a4ebe9ca1960906afaa8c76d8dc9eb',
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
'de.hdodenhof:circleimageview:c76d936395b50705a3f98c9220c22d2599aeb9e609f559f6048975cfc1f686b8',
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
......
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M20,0L4,0v2h16L20,0zM4,24h16v-2L4,22v2zM20,4L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,6c0,-1.1 -0.9,-2 -2,-2zM12,6.75c1.24,0 2.25,1.01 2.25,2.25s-1.01,2.25 -2.25,2.25S9.75,10.24 9.75,9 10.76,6.75 12,6.75zM17,17L7,17v-1.5c0,-1.67 3.33,-2.5 5,-2.5s5,0.83 5,2.5L17,17z"/>
</vector>
<?xml version="1.0" encoding="utf-8"?>
<org.briarproject.android.view.BriarRecyclerView
android:id="@+id/invitationsView"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<?xml version="1.0" encoding="utf-8"?>
<org.briarproject.android.view.BriarRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contactList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<?xml version="1.0" encoding="utf-8"?>
<org.briarproject.android.view.BriarRecyclerView
android:id="@+id/list"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/contactList"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:listitem="@layout/list_item_contact"/>
app:scrollToEnd="false"/>
......@@ -15,7 +15,8 @@
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/listitem_horizontal_margin"/>
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginTop="@dimen/listitem_horizontal_margin"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/forumNameView"
......
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:background="?attr/selectableItemBackground">
<org.briarproject.android.view.TextAvatarView
android:id="@+id/avatarView"
android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginTop="@dimen/listitem_horizontal_margin"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/nameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="@dimen/listitem_horizontal_margin"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:maxLines="2"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a Private Group"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/creatorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/nameView"
android:layout_marginBottom="@dimen/margin_small"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:paddingTop="@dimen/margin_small"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="Created by Santa Claus"/>
<TextView
android:id="@+id/messageCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/creatorView"
android:layout_marginBottom="@dimen/margin_small"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:paddingTop="@dimen/margin_small"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="1337 messages"
tools:visibility="visible"/>
<TextView
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/creatorView"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/margin_small"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="3 weeks ago, 12:00"
tools:visibility="visible"/>
<TextView
android:id="@+id/statusView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/messageCountView"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:paddingTop="@dimen/margin_small"
android:textColor="@color/briar_text_tertiary"
tools:text="@string/groups_group_is_empty"/>
<Button
android:id="@+id/removeButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/divider"
android:layout_alignParentRight="true"
android:layout_toRightOf="@+id/statusView"
android:text="@string/groups_remove"
tools:visibility="gone"/>
<View
android:id="@+id/divider"
style="@style/Divider.ForumList"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/statusView"
android:layout_marginTop="@dimen/listitem_horizontal_margin"/>
</RelativeLayout>
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
<ImageView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:showIn="@layout/navigation_menu">
<ImageView
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="top|center_horizontal"
android:layout_margin="@dimen/margin_medium"
android:contentDescription="@string/app_name"
android:src="@drawable/briar_logo_large"/>
<View
style="@style/Divider.Horizontal"
android:layout_gravity="bottom"/>
</FrameLayout>
\ No newline at end of file
android:layout_height="100dp"
android:contentDescription="@string/app_name"
android:scaleType="fitStart"
android:src="@drawable/navigation_drawer_header"
tools:showIn="@layout/navigation_menu"/>
<?xml version="1.0" encoding="utf-8"?>
<merge
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="@layout/list_item_forum">
......@@ -11,16 +10,15 @@
android:layout_width="@dimen/avatar_forum_size"
android:layout_height="@dimen/avatar_forum_size"
android:layout_gravity="bottom|left"
android:src="@android:color/transparent"
app:civ_fill_color="@color/briar_button_positive"/>
android:src="@color/briar_button_positive"/>
<android.support.v7.widget.AppCompatTextView
android:id="@+id/textAvatarView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginRight="@dimen/listitem_picture_frame_offset"
android:layout_marginTop="@dimen/listitem_picture_frame_offset"
android:layout_marginRight="@dimen/listitem_picture_frame_offset_horizontal"
android:layout_marginTop="@dimen/listitem_picture_frame_offset_vertical"
android:maxLength="1"
android:shadowColor="@color/forum_avatar_shadow"
android:shadowDx="0"
......
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_add_group"
android:icon="@drawable/ic_add_white"
android:title="@string/groups_add_group_title"
app:showAsAction="ifRoom"/>
</menu>
\ No newline at end of file
......@@ -5,8 +5,12 @@
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_btn_contacts"
android:icon="@drawable/ic_contacts_black_24dp"
android:icon="@drawable/ic_contacts"
android:title="@string/contact_list_button"/>
<item
android:id="@+id/nav_btn_groups"
android:icon="@drawable/ic_group"
android:title="@string/groups_button"/>
<item
android:id="@+id/nav_btn_forums"
android:icon="@drawable/ic_forums_black_24dp"
......@@ -15,6 +19,9 @@
android:id="@+id/nav_btn_blogs"
android:icon="@drawable/blogs"
android:title="@string/blogs_button"/>
</group>
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_btn_settings"
android:icon="@drawable/ic_settings_black_24dp"
......
......@@ -24,8 +24,9 @@
<dimen name="listitem_height_one_line_avatar">56dp</dimen>
<dimen name="listitem_picture_size">48dp</dimen>
<dimen name="listitem_picture_size_small">23dp</dimen>
<dimen name="listitem_picture_frame_size">53dp</dimen>
<dimen name="listitem_picture_frame_offset">2dp</dimen>
<dimen name="listitem_picture_frame_size">51dp</dimen>
<dimen name="listitem_picture_frame_offset_horizontal">1dp</dimen>
<dimen name="listitem_picture_frame_offset_vertical">2dp</dimen>
<dimen name="listitem_selectable_picture_size">40dp</dimen>
<dimen name="avatar_forum_size">48dp</dimen>
<dimen name="avatar_border_width">2dp</dimen>
......
......@@ -34,6 +34,7 @@
<string name="nav_drawer_open_description">Open the navigation drawer</string>
<string name="nav_drawer_close_description">Close the navigation drawer</string>
<string name="contact_list_button">Contacts</string>
<string name="groups_button">Private Groups</string>
<string name="forums_button">Forums</string>
<string name="blogs_button">Blogs</string>
<string name="settings_button">Settings</string>
......@@ -143,6 +144,18 @@
<item quantity="other">%d new contacts added.</item>
</plurals>
<!-- Private Groups -->
<string name="groups_list_empty">You are not participating in any groups.\n\nTap the + icon at the top to create a group yourself or ask your contacts to get invited into one of their groups.</string>
<string name="groups_created_by">Created by %s</string>
<plurals name="messages">
<item quantity="one">%d message</item>
<item quantity="other">%d messages</item>
</plurals>
<string name="groups_group_is_empty">This group is empty</string>
<string name="groups_group_is_dissolved">This group is dissolved</string>
<string name="groups_remove">Remove</string>
<string name="groups_add_group_title">Add Private Group</string>
<!-- Forums -->
<string name="no_forums">You don\'t have any forums yet.\n\nWhy don\'t you create a new one yourself by tapping the + icon at the top?\n\nYou can also ask your contacts to share forums with you.</string>
<string name="create_forum_title">New Forum</string>
......
......@@ -93,9 +93,6 @@
<style name="BriarAvatar">
<item name="civ_border_width">@dimen/avatar_border_width</item>
<item name="civ_border_color">@color/briar_primary</item>
<!-- Remove when we are using 'de.hdodenhof:circleimageview:2.1.0' -->
<item name="civ_border_overlay">true</item>
</style>
<style name="NavMenuButton" parent="Widget.AppCompat.Button.Borderless.Colored">
......
......@@ -28,6 +28,7 @@ import org.briarproject.android.keyagreement.KeyAgreementActivity;
import org.briarproject.android.keyagreement.ShowQrCodeFragment;
import org.briarproject.android.panic.PanicPreferencesActivity;
import org.briarproject.android.panic.PanicResponderActivity;
import org.briarproject.android.privategroup.list.GroupListFragment;
import org.briarproject.android.sharing.ContactSelectorFragment;
import org.briarproject.android.sharing.InvitationsBlogActivity;
import org.briarproject.android.sharing.InvitationsForumActivity;
......@@ -114,6 +115,7 @@ public interface ActivityComponent {
// Fragments
void inject(ContactListFragment fragment);
void inject(GroupListFragment fragment);
void inject(ForumListFragment fragment);
void inject(FeedFragment fragment);
void inject(IntroFragment fragment);
......
......@@ -22,6 +22,8 @@ import org.briarproject.android.controller.SetupController;
import org.briarproject.android.controller.SetupControllerImpl;
import org.briarproject.android.forum.ForumController;
import org.briarproject.android.forum.ForumControllerImpl;
import org.briarproject.android.privategroup.list.GroupListController;
import org.briarproject.android.privategroup.list.GroupListControllerImpl;
import dagger.Module;
import dagger.Provides;
......@@ -90,6 +92,13 @@ public class ActivityModule {
return dbController;
}
@ActivityScope
@Provides
protected GroupListController provideGroupListController(
GroupListControllerImpl groupListController) {
return groupListController;
}
@ActivityScope
@Provides
protected ForumController provideForumController(
......
......@@ -35,6 +35,7 @@ import org.briarproject.api.messaging.MessagingManager;
import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.plugins.ConnectionRegistry;
import org.briarproject.api.plugins.PluginManager;
import org.briarproject.api.privategroup.PrivateGroupManager;
import org.briarproject.api.settings.SettingsManager;
import org.briarproject.plugins.AndroidPluginsModule;
import org.briarproject.system.AndroidSystemModule;
......@@ -93,6 +94,8 @@ public interface AndroidComponent extends CoreEagerSingletons {
PrivateMessageFactory privateMessageFactory();
PrivateGroupManager privateGroupManager();
ForumManager forumManager();
ForumSharingManager forumSharingManager();
......
......@@ -10,6 +10,7 @@ import org.briarproject.android.blogs.FeedFragment;
import org.briarproject.android.contact.ContactListFragment;
import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.fragment.BaseFragment;
import org.briarproject.android.privategroup.list.GroupListFragment;
import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
......@@ -26,6 +27,8 @@ public abstract class BriarFragmentActivity extends BriarActivity {
if (fragmentTag.equals(ContactListFragment.TAG)) {
actionBar.setTitle(R.string.contact_list_button);
} else if (fragmentTag.equals(GroupListFragment.TAG)) {
actionBar.setTitle(R.string.groups_button);
} else if (fragmentTag.equals(ForumListFragment.TAG)) {
actionBar.setTitle(R.string.forums_button);
} else if (fragmentTag.equals(FeedFragment.TAG)) {
......
......@@ -28,6 +28,7 @@ import org.briarproject.android.controller.TransportStateListener;
import org.briarproject.android.controller.handler.UiResultHandler;
import org.briarproject.android.forum.ForumListFragment;
import org.briarproject.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.android.privategroup.list.GroupListFragment;
import org.briarproject.api.TransportId;
import org.briarproject.api.identity.LocalAuthor;
......@@ -180,6 +181,9 @@ public class NavDrawerActivity extends BriarFragmentActivity implements
case R.id.nav_btn_contacts:
startFragment(ContactListFragment.newInstance());
break;
case R.id.nav_btn_groups:
startFragment(GroupListFragment.newInstance());
break;
case R.id.nav_btn_forums:
startFragment(ForumListFragment.newInstance());
break;
......
......@@ -109,7 +109,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
setHasOptionsMenu(true);
View contentView =
inflater.inflate(R.layout.fragment_contact_list, container,
inflater.inflate(R.layout.list, container,
false);
BaseContactListAdapter.OnItemClickListener onItemClickListener =
......@@ -141,7 +141,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
};
adapter = new ContactListAdapter(getContext(), onItemClickListener);
list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
list = (BriarRecyclerView) contentView.findViewById(R.id.list);
list.setLayoutManager(new LinearLayoutManager(getContext()));
list.setAdapter(adapter);
list.setEmptyText(getString(R.string.no_contacts));
......
......@@ -77,10 +77,7 @@ public class ContactChooserFragment extends BaseFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View contentView =
inflater.inflate(R.layout.introduction_contact_chooser,
container, false);
View contentView = inflater.inflate(R.layout.list, container, false);
if (Build.VERSION.SDK_INT >= 21) {
setExitTransition(new Fade());
......@@ -105,7 +102,7 @@ public class ContactChooserFragment extends BaseFragment {
};
adapter = new ContactChooserAdapter(getActivity(), onItemClickListener);
list = (BriarRecyclerView) contentView.findViewById(R.id.contactList);
list = (BriarRecyclerView) contentView.findViewById(R.id.list);
list.setLayoutManager(new LinearLayoutManager(getActivity()));
list.setAdapter(adapter);
list.setEmptyText(getString(R.string.no_contacts));
......
package org.briarproject.android.privategroup.list;
import org.briarproject.api.identity.Author;
import org.briarproject.api.privategroup.GroupMessageHeader;
import org.briarproject.api.privategroup.PrivateGroup;
import org.briarproject.api.sync.GroupId;
import org.jetbrains.annotations.NotNull;
// This class is not thread-safe
class GroupItem {
private final PrivateGroup privateGroup;
private int messageCount;
private long lastUpdate;
private int unreadCount;
private boolean dissolved;
GroupItem(@NotNull PrivateGroup privateGroup, int messageCount,
long lastUpdate, int unreadCount, boolean dissolved) {
this.privateGroup = privateGroup;
this.messageCount = messageCount;
this.lastUpdate = lastUpdate;
this.unreadCount = unreadCount;
this.dissolved = dissolved;
}
void addMessageHeader(GroupMessageHeader header) {
messageCount++;
if (header.getTimestamp() > lastUpdate) {
lastUpdate = header.getTimestamp();
}
if (!header.isRead()) {
unreadCount++;
}
}
@NotNull
PrivateGroup getPrivateGroup() {
return privateGroup;
}
@NotNull
GroupId getId() {
return privateGroup.getId();
}
@NotNull
Author getCreator() {
return privateGroup.getAuthor();
}
@NotNull
String getName() {
return privateGroup.getName();
}
boolean isEmpty() {
return messageCount == 0;
}
int getMessageCount() {
return messageCount;
}
long getLastUpdate() {
return lastUpdate;
}
int getUnreadCount() {
return unreadCount;
}
boolean isDissolved() {
return dissolved;
}
}
package org.briarproject.android.privategroup.list;
import android.content.Context;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.briarproject.R;
import org.briarproject.android.privategroup.list.GroupViewHolder.OnGroupRemoveClickListener;
import org.briarproject.android.util.BriarAdapter;
import org.briarproject.api.sync.GroupId;
import org.jetbrains.annotations.NotNull;
import static android.support.v7.util.SortedList.INVALID_POSITION;
class GroupListAdapter extends BriarAdapter<GroupItem, GroupViewHolder> {
private final OnGroupRemoveClickListener listener;
GroupListAdapter(Context ctx, OnGroupRemoveClickListener listener) {
super(ctx, GroupItem.class);
this.listener = listener;
}
@Override
public GroupViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(ctx).inflate(
R.layout.list_item_group, parent, false);
return new GroupViewHolder(v);
}
@Override
public void onBindViewHolder(GroupViewHolder ui, int position) {
ui.bindView(ctx, getItemAt(position), listener);
}
@Override
public int compare(GroupItem a, GroupItem b) {
if (a == b) return 0;
// The group with the latest message comes first
long aTime = a.getLastUpdate(), bTime = b.getLastUpdate();
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 <