diff --git a/briar-android/AndroidManifest.xml b/briar-android/AndroidManifest.xml index 31bbf5a44af9a8dde294d2dbc9eec343679a45af..13747b0e519238facbc094dcb9a16380c4e3e664 100644 --- a/briar-android/AndroidManifest.xml +++ b/briar-android/AndroidManifest.xml @@ -132,9 +132,9 @@ /> </activity> <activity - android:name=".android.groups.ConfigureGroupActivity" + android:name=".android.groups.AvailableGroupsActivity" android:logo="@drawable/logo" - android:label="@string/app_name" + android:label="@string/available_forums_title" android:parentActivityName=".android.groups.GroupListActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" @@ -173,9 +173,9 @@ /> </activity> <activity - android:name=".android.groups.ManageGroupsActivity" + android:name=".android.groups.ReadGroupPostActivity" android:logo="@drawable/logo" - android:label="@string/manage_forums_title" + android:label="@string/app_name" android:parentActivityName=".android.groups.GroupListActivity" > <meta-data android:name="android.support.PARENT_ACTIVITY" @@ -183,7 +183,7 @@ /> </activity> <activity - android:name=".android.groups.ReadGroupPostActivity" + android:name=".android.groups.ShareGroupActivity" android:logo="@drawable/logo" android:label="@string/app_name" android:parentActivityName=".android.groups.GroupListActivity" > diff --git a/briar-android/res/values/color.xml b/briar-android/res/values/color.xml index cada1c6bfe940657204122ac079ed97cb430686b..00b5d2412b0ae23a6aafb492a0935dcc6e1811fc 100644 --- a/briar-android/res/values/color.xml +++ b/briar-android/res/values/color.xml @@ -9,7 +9,7 @@ <color name="private_message_date">#AAAAAA</color> <color name="unread_background">#FFFFFF</color> <color name="horizontal_border">#CCCCCC</color> - <color name="groups_available_background">#FCCF1C</color> + <color name="forums_available_background">#FCCF1C</color> <color name="no_private_messages">#AAAAAA</color> <color name="no_posts">#AAAAAA</color> <color name="settings_title_text">#2D3E50</color> diff --git a/briar-android/res/values/strings.xml b/briar-android/res/values/strings.xml index 3096f600ecc0e7063df19e31fa4a49d41a9223f9..3e0ed4508cb2ac54ee20416fe0f582db8af7cee5 100644 --- a/briar-android/res/values/strings.xml +++ b/briar-android/res/values/strings.xml @@ -11,8 +11,10 @@ <string name="name_too_long">Name is too long</string> <string name="password_too_weak">Password is too weak</string> <string name="passwords_do_not_match">Passwords do not match</string> + <string name="create_account_button">Create Account</string> <string name="enter_password">Enter your password:</string> <string name="try_again">Wrong password, try again:</string> + <string name="sign_in_button">Sign In</string> <string name="startup_failed_notification_title">Briar could not start</string> <string name="startup_failed_notification_text">You may need to reinstall Briar.</string> <string name="expiry_warning">This software has expired.\nPlease install a newer version.</string> @@ -33,7 +35,7 @@ <string name="searching_format">Searching for %06d\u2026</string> <string name="connection_failed">Connection failed</string> <string name="could_not_find_contact">Briar could not find your contact nearby</string> - <string name="try_again_button">Try again</string> + <string name="try_again_button">Try Again</string> <string name="connected_to_contact">Connected to contact</string> <string name="calculating_confirmation_code">Calculating confirmation code\u2026</string> <string name="your_confirmation_code">Your confirmation code is</string> @@ -49,45 +51,40 @@ <string name="format_from">From: %s</string> <string name="forums_title">Forums</string> <string name="no_forums">No forums</string> - <plurals name="forums_available"> - <item quantity="one">%d forum available from contacts</item> - <item quantity="other">%d forums available from contacts</item> + <plurals name="forums_shared"> + <item quantity="one">%d forum shared by contacts</item> + <item quantity="other">%d forums shared by contacts</item> </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="unsubscribe">Unsubscribe</string> + <string name="unsubscribed_toast">Unsubscribed</string> + <string name="no_forum_posts">No posts</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> - <string name="forum_visible_to_some">Share this forum with chosen contacts</string> - <string name="done_button">Done</string> + <string name="create_forum_button">Create Forum</string> <string name="forum_created_toast">Forum created</string> + <string name="forum_share_with_all">Share this forum with all contacts</string> + <string name="forum_share_with_some">Share this forum with chosen contacts</string> + <string name="share_button">Share Forum</string> <string name="from">From:</string> <string name="anonymous">Anonymous</string> <string name="new_identity_item">New identity\u2026</string> - <string name="group_post_hint">Type forum post</string> - <string name="manage_forums_title">Available Forums</string> - <string name="no_forums_available">No forums available</string> - <string name="subscribed_all">Subscribed, shared with all contacts</string> - <string name="subscribed_some">Subscribed, shared with chosen contacts</string> - <string name="not_subscribed">Not subscribed</string> <string name="new_identity_title">New Identity</string> - <string name="create_button">Create</string> + <string name="create_identity_button">Create Identity</string> <string name="identity_created_toast">Identity created</string> + <string name="forum_post_hint">Type forum post</string> + <string name="available_forums_title">Available Forums</string> + <string name="subscribed_toast">Subscribed</string> + <string name="shared_by_format">Shared by %s</string> <string name="no_contacts_prompt">You don\'t have any contacts. Add a contact now?</string> <string name="add_button">Add</string> <string name="cancel_button">Cancel</string> - <string name="post_sent_toast">Post sent</string> + <string name="done_button">Done</string> + <string name="post_sent_toast">Forum post sent</string> <plurals name="private_message_notification_text"> <item quantity="one">New private message.</item> <item quantity="other">%d new private messages.</item> </plurals> - <plurals name="group_post_notification_text"> + <plurals name="forum_post_notification_text"> <item quantity="one">New forum post.</item> <item quantity="other">%d new forum posts.</item> </plurals> diff --git a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java index 0f6bccfe6f2a493c3c23717335c832ca3136b048..a9804413cb6dadbb25940e2b4980c2eaa719f185 100644 --- a/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java +++ b/briar-android/src/org/briarproject/android/AndroidNotificationManagerImpl.java @@ -236,7 +236,7 @@ Service, EventListener { b.setSmallIcon(R.drawable.message_notification_icon); b.setContentTitle(appContext.getText(R.string.app_name)); b.setContentText(appContext.getResources().getQuantityString( - R.plurals.group_post_notification_text, groupTotal, + R.plurals.forum_post_notification_text, groupTotal, groupTotal)); String ringtoneUri = settings.get("notifyRingtoneUri"); if(!StringUtils.isNullOrEmpty(ringtoneUri)) diff --git a/briar-android/src/org/briarproject/android/PasswordActivity.java b/briar-android/src/org/briarproject/android/PasswordActivity.java index ce3672019cccf783fc67950358bf07d1e8ae36c4..17173a4f6174757e8a7050cf576c59e62fa7982f 100644 --- a/briar-android/src/org/briarproject/android/PasswordActivity.java +++ b/briar-android/src/org/briarproject/android/PasswordActivity.java @@ -47,7 +47,7 @@ public class PasswordActivity extends RoboActivity { @Inject @CryptoExecutor private Executor cryptoExecutor; private TextView enterPassword = null; - private Button continueButton = null; + private Button signInButton = null; private ProgressBar progress = null; // Fields that are accessed from background threads must be volatile @@ -102,15 +102,15 @@ public class PasswordActivity extends RoboActivity { // Adjusting the padding of buttons and EditTexts has the wrong results layout.addView(new FixedVerticalSpace(this)); - continueButton = new Button(this); - continueButton.setLayoutParams(WRAP_WRAP); - continueButton.setText(R.string.continue_button); - continueButton.setOnClickListener(new OnClickListener() { + signInButton = new Button(this); + signInButton.setLayoutParams(WRAP_WRAP); + signInButton.setText(R.string.sign_in_button); + signInButton.setOnClickListener(new OnClickListener() { public void onClick(View v) { validatePassword(encrypted, passwordEntry.getText()); } }); - layout.addView(continueButton); + layout.addView(signInButton); progress = new ProgressBar(this); progress.setLayoutParams(WRAP_WRAP); @@ -131,7 +131,7 @@ public class PasswordActivity extends RoboActivity { Object o = getSystemService(INPUT_METHOD_SERVICE); ((InputMethodManager) o).toggleSoftInput(HIDE_IMPLICIT_ONLY, 0); // Replace the button with a progress bar - continueButton.setVisibility(GONE); + signInButton.setVisibility(GONE); progress.setVisibility(VISIBLE); // Decrypt the database key in a background thread final String password = e.toString(); @@ -152,7 +152,7 @@ public class PasswordActivity extends RoboActivity { runOnUiThread(new Runnable() { public void run() { enterPassword.setText(R.string.try_again); - continueButton.setVisibility(VISIBLE); + signInButton.setVisibility(VISIBLE); progress.setVisibility(GONE); } }); diff --git a/briar-android/src/org/briarproject/android/SetupActivity.java b/briar-android/src/org/briarproject/android/SetupActivity.java index 1dad71690c0140e89b027559cb60d82110028cf6..7d05038ed61761be0a0c338fbc932b79ba11125d 100644 --- a/briar-android/src/org/briarproject/android/SetupActivity.java +++ b/briar-android/src/org/briarproject/android/SetupActivity.java @@ -67,7 +67,7 @@ OnEditorActionListener { private EditText passwordEntry = null, passwordConfirmation = null; private StrengthMeter strengthMeter = null; private TextView feedback = null; - private Button continueButton = null; + private Button createAccountButton = null; private ProgressBar progress = null; // Fields that are accessed from background threads must be volatile @@ -160,12 +160,12 @@ OnEditorActionListener { feedback.setText(""); layout.addView(feedback); - continueButton = new Button(this); - continueButton.setLayoutParams(WRAP_WRAP); - continueButton.setText(R.string.continue_button); - continueButton.setEnabled(false); - continueButton.setOnClickListener(this); - layout.addView(continueButton); + createAccountButton = new Button(this); + createAccountButton.setLayoutParams(WRAP_WRAP); + createAccountButton.setText(R.string.create_account_button); + createAccountButton.setEnabled(false); + createAccountButton.setOnClickListener(this); + layout.addView(createAccountButton); progress = new ProgressBar(this); progress.setLayoutParams(WRAP_WRAP); @@ -205,7 +205,7 @@ OnEditorActionListener { } else { feedback.setText(""); } - continueButton.setEnabled(nicknameLength > 0 + createAccountButton.setEnabled(nicknameLength > 0 && nicknameLength <= MAX_AUTHOR_NAME_LENGTH && passwordsMatch && strength >= WEAK); } @@ -220,7 +220,7 @@ OnEditorActionListener { public void onClick(View view) { // Replace the feedback text and button with a progress bar feedback.setVisibility(GONE); - continueButton.setVisibility(GONE); + createAccountButton.setVisibility(GONE); progress.setVisibility(VISIBLE); final String nickname = nicknameEntry.getText().toString(); final String password = passwordEntry.getText().toString(); diff --git a/briar-android/src/org/briarproject/android/groups/ManageGroupsActivity.java b/briar-android/src/org/briarproject/android/groups/AvailableGroupsActivity.java similarity index 60% rename from briar-android/src/org/briarproject/android/groups/ManageGroupsActivity.java rename to briar-android/src/org/briarproject/android/groups/AvailableGroupsActivity.java index 799bc84b279eca66c26f47b3966da6337ce76131..01d5bcfcd86f941e336b09f729b524b97d5e6350 100644 --- a/briar-android/src/org/briarproject/android/groups/ManageGroupsActivity.java +++ b/briar-android/src/org/briarproject/android/groups/AvailableGroupsActivity.java @@ -1,10 +1,11 @@ package org.briarproject.android.groups; -import static android.view.Gravity.CENTER; +import static android.widget.Toast.LENGTH_SHORT; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; +import java.util.ArrayList; import java.util.Collection; import java.util.logging.Logger; @@ -13,8 +14,11 @@ import javax.inject.Inject; import org.briarproject.R; import org.briarproject.android.BriarActivity; import org.briarproject.android.util.ListLoadingProgressBar; +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.db.NoSuchSubscriptionException; import org.briarproject.api.event.Event; import org.briarproject.api.event.EventBus; import org.briarproject.api.event.EventListener; @@ -22,24 +26,22 @@ import org.briarproject.api.event.RemoteSubscriptionsUpdatedEvent; import org.briarproject.api.event.SubscriptionAddedEvent; import org.briarproject.api.event.SubscriptionRemovedEvent; import org.briarproject.api.messaging.Group; -import org.briarproject.api.messaging.GroupStatus; +import org.briarproject.api.messaging.GroupId; -import android.content.Intent; import android.os.Bundle; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ListView; -import android.widget.TextView; +import android.widget.Toast; -public class ManageGroupsActivity extends BriarActivity +public class AvailableGroupsActivity extends BriarActivity implements EventListener, OnItemClickListener { private static final Logger LOG = - Logger.getLogger(ManageGroupsActivity.class.getName()); + Logger.getLogger(AvailableGroupsActivity.class.getName()); - private TextView empty = null; - private ManageGroupsAdapter adapter = null; + private AvailableGroupsAdapter adapter = null; private ListView list = null; private ListLoadingProgressBar loading = null; @@ -51,13 +53,7 @@ implements EventListener, OnItemClickListener { public void onCreate(Bundle state) { super.onCreate(state); - empty = new TextView(this); - empty.setLayoutParams(MATCH_MATCH); - empty.setGravity(CENTER); - empty.setTextSize(18); - empty.setText(R.string.no_forums_available); - - adapter = new ManageGroupsAdapter(this); + adapter = new AvailableGroupsAdapter(this); list = new ListView(this); list.setLayoutParams(MATCH_MATCH); list.setAdapter(adapter); @@ -79,8 +75,18 @@ implements EventListener, OnItemClickListener { runOnDbThread(new Runnable() { public void run() { try { + Collection<GroupContacts> available = + new ArrayList<GroupContacts>(); long now = System.currentTimeMillis(); - Collection<GroupStatus> available = db.getAvailableGroups(); + for(Group g : db.getAvailableGroups()) { + try { + GroupId id = g.getId(); + Collection<Contact> c = db.getSubscribers(id); + available.add(new GroupContacts(g, c)); + } catch(NoSuchSubscriptionException e) { + continue; + } + } long duration = System.currentTimeMillis() - now; if(LOG.isLoggable(INFO)) LOG.info("Load took " + duration + " ms"); @@ -93,17 +99,18 @@ implements EventListener, OnItemClickListener { }); } - private void displayGroups(final Collection<GroupStatus> available) { + private void displayGroups(final Collection<GroupContacts> available) { runOnUiThread(new Runnable() { public void run() { if(available.isEmpty()) { - setContentView(empty); + LOG.info("No groups available, finishing"); + finish(); } else { setContentView(list); adapter.clear(); - for(GroupStatus s : available) - adapter.add(new ManageGroupsItem(s)); - adapter.sort(ManageGroupsItemComparator.INSTANCE); + for(GroupContacts g : available) + adapter.add(new AvailableGroupsItem(g)); + adapter.sort(AvailableGroupsItemComparator.INSTANCE); adapter.notifyDataSetChanged(); } } @@ -121,25 +128,36 @@ implements EventListener, OnItemClickListener { LOG.info("Remote subscriptions changed, reloading"); loadGroups(); } else if(e instanceof SubscriptionAddedEvent) { - LOG.info("Group added, reloading"); + LOG.info("Subscription added, reloading"); loadGroups(); } else if(e instanceof SubscriptionRemovedEvent) { - LOG.info("Group removed, reloading"); + LOG.info("Subscription removed, reloading"); loadGroups(); } } public void onItemClick(AdapterView<?> parent, View view, int position, long id) { - ManageGroupsItem item = adapter.getItem(position); - GroupStatus s = item.getGroupStatus(); - Group g = s.getGroup(); - Intent i = new Intent(this, ConfigureGroupActivity.class); - i.putExtra("briar.GROUP_ID", g.getId().getBytes()); - i.putExtra("briar.GROUP_NAME", g.getName()); - i.putExtra("briar.GROUP_SALT", g.getSalt()); - i.putExtra("briar.SUBSCRIBED", s.isSubscribed()); - i.putExtra("briar.VISIBLE_TO_ALL", s.isVisibleToAll()); - startActivity(i); + AvailableGroupsItem item = adapter.getItem(position); + Collection<ContactId> visible = new ArrayList<ContactId>(); + for(Contact c : item.getContacts()) visible.add(c.getId()); + addSubscription(item.getGroup(), visible); + String subscribed = getString(R.string.subscribed_toast); + Toast.makeText(this, subscribed, LENGTH_SHORT).show(); + } + + private void addSubscription(final Group g, + final Collection<ContactId> visible) { + runOnDbThread(new Runnable() { + public void run() { + try { + db.addGroup(g); + db.setVisibility(g.getId(), visible); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); } } diff --git a/briar-android/src/org/briarproject/android/groups/AvailableGroupsAdapter.java b/briar-android/src/org/briarproject/android/groups/AvailableGroupsAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..7e0528f14b6a850ead15f9350546cae66975b997 --- /dev/null +++ b/briar-android/src/org/briarproject/android/groups/AvailableGroupsAdapter.java @@ -0,0 +1,57 @@ +package org.briarproject.android.groups; + +import static android.text.TextUtils.TruncateAt.END; +import static android.widget.LinearLayout.VERTICAL; + +import java.util.ArrayList; +import java.util.Collection; + +import org.briarproject.R; +import org.briarproject.android.util.LayoutUtils; +import org.briarproject.api.Contact; +import org.briarproject.util.StringUtils; + +import android.content.Context; +import android.view.View; +import android.view.ViewGroup; +import android.widget.ArrayAdapter; +import android.widget.LinearLayout; +import android.widget.TextView; + +class AvailableGroupsAdapter extends ArrayAdapter<AvailableGroupsItem> { + + private final int pad; + + AvailableGroupsAdapter(Context ctx) { + super(ctx, android.R.layout.simple_expandable_list_item_1, + new ArrayList<AvailableGroupsItem>()); + pad = LayoutUtils.getPadding(ctx); + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + AvailableGroupsItem item = getItem(position); + Context ctx = getContext(); + + LinearLayout layout = new LinearLayout(ctx); + layout.setOrientation(VERTICAL); + + TextView name = new TextView(ctx); + name.setTextSize(18); + name.setSingleLine(); + name.setEllipsize(END); + name.setPadding(pad, pad, pad, pad); + name.setText(item.getGroup().getName()); + layout.addView(name); + + TextView status = new TextView(ctx); + status.setPadding(pad, 0, pad, pad); + Collection<String> names = new ArrayList<String>(); + for(Contact c : item.getContacts()) names.add(c.getAuthor().getName()); + String format = ctx.getString(R.string.shared_by_format); + status.setText(String.format(format, StringUtils.join(names, ", "))); + layout.addView(status); + + return layout; + } +} diff --git a/briar-android/src/org/briarproject/android/groups/AvailableGroupsItem.java b/briar-android/src/org/briarproject/android/groups/AvailableGroupsItem.java new file mode 100644 index 0000000000000000000000000000000000000000..aaf39676f631b7fdaa7c9447ee6c054e3d5fcad5 --- /dev/null +++ b/briar-android/src/org/briarproject/android/groups/AvailableGroupsItem.java @@ -0,0 +1,23 @@ +package org.briarproject.android.groups; + +import java.util.Collection; + +import org.briarproject.api.Contact; +import org.briarproject.api.messaging.Group; + +class AvailableGroupsItem { + + private final GroupContacts groupContacts; + + AvailableGroupsItem(GroupContacts groupContacts) { + this.groupContacts = groupContacts; + } + + Group getGroup() { + return groupContacts.getGroup(); + } + + Collection<Contact> getContacts() { + return groupContacts.getContacts(); + } +} diff --git a/briar-android/src/org/briarproject/android/groups/AvailableGroupsItemComparator.java b/briar-android/src/org/briarproject/android/groups/AvailableGroupsItemComparator.java new file mode 100644 index 0000000000000000000000000000000000000000..2b9fce5fcd24a39095089ccba6338ac431ac9327 --- /dev/null +++ b/briar-android/src/org/briarproject/android/groups/AvailableGroupsItemComparator.java @@ -0,0 +1,16 @@ +package org.briarproject.android.groups; + +import java.util.Comparator; + +class AvailableGroupsItemComparator implements Comparator<AvailableGroupsItem> { + + static final AvailableGroupsItemComparator INSTANCE = + new AvailableGroupsItemComparator(); + + public int compare(AvailableGroupsItem a, AvailableGroupsItem b) { + if(a == b) return 0; + String aName = a.getGroup().getName(); + String bName = b.getGroup().getName(); + return String.CASE_INSENSITIVE_ORDER.compare(aName, bName); + } +} diff --git a/briar-android/src/org/briarproject/android/groups/ConfigureGroupActivity.java b/briar-android/src/org/briarproject/android/groups/ConfigureGroupActivity.java deleted file mode 100644 index 4c7a2079ed0e83a7c1b2c496b81206609a9d9c49..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/groups/ConfigureGroupActivity.java +++ /dev/null @@ -1,356 +0,0 @@ -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; - -import org.briarproject.R; -import org.briarproject.android.BriarActivity; -import org.briarproject.android.contact.SelectContactsDialog; -import org.briarproject.android.invitation.AddContactActivity; -import org.briarproject.android.util.LayoutUtils; -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.EventBus; -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, EventListener, NoContactsDialog.Listener, -SelectContactsDialog.Listener { - - private static final Logger LOG = - Logger.getLogger(ConfigureGroupActivity.class.getName()); - - private String groupName = null; - private CheckBox subscribeCheckBox = null; - 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; - - // Fields that are accessed from background threads must be volatile - @Inject private volatile DatabaseComponent db; - @Inject private volatile EventBus eventBus; - private volatile GroupId groupId = null; - private volatile Group group = null; - private volatile boolean subscribed = false; - private volatile Collection<Contact> contacts = null; - private volatile Collection<ContactId> selected = null; - - @Override - public void onCreate(Bundle state) { - super.onCreate(state); - - Intent i = getIntent(); - byte[] b = i.getByteArrayExtra("briar.GROUP_ID"); - if(b == null) throw new IllegalStateException(); - groupId = new GroupId(b); - groupName = i.getStringExtra("briar.GROUP_NAME"); - if(groupName == null) throw new IllegalStateException(); - setTitle(groupName); - b = i.getByteArrayExtra("briar.GROUP_SALT"); - if(b == null) throw new IllegalStateException(); - group = new Group(groupId, groupName, b); - subscribed = i.getBooleanExtra("briar.SUBSCRIBED", false); - boolean all = i.getBooleanExtra("briar.VISIBLE_TO_ALL", false); - - LinearLayout layout = new LinearLayout(this); - layout.setLayoutParams(MATCH_MATCH); - layout.setOrientation(VERTICAL); - layout.setGravity(CENTER_HORIZONTAL); - 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); - subscribeCheckBox.setChecked(subscribed); - subscribeCheckBox.setOnClickListener(this); - layout.addView(subscribeCheckBox); - - radioGroup = new RadioGroup(this); - radioGroup.setOrientation(VERTICAL); - - visibleToAll = new RadioButton(this); - visibleToAll.setId(2); - visibleToAll.setText(R.string.forum_visible_to_all); - visibleToAll.setEnabled(subscribed); - visibleToAll.setOnClickListener(this); - radioGroup.addView(visibleToAll); - - visibleToSome = new RadioButton(this); - visibleToSome.setId(3); - visibleToSome.setText(R.string.forum_visible_to_some); - visibleToSome.setEnabled(subscribed); - visibleToSome.setOnClickListener(this); - radioGroup.addView(visibleToSome); - - if(!subscribed || all) radioGroup.check(visibleToAll.getId()); - else radioGroup.check(visibleToSome.getId()); - layout.addView(radioGroup); - - doneButton = new Button(this); - doneButton.setLayoutParams(WRAP_WRAP); - doneButton.setText(R.string.done_button); - doneButton.setOnClickListener(this); - layout.addView(doneButton); - - progress = new ProgressBar(this); - progress.setLayoutParams(WRAP_WRAP); - progress.setIndeterminate(true); - progress.setVisibility(GONE); - layout.addView(progress); - - setContentView(layout); - } - - @Override - public void onResume() { - super.onResume(); - eventBus.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(); - eventBus.removeListener(this); - } - - public void onClick(View view) { - if(view == subscribeCheckBox) { - changed = true; - boolean subscribe = subscribeCheckBox.isChecked(); - visibleToAll.setEnabled(subscribe); - visibleToSome.setEnabled(subscribe); - } else if(view == visibleToAll) { - changed = true; - } else if(view == visibleToSome) { - changed = true; - if(contacts == null) loadVisibleContacts(); - else displayVisibleContacts(); - } else if(view == doneButton) { - if(changed) { - // Replace the button with a progress bar - doneButton.setVisibility(GONE); - progress.setVisibility(VISIBLE); - // Update the group in a background thread - boolean subscribe = subscribeCheckBox.isChecked(); - boolean all = visibleToAll.isChecked(); - updateGroup(subscribe, all); - } else { - finish(); - } - } - } - - private void loadVisibleContacts() { - runOnDbThread(new Runnable() { - public void run() { - try { - long now = System.currentTimeMillis(); - contacts = db.getContacts(); - selected = db.getVisibility(groupId); - long duration = System.currentTimeMillis() - now; - if(LOG.isLoggable(INFO)) - LOG.info("Load took " + duration + " ms"); - displayVisibleContacts(); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } - - private void displayVisibleContacts() { - runOnUiThread(new Runnable() { - public void run() { - if(contacts.isEmpty()) { - NoContactsDialog builder = new NoContactsDialog(); - builder.setListener(ConfigureGroupActivity.this); - builder.build(ConfigureGroupActivity.this).show(); - } else { - SelectContactsDialog builder = new SelectContactsDialog(); - builder.setListener(ConfigureGroupActivity.this); - builder.setContacts(contacts); - builder.setSelected(selected); - builder.build(ConfigureGroupActivity.this).show(); - } - } - }); - } - - private void updateGroup(final boolean subscribe, final boolean all) { - runOnDbThread(new Runnable() { - public void run() { - try { - long now = System.currentTimeMillis(); - if(subscribe) { - if(!subscribed) db.addGroup(group); - db.setVisibleToAll(groupId, all); - if(!all) db.setVisibility(groupId, selected); - } else if(subscribed) { - db.removeGroup(group); - } - long duration = System.currentTimeMillis() - now; - if(LOG.isLoggable(INFO)) - LOG.info("Update took " + duration + " ms"); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - if(subscribe) showGroup(); - else finishOnUiThread(); - } - }); - } - - private void showGroup() { - runOnUiThread(new Runnable() { - public void run() { - Intent i = new Intent(ConfigureGroupActivity.this, - GroupActivity.class); - i.setFlags(FLAG_ACTIVITY_CLEAR_TOP); - i.putExtra("briar.GROUP_ID", groupId.getBytes()); - i.putExtra("briar.GROUP_NAME", groupName); - startActivity(i); - finish(); - } - }); - } - - 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)); - } - - public void contactCreationCancelled() {} - - public void contactsSelected(Collection<ContactId> selected) { - this.selected = Collections.unmodifiableCollection(selected); - } - - public void contactSelectionCancelled() {} -} diff --git a/briar-android/src/org/briarproject/android/groups/CreateGroupActivity.java b/briar-android/src/org/briarproject/android/groups/CreateGroupActivity.java index 568788e963a95fb23fc5c06a9a32e45aba85239a..85d16deb29deb77819cfbb60a67588efa5ccbd65 100644 --- a/briar-android/src/org/briarproject/android/groups/CreateGroupActivity.java +++ b/briar-android/src/org/briarproject/android/groups/CreateGroupActivity.java @@ -14,19 +14,13 @@ import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; import static org.briarproject.android.util.CommonLayoutParams.WRAP_WRAP; import static org.briarproject.api.messaging.MessagingConstants.MAX_GROUP_NAME_LENGTH; -import java.util.Collection; -import java.util.Collections; import java.util.logging.Logger; import javax.inject.Inject; import org.briarproject.R; import org.briarproject.android.BriarActivity; -import org.briarproject.android.contact.SelectContactsDialog; -import org.briarproject.android.invitation.AddContactActivity; import org.briarproject.android.util.LayoutUtils; -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.messaging.Group; @@ -42,31 +36,24 @@ import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.ProgressBar; -import android.widget.RadioButton; -import android.widget.RadioGroup; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; public class CreateGroupActivity extends BriarActivity -implements OnEditorActionListener, OnClickListener, NoContactsDialog.Listener, -SelectContactsDialog.Listener { +implements OnEditorActionListener, OnClickListener { private static final Logger LOG = Logger.getLogger(CreateGroupActivity.class.getName()); private EditText nameEntry = null; - private RadioGroup radioGroup = null; - private RadioButton visibleToAll = null, visibleToSome = null; - private Button createButton = null; + private Button createForumButton = null; private ProgressBar progress = null; private TextView feedback = null; // Fields that are accessed from background threads must be volatile @Inject private volatile GroupFactory groupFactory; @Inject private volatile DatabaseComponent db; - private volatile Collection<Contact> contacts = null; - private volatile Collection<ContactId> selected = Collections.emptySet(); @Override public void onCreate(Bundle state) { @@ -97,32 +84,16 @@ SelectContactsDialog.Listener { nameEntry.setOnEditorActionListener(this); layout.addView(nameEntry); - radioGroup = new RadioGroup(this); - radioGroup.setOrientation(VERTICAL); - - visibleToAll = new RadioButton(this); - visibleToAll.setId(2); - visibleToAll.setText(R.string.forum_visible_to_all); - visibleToAll.setOnClickListener(this); - radioGroup.addView(visibleToAll); - - visibleToSome = new RadioButton(this); - visibleToSome.setId(3); - visibleToSome.setText(R.string.forum_visible_to_some); - visibleToSome.setOnClickListener(this); - radioGroup.addView(visibleToSome); - layout.addView(radioGroup); - feedback = new TextView(this); feedback.setGravity(CENTER); feedback.setPadding(0, pad, 0, pad); layout.addView(feedback); - createButton = new Button(this); - createButton.setLayoutParams(WRAP_WRAP); - createButton.setText(R.string.create_button); - createButton.setOnClickListener(this); - layout.addView(createButton); + createForumButton = new Button(this); + createForumButton.setLayoutParams(WRAP_WRAP); + createForumButton.setText(R.string.create_forum_button); + createForumButton.setOnClickListener(this); + layout.addView(createForumButton); progress = new ProgressBar(this); progress.setLayoutParams(WRAP_WRAP); @@ -135,9 +106,7 @@ SelectContactsDialog.Listener { private void enableOrDisableCreateButton() { if(progress == null) return; // Not created yet - boolean nameValid = validateName(); - boolean visibilitySelected = radioGroup.getCheckedRadioButtonId() != -1; - createButton.setEnabled(nameValid && visibilitySelected); + createForumButton.setEnabled(validateName()); } public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) { @@ -156,67 +125,22 @@ SelectContactsDialog.Listener { } public void onClick(View view) { - if(view == visibleToAll) { - enableOrDisableCreateButton(); - } else if(view == visibleToSome) { - if(contacts == null) loadContacts(); - else displayContacts(); - } else if(view == createButton) { + if(view == createForumButton) { hideSoftKeyboard(); if(!validateName()) return; - createButton.setVisibility(GONE); + createForumButton.setVisibility(GONE); progress.setVisibility(VISIBLE); - String name = nameEntry.getText().toString(); - boolean all = visibleToAll.isChecked(); - storeGroup(name, all); + storeGroup(nameEntry.getText().toString()); } } - private void loadContacts() { - runOnDbThread(new Runnable() { - public void run() { - try { - long now = System.currentTimeMillis(); - contacts = db.getContacts(); - long duration = System.currentTimeMillis() - now; - if(LOG.isLoggable(INFO)) - LOG.info("Load took " + duration + " ms"); - displayContacts(); - } catch(DbException e) { - if(LOG.isLoggable(WARNING)) - LOG.log(WARNING, e.toString(), e); - } - } - }); - } - - private void displayContacts() { - runOnUiThread(new Runnable() { - public void run() { - if(contacts.isEmpty()) { - NoContactsDialog builder = new NoContactsDialog(); - builder.setListener(CreateGroupActivity.this); - builder.build(CreateGroupActivity.this).show(); - } else { - SelectContactsDialog builder = new SelectContactsDialog(); - builder.setListener(CreateGroupActivity.this); - builder.setContacts(contacts); - builder.setSelected(selected); - builder.build(CreateGroupActivity.this).show(); - } - } - }); - } - - private void storeGroup(final String name, final boolean all) { + private void storeGroup(final String name) { runOnDbThread(new Runnable() { public void run() { try { Group g = groupFactory.createGroup(name); long now = System.currentTimeMillis(); db.addGroup(g); - if(all) db.setVisibleToAll(g.getId(), true); - else db.setVisibility(g.getId(), selected); long duration = System.currentTimeMillis() - now; if(LOG.isLoggable(INFO)) LOG.info("Storing group took " + duration + " ms"); @@ -244,21 +168,4 @@ SelectContactsDialog.Listener { } }); } - - public void contactCreationSelected() { - startActivity(new Intent(this, AddContactActivity.class)); - } - - public void contactCreationCancelled() { - enableOrDisableCreateButton(); - } - - public void contactsSelected(Collection<ContactId> selected) { - this.selected = Collections.unmodifiableCollection(selected); - enableOrDisableCreateButton(); - } - - public void contactSelectionCancelled() { - enableOrDisableCreateButton(); - } } diff --git a/briar-android/src/org/briarproject/android/groups/GroupActivity.java b/briar-android/src/org/briarproject/android/groups/GroupActivity.java index 7d8aa13d42ba1620311bd3fff6e6d97f5bbad14f..ee516fc919987e18b29333566c5429c26927dae0 100644 --- a/briar-android/src/org/briarproject/android/groups/GroupActivity.java +++ b/briar-android/src/org/briarproject/android/groups/GroupActivity.java @@ -69,7 +69,7 @@ OnClickListener, OnItemClickListener { private GroupAdapter adapter = null; private ListView list = null; private ListLoadingProgressBar loading = null; - private ImageButton composeButton = null, configureButton = null; + private ImageButton composeButton = null, shareButton = null; // Fields that are accessed from background threads must be volatile @Inject private volatile DatabaseComponent db; @@ -85,6 +85,8 @@ OnClickListener, OnItemClickListener { byte[] b = i.getByteArrayExtra("briar.GROUP_ID"); if(b == null) throw new IllegalStateException(); groupId = new GroupId(b); + String name = i.getStringExtra("briar.GROUP_NAME"); + if(name != null) setTitle(name); LinearLayout layout = new LinearLayout(this); layout.setLayoutParams(MATCH_MATCH); @@ -95,7 +97,7 @@ OnClickListener, OnItemClickListener { empty.setLayoutParams(MATCH_WRAP_1); empty.setGravity(CENTER); empty.setTextSize(18); - empty.setText(R.string.no_posts); + empty.setText(R.string.no_forum_posts); empty.setVisibility(GONE); layout.addView(empty); @@ -127,11 +129,11 @@ OnClickListener, OnItemClickListener { footer.addView(composeButton); footer.addView(new ElasticHorizontalSpace(this)); - configureButton = new ImageButton(this); - configureButton.setBackgroundResource(0); - configureButton.setImageResource(R.drawable.action_settings); - configureButton.setOnClickListener(this); - footer.addView(configureButton); + shareButton = new ImageButton(this); + shareButton.setBackgroundResource(0); + shareButton.setImageResource(R.drawable.social_share); + shareButton.setOnClickListener(this); + footer.addView(shareButton); footer.addView(new ElasticHorizontalSpace(this)); layout.addView(footer); @@ -334,12 +336,10 @@ OnClickListener, OnItemClickListener { i.putExtra("briar.GROUP_NAME", group.getName()); i.putExtra("briar.MIN_TIMESTAMP", getMinTimestampForNewMessage()); startActivity(i); - } else if(view == configureButton) { - Intent i = new Intent(this, ConfigureGroupActivity.class); + } else if(view == shareButton) { + Intent i = new Intent(this, ShareGroupActivity.class); i.putExtra("briar.GROUP_ID", groupId.getBytes()); i.putExtra("briar.GROUP_NAME", group.getName()); - i.putExtra("briar.GROUP_SALT", group.getSalt()); - i.putExtra("briar.SUBSCRIBED", true); startActivity(i); } } diff --git a/briar-android/src/org/briarproject/android/groups/GroupContacts.java b/briar-android/src/org/briarproject/android/groups/GroupContacts.java new file mode 100644 index 0000000000000000000000000000000000000000..62bca859e3ae4983406a503dd2a5cde669f55755 --- /dev/null +++ b/briar-android/src/org/briarproject/android/groups/GroupContacts.java @@ -0,0 +1,25 @@ +package org.briarproject.android.groups; + +import java.util.Collection; + +import org.briarproject.api.Contact; +import org.briarproject.api.messaging.Group; + +class GroupContacts { + + private final Group group; + private final Collection<Contact> contacts; + + GroupContacts(Group group, Collection<Contact> contacts) { + this.group = group; + this.contacts = contacts; + } + + Group getGroup() { + return group; + } + + Collection<Contact> getContacts() { + return contacts; + } +} diff --git a/briar-android/src/org/briarproject/android/groups/GroupListActivity.java b/briar-android/src/org/briarproject/android/groups/GroupListActivity.java index 769818a42d2bc986184034f09b3b4d19e402516b..6258b32de8e08f1e63cc41bde07fa01e4bf45281 100644 --- a/briar-android/src/org/briarproject/android/groups/GroupListActivity.java +++ b/briar-android/src/org/briarproject/android/groups/GroupListActivity.java @@ -2,10 +2,12 @@ package org.briarproject.android.groups; import static android.view.Gravity.CENTER; import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.Menu.NONE; 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 android.widget.Toast.LENGTH_SHORT; import static java.util.logging.Level.INFO; import static java.util.logging.Level.WARNING; import static org.briarproject.android.util.CommonLayoutParams.MATCH_MATCH; @@ -21,7 +23,6 @@ import javax.inject.Inject; import org.briarproject.R; import org.briarproject.android.BriarActivity; -import org.briarproject.android.util.ElasticHorizontalSpace; import org.briarproject.android.util.HorizontalBorder; import org.briarproject.android.util.LayoutUtils; import org.briarproject.android.util.ListLoadingProgressBar; @@ -39,23 +40,30 @@ import org.briarproject.api.event.SubscriptionAddedEvent; import org.briarproject.api.event.SubscriptionRemovedEvent; import org.briarproject.api.messaging.Group; import org.briarproject.api.messaging.GroupId; -import org.briarproject.api.messaging.GroupStatus; import android.content.Intent; import android.content.res.Resources; import android.os.Bundle; +import android.view.ContextMenu; +import android.view.ContextMenu.ContextMenuInfo; +import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; +import android.view.View.OnCreateContextMenuListener; import android.widget.AdapterView; +import android.widget.AdapterView.AdapterContextMenuInfo; import android.widget.AdapterView.OnItemClickListener; import android.widget.ImageButton; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.TextView; +import android.widget.Toast; public class GroupListActivity extends BriarActivity -implements EventListener, OnClickListener, OnItemClickListener { +implements EventListener, OnClickListener, OnItemClickListener, +OnCreateContextMenuListener { + private static final int MENU_ITEM_UNSUBSCRIBE = 1; private static final Logger LOG = Logger.getLogger(GroupListActivity.class.getName()); @@ -67,7 +75,7 @@ implements EventListener, OnClickListener, OnItemClickListener { private ListView list = null; private ListLoadingProgressBar loading = null; private TextView available = null; - private ImageButton newGroupButton = null, manageGroupsButton = null; + private ImageButton newGroupButton = null; // Fields that are accessed from background threads must be volatile @Inject private volatile DatabaseComponent db; @@ -96,6 +104,7 @@ implements EventListener, OnClickListener, OnItemClickListener { list.setLayoutParams(MATCH_WRAP_1); list.setAdapter(adapter); list.setOnItemClickListener(this); + list.setOnCreateContextMenuListener(this); list.setVisibility(GONE); layout.addView(list); @@ -107,10 +116,10 @@ implements EventListener, OnClickListener, OnItemClickListener { available.setLayoutParams(MATCH_WRAP); available.setGravity(CENTER); available.setTextSize(18); + available.setPadding(pad, pad, pad, pad); Resources res = getResources(); - int background = res.getColor(R.color.groups_available_background); + int background = res.getColor(R.color.forums_available_background); available.setBackgroundColor(background); - available.setPadding(pad, pad, pad, pad); available.setOnClickListener(this); available.setVisibility(GONE); layout.addView(available); @@ -122,21 +131,11 @@ implements EventListener, OnClickListener, OnItemClickListener { footer.setOrientation(HORIZONTAL); footer.setGravity(CENTER); footer.setBackgroundColor(res.getColor(R.color.button_bar_background)); - footer.addView(new ElasticHorizontalSpace(this)); - newGroupButton = new ImageButton(this); newGroupButton.setBackgroundResource(0); newGroupButton.setImageResource(R.drawable.social_new_chat); newGroupButton.setOnClickListener(this); footer.addView(newGroupButton); - footer.addView(new ElasticHorizontalSpace(this)); - - manageGroupsButton = new ImageButton(this); - manageGroupsButton.setBackgroundResource(0); - manageGroupsButton.setImageResource(R.drawable.action_settings); - manageGroupsButton.setOnClickListener(this); - footer.addView(manageGroupsButton); - footer.addView(new ElasticHorizontalSpace(this)); layout.addView(footer); setContentView(layout); @@ -154,26 +153,19 @@ implements EventListener, OnClickListener, OnItemClickListener { runOnDbThread(new Runnable() { public void run() { try { - int availableCount = 0; long now = System.currentTimeMillis(); - for(GroupStatus s : db.getAvailableGroups()) { - Group g = s.getGroup(); - if(s.isSubscribed()) { - try { - Collection<MessageHeader> headers = - db.getMessageHeaders(g.getId()); - displayHeaders(g, headers); - } catch(NoSuchSubscriptionException e) { - // Continue - } - } else { - availableCount++; + for(Group g : db.getGroups()) { + try { + displayHeaders(g, db.getMessageHeaders(g.getId())); + } catch(NoSuchSubscriptionException e) { + // Continue } } + int available = db.getAvailableGroups().size(); long duration = System.currentTimeMillis() - now; if(LOG.isLoggable(INFO)) LOG.info("Full load took " + duration + " ms"); - displayAvailable(availableCount); + displayAvailable(available); } catch(DbException e) { if(LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e); @@ -226,7 +218,7 @@ implements EventListener, OnClickListener, OnItemClickListener { } else { available.setVisibility(VISIBLE); available.setText(getResources().getQuantityString( - R.plurals.forums_available, availableCount, + R.plurals.forums_shared, availableCount, availableCount)); } } @@ -279,9 +271,8 @@ implements EventListener, OnClickListener, OnItemClickListener { } else if(e instanceof SubscriptionRemovedEvent) { Group g = ((SubscriptionRemovedEvent) e).getGroup(); if(groups.containsKey(g.getId())) { - // Reload the group, expecting NoSuchSubscriptionException LOG.info("Group removed, reloading"); - loadHeaders(g); + loadHeaders(); } } } @@ -330,10 +321,8 @@ implements EventListener, OnClickListener, OnItemClickListener { runOnDbThread(new Runnable() { public void run() { try { - int available = 0; long now = System.currentTimeMillis(); - for(GroupStatus s : db.getAvailableGroups()) - if(!s.isSubscribed()) available++; + int available = db.getAvailableGroups().size(); long duration = System.currentTimeMillis() - now; if(LOG.isLoggable(INFO)) LOG.info("Loading available took " + duration + " ms"); @@ -348,11 +337,9 @@ implements EventListener, OnClickListener, OnItemClickListener { public void onClick(View view) { if(view == available) { - startActivity(new Intent(this, ManageGroupsActivity.class)); + startActivity(new Intent(this, AvailableGroupsActivity.class)); } else if(view == newGroupButton) { startActivity(new Intent(this, CreateGroupActivity.class)); - } else if(view == manageGroupsButton) { - startActivity(new Intent(this, ManageGroupsActivity.class)); } } @@ -364,4 +351,41 @@ implements EventListener, OnClickListener, OnItemClickListener { i.putExtra("briar.GROUP_NAME", g.getName()); startActivity(i); } + + @Override + public void onCreateContextMenu(ContextMenu menu, View view, + ContextMenu.ContextMenuInfo info) { + String delete = getString(R.string.unsubscribe); + menu.add(NONE, MENU_ITEM_UNSUBSCRIBE, NONE, delete); + } + + @Override + public boolean onContextItemSelected(MenuItem menuItem) { + if(menuItem.getItemId() == MENU_ITEM_UNSUBSCRIBE) { + ContextMenuInfo info = menuItem.getMenuInfo(); + int position = ((AdapterContextMenuInfo) info).position; + GroupListItem item = adapter.getItem(position); + removeSubscription(item.getGroup()); + String unsubscribed = getString(R.string.unsubscribed_toast); + Toast.makeText(this, unsubscribed, LENGTH_SHORT).show(); + } + return true; + } + + private void removeSubscription(final Group g) { + runOnDbThread(new Runnable() { + public void run() { + try { + long now = System.currentTimeMillis(); + db.removeGroup(g); + long duration = System.currentTimeMillis() - now; + if(LOG.isLoggable(INFO)) + LOG.info("Removing group took " + duration + " ms"); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } } \ No newline at end of file diff --git a/briar-android/src/org/briarproject/android/groups/GroupListAdapter.java b/briar-android/src/org/briarproject/android/groups/GroupListAdapter.java index a3df0e9f01f3ae463c13885117390b31603e25e8..b9018350f89ea9de76e40e523273480a1ca43105 100644 --- a/briar-android/src/org/briarproject/android/groups/GroupListAdapter.java +++ b/briar-android/src/org/briarproject/android/groups/GroupListAdapter.java @@ -55,7 +55,7 @@ class GroupListAdapter extends ArrayAdapter<GroupListItem> { TextView noPosts = new TextView(ctx); noPosts.setPadding(pad, 0, pad, pad); noPosts.setTextColor(res.getColor(R.color.no_posts)); - noPosts.setText(R.string.no_posts); + noPosts.setText(R.string.no_forum_posts); layout.addView(noPosts); } else { TextView date = new TextView(ctx); diff --git a/briar-android/src/org/briarproject/android/groups/ManageGroupsAdapter.java b/briar-android/src/org/briarproject/android/groups/ManageGroupsAdapter.java deleted file mode 100644 index 38f419a4196249ff0fc5e966da05b46faa688504..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/groups/ManageGroupsAdapter.java +++ /dev/null @@ -1,72 +0,0 @@ -package org.briarproject.android.groups; - -import static android.text.TextUtils.TruncateAt.END; -import static android.view.View.INVISIBLE; -import static android.widget.LinearLayout.HORIZONTAL; -import static android.widget.LinearLayout.VERTICAL; - -import java.util.ArrayList; - -import org.briarproject.R; -import org.briarproject.android.util.LayoutUtils; -import org.briarproject.api.messaging.GroupStatus; - -import android.content.Context; -import android.view.View; -import android.view.ViewGroup; -import android.widget.ArrayAdapter; -import android.widget.ImageView; -import android.widget.LinearLayout; -import android.widget.TextView; - -class ManageGroupsAdapter extends ArrayAdapter<ManageGroupsItem> { - - private final int pad; - - ManageGroupsAdapter(Context ctx) { - super(ctx, android.R.layout.simple_expandable_list_item_1, - new ArrayList<ManageGroupsItem>()); - pad = LayoutUtils.getPadding(ctx); - } - - @Override - public View getView(int position, View convertView, ViewGroup parent) { - ManageGroupsItem item = getItem(position); - GroupStatus groupStatus = item.getGroupStatus(); - Context ctx = getContext(); - - LinearLayout layout = new LinearLayout(ctx); - layout.setOrientation(HORIZONTAL); - - ImageView subscribed = new ImageView(ctx); - subscribed.setPadding(pad, pad, pad, pad); - subscribed.setImageResource(R.drawable.navigation_accept); - if(!groupStatus.isSubscribed()) subscribed.setVisibility(INVISIBLE); - layout.addView(subscribed); - - LinearLayout innerLayout = new LinearLayout(ctx); - innerLayout.setOrientation(VERTICAL); - - TextView name = new TextView(ctx); - name.setTextSize(18); - name.setSingleLine(); - name.setEllipsize(END); - name.setPadding(0, pad, pad, pad); - name.setText(groupStatus.getGroup().getName()); - innerLayout.addView(name); - - TextView status = new TextView(ctx); - status.setPadding(0, 0, pad, pad); - if(groupStatus.isSubscribed()) { - if(groupStatus.isVisibleToAll()) - status.setText(R.string.subscribed_all); - else status.setText(R.string.subscribed_some); - } else { - status.setText(R.string.not_subscribed); - } - innerLayout.addView(status); - layout.addView(innerLayout); - - return layout; - } -} diff --git a/briar-android/src/org/briarproject/android/groups/ManageGroupsItem.java b/briar-android/src/org/briarproject/android/groups/ManageGroupsItem.java deleted file mode 100644 index 16f67beaae553c0c2a2637fa203bfdc46c6a621f..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/groups/ManageGroupsItem.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.briarproject.android.groups; - -import org.briarproject.api.messaging.GroupStatus; - -class ManageGroupsItem { - - private final GroupStatus status; - - ManageGroupsItem(GroupStatus status) { - this.status = status; - } - - GroupStatus getGroupStatus() { - return status; - } -} diff --git a/briar-android/src/org/briarproject/android/groups/ManageGroupsItemComparator.java b/briar-android/src/org/briarproject/android/groups/ManageGroupsItemComparator.java deleted file mode 100644 index def3fa7a7c87e48355b3c536a0615c099035154c..0000000000000000000000000000000000000000 --- a/briar-android/src/org/briarproject/android/groups/ManageGroupsItemComparator.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.briarproject.android.groups; - -import java.util.Comparator; - -class ManageGroupsItemComparator implements Comparator<ManageGroupsItem> { - - static final ManageGroupsItemComparator INSTANCE = - new ManageGroupsItemComparator(); - - public int compare(ManageGroupsItem a, ManageGroupsItem b) { - if(a == b) return 0; - String aName = a.getGroupStatus().getGroup().getName(); - String bName = b.getGroupStatus().getGroup().getName(); - return String.CASE_INSENSITIVE_ORDER.compare(aName, bName); - } -} diff --git a/briar-android/src/org/briarproject/android/groups/ShareGroupActivity.java b/briar-android/src/org/briarproject/android/groups/ShareGroupActivity.java new file mode 100644 index 0000000000000000000000000000000000000000..b0eed589992f2d3b30e714aa6ba92321e4a20fec --- /dev/null +++ b/briar-android/src/org/briarproject/android/groups/ShareGroupActivity.java @@ -0,0 +1,202 @@ +package org.briarproject.android.groups; + +import static android.view.Gravity.CENTER_HORIZONTAL; +import static android.view.View.GONE; +import static android.view.View.VISIBLE; +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.Collection; +import java.util.Collections; +import java.util.logging.Logger; + +import javax.inject.Inject; + +import org.briarproject.R; +import org.briarproject.android.BriarActivity; +import org.briarproject.android.contact.SelectContactsDialog; +import org.briarproject.android.invitation.AddContactActivity; +import org.briarproject.android.util.LayoutUtils; +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.messaging.GroupId; + +import android.content.Intent; +import android.os.Bundle; +import android.view.View; +import android.view.View.OnClickListener; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.ProgressBar; +import android.widget.RadioButton; +import android.widget.RadioGroup; + +public class ShareGroupActivity extends BriarActivity +implements OnClickListener, NoContactsDialog.Listener, +SelectContactsDialog.Listener { + + private static final Logger LOG = + Logger.getLogger(ShareGroupActivity.class.getName()); + + private String groupName = null; + private RadioGroup radioGroup = null; + private RadioButton shareWithAll = null, shareWithSome = null; + private Button shareButton = null; + private ProgressBar progress = null; + private boolean changed = false; + + // Fields that are accessed from background threads must be volatile + @Inject private volatile DatabaseComponent db; + private volatile GroupId groupId = null; + private volatile Collection<Contact> contacts = null; + private volatile Collection<ContactId> selected = null; + + @Override + public void onCreate(Bundle state) { + super.onCreate(state); + + Intent i = getIntent(); + byte[] b = i.getByteArrayExtra("briar.GROUP_ID"); + if(b == null) throw new IllegalStateException(); + groupId = new GroupId(b); + groupName = i.getStringExtra("briar.GROUP_NAME"); + if(groupName == null) throw new IllegalStateException(); + setTitle(groupName); + + LinearLayout layout = new LinearLayout(this); + layout.setLayoutParams(MATCH_MATCH); + layout.setOrientation(VERTICAL); + layout.setGravity(CENTER_HORIZONTAL); + int pad = LayoutUtils.getPadding(this); + layout.setPadding(pad, pad, pad, pad); + + radioGroup = new RadioGroup(this); + radioGroup.setOrientation(VERTICAL); + radioGroup.setPadding(0, 0, 0, pad); + + shareWithAll = new RadioButton(this); + shareWithAll.setId(2); + shareWithAll.setText(R.string.forum_share_with_all); + shareWithAll.setOnClickListener(this); + radioGroup.addView(shareWithAll); + + shareWithSome = new RadioButton(this); + shareWithSome.setId(3); + shareWithSome.setText(R.string.forum_share_with_some); + shareWithSome.setOnClickListener(this); + radioGroup.addView(shareWithSome); + + layout.addView(radioGroup); + + shareButton = new Button(this); + shareButton.setLayoutParams(WRAP_WRAP); + shareButton.setText(R.string.share_button); + shareButton.setOnClickListener(this); + layout.addView(shareButton); + + progress = new ProgressBar(this); + progress.setLayoutParams(WRAP_WRAP); + progress.setIndeterminate(true); + progress.setVisibility(GONE); + layout.addView(progress); + + setContentView(layout); + } + + public void onClick(View view) { + if(view == shareWithAll) { + changed = true; + } else if(view == shareWithSome) { + changed = true; + if(contacts == null) loadVisibility(); + else displayVisibility(); + } else if(view == shareButton) { + if(changed) { + // Replace the button with a progress bar + shareButton.setVisibility(GONE); + progress.setVisibility(VISIBLE); + // Update the group in a background thread + storeVisibility(shareWithAll.isChecked()); + } else { + finish(); + } + } + } + + private void loadVisibility() { + runOnDbThread(new Runnable() { + public void run() { + try { + long now = System.currentTimeMillis(); + contacts = db.getContacts(); + selected = db.getVisibility(groupId); + long duration = System.currentTimeMillis() - now; + if(LOG.isLoggable(INFO)) + LOG.info("Load took " + duration + " ms"); + displayVisibility(); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + } + }); + } + + private void displayVisibility() { + runOnUiThread(new Runnable() { + public void run() { + if(contacts.isEmpty()) { + NoContactsDialog builder = new NoContactsDialog(); + builder.setListener(ShareGroupActivity.this); + builder.build(ShareGroupActivity.this).show(); + } else { + SelectContactsDialog builder = new SelectContactsDialog(); + builder.setListener(ShareGroupActivity.this); + builder.setContacts(contacts); + builder.setSelected(selected); + builder.build(ShareGroupActivity.this).show(); + } + } + }); + } + + private void storeVisibility(final boolean all) { + runOnDbThread(new Runnable() { + public void run() { + try { + long now = System.currentTimeMillis(); + db.setVisibleToAll(groupId, all); + if(!all) db.setVisibility(groupId, selected); + long duration = System.currentTimeMillis() - now; + if(LOG.isLoggable(INFO)) + LOG.info("Update took " + duration + " ms"); + } catch(DbException e) { + if(LOG.isLoggable(WARNING)) + LOG.log(WARNING, e.toString(), e); + } + finishOnUiThread(); + } + }); + } + + public void contactCreationSelected() { + startActivity(new Intent(this, AddContactActivity.class)); + } + + public void contactCreationCancelled() { + radioGroup.clearCheck(); + } + + public void contactsSelected(Collection<ContactId> selected) { + this.selected = Collections.unmodifiableCollection(selected); + } + + public void contactSelectionCancelled() { + radioGroup.clearCheck(); + } +} diff --git a/briar-android/src/org/briarproject/android/groups/WriteGroupPostActivity.java b/briar-android/src/org/briarproject/android/groups/WriteGroupPostActivity.java index 27d8f92852f7bb4431ac5eec5eb3d0e26120aa04..c2c3bea20f5695cded15dbb513fcf0b0d45cab3c 100644 --- a/briar-android/src/org/briarproject/android/groups/WriteGroupPostActivity.java +++ b/briar-android/src/org/briarproject/android/groups/WriteGroupPostActivity.java @@ -149,7 +149,7 @@ implements OnItemSelectedListener, OnClickListener { int inputType = TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | TYPE_TEXT_FLAG_CAP_SENTENCES; content.setInputType(inputType); - content.setHint(R.string.group_post_hint); + content.setHint(R.string.forum_post_hint); layout.addView(content); setContentView(layout); diff --git a/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java b/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java index e6e1a480bce3aa787c1d2a430b8ff3768b8ba2a5..ded8912dbc986d14707ead366bff1c55612b7710 100644 --- a/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java +++ b/briar-android/src/org/briarproject/android/identity/CreateIdentityActivity.java @@ -52,7 +52,7 @@ implements OnEditorActionListener, OnClickListener { @Inject @CryptoExecutor private Executor cryptoExecutor; private EditText nicknameEntry = null; - private Button createButton = null; + private Button createIdentityButton = null; private ProgressBar progress = null; private TextView feedback = null; @@ -96,12 +96,12 @@ implements OnEditorActionListener, OnClickListener { feedback.setPadding(0, pad, 0, pad); layout.addView(feedback); - createButton = new Button(this); - createButton.setLayoutParams(WRAP_WRAP); - createButton.setText(R.string.create_button); - createButton.setEnabled(false); - createButton.setOnClickListener(this); - layout.addView(createButton); + createIdentityButton = new Button(this); + createIdentityButton.setLayoutParams(WRAP_WRAP); + createIdentityButton.setText(R.string.create_identity_button); + createIdentityButton.setEnabled(false); + createIdentityButton.setOnClickListener(this); + layout.addView(createIdentityButton); progress = new ProgressBar(this); progress.setLayoutParams(WRAP_WRAP); @@ -114,7 +114,7 @@ implements OnEditorActionListener, OnClickListener { private void enableOrDisableCreateButton() { if(progress == null) return; // Not created yet - createButton.setEnabled(validateNickname()); + createIdentityButton.setEnabled(validateNickname()); } public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) { @@ -137,7 +137,7 @@ implements OnEditorActionListener, OnClickListener { hideSoftKeyboard(); if(!validateNickname()) return; // Replace the button with a progress bar - createButton.setVisibility(GONE); + createIdentityButton.setVisibility(GONE); progress.setVisibility(VISIBLE); // Create the identity in a background thread final String nickname = nicknameEntry.getText().toString(); diff --git a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java index 5159ac0220bc19863092d67a1fe6b0427c15143c..852b84a9fc20113bded8bf149219e1411ea54c9d 100644 --- a/briar-api/src/org/briarproject/api/db/DatabaseComponent.java +++ b/briar-api/src/org/briarproject/api/db/DatabaseComponent.java @@ -16,7 +16,6 @@ import org.briarproject.api.TransportProperties; import org.briarproject.api.messaging.Ack; import org.briarproject.api.messaging.Group; import org.briarproject.api.messaging.GroupId; -import org.briarproject.api.messaging.GroupStatus; import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageId; import org.briarproject.api.messaging.Offer; @@ -157,11 +156,8 @@ public interface DatabaseComponent { Collection<TransportUpdate> generateTransportUpdates(ContactId c, int maxLatency) throws DbException; - /** - * Returns the status of all groups to which the user subscribes or can - * subscribe, excluding inbox groups. - */ - Collection<GroupStatus> getAvailableGroups() throws DbException; + /** Returns all groups to which the user could subscribe. */ + Collection<Group> getAvailableGroups() throws DbException; /** Returns the configuration for the given transport. */ TransportConfig getConfig(TransportId t) throws DbException; @@ -175,7 +171,7 @@ public interface DatabaseComponent { /** Returns the group with the given ID, if the user subscribes to it. */ Group getGroup(GroupId g) throws DbException; - /** Returns all groups to which the user subscribes. */ + /** Returns all groups to which the user subscribes, excluding inboxes. */ Collection<Group> getGroups() throws DbException; /** diff --git a/briar-api/src/org/briarproject/api/messaging/GroupStatus.java b/briar-api/src/org/briarproject/api/messaging/GroupStatus.java deleted file mode 100644 index 796dcdddcae8850e13a6791c80ba9b95a2eb5efc..0000000000000000000000000000000000000000 --- a/briar-api/src/org/briarproject/api/messaging/GroupStatus.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.briarproject.api.messaging; - -public class GroupStatus { - - private final Group group; - private final boolean subscribed, visibleToAll; - - public GroupStatus(Group group, boolean subscribed, boolean visibleToAll) { - this.group = group; - this.subscribed = subscribed; - this.visibleToAll = visibleToAll; - } - - public Group getGroup() { - return group; - } - - public boolean isSubscribed() { - return subscribed; - } - - public boolean isVisibleToAll() { - return visibleToAll; - } -} diff --git a/briar-core/src/org/briarproject/db/Database.java b/briar-core/src/org/briarproject/db/Database.java index ba3c4ffafc2031fc19e8051a50db502013b7ebf3..da83486e42fb51e4c8abca90f6dc986f12f411f5 100644 --- a/briar-core/src/org/briarproject/db/Database.java +++ b/briar-core/src/org/briarproject/db/Database.java @@ -17,7 +17,6 @@ import org.briarproject.api.db.DbException; import org.briarproject.api.db.MessageHeader; import org.briarproject.api.messaging.Group; import org.briarproject.api.messaging.GroupId; -import org.briarproject.api.messaging.GroupStatus; import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageId; import org.briarproject.api.messaging.RetentionAck; @@ -230,12 +229,11 @@ interface Database<T> { int countOfferedMessages(T txn, ContactId c) throws DbException; /** - * Returns the status of all groups to which the user subscribes or can - * subscribe, excluding inbox groups. + * Returns all groups to which the user could subscribe. * <p> * Locking: read. */ - Collection<GroupStatus> getAvailableGroups(T txn) throws DbException; + Collection<Group> getAvailableGroups(T txn) throws DbException; /** * Returns the configuration for the given transport. @@ -294,7 +292,7 @@ interface Database<T> { Group getGroup(T txn, GroupId g) throws DbException; /** - * Returns all groups to which the user subscribes. + * Returns all groups to which the user subscribes, excluding inboxes. * <p> * Locking: read. */ diff --git a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java index 1c8bc564d7cc6cbdedfef4a15bbcae1605d07d91..e4ed61e501d6f77cadda4342bd01a04f46c24880 100644 --- a/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java +++ b/briar-core/src/org/briarproject/db/DatabaseComponentImpl.java @@ -64,7 +64,6 @@ import org.briarproject.api.lifecycle.ShutdownManager; import org.briarproject.api.messaging.Ack; import org.briarproject.api.messaging.Group; import org.briarproject.api.messaging.GroupId; -import org.briarproject.api.messaging.GroupStatus; import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageId; import org.briarproject.api.messaging.Offer; @@ -580,12 +579,12 @@ DatabaseCleaner.Callback { } } - public Collection<GroupStatus> getAvailableGroups() throws DbException { + public Collection<Group> getAvailableGroups() throws DbException { lock.readLock().lock(); try { T txn = db.startTransaction(); try { - Collection<GroupStatus> groups = db.getAvailableGroups(txn); + Collection<Group> groups = db.getAvailableGroups(txn); db.commitTransaction(txn); return groups; } catch(DbException e) { diff --git a/briar-core/src/org/briarproject/db/JdbcDatabase.java b/briar-core/src/org/briarproject/db/JdbcDatabase.java index 4b45adcdc49301aab0acdf01db5dadc493e8b880..06402b1cce6a0dc2424d41929631bc7580119af8 100644 --- a/briar-core/src/org/briarproject/db/JdbcDatabase.java +++ b/briar-core/src/org/briarproject/db/JdbcDatabase.java @@ -46,7 +46,6 @@ import org.briarproject.api.db.DbException; import org.briarproject.api.db.MessageHeader; import org.briarproject.api.messaging.Group; import org.briarproject.api.messaging.GroupId; -import org.briarproject.api.messaging.GroupStatus; import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageId; import org.briarproject.api.messaging.RetentionAck; @@ -1174,35 +1173,12 @@ abstract class JdbcDatabase implements Database<Connection> { } } - public Collection<GroupStatus> getAvailableGroups(Connection txn) + public Collection<Group> getAvailableGroups(Connection txn) throws DbException { PreparedStatement ps = null; ResultSet rs = null; try { - // Add all subscribed groups to the list, except inbox groups - String sql = "SELECT DISTINCT g.groupId, name, salt, visibleToAll" - + " FROM groups AS g" - + " LEFT OUTER JOIN groupVisibilities AS gv" - + " ON g.groupId = gv.groupId" - + " WHERE inbox = FALSE OR inbox IS NULL" - + " GROUP BY g.groupId"; - ps = txn.prepareStatement(sql); - rs = ps.executeQuery(); - List<GroupStatus> groups = new ArrayList<GroupStatus>(); - Set<GroupId> ids = new HashSet<GroupId>(); - while(rs.next()) { - GroupId id = new GroupId(rs.getBytes(1)); - if(!ids.add(id)) throw new DbStateException(); - String name = rs.getString(2); - byte[] salt = rs.getBytes(3); - Group group = new Group(id, name, salt); - boolean visibleToAll = rs.getBoolean(4); - groups.add(new GroupStatus(group, true, visibleToAll)); - } - rs.close(); - ps.close(); - // Add all unsubscribed groups to the list - sql = "SELECT DISTINCT cg.groupId, cg.name, cg.salt" + String sql = "SELECT DISTINCT cg.groupId, cg.name, cg.salt" + " FROM contactGroups AS cg" + " LEFT OUTER JOIN groups AS g" + " ON cg.groupId = g.groupId" @@ -1210,13 +1186,14 @@ abstract class JdbcDatabase implements Database<Connection> { + " GROUP BY cg.groupId"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); + List<Group> groups = new ArrayList<Group>(); + Set<GroupId> ids = new HashSet<GroupId>(); while(rs.next()) { GroupId id = new GroupId(rs.getBytes(1)); if(!ids.add(id)) throw new DbStateException(); String name = rs.getString(2); byte[] salt = rs.getBytes(3); - Group group = new Group(id, name, salt); - groups.add(new GroupStatus(group, false, false)); + groups.add(new Group(id, name, salt)); } rs.close(); ps.close(); @@ -1399,7 +1376,12 @@ abstract class JdbcDatabase implements Database<Connection> { PreparedStatement ps = null; ResultSet rs = null; try { - String sql = "SELECT groupId, name, salt FROM groups"; + String sql = "SELECT DISTINCT g.groupId, name, salt" + + " FROM groups AS g" + + " LEFT OUTER JOIN groupVisibilities AS gv" + + " ON g.groupId = gv.groupId" + + " WHERE gv.inbox IS NULL OR gv.inbox = FALSE" + + " GROUP BY g.groupId"; ps = txn.prepareStatement(sql); rs = ps.executeQuery(); List<Group> groups = new ArrayList<Group>(); diff --git a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java index b567c2778ca97c8473bed2d2a2ba5b5d486f59f2..6e2d634dd10116e19f2c68de42bbe519e89580d0 100644 --- a/briar-tests/src/org/briarproject/db/H2DatabaseTest.java +++ b/briar-tests/src/org/briarproject/db/H2DatabaseTest.java @@ -35,7 +35,6 @@ import org.briarproject.api.db.DbException; import org.briarproject.api.db.MessageHeader; import org.briarproject.api.messaging.Group; import org.briarproject.api.messaging.GroupId; -import org.briarproject.api.messaging.GroupStatus; import org.briarproject.api.messaging.Message; import org.briarproject.api.messaging.MessageId; import org.briarproject.api.transport.Endpoint; @@ -1430,66 +1429,26 @@ public class H2DatabaseTest extends BriarTestCase { db.setGroups(txn, contactId, Arrays.asList(group), 1); db.setGroups(txn, contactId1, Arrays.asList(group), 1); - // The group should be available, not subscribed, not visible to all + // The group should be available assertEquals(Collections.emptyList(), db.getGroups(txn)); - Iterator<GroupStatus> it = db.getAvailableGroups(txn).iterator(); - assertTrue(it.hasNext()); - GroupStatus status = it.next(); - assertEquals(group, status.getGroup()); - assertFalse(status.isSubscribed()); - assertFalse(status.isVisibleToAll()); - assertFalse(it.hasNext()); + assertEquals(Arrays.asList(group), db.getAvailableGroups(txn)); - // Subscribe to the group - it should be available, subscribed, - // not visible to all + // Subscribe to the group - it should no longer be available db.addGroup(txn, group); assertEquals(Arrays.asList(group), db.getGroups(txn)); - it = db.getAvailableGroups(txn).iterator(); - assertTrue(it.hasNext()); - status = it.next(); - assertEquals(group, status.getGroup()); - assertTrue(status.isSubscribed()); - assertFalse(status.isVisibleToAll()); - assertFalse(it.hasNext()); - - // The first contact unsubscribes - the group should be available, - // subscribed, not visible to all - db.setGroups(txn, contactId, Collections.<Group>emptyList(), 2); - assertEquals(Arrays.asList(group), db.getGroups(txn)); - it = db.getAvailableGroups(txn).iterator(); - assertTrue(it.hasNext()); - status = it.next(); - assertEquals(group, status.getGroup()); - assertTrue(status.isSubscribed()); - assertFalse(status.isVisibleToAll()); - assertFalse(it.hasNext()); - - // Make the group visible to all contacts - it should be available, - // subscribed, visible to all - db.setVisibleToAll(txn, groupId, true); - assertEquals(Arrays.asList(group), db.getGroups(txn)); - it = db.getAvailableGroups(txn).iterator(); - assertTrue(it.hasNext()); - status = it.next(); - assertEquals(group, status.getGroup()); - assertTrue(status.isSubscribed()); - assertTrue(status.isVisibleToAll()); - assertFalse(it.hasNext()); + assertEquals(Collections.emptyList(), db.getAvailableGroups(txn)); - // Unsubscribe from the group - it should be available, not subscribed, - // not visible to all + // Unsubscribe from the group - it should be available again db.removeGroup(txn, groupId); assertEquals(Collections.emptyList(), db.getGroups(txn)); - it = db.getAvailableGroups(txn).iterator(); - assertTrue(it.hasNext()); - status = it.next(); - assertEquals(group, status.getGroup()); - assertFalse(status.isSubscribed()); - assertFalse(status.isVisibleToAll()); - assertFalse(it.hasNext()); + assertEquals(Arrays.asList(group), db.getAvailableGroups(txn)); + + // The first contact unsubscribes - it should still be available + db.setGroups(txn, contactId, Collections.<Group>emptyList(), 2); + assertEquals(Collections.emptyList(), db.getGroups(txn)); + assertEquals(Arrays.asList(group), db.getAvailableGroups(txn)); - // The second contact unsubscribes - the group should no longer be - // available + // The second contact unsubscribes - it should no longer be available db.setGroups(txn, contactId1, Collections.<Group>emptyList(), 2); assertEquals(Collections.emptyList(), db.getGroups(txn)); assertEquals(Collections.emptyList(), db.getAvailableGroups(txn));