Show which contacts subscribe to each forum. Dev task #79.

This commit is contained in:
akwizgran
2014-03-21 18:08:08 +00:00
parent e1d099903d
commit 4dcf9f632e
6 changed files with 187 additions and 10 deletions

View File

@@ -53,6 +53,14 @@
</plurals>
<string name="no_posts">No posts</string>
<string name="subscribe_to_this_forum">Subscribe to this forum</string>
<string name="no_subscribers">No contacts subscribe to this forum</string>
<plurals name="subscribers">
<item quantity="one">%d contact subscribes to this forum:</item>
<item quantity="other">%d contacts subscribe to this forum:</item>
</plurals>
<string name="public_space_warning">Forums are public spaces. There may be other subscribers who are not your contacts.</string>
<string name="subscribe_button">Subscribe</string>
<string name="unsubscribe_button">Unsubscribe</string>
<string name="create_forum_title">New Forum</string>
<string name="choose_forum_name">Choose a name for your forum:</string>
<string name="forum_visible_to_all">Share this forum with all contacts</string>

View File

@@ -1,17 +1,21 @@
package org.briarproject.android.groups;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.Gravity.CENTER;
import static android.view.Gravity.CENTER_HORIZONTAL;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static android.widget.LinearLayout.HORIZONTAL;
import static android.widget.LinearLayout.VERTICAL;
import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH;
import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;
import javax.inject.Inject;
@@ -25,22 +29,29 @@ import org.briarproject.api.Contact;
import org.briarproject.api.ContactId;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.LocalSubscriptionsUpdatedEvent;
import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent;
import org.briarproject.api.messaging.Group;
import org.briarproject.api.messaging.GroupId;
import android.content.Intent;
import android.content.res.Resources;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ProgressBar;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.TextView;
public class ConfigureGroupActivity extends BriarActivity
implements OnClickListener, NoContactsDialog.Listener,
implements OnClickListener, EventListener, NoContactsDialog.Listener,
SelectContactsDialog.Listener {
private static final Logger LOG =
@@ -51,6 +62,8 @@ SelectContactsDialog.Listener {
private RadioGroup radioGroup = null;
private RadioButton visibleToAll = null, visibleToSome = null;
private Button doneButton = null;
private TextView subscribers = null;
private TextView subscriberNames = null;
private ProgressBar progress = null;
private boolean changed = false;
@@ -86,6 +99,32 @@ SelectContactsDialog.Listener {
int pad = LayoutUtils.getPadding(this);
layout.setPadding(pad, pad, pad, pad);
subscribers = new TextView(this);
subscribers.setGravity(CENTER);
subscribers.setTextSize(18);
subscribers.setText("\u2026");
layout.addView(subscribers);
subscriberNames = new TextView(this);
subscriberNames.setGravity(CENTER);
subscriberNames.setTextSize(18);
subscriberNames.setVisibility(GONE);
layout.addView(subscriberNames);
LinearLayout warning = new LinearLayout(this);
warning.setOrientation(HORIZONTAL);
warning.setPadding(pad, pad, pad, pad);
ImageView icon = new ImageView(this);
icon.setImageResource(R.drawable.action_about);
warning.addView(icon);
TextView publicSpace = new TextView(this);
publicSpace.setPadding(pad, 0, 0, 0);
publicSpace.setText(R.string.public_space_warning);
warning.addView(publicSpace);
layout.addView(warning);
subscribeCheckBox = new CheckBox(this);
subscribeCheckBox.setId(1);
subscribeCheckBox.setText(R.string.subscribe_to_this_forum);
@@ -129,6 +168,65 @@ SelectContactsDialog.Listener {
setContentView(layout);
}
@Override
public void onResume() {
super.onResume();
db.addListener(this);
loadSubscribers();
}
private void loadSubscribers() {
runOnDbThread(new Runnable() {
public void run() {
try {
long now = System.currentTimeMillis();
Collection<Contact> contacts = db.getSubscribers(groupId);
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
LOG.info("Load took " + duration + " ms");
displaySubscribers(contacts);
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
private void displaySubscribers(final Collection<Contact> contacts) {
runOnUiThread(new Runnable() {
public void run() {
if(contacts.isEmpty()) {
subscribers.setText(R.string.no_subscribers);
subscriberNames.setVisibility(GONE);
} else {
int count = contacts.size();
Resources res = getResources();
String title = res.getQuantityString(R.plurals.subscribers,
count, count);
subscribers.setText(title);
List<String> names = new ArrayList<String>();
for(Contact c : contacts)
names.add(c.getAuthor().getName());
Collections.sort(names, String.CASE_INSENSITIVE_ORDER);
StringBuilder s = new StringBuilder();
for(int i = 0; i < count; i++) {
s.append(names.get(i));
if(i + 1 < count) s.append(", ");
}
subscriberNames.setText(s.toString());
subscriberNames.setVisibility(VISIBLE);
}
}
});
}
@Override
public void onPause() {
super.onPause();
db.removeListener(this);
}
public void onClick(View view) {
if(view == subscribeCheckBox) {
changed = true;
@@ -139,24 +237,24 @@ SelectContactsDialog.Listener {
changed = true;
} else if(view == visibleToSome) {
changed = true;
if(contacts == null) loadContacts();
else displayContacts();
if(contacts == null) loadVisibleContacts();
else displayVisibleContacts();
} else if(view == doneButton) {
if(changed) {
boolean subscribe = subscribeCheckBox.isChecked();
boolean all = visibleToAll.isChecked();
// Replace the button with a progress bar
doneButton.setVisibility(GONE);
progress.setVisibility(VISIBLE);
// Update the blog in a background thread
if(subscribe || subscribed) updateGroup(subscribe, all);
// Update the group in a background thread
boolean subscribe = subscribeCheckBox.isChecked();
boolean all = visibleToAll.isChecked();
updateGroup(subscribe, all);
} else {
finish();
}
}
}
private void loadContacts() {
private void loadVisibleContacts() {
runOnDbThread(new Runnable() {
public void run() {
try {
@@ -166,7 +264,7 @@ SelectContactsDialog.Listener {
long duration = System.currentTimeMillis() - now;
if(LOG.isLoggable(INFO))
LOG.info("Load took " + duration + " ms");
displayContacts();
displayVisibleContacts();
} catch(DbException e) {
if(LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
@@ -175,7 +273,7 @@ SelectContactsDialog.Listener {
});
}
private void displayContacts() {
private void displayVisibleContacts() {
runOnUiThread(new Runnable() {
public void run() {
if(contacts.isEmpty()) {
@@ -232,6 +330,16 @@ SelectContactsDialog.Listener {
});
}
public void eventOccurred(Event e) {
if(e instanceof LocalSubscriptionsUpdatedEvent) {
LOG.info("Local subscriptions updated, reloading");
loadSubscribers();
} else if(e instanceof RemoteSubscriptionsUpdatedEvent) {
LOG.info("Remote subscriptions updated, reloading");
loadSubscribers();
}
}
public void contactCreationSelected() {
startActivity(new Intent(this, AddContactActivity.class));
}

View File

@@ -231,6 +231,9 @@ public interface DatabaseComponent {
/** Returns all settings. */
Settings getSettings() throws DbException;
/** Returns all contacts who subscribe to the given group. */
Collection<Contact> getSubscribers(GroupId g) throws DbException;
/** Returns the maximum latencies of all local transports. */
Map<TransportId, Long> getTransportLatencies() throws DbException;

View File

@@ -486,6 +486,13 @@ interface Database<T> {
*/
Settings getSettings(T txn) throws DbException;
/**
* Returns all contacts who subscribe to the given group.
* <p>
* Locking: subscription read.
*/
Collection<Contact> getSubscribers(T txn, GroupId g) throws DbException;
/**
* Returns a subscription ack for the given contact, or null if no ack is
* due.

View File

@@ -1154,6 +1154,23 @@ DatabaseCleaner.Callback {
}
}
public Collection<Contact> getSubscribers(GroupId g) throws DbException {
subscriptionLock.readLock().lock();
try {
T txn = db.startTransaction();
try {
Collection<Contact> contacts = db.getSubscribers(txn, g);
db.commitTransaction(txn);
return contacts;
} catch(DbException e) {
db.abortTransaction(txn);
throw e;
}
} finally {
subscriptionLock.readLock().unlock();
}
}
public Map<TransportId, Long> getTransportLatencies() throws DbException {
transportLock.readLock().lock();
try {

View File

@@ -2154,6 +2154,40 @@ abstract class JdbcDatabase implements Database<Connection> {
}
}
public Collection<Contact> getSubscribers(Connection txn, GroupId g)
throws DbException {
PreparedStatement ps = null;
ResultSet rs = null;
try {
String sql = "SELECT c.contactId, authorId, c.name, publicKey,"
+ " localAuthorId"
+ " FROM contacts AS c"
+ " JOIN contactGroups AS cg"
+ " ON c.contactId = cg.contactId"
+ " WHERE groupId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, g.getBytes());
rs = ps.executeQuery();
List<Contact> contacts = new ArrayList<Contact>();
while(rs.next()) {
ContactId contactId = new ContactId(rs.getInt(1));
AuthorId authorId = new AuthorId(rs.getBytes(2));
String name = rs.getString(3);
byte[] publicKey = rs.getBytes(4);
Author author = new Author(authorId, name, publicKey);
AuthorId localAuthorId = new AuthorId(rs.getBytes(5));
contacts.add(new Contact(contactId, author, localAuthorId));
}
rs.close();
ps.close();
return Collections.unmodifiableList(contacts);
} catch(SQLException e) {
tryToClose(rs);
tryToClose(ps);
throw new DbException(e);
}
}
public SubscriptionAck getSubscriptionAck(Connection txn, ContactId c)
throws DbException {
PreparedStatement ps = null;