Compare commits

...

661 Commits

Author SHA1 Message Date
Ernir Erlingsson
71d8947265 fixing the result handlers and orientation changes 2016-11-24 23:59:26 +01:00
Torsten Grote
6fc42f7296 Update also Malaysian translation (removes unused strings) 2016-11-23 21:08:51 -02:00
Torsten Grote
55af22ca04 Update translations and expiry date 2016-11-23 21:05:06 -02:00
akwizgran
6ab8219394 Merge branch '666-transport-icons-overlap-navigation-items' into 'master'
Make navigation drawer scrollable

![device-2016-11-22-095642](/uploads/bcd5f68d40816ed2e44c3194eb977695/device-2016-11-22-095642.png)![device-2016-11-22-095658](/uploads/da1807e981632936ba04db24dc7b8ae7/device-2016-11-22-095658.png)

Closes #666

See merge request !432
2016-11-23 15:11:43 +00:00
akwizgran
7d1ddb6d65 Merge branch '504-define-and-design-primary-and-secondary-actions' into 'master'
Make dialog actions consistent

Closes #504

See merge request !430
2016-11-23 15:08:58 +00:00
akwizgran
f1371f1db8 Merge branch '774-adding-contacts-via-bluetooth-only-fails' into 'master'
Use uuid created from the commitment/payload instead of the uuid returned from getUuid()

Signed-off-by: goapunk <noobie@goapunks.net>

Closes #774

See merge request !435
2016-11-23 15:06:16 +00:00
goapunk
491e0adc9c Use uuid created from the commitment/payload instead of the uuid returned from getUuid()
Signed-off-by: goapunk <noobie@goapunks.net>
2016-11-23 10:07:47 +01:00
Torsten Grote
f10ac13350 Make navigation drawer scrollable 2016-11-22 09:59:42 -02:00
Torsten Grote
38f46a9c60 Make dialog actions consistent 2016-11-21 17:42:23 -02:00
Torsten Grote
20de6f1aa5 Merge branch '747-remove-injected-field-from-unit-test' into 'master'
Remove injected field from unit test

Getting this off my todo list...

Closes #747

See merge request !428
2016-11-21 16:43:49 +00:00
akwizgran
cbdeb0ad32 Replace unused injected field with a mock. 2016-11-21 16:41:01 +00:00
akwizgran
a60414517c Merge branch '639-reblogger-and-author-look-similar-their-roles-are-unclear' into 'master'
Make original author look like commenter when reblogged

This wasn't as simple as changing the persona in the XML,
because the same layout is used for a post whether reblogged or not.
So the persona needs to be changed programmatically for reblogged posts.
For this, the `AuthorView#setPersona()` method has been made public and
was changed to always set all views into the desired state to support
usage in a RecyclerView.

![device-2016-11-21-094221](/uploads/e508937ad2d2d84f97f5be29bdeaac2e/device-2016-11-21-094221.png)

Closes #639

See merge request !426
2016-11-21 15:51:43 +00:00
akwizgran
988c3e4b58 Merge branch '768-keyagreementconnector-regression-unsupportedoperationexception' into 'master'
Fix KeyAgreement after regression

Closes #768

See merge request !427
2016-11-21 12:34:33 +00:00
Torsten Grote
b0098fb054 Fix KeyAgreement after regression 2016-11-21 10:32:06 -02:00
Torsten Grote
a7c28f04de Make original author look like commenter when reblogged
This wasn't as simple as changing the persona in the XML,
because the same layout is used for a post whether reblogged or not.
So the persona needs to be changed programmatically for reblogged posts.
For this, the `AuthorView#setPersona()` method has been made public and
was changed to always set all views into the desired state to support
usage in a RecyclerView.
2016-11-21 09:43:22 -02:00
akwizgran
4eec29a631 Merge branch '762-textinput_does_not_increase' into 'master'
make TextInputView resizable again

* TextInputView can resize up to 3 lines again

Signed-off-by: goapunk <noobie@goapunks.net>

Closes #762

See merge request !425
2016-11-21 10:11:00 +00:00
goapunk
76504387ff make TextInputView resizable again
* TextInputView can resize up to 3 lines again

Signed-off-by: goapunk <noobie@goapunks.net>
2016-11-18 18:05:31 +01:00
Torsten Grote
ea0ad08f57 Merge branch '763-remove-signature-api' into 'master'
Remove old signature class from API

I decided not to remove the SignatureImpl class, as it's not a trivial wrapper around the Bouncy Castle class, but I moved the interface into briar-core and made it  package-private.

Closes #763

See merge request !424
2016-11-18 16:23:01 +00:00
akwizgran
e4676517ef Merge branch '524-check-that-acra-is-catching-all-uncaught-exceptions' into 'master'
Introduce a @Scheduler annotation

and make sure work is offloaded to an executor, so exceptions can be caught.

Closes #524

See merge request !422
2016-11-18 16:00:22 +00:00
akwizgran
dbbeb37485 Merge branch '757-remove-deviceid-code' into 'master'
Remove Device ID

Closes #757

See merge request !423
2016-11-18 15:54:28 +00:00
akwizgran
89a7f41a07 Remove old signature class from API. 2016-11-18 15:30:18 +00:00
akwizgran
936ee5e95b Merge branch '549-require-a-label-for-signing' into 'master'
Require a label for signing

This adds a sign() and a verify() method to the CryptoComponent
that take a mandatory label argument to ensure that signatures can't be
repurposed.

Closes #549

See merge request !419
2016-11-18 15:16:41 +00:00
Torsten Grote
1697c2af04 Remove Device ID 2016-11-18 12:30:25 -02:00
Torsten Grote
cb8e0beea9 Fix Transport IDs 2016-11-18 12:29:57 -02:00
Torsten Grote
98cb077dd9 Migrate all custom signature code to new methods and add test 2016-11-18 12:19:03 -02:00
akwizgran
e6a8ad5d49 Merge branch '754-add-visibility-information-to-group-member-list' into 'master'
Add visibility information to group member list

![device-2016-11-17-120740](/uploads/f16dd42c3637e0030722559bace9c391/device-2016-11-17-120740.png)![device-2016-11-17-121008](/uploads/a83fe1f27cd3a505cf45a94fec76cdcf/device-2016-11-17-121008.png)

Closes #754

See merge request !418
2016-11-18 13:19:45 +00:00
akwizgran
5e4116efa0 Merge branch '761-keyboard_doesnt_close_on_userinteraction' into 'master'
use SHOW_IMPLICIT when showing the softkeyboard, otherwise it won't auto hide

* remove the forced hiding in ThreadListActivity as it should no longer be required

Signed-off-by: goapunk <noobie@goapunks.net>

Closes #761

See merge request !421
2016-11-18 13:14:51 +00:00
akwizgran
a01eeafe4e Merge branch '752-refactor-android-dagger-module' into 'master'
Activity module refactor

Broke the Activity module into numerous modules to be able to make numerous controller's package visible

Closes #752

See merge request !417
2016-11-18 13:13:12 +00:00
Torsten Grote
c86d971166 Require a label for signing
This adds a sign() and a verify() method to the CryptoComponent
that take a mandatory label argument to ensure that signatures can't be
repurposed.
2016-11-18 11:05:19 -02:00
Torsten Grote
d5f8808597 Add visibility information to group member list 2016-11-18 11:02:54 -02:00
goapunk
e98f4901f5 use SHOW_IMPLICIT when showing the softkeyboard, otherwise it won't auto hide
* remove the forced hiding in ThreadListActivity as it should no longer be required

Signed-off-by: goapunk <noobie@goapunks.net>
2016-11-18 13:50:04 +01:00
akwizgran
9b09b64ad3 Merge branch '558-use-namespaced-strings-for-transport-ids' into 'master'
Use namespaced strings for Transport IDs

Closes #558

See merge request !420
2016-11-18 12:49:57 +00:00
Torsten Grote
20708bc156 Introduce a @Scheduler annotation
and make sure work is offloaded to an executor, so exceptions can be
caught.
2016-11-18 10:48:20 -02:00
akwizgran
0814458cb9 Merge branch '759-introduction-responses-are-not-marked-as-read' into 'master'
Do not track incoming positive introduction responses

Positive introduction responses are not shown in the UI (for introducees) and are therefore not marked as read. If they would be tracked, the unread message count would be higher than it actually is and would never decrease.

This is a minimal fix that could be better, but I didn't bother to refactor anything, because we need to rewrite the introduction client eventually anyway once more.

Closes #759

See merge request !416
2016-11-18 12:20:40 +00:00
Torsten Grote
e32313c30b Use namespaced strings for Transport IDs 2016-11-17 17:06:26 -02:00
Torsten Grote
593152e7cd Do not track incoming positive introduction responses
because they are not shown in the UI and are therefore not marked as
read. This fixes the unread message count.
2016-11-17 10:59:20 -02:00
Ernir Erlingsson
ab91520813 refactored the activity module 2016-11-17 13:57:02 +01:00
Torsten Grote
37e61c97ea Merge branch 'remove-unmodifiable-wrappers' into 'master'
Remove unnecessary unmodifiable collection wrappers

Part of #379.

See merge request !414
2016-11-16 18:02:34 +00:00
Torsten Grote
303fcc9ece Merge branch 'validator-unit-tests' into 'master'
Unit tests for some validators

See merge request !413
2016-11-16 17:44:08 +00:00
akwizgran
e2bbe7429b Moved some boilerplate into a field. 2016-11-16 17:00:53 +00:00
akwizgran
eaf17c054f Moved common fields to superclass. 2016-11-16 16:39:30 +00:00
Torsten Grote
f97f30ce42 Merge branch '756-avoid-lost-messages' into 'master'
Use new group visibility state to avoid lost messages

Depends on !410. Closes #756.

See merge request !411
2016-11-16 16:22:30 +00:00
akwizgran
f4c26d9cc7 Remove unnecessary unmodifiable collection wrappers. 2016-11-16 16:19:47 +00:00
akwizgran
68abf8ba1a Merge branch '756-group-visibility' into 'master'
Add third group visibility state

This branch adds a third group visibility state: each group is either invisible, visible, or shared with respect to each contact.

Invisible means that the contact doesn't see any sign that we subscribe to the group. Visible means that the contact can send us messages in the group, but we won't send the contact messages in the group. Shared means that the contact can send us messages in the group and we'll send the contact any shared messages in the group.

This is a preparatory step for #756. I'll put up another MR with the changes that close that ticket.

See merge request !410
2016-11-16 15:56:37 +00:00
akwizgran
58f6af513d Use new group visibility state to avoid lost messages. #756 2016-11-16 15:50:07 +00:00
akwizgran
c9170fa5a0 Removed unnecessary use of unmodifiable collections. 2016-11-16 15:48:08 +00:00
akwizgran
ec1f4dccdb Added third group visibility state. 2016-11-16 15:40:51 +00:00
akwizgran
8c3b598ab2 Unit tests for ForumSharingValidator. 2016-11-16 15:35:27 +00:00
akwizgran
bd3bba6e8a Unit tests for ForumPostValidator. 2016-11-16 14:36:22 +00:00
akwizgran
11fcad89c6 Unit tests for BdfMessageValidator. 2016-11-16 14:32:22 +00:00
Torsten Grote
007df4288b Merge branch '709-track-private-group-invitation-messages' into 'master'
Use MessageTracker for private group invitation messages

Depends on !405.

See merge request !412
2016-11-16 14:14:48 +00:00
Torsten Grote
9798654c23 Merge branch 'message-tracker-refactoring' into 'master'
Factor MessageTracker out of BdfIncomingMessageHook

The branch moves the MessageTracker implementation from BdfIncomingMessageHook to a separate class. This will allow the private group invitation client to track messages from classes other than the one that implements the delivery hook.

I've also fixed a couple of bugs, removed some redundant code from the validation manager, and added null-safety annotations (which is how I noticed the bugs).

See merge request !405
2016-11-16 14:04:14 +00:00
akwizgran
7b58d003a1 Merge branch '732-reveal-contacts-ui-join-notices' into 'master'
Add visibility and OPTIONS button to private group join notices

![device-2016-11-11-180658](/uploads/e00d175a1e1f34307c5f0d80fa0d1cdf/device-2016-11-11-180658.png)
![device-2016-11-11-181325](/uploads/0f6010094d529a4f151db8ebce974885/device-2016-11-11-181325.png)

Closes #732

See merge request !408
2016-11-16 13:44:32 +00:00
akwizgran
b60f6b0789 Merge branch '755-group-message-timestamp' into 'master'
Made private group timestamp greater than that of latest message

I did not change `getPreviousMsgId()` to `getPreviousMsgHeader()` because there doesn't seem to be a need for it anymore.

Closes #755

See merge request !409
2016-11-16 13:29:59 +00:00
Torsten Grote
914b72505a Made private group timestamp greater than that of latest message 2016-11-16 11:20:25 -02:00
Torsten Grote
24dd4fda69 Address review issues 2016-11-16 11:08:00 -02:00
akwizgran
52eb261a11 Unit tests for PrivateMessageValidator. 2016-11-16 11:49:49 +00:00
Torsten Grote
980a6d18bb Add visibility and OPTIONS button to private group join notices 2016-11-16 09:25:22 -02:00
akwizgran
c4a152b543 Merge branch '732-reveal-contacts-ui-onboarding' into 'master'
Add onboarding dialog for revealing contacts screen

This MR is based on !406.

![device-2016-11-11-162417](/uploads/8df831905eebf41ecdbe368890cdd429/device-2016-11-11-162417.png)

See merge request !407
2016-11-14 12:44:41 +00:00
Torsten Grote
ed728e816e Add onboarding dialog for revealing contacts screen 2016-11-14 09:14:30 -02:00
akwizgran
efbce95399 Merge branch '732-reveal-contacts-ui' into 'master'
Add UI for revealing contacts within a private group

This addresses one part of #732. Join notices and onboarding will follow in separate MRs.

Although this MR is technically not based on !402, it does require it to be merged first to compile.

![device-2016-11-11-160454](/uploads/8cb6d8bdd0f3761657875024739e730a/device-2016-11-11-160454.png)

See merge request !406
2016-11-14 10:48:42 +00:00
akwizgran
98c81f71b4 Merge branch '709-reveal-relationships' into 'master'
Mark relationship visible when syncing group with peer

This branch updates the private group invitation protocol to use @grote's new method for marking a contact relationship visible to the group.

I've changed the method slightly because the protocol state machine allows us to leave and re-enter the BOTH_JOINED state (see diagram on #659), so the relationship may already be visible when the method is called. In that case the visibility isn't updated, so we stick with whichever of revealed-by-us and revealed-by-contact happened first.

See merge request !402
2016-11-14 10:32:56 +00:00
akwizgran
da543c1004 Added javadocs, removed redundant exception. 2016-11-14 10:30:38 +00:00
akwizgran
7ab4d12d83 Track private group invitation messages. 2016-11-14 10:10:14 +00:00
Torsten Grote
59964c5087 Add UI for revealing contacts within a private group 2016-11-11 15:40:02 -02:00
Torsten Grote
b885e49ba2 Don't indicate that we are sharing with ourselves in group memberlist 2016-11-11 15:40:01 -02:00
akwizgran
63da860681 Merge branch '732-reveal-contacts-ui-preparation' into 'master'
Prepare UI for revealing contacts

This changes the visibility of some methods (that need to be accessed from another package), removes unnecessary abstractions and fixes erroneous static import of GroupId constant.

See merge request !404
2016-11-11 17:33:54 +00:00
akwizgran
aa210fc555 Factor MessageTracker out of BdfIncomingMessageHook. 2016-11-11 16:59:10 +00:00
Torsten Grote
68f0e91f32 Prepare UI for revealing contacts
This changes the visibility of some methods, removes unnecessary
abstractions and fixes static import of GroupId constant.
2016-11-11 14:49:26 -02:00
akwizgran
3a2205123f Added a method for revealing a contact to a private group. 2016-11-11 13:49:49 +00:00
akwizgran
ab16ee7465 Merge branch 'exception-handler' into 'master'
Add new Exception handler

While working on #732 I again needed to use a `UiResultExceptionHandler`  when I actually don't need to return a result. We have some other places in the code like this. So I introduced a `UiExceptionHandler` without the result part and used it where appropriate. While I was touching some classes, I also added the new annotations.

This MR includes another small commit that notifies only on local group messages. Joining a group is a message as well and without this change, you are notified about a new message when you yourself joined your newly created group.

See merge request !403
2016-11-11 13:34:09 +00:00
Torsten Grote
eb66924e21 Do not show notification for local group messages 2016-11-11 10:03:03 -02:00
Torsten Grote
563d897651 Introduce Exception handler for when no result needs to be returned
Also add NotNull annotation to classes that were touched
2016-11-11 10:03:02 -02:00
akwizgran
98cf6b5bba Mark relationship visible when syncing group with peer. 2016-11-11 11:29:55 +00:00
akwizgran
ade7e50f65 Merge branch 'contact-selector-controller' into 'master'
Add a controller for contact selection lists

See merge request !401
2016-11-11 10:54:07 +00:00
Torsten Grote
ccc9d53ac7 Address review comments 2016-11-10 16:49:20 -02:00
Torsten Grote
d232529eb3 Add a controller for contact selection lists 2016-11-10 15:33:53 -02:00
akwizgran
a532f03784 Merge branch '732-reveal-backennd' into 'master'
Add support for revealing contacts to the PrivateGroupManager

This also adds three integration tests and improves some small details here and there in the private group client.

Prerequisite for #732.

See merge request !396
2016-11-10 17:00:31 +00:00
Torsten Grote
5e5bf7ec05 Add ContactRelationshipRevealedEvent and address review comments 2016-11-10 13:56:42 -02:00
akwizgran
7414abd1ce Merge branch '748-qr-code-payload-order' into 'master'
Preserve the order of descriptors in QR code payloads

This fixes a regression caused by my recent changes to the Payload class.

Closes #748

See merge request !399
2016-11-10 12:21:36 +00:00
Torsten Grote
dc76ce2be2 Merge branch '720-camera-surface-illegal-state-exception' into 'master'
Don't crash if camera is reopened or surface is recreated

This branch fixes the crash is described in #720, which can be reproduced easily by scanning a QR code and failing to connect (for example, scan a screenshot of a QR code from a device that's no longer listening). When the camera view becomes visible again after trying to connect, its surfaceCreated() callback is called again with the same surface. An IllegalStateException added in !340 causes the crash.

Closes #720

See merge request !397
2016-11-10 11:12:24 +00:00
Torsten Grote
3eed0bfe81 Add visibility of contact relationship to JoinMessageHeader 2016-11-09 16:35:27 -02:00
Torsten Grote
ec8982438a Add support for revealing contacts to the PrivateGroupManager
This also adds two integration tests and improves some small details
2016-11-09 16:34:58 -02:00
akwizgran
501c2dab31 Preserve the order of descriptors in QR code payloads. 2016-11-09 15:57:41 +00:00
akwizgran
2fe69af6d8 Don't try to get parameters after releasing camera. 2016-11-09 14:51:05 +00:00
Torsten Grote
b20c107010 Merge branch 'feed-pager-events' into 'master'
Make the feed pager respond to events, block notifications

This branch fixes a bug I found while working on #705: FeedPostPagerFragment doesn't start or stop the controller, so it doesn't load newly received posts or block notifications, unlike FeedFragment.

See merge request !398
2016-11-09 14:16:57 +00:00
akwizgran
f410e4eddd Make the feed pager respond to events, block notifications. 2016-11-09 13:24:44 +00:00
akwizgran
bb82bd70e2 Don't crash if camera is reopened or surface is recreated. 2016-11-09 12:43:33 +00:00
akwizgran
138a6e11a7 Merge branch '346-smaller-qr-codes' into 'master'
Encode transport properties more compactly in QR codes

The [original BQP spec](https://code.briarproject.org/akwizgran/briar/wikis/BQP) described a compact encoding for transport properties, with the goal of making the QR code as small as possible. At some point during the implementation, I asked @str4d to use TransportIds and TransportProperties instead, as described in the [current spec](https://code.briarproject.org/akwizgran/briar-spec/blob/master/protocols/BQP.md). That was a mistake.

Using the original format reduces the payload from 60 to 34 bytes (43% smaller) for Bluetooth only, and from 96 to 49 bytes (49% smaller) for Bluetooth and LAN. This makes it easier to scan codes from low-resolution screens using fixed-focus and/or low-resolution cameras. Using this branch I can exchange codes between the Sony Xperia Tipo (320x480 screen, fixed focus, 640x480 preview size) and the Huawei Ascend Y300 (480x800 screen, infinity focus, 1280x720 preview size).

This also removes an obstacle to implementing #558, as TransportIds are no longer included in QR codes.

Closes #346.

See merge request !394
2016-11-08 17:32:26 +00:00
akwizgran
178d72114b Merge branch '709-private-group-invitation-protocol' into 'master'
Private group invitation protocol

This branch implements the private group invitation protocol. The implementation is something of an experiment with a new way of writing client protocols.

We start with a role enum that lists the roles in the protocol, and a state enum for each role, which lists the states in the role's state machine. Then there's a session class, parameterised by the state class and therefore by the role, which represents the session information held by that role. Then there's an engine interface, parameterised by the session class and therefore by the role, which encapsulates the protocol logic for the role. Most of this stuff can be created pretty mechanically from the state machine diagrams.

The engine interface has a method for each type of message and each local action. I started out with one method for all messages and another for all local actions, but that turned out to be a bad design - the information about what kind of message was being handled was lost when the message was passed to the engine, and had to be recovered using an instanceof ladder.

Each engine method takes a message or an action and a session, and returns an updated session. A transaction is passed in so the engine can send messages, attach events, and do any other work it needs to do (such as changing the visibility of groups, in the case of this protocol). This removes the need to run tasks outside the engine, so the protocol logic is better encapsulated inside the engine.

Parsing and encoding of messages and sessions is separated from protocol logic. MessageParser, MessageEncoder and the validator are the only classes that know how messages and their metadata are formatted, and likewise SessionParser and SessionEncoder are the only classes that know how sessions are formatted. The metadata keys are declared in a package-private interface.

It's common knowledge that I never make mistakes, so to keep things interesting I've hidden 114 deliberate mistakes in this code. See how many you can spot!

Needs tests before #709 is closed.

See merge request !382
2016-11-08 17:22:00 +00:00
Torsten Grote
c1f1eb7dfa Merge branch '709-preliminaries' into 'master'
Preliminaries for private group invitation protocol

As promised, here's the preliminary stuff for #709 as a separate MR.

See merge request !395
2016-11-08 17:02:45 +00:00
akwizgran
d2a3804cfe Added null safety annotations to plugin interfaces. 2016-11-08 16:59:56 +00:00
akwizgran
fb095c1f4d Broadcast events for private group invitations. 2016-11-08 16:08:51 +00:00
akwizgran
f89d8cbe38 Updated peer state machine for automatic join response. 2016-11-08 16:08:50 +00:00
akwizgran
d2434123a9 Private group invitation protocol. 2016-11-08 16:08:50 +00:00
akwizgran
edbf5ff5b4 Preliminaries for private group invitation protocol. 2016-11-08 15:45:04 +00:00
akwizgran
32f0b53d15 Increase test timeouts to avoid spurious failures. 2016-11-08 15:17:08 +00:00
akwizgran
55ecdd9a13 Merge branch '557-use-namespaced-strings-for-client-ids' into 'master'
Use namespaced strings for client IDs

Closes #557

See merge request !393
2016-11-08 13:25:41 +00:00
akwizgran
e47e3242a6 Merge branch '196-mark-messages-read' into 'master'
Mark private messages read properly

Depends on !386.

This branch uses the same approach as forums to mark messages read, i.e. each message is marked read when it becomes visible, rather than marking all messages read in a batch when the activity finishes. This fixes two problems: messages not being marked read when isFinishing() is false, for example when leaving the activity via the home button, and a race condition between updating and loading the group count when leaving the activity, resulting in a stale unread message count in the contact list.

Closes #196.

See merge request !388
2016-11-08 13:25:00 +00:00
Torsten Grote
4387bfc5bd Merge branch '733-incoming-messages-aren-t-added-to-private-group-conversation' into 'master'
Broadcast GroupMessageAddedEvent for the UI to update when received

Closes #733

See merge request !390
2016-11-08 13:24:20 +00:00
Torsten Grote
d11ee5e43b Broadcast GroupMessageAddedEvent for the UI to update when received 2016-11-08 11:23:52 -02:00
akwizgran
ce53589c33 Merge branch '736-private-group-list-is-not-updated-when-group-is-dissolved' into 'master'
Create GroupDissolvedEvent and react to it

This MR also makes the private group list react to incoming group invitations (once they are implemented).

![device-2016-11-07-155240](/uploads/84ed51cf72bf2c7c597a0fd68b5d868c/device-2016-11-07-155240.png)
![device-2016-11-07-155302](/uploads/446c98d70d674d0bcd79bfb7682dfed5/device-2016-11-07-155302.png)

Closes #736, #737

See merge request !392
2016-11-08 13:20:28 +00:00
Torsten Grote
e96b3a8c68 Use namespaced strings for client IDs 2016-11-08 11:17:52 -02:00
Torsten Grote
62040d45b8 Create GroupDissolvedEvent and react to it
Also react to incoming group invitations
2016-11-08 10:28:54 -02:00
akwizgran
1809943f1d Merge branch '734-notifications-for-private-group-messages' into 'master'
Show Notifications for Group Messages

Closes #734

See merge request !391
2016-11-08 12:06:15 +00:00
akwizgran
d204757395 Merge branch '735-back-button-in-invite-members-screen-returns-to-group-list' into 'master'
Return to group after not inviting new members

This MR also closes the keyboard when returning from the message fragment.

Closes #735

See merge request !389
2016-11-08 11:55:36 +00:00
akwizgran
c640ee8e51 Merge branch '714-asynchronous-context-leaks' into 'master'
Fixed asynchronous Activity leaks in Fragments

If a Fragment has been detached its `getActivity()` method will return null, providing numerous crash possibilities within the app.

My approach to fixing this is to make Fragments use their own `runOnUiThreadUnlessDestroyed` method, which also checks if the Fragment has been detached before running the Runnable

Closes #714

See merge request !387
2016-11-08 11:49:45 +00:00
akwizgran
04d4ecad05 Encode transport properties more compactly in QR codes. 2016-11-08 11:28:44 +00:00
Ernir Erlingsson
c36bb3e60e created runOnUiThreadUnlessDestroyed fragment wrapper 2016-11-07 17:11:57 +01:00
akwizgran
7327029fca Log the QR code payload length. 2016-11-07 16:04:07 +00:00
Torsten Grote
81d341374d Show Notifications for Group Messages 2016-11-07 13:52:59 -02:00
Torsten Grote
7b884d2425 Return to group after not inviting new members
Also close keyboard when returning from message fragment
2016-11-07 10:44:22 -02:00
Torsten Grote
fbcf334941 Merge branch 'use-contact-id-as-conversation-id' into 'master'
Use contact ID rather than messaging group ID to identify conversation

We originally used the private messaging group ID to identify the private conversation, but now that the conversation includes messages from multiple clients it's more appropriate to use the contact ID.

This refactoring isn't urgent - I've had the branch lying around for a while, but I'm putting it up for review because #734 will touch some of the same code.

See merge request !386
2016-11-07 11:24:11 +00:00
akwizgran
238100bcac Mark messages read properly in private conversation. 2016-11-07 10:43:24 +00:00
akwizgran
7c3805260d Merge branch '643-allow-messages-to-be-deleted-in-the-delivery-hook' into 'master'
Allow messages to be deleted in delivery hook

Closes #643

See merge request !385
2016-11-04 16:14:44 +00:00
akwizgran
51bcf7b1b8 Don't use messaging group ID as proxy for contact ID. 2016-11-04 15:50:44 +00:00
Torsten Grote
719a53dc94 Address review comments 2016-11-04 12:58:12 -02:00
Torsten Grote
3f9a254a0b Allow messages to be deleted in delivery hook 2016-11-04 12:52:33 -02:00
akwizgran
e810a1265a Merge branch '205-unit-tests-for-keymanagerimpl-and-transportkeymanager' into 'master'
Add unit tests for KeyManagerImpl

This also creates a `TransportKeyManager` interface and a factory for that to be able to test things separately.

Closes #205

See merge request !380
2016-11-04 14:29:21 +00:00
Torsten Grote
c36f5c795b Address review comments for TransportKeyManagerImplTest 2016-11-04 11:04:44 -02:00
Torsten Grote
f52186ac8c Add unit tests for KeyManagerImpl and create TransportKeyManager
interface and a factory for that.
2016-11-04 10:54:56 -02:00
akwizgran
e0f4be931d Merge branch '708-private-group-fixup' into 'master'
Let only the creator invite new members to private groups

A little bug I noticed when reviewing the implementation of the invitation protocol.

See merge request !383
2016-11-04 09:33:50 +00:00
akwizgran
7536c00a34 Merge branch '708-private-group-remove-new-member-announcement' into 'master'
Remove new member announcement and add signature to join message



See merge request !384
2016-11-04 09:32:40 +00:00
akwizgran
847b6e4179 Added comments to integration test. 2016-11-04 09:32:21 +00:00
Torsten Grote
58793068c3 Address review comments 2016-11-03 17:26:37 -02:00
Torsten Grote
7125248677 Remove new member announcement and add signature to invitation 2016-11-03 17:24:31 -02:00
Torsten Grote
4bad7076e7 Merge branch '674-ending-a-transaction-can-throw-an-exception-in-a-finally-block' into 'master'
Replace transaction.setComplete() by database.commitTransaction()

Closes #674

See merge request !374
2016-11-03 19:23:16 +00:00
akwizgran
2bb16bb75f Merge branch '348-testers-did-not-understand-qr-code-workflow' into 'master'
Improve QR code workflow slightly

* Improve wording so contacts know they need meet up to scan
  and scan each other's codes
* Use consistent progress bar styles

Closes #348

See merge request !381
2016-11-03 15:56:19 +00:00
Torsten Grote
b5a427f876 Let only the creator invite new members to private groups 2016-11-02 17:40:33 -02:00
Torsten Grote
b34b4623ed Replace transaction.setComplete() by database.commitTransaction() 2016-11-02 13:04:31 -02:00
Torsten Grote
f3b9214702 Improve QR code workflow slightly
* Improve wording so contacts know they need meet up to scan
  and scan each other's codes
* Use consistent progress bar styles
2016-11-02 12:43:41 -02:00
akwizgran
36f087c512 Merge branch '724-unit-tests-for-clienthelperimpl' into 'master'
Add Unit Tests for ClientHelper

Closes #724

See merge request !379
2016-11-02 11:04:07 +00:00
akwizgran
0c30f16d7e Merge branch '731-bdf-reader-open-lists-and-dictionaries' into 'master'
Don't throw IllegalStateException if BDF input is incomplete

Closes #731

See merge request !378
2016-11-01 17:40:13 +00:00
akwizgran
4d8a84a48d Don't throw IllegalStateException if BDF input is incomplete. 2016-11-01 17:38:23 +00:00
akwizgran
2650f3114e Merge branch '518-limit-the-depth-of-nested-bdf-structures' into 'master'
Limit the depth of nested BDF structures

Closes #518

See merge request !375
2016-11-01 17:30:50 +00:00
Torsten Grote
dfdde9799f Add Unit Tests for ClientHelper 2016-11-01 15:28:28 -02:00
akwizgran
114a2dc8f2 Merge branch '427-local-author-caching' into 'master'
Cache the local author and load before the db latch is released

Closes #427, #588 

See merge request !354
2016-11-01 17:21:14 +00:00
akwizgran
642fa7df18 Removed unused field, renamed nickname methods. 2016-11-01 17:17:40 +00:00
akwizgran
c85767d2a0 Nickname is all one word. 2016-11-01 17:12:30 +00:00
Torsten Grote
55af1b954e Limit the depth of nested BDF structures 2016-11-01 14:52:00 -02:00
Ernir Erlingsson
88272c5d61 improvements after dev comments 2016-11-01 13:33:12 +01:00
Ernir Erlingsson
eaa393a7ed added a cache to the IdentityManager, changed its signature, modified when and where the author is stored
made the author creation single-threaded again in the LifecycleManager, removed redundant code
2016-11-01 12:51:49 +01:00
akwizgran
19080ad957 Merge branch '723-unit-tests-for-contactmanagerimpl' into 'master'
Add Unit tests for ContactManager

Closes #723

See merge request !376
2016-11-01 11:50:47 +00:00
Torsten Grote
7eeeb5f1ed Add Unit tests for ContactManager 2016-11-01 09:40:05 -02:00
akwizgran
d55503ee92 Merge branch '722-implement-ux-design-for-inviting-new-members-to-a-group' into 'master'
Implement UX design for inviting new members to a group

Closes #722

See merge request !373
2016-11-01 11:38:29 +00:00
Torsten Grote
8448d27d20 Implement UX design for inviting new members to a group 2016-11-01 09:27:23 -02:00
akwizgran
47d6fc526f Merge branch '678-implement-ux-for-viewing-the-membership-of-a-private-group' into 'master'
Implement UX for viewing the membership of a private group

This MR is the second and last MR to address #678. The first part is in !377.

![device-2016-10-26-112000](/uploads/8cbdee65c123a6d5329e208d9983d0b0/device-2016-10-26-112000.png)

Closes #678

See merge request !364
2016-11-01 11:12:21 +00:00
Torsten Grote
3da879cfd9 Address review comments for group member list 2016-11-01 09:09:22 -02:00
Torsten Grote
8fdce5ba51 Group Member List UI 2016-11-01 09:09:20 -02:00
akwizgran
f759a7506f Merge branch '600-remove-content-type-from-private-messages' into 'master'
Remove content-type and parentId from private messages

and turn them into a regular string.

Closes #600

See merge request !372
2016-11-01 11:05:30 +00:00
akwizgran
0b11aea7a2 Merge branch '672-implement-ux-for-dissolving-a-group' into 'master'
Implement UX for when a group has been dissolved

This MR is based on !367.

Closes #672

See merge request !369
2016-11-01 11:02:27 +00:00
akwizgran
aa954cee63 Merge branch '671-implement-ux-for-leaving-a-group' into 'master'
Implement UX for leaving a group

This MR also includes the creator's part of the UX for dissolving a group since it is almost the same.

![device-2016-10-26-185615](/uploads/8689a1ee103fcee23105a469b37c59de/device-2016-10-26-185615.png)

Closes #671

See merge request !367
2016-11-01 10:57:33 +00:00
akwizgran
68024c264e Merge branch '700-update-blog-backend-to-match-current-usage' into 'master'
Update blog backend to match current usage

Closes #700

See merge request !371
2016-11-01 10:56:02 +00:00
Torsten Grote
78740a6942 Remove content-type and parentId from private messages
and turn them into a regular string.
2016-11-01 08:39:15 -02:00
Torsten Grote
9e553ef9c8 Update blog backend to match current usage 2016-11-01 08:34:29 -02:00
Torsten Grote
1147b8ffaf Disable group before loading messages 2016-11-01 08:26:40 -02:00
Torsten Grote
67866dbe66 Implement UX for when a group has been dissolved 2016-11-01 07:41:55 -02:00
Torsten Grote
8dac2d1ca6 Implement UI for dissolving and leaving group 2016-11-01 07:40:56 -02:00
Torsten Grote
b0a5a69b81 Remove group from database in PrivateGroupManager 2016-11-01 07:39:35 -02:00
akwizgran
a18317e912 Merge branch '681-convert-forum-post-bodies-to-strings-remove-content-type' into 'master'
Remove forum content type and change bodies to string

Also removes support for anonymous forum posts.

This MR depends on !360.

Closes #698, #681

See merge request !370
2016-10-31 21:34:24 +00:00
akwizgran
ad7d0d8e74 Merge branch '678-private-group-hooks-and-membership' into 'master'
Add methods and hooks to PrivateGroupManager related to members and removal

This MR is the first of two MRs related to #678.

See merge request !377
2016-10-31 16:27:15 +00:00
akwizgran
fe79131f4a Merge branch '678-contact-list-refactoring' into 'master'
Refactor contact lists, their adapters and items

This was supposed to be a preparation for #678 to make the contacts lists cleaner and easier to re-use for different use-cases. Turns out #678 can't use this work, but it is probably nice to have anyway.

During this work, support for multiple identities has been removed from the various contact lists.

See merge request !363
2016-10-31 15:39:48 +00:00
Torsten Grote
cb61d91074 Add methods and hooks to PrivateGroupManager related to members and removal 2016-10-31 13:23:03 -02:00
akwizgran
399a4890de Merge branch '708-implement-protocol-for-private-group-messaging' into 'master'
Implement protocol for private group messaging

Closes #708

See merge request !360
2016-10-31 15:17:51 +00:00
Torsten Grote
656a947f5a Last minor review comments addressed 2016-10-31 13:13:59 -02:00
Torsten Grote
7191967092 Refactor contact lists, their adapters and items 2016-10-31 10:29:53 -02:00
Torsten Grote
5ce8b1978d Remove forum content type and move bodies to string
Also removes support for anonymous forum posts.
Closes #698
2016-10-31 10:25:12 -02:00
Torsten Grote
c0aa255bb6 Address review comments 2016-10-31 10:16:48 -02:00
Torsten Grote
c79ce61f6d Add PrivateGroupManager integration tests 2016-10-31 10:13:21 -02:00
Torsten Grote
0caabda303 Do additional validation on incoming private group messages 2016-10-31 10:13:21 -02:00
Torsten Grote
679b54b2b4 Show join messages properly in the threaded conversation 2016-10-31 10:13:20 -02:00
Torsten Grote
349a34ffd8 Return actual private group message headers and bodies to the UI 2016-10-31 10:12:26 -02:00
Torsten Grote
2c8aaa215c Posting group messages takes previous message into account 2016-10-31 10:11:35 -02:00
Torsten Grote
4f4f1956eb Creator automatically joins the group after creating it 2016-10-31 10:11:33 -02:00
Torsten Grote
e06726b2f9 Implement New Member and Join Announcements in GroupMessageFactory 2016-10-31 10:10:14 -02:00
Torsten Grote
a6e3827127 Implement first prototype of GroupMessageValidator 2016-10-31 10:09:17 -02:00
Torsten Grote
8dc529cc3f Move validator's signature verification into ClientHelper 2016-10-31 10:08:26 -02:00
akwizgran
1e36f21cc8 Merge branch '707-implement-ux-for-showing-and-answering-private-group-invitations' into 'master'
Implement UX for showing and answering private group invitations

As usual, this MR contains several logically separate commits that could be split out into smaller MRs if desired. It consists of two main parts:
* Showing open invitations in the list of private groups with a snackbar
* Showing invitations and responses in the private conversation

For both parts, the existing code was refactored to allow for a smooth implementation and to leave maintainable code behind.

![device-2016-10-18-101549](/uploads/66582dbe97736fdcd2498e87e1c7dfd1/device-2016-10-18-101549.png)
![device-2016-10-18-101612](/uploads/8c25eff8171f330796a55cb27cdb2552/device-2016-10-18-101612.png)
![device-2016-10-18-101534](/uploads/ebba4c0a2c0f727dcadac8c2ec57b48f/device-2016-10-18-101534.png)

Closes #707

See merge request !357
2016-10-31 12:02:22 +00:00
Torsten Grote
2cc650d85f Address review comments 2016-10-28 15:29:47 -02:00
akwizgran
3407d0c0a8 Merge branch 'protocol-state-exception' into 'master'
ProtocolStateException for client protocols

Methods that implement local actions in a client protocol (for example, accepting an invitation) can throw this exception to indicate that the action wasn't taken because the action isn't applicable to the current state. This can happen if the protocol state machine is updated by an incoming message and the user takes an action before the UI has been updated.

See merge request !368
2016-10-27 15:46:03 +00:00
akwizgran
ca8d3babaa Added ProtocolStateException for client protocols. 2016-10-27 11:57:36 +01:00
Torsten Grote
7b627bb427 Remove PartialItem interface and the need for casting ConversationItems 2016-10-27 08:22:52 -02:00
Torsten Grote
f027b832d4 Address review issues 2016-10-27 08:14:33 -02:00
Torsten Grote
42175dca7a Show group invitations and responses in private conversation 2016-10-27 08:14:33 -02:00
Torsten Grote
5ffcdc4e46 Refactor ConversationAdapter and its ConversationItems 2016-10-27 08:14:32 -02:00
Torsten Grote
e00219c15f Allow responding to sharing invitations based on SessionId 2016-10-27 08:14:32 -02:00
Torsten Grote
96666273d3 Show group invitations in group list 2016-10-27 08:14:32 -02:00
Torsten Grote
a92f7e1c9f Controllerize invitation activities 2016-10-27 08:14:31 -02:00
Torsten Grote
02a39f5694 Refactor events based on InvitationRequestReceivedEvent 2016-10-27 08:14:31 -02:00
Torsten Grote
a33d7d1663 Add a stub for a GroupInvitationManager 2016-10-27 08:14:30 -02:00
Ernir Erlingsson
8eeaf4e347 Merge branch '688-proguard-warns-about-missing-descriptor-classes' into 'master'
Fix proguard notes about unkept descriptor classes

Closes #688

See merge request !366
2016-10-26 20:32:43 +00:00
Torsten Grote
835bd86346 Fix proguard notes about unkept descriptor classes 2016-10-26 15:57:52 -02:00
akwizgran
84b2a171ab Merge branch '718-creating-a-group-without-having-contacts-can-cause-crash' into 'master'
Fix crash when navigating back in contact selector

Closes #718

See merge request !365
2016-10-26 17:08:07 +00:00
Torsten Grote
1df00f5702 Fix crash when navigating back in contact selector 2016-10-26 14:49:45 -02:00
akwizgran
292e1c3e8e Merge branch '715-long-posts-aren-t-rendered' into 'master'
Disable EmojiTextView software layer rendering when cache is too small

This needs to be tested if it works as intended on several devices.

See merge request !362
2016-10-26 15:09:56 +00:00
akwizgran
c8c0281efc Merge branch '686-crash-when-transitioning-out-of-reblogactivity' into 'master'
Limit scene transition animations to API 23 and above

to work-around [android bug #224270](https://code.google.com/p/android/issues/detail?id=224270).
This is only necessary if the transitioning view might not be available anymore when the exit transition is made.

Closes #686

See merge request !361
2016-10-26 10:11:32 +00:00
akwizgran
e8c48ccf8d Merge branch '661-implement-ux-for-creating-a-private-group' into 'master'
Implement UX for creating a private group

This MR allows the user to create a new private group and select contacts to be invited into the group.

There are currently 6 commits starting with some small refactoring for code reuse and making more functionality available in the backend. Each commit could be split up into a dedicated MR if desired.

![create-groups](/uploads/5229f102ee2611c05d5e9d1e2aac510d/create-groups.gif)

Closes #661

See merge request !353
2016-10-26 09:32:34 +00:00
Torsten Grote
09baa2ebe1 Add own constant for maximum group invitation message length 2016-10-26 07:28:51 -02:00
Torsten Grote
1176741ea4 Address actual review issues 2016-10-25 15:22:11 -02:00
Torsten Grote
e6def70030 Pre-address potential review issues 2016-10-25 14:58:50 -02:00
Torsten Grote
1a812f1327 UI for creating private groups 2016-10-25 14:58:47 -02:00
Torsten Grote
8dc3bd2c4c Implement private group creation and fetching in PrivateGroupManager 2016-10-25 14:57:43 -02:00
Torsten Grote
c934ec30aa Move Up button handling into BaseFragment 2016-10-25 14:57:43 -02:00
Torsten Grote
d5f6e71cba Create a reusable ContactSelectorActivity 2016-10-25 14:57:41 -02:00
Torsten Grote
feed2581c9 Factor out a reuseable MessageFragment 2016-10-25 14:56:25 -02:00
Torsten Grote
bd1f3fc2bd Make ContactSelectorFragment reusable 2016-10-25 14:52:27 -02:00
akwizgran
d25f4d1fbe Merge branch 'string-truncation' into 'master'
Truncate all messages to valid length before sending



See merge request !358
2016-10-25 15:52:35 +00:00
Torsten Grote
e84d1c5996 Update translations and expiry date 2016-10-25 08:19:15 -02:00
Torsten Grote
06831bafc3 Disable EmojiTextView software layer rendering when cache is too small 2016-10-24 17:20:57 -02:00
Torsten Grote
9284167a2e Limit scene transition animations to API 23 and above
to work-around android bug #224270.
This is only necessary if the transitioning view might not be available
anymore when the exit transition is made.
2016-10-24 15:34:24 -02:00
akwizgran
df44015ccb Merge branch '705-adapter-revisions' into 'master'
Fix race conditions when updating UI from events (again)

This is my second attempt at fixing race conditions caused by updating the UI from events while background tasks are loading data from the DB. Unlike my first attempt, this one is pretty simple and doesn't require too much reasoning about possible races.

The first commit fixes a few list loading bugs I found while working on this problem, and moves the lifecycle callbacks from resume/pause to start/stop, closing #609. The second commit contains the fix for #705, which works as follows:

* Each BriarAdapter has a revision counter
* Before making a change to the adapter that could be overwritten by a background task, increment the revision
* Before starting a background task that could overwrite other changes, get the current revision
* Before applying changes from a background task that could overwrite other changes, check whether the revision has changed
* If the revision has changed, restart the background task
* Otherwise apply the changes

Closes #609. #705 remains open because the PagerAdapters for blogs need to be updated.

See merge request !356
2016-10-21 10:28:02 +00:00
akwizgran
c4716ca457 BlogFragment doesn't need to use adapter revisions.
All changes to the adapter are cumulative.
2016-10-20 14:21:10 +01:00
akwizgran
9bb16b424f Moved revision counter methods into their own interface. 2016-10-20 12:44:09 +01:00
akwizgran
e8ebdc2884 Don't finish nav drawer fragments on error. 2016-10-20 11:55:39 +01:00
akwizgran
2140a290e4 Avoid race conditions when updating the UI from events. 2016-10-20 11:28:03 +01:00
akwizgran
50a70f7649 Use start/stop lifecycle callbacks rather than pause/resume.
Also fixed a couple of bugs.
2016-10-20 10:40:10 +01:00
akwizgran
b3e5d1ff85 Finished renaming entry to item, reduced some visibility. 2016-10-19 20:49:45 +01:00
Torsten Grote
690142ce07 Merge branch '712-bdf-list-out-of-bounds' into 'master'
Throw FormatException if BdfList index is out of bounds

Closes #712

See merge request !359
2016-10-19 17:49:14 +00:00
akwizgran
82eea6bb77 Throw FormatException if BdfList index is out of bounds. 2016-10-19 18:11:31 +01:00
Torsten Grote
3ad3332649 Merge branch '663-implement-ux-for-displaying-message-threads-in-private-groups' into 'master'
Private Group Threaded Conversation

This MR refactors the forum activity, its controller, its adapter and view holder so *most* of the code can be re-used for private groups by making heavy use of generics.

The refactoring has 1383 additions and 1087 deletions, so just grows the code-base slightly and adding the private group conversation just takes an additional 400 lines.

The MR also includes one commit that moves post/message creation more into clients, so the UI doesn't need to keep track of timestamps. This commit can of course be split out into a separate MR if desired.

Closes #662, #663 

See merge request !350
2016-10-19 16:42:24 +00:00
Torsten Grote
5a0fa5dcc7 Last round of addressing review issues 2016-10-19 14:38:11 -02:00
akwizgran
97223cce97 Fixed a typo in a constant. 2016-10-19 15:14:07 +01:00
akwizgran
08b191d72e Fixed a typo in a comment in a test. URGENT STUFF! 2016-10-19 15:04:24 +01:00
akwizgran
06335c2c30 Truncate all messages to valid length before sending. 2016-10-19 14:49:09 +01:00
Torsten Grote
8f882dc910 Addressing second round of review issues 2016-10-19 10:43:02 -02:00
Torsten Grote
0523c4e718 Address issues found in code review 2016-10-19 10:43:01 -02:00
Torsten Grote
7bf4aebdaf Move post/message creation into clients
This way the forum and private group client do not need to keep track of
message timestamps themselves and do not need to interact with
post/message factories.
2016-10-19 10:43:01 -02:00
Torsten Grote
6db59ffce5 Parsing and retrieval of private groups in PrivateGroupManager 2016-10-19 10:43:01 -02:00
Torsten Grote
e0835ad460 Add "Created by" to ActionBar 2016-10-19 10:43:00 -02:00
Torsten Grote
c83d4bbb39 Implement first prototype of private group message threads 2016-10-19 10:43:00 -02:00
Torsten Grote
65b47bb5d2 Refactor Forum Controller, so it can be used by private groups 2016-10-19 10:42:59 -02:00
Torsten Grote
9ce95d6de7 Refactor Forum Activity and adapters to be re-used for private groups 2016-10-19 10:42:59 -02:00
akwizgran
9d2c56e75f Upgraded Gradle plugin to 2.2.1. 2016-10-19 12:24:22 +01:00
akwizgran
32c4f61e68 Merge branch '670-uncaught-exceptions-do-not-print-a-stack-trace-in-introductionintegrationtest' into 'master'
Print stack trace for uncaught exceptions during tests for easier debugging

Closes #670

See merge request !355
2016-10-14 12:14:05 +00:00
Torsten Grote
6e04664915 Print stack trace for uncaught exceptions during tests for easier debugging 2016-10-14 08:44:47 -03:00
akwizgran
5674ee2d88 Converted group list controller to constructor injection. 2016-10-12 17:28:44 +01:00
akwizgran
8637faf858 Merge branch '704-constructor-injection' into 'master'
Use constructor injection for controllers

Also made some listeners volatile.

This is part of #704 - if I don't find any other classes that need constructor injection I'll close the ticket.

See merge request !351
2016-10-12 16:02:53 +00:00
akwizgran
970cbbf557 Merge branch 'not-null-by-default' into 'master'
Null safety annotations

The @NotNullByDefault annotation marks all fields, methods and parameters in a class or package @NotNull, so Android Studio will warn if values that may be null are used. Please use this annotation for new classes, and specify @Nullable for any fields, methods and parameters that may be null.

Injected fields are initialised to null, so injected classes should use @MethodsNotNullByDefault and @ParametersNotNullByDefault, or specify @Nullable for injected fields.

See merge request !349
2016-10-12 16:00:54 +00:00
akwizgran
57ac4a5374 Removed Maven Central repo. 2016-10-12 17:00:03 +01:00
akwizgran
2b91631ba5 Use constructor injection for controllers.
Also made some listeners volatile.
2016-10-11 12:31:21 +01:00
akwizgran
b327122255 Merge branch '660-implement-ux-for-the-list-of-private-groups' into 'master'
Private Group List UI

This MR implements the UI for the list of private groups.

It reacts to three types of events to refresh the displayed data:
* new group message received
* private group added
* private group removed

Missing from final implementation:
* entering groups
* adding new groups
* reacting to a future group dissolved event
* actually removing a dissolved group

![device-2016-09-29-180741](/uploads/666f04e8c9e2c81bdfe7f5648c14e71d/device-2016-09-29-180741.png)
![device-2016-10-03-141200](/uploads/5d0a54ea5a31d64404591c03ccf1e3b6/device-2016-10-03-141200.png)
![groups](/uploads/3eba757e21837739a129aab15100c06a/groups.gif)

Closes #660

See merge request !335
2016-10-11 10:22:26 +00:00
akwizgran
0b3ec9aa4c Merge branch '676-keyboard-isn-t-shown-when-forum-text-entry-field-gets-focus' into 'master'
Always show the keyboard when asked for it

The main fix is maintaining the internal keyboard state when the entire view gets hidden, because `onMeasure()` isn't called anymore in that case and can't update it itself.

Closes #676

See merge request !348
2016-10-11 09:47:27 +00:00
Torsten Grote
154e02723f Always show the keyboard when asked for it
and maintain keyboard state when hiding view.
2016-10-10 13:53:53 -03:00
Torsten Grote
b09e30a95f Private Group List 2016-10-10 13:48:34 -03:00
akwizgran
3ea36bbd40 Merge branch '551-destroyable-context' into 'master'
Always check whether the context has been destroyed

#551 has the same root cause as #610, which is that when a background operation completes, we need to check whether the activity or fragment that started the operation has been destroyed before doing anything with the UI. 

DestroyableActivity has been renamed to DestroyableContext because it's now implemented by some fragments as well. Various existing listener interfaces now extend DestroyableContext.

I also modified the ActivityLifecycleController interface so the activity is passed into the onActivityCreate() method rather than being injected - @ernir please check I haven't broken anything!

Closes #551

See merge request !341
2016-10-10 15:00:15 +00:00
akwizgran
cb983f02c2 Always check whether context has been destroyed. 2016-10-10 15:54:08 +01:00
akwizgran
f1730aa7d9 Merge branch '696-npe-key-agreement-task' into 'master'
Fix NPE when stopping KeyAgreementTask, improve thread safety

This branch fixes #696 and improves the thread safety of the camera code, mostly by adding @UiThread annotations and occasionally by moving stuff onto the UI thread that might have happened on other threads before.

Closes #696

See merge request !345
2016-10-10 14:46:25 +00:00
akwizgran
857665db79 Merge branch '373-slow-contact-list' into 'master'
Use new group metadata for showing lists

What was supposed to be a minimal change turned into a rather large MR. I did my best to keep things in separate commits, so I can still split this into smaller MRs if desired.

While making use of the new group metadata in the contact and forum list, I noticed some other things in need of improvement to get rid of needing to load all messages:
* Refactor `SharingManager` so its events provide message headers that can be used to update list items
* Add `GroupId` to conversation items, so the metadata of the respective group can be updated as well when marking the items read
* Create a very basic `ConversationManager` so the GroupCount for the various clients can be queried in one go without needing to know all their groups per contact
* Fix a nasty bug that caused forum and blog invitation to not update their read state
* Fix some bugs related to displaying the forum list with proper unread count

Some casual measurements with just a few contacts and messages showed a reduction of the contact list load time by one third.

See merge request !343
2016-10-10 14:00:36 +00:00
Torsten Grote
7f2db71160 Address review comments 2016-10-10 10:46:30 -03:00
akwizgran
60dee5c4cb Added null safety annotations. 2016-10-10 14:32:26 +01:00
Torsten Grote
784561144a Use new GroupCount to display Forum List
Fixes #531, #532
2016-10-10 08:00:17 -03:00
Torsten Grote
70d39d03bc Use group metadata from ConversationManager for showing contact lists
Fixes #373
2016-10-10 08:00:17 -03:00
Torsten Grote
48a3db46bc Properly pass message read state for sharing invitations and responses
Fixes #350
2016-10-10 08:00:16 -03:00
Torsten Grote
f52819f4ca Create a basic ConversationManager for querying GroupCount
This is also lays the groundwork for #384
2016-10-10 08:00:16 -03:00
Torsten Grote
457c30f3f2 Add GroupId to conversation items 2016-10-10 08:00:15 -03:00
Torsten Grote
1731369d7a Refactor SharingManager so its events provide message header 2016-10-10 08:00:12 -03:00
akwizgran
064b920626 Merge branch '687-refactor-adapters' into 'master'
Refactor existing adapters into a generic superclass

This MR also moves various blog classes into their own packages and makes the required visibility changes.

Closes #687

See merge request !346
2016-10-10 10:15:13 +00:00
Torsten Grote
b2fa039474 Refactor existing adapters into a generic superclass
This commit also moves various blog classes into their own packages and
makes the required visibility changes.
2016-10-06 11:30:10 -03:00
akwizgran
9112d17a4b Merge branch '611-body-cache-thread-safety' into 'master'
Make body cache thread-safe, reduce visibility of classes

Closes #611

See merge request !347
2016-10-06 13:43:09 +00:00
akwizgran
86fbb89637 Make body cache thread-safe, reduce visibility of classes. 2016-10-06 14:41:55 +01:00
akwizgran
b3bea1f945 Merge branch 'fix-introduction-unit-tests' into 'master'
Fix IntroductionManager unit tests

I forgot to run the unit tests after changing the `GroupCount` serialization in response to a review comment. This MR fixes the tests.

See merge request !344
2016-10-06 10:59:27 +00:00
akwizgran
543304973e Fixed an NPE, improved thread safety of camera code. 2016-10-06 11:53:07 +01:00
akwizgran
fc38738428 Merge branch '598-remove-unused-code' into 'master'
Remove unused UI code and layouts

If we ever need this code, it will be in the git history.

Closes #598

See merge request !342
2016-10-06 08:55:33 +00:00
Torsten Grote
d661fa0661 Fix IntroductionManager unit tests 2016-10-05 18:51:03 -03:00
akwizgran
e2eda8fef0 Merge branch '584-store-latest-timestamp-and-unread-count-in-group-metadata-for-private-messaging' into 'master'
Store message count, unread count and timestamp of latest message in group metadata

This is to eventually address #373 and slowness of other lists. The group metadata is not yet used, but if this MR isn't merged fast, another commit that actually uses it and thus takes care of the slowness will be added.

Closes #584, #585, #586

See merge request !336
2016-10-05 16:20:32 +00:00
akwizgran
604542c19b Remove unused UI code and layouts. 2016-10-05 17:09:16 +01:00
Torsten Grote
a727a0817e Store message count, unread count and timestamp of latest message
in group metadata to be able to speed up group listings.

Closes #584, #586, #585
2016-10-05 12:34:37 -03:00
Torsten Grote
3fa84ec7a8 Merge branch '680-release-camera-surface' into 'master'
Release camera surface to work around Android bug #54285

Closes #680

See merge request !340
2016-10-05 14:41:19 +00:00
Torsten Grote
e5f5511112 Merge branch 'identicon-cleanup' into 'master'
Clean up identicon code, remove unused classes

I noticed some debug-level logging coming from the identicon code, went in there to remove it, and realised half the code was unused, so I removed that too.

No functional changes except that the logging is gone and the opacity is now OPAQUE rather than UNKNOWN, which wasn't valid in this context.

See merge request !339
2016-10-05 14:38:17 +00:00
akwizgran
16ecb2ce8d Release surface to work around Android bug #54285. 2016-10-05 14:47:23 +01:00
akwizgran
c49c888f9f Merge branch '644-missing-header' into 'master'
Remove loading callbacks from fragment listener

Now fragments are responsible for their own Progress bars.

Closes #642

See merge request !316
2016-10-05 13:34:09 +00:00
Ernir Erlingsson
6b3db67ef5 removed fragment progress callback and fixed missing header 2016-10-05 14:21:14 +02:00
akwizgran
293c06fd61 Merge branch '92-emoticons' into 'master'
Emoji Support for all user input

All text that can be generated by users will show emoji from the shipped sprites by using the `EmojiTextView` instead of the normal `TextView`.

For all messages and posts, the custom emoji keyboard is now available as well. For this, a new `LargeTextInputView` has been introduced that is a sub-class of `TextInputView`. In order for the emoticon keyboard to work properly the existing views had to be modified heavily, sometimes resulting in new behavior such as scroll views now being above the fixed input field. Actual testing on a device (preferably with a tiny screen) is recommended to make sure this still works as expected. Screenshots will be included at the end of this post.

This MR also disables menu actions rather than hiding them and it includes a fix for a regression that was not showing the keyboard automatically in forums.

![device-2016-09-28-104914](/uploads/8ce9c8f61b32dc5a46b89a3a2cf1e6a4/device-2016-09-28-104914.png)
![device-2016-09-28-105026](/uploads/96ef9c40547c7a44fbc4517c87637a7e/device-2016-09-28-105026.png)
![device-2016-09-28-105041](/uploads/a0d1ed23ccb66e8bf9541737407142e3/device-2016-09-28-105041.png)
![device-2016-09-28-105440](/uploads/fb76ed2adc87600cfb0fc33c3962d4a6/device-2016-09-28-105440.png)
![device-2016-09-28-105515](/uploads/51f845d697cee3df38d1b9eef3c0ddfc/device-2016-09-28-105515.png)

Closes #92

See merge request !329
2016-10-05 11:01:12 +00:00
akwizgran
1c55fae704 Merge branch '695-introduction-failure' into 'master'
Fix regression in IntroduceeManager

This was happening when the remote response arrives before the local
response is made and thus the local response needs to be send with the
ACK following. The problem was that we ACK was sent before the response
which is not allowed and resulted in the session being aborted by the
introducee. This was happening, because recursion is hard ;)

The fix is only restarting another protocol engine to send the ACK
after the first run has been completed.

An integration test was added to prevent such regression in the future
and to test this code path.

Closes #695

See merge request !338
2016-10-04 21:38:48 +00:00
Torsten Grote
95670937c3 Fix regression in IntroduceeManager
This was happening when the remote response arrives before the local
response is made and thus the local response needs to be send with the
ACK following. The problem was that we ACK was sent before the response
which is not allowed and resulted in the session being aborted by the
introducee. This was happening, because recursion is hard ;)

The fix is only restarting another protocol engine to send the ACK
after the first run has been completed.

An integration test was added to prevent such regression in the future
and to test this code path.
2016-10-04 17:44:47 -03:00
Ernir Erlingsson
165deebb40 Merge branch '690-introduction-onboarding-is-shown-again-if-it-s-dismissed' into 'master'
Don't show introduction onboarding again no matter how dismissed

Closes #690

See merge request !337
2016-10-04 20:30:08 +00:00
Torsten Grote
0b0cae06ae Don't show introduction onboarding again no matter how dismissed 2016-10-04 14:51:37 -03:00
Torsten Grote
f8e0441de8 Emoji Support for all user input
All text that can be generated by users will show emoji from the
shipped sprites.

For all messages and posts, the custom emoji keyboard is now available.

This also disables menu actions rather than hiding them and thus
closes #677

Included is a fix for a regression that was not showing the keyboard
automatically in forums and thus
closes #676
2016-10-04 14:12:17 -03:00
akwizgran
a422c626b3 Merge branch '673-privategroupmanager-facade' into 'master'
Create PrivateGroupManager Facade and stub implementation

Some classes were renamed and new base classes introduced in the process. I suggest to expand the "Changed files" before reviewing to get an overview over the changes.

Closes #673

See merge request !332
2016-10-03 19:46:55 +00:00
Torsten Grote
6ece398a21 Create PrivateGroupManager Facade and stub implementation 2016-09-30 12:05:35 -03:00
akwizgran
8b50cb1461 Merge branch 'fix-integration-tests' into 'master'
Fix integration tests

First problem was a race condition with message delivery and the second
one due to the fact that we no longer plan to allow adding of additional
blogs, so the test for that has simply been removed.

See merge request !333
2016-09-29 16:43:56 +00:00
Torsten Grote
f28bc691a5 Merge branch '685-teaser-length' into 'master'
Check length of text after spanning

Closes #685

See merge request !334
2016-09-29 14:12:10 +00:00
akwizgran
42056720fa Check length of text after spanning. #685 2016-09-29 15:06:50 +01:00
Torsten Grote
0861ee1f10 Fix integration tests
First problem was a race condition with message delivery and the second
one due to the fact that we no longer plan to allow adding of additional
blogs, so the test for that has simply been removed.
2016-09-29 11:02:44 -03:00
akwizgran
a30de6309d Removed translated strings that were causing lint errors.
The app _name string is marked as non-translatable and the new_identity_item string no longer exists.
2016-09-29 12:47:29 +01:00
akwizgran
d112b42d20 Fixed obsolete ID in layout. 2016-09-29 12:45:56 +01:00
akwizgran
fd6719301a Merge branch '556-thread-safety-blocking-issues' into 'master'
Forum controller thread safety and tree safety

This branch solves the concurrent forum issues by code restructure and refactoring.

Closes #556 
Closes #552 

See merge request !262
2016-09-29 09:30:51 +00:00
Ernir Erlingsson
92f2e7b0fc merge with master and fixes after comments 2016-09-29 01:30:13 +02:00
akwizgran
ee98900613 Merge branch '644-missing-header-only' into 'master'
Remove toolbar animation to fix the missing toolbar on first start

Closes #644

See merge request !331
2016-09-28 17:03:55 +00:00
akwizgran
747553f577 Cleaned up identicon code, removed unused classes. 2016-09-28 17:34:12 +01:00
Torsten Grote
8a4c162bba Remove toolbar animation to fix the missing toolbar on first start 2016-09-28 13:33:09 -03:00
akwizgran
7e806c8cf2 Merge branch '679-own-personal-blogs-can-be-removed' into 'master'
Prevent personal blogs from being removed

This also adds unit tests to prevent regressions like this in the future.

Closes #679

See merge request !330
2016-09-28 16:22:46 +00:00
akwizgran
e466ed580c Merge branch '589-when-a-message-is-shared-share-its-transitive-dependencies' into 'master'
When a message is shared, share its transitive dependencies

Like other recursive operations on the dependency graph, this is
not done in a single transaction to prevent an attacker from creating
arbitrary large transactions.

So at startup, the `ValidationManager` finds and resumes any
unfinished operations, by looking for unshared messages with shared
dependents.

Closes #589

See merge request !325
2016-09-28 16:19:43 +00:00
Torsten Grote
d058172429 When a message is shared, share its transitive dependencies
Like other recursive operations on the dependency graph, this is
not done in a single transaction to prevent an attacker from creating
arbitrary large transactions.

So at startup, the `ValidationManager` finds and resumes any
unfinished operations, by looking for shared messages with unshared
dependencies.
2016-09-28 13:17:11 -03:00
Torsten Grote
1f0b305139 Prevent personal blogs from being removed
This also adds unit tests to prevent regressions like this in the
future.
2016-09-28 13:12:20 -03:00
Torsten Grote
7a0db798d1 bump expiry date and update translations 2016-09-28 11:56:55 -03:00
Torsten Grote
7e6a522eee Merge branch '346-camera-parameters' into 'master'
Try harder to find suitable camera parameters

This branch fixes QR code scanning on the Galaxy Nexus running Cyanogen Mod 12.1 (Android 5.1.1), without breaking QR code scanning on any of the other test devices.

The problem on the Galaxy Nexus was that the selected scene mode was overriding the selected focus mode, so we asked for continuous picture mode but got macro mode. Macro mode requires startAutoFocus() to be called, but we weren't calling it because we'd asked for continuous picture mode.

The fix for that problem is to query the focus mode after applying the parameters and call startAutoFocus() based on the actual mode rather than the requested mode.

But then I discovered another problem: barcode scene mode was setting the flash to auto, so in low light the flash was turning on and off while trying to scan QR codes. That might work well for printed QR codes, but it's terrible when scanning from a screen.

The fix for the new problem is to select barcode scene mode, then try to disable the flash, and if that fails, reset the scene mode. Then we pick the best available video stabilisation, focus mode and preview size.

On the Galaxy Nexus with CM 12.1, that means we use continuous picture mode instead of barcode scene mode, which works fine. All the other test devices pick the same settings as before.

See merge request !321
2016-09-28 14:40:56 +00:00
akwizgran
33795e7046 Use compareAndSet() instead of locking. 2016-09-27 23:02:54 +02:00
Ernir Erlingsson
2d59b9095c Fixing concurrency issues and refactoring code 2016-09-27 23:02:44 +02:00
Torsten Grote
8fb820c967 Merge branch 'check-if-listfiles-returns-null' into 'master'
Check whether File#listFiles() returns null

The docs say this can happen if there's an I/O error. Also fixed a throw-in-finally-block warning and renamed an IoUtils method to make its contract clearer.

See merge request !328
2016-09-27 16:17:09 +00:00
akwizgran
53d0b8b21e Check whether File#listFiles() returns null.
The docs say this can happen for a directory if there's an I/O error.
2016-09-27 15:52:57 +01:00
akwizgran
d389f79a48 Merge branch '346-remove-base32-todos' into 'master'
Remove base32 TODOs, fix a potential NPE

I thought we'd be able to get higher data density in QR codes by using base32 instead of base64, allowing the QR code to use alphanumeric mode instead of byte mode. But I tried it, and although the QR code does use alphanumeric mode, it comes out at exactly the same size (see the 346-use-base32-for-qr-codes branch). So this MR removes the TODOs and fixes a potential NPE I spotted while working on the other branch.

See merge request !327
2016-09-27 14:19:45 +00:00
akwizgran
b278c7d9cb Remove base32 TODOs - base64 is just as good. 2016-09-27 13:31:07 +01:00
Torsten Grote
a85043efb5 Merge branch 'recreate-cache-dir' into 'master'
Recreate cache directory after deleting app data

This fixes a warning on the Moto G:
```
E/libEGL: error creating cache file /data/data/org.briarproject/cache/com.android.opengl.shaders_cache: No such file or directory (2)
```

@grote might possibly be related to hardware rendering issues.

See merge request !326
2016-09-27 12:07:43 +00:00
akwizgran
133722dd2c Code cleanup. 2016-09-27 11:51:24 +01:00
akwizgran
9e3db12ea2 Recreate the cache dir after deleting app data. 2016-09-27 11:44:14 +01:00
akwizgran
f461ec4ab0 Merge branch '627-tests-for-introduction-security-properties' into 'master'
Add more introduction tests for fake MAC and modified timestamp

Closes #627, #669

See merge request !315
2016-09-26 17:09:56 +00:00
Torsten Grote
e87c301e3a Add more introduction tests for fake MAC and modified timestamp 2016-09-26 13:31:00 -03:00
akwizgran
17bacc1116 Merge branch '357-introduction-feature-is-not-very-visible' into 'master'
Add first onboarding screen

When the user enters a private conversation after adding her second
contact, an onboarding screen will be shown highlighting the possibility
of introducing the contacts to each other.

![intro2](/uploads/b2cd4377f39974ca1fbd95649558e487/intro2.gif)

Closes #357

See merge request !324
2016-09-26 14:30:45 +00:00
Torsten Grote
401abf2c0c Add first onboarding screen
When the user enters a private conversation after adding her second
contact, an onboarding screen will be shown highlighting the possibility
of introducing the contacts to each other.
2016-09-26 10:42:20 -03:00
akwizgran
a4d08f4cf1 Try harder to find suitable camera parameters. #346 2016-09-26 12:14:23 +01:00
akwizgran
eb6189150f Merge branch '92-emoticons' into 'master'
Emoji

This MR introduces a custom Emoji implementation to Briar for devices that do not support Emoji sufficiently. It is heavily based on Signal's implementation. Hence, the license for the Android part has been changed to GPLv3.

So far, emoji input is only supported for forums and private conversations that both rely on the same `TextInputView`.

![emoji2](/uploads/f69af031e72efb0bd6f0f67a9574d11f/emoji2.gif)

See merge request !317
2016-09-23 16:31:38 +00:00
akwizgran
c917110e6a Emoji: minor bug fixes, code cleanup, logging, visibility, 2016-09-23 17:18:21 +01:00
Torsten Grote
d5beca5351 Port Signal's emoji implementation to Briar
Add functionality to save and restore recently used Emojis

Update emoji and add new categories based on AOSP's XML file
2016-09-23 17:18:18 +01:00
akwizgran
1fdbe65dde Fixed a broken test. 2016-09-23 11:34:02 +01:00
Torsten Grote
1583163f88 Merge branch '538-recipient-offers-message-to-sender' into 'master'
Don't offer messages back to the sender

Closes #538

See merge request !323
2016-09-22 15:02:06 +00:00
akwizgran
ee35110167 Merge branch '625-avoid-repeated-author-status-lookups' into 'master'
Avoid repeated author status lookups

Closes #625

See merge request !322
2016-09-22 13:18:45 +00:00
akwizgran
72bf701345 Mark a received message as seen by the sender. 2016-09-22 11:31:32 +01:00
Torsten Grote
fd4dbdc081 Avoid repeated author status lookups 2016-09-21 17:26:24 -03:00
akwizgran
97937428bb Merge branch '329-btp-header' into 'master'
Include stream number in stream header nonce

See the corresponding change in the BTP spec for an explanation:
388e1d23c0

Closes #329

See merge request !320
2016-09-21 08:58:36 +00:00
akwizgran
4be1c1bb7d Include stream number in stream header nonce. 2016-09-20 15:27:01 +01:00
Torsten Grote
89ab4dd04e Merge branch '665-android-executor-npe' into 'master'
Fix potential NPE in AndroidExecutorImpl

Always wait for the startup latch to ensure the background thread's Handler is initialised before using it.

Closes #665.

See merge request !319
2016-09-19 16:15:41 +00:00
akwizgran
f3ea731565 Always wait for latch before using background handler. 2016-09-19 10:46:25 +01:00
Torsten Grote
bf023874f0 Merge branch '664-no-delivery-hook' into 'master'
Don't throw an exception if the client has no delivery hook

Closes #664.

See merge request !318
2016-09-16 23:41:53 +00:00
akwizgran
16ac73d002 Don't throw an exception if the client has no delivery hook. 2016-09-16 22:02:04 +01:00
akwizgran
cc439aa74f Merge branch '569-convert-html-to-plain-text-safely-and-readably' into 'master'
Clean HTML from RSS feeds with Jsoup and Show Link Warning

This adds HTML cleaning with the Jsoup library based on a whitelist. The resulting HTML is then used to create a `Spannable` in Android. This spannable is traversed and URLs are replaced by a custom dialog fragment that shows the following warning.

![device-2016-09-07-154936](/uploads/87a9800bde2676b0f2ddf5a894aa04eb/device-2016-09-07-154936.png)

Closes #569

See merge request !311
2016-09-15 10:29:39 +00:00
Torsten Grote
7a1003178d Introduce new MovementMethod for text selection and link clicks 2016-09-13 15:02:27 -03:00
Torsten Grote
cab667ef6a Show all apps that can open links and dismis dialog after click 2016-09-13 14:47:52 -03:00
Torsten Grote
7035d8063c Clean HTML from RSS feeds with Jsoup and show Link Warning 2016-09-13 14:47:51 -03:00
Torsten Grote
2fbbb663a3 Merge branch '619-validation-transactions' into 'master'
Refactor ValidationManager and fix some bugs

The validation manager uses a pattern of throwing an exception from within a transaction, then trying to ensure that the transaction gets committed anyway by setting it complete in a finally block. This is a bad idea - if the intentional exception isn't thrown but a later method throws an unintentional exception, the changes up to that point will be committed instead of rolled back.

I reworked the control flow to remove this pattern, and in the process realised that the validation manager was doing a lot of work to provide objects to attach to MessageStateChangedEvents, which are only used by tests these days. So I fixed that and a couple of other issues.

The VALID state is no longer needed and has been removed.

Closes #619

See merge request !313
2016-09-13 15:18:16 +00:00
akwizgran
b4714b5360 Removed redundant message state check. 2016-09-12 12:09:21 +01:00
akwizgran
8a3e5bfb50 Refactor ValidationManager and fix some bugs. #619 2016-09-09 18:03:14 +01:00
akwizgran
fd4275733f Merge branch 'fix-master-ui-handlers' into 'master'
Introduce a DestroyableActivity for UI result handlers



See merge request !314
2016-09-08 14:52:07 +00:00
Torsten Grote
af5b246d7b Introduce a DestroyableActivity for UI result handlers 2016-09-08 11:25:02 -03:00
akwizgran
49e9ee2b6d Merge branch '637-effect-of-touching-a-post-is-inconsistent' into 'master'
Resolve Outstanding Blog Issues

This MR has several commits which fix individual small issues. The biggest commit is a refactoring of the `BlogActivity`, so its `ViewPager` can be used also for the Blog feed.

Closes #637

See merge request !309
2016-09-08 11:57:18 +00:00
akwizgran
1a611d55be Merge branch '610-result-handlers-can-return-results-to-destroyed-activities-fragments' into '637-effect-of-touching-a-post-is-inconsistent'
Check if activity is still alive before returning results to it

Closes #610

See merge request !312
2016-09-08 11:45:52 +00:00
Torsten Grote
7c5945de01 Check if activity is still alive before returning results to it 2016-09-07 17:44:07 -03:00
Torsten Grote
ccc49df08e Address review issues 2016-09-07 14:24:19 -03:00
Torsten Grote
ac325d4423 remove unused resources 2016-09-07 12:51:43 -03:00
Torsten Grote
0a0e1b4280 Always replace current blog activity when new one opened
Fixes #635
2016-09-07 12:51:42 -03:00
Torsten Grote
cb64740916 Set blog title when opening blog
Fixes #634
2016-09-07 12:51:42 -03:00
Torsten Grote
73bc86df4a show only a teaser for blog posts in lists 2016-09-07 12:51:41 -03:00
Torsten Grote
307e124ee8 Make the blog post pager usable for the feed and individual blogs 2016-09-07 12:51:41 -03:00
Torsten Grote
60b4c5649a Make blog post text clickable
by making it only selectable in detail view
2016-09-07 12:51:40 -03:00
akwizgran
e0e46a726e Merge branch '641-rss-feed-entry-comparator-wrongly-assumes-existing-date' into 'master'
Don't crash if an RSS entry has no date

Closes #641

See merge request !310
2016-09-07 14:06:06 +00:00
akwizgran
668eaf45f6 Merge branch '636-blogpostfragment-should-use-the-same-layout-as-the-blog-feed' into 'master'
Show Blog Posts always in the same design

This also adds comments to the blog post detail screen that were previously missing.
It re-uses the existing `BlogPostViewHolder` and its `bind()` methods effectively reducing the amount of code necessary.

Closes #636

See merge request !307
2016-09-07 13:12:25 +00:00
akwizgran
fb9d743d11 Merge branch '627-tests-for-introduction-security-properties' into 'master'
Add one introduction test for modified response message

This is only the first part of #627, but I am putting it up for review already, since the second part will be very similar and issues found here will likely apply to both.

See merge request !306
2016-09-07 13:08:34 +00:00
akwizgran
fc2213c042 Merge branch '633-blog-posts-from-other-users-sometimes-have-bold-nicknames' into 'master'
Fix view recycling issue with author name typeface

Closes #633

See merge request !305
2016-09-07 12:43:08 +00:00
akwizgran
34f9311f43 Merge branch '638-illegalstateexception-when-navigating-out-of-combined-feed-and-back-in' into 'master'
Allow Blog Controller listener to re-attach

Closes #638

See merge request !304
2016-09-07 12:39:04 +00:00
akwizgran
28fd84b261 Merge branch '632-badtokenexception-if-rss-error-dialog-is-show-after-activity-is-destroyed' into 'master'
Only show RSS Feed Import Failed Dialog when Activity lives

Closes #632

See merge request !303
2016-09-07 12:36:12 +00:00
Torsten Grote
448bf86379 don't crash if an RSS entry has no date 2016-09-06 15:55:53 -03:00
Torsten Grote
91e529a9e9 Merge branch 'socks-socket' into 'master'
Fetch RSS feeds via Tor

This patch replaces jsocks with our own minimal SOCKS 5 implementation, which is compatible with Android's OpenSSL hacks (see discussion on #599 for the horrifying details). This allows us to use OkHttp over Tor to fetch RSS feeds.

It turns out that SOCKS 5 without authentication is a really simple protocol: https://tools.ietf.org/html/rfc1928

Closes #599.

See merge request !308
2016-09-06 18:37:38 +00:00
akwizgran
74f9a0dd73 Use our own SOCKS implementation to talk to Tor. 2016-09-05 19:59:22 +01:00
Torsten Grote
54290bc374 Show Blog Posts always in the same design
This also adds comments to the blog post detail screen that were
previously missing.
2016-09-05 13:33:44 -03:00
Torsten Grote
3e39ad8a68 Add one introduction test for modified response message 2016-09-02 11:49:10 -03:00
Torsten Grote
07445ada41 Fix view recycling issue with author name typeface 2016-09-02 11:38:44 -03:00
Torsten Grote
5281d8b468 Allow Blog Controller listener to re-attach 2016-09-02 11:25:17 -03:00
Torsten Grote
0deac1d1b6 Only show RSS Feed Import Failed Dialog when Activity lives 2016-09-02 11:12:24 -03:00
akwizgran
387e44d114 Merge branch '496-implement-ui-for-reblogging-and-blog-comments' into 'master'
UI for reblogging and blog comments

This MR wraps all blog posts inside a `CardView` and adds a reblog button to blog posts. When this button is pressed, a new activity opens that shows the blog post again and allows the user to reblog it into their own blog. An optional comment can be added.

Also a new compound view `AuthorView` is introduced to display authors and their trust level in a consistent way.

The `BlogController` has been refactored, so the `FeedController` can share most code through an abstract base class.

![reblog2](/uploads/ce4764851c4cbe39684c95a3b427e3a2/reblog2.gif)

Closes #496, #437 

See merge request !300
2016-09-02 11:16:28 +00:00
akwizgran
bb00412187 Added @UiThread annotations, minor code cleanups. 2016-09-02 11:53:16 +01:00
Torsten Grote
bb0a2a5b32 Add a scene transition animation when reblogging a blog post 2016-09-02 11:53:16 +01:00
Torsten Grote
6d7e0aab07 Reblogging and Comment UI 2016-09-02 11:53:08 +01:00
Torsten Grote
da08072409 Make the user's own identities visually distinct
Closes #593
2016-09-02 11:52:34 +01:00
Torsten Grote
9664aea520 Introduce and use CardView and AuthorView for Blog Posts 2016-09-02 11:52:34 +01:00
akwizgran
636b587b9c Merge branch '605-add-database-method-for-retrieving-a-contact-by-local-and-remote-author-ids' into 'master'
Add database method for retrieving a contact by local and remote author IDs

Closes #605

See merge request !302
2016-09-01 20:00:27 +00:00
akwizgran
0afab09fdf Merge branch '616-continue-button-is-off-screen-when-adding-a-contact' into 'master'
Scroll down Adding Contact screen

Normally, you would introduce a new layout only for small screens, but in this case it is easier and has less maintenance overhead to just scroll down the screen, so the button is always visible after loading.

While I was at it, I also replaced `getLocalAuthors()` by the new `getLocalAuthor()`.

Closes #616

See merge request !294
2016-09-01 19:54:31 +00:00
Torsten Grote
fe4f71fe0f Remove lots of unused code for identity selection 2016-09-01 16:13:55 -03:00
Torsten Grote
6f0ffa8439 Add database method for retrieving a contact by author ID
and use it for retreiving the status of an author faster.
Also add tests for both.
2016-09-01 14:06:38 -03:00
Torsten Grote
69dd399bd2 Scroll down Adding Contact screen
Closes #616
2016-09-01 12:16:26 -03:00
akwizgran
8b7304f4cb Merge branch '599-fetch-rss-feeds-via-tor' into 'master'
Factor out Tor Socks Port

This MR factors out the local Tor port, so other clients can listen to an `TransportEnabledEvent` for Tor and start tasks as soon as Tor becomes available. This would also enable client to pass traffic through Tor's local SOCKS proxy.

See merge request !301
2016-09-01 11:57:13 +00:00
Torsten Grote
e023e5d8fc Start RSS Feed Fetcher only after TransportEnabledEvent for Tor 2016-09-01 12:55:25 +01:00
akwizgran
042050453a Merge branch '359-introduction-message-can-be-overlooked' into 'master'
Attach optional messages to invitations in Conversation view

![device-2016-08-29-115823](/uploads/e819324046b8a37ff06da7300046369c/device-2016-08-29-115823.png)![device-2016-08-29-115834](/uploads/051b4b64938bfa6f3ed610db28909865/device-2016-08-29-115834.png)

Closes #359

See merge request !299
2016-09-01 11:43:18 +00:00
akwizgran
1c15e690e1 Merge branch '352-conversation-screen-has-too-much-padding' into 'master'
Improve conversation screen graphics with less padding

This is how the conversation screen looks with this MR:

![1](https://code.briarproject.org/akwizgran/briar/uploads/3078636c93f7edc6b07034ad808ffc7c/device-2016-08-26-105325.png)
![2](https://code.briarproject.org/akwizgran/briar/uploads/fe0dcd3b1cbc6593aabed6faab1801d7/device-2016-08-26-105425.png)

This MR also replaces more raster graphics with vector graphics.

Closes #352

See merge request !298
2016-09-01 11:37:10 +00:00
Torsten Grote
61c05c1dd4 Fix tests broken by latest merges! 2016-08-31 11:32:53 -03:00
akwizgran
d8a9d83caf Merge branch '494-implement-backend-for-reblogging-and-blog-comments' into 'master'
Add support for comments and reblogging to Blog Client

Comments and reblogs need to depend on the post they refer to.
Since message dependencies are limited to one group,
the post and also the comments need to be wrapped
when commented on or reblogged to another blog (and group).

For this reason, in addition to comments, two new wrapping message types
are introduced. They retain all data of the original messages and allow
for reconstruction and signature verification.

This MR breaks backwards compatibility with old blog posts.
It removes the content type, title and parent ID from the post.

Furthermore, it includes one commit that replaces the `Message` in `MessageSharedEvent` with a `MessageId`.

Closes #494

See merge request !285
2016-08-30 23:09:31 +00:00
akwizgran
70a44e8a27 Merge branch '364-introduction-responses-should-be-signed' into 'master'
Sign Introduction Information to be able to discover MITM attack

The local introducee doesn't know whether each piece of information received from the introducer originates from the remote introducee or has been replaced by the introducer, i.e. whether the introducer is carrying out a man-in-the-middle attack.

The introduction protocol doesn't aim to detect or prevent man-in-the-middle attacks. We only aim to establish that if the remote identity public key is not replaced then the remote ephemeral public key, transport properties and timestamp are not replaced either. This MR adds a MAC and a signature to the introduction protocol's ACK message to fulfill that aim. See #364 for the detailed security argument.

Later, when the local introducee verifies that the remote identity public key belongs to a particular person (#513), she can also be sure that the remote ephemeral public key, transport properties and timestamp originated from that person.

Closes #364

See merge request !297
2016-08-30 23:05:25 +00:00
Torsten Grote
fc5a7290e3 Calculate and verify signature and MAC for Introduction ACKs
Before the introducee sends her ACK,
she derives a master key from the ephemeral shared secret as before.

Two nonces and a MAC key are then derived from the master key.
The local introducee signs one of the nonces and calculates a MAC
over her own identity public key, ephemeral public key,
transport properties and timestamp.
The local introducee includes the signature and MAC in her ACK.

On receiving the remote introducee's ACK,
the local introducee verifies the signature and MAC.
Should the verification fail, an ABORT is sent to the introducer and
the remote introducee that was added as inactive is deleted again.
2016-08-30 19:58:54 -03:00
Torsten Grote
7db0e4472a Introduction Client: Add MAC and signature to ACK message
The MAC and signature are not yet generated and verified.
This will happen in a later commit.
2016-08-30 19:25:53 -03:00
Torsten Grote
65bdd5558e Address bugs and comments from reblogging code review 2016-08-30 18:52:33 -03:00
Torsten Grote
3dd3a18694 Add support for comments and reblogging to Blog Client
Comments and reblogs need to depend on the post they refer to.
Since message dependencies are limited to one group,
the post and also the comments need to be wrapped
when commented on or reblogged to another blog.

For this reason, in addition to comments, two new wrapping message types
are introduced. They retain all data of the original messages and allow
for reconstruction and signature verification.

This commit breaks backwards compatibility with old blog posts.
It removes the content type, title and parent ID from the post
message structure.
2016-08-29 13:37:20 -03:00
Torsten Grote
743fc7dd1f Do not require an entire Message for the MessageSharedEvent 2016-08-29 13:36:09 -03:00
akwizgran
eb3da2aac9 Merge branch '539-564-568-notification-overhaul' into 'master'
Fix various notification bugs

Fixes #539, #564, #568.

Depends on !276.

See merge request !286
2016-08-29 16:27:47 +00:00
Torsten Grote
4518f59a54 Improving notices with messages attached in conversation view 2016-08-29 12:01:51 -03:00
Torsten Grote
11c0cb667f Improve conversation screen graphics with less padding
Also replace more raster graphics with vector graphics.
2016-08-29 10:02:15 -03:00
Torsten Grote
132a03d0a9 Merge branch '364-add-mac-to-crypto-component' into 'master'
Add MAC function to crypto component



See merge request !295
2016-08-26 13:34:56 +00:00
Torsten Grote
c666b0493c Merge branch '618-bdf-dictionary-iteration-order' into 'master'
Define iteration order of BdfDictionary

Closes #618

See merge request !296
2016-08-26 13:29:06 +00:00
akwizgran
625276067a Define iteration order of BdfDictionary. 2016-08-26 10:57:57 +01:00
akwizgran
30f2c192c6 Added MAC function to crypto component. 2016-08-26 10:06:24 +01:00
Torsten Grote
55602ed76a Merge branch '614-dev-reporter-does-not-close-stream' into 'master'
Use Briar's IoUtils.copy(), not H2's IOUtils.copy()

Our implementation closes both streams, H2's implementation leaves them open.

Closes #614.

See merge request !293
2016-08-25 13:11:54 +00:00
akwizgran
4ad0df2640 Use Briar's IoUtils, not H2's IOUtils. 2016-08-25 11:57:21 +01:00
Torsten Grote
d93f59b4ef Merge branch '555-blog-controller' into 'master'
Blog controller thread safety

This patch removes the mutable list of posts from the blog controller to make it thread-safe, and adds a cache of message bodies to speed up reloads.

Closes #555.

See merge request !276
2016-08-24 19:36:06 +00:00
akwizgran
c5f0272621 Merge branch '597-gradle-connectedcheck-fails-due-to-method-limit' into 'master'
Cleanup Gradle and Proguard to fix connectedCheck

Closes #597

See merge request !284
2016-08-24 17:22:30 +00:00
akwizgran
419aa2e97e Merge branch '311-audit-crash-report-and-feedback-fields-for-sensitive-or-identifying-information' into 'master'
Remove sensitive information from crash and feedback reports

This depends on MR !290 and removes also the crash report settings.

Closes #311

See merge request !291
2016-08-24 17:20:49 +00:00
akwizgran
015e45752e Merge branch '592-scrub-addresses-before-logging-them' into 'master'
Scrub addresses before logging them

MAC, IP and onion addresses are now scrubbed before logging to ensure we don't leave any sensitive information in plaintext on the device or send it in crash reports or feedback.

* Bluetooth MAC addresses keep the first and last octets
* IPv4 addresses keep the first and last octets
* IPv6 addresses should be scrubbed completely (couldn't test)
* Onion addresses keep the first three characters

If an address is invalid it will not be scrubbed to enable debugging, because it is most likely not sensitive.

Closes #592

See merge request !290
2016-08-24 17:18:48 +00:00
Torsten Grote
743053930a Remove crash report settings
Closes #311
2016-08-24 13:33:03 -03:00
Torsten Grote
cc7602e566 Remove sensitive information from crash and feedback reports 2016-08-24 13:24:12 -03:00
Torsten Grote
8b56e082b3 Scrub IP addresses before logging 2016-08-24 13:22:28 -03:00
akwizgran
b4889fe293 Merge branch '483-design-ux-for-importing-an-rss-feed' into 'master'
Add feed title to imported entries

Also fixes one bug where a new feed was not saved and improved HTML stripping a bit.

Closes #483

See merge request !287
2016-08-24 16:15:14 +00:00
Torsten Grote
ca094620d8 Scrub Bluetooth MAC addresses before logging 2016-08-24 12:47:20 -03:00
Torsten Grote
83be5c766e Scrub onion addresses from log 2016-08-24 12:47:20 -03:00
Torsten Grote
72fb4e9bc7 Post RSS entries within one transaction
This also fixes a bug where new feeds was not added properly.
2016-08-24 12:39:04 -03:00
Torsten Grote
e10f68b496 Add feed title to imported entries
Also fixes one bug where a new feed was not saved and
improved HTML stripping a bit.
2016-08-24 11:20:45 -03:00
Torsten Grote
22e7ec5b27 Merge branch 'gradle-2.14.1' into 'master'
Upgrade gradle to 2.14.1 and gradle plugin to 2.1.3.



See merge request !292
2016-08-24 13:44:47 +00:00
akwizgran
3bccdfa947 Upgraded gradle to 2.14.1 and gradle plugin to 2.1.3. 2016-08-24 11:06:49 +01:00
akwizgran
ef02908082 Improved blog controller's caching. 2016-08-23 23:34:33 +01:00
akwizgran
3aa3811d1b Fix progress bar issues. 2016-08-23 17:04:48 +01:00
Torsten Grote
00240bfa57 Merge branch '573-hidden-service-address' into 'master'
Add hidden service address for dev reports

Closes #573

See merge request !289
2016-08-23 13:14:35 +00:00
akwizgran
1f5de42844 Added hidden service address for dev reports. 2016-08-23 10:54:21 +01:00
akwizgran
17f9972a56 Merge branch '573-hidden-service-for-crash-reports-and-feedback' into 'master'
Server-side code for accepting crash reports and feedback

* Moved some shared code for copying InputStreams to OutputStreams into a utility class
* Modified the  dev report sender to send one report per connection
  * Easier to handle on the server side
  * If the connection fails after sending any reports, they don't need to be resent
  * Tor will reuse the circuit, so it's cheap
* Added server-side code for accepting dev reports
  * We need to protect the server's resources from DoS attacks
  * Reports can't be larger than 1 MB
  * Connections are limited to an average rate of one per minute
  * The rate limiter uses a token bucket to allow bursts of up to 1,000 connections
  * If the rate limit is exceeded, connection attempts will fail - clients will retry next time they sign in
  * The limits can be raised when we move to a bigger server (and when we have some users)


See merge request !288
2016-08-22 20:09:46 +00:00
akwizgran
18dd7d7aa9 Use socket timeout to limit number of open sockets. 2016-08-22 18:11:04 +01:00
Torsten Grote
bc6557e82d Merge branch '566-raise-api-level' into 'master'
Stop supporting Gingerbread

Closes #566

See merge request !278
2016-08-22 16:52:41 +00:00
akwizgran
5f86dd0207 Simple rate-limited server for saving dev reports. 2016-08-22 17:35:23 +01:00
Torsten Grote
659cf85ca1 really bump the expiry date 2016-08-22 12:56:42 -03:00
Torsten Grote
294bc0bdd2 update translations and bump expiry date 2016-08-22 12:23:21 -03:00
akwizgran
be84afc543 System.lineSeparator() doesn't exist in Java 6. 2016-08-22 16:20:38 +01:00
akwizgran
c6c62cab6c Use one connection per dev report.
This allows simpler server-side code, and a failure part-way through sending won't require restarting from the beginning next time.
2016-08-22 16:12:57 +01:00
akwizgran
d24e18a5d9 Use IoUtils.copy() in Tor plugin. 2016-08-22 16:05:15 +01:00
akwizgran
34a4a3f3c6 Renamed FIleUtils to IoUtils, added copy() method. 2016-08-22 15:29:13 +01:00
Ernir Erlingsson
48e1a65a24 raised api level and made necessary code changes 2016-08-22 00:08:47 +02:00
akwizgran
28be7d5de3 Added UI thread methods to AndroidExecutor. 2016-08-20 18:41:05 +01:00
akwizgran
dbdf567d4e Overhaul notifications to fix various bugs.
Fixes #539, #564 and #568.
2016-08-20 18:40:59 +01:00
akwizgran
98337a16ec Make BlogController thread-safe. #555 2016-08-20 16:07:04 +01:00
akwizgran
d34afa5f30 Merge branch '583-npe-org-briarproject-android-util-qrcodeutils-createqrcode' into 'master'
Fix two crashes when scanning QR codes

One crash was two due the `Context` being `null` and the other due to the camera being opened within an `AsyncTask`.

Closes #583

See merge request !282
2016-08-19 19:32:24 +00:00
akwizgran
746fce1b95 Merge branch '580-introduced-contacts-have-status-verified' into 'master'
Add introduced contacts as UNVERIFIED

This MR **breaks** the database schema requiring you to create a new account.

Closes #580

See merge request !280
2016-08-19 16:18:35 +00:00
Torsten Grote
c3a14d9275 Add a new event that is broadcasted when a contact is verified
Also, don't support unverifying contacts.
2016-08-19 12:41:10 -03:00
Torsten Grote
e690bcb3cc Add introduced contacts as UNVERIFIED
Closes #580
2016-08-19 12:31:00 -03:00
akwizgran
70b311db13 Merge branch '591-add-new-message-types-to-blogvalidator' into 'master'
Validate New Messages for Reblogging and Comments of Blog Posts

Closes #591

See merge request !279
2016-08-19 14:50:27 +00:00
Torsten Grote
caee7fe61b Validate New Messages for Reblogging and Comments of Blog Posts
Also includes unit tests for the new message types.

Closes #591
2016-08-15 14:28:17 -03:00
akwizgran
84d4bf2205 Merge branch '595-clients-should-decide-whether-to-share-messages' into 'master'
Let clients decide whether to share messages or not

Before this MR, the `ValidationManager` was sharing all messages after they had been delivered. Now, it is within the client's responsibility whether to share messages or not. So far, only the Blog and the Forum client are sharing incoming messages.

Closes #595

See merge request !283
2016-08-15 15:03:59 +00:00
Torsten Grote
bcf7488afd Cleanup Gradle and Proguard to fix connectedCheck 2016-08-11 14:54:31 -03:00
Torsten Grote
71196e3494 Let clients decide whether to share messages or not 2016-08-11 12:19:10 -03:00
Torsten Grote
9defb099dd Merge branch '342-organise-strings-xml-to-make-life-easier-for-translators' into 'master'
Group strings and remove unused resources

Before I grouped the strings, I removed unused resources as well to not group things that are not being used anymore.

The grouping tries to follow a normal usage lifecycle of the app where the user works their way down the navigation drawer and explores all features in one area.

Closes #342

See merge request !277
2016-08-11 14:28:19 +00:00
Torsten Grote
b3d6e7d12d Open Camera in UI Thread to prevent crash on some devices
Also properly handle back navigation when trying to add contacts.
2016-08-10 20:00:01 -03:00
Torsten Grote
739e8b4511 Fix one crash when showing QR Code 2016-08-10 18:44:55 -03:00
akwizgran
cb9a85eb88 Merge branch '447-lan-ports' into 'master'
Don't try to reuse already bound ports for key agreement

This is one cause of #447, but probably not the only one.

See merge request !281
2016-08-10 21:12:02 +00:00
akwizgran
7e086d0f4e Don't try to reuse already bound ports for key agreement. 2016-08-10 18:06:07 +01:00
akwizgran
dccc2e6ded Merge branch '587-java-lang-illegalstateexception-no-group-in-intent' into 'master'
Don't crash when pressing Up button in WriteBlogPostActivity

Closes #587

See merge request !275
2016-08-10 10:13:04 +00:00
Torsten Grote
72906acaee Group strings and remove unused resources
Closes #342
2016-08-09 15:06:11 -03:00
Torsten Grote
6fc7e69849 Harmonize Text Color of Blog Posts in Detail View 2016-08-08 13:00:25 -03:00
Torsten Grote
30092550c7 Don't crash when pressing Up button in WriteBlogPostActivity
Closes #587
2016-08-08 13:00:11 -03:00
Torsten Grote
b17fbcb135 Merge branch '582-tor-assets' into 'master'
Update Tor assets if they're older than the APK

Extract the Tor binary, GeoIP database and config file from the APK if they haven't been extracted since the APK was last updated.

On the Galaxy Nexus, skipping extraction of the binary if it's already up to date shaves about 1.5 seconds off the Tor plugin's startup time.

Closes #582.

Depends on !272.

See merge request !273
2016-08-08 14:47:24 +00:00
Ernir Erlingsson
060860bea5 Merge branch 'care_for_lint' into 'master'
Care for lint errors

No more general disabling of abortOnError
Resolved all *errors* beside language ones  MissingTranslation & ImpliedQuantity
Issue context: #567

See merge request !274
2016-08-08 14:35:07 +00:00
Torsten Grote
f8337d09ae Merge branch '578-tor-ports' into 'master'
Don't connect to Tor if it's already running

For some time now we've had a reliable way of shutting down the Tor process (the __OwningControllerProcess command line argument combined with the TAKEOWNERSHIP command), but TorPlugin#start() still assumes that Tor may already be running. This allows another app to bind the Tor control and SOCKS ports and collect confidential data from Briar (#578). It also allows two Briar instances running on the same device to try to communicate with the same Tor process, which prevents proper shutdown (#572).

This patch prevents the Tor plugin from starting unless it's able to start its own Tor process with the expected control and SOCKS ports. If two Briar instances are running on the same device, only one of them will be able to use Tor. The other should fail to start its Tor plugin and then function normally without Tor access, including normal shutdown.

Fixes #572, #578. Open another ticket if you want two Briar instances on the same device to have their own Tor processes. :-)

See merge request !272
2016-08-08 14:27:28 +00:00
ligi
4055bbfcd4 Care for lint errors
No more general disabling of abortOnError
Resolved all *errors* beside language ones  MissingTranslation & ImpliedQuantity
Issue context: #567
2016-08-07 17:12:27 +02:00
akwizgran
44d13ef28e Merge branch '579-unsubscribe-from-shared-blogs' into 'master'
Allow unsubscribing from shared blogs

Only personal blogs from non-contacts can be removed.
This also adds integration tests that check the conditions under which blogs can actually be removed.

Closes #579

See merge request !268
2016-08-05 17:36:36 +00:00
Torsten Grote
1ec56fa3ef Allow unsubscribing from shared blogs
Only personal blogs from non-contacts can be removed.
This also adds integration tests that check if blogs can actually be removed.

Closes #579
2016-08-05 14:08:09 -03:00
akwizgran
fce104487a Merge branch '470-second-invitation-doesn-t-disappear-from-available-forums' into 'master'
Return proper Invitation objects in SharingManager

Fixes #470

See merge request !265
2016-08-05 16:46:27 +00:00
Torsten Grote
17db03d40a Return proper Invitation objects in SharingManager
Fixes #470
2016-08-05 13:07:16 -03:00
akwizgran
3bbc8dcc4e Merge branch '429-explain-that-qr-codes-can-not-be-scanned-remotely' into 'master'
Show Explanatory Graphic Before Scanning QR Codes

![device-2016-08-04-182045](/uploads/9d6a5fea975d55e86dfe6d9bdbacb51c/device-2016-08-04-182045.png)
![device-2016-08-04-182058](/uploads/3ce86635f37ce2b903144a842d814d15/device-2016-08-04-182058.png)

Closes #429

See merge request !270
2016-08-05 15:55:46 +00:00
akwizgran
930194accf Merge branch '514-npe-crash-when-adding-contacts-in-emulator' into 'master'
Fix crashes when no Bluetooth or no Camera is available

Briar crashed when run in a device without bluetooth or without camera
such as an emulator.

Closes #514

See merge request !269
2016-08-05 15:51:32 +00:00
Torsten Grote
b04bde4f41 Fix crashes when no Bluetooth or no Camera is available
Briar crashed when run in a device without bluetooth or without camera
such as an emulator.

Closes #514
2016-08-05 12:30:57 -03:00
akwizgran
054a0d467c Merge branch '576-empty-state-messages-are-briefly-shown-when-they-shouldn-t' into 'master'
Prevent empty state messages from showing briefly

When we clear a list in `onPause()`, the proper behavior is that the empty state
message is shown, because the list is indeed empty.

However, we will reload the list content again in `onResume()`,
so the fix chosen in this MR is to force showing the progress bar
right after clearing the list.

Closes #576

See merge request !267
2016-08-05 15:02:52 +00:00
akwizgran
adc85dab7e Merge branch '575-fix-sharerleavesbeforeresponse-test' into 'master'
Fix SharerLeavesBeforeResponse Integration Test

Closes #575

See merge request !266
2016-08-05 15:01:10 +00:00
akwizgran
55b7e95d35 Update Tor assets if they're older than the APK. #582 2016-08-05 15:37:39 +01:00
akwizgran
36d15358a1 Don't connect to Tor if it's already running.
Fixes #572, #578.
2016-08-05 13:59:25 +01:00
akwizgran
d15d82ccec Merge branch '565-forums-sometimes-appear-empty' into 'master'
Fix regression where forum entries were not shown

Closes #565

See merge request !271
2016-08-05 09:12:49 +00:00
Torsten Grote
7be17668c1 Fix regression where forum entries were not shown
Closes #565
2016-08-04 19:07:30 -03:00
Torsten Grote
f8f1c7f0d4 Show Explanatory Graphic Before Scanning QR Codes
Closes #429
2016-08-04 18:22:48 -03:00
Torsten Grote
dd0d23359b Prevent empty state messages from showing briefly
When we clear a list in onPause(), the proper behavior is that the empty state
message is shown, because the list is indeed empty.

However, we will reload the list content again in onResume(),
so the fix chosen in this commit is to force showing the progress bar
right after clearing the list.

Closes #576
2016-08-03 19:12:53 -03:00
Torsten Grote
1a32458783 Merge branch '498-implement-ui-for-sharing-blogs' into 'master'
UI for sharing blogs

Not posting any screenshots, because the UI is the same as for forums.

This does not yet offer the possibility to unsubscribe from blogs again. Should be done in a different MR as this one is big enough already.

Closes #498,  #497

See merge request !263
2016-08-03 21:47:02 +00:00
Torsten Grote
a4cf91fba5 Use Inheritence for shared Forum and Blog Sharing Code 2016-08-03 18:46:20 -03:00
Torsten Grote
16da3f2cab Fix SharerLeavesBeforeResponse Integration Test
Closes #575
2016-08-03 18:15:14 -03:00
Torsten Grote
a3b2358164 Show Sharing Status for Blogs
This refactors the current SharingStatusActivity
so it can be used for forums and blogs.
2016-08-03 13:01:25 -03:00
Torsten Grote
e4f5d8e6e7 Show Blog Invitations
This refactors the forum invitation code,
so it can be used by both: forums and blogs.
2016-08-03 13:01:25 -03:00
Torsten Grote
a69a4028b0 Fix Blog Sharing Backend and Add Blog Sharing Integration Tests 2016-08-03 13:01:24 -03:00
Torsten Grote
a552d1b6a6 Show blog invitation requests and responses in private conversation 2016-08-03 13:01:24 -03:00
Torsten Grote
2f7d188a07 UI for Sharing Blogs
This commit refactors the code for sharing forums,
so it can be used for sharing blogs as well.

It does not yet include code for responding to blog invitations.
2016-08-03 13:01:23 -03:00
akwizgran
4a4366078a Moved briar-android tests into their respective packages. 2016-08-03 14:46:15 +01:00
akwizgran
9a32a13767 Merge branch '571-npe-in-feedfragment' into 'master'
Fix two Blog NPEs

Closes #571

See merge request !264
2016-08-03 13:44:21 +00:00
Torsten Grote
50d2742cae Fix two Blog NPEs
Closes #571
2016-08-03 10:38:45 -03:00
akwizgran
747ed023e4 Merge branch '517-simple-ui-for-managing-rss-feeds' into 'master'
Simple UI for Managing and Importing RSS Feeds

Please note that this does not yet include the reblogging style for displaying imported RSS entries.

![device-2016-07-26-170732](/uploads/e7f53f72e955d0c37518e0d552121105/device-2016-07-26-170732.png)
![device-2016-07-26-170755](/uploads/b8e0c91ed496b618d330364e840458dc/device-2016-07-26-170755.png)
![device-2016-07-26-171519](/uploads/fae5c16255d5a67673a4bd9be73c165f/device-2016-07-26-171519.png)

Closes #517

See merge request !251
2016-08-03 09:42:51 +00:00
akwizgran
e8c398e996 Merge branch '486-convert-rss-feed-entries-into-briar-blog-posts' into 'master'
Post new RSS entries into the user's personal blog

Closes #486

See merge request !250
2016-08-03 09:36:55 +00:00
akwizgran
85c9b48196 Merge branch '485-extract-new-entries-from-rss-feed' into 'master'
Keep track of which RSS entries have been seen

This is done by remembering the time of the latest entry.
All entries newer than that are considered new and will be posted.

Closes #485

See merge request !249
2016-08-03 09:25:45 +00:00
akwizgran
2a2966bd6c Merge branch '484-implement-background-task-for-fetching-rss-feeds' into 'master'
Implement background task for fetching RSS feeds

* Implemented in briar-core as a `ScheduledExecutorService`
  that gets started when the app starts
* The briar-api has a `FeedManager` interface
  that the UI can use to register and unregister feeds
* In this first iteration, feeds are fetched via HTTP(S), not Tor

Closes #484

See merge request !247
2016-08-03 09:23:07 +00:00
Torsten Grote
62c1c3e08d Simple UI for Managing and Importing RSS Feeds
Closes #517
2016-08-02 20:21:54 -03:00
Torsten Grote
6454acdaa5 Post new RSS entries into the user's personal blog
Closes #486
2016-08-02 20:02:40 -03:00
Torsten Grote
8d1a26ba72 Keep track of which RSS entries have been seen
This is done by remembering the time of the latest entry.
All entries newer than that are considered new and will be posted.

Closes #485
2016-08-02 19:19:48 -03:00
Torsten Grote
e527e30712 Implement background task for fetching RSS feeds
* Implemented in briar-core as a `ScheduledExecutorService`
  that gets started when the app starts
* The briar-api has a `FeedManager` interface
  that the UI can use to register and unregister feeds
* In this first iteration, feeds are fetched via HTTP(S), not Tor

Closes #484
2016-08-02 19:18:55 -03:00
akwizgran
4af5dbb45b Merge branch '562-npe-in-contactlistfragment' into 'master'
Fix NPE in ContactListFragment

Closes #562

See merge request !261
2016-08-02 09:32:59 +00:00
akwizgran
7132c88644 Merge branch '335-report-ui-improvements' into 'master'
UX improvements for crash reports and feedback

(The background color of the first screenshot is white, but I now changed it to use the same background color like everywhere)
![device-2016-08-01-174433](/uploads/a457489f61a7f0af48d4ebf8e3a10bf0/device-2016-08-01-174433.png)![device-2016-08-01-174500](/uploads/5275bff37db5a35e9ac9ad5038c06142/device-2016-08-01-174500.png)![device-2016-08-01-174532](/uploads/d0281156feff94262789102bd420ffeb/device-2016-08-01-174532.png)![device-2016-08-01-180330](/uploads/ed2d2d35b465df22b717cb84567a555f/device-2016-08-01-180330.png)

Closes #335

See merge request !246
2016-08-02 09:26:42 +00:00
Torsten Grote
a452060b41 Address issues found in code review 2016-08-01 18:08:05 -03:00
str4d
60a381430e Use full-screen overlay with microcopy instead of dialog to request report 2016-08-01 16:30:37 -03:00
str4d
827fd0aebb Move send action from FAB into toolbar 2016-08-01 16:30:37 -03:00
str4d
23eb5acafa Use AppCompatDelegate to add AppCompat support to BaseCrashReportDialog subclass
This enables the toolbar to be used as an action bar, and tinting of UI elements
like checkboxes.
2016-08-01 16:30:36 -03:00
str4d
2170d291a2 Hide debug report by default for both crash reports and feedback 2016-08-01 16:30:36 -03:00
Torsten Grote
31cd6e8958 Fix NPE in ContactListFragment
Closes #562
2016-08-01 16:26:01 -03:00
akwizgran
a51d2f47af Send messages in ascending order of timestamp.
The old behaviour was a leftover from the days of limited retention periods. The new behaviour will interact better with dependencies and message queues.
2016-08-01 18:49:24 +01:00
akwizgran
0d9cbb2793 Merge branch '543-flicker-when-forum-invitation-is-removed-from-list' into 'master'
Remove forum invitations from list instead of reloading

Closes #543

See merge request !255
2016-08-01 17:16:25 +00:00
Torsten Grote
c17ef86968 Clear Forum Invitation adapter only when invitations could be removed 2016-08-01 12:55:56 -03:00
akwizgran
ff28ae296e Merge branch '559-show-elapsed-time-in-blog-feeds-and-individual-blog-posts' into 'master'
Show Blog Post times with new timestamp and update it periodically

Closes #559

See merge request !260
2016-08-01 15:44:04 +00:00
Torsten Grote
ce2cb01558 Show Blog Post times with new timestamp and update it periodically
Closes #559
2016-08-01 12:37:14 -03:00
akwizgran
d096c1fead Merge branch '472-forum-no-message-for-the-inviter-if-an-invitee-accepts-or-declines' into 'master'
Show Responses to Forum Invitations in Private Conversation

Closes #472

See merge request !257
2016-08-01 13:46:30 +00:00
akwizgran
36dd9c30f8 Merge branch '553-pause-periodic-list-refresh-when-view-is-not-visible' into 'master'
Pause Periodic List Refresh when View is not Visible

This MR also sneaks in a second commit which stops the cevron in the forum list to sometimes lose its blue color. I noticed this when testing this MR with short refresh intervals. This is not as nice of specifying the color as a tint, but unfortunately tints are not yet ready for primetime on Android.

Closes #553

See merge request !259
2016-08-01 13:45:00 +00:00
akwizgran
2c993b62c2 Made the logger and a constant static. 2016-08-01 14:38:58 +01:00
akwizgran
737f0dac1e Initialise adapter in onCreate() to avoid double refreshing. 2016-08-01 14:38:55 +01:00
Torsten Grote
5a2fd2018f Fix Chevron in Forum sometimes losing its Blue Color 2016-08-01 14:35:52 +01:00
Torsten Grote
15d139afd4 Pause Periodic List Refresh when View is not Visible
Closes #553
2016-08-01 14:35:51 +01:00
akwizgran
2577b2ab2a Merge branch '488-show-notifications-for-new-blog-posts' into 'master'
Show notifications for new blog posts

This also adds a setting allowing people to turn blog post notifications off.

As instructed, this does not yet improve upon the current notification mechanism, but rather follows it, leaving the improvements for  #289.

Closes #488

See merge request !258
2016-08-01 12:36:16 +00:00
Torsten Grote
04af39f567 Show notifications for new blog posts
This also adds a setting allowing people to turn blog post notifications off.

Closes #488
2016-08-01 13:21:12 +01:00
Torsten Grote
64b596d0f9 Show Responses to Forum Invitations in Private Conversation
Closes #472
2016-08-01 12:13:51 +01:00
akwizgran
8bbb2184ff Merge branch '415-blog-activity-for-viewing-list-of-blog-posts' into 'master'
Show Personal Blog When Clicking On Post in Combined Blog Feed

This is your own personal blog when clicking on your own posts. Note how you can also write posts from that screen and no trust indicator is shown for the posts:
![device-2016-07-28-164921](/uploads/92993d579e26307ee0c6172cfd67bcd8/device-2016-07-28-164921.png)

This is somebody else's blog on a dedicated screen after clicking on a blog post:
![device-2016-07-28-164949](/uploads/d6c18d5b2ea81fd16b0300ae7b76aac5/device-2016-07-28-164949.png)

Closes #415

See merge request !256
2016-08-01 11:10:08 +00:00
Torsten Grote
bca12bb0e5 Show Personal Blog When Clicking On Post in Combined Blog Feed
Closes #415
2016-08-01 11:26:53 +01:00
akwizgran
32578e2cab Merge branch '535-formatexception-when-loading-contact-list-after-receiving-invitation' into 'master'
Use Client Layer Events in ContactListFragment

This prevents trying to access the same group metadata in different groups.
Also, the conversation does not need to be reloaded once introduction messages arrive.

Closes #535

See merge request !254
2016-08-01 10:21:31 +00:00
akwizgran
0ffbc28792 Validator should call getMessageMetadataForValidator(). 2016-08-01 11:16:22 +01:00
akwizgran
38979ef504 Controllers should depend on ResultHandler not its impl. 2016-07-31 15:21:15 +01:00
akwizgran
5c186db4e4 Fixed a test that was broken by validation changes. 2016-07-31 15:18:14 +01:00
akwizgran
49437522f6 Fixed a test that was broken by my code cleanup. 2016-07-31 14:15:23 +01:00
akwizgran
6511c13752 Merge branch '544-persistentdata-classes-aren-t-thread-safe' into 'master'
Remove PersistentData classes as they aren't thread-safe

This MR depends on !244 and does not claim to resolve *all* issues with the ForumActivity.

Closes #544

See merge request !253
2016-07-30 15:52:19 +00:00
akwizgran
1bfa1016b4 Code cleanup, added FIXMEs for bigger issues. 2016-07-30 16:43:15 +01:00
akwizgran
f3d0ffa09f Merge branch '310-client-layer-events-for-forums' into 'master'
Introduce client layer events for forums

The forum UI depended on sync layer events such as `MessageStateChangedEvent`.
Now, the forum client broadcasts its own high-level event (`ForumPostReceivedEvent`)
with the information the UI needs (`ForumPostHeader`).

Closes #310

See merge request !244
2016-07-30 14:34:17 +00:00
Torsten Grote
d8272d875b Remove ForumPersistentData 2016-07-29 15:36:15 -03:00
Torsten Grote
e782e699fe Remove BlogPersistentData 2016-07-29 15:29:04 -03:00
Torsten Grote
bdb876552d Allow Validator to access metadata for pending messages
Database queries for metadata only returned it for messages that were delivered already.
However, there are cases (e.g. a pending message needs to be delivered) where
the validator needs to retrieve the metadata from the database.

For these cases, a special database query has been introduced.
2016-07-29 15:17:18 -03:00
Torsten Grote
e1bdede4f5 Introduce client layer events for forums
The forum UI depended on sync layer events such as MessageStateChangedEvent.
Now, the forum client broadcasts its own high-level event (`ForumPostReceivedEvent`)
with the information the UI needs (`ForumPostHeader`).

Closes #310
2016-07-29 15:16:52 -03:00
akwizgran
9ff4758683 Merge branch '533-explain-why-forum-can-t-be-shared-with-contact' into 'master'
Explain why forum can't be shared with contact

Closes #533

See merge request !252
2016-07-29 17:33:17 +00:00
akwizgran
fa0639d220 Overlay background colour as an alternative to setAlpha(). 2016-07-29 18:29:14 +01:00
Torsten Grote
cf6aa01905 Remove forum invitations from list instead of reloading
Closes #543
2016-07-28 15:30:48 -03:00
Torsten Grote
3d22d43868 Use Client Layer Events in ContactListFragment
This prevents trying to access the same group metadata in different groups.
Also, the conversation does not need to be reloaded once introduction messages arrive.

Closes #535
2016-07-28 14:30:18 -03:00
Torsten Grote
bbf12ca0c4 Explain why forum can't be shared with contact
Closes #533
2016-07-28 11:21:34 -03:00
Ernir Erlingsson
04d2ede2e1 Merge branch '515-starting-intent-from-preferences-does-not-work-with-different-app-id' into 'master'
Allow different package IDs (for preference intents)

Closes #515

See merge request !248
2016-07-27 19:34:41 +00:00
Torsten Grote
c47171c06e Update translations and expiry date 2016-07-27 15:09:25 -03:00
Torsten Grote
7318aa562e Allow different package IDs (for preference intents)
Closes #515
2016-07-25 13:15:57 -03:00
str4d
ddbac36913 Merge branch '410-my-blogs-tab-with-option-to-add-new-blogs' into 'master'
Micro Blogs UI

**Attention:** This MR includes several other commits which are supposed to end up in separate MRs. I suggest that you review **per commit**. Once the first two commits have green light, I can split out the other commits into other MRs. This way I don't have to work myself through a long rebase chain every time I make a change to the bottom MR.

This MR is full of commits that introduce features that we will not be using initially. The last commit implements the Micro Blogs UI on top of the framework the first commits establish and hides/disables all future features for now.

I suggest we merge this as is and clean things up later when we have a clearer idea what features we will be doing eventually.

![device-2016-06-23-135016](/uploads/600fc7c28c0c6c4a60d8273ffb55f30a/device-2016-06-23-135016.png)
![device-2016-06-22-181934](/uploads/e57cfbc162150bcd01d4683d4164406e/device-2016-06-22-181934.png)
![device-2016-06-23-142422](/uploads/5814f1e13b6d2230f2e4c12a8c5cd599/device-2016-06-23-142422.png)
![device-2016-06-23-142506](/uploads/d3ac268082b98bbcd068f3d6fe0c6712/device-2016-06-23-142506.png)
![device-2016-06-22-181913](/uploads/f5dcc1ed9a40ec5fa8f634864903f4cc/device-2016-06-22-181913.png)

Closes #436 

See merge request !214
2016-07-17 03:35:36 +00:00
str4d
c019d8ecf0 Address review comments 2016-07-17 03:33:25 +00:00
Ernir Erlingsson
c12c60d000 Merge branch '356-make-it-clearer-who-will-be-introduced' into 'master'
Make it clearer who will be introduced

Show contact names under avatars before sending introduction to make it clearer who will be introduced.

![device-2016-07-08-121836](/uploads/de13f6e75b9a573e561e9c810bbdb08b/device-2016-07-08-121836.png)

Closes #356

See merge request !242
2016-07-13 18:47:09 +00:00
str4d
8504cfa88b Re-enable UI if blog post fails to publish 2016-07-13 03:16:59 +00:00
str4d
dd1eed5aa7 Cleanup after review 2016-07-13 03:08:37 +00:00
str4d
39d91e026b Update introduction message text colours 2016-07-12 23:45:36 +00:00
str4d
67aa7c3269 Merge branch '461-forum-invitation-can-not-be-accepted-if-forum-already-added' into 'master'
Show all Forum Invitations, not only the first one to prevent cut-offs and forks

This MR solves the problems outlined in #461 by always showing forum invitations, even if the user already subscribed to the forum.

Available Forums have been renamed to Forum Invitations in the UI:

![device-2016-07-07-144141](/uploads/9c8d06decdd19117250e3faca0c38192/device-2016-07-07-144141.png)
![device-2016-07-07-144150](/uploads/9803f567c962a721b24d79359974a29f/device-2016-07-07-144150.png)

Closes #461

See merge request !241
2016-07-12 23:34:22 +00:00
str4d
85f96d8865 Update forum-exists text 2016-07-12 23:32:48 +00:00
str4d
89469f9d35 Merge branch '459-remove-step-string' into 'master'
Improved the contact addition header

Closes #459

See merge request !245
2016-07-12 12:34:40 +00:00
Ernir Erlingsson
55601bc2c3 removed step string 2016-07-12 12:21:50 +02:00
Ernir Erlingsson
9a4bf598ae Merge branch '346-some-cameras-can-t-focus-on-qr-codes' into 'master'
Set focus mode properly when scanning QR codes

This simple fix contributed by @m8r-v9bo74 fixed the issue on my Xperia
Pro that could never focus on the QR code before. Focusing and scanning
still works on my other test devices.

Closes #346

See merge request !243
2016-07-12 08:37:48 +00:00
str4d
ee68e35b36 Merge branch '360-feedback-for-introducer-when-introduction-request-is-sent' into 'master'
Show Snackbar after introduction request has been sent

Closes #360

See merge request !239
2016-07-12 05:33:09 +00:00
Ernir Erlingsson
8f02cf8307 Merge branch '151-explain-what-account-creation-means-on-first-run' into 'master'
Improve Account Setup Screen

* Explain what "account creation" means on first run (#151)
* Use hints instead of dedicated text views
* Move password strength meter up into the user's view
* Always move user's view to current input field
* Improve "Forgot Password" dialog

![Screenshot_20160627-142116](/uploads/7fadc812f62c5f22f6ba73735d139064/Screenshot_20160627-142116.png)
![Screenshot_20160627-142127](/uploads/da27fb864bbe34168665a51b60747181/Screenshot_20160627-142127.png)
![Screenshot_20160627-142135](/uploads/19ddb247a043f56f9429a98e9655cd9f/Screenshot_20160627-142135.png)
![Screenshot_20160627-142153](/uploads/5d859e029a9584598a5cd6a391e697d3/Screenshot_20160627-142153.png)
![Screenshot_20160627-142204](/uploads/f59a79fc30528a17aece31f2c45d4638/Screenshot_20160627-142204.png)
![Screenshot_20160627-142241](/uploads/a493df940ae69b7ae1eecba598b4d378/Screenshot_20160627-142241.png)

Please note that the dialog button colors will be changed in !214 to the official design. Here they have the old colors.

Closes #151, #207

See merge request !229
2016-07-11 08:36:25 +00:00
Torsten Grote
193cdc42d9 Set focus mode properly when scanning QR codes
This simple fix contributed by @m8r-v9bo74 fixed the issue on my Xperia
Pro that could never focus on the QR code before. Focusing and scanning
still works on my other test devices.

Closes #346
2016-07-08 13:12:32 -03:00
Torsten Grote
91492c3068 Improve Account Setup Screen
* Explain what "account creation" means on first run
* Use hints instead of dedicated text views
* Move password strength meter up into the user's view
* Always move user's view to current input field
* Improve "Forgot Password" dialog

Closes #151
2016-07-08 12:55:04 -03:00
Torsten Grote
412bf162ce Show contact names under avatars before sending introduction
to make it clearer who will be introduced.
2016-07-08 12:39:26 -03:00
Ernir Erlingsson
3a3d717884 Merge branch '327-empty-state-design' into 'master'
Simple Empty State Messages

This MR implements very basic and simple empty state messages for the
current features.

This is how it looks (note that the contact add FAB will be removed soon):

![Screenshot_20160629-125146](/uploads/4c6a8be041cc48b0aba165dd507453d5/Screenshot_20160629-125146.png)
![device-2016-06-29-130834](/uploads/821677074d7d3e6435574a0711f3a0a7/device-2016-06-29-130834.png)
![Screenshot_20160629-125138](/uploads/a39a465db51f8e009ab0b6ee34d4a7e7/Screenshot_20160629-125138.png)
![Screenshot_20160629-125853](/uploads/1dab114bbe7ba379b20a5d298c8fa25c/Screenshot_20160629-125853.png)

Closes #327

See merge request !236
2016-07-08 08:50:10 +00:00
Torsten Grote
6301b2a9a2 Change Available Forums into Forum Invitations 2016-07-07 14:48:28 -03:00
Torsten Grote
c577efacbe Show all Forum Invitations, not only the first 2016-07-07 14:48:28 -03:00
Torsten Grote
fa5304c145 Add tests to ensure Forum Posts are synchronized both ways
even after re-sharing an existing forum.
2016-07-07 14:48:06 -03:00
Torsten Grote
f0a784b4af Merge branch '449-contact-fab' into 'master'
Removed the Fab and moved the icon into the toolbar

Closes #449

See merge request !240
2016-07-07 14:09:22 +00:00
Ernir Erlingsson
b2b97f31f3 Removed the Fab and moved the icon into the toolbar 2016-07-07 15:40:44 +02:00
Ernir Erlingsson
e9789435cc Merge branch '452-hide-identities-initially' into 'master'
Hide Identity Selector When Adding Contact

Closes #452

See merge request !233
2016-07-06 20:29:18 +00:00
Ernir Erlingsson
ba86690ce0 Merge branch '455-update-timestamps-of-messages-every-minute' into 'master'
Update Timestamps Every Minute

Yes by using `notifyDataSetChanged()` I am invalidating everything, but view binding is very fast and the RecyclerView is smart about it. I could parse the dataset and use `notifyItemChanged()` only on the elements that matter, but this is not generic and a lot more code which is not necessarily faster. I suggest we go with my simple and generic approach for now and optimize when we actually notice a performance problem.

Closes #455

See merge request !238
2016-07-06 20:25:35 +00:00
Torsten Grote
db2d965006 Show Snackbar after introduction request has been sent
Closes #360
2016-07-06 13:58:20 -03:00
Torsten Grote
b99650b070 Hide Identity Selector When Adding Contact
Closes #452
2016-07-06 13:09:41 -03:00
Torsten Grote
bc0b226fff Update Timestamps Every Minute
Closes #455
2016-07-06 11:33:25 -03:00
Ernir Erlingsson
3ae3fdab63 Merge branch '450-blog-client-unit-tests' into 'master'
Blog Client Unit and Integration Tests

Closes #450

See merge request !230
2016-07-06 09:55:16 +00:00
Torsten Grote
d8b5710a42 Blog Feed: Show 'Scroll To' link only if scrolling possible 2016-07-04 16:28:57 -03:00
Torsten Grote
fd7278b488 Combined Blog Feed
This commit addes a combined blog feed that shows all posts of all
subscribed blogs in the order the blog posts have been received.

For now, this commit also hides other blog functionality like adding
additional blogs and browsing individual blogs.

Closes #417
2016-07-04 14:37:23 -03:00
Torsten Grote
e0d2d09bdd UI for Deleting a Blog
Closes #418
2016-07-04 14:37:22 -03:00
Torsten Grote
761525ad85 Extend BlogActivity to also show individual posts
This allows for swiping left/right to read other posts by using
a ViewPager.

This hasn't been done as a separate activity, but with
fragments, so both can share the `BlogPersistentData` without
needing to reload it.

Closes #428
2016-07-04 14:35:09 -03:00
Torsten Grote
f7d5c1f63c Add an activity to write blog posts
Closes #411
2016-07-04 14:35:09 -03:00
Torsten Grote
365fbb45ad Add a BlogActivity that shows a list of blog posts
This commit lays the groundwork for #415
2016-07-04 14:35:08 -03:00
Torsten Grote
d05237d2c1 My Blogs tab: Show all blogs the user created
This does not yet support multiple identities. It just shows blogs
created by the first identity, but can easily be adapted for
multi-identity support.

Closes #410
2016-07-04 14:35:08 -03:00
Torsten Grote
4c4f4ad2d5 Add a My Blogs tab with option to add new blogs
Clicking the plus in the toolbar open the `CreateBlogActivity` which
allows the user to create a new blog. Only the first identity is
considered, but support for more identities can be easily added later.

The actual list of blogs in the My Blogs tab will be done in the next
commit.
2016-07-04 14:35:07 -03:00
Torsten Grote
37a5d884b8 Simple Empty State Messages
This implements very basic and simple empty state messages for the
current features.

Closes #327
2016-07-04 14:15:52 -03:00
str4d
759b1c7448 Merge branch '333-font-color' into 'master'
Font Colors and Separate Themes

Once applied this commit will change the font colors according to the
design and move all themes into a dedicated file.

Closes #333

See merge request !228
2016-07-03 00:07:03 +00:00
Ernir Erlingsson
57ce7e3f63 Merge branch '397-remove-unused-java-layout-helpers' into 'master'
Remove unused Java layout helpers

Closes #397

See merge request !232
2016-07-01 13:13:16 +00:00
Torsten Grote
f84bba5d79 Remove unused Java layout helpers
Closes #397
2016-06-30 12:03:12 -03:00
Torsten Grote
bb6e249980 Merge branch '230-make-timestamps-prettier-and-more-useful' into 'master'
Make timestamps prettier and more useful

Here's some example timestamps I have been testing with. That's how it will look like when this MR is merged:

![device-2016-06-28-184748](/uploads/c082ac0f54b50c27f9a1c5ef8865998b/device-2016-06-28-184748.png)

Closes #230

See merge request !235
2016-06-30 14:16:07 +00:00
Torsten Grote
d8df9c9955 Make timestamps prettier and more useful 2016-06-30 11:14:12 -03:00
Ernir Erlingsson
ebe80c773d Merge branch '326-trim-leading-and-trailing-whitespace-when-displaying-messages' into 'master'
Trim leading and trailing whitespace when displaying messages

Closes #326

See merge request !234
2016-06-29 21:24:37 +00:00
Torsten Grote
123f59cd96 Trim leading and trailing whitespace when displaying messages
Closes #326
2016-06-28 17:01:41 -03:00
Torsten Grote
f7965edc71 Add Blog Manager Integration Test 2016-06-28 14:43:55 -03:00
Torsten Grote
66a1f85859 Font Colors and Separate Themes
Once applied this commit will change the font colors according to the
design and move all themes into a dedicated file.

Closes #333
2016-06-28 12:16:57 -03:00
Torsten Grote
7a9290e3a6 Blog Client Unit Tests
Closes #450
2016-06-27 18:04:19 -03:00
Torsten Grote
f270624f39 Merge branch '365-contact-exchange-protocol' into 'master'
Deduplicate contact exchange protocol

`ContactExchangeTask` was originally extracted from the Bluetooth invitation task; this completes the refactoring.

Closes #365.

See merge request !199
2016-06-27 16:06:38 +00:00
Torsten Grote
94927c2f60 Bumped expiry date to 1 August 2016. 2016-06-27 11:39:43 -03:00
Torsten Grote
ecbf5621f2 Merge branch '448-make-use-of-existing-translations' into 'master'
Make use of Existing Translations

Adds first translations to source code and sets up transifex config
file. Pull more translations later with:

```bash
 cd briar-android
 tx pull --mode=developer
```
Closes #448

![device-2016-06-23-145337](/uploads/14c557adf185dce516ca94295b4db965/device-2016-06-23-145337.png)

See merge request !226
2016-06-27 14:35:04 +00:00
Torsten Grote
4d3a333915 Make use of Existing Translations
Adds first translations to source code and sets up transifex config
file. Pull more translations later with:

 cd briar-android
 tx pull --mode=developer

Closes #448
2016-06-27 11:31:42 -03:00
Ernir Erlingsson
9ac1a13711 Merge branch '442-author-s-identicon-nickname-and-status-are-separated-in-forum-post-layout' into 'master'
Separate Date and Author Name in Forum Posts

This also fixes the issue there the trust indicator was shown at the top on some devices.

![device-2016-06-23-152315](/uploads/62786502942bbccadf809fedd72cedd1/device-2016-06-23-152315.png)

Closes #442

See merge request !227
2016-06-27 10:09:54 +00:00
Ernir Erlingsson
946d7cb3e7 Merge branch '446-addidentityhook-sometimes-not-run-for-first-identity' into 'master'
Fix IdentityManager hooks

This commit makes sure that there is only one identity manager that is
initialized early enough so hooks can be added properly.

Closes #446

See merge request !225
2016-06-27 10:00:31 +00:00
str4d
b71a303bf8 Deduplicate contact exchange protocol
ContactExchangeTask was originally extracted from the Bluetooth invitation
task; this completes the refactoring.
2016-06-25 13:41:58 +12:00
Torsten Grote
a9996b4fb1 Separate Date and Author Name in Forum Posts
Closes #442
2016-06-23 15:24:21 -03:00
Torsten Grote
9c2250277b Fix IdentityManager hooks
This commit makes sure that there is only one identity manager that is
initialized early enough so hooks can be added properly.

Closes #446
2016-06-23 14:04:25 -03:00
Torsten Grote
4176f592df Merge branch '436-automatic-personal-blogs-backend' into 'master'
Backend for Automatic Micro Blogs

This MR introduces automatic personal blogs to the Blog client. When a contact is added, her personal blog will also be added automatically. Also, when a new identity is added, a personal blog for that identity is created.

The first commit changes the blog paradigm to short-form blogs and introduces other things that will be useful later in the UI (!214) such as a BlogPostAdded event and the possibility to delete/remove blogs (not the personal ones).

This MR is based on !224 to prevent crashes that are fixed by it. So please review and merge !224 first.

See merge request !223
2016-06-23 15:05:46 +00:00
Torsten Grote
91aa81041e Store and expose time a blog post was received
This is useful when sorting blog posts in the combined feed. It ensures
you do not miss older posts that were just synced to you.
2016-06-23 11:58:25 -03:00
Torsten Grote
a8f51fcb8a Backend for Automatic Personal Blogs
When a contact is added, her personal blog will also be added automatically.
Also, when a new identity is added, a personal blog for that identity is created.

Part of #436
2016-06-23 11:58:25 -03:00
Torsten Grote
30fe9f6e2a Change Blog Paradigm to Short-Form
Removes teaser and makes body mandatory.

It also adds support for deleting blogs and
introduces a getAuthorStatus() method to the IdentityManager
that takes a running transaction.
2016-06-23 10:47:44 -03:00
str4d
5a9615265e Merge branch '441-group-descriptor-can-be-too-long-causes-crash' into 'master'
Remove MAX_GROUP_DESCRIPTOR_LENGTH

Closes #441

See merge request !224
2016-06-23 09:27:48 +00:00
Torsten Grote
59320f02b7 Remove MAX_GROUP_DESCRIPTOR_LENGTH
Closes #441
2016-06-22 11:47:38 -03:00
Torsten Grote
3e4be038f2 Merge branch '426-forum-improvements' into 'master'
Forum improvements

Forum Activity improvements:

* Orientation changes now restore the activity properly, e.g. input text and state is retained
* Snack bar is now, when appropriate, clickable
* The bottom divider is no longer visible for the bottom entry
* Code refactoring for improved simplicity and less redundancy
* Timestamp check to ensure that new posts are not older than the latest post

Closes #426 
Closes #423 
Closes #424 

See merge request !218
2016-06-22 14:45:44 +00:00
Ernir Erlingsson
a2a5b746c5 Forum improvements 2016-06-22 13:50:12 +02:00
Torsten Grote
08cc5500ef Merge branch 'nav-drawer-instance-restore-fix' into 'master'
Fixing fragment instance restore

Alas, it turns out that my neat way of integrating the Fragments into the dependency graph was not without problems. Me and @grote discovered that this presented us with a lot of problems on orientation changes as the Activity is re-created, and therefore parts of the dependency graph. The simplest solution was simply to abandon the idea, i.e. with this branch Fragments are no longer part of the dependency graph. 

See merge request !215
2016-06-21 13:59:56 +00:00
Torsten Grote
ee0c4c07a7 Merge branch '354-private-chat-input-field' into 'master'
synched conversation and forum input

Closes #354

See merge request !219
2016-06-21 12:10:35 +00:00
Ernir Erlingsson
ac8c1c0eee synched conversation and forum input 2016-06-21 13:47:33 +02:00
Ernir Erlingsson
8a29207bd0 Fixing fragment instance restore 2016-06-20 21:56:50 +02:00
Torsten Grote
192ee41600 Merge branch '403-blog-sharing-client' into 'master'
Implement Blog Sharing Client

Closes #403

See merge request !220
2016-06-20 17:16:45 +00:00
str4d
13e3eec6b3 Implement BlogSharingManager 2016-06-18 19:02:52 +12:00
str4d
9ae64124d3 Factor out generic sharing code from ForumSharingManger 2016-06-18 19:02:48 +12:00
Ernir Erlingsson
5df2776dc2 Merge branch '78-fix-test' into 'master'
Fix test broken by #78



See merge request !221
2016-06-17 08:31:35 +00:00
str4d
19fdf3ffd0 Fix test broken by #78 2016-06-17 04:48:13 +00:00
Ernir Erlingsson
e7dce02709 Merge branch '402-blog-client-implementation' into 'master'
Blog Client with Factories and Validator

This implements a simple initial blog client that covers the basic blog actions, but no deletion/removal of blogs, yet.

Classes for Blogs and Blog Post Headers have been introduced along with the associated factories.

A `BlogPostValidator` has been added that validates incoming blog posts.

Closes #402
Closes #404

See merge request !212
2016-06-15 20:26:35 +00:00
Ernir Erlingsson
f823b8ba4c Merge branch '78-trust-indicators' into '402-blog-client-implementation'
New Trust Level Indicator Replaces The Stars

This implements a generic `TrustIndicatorView` with a `setTrustLevel()`
method which updates the drawable according to the `Author.State`.

It also removes unused files from the repo.

This is based on !212 and should only be merged after it.

Closes #78

See merge request !217
2016-06-15 20:17:59 +00:00
Torsten Grote
dc048187f5 New Trust Level Indicator Replaces The Stars
This implements a generic `TrustIndicatorView` with a `setTrustLevel()`
method which updates the drawable according to the `Author.State`.

Closes #78
2016-06-15 12:41:43 -03:00
Torsten Grote
da68ef78f1 Blog Client with Factory and Validator
This implements a simple initial blog client that covers the basic blog
actions, but no deletion/removal of blogs, yet.

Classes for Blogs and Blog Post Headers have been introduced along with
the associated factories.

A `BlogPostValidator` has been added that validates incoming blog posts.

Closes #402
Closes #404
2016-06-15 11:16:28 -03:00
Torsten Grote
bbed673150 Merge branch '433-forum-persistance' into 'master'
fix persistance

Closes #433

See merge request !216
2016-06-15 14:11:42 +00:00
Ernir Erlingsson
69b0bec033 fix persistance 2016-06-14 23:53:05 +02:00
str4d
a11e30c2b3 Merge branch '390-change-password' into 'master'
Add a UI for changing the password

Closes #390

See merge request !213
2016-06-11 22:51:24 +00:00
str4d
c5708ee3ce Add a UI for changing the password 2016-06-11 22:49:45 +00:00
Torsten Grote
e96838e731 Merge branch '430-npe-in-forumsharingstatusactivity' into 'master'
Fix NPE in ForumSharingStatusActivity by only accessing group in contact list

Only access group in contact list where it is guaranteed to exist.

Closes #430

See merge request !211
2016-06-08 13:49:09 +00:00
Torsten Grote
6697b06530 Fix NPE in ForumSharingStatusActivity
by checking if a group ID exists.

Closes #430
2016-06-08 10:47:47 -03:00
Torsten Grote
1bb17a3979 Merge branch '309-client-layer-events-for-messaging' into 'master'
Client layer events for messaging

Adds and uses a new `PrivateMessageReceivedEvent` and eliminate the need for an event for adding local messages. Both done in separate commits.

This addresses part of #309

See merge request !208
2016-06-08 13:37:34 +00:00
Torsten Grote
9d826631e9 Add private message to list right away, so eliminate need for an event 2016-06-08 10:31:26 -03:00
Torsten Grote
b26acdf228 Introduce a PrivateMessageReceivedEvent and use it in the Android app 2016-06-08 10:31:26 -03:00
Torsten Grote
69fba1aa17 Merge branch '388-forum-sharing-race-condition' into 'master'
Address Race-Condition in Forum Sharing

When Two Contacts Share Same Forum with each other at the same time a race condition causes them both to delete each other's invitation. This is solved by an Alice/Bob test as suggested in #388.

See merge request !205
2016-06-08 13:29:12 +00:00
Torsten Grote
1a1dd80810 Merge branch '408-forum-list-unread-posts-badges' into 'master'
Adds badges to the forum list that indicate unread posts

This MR adds  badges to the forum list that indicate unread posts. It does so by extending the compound view `TextAvatarView` and provides convenient setters that take care of the required UI changes. The new badge can also be used to indicate a problem with the forum.

![device-2016-06-02-134048](/uploads/133f365f5654be304f590a4f5b667d06/device-2016-06-02-134048.png)![device-2016-06-02-134453](/uploads/423a88f6306a558828181f0c539aa1cb/device-2016-06-02-134453.png)

I decided not to check if the forum is shared from someone or with someone and indicate a problem in this case, because this requires iterating two times over all contacts and their group metadata for each forum. If someone considers it important to check for this information, we can create a ticket for doing this within the forum and not in the forum list.

See merge request !207
2016-06-07 14:36:42 +00:00
Ernir Erlingsson
02f432c04e Merge branch '409-blog-fragment-in-navigation-drawer-with-tabs' into 'master'
Blog Fragment in Navigation Drawer with Tabs

This replaces the custom layouts in the navigation drawer with a
`NavigationView` and adds a menu entry for Blogs.

A Main Blogs fragment is added that holds a `TabLayout` and a
`ViewPager`. Five tabs are already added, but they just have a single
placeholder fragment that is to be replaced by the actual fragments.

Please note that the header in the navigation drawer is just provisional. I am sure @Megalox can design a better one.

![device-2016-06-02-191101](/uploads/ddffee792f67fad01158d7adc7ca904d/device-2016-06-02-191101.png)
![device-2016-06-02-191048](/uploads/a5e6def118646a02c6bcbf17e0398484/device-2016-06-02-191048.png)

Closes #409

See merge request !210
2016-06-06 09:48:08 +00:00
Torsten Grote
3f838b0472 Blog Fragment in Navigation Drawer with Tabs
This replaces the custom layouts in the navigation drawer with a
`NavigationView` and adds a menu entry for Blogs.

A Main Blogs fragment is added that holds a `TabLayout` and a
`ViewPager`. Five tabs are already added, but they just have a single
placeholder fragment that is to be replaced by the actual fragments.

Closes #409
2016-06-03 13:51:31 -03:00
Torsten Grote
5d06f42000 This commit adds badges to the forum list that indicate unread posts.
It does so by extending the compound view `TextAvatarView` and provides
convienient setters that take care of the required UI changes.
The new badge can also be used to indicate a problem with the forum.

Closes #408
2016-06-03 12:44:03 -03:00
Torsten Grote
4acb59b22a Address Race-Condition when Two Contacts Share Same Forum
with each other at the same time.

Closes #388
2016-06-02 14:02:32 -03:00
str4d
bba7083660 Merge branch '422-forum-subtree-duplicates' into 'master'
fixed sub-tree duplicate bug

Closes #422

See merge request !206
2016-06-02 00:27:08 +00:00
Ernir Erlingsson
e3fc015ec3 fixed sub-tree duplicate bug and also wrong reply count 2016-06-01 23:32:19 +02:00
Ernir Erlingsson
441fce8500 fixed forum crash bug 2016-05-31 23:12:33 +02:00
Ernir Erlingsson
a58fb72ab1 Merge branch '122-threaded-discussion-ui' into 'master'
122 threaded discussions

This branch contains the complete code for the nested forums (UI & back-end).

* This branch has an optional randomized set of dummy test data, uncomment one line in ForumActivity.java and then open up any forum.


See merge request !201
2016-05-31 16:06:22 +00:00
Ernir Erlingsson
86039b81c0 Forum, nested discussions front end UI/UX, rev 2 2016-05-30 23:57:01 +02:00
str4d
661140f623 Merge branch '382-message-dependencies-2' into 'master'
Message Dependencies

This MR adds a new table to the database to hold message dependencies.
It introduces two more message states  (pending and delivered) and completely changes the validation and message delivery logic.

Since this is a **very invasive** change, please **review carefully**. It is better to catch issues now than having to debug things later. If you notice an area without tests, please let me know. I tried to have tests for all changes.

Unfortunately, this MR turned out rather large, but most of it are actually tests and you can review one commit after the other without needing to see the full changelog.

In order to reflect the new states, things that were previously true for `VALID` messages have been changed to now be true for `DELIVERED` messages. This might have changed the semantics in some places and can have unindented side-effects.

The `DatabaseComponent` was extended with methods for:
* adding dependencies
* getting dependencies and states of a message
* getting messages that depend on a message (dependents) and their states
* getting messages to be delivered (by startup hook)
* getting pending messages to be possibly delivered (by startup hook)

The `valid` column in the database was renamed to `state` along with the associated methods to better reflect its new extended meaning.

Since pending messages should not be available to clients, many database queries have been modified to only return results for delivered messages. I might have missed some. Please check carefully. Also you might want to think about how you could create arbitrarily large transactions by sending malicious messages.

Please note that the database version was bumped in this MR.

Closes #382

See merge request !198
2016-05-28 00:20:39 +00:00
str4d
19b6afa69f Move new methods in Database interface to match existing alphabetic sorting 2016-05-28 00:17:17 +00:00
str4d
7ac7fae30e Update comments 2016-05-28 00:16:24 +00:00
Torsten Grote
d3b83a50a6 Let the ForumValidator return parent posts as dependency
and add integration tests that make sure that dependencies are handled
properly.
2016-05-26 13:49:59 -03:00
Torsten Grote
5561532c5d Implement new message validation logic
that handles message dependencies reported from clients.

The MessageValidatedEvent has been renamed into a MessageDeliveredEvent
since there were no real use cases for the former any more.
2016-05-26 13:49:59 -03:00
Torsten Grote
b03d0a206b Add Message Dependencies to Database
This adds a new table to the database to hold message dependencies.
It introduces two more message states: pending and delivered
The valid column in the database was renamed to state to better reflect
its new extended meaning.

The DatabaseComponent was extended with three methods for:
* adding dependencies
* getting dependencies of a message
* getting messages that depend on a message (dependents)
* getting messages to be delivered (by startup hook)
* getting pending messages to be possibly delivered (by startup hook)

In order to reflect the new states, things that were previously true for
VALID messages have been changed to now be true for DELIVERED messages.

Since pending messages should not be available to clients, many database
queries have been modified to only return results for delivered
messages.

All added methods and changes should come with updated unit tests.

Please note that the database version was bumped in this commit.
2016-05-26 13:49:03 -03:00
Ernir Erlingsson
512940e82b Merge branch '393-message-tree' into 'master'
Sort threaded messages generic

Constructed a generic that we can use to sort threaded messages.

Closes #393

See merge request !203
2016-05-26 11:13:06 +00:00
Ernir Erlingsson
d2ba7c9b7f generic sorted message tree 2016-05-25 22:12:20 +02:00
Torsten Grote
a652f1a5d7 Merge branch '122-forum-manager' into 'master'
added parent id to metadata and a unit test

The parent id, a necessary attribute for nested forums, was not being added to the metadata in the forum manager. Additionally, a simple forum manager unit was created.

See merge request !202
2016-05-24 18:13:15 +00:00
Ernir Erlingsson
21b237a629 ForumManager: Added parent id to metadata and a unit test 2016-05-24 15:12:37 -03:00
Torsten Grote
2a7990f5c4 Merge branch '334-nav-menu-button-crash' into 'master'
android.support.v7.widget.AppCompatButton -> Button to use AppCompat inflater

Fixes the following crash that occurs after upgrading to support 23.3.0:
https://code.google.com/p/android/issues/detail?id=174871

Closes #334.

See merge request !200
2016-05-20 14:12:11 +00:00
str4d
92d3e02c82 android.support.v7.widget.AppCompatButton -> Button to use AppCompat inflater
Fixes the following crash that occurs after upgrading to support 23.3.0:
https://code.google.com/p/android/issues/detail?id=174871
2016-05-20 03:30:10 +00:00
str4d
0d16dd0358 Merge branch '382-message-dependencies' into 'master'
Introduce a MessageContext class to be used by all validators

This change will allow to pass message dependencies from the client validators to the `ValidationManager`.

Please see my thoughts in #382 for more details.

See merge request !197
2016-05-20 02:49:04 +00:00
Torsten Grote
3f2b85ac0d Introduce a MessageContext class for more flexibility
This change will allow to pass message dependencies from the client
validators to the ValidationManager.
2016-05-20 02:46:37 +00:00
Torsten Grote
d0852d2265 Sorry for breaking master. Don't know how that happened :( 2016-05-19 21:33:38 -03:00
Torsten Grote
9149b82cc4 Merge branch '398-forum-sharing-status' into 'master'
Forum Sharing Status

The new activity shows who you are sharing a forum with and who shares a
forum with you. It is accessible from the overflow menu when in a forum.

![device-2016-05-13-153259](/uploads/2e1c651bc72094018dd0f97a37453d1b/device-2016-05-13-153259.png)

Closes #398

See merge request !191
2016-05-19 14:44:05 +00:00
Torsten Grote
b13bf66165 Add a new activity that shows sharing status of forum
The new activity shows who you are sharing a forum with and who shares a
forum with you. It is accessible from the overflow menu when in a forum.

Closes #398
2016-05-19 11:42:05 -03:00
Torsten Grote
8c2fd905a1 Merge branch '392-use-new-metadata-queries-for-forum-sharing' into 'master'
Handle invitations to the same forum by multiple contacts

This also uses the new metadata queries in the forum sharing client.

Please note that this is based on !184.

See merge request !188
2016-05-19 14:35:58 +00:00
Torsten Grote
38a4f73cdc Handle invitations to the same forum by multiple contacts
Closes #391
2016-05-19 11:34:28 -03:00
Torsten Grote
1a175beac9 Use new metadata queries in Forum Sharing Client
Closes #392
2016-05-19 11:34:28 -03:00
Torsten Grote
7e9ff40837 Merge branch '378-replace-bdf-with-classes-for-forum-sharing-client' into 'master'
Replace BDF data structures with classes in forum sharing client

This introduces two new classes for protocol session states: One for the sharer and one for the invitee.
The respective classes for protocol state machines and actions have been moved into these classes as inner classes.
The two new classes replace the `BdfDictionary` that was used before to represent the local state information of a forum sharing session.

A similar technique is used for local actions and protocol messages.

Local actions are just represented by one Enum and protocol messages have their own classes now that also handle serialization into BdfDictionaries and BdfLists.

Closes #378 

See merge request !184
2016-05-19 14:32:41 +00:00
Torsten Grote
bd01c3732e Also use dedicated classes to represent messages instead of BdfDictionary 2016-05-19 11:32:09 -03:00
Torsten Grote
9532a60f43 Use dedicated Enum for protocol Actions 2016-05-19 11:30:23 -03:00
Torsten Grote
d2722eed92 Turn local session state into its own class instead of BdfDictionary 2016-05-19 11:30:19 -03:00
Torsten Grote
886c8feb34 Merge branch '395-convert-createidentityactivity-to-xml' into 'master'
Turn the UI of `CreateIdentityActivity` into XML

Closes #395

See merge request !194
2016-05-19 14:24:35 +00:00
str4d
c7f73f9247 Merge branch '399-contact-list-unread-messages' into 'master'
Contact List Unread Messages

Adds unread count as bubble to avatar image in contact list.

Please note that this is based on !190.

![device-2016-05-18-124534](/uploads/647dbdcff73a69eb6cf1814f85cbffe6/device-2016-05-18-124534.png)![device-2016-05-18-124339](/uploads/fa56a44f229234bb86fdebf8f9060927/device-2016-05-18-124339.png)

Closes #399

See merge request !192
2016-05-19 03:45:13 +00:00
str4d
35156d698f Merge branch '337-avatars' into 'master'
Increase Avatar border and change shadow color

![device-2016-05-16-123230](/uploads/8000fb506ee91e61149f838b3f661b46/device-2016-05-16-123230.png)
![device-2016-05-16-123251](/uploads/723bf452704f18b519c3fbdc469595a7/device-2016-05-16-123251.png)

See merge request !190
2016-05-19 03:38:09 +00:00
str4d
1366972449 Merge branch '396-convert-expiredactivity-to-xml' into 'master'
Convert ExpiredActivity to XML

Closes #396

See merge request !196
2016-05-19 03:31:06 +00:00
Torsten Grote
3d25c41e7a Add unread count as bubble to avatar image in contact list
Closes #399
2016-05-18 12:46:38 -03:00
Torsten Grote
ba928875df Merge branch '375-extract-forumfactory-from-forummanager' into 'master'
Extract ForumFactory from ForumManager

The code for creating forums in ForumManager was used by
ForumSharingManager and also needed by InviteeEngine.
This extracts it into its own class.

Closes #375

See merge request !195
2016-05-18 14:59:29 +00:00
Torsten Grote
cac0f30816 Convert ExpiredActivity to XML
Closes #396
2016-05-16 17:08:48 -03:00
Torsten Grote
aad9f5142b Extract ForumFactory from ForumManager
The code for creating forums in ForumManager was used by
ForumSharingManager and also needed by InviteeEngine.
This extracts it into its own class.

Closes #375
2016-05-16 16:56:44 -03:00
Torsten Grote
9d686e16e0 Increase Avatar border and change shadow color 2016-05-16 12:36:59 -03:00
Torsten Grote
2880043c07 Turn the UI of CreateIdentityActivity into XML
Closes #395
2016-05-16 12:19:47 -03:00
Torsten Grote
fb85345392 Merge branch '318-conversation-avatar-animation' into 'master'
Disable Conversation Exit Transition and Animate Bulb

Works-around #318

See merge request !193
2016-05-16 13:22:04 +00:00
Torsten Grote
0b67ec9201 Merge branch '372-clean-up-introduction-session-states-when-removing-contact' into 'master'
Clean up Introduction Session States

...for introducer when both introducees have been deleted.

It can't be deleted when only one introducee is removed, because then all messages in the private conversation with the other introducee will disappear, because their corresponding session state can't be found anymore.

Closes #372

See merge request !189
2016-05-16 13:18:57 +00:00
Ernir Erlingsson
fcf21b7ed7 Merge branch 'gradle-upgrade' into 'master'
Upgrade Gradle to 2.13, add local Maven repo



See merge request !186
2016-05-14 19:22:42 +00:00
Torsten Grote
6e545d0100 disable exit transition until we have a fix for it 2016-05-13 17:25:14 -03:00
str4d
69026054cd Add bulb to scene transition, make transitionName unique for each contact 2016-05-13 16:51:24 -03:00
Torsten Grote
db4b79fcae Clean up Introduction Session States
for introducer when both introducees have been deleted.

Closes #372
2016-05-12 18:21:18 -03:00
akwizgran
7412ca59ac Looks like we don't need Maven Central. 2016-05-12 14:25:00 +01:00
akwizgran
ec083d617e Upgraded Gradle to 2.13, added local Maven repo. 2016-05-12 13:36:34 +01:00
981 changed files with 54232 additions and 14268 deletions

View File

@@ -31,6 +31,8 @@
</value>
</option>
<option name="RIGHT_MARGIN" value="100" />
<option name="JD_ALIGN_PARAM_COMMENTS" value="false" />
<option name="JD_ALIGN_EXCEPTION_COMMENTS" value="false" />
<AndroidXmlCodeStyleSettings>
<option name="USE_CUSTOM_SETTINGS" value="true" />
</AndroidXmlCodeStyleSettings>
@@ -68,6 +70,12 @@
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="Groovy">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="80" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />

View File

@@ -13,23 +13,6 @@
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
-------------------------------------------------------------------------
All files under the directories briar-android/src, briar-api/src,
briar-core/src, briar-desktop/src and briar-test/src are licensed
under the Apache License, version 2.0 (the "License"); you may not
use these files except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied. See the License for the specific language governing
permissions and limitations under the License.
-------------------------------------------------------------------------
GNU GENERAL PUBLIC LICENSE

View File

@@ -1,30 +1,16 @@
apply plugin: 'com.android.library'
apply plugin: 'witness'
apply plugin: 'com.neenbedankt.android-apt'
repositories {
maven { url 'http://repo1.maven.org/maven2' }
}
android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
minSdkVersion 9
targetSdkVersion 22
versionCode 1
versionName "1.0"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
dexOptions {
incremental true
minSdkVersion 14
proguardFiles getDefaultProguardFile('proguard-android.txt'), '../briar-android/proguard-rules.txt'
consumerProguardFiles getDefaultProguardFile('proguard-android.txt'), '../briar-android/proguard-rules.txt'
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
@@ -32,23 +18,12 @@ android {
}
dependencies {
compile fileTree(dir: 'libs', include: ['*.jar'])
compile project(':briar-api')
compile project(':briar-core')
testCompile project(':briar-api')
testCompile project(':briar-core')
testCompile 'junit:junit:4.12'
testCompile 'net.jodah:concurrentunit:0.4.2'
compile 'com.android.support:appcompat-v7:23.2.1'
testCompile 'com.android.support:appcompat-v7:23.2.1'
testApt 'com.google.dagger:dagger-compiler:2.0.2'
provided 'javax.annotation:jsr250-api:1.0'
testCompile project(':briar-tests')
}
dependencyVerification {
verify = [
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f',
]
}

View File

@@ -1,17 +0,0 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /home/ernir/dev/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@@ -0,0 +1,619 @@
package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogCommentHeader;
import org.briarproject.api.blogs.BlogFactory;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPost;
import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.blogs.BlogPostHeader;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.system.Clock;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static junit.framework.Assert.assertFalse;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.blogs.MessageType.COMMENT;
import static org.briarproject.api.blogs.MessageType.POST;
import static org.briarproject.api.blogs.MessageType.WRAPPED_COMMENT;
import static org.briarproject.api.blogs.MessageType.WRAPPED_POST;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BlogManagerTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1;
private SyncSessionFactory sync0, sync1;
private BlogManager blogManager0, blogManager1;
private ContactManager contactManager0, contactManager1;
private ContactId contactId0,contactId1;
private IdentityManager identityManager0, identityManager1;
private LocalAuthor author0, author1;
private Blog blog0, blog1;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
CryptoComponent crypto;
@Inject
BlogFactory blogFactory;
@Inject
BlogPostFactory blogPostFactory;
// objects accessed from background threads need to be volatile
private volatile Waiter validationWaiter;
private volatile Waiter deliveryWaiter;
private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000;
private final String AUTHOR1 = "Author 1";
private final String AUTHOR2 = "Author 2";
private static final Logger LOG =
Logger.getLogger(BlogManagerTest.class.getName());
private BlogManagerTestComponent t0, t1;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws Exception {
BlogManagerTestComponent component =
DaggerBlogManagerTestComponent.builder().build();
component.inject(this);
injectEagerSingletons(component);
assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, AUTHOR1);
t0 = DaggerBlogManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0);
File t1Dir = new File(testDir, AUTHOR2);
t1 = DaggerBlogManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager();
contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager();
blogManager0 = t0.getBlogManager();
blogManager1 = t1.getBlogManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
// initialize waiters fresh for each test
validationWaiter = new Waiter();
deliveryWaiter = new Waiter();
}
@Test
public void testPersonalBlogInitialisation() throws Exception {
startLifecycles();
defaultInit();
Collection<Blog> blogs0 = blogManager0.getBlogs();
assertEquals(2, blogs0.size());
Iterator<Blog> i0 = blogs0.iterator();
assertEquals(author0, i0.next().getAuthor());
assertEquals(author1, i0.next().getAuthor());
Collection<Blog> blogs1 = blogManager1.getBlogs();
assertEquals(2, blogs1.size());
Iterator<Blog> i1 = blogs1.iterator();
assertEquals(author1, i1.next().getAuthor());
assertEquals(author0, i1.next().getAuthor());
assertEquals(blog0, blogManager0.getPersonalBlog(author0));
assertEquals(blog0, blogManager1.getPersonalBlog(author0));
assertEquals(blog1, blogManager0.getPersonalBlog(author1));
assertEquals(blog1, blogManager1.getPersonalBlog(author1));
assertEquals(blog0, blogManager0.getBlog(blog0.getId()));
assertEquals(blog0, blogManager1.getBlog(blog0.getId()));
assertEquals(blog1, blogManager0.getBlog(blog1.getId()));
assertEquals(blog1, blogManager1.getBlog(blog1.getId()));
assertEquals(1, blogManager0.getBlogs(author0).size());
assertEquals(1, blogManager1.getBlogs(author0).size());
assertEquals(1, blogManager0.getBlogs(author1).size());
assertEquals(1, blogManager1.getBlogs(author1).size());
stopLifecycles();
}
@Test
public void testBlogPost() throws Exception {
startLifecycles();
defaultInit();
// check that blog0 has no posts
final String body = TestUtils.getRandomString(42);
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog0.getId());
assertEquals(0, headers0.size());
// add a post to blog0
BlogPost p = blogPostFactory
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
author0, body);
blogManager0.addLocalPost(p);
// check that post is now in blog0
headers0 = blogManager0.getPostHeaders(blog0.getId());
assertEquals(1, headers0.size());
// check that body is there
assertEquals(body, blogManager0.getPostBody(p.getMessage().getId()));
// make sure that blog0 at author1 doesn't have the post yet
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog0.getId());
assertEquals(0, headers1.size());
// sync the post over
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// make sure post arrived
headers1 = blogManager1.getPostHeaders(blog0.getId());
assertEquals(1, headers1.size());
assertEquals(POST, headers1.iterator().next().getType());
// check that body is there
assertEquals(body, blogManager1.getPostBody(p.getMessage().getId()));
stopLifecycles();
}
@Test
public void testBlogPostInWrongBlog() throws Exception {
startLifecycles();
defaultInit();
// add a post to blog1
final String body = TestUtils.getRandomString(42);
BlogPost p = blogPostFactory
.createBlogPost(blog1.getId(), clock.currentTimeMillis(), null,
author0, body);
blogManager0.addLocalPost(p);
// check that post is now in blog1
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog1.getId());
assertEquals(1, headers0.size());
// sync the post over
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// make sure post did not arrive, because of wrong signature
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog1.getId());
assertEquals(0, headers1.size());
stopLifecycles();
}
@Test
public void testCanNotRemoveContactsPersonalBlog() throws Exception {
startLifecycles();
defaultInit();
assertFalse(blogManager0.canBeRemoved(blog1.getId()));
assertFalse(blogManager1.canBeRemoved(blog0.getId()));
// the following two calls should throw a DbException now
thrown.expect(DbException.class);
blogManager0.removeBlog(blog1);
blogManager1.removeBlog(blog0);
// blogs have not been removed
assertEquals(2, blogManager0.getBlogs().size());
assertEquals(2, blogManager1.getBlogs().size());
stopLifecycles();
}
@Test
public void testBlogComment() throws Exception {
startLifecycles();
defaultInit();
// add a post to blog0
final String body = TestUtils.getRandomString(42);
BlogPost p = blogPostFactory
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
author0, body);
blogManager0.addLocalPost(p);
// sync the post over
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// make sure post arrived
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog0.getId());
assertEquals(1, headers1.size());
assertEquals(POST, headers1.iterator().next().getType());
// 1 adds a comment to that blog post
String comment = "This is a comment on a blog post!";
blogManager1
.addLocalComment(author1, blog1.getId(), comment,
headers1.iterator().next());
// sync comment over
sync1To0();
deliveryWaiter.await(TIMEOUT, 2);
// make sure comment and wrapped post arrived
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog1.getId());
assertEquals(1, headers0.size());
assertEquals(COMMENT, headers0.iterator().next().getType());
BlogCommentHeader h = (BlogCommentHeader) headers0.iterator().next();
assertEquals(author0, h.getParent().getAuthor());
// ensure that body can be retrieved from wrapped post
assertEquals(body, blogManager0.getPostBody(h.getParentId()));
// 1 has only their own comment in their blog
headers1 = blogManager1.getPostHeaders(blog1.getId());
assertEquals(1, headers1.size());
stopLifecycles();
}
@Test
public void testBlogCommentOnOwnPost() throws Exception {
startLifecycles();
defaultInit();
// add a post to blog0
final String body = TestUtils.getRandomString(42);
BlogPost p = blogPostFactory
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
author0, body);
blogManager0.addLocalPost(p);
// get header of own post
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog0.getId());
assertEquals(1, headers0.size());
BlogPostHeader header = headers0.iterator().next();
// add a comment on own post
String comment = "This is a comment on my own blog post!";
blogManager0
.addLocalComment(author0, blog0.getId(), comment, header);
// sync the post and comment over
sync0To1();
deliveryWaiter.await(TIMEOUT, 2);
// make sure post arrived
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog0.getId());
assertEquals(2, headers1.size());
for (BlogPostHeader h : headers1) {
if (h.getType() == POST) {
assertEquals(body, blogManager1.getPostBody(h.getId()));
} else {
assertEquals(comment, ((BlogCommentHeader)h).getComment());
}
}
stopLifecycles();
}
@Test
public void testCommentOnComment() throws Exception {
startLifecycles();
defaultInit();
// add a post to blog0
final String body = TestUtils.getRandomString(42);
BlogPost p = blogPostFactory
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
author0, body);
blogManager0.addLocalPost(p);
// sync the post over
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// make sure post arrived
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog0.getId());
assertEquals(1, headers1.size());
assertEquals(POST, headers1.iterator().next().getType());
// 1 reblogs that blog post
blogManager1
.addLocalComment(author1, blog1.getId(), null,
headers1.iterator().next());
// sync comment over
sync1To0();
deliveryWaiter.await(TIMEOUT, 2);
// make sure comment and wrapped post arrived
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog1.getId());
assertEquals(1, headers0.size());
// get header of comment
BlogPostHeader cHeader = headers0.iterator().next();
assertEquals(COMMENT, cHeader.getType());
// comment on the comment
String comment = "This is a comment on a reblogged post.";
blogManager0
.addLocalComment(author0, blog0.getId(), comment, cHeader);
// sync comment over
sync0To1();
deliveryWaiter.await(TIMEOUT, 3);
// check that comment arrived
headers1 =
blogManager1.getPostHeaders(blog0.getId());
assertEquals(2, headers1.size());
// get header of comment
cHeader = null;
for (BlogPostHeader h : headers1) {
if (h.getType() == COMMENT) {
cHeader = h;
}
}
assertTrue(cHeader != null);
// another comment on the comment
String comment2 = "This is a comment on a comment.";
blogManager1.addLocalComment(author1, blog1.getId(), comment2, cHeader);
// sync comment over
sync1To0();
deliveryWaiter.await(TIMEOUT, 4);
// make sure new comment arrived
headers0 =
blogManager0.getPostHeaders(blog1.getId());
assertEquals(2, headers0.size());
boolean satisfied = false;
for (BlogPostHeader h : headers0) {
assertEquals(COMMENT, h.getType());
BlogCommentHeader c = (BlogCommentHeader) h;
if (c.getComment() != null && c.getComment().equals(comment2)) {
assertEquals(author0, c.getParent().getAuthor());
assertEquals(WRAPPED_COMMENT, c.getParent().getType());
assertEquals(comment,
((BlogCommentHeader) c.getParent()).getComment());
assertEquals(WRAPPED_COMMENT,
((BlogCommentHeader) c.getParent()).getParent()
.getType());
assertEquals(WRAPPED_POST,
((BlogCommentHeader) ((BlogCommentHeader) c
.getParent()).getParent()).getParent()
.getType());
satisfied = true;
}
}
assertTrue(satisfied);
stopLifecycles();
}
@Test
public void testCommentOnOwnComment() throws Exception {
startLifecycles();
defaultInit();
// add a post to blog0
final String body = TestUtils.getRandomString(42);
BlogPost p = blogPostFactory
.createBlogPost(blog0.getId(), clock.currentTimeMillis(), null,
author0, body);
blogManager0.addLocalPost(p);
// sync the post over
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// make sure post arrived
Collection<BlogPostHeader> headers1 =
blogManager1.getPostHeaders(blog0.getId());
assertEquals(1, headers1.size());
assertEquals(POST, headers1.iterator().next().getType());
// 1 reblogs that blog post with a comment
String comment = "This is a comment on a post.";
blogManager1
.addLocalComment(author1, blog1.getId(), comment,
headers1.iterator().next());
// get comment from own blog
headers1 = blogManager1.getPostHeaders(blog1.getId());
assertEquals(1, headers1.size());
assertEquals(COMMENT, headers1.iterator().next().getType());
BlogCommentHeader ch = (BlogCommentHeader) headers1.iterator().next();
assertEquals(comment, ch.getComment());
comment = "This is a comment on a post with a comment.";
blogManager1.addLocalComment(author1, blog1.getId(), comment, ch);
// sync both comments over (2 comments + 1 wrapped post)
sync1To0();
deliveryWaiter.await(TIMEOUT, 3);
// make sure both comments arrived
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog1.getId());
assertEquals(2, headers0.size());
stopLifecycles();
}
@After
public void tearDown() throws Exception {
TestUtils.deleteTestDirectory(testDir);
}
private class Listener implements EventListener {
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (!event.isLocal()) {
if (event.getState() == DELIVERED) {
deliveryWaiter.resume();
} else if (event.getState() == INVALID ||
event.getState() == PENDING) {
validationWaiter.resume();
}
}
}
}
}
private void defaultInit() throws DbException {
getDefaultIdentities();
addDefaultContacts();
listenToEvents();
}
private void getDefaultIdentities() throws DbException {
author0 = identityManager0.getLocalAuthor();
author1 = identityManager1.getLocalAuthor();
blog0 = blogFactory.createBlog(author0);
blog1 = blogFactory.createBlog(author1);
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true, true
);
// invitee adds sharer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true, true
);
}
private void listenToEvents() {
Listener listener0 = new Listener();
t0.getEventBus().addListener(listener0);
Listener listener1 = new Listener();
t1.getEventBus().addListener(listener1);
}
private void sync0To1() throws IOException, TimeoutException {
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
}
private void sync1To0() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
}
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
SyncSessionFactory toSync, ContactId toId, String debug)
throws IOException, TimeoutException {
if (debug != null) LOG.info("TEST: Sending message from " + debug);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
// Write whatever needs to be written
sessionFrom.run();
out.close();
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Create an incoming sync session
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
// Read whatever needs to be read
sessionTo.run();
in.close();
}
private void startLifecycles() throws InterruptedException {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager0.startServices(AUTHOR1);
lifecycleManager1.startServices(AUTHOR2);
lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup();
}
private void stopLifecycles() throws InterruptedException {
// Clean up
lifecycleManager0.stopServices();
lifecycleManager1.stopServices();
lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown();
}
private void injectEagerSingletons(BlogManagerTestComponent component) {
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new BlogsModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons());
}
}

View File

@@ -0,0 +1,78 @@
package org.briarproject;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestDatabaseModule.class,
TestPluginsModule.class,
TestSeedProviderModule.class,
ClientsModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
BlogsModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
})
interface BlogManagerTestComponent {
void inject(BlogManagerTest testCase);
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(BlogsModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(PropertiesModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
IdentityManager getIdentityManager();
ContactManager getContactManager();
BlogManager getBlogManager();
SyncSessionFactory getSyncSessionFactory();
}

View File

@@ -0,0 +1,743 @@
package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.blogs.Blog;
import org.briarproject.api.blogs.BlogInvitationRequest;
import org.briarproject.api.blogs.BlogInvitationResponse;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogPostFactory;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.BlogInvitationReceivedEvent;
import org.briarproject.api.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.system.Clock;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.blogs.BlogSharingManager.CLIENT_ID;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class BlogSharingIntegrationTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1,
lifecycleManager2;
private SyncSessionFactory sync0, sync1;
private BlogManager blogManager0, blogManager1;
private MessageTracker messageTracker0, messageTracker1;
private ContactManager contactManager0, contactManager1, contactManager2;
private Contact contact1, contact2, contact01, contact02;
private ContactId contactId1, contactId01;
private IdentityManager identityManager0, identityManager1,
identityManager2;
private LocalAuthor author0, author1, author2;
private Blog blog0, blog1, blog2;
private SharerListener listener0;
private InviteeListener listener1;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
ContactGroupFactory contactGroupFactory;
@Inject
BlogPostFactory blogPostFactory;
@Inject
CryptoComponent cryptoComponent;
// objects accessed from background threads need to be volatile
private volatile BlogSharingManager blogSharingManager0;
private volatile BlogSharingManager blogSharingManager1;
private volatile BlogSharingManager blogSharingManager2;
private volatile Waiter eventWaiter;
private volatile Waiter msgWaiter;
private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000;
private final String SHARER = "Sharer";
private final String INVITEE = "Invitee";
private final String CONTACT2 = "Contact2";
private static final Logger LOG =
Logger.getLogger(BlogSharingIntegrationTest.class.getName());
private BlogSharingIntegrationTestComponent t0, t1, t2;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() {
BlogSharingIntegrationTestComponent component =
DaggerBlogSharingIntegrationTestComponent.builder().build();
component.inject(this);
injectEagerSingletons(component);
assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, SHARER);
t0 = DaggerBlogSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0);
File t1Dir = new File(testDir, INVITEE);
t1 = DaggerBlogSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
File t2Dir = new File(testDir, CONTACT2);
t2 = DaggerBlogSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
injectEagerSingletons(t2);
identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager();
identityManager2 = t2.getIdentityManager();
contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager();
contactManager2 = t2.getContactManager();
blogManager0 = t0.getBlogManager();
blogManager1 = t1.getBlogManager();
messageTracker0 = t0.getMessageTracker();
messageTracker1 = t1.getMessageTracker();
blogSharingManager0 = t0.getBlogSharingManager();
blogSharingManager1 = t1.getBlogSharingManager();
blogSharingManager2 = t2.getBlogSharingManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
// initialize waiters fresh for each test
eventWaiter = new Waiter();
msgWaiter = new Waiter();
}
@Test
public void testPersonalBlogCannotBeSharedWithOwner() throws Exception {
startLifecycles();
defaultInit(true);
assertFalse(blogSharingManager0.canBeShared(blog1.getId(), contact1));
assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact2));
assertFalse(blogSharingManager1.canBeShared(blog0.getId(), contact01));
assertFalse(blogSharingManager2.canBeShared(blog0.getId(), contact02));
// create invitation
blogSharingManager0
.sendInvitation(blog1.getId(), contactId1, "Hi!");
// sync invitation
sync0To1();
// make sure the invitee ignored the request for their own blog
assertFalse(listener1.requestReceived);
stopLifecycles();
}
@Test
public void testSuccessfulSharing() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// invitee has own blog and that of the sharer
assertEquals(2, blogManager1.getBlogs().size());
// get sharing group and assert group message count
GroupId g = contactGroupFactory.createContactGroup(CLIENT_ID, contact1)
.getId();
assertGroupCount(messageTracker0, g, 1, 0);
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
assertGroupCount(messageTracker1, g, 2, 1);
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
assertGroupCount(messageTracker0, g, 2, 1);
// blog was added successfully
assertEquals(0, blogSharingManager0.getInvitations().size());
assertEquals(3, blogManager1.getBlogs().size());
// invitee has one invitation message from sharer
List<InvitationMessage> list =
new ArrayList<>(blogSharingManager1
.getInvitationMessages(contactId01));
assertEquals(2, list.size());
// check other things are alright with the message
for (InvitationMessage m : list) {
if (m instanceof BlogInvitationRequest) {
BlogInvitationRequest invitation =
(BlogInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(blog2.getAuthor().getName(),
invitation.getBlogAuthorName());
assertEquals(contactId1, invitation.getContactId());
assertEquals("Hi!", invitation.getMessage());
} else {
BlogInvitationResponse response =
(BlogInvitationResponse) m;
assertEquals(contactId01, response.getContactId());
assertTrue(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
blogSharingManager0.getInvitationMessages(contactId1)
.size());
// blog can not be shared again
assertFalse(blogSharingManager0.canBeShared(blog2.getId(), contact1));
assertFalse(blogSharingManager1.canBeShared(blog2.getId(), contact01));
// group message count is still correct
assertGroupCount(messageTracker0, g, 2, 1);
assertGroupCount(messageTracker1, g, 2, 1);
stopLifecycles();
}
@Test
public void testDeclinedSharing() throws Exception {
startLifecycles();
// initialize and let invitee deny all requests
defaultInit(false);
// send invitation
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, null);
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was not added
assertEquals(0, blogSharingManager0.getInvitations().size());
assertEquals(2, blogManager1.getBlogs().size());
// blog is no longer available to invitee who declined
assertEquals(0, blogSharingManager1.getInvitations().size());
// invitee has one invitation message from sharer and one response
List<InvitationMessage> list =
new ArrayList<>(blogSharingManager1
.getInvitationMessages(contactId01));
assertEquals(2, list.size());
// check things are alright with the message
for (InvitationMessage m : list) {
if (m instanceof BlogInvitationRequest) {
BlogInvitationRequest invitation =
(BlogInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(blog2.getAuthor().getName(),
invitation.getBlogAuthorName());
assertEquals(contactId1, invitation.getContactId());
assertEquals(null, invitation.getMessage());
} else {
BlogInvitationResponse response =
(BlogInvitationResponse) m;
assertEquals(contactId01, response.getContactId());
assertFalse(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
blogSharingManager0.getInvitationMessages(contactId1)
.size());
// blog can be shared again
assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
stopLifecycles();
}
@Test
public void testInviteeLeavesAfterFinished() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was added successfully
assertEquals(0, blogSharingManager0.getInvitations().size());
assertEquals(3, blogManager1.getBlogs().size());
assertTrue(blogManager1.getBlogs().contains(blog2));
// sharer shares blog with invitee
assertTrue(blogSharingManager0.getSharedWith(blog2.getId())
.contains(contact1));
// invitee gets blog shared by sharer
assertTrue(blogSharingManager1.getSharedBy(blog2.getId())
.contains(contact01));
// invitee un-subscribes from blog
blogManager1.removeBlog(blog2);
// send leave message to sharer
sync1To0();
// blog is gone
assertEquals(0, blogSharingManager0.getInvitations().size());
assertEquals(2, blogManager1.getBlogs().size());
// sharer no longer shares blog with invitee
assertFalse(blogSharingManager0.getSharedWith(blog2.getId())
.contains(contact1));
// invitee no longer gets blog shared by sharer
assertFalse(blogSharingManager1.getSharedBy(blog2.getId())
.contains(contact01));
// blog can be shared again
assertTrue(blogSharingManager0.canBeShared(blog2.getId(), contact1));
assertTrue(blogSharingManager1.canBeShared(blog2.getId(), contact01));
stopLifecycles();
}
@Test
public void testInvitationForExistingBlog() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// 1 and 2 are adding each other
contactManager1.addContact(author2,
author1.getId(), master, clock.currentTimeMillis(), true,
true, true
);
contactManager2.addContact(author1,
author2.getId(), master, clock.currentTimeMillis(), true,
true, true
);
assertEquals(3, blogManager1.getBlogs().size());
// sharer sends invitation for 2's blog to 1
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// make sure blog2 is shared by 0
Collection<Contact> contacts =
blogSharingManager1.getSharedBy(blog2.getId());
assertEquals(1, contacts.size());
assertTrue(contacts.contains(contact01));
// make sure 1 knows that they have blog2 already
Collection<InvitationMessage> messages =
blogSharingManager1.getInvitationMessages(contactId01);
assertEquals(2, messages.size());
assertEquals(blog2, blogManager1.getBlog(blog2.getId()));
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was not added, because it was there already
assertEquals(0, blogSharingManager0.getInvitations().size());
assertEquals(3, blogManager1.getBlogs().size());
stopLifecycles();
}
@Test
public void testRemovingSharedBlog() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was added successfully and is shared both ways
assertEquals(3, blogManager1.getBlogs().size());
Collection<Contact> sharedWith =
blogSharingManager0.getSharedWith(blog2.getId());
assertEquals(1, sharedWith.size());
assertEquals(contact1, sharedWith.iterator().next());
Collection<Contact> sharedBy =
blogSharingManager1.getSharedBy(blog2.getId());
assertEquals(1, sharedBy.size());
assertEquals(contact01, sharedBy.iterator().next());
// shared blog can be removed
assertTrue(blogManager1.canBeRemoved(blog2.getId()));
// invitee removes blog again
blogManager1.removeBlog(blog2);
// sync LEAVE message
sync1To0();
// sharer does not share this blog anymore with invitee
sharedWith =
blogSharingManager0.getSharedWith(blog2.getId());
assertEquals(0, sharedWith.size());
stopLifecycles();
}
@Test
public void testSharedBlogBecomesPermanent() throws Exception {
startLifecycles();
// initialize and let invitee accept all requests
defaultInit(true);
// invitee only sees two blogs
assertEquals(2, blogManager1.getBlogs().size());
// sharer sends invitation for 2's blog to 1
blogSharingManager0
.sendInvitation(blog2.getId(), contactId1, "Hi!");
// sync first request message
sync0To1();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// make sure blog2 is shared by 0
Collection<Contact> contacts =
blogSharingManager1.getSharedBy(blog2.getId());
assertEquals(1, contacts.size());
assertTrue(contacts.contains(contact01));
// sync response back
sync1To0();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
// blog was added and can be removed
assertEquals(3, blogManager1.getBlogs().size());
assertTrue(blogManager1.canBeRemoved(blog2.getId()));
// 1 and 2 are adding each other
contactManager1.addContact(author2,
author1.getId(), master, clock.currentTimeMillis(), true,
true, true
);
contactManager2.addContact(author1,
author2.getId(), master, clock.currentTimeMillis(), true,
true, true
);
assertEquals(3, blogManager1.getBlogs().size());
// now blog can not be removed anymore
assertFalse(blogManager1.canBeRemoved(blog2.getId()));
stopLifecycles();
}
@After
public void tearDown() throws InterruptedException {
TestUtils.deleteTestDirectory(testDir);
}
private class SharerListener implements EventListener {
private volatile boolean responseReceived = false;
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
LOG.info("TEST: Sharer received message");
msgWaiter.resume();
}
} else if (e instanceof BlogInvitationResponseReceivedEvent) {
BlogInvitationResponseReceivedEvent event =
(BlogInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId1, event.getContactId());
responseReceived = true;
eventWaiter.resume();
}
// this is only needed for tests where a blog is re-shared
else if (e instanceof BlogInvitationReceivedEvent) {
BlogInvitationReceivedEvent event =
(BlogInvitationReceivedEvent) e;
eventWaiter.assertEquals(contactId1, event.getContactId());
Blog b = event.getShareable();
try {
Contact c = contactManager0.getContact(contactId1);
blogSharingManager0.respondToInvitation(b, c, true);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
eventWaiter.resume();
}
}
}
}
private class InviteeListener implements EventListener {
private volatile boolean requestReceived = false;
private final boolean accept, answer;
private InviteeListener(boolean accept, boolean answer) {
this.accept = accept;
this.answer = answer;
}
private InviteeListener(boolean accept) {
this(accept, true);
}
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
if ((s == DELIVERED || s == INVALID) && !event.isLocal()) {
LOG.info("TEST: Invitee received message");
msgWaiter.resume();
}
} else if (e instanceof BlogInvitationReceivedEvent) {
BlogInvitationReceivedEvent event =
(BlogInvitationReceivedEvent) e;
requestReceived = true;
if (!answer) return;
Blog b = event.getShareable();
try {
eventWaiter.assertEquals(1,
blogSharingManager1.getInvitations().size());
Contact c =
contactManager1.getContact(event.getContactId());
blogSharingManager1.respondToInvitation(b, c, accept);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
eventWaiter.resume();
}
}
// this is only needed for tests where a blog is re-shared
else if (e instanceof BlogInvitationResponseReceivedEvent) {
BlogInvitationResponseReceivedEvent event =
(BlogInvitationResponseReceivedEvent) e;
eventWaiter.assertEquals(contactId01, event.getContactId());
eventWaiter.resume();
}
}
}
private void startLifecycles() throws InterruptedException {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager2 = t2.getLifecycleManager();
lifecycleManager0.startServices(SHARER);
lifecycleManager1.startServices(INVITEE);
lifecycleManager2.startServices(CONTACT2);
lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup();
lifecycleManager2.waitForStartup();
}
private void stopLifecycles() throws InterruptedException {
// Clean up
lifecycleManager0.stopServices();
lifecycleManager1.stopServices();
lifecycleManager2.stopServices();
lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown();
lifecycleManager2.waitForShutdown();
}
private void defaultInit(boolean accept) throws DbException {
getDefaultIdentities();
addDefaultContacts();
getPersonalBlogOfSharer();
listenToEvents(accept);
}
private void getDefaultIdentities() throws DbException {
author0 = identityManager0.getLocalAuthor();
author1 = identityManager1.getLocalAuthor();
author2 = identityManager2.getLocalAuthor();
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true, true
);
contact1 = contactManager0.getContact(contactId1);
// sharer adds second contact
ContactId contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true, true
);
contact2 = contactManager0.getContact(contactId2);
// contacts add sharer back
contactId01 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true, true
);
contact01 = contactManager1.getContact(contactId01);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true, true
);
contact02 = contactManager2.getContact(contactId02);
}
private void getPersonalBlogOfSharer() throws DbException {
blog0 = blogManager0.getPersonalBlog(author0);
blog1 = blogManager0.getPersonalBlog(author1);
blog2 = blogManager0.getPersonalBlog(author2);
}
private void listenToEvents(boolean accept) {
listener0 = new SharerListener();
t0.getEventBus().addListener(listener0);
listener1 = new InviteeListener(accept);
t1.getEventBus().addListener(listener1);
SharerListener listener2 = new SharerListener();
t2.getEventBus().addListener(listener2);
}
private void sync0To1() throws IOException, TimeoutException {
deliverMessage(sync0, contactId01, sync1, contactId1,
"Sharer to Invitee");
}
private void sync1To0() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId01,
"Invitee to Sharer");
}
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
SyncSessionFactory toSync, ContactId toId, String debug)
throws IOException, TimeoutException {
if (debug != null) LOG.info("TEST: Sending message from " + debug);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
// Write whatever needs to be written
sessionFrom.run();
out.close();
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Create an incoming sync session
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
// Read whatever needs to be read
sessionTo.run();
in.close();
// wait for message to actually arrive
msgWaiter.await(TIMEOUT, 1);
}
private void injectEagerSingletons(
BlogSharingIntegrationTestComponent component) {
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new BlogsModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new SharingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons());
}
}

View File

@@ -0,0 +1,90 @@
package org.briarproject;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestDatabaseModule.class,
TestPluginsModule.class,
TestSeedProviderModule.class,
ClientsModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
BlogsModule.class,
ForumModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class,
MessagingModule.class
})
interface BlogSharingIntegrationTestComponent {
void inject(BlogSharingIntegrationTest testCase);
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(BlogsModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
IdentityManager getIdentityManager();
ContactManager getContactManager();
BlogSharingManager getBlogSharingManager();
BlogManager getBlogManager();
MessageTracker getMessageTracker();
SyncSessionFactory getSyncSessionFactory();
}

View File

@@ -0,0 +1,30 @@
package org.briarproject;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.db.DbException;
import org.briarproject.api.sync.GroupId;
import static org.junit.Assert.assertEquals;
public abstract class BriarIntegrationTest extends BriarTestCase {
protected static void assertGroupCount(MessageTracker tracker, GroupId g,
long msgCount, long unreadCount, long latestMsg)
throws DbException {
GroupCount groupCount = tracker.getGroupCount(g);
assertEquals(msgCount, groupCount.getMsgCount());
assertEquals(unreadCount, groupCount.getUnreadCount());
assertEquals(latestMsg, groupCount.getLatestMsgTime());
}
protected static void assertGroupCount(MessageTracker tracker, GroupId g,
long msgCount, long unreadCount) throws DbException {
GroupCount c1 = tracker.getGroupCount(g);
assertEquals(msgCount, c1.getMsgCount());
assertEquals(unreadCount, c1.getUnreadCount());
}
}

View File

@@ -0,0 +1,462 @@
package org.briarproject;
import junit.framework.Assert;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.db.DbException;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPost;
import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.forum.ForumPostHeader;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.system.Clock;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.jetbrains.annotations.Nullable;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static junit.framework.Assert.assertEquals;
import static junit.framework.Assert.assertNull;
import static junit.framework.TestCase.assertFalse;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
import static org.junit.Assert.assertTrue;
public class ForumManagerTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1;
private SyncSessionFactory sync0, sync1;
private ForumManager forumManager0, forumManager1;
private ContactManager contactManager0, contactManager1;
private MessageTracker messageTracker0, messageTracker1;
private ContactId contactId0,contactId1;
private IdentityManager identityManager0, identityManager1;
private LocalAuthor author0, author1;
private Forum forum0;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
CryptoComponent crypto;
@Inject
ForumPostFactory forumPostFactory;
// objects accessed from background threads need to be volatile
private volatile ForumSharingManager forumSharingManager0;
private volatile ForumSharingManager forumSharingManager1;
private volatile Waiter validationWaiter;
private volatile Waiter deliveryWaiter;
private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000;
private final String SHARER = "Sharer";
private final String INVITEE = "Invitee";
private static final Logger LOG =
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
private ForumManagerTestComponent t0, t1;
@Before
public void setUp() throws Exception {
ForumManagerTestComponent component =
DaggerForumManagerTestComponent.builder().build();
component.inject(this);
injectEagerSingletons(component);
assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, SHARER);
t0 = DaggerForumManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0);
File t1Dir = new File(testDir, INVITEE);
t1 = DaggerForumManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager();
contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager();
messageTracker0 = t0.getMessageTracker();
messageTracker1 = t1.getMessageTracker();
forumManager0 = t0.getForumManager();
forumManager1 = t1.getForumManager();
forumSharingManager0 = t0.getForumSharingManager();
forumSharingManager1 = t1.getForumSharingManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
// initialize waiters fresh for each test
validationWaiter = new Waiter();
deliveryWaiter = new Waiter();
}
private ForumPost createForumPost(GroupId groupId,
@Nullable ForumPost parent, String body, long ms) throws Exception {
return forumPostFactory.createPost(groupId, ms,
parent == null ? null : parent.getMessage().getId(),
author0, body);
}
@Test
public void testForumPost() throws Exception {
startLifecycles();
getDefaultIdentities();
Forum forum = forumManager0.addForum("TestForum");
assertEquals(1, forumManager0.getForums().size());
final long ms1 = clock.currentTimeMillis() - 1000L;
final String body1 = "some forum text";
final long ms2 = clock.currentTimeMillis();
final String body2 = "some other forum text";
ForumPost post1 =
createForumPost(forum.getGroup().getId(), null, body1, ms1);
assertEquals(ms1, post1.getMessage().getTimestamp());
ForumPost post2 =
createForumPost(forum.getGroup().getId(), post1, body2, ms2);
assertEquals(ms2, post2.getMessage().getTimestamp());
forumManager0.addLocalPost(post1);
forumManager0.setReadFlag(forum.getGroup().getId(),
post1.getMessage().getId(), true);
assertGroupCount(messageTracker0, forum.getGroup().getId(), 1, 0,
post1.getMessage().getTimestamp());
forumManager0.addLocalPost(post2);
forumManager0.setReadFlag(forum.getGroup().getId(),
post2.getMessage().getId(), false);
assertGroupCount(messageTracker0, forum.getGroup().getId(), 2, 1,
post2.getMessage().getTimestamp());
forumManager0.setReadFlag(forum.getGroup().getId(),
post2.getMessage().getId(), false);
assertGroupCount(messageTracker0, forum.getGroup().getId(), 2, 1,
post2.getMessage().getTimestamp());
Collection<ForumPostHeader> headers =
forumManager0.getPostHeaders(forum.getGroup().getId());
assertEquals(2, headers.size());
for (ForumPostHeader h : headers) {
final String hBody = forumManager0.getPostBody(h.getId());
boolean isPost1 = h.getId().equals(post1.getMessage().getId());
boolean isPost2 = h.getId().equals(post2.getMessage().getId());
Assert.assertTrue(isPost1 || isPost2);
if (isPost1) {
assertEquals(h.getTimestamp(), ms1);
assertEquals(body1, hBody);
assertNull(h.getParentId());
assertTrue(h.isRead());
}
else {
assertEquals(h.getTimestamp(), ms2);
assertEquals(body2, hBody);
assertEquals(h.getParentId(), post2.getParent());
assertFalse(h.isRead());
}
}
forumManager0.removeForum(forum);
assertEquals(0, forumManager0.getForums().size());
stopLifecycles();
}
@Test
public void testForumPostDelivery() throws Exception {
startLifecycles();
defaultInit();
// share forum
GroupId g = forum0.getId();
forumSharingManager0.sendInvitation(g, contactId1, null);
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
Contact c0 = contactManager1.getContact(contactId0);
forumSharingManager1.respondToInvitation(forum0, c0, true);
sync1To0();
deliveryWaiter.await(TIMEOUT, 1);
// add one forum post
long time = clock.currentTimeMillis();
ForumPost post1 = createForumPost(g, null, "a", time);
forumManager0.addLocalPost(post1);
assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(0, forumManager1.getPostHeaders(g).size());
assertGroupCount(messageTracker0, g, 1, 0, time);
assertGroupCount(messageTracker1, g, 0, 0, 0);
// send post to 1
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
assertEquals(1, forumManager1.getPostHeaders(g).size());
assertGroupCount(messageTracker1, g, 1, 1, time);
// add another forum post
long time2 = clock.currentTimeMillis();
ForumPost post2 = createForumPost(g, null, "b", time2);
forumManager1.addLocalPost(post2);
assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(2, forumManager1.getPostHeaders(g).size());
assertGroupCount(messageTracker0, g, 1, 0, time);
assertGroupCount(messageTracker1, g, 2, 1, time2);
// send post to 0
sync1To0();
deliveryWaiter.await(TIMEOUT, 1);
assertEquals(2, forumManager1.getPostHeaders(g).size());
assertGroupCount(messageTracker0, g, 2, 1, time2);
stopLifecycles();
}
@Test
public void testForumPostDeliveredAfterParent() throws Exception {
startLifecycles();
defaultInit();
// share forum
GroupId g = forum0.getId();
forumSharingManager0.sendInvitation(g, contactId1, null);
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
Contact c0 = contactManager1.getContact(contactId0);
forumSharingManager1.respondToInvitation(forum0, c0, true);
sync1To0();
deliveryWaiter.await(TIMEOUT, 1);
// add one forum post without the parent
long time = clock.currentTimeMillis();
ForumPost post1 = createForumPost(g, null, "a", time);
ForumPost post2 = createForumPost(g, post1, "a", time);
forumManager0.addLocalPost(post2);
assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(0, forumManager1.getPostHeaders(g).size());
// send post to 1 without waiting for message delivery
sync0To1();
validationWaiter.await(TIMEOUT, 1);
assertEquals(0, forumManager1.getPostHeaders(g).size());
// now add the parent post as well
forumManager0.addLocalPost(post1);
assertEquals(2, forumManager0.getPostHeaders(g).size());
assertEquals(0, forumManager1.getPostHeaders(g).size());
// and send it over to 1 and wait for a second message to be delivered
sync0To1();
deliveryWaiter.await(TIMEOUT, 2);
assertEquals(2, forumManager1.getPostHeaders(g).size());
stopLifecycles();
}
@Test
public void testForumPostWithParentInOtherGroup() throws Exception {
startLifecycles();
defaultInit();
// share forum
GroupId g = forum0.getId();
forumSharingManager0.sendInvitation(g, contactId1, null);
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
Contact c0 = contactManager1.getContact(contactId0);
forumSharingManager1.respondToInvitation(forum0, c0, true);
sync1To0();
deliveryWaiter.await(TIMEOUT, 1);
// share a second forum
Forum forum1 = forumManager0.addForum("Test Forum1");
GroupId g1 = forum1.getId();
forumSharingManager0.sendInvitation(g1, contactId1, null);
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
forumSharingManager1.respondToInvitation(forum1, c0, true);
sync1To0();
deliveryWaiter.await(TIMEOUT, 1);
// add one forum post with a parent in another forum
long time = clock.currentTimeMillis();
ForumPost post1 = createForumPost(g1, null, "a", time);
ForumPost post = createForumPost(g, post1, "b", time);
forumManager0.addLocalPost(post);
assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(0, forumManager1.getPostHeaders(g).size());
// send the child post to 1
sync0To1();
validationWaiter.await(TIMEOUT, 1);
assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(0, forumManager1.getPostHeaders(g).size());
// now also add the parent post which is in another group
forumManager0.addLocalPost(post1);
assertEquals(1, forumManager0.getPostHeaders(g1).size());
assertEquals(0, forumManager1.getPostHeaders(g1).size());
// send posts to 1
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(1, forumManager0.getPostHeaders(g1).size());
// the next line is critical, makes sure post doesn't show up
assertEquals(0, forumManager1.getPostHeaders(g).size());
assertEquals(1, forumManager1.getPostHeaders(g1).size());
stopLifecycles();
}
@After
public void tearDown() throws Exception {
TestUtils.deleteTestDirectory(testDir);
}
private class Listener implements EventListener {
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (!event.isLocal()) {
if (event.getState() == DELIVERED) {
deliveryWaiter.resume();
} else if (event.getState() == INVALID ||
event.getState() == PENDING) {
validationWaiter.resume();
}
}
}
}
}
private void defaultInit() throws DbException {
getDefaultIdentities();
addDefaultContacts();
addForum();
listenToEvents();
}
private void getDefaultIdentities() throws DbException {
author0 = identityManager0.getLocalAuthor();
author1 = identityManager1.getLocalAuthor();
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true, true
);
// invitee adds sharer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true, true
);
}
private void addForum() throws DbException {
forum0 = forumManager0.addForum("Test Forum");
}
private void listenToEvents() {
Listener listener0 = new Listener();
t0.getEventBus().addListener(listener0);
Listener listener1 = new Listener();
t1.getEventBus().addListener(listener1);
}
private void sync0To1() throws IOException, TimeoutException {
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
}
private void sync1To0() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId0, "1 to 0");
}
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
SyncSessionFactory toSync, ContactId toId, String debug)
throws IOException, TimeoutException {
if (debug != null) LOG.info("TEST: Sending message from " + debug);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
// Write whatever needs to be written
sessionFrom.run();
out.close();
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Create an incoming sync session
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
// Read whatever needs to be read
sessionTo.run();
in.close();
}
private void startLifecycles() throws InterruptedException {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager0.startServices(SHARER);
lifecycleManager1.startServices(INVITEE);
lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup();
}
private void stopLifecycles() throws InterruptedException {
// Clean up
lifecycleManager0.stopServices();
lifecycleManager1.stopServices();
lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown();
}
private void injectEagerSingletons(ForumManagerTestComponent component) {
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new ForumModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new SharingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons());
}
}

View File

@@ -0,0 +1,90 @@
package org.briarproject;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestDatabaseModule.class,
TestPluginsModule.class,
TestSeedProviderModule.class,
ClientsModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
ForumModule.class,
BlogsModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class,
MessagingModule.class
})
interface ForumManagerTestComponent {
void inject(ForumManagerTest testCase);
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(ForumModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
IdentityManager getIdentityManager();
ContactManager getContactManager();
MessageTracker getMessageTracker();
ForumSharingManager getForumSharingManager();
ForumManager getForumManager();
SyncSessionFactory getSyncSessionFactory();
}

View File

@@ -2,7 +2,7 @@ package org.briarproject;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
@@ -11,6 +11,7 @@ import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
@@ -20,7 +21,9 @@ import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
@@ -41,14 +44,17 @@ import dagger.Component;
DatabaseModule.class,
EventModule.class,
ForumModule.class,
BlogsModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
TransportModule.class,
MessagingModule.class
})
public interface ForumSharingIntegrationTestComponent {
interface ForumSharingIntegrationTestComponent {
void inject(ForumSharingIntegrationTest testCase);
@@ -62,6 +68,8 @@ public interface ForumSharingIntegrationTestComponent {
void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
@@ -84,7 +92,7 @@ public interface ForumSharingIntegrationTestComponent {
DatabaseComponent getDatabaseComponent();
PrivateGroupFactory getPrivateGroupFactory();
ContactGroupFactory getContactGroupFactory();
ClientHelper getClientHelper();

View File

@@ -6,14 +6,14 @@ import org.briarproject.api.crypto.PrivateKey;
import org.briarproject.api.forum.ForumConstants;
import org.briarproject.api.forum.ForumPost;
import org.briarproject.api.forum.ForumPostFactory;
import org.briarproject.api.identity.Author;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.messaging.MessagingConstants;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.messaging.PrivateMessage;
import org.briarproject.api.messaging.PrivateMessageFactory;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import org.briarproject.system.SystemModule;
import org.briarproject.util.StringUtils;
import org.junit.Test;
import javax.inject.Inject;
@@ -48,17 +48,14 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
// Create a maximum-length private message
GroupId groupId = new GroupId(TestUtils.getRandomId());
long timestamp = Long.MAX_VALUE;
MessageId parent = new MessageId(TestUtils.getRandomId());
String contentType = TestUtils.getRandomString(
MessagingConstants.MAX_CONTENT_TYPE_LENGTH);
byte[] body = new byte[MAX_PRIVATE_MESSAGE_BODY_LENGTH];
String body =
StringUtils.fromUtf8(new byte[MAX_PRIVATE_MESSAGE_BODY_LENGTH]);
PrivateMessage message = privateMessageFactory.createPrivateMessage(
groupId, timestamp, parent, contentType, body);
groupId, timestamp, body);
// Check the size of the serialised message
int length = message.getMessage().getRaw().length;
assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH
+ MessagingConstants.MAX_CONTENT_TYPE_LENGTH
+ MAX_PRIVATE_MESSAGE_BODY_LENGTH);
assertTrue(
length > UniqueId.LENGTH + 8 + MAX_PRIVATE_MESSAGE_BODY_LENGTH);
assertTrue(length <= MAX_PACKET_PAYLOAD_LENGTH);
}
@@ -68,17 +65,17 @@ public class MessageSizeIntegrationTest extends BriarTestCase {
String authorName = TestUtils.getRandomString(
MAX_AUTHOR_NAME_LENGTH);
byte[] authorPublic = new byte[MAX_PUBLIC_KEY_LENGTH];
Author author = authorFactory.createAuthor(authorName, authorPublic);
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
LocalAuthor author = authorFactory
.createLocalAuthor(authorName, authorPublic,
privateKey.getEncoded());
// Create a maximum-length forum post
GroupId groupId = new GroupId(TestUtils.getRandomId());
long timestamp = Long.MAX_VALUE;
MessageId parent = new MessageId(TestUtils.getRandomId());
String contentType = TestUtils.getRandomString(
ForumConstants.MAX_CONTENT_TYPE_LENGTH);
byte[] body = new byte[MAX_FORUM_POST_BODY_LENGTH];
PrivateKey privateKey = crypto.generateSignatureKeyPair().getPrivate();
ForumPost post = forumPostFactory.createPseudonymousPost(groupId,
timestamp, parent, author, contentType, body, privateKey);
String body = TestUtils.getRandomString(MAX_FORUM_POST_BODY_LENGTH);
ForumPost post = forumPostFactory.createPost(groupId,
timestamp, parent, author, body);
// Check the size of the serialised message
int length = post.getMessage().getRaw().length;
assertTrue(length > UniqueId.LENGTH + 8 + UniqueId.LENGTH

View File

@@ -0,0 +1,838 @@
package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.ContactGroupFactory;
import org.briarproject.api.clients.MessageTracker.GroupCount;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.crypto.CryptoComponent;
import org.briarproject.api.crypto.KeyPair;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.db.DbException;
import org.briarproject.api.db.Transaction;
import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.privategroup.GroupMember;
import org.briarproject.api.privategroup.GroupMessage;
import org.briarproject.api.privategroup.GroupMessageFactory;
import org.briarproject.api.privategroup.GroupMessageHeader;
import org.briarproject.api.privategroup.JoinMessageHeader;
import org.briarproject.api.privategroup.PrivateGroup;
import org.briarproject.api.privategroup.PrivateGroupFactory;
import org.briarproject.api.privategroup.PrivateGroupManager;
import org.briarproject.api.privategroup.invitation.GroupInvitationFactory;
import org.briarproject.api.sync.GroupId;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.system.Clock;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.privategroup.PrivateGroupModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.concurrent.TimeoutException;
import java.util.logging.Logger;
import javax.inject.Inject;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.TestUtils.getRandomBytes;
import static org.briarproject.api.identity.Author.Status.VERIFIED;
import static org.briarproject.api.privategroup.Visibility.INVISIBLE;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_CONTACT;
import static org.briarproject.api.privategroup.Visibility.REVEALED_BY_US;
import static org.briarproject.api.privategroup.Visibility.VISIBLE;
import static org.briarproject.api.privategroup.invitation.GroupInvitationFactory.SIGNING_LABEL_INVITE;
import static org.briarproject.api.sync.Group.Visibility.SHARED;
import static org.briarproject.api.sync.ValidationManager.State.DELIVERED;
import static org.briarproject.api.sync.ValidationManager.State.INVALID;
import static org.briarproject.api.sync.ValidationManager.State.PENDING;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
public class PrivateGroupManagerTest extends BriarIntegrationTest {
private LifecycleManager lifecycleManager0, lifecycleManager1,
lifecycleManager2;
private SyncSessionFactory sync0, sync1, sync2;
private PrivateGroupManager groupManager0, groupManager1, groupManager2;
private ContactManager contactManager0, contactManager1, contactManager2;
private ContactId contactId01, contactId02, contactId1, contactId2;
private IdentityManager identityManager0, identityManager1,
identityManager2;
private LocalAuthor author0, author1, author2;
private DatabaseComponent db0, db1, db2;
private PrivateGroup privateGroup0;
private GroupId groupId0;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
ClientHelper clientHelper;
@Inject
CryptoComponent crypto;
@Inject
ContactGroupFactory contactGroupFactory;
@Inject
PrivateGroupFactory privateGroupFactory;
@Inject
GroupMessageFactory groupMessageFactory;
@Inject
GroupInvitationFactory groupInvitationFactory;
// objects accessed from background threads need to be volatile
private volatile Waiter validationWaiter;
private volatile Waiter deliveryWaiter;
private final File testDir = TestUtils.getTestDirectory();
private final SecretKey master = TestUtils.getSecretKey();
private final int TIMEOUT = 15000;
private final String AUTHOR0 = "Author 0";
private final String AUTHOR1 = "Author 1";
private final String AUTHOR2 = "Author 2";
private static final Logger LOG =
Logger.getLogger(PrivateGroupManagerTest.class.getName());
private PrivateGroupManagerTestComponent t0, t1, t2;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws Exception {
PrivateGroupManagerTestComponent component =
DaggerPrivateGroupManagerTestComponent.builder().build();
component.inject(this);
assertTrue(testDir.mkdirs());
File t0Dir = new File(testDir, AUTHOR0);
t0 = DaggerPrivateGroupManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t0Dir)).build();
injectEagerSingletons(t0);
File t1Dir = new File(testDir, AUTHOR1);
t1 = DaggerPrivateGroupManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
File t2Dir = new File(testDir, AUTHOR2);
t2 = DaggerPrivateGroupManagerTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t2Dir)).build();
injectEagerSingletons(t2);
identityManager0 = t0.getIdentityManager();
identityManager1 = t1.getIdentityManager();
identityManager2 = t2.getIdentityManager();
contactManager0 = t0.getContactManager();
contactManager1 = t1.getContactManager();
contactManager2 = t2.getContactManager();
db0 = t0.getDatabaseComponent();
db1 = t1.getDatabaseComponent();
db2 = t2.getDatabaseComponent();
groupManager0 = t0.getPrivateGroupManager();
groupManager1 = t1.getPrivateGroupManager();
groupManager2 = t2.getPrivateGroupManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
sync2 = t2.getSyncSessionFactory();
// initialize waiters fresh for each test
validationWaiter = new Waiter();
deliveryWaiter = new Waiter();
startLifecycles();
}
@Test
public void testSendingMessage() throws Exception {
defaultInit();
// create and add test message
long time = clock.currentTimeMillis();
String body = "This is a test message!";
MessageId previousMsgId =
groupManager0.getPreviousMsgId(groupId0);
GroupMessage msg = groupMessageFactory
.createGroupMessage(groupId0, time, null, author0, body,
previousMsgId);
groupManager0.addLocalMessage(msg);
assertEquals(msg.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0));
// sync test message
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// assert that message arrived as expected
Collection<GroupMessageHeader> headers =
groupManager1.getHeaders(groupId0);
assertEquals(3, headers.size());
GroupMessageHeader header = null;
for (GroupMessageHeader h : headers) {
if (!(h instanceof JoinMessageHeader)) {
header = h;
}
}
assertTrue(header != null);
assertFalse(header.isRead());
assertEquals(author0, header.getAuthor());
assertEquals(time, header.getTimestamp());
assertEquals(VERIFIED, header.getAuthorStatus());
assertEquals(body, groupManager1.getMessageBody(header.getId()));
GroupCount count = groupManager1.getGroupCount(groupId0);
assertEquals(2, count.getUnreadCount());
assertEquals(time, count.getLatestMsgTime());
assertEquals(3, count.getMsgCount());
}
@Test
public void testMessageWithWrongPreviousMsgId() throws Exception {
defaultInit();
// create and add test message with no previousMsgId
@SuppressWarnings("ConstantConditions")
GroupMessage msg = groupMessageFactory
.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
author0, "test", null);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size());
// create and add test message with random previousMsgId
MessageId previousMsgId = new MessageId(TestUtils.getRandomId());
msg = groupMessageFactory
.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
author0, "test", previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size());
// create and add test message with wrong previousMsgId
previousMsgId = groupManager1.getPreviousMsgId(groupId0);
msg = groupMessageFactory
.createGroupMessage(groupId0, clock.currentTimeMillis(), null,
author0, "test", previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size());
}
@Test
public void testMessageWithWrongParentMsgId() throws Exception {
defaultInit();
// create and add test message with random parentMsgId
MessageId parentMsgId = new MessageId(TestUtils.getRandomId());
MessageId previousMsgId = groupManager0.getPreviousMsgId(groupId0);
GroupMessage msg = groupMessageFactory
.createGroupMessage(groupId0, clock.currentTimeMillis(),
parentMsgId, author0, "test", previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size());
// create and add test message with wrong parentMsgId
parentMsgId = previousMsgId;
msg = groupMessageFactory
.createGroupMessage(groupId0, clock.currentTimeMillis(),
parentMsgId, author0, "test", previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size());
}
@Test
public void testMessageWithWrongTimestamp() throws Exception {
defaultInit();
// create and add test message with wrong timestamp
MessageId previousMsgId = groupManager0.getPreviousMsgId(groupId0);
GroupMessage msg = groupMessageFactory
.createGroupMessage(groupId0, 42, null, author0, "test",
previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(2, groupManager1.getHeaders(groupId0).size());
// create and add test message with good timestamp
long time = clock.currentTimeMillis();
msg = groupMessageFactory
.createGroupMessage(groupId0, time, null, author0, "test",
previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
assertEquals(3, groupManager1.getHeaders(groupId0).size());
// create and add test message with same timestamp as previous message
previousMsgId = msg.getMessage().getId();
msg = groupMessageFactory
.createGroupMessage(groupId0, time, previousMsgId, author0,
"test2", previousMsgId);
groupManager0.addLocalMessage(msg);
// sync test message
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that message did not arrive
assertEquals(3, groupManager1.getHeaders(groupId0).size());
}
@Test
public void testWrongJoinMessages1() throws Exception {
addDefaultIdentities();
addDefaultContacts();
listenToEvents();
// author0 joins privateGroup0 with wrong join message
long joinTime = clock.currentTimeMillis();
GroupMessage joinMsg0 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author0,
joinTime, getRandomBytes(12));
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
assertEquals(joinMsg0.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0));
// share the group with 1
Transaction txn0 = db0.startTransaction(false);
db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0);
db0.endTransaction(txn0);
// author1 joins privateGroup0 with wrong timestamp
joinTime = clock.currentTimeMillis();
long inviteTime = joinTime;
Contact c1 = contactManager0.getContact(contactId1);
byte[] creatorSignature = groupInvitationFactory
.signInvitation(c1, privateGroup0.getId(), inviteTime,
author0.getPrivateKey());
GroupMessage joinMsg1 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1,
inviteTime, creatorSignature);
groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false);
assertEquals(joinMsg1.getMessage().getId(),
groupManager1.getPreviousMsgId(groupId0));
// share the group with 0
Transaction txn1 = db1.startTransaction(false);
db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(),
SHARED);
db1.commitTransaction(txn1);
db1.endTransaction(txn1);
// sync join messages
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that 0 never joined the group from 1's perspective
assertEquals(1, groupManager1.getHeaders(groupId0).size());
sync1To0();
validationWaiter.await(TIMEOUT, 1);
// assert that 1 never joined the group from 0's perspective
assertEquals(1, groupManager0.getHeaders(groupId0).size());
}
@Test
public void testWrongJoinMessages2() throws Exception {
addDefaultIdentities();
addDefaultContacts();
listenToEvents();
// author0 joins privateGroup0 with wrong member's join message
long joinTime = clock.currentTimeMillis();
long inviteTime = joinTime - 1;
BdfList toSign = groupInvitationFactory
.createInviteToken(author0.getId(), author0.getId(),
privateGroup0.getId(), inviteTime);
byte[] creatorSignature = clientHelper
.sign(SIGNING_LABEL_INVITE, toSign, author0.getPrivateKey());
// join message should not include invite time and creator's signature
GroupMessage joinMsg0 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author0,
inviteTime, creatorSignature);
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
assertEquals(joinMsg0.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0));
// share the group with 1
Transaction txn0 = db0.startTransaction(false);
db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0);
db0.endTransaction(txn0);
// author1 joins privateGroup0 with wrong signature in join message
joinTime = clock.currentTimeMillis();
inviteTime = joinTime - 1;
// signature uses joiner's key, not creator's key
Contact c1 = contactManager0.getContact(contactId1);
creatorSignature = groupInvitationFactory
.signInvitation(c1, privateGroup0.getId(), inviteTime,
author1.getPrivateKey());
GroupMessage joinMsg1 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1,
inviteTime, creatorSignature);
groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false);
assertEquals(joinMsg1.getMessage().getId(),
groupManager1.getPreviousMsgId(groupId0));
// share the group with 0
Transaction txn1 = db1.startTransaction(false);
db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(), SHARED);
db1.commitTransaction(txn1);
db1.endTransaction(txn1);
// sync join messages
sync0To1();
validationWaiter.await(TIMEOUT, 1);
// assert that 0 never joined the group from 1's perspective
assertEquals(1, groupManager1.getHeaders(groupId0).size());
sync1To0();
validationWaiter.await(TIMEOUT, 1);
// assert that 1 never joined the group from 0's perspective
assertEquals(1, groupManager0.getHeaders(groupId0).size());
}
@Test
public void testGetMembers() throws Exception {
defaultInit();
Collection<GroupMember> members0 = groupManager0.getMembers(groupId0);
assertEquals(2, members0.size());
for (GroupMember m : members0) {
if (m.getAuthor().equals(author0)) {
assertEquals(VISIBLE, m.getVisibility());
} else {
assertEquals(author1, m.getAuthor());
assertEquals(VISIBLE, m.getVisibility());
}
}
Collection<GroupMember> members1 = groupManager1.getMembers(groupId0);
assertEquals(2, members1.size());
for (GroupMember m : members1) {
if (m.getAuthor().equals(author1)) {
assertEquals(VISIBLE, m.getVisibility());
} else {
assertEquals(author0, m.getAuthor());
assertEquals(VISIBLE, m.getVisibility());
}
}
}
@Test
public void testJoinMessages() throws Exception {
defaultInit();
Collection<GroupMessageHeader> headers0 =
groupManager0.getHeaders(groupId0);
for (GroupMessageHeader h : headers0) {
if (h instanceof JoinMessageHeader) {
JoinMessageHeader j = (JoinMessageHeader) h;
// all relationships of the creator are visible
assertEquals(VISIBLE, j.getVisibility());
}
}
Collection<GroupMessageHeader> headers1 =
groupManager1.getHeaders(groupId0);
for (GroupMessageHeader h : headers1) {
if (h instanceof JoinMessageHeader) {
JoinMessageHeader j = (JoinMessageHeader) h;
if (h.getAuthor().equals(author1))
// we are visible to ourselves
assertEquals(VISIBLE, j.getVisibility());
else
// our relationship to the creator is visible
assertEquals(VISIBLE, j.getVisibility());
}
}
}
@Test
public void testRevealingRelationships() throws Exception {
defaultInit();
// share the group with 2
Transaction txn0 = db0.startTransaction(false);
db0.setGroupVisibility(txn0, contactId2, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0);
db0.endTransaction(txn0);
// author2 joins privateGroup0
long joinTime = clock.currentTimeMillis();
long inviteTime = joinTime - 1;
Contact c2 = contactManager0.getContact(contactId2);
byte[] creatorSignature = groupInvitationFactory
.signInvitation(c2, privateGroup0.getId(), inviteTime,
author0.getPrivateKey());
GroupMessage joinMsg2 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author2,
inviteTime, creatorSignature);
Transaction txn2 = db2.startTransaction(false);
groupManager2.addPrivateGroup(txn2, privateGroup0, joinMsg2, false);
// share the group with 0
db2.setGroupVisibility(txn2, contactId01, privateGroup0.getId(),
SHARED);
db2.commitTransaction(txn2);
db2.endTransaction(txn2);
// sync join messages
deliverMessage(sync2, contactId2, sync0, contactId02, "2 to 0");
deliveryWaiter.await(TIMEOUT, 1);
deliverMessage(sync0, contactId02, sync2, contactId2, "0 to 2");
deliveryWaiter.await(TIMEOUT, 2);
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
// check that everybody sees everybody else as joined
Collection<GroupMember> members0 = groupManager0.getMembers(groupId0);
assertEquals(3, members0.size());
Collection<GroupMember> members1 = groupManager1.getMembers(groupId0);
assertEquals(3, members1.size());
Collection<GroupMember> members2 = groupManager2.getMembers(groupId0);
assertEquals(3, members2.size());
// assert that contact relationship is not revealed initially
for (GroupMember m : members1) {
if (m.getAuthor().equals(author2)) {
assertEquals(INVISIBLE, m.getVisibility());
}
}
for (GroupMember m : members2) {
if (m.getAuthor().equals(author1)) {
assertEquals(INVISIBLE, m.getVisibility());
}
}
// reveal contact relationship
Transaction txn1 = db1.startTransaction(false);
groupManager1
.relationshipRevealed(txn1, groupId0, author2.getId(), false);
db1.commitTransaction(txn1);
db1.endTransaction(txn1);
txn2 = db2.startTransaction(false);
groupManager2
.relationshipRevealed(txn2, groupId0, author1.getId(), true);
db2.commitTransaction(txn2);
db2.endTransaction(txn2);
// assert that contact relationship is now revealed properly
members1 = groupManager1.getMembers(groupId0);
for (GroupMember m : members1) {
if (m.getAuthor().equals(author2)) {
assertEquals(REVEALED_BY_US, m.getVisibility());
}
}
members2 = groupManager2.getMembers(groupId0);
for (GroupMember m : members2) {
if (m.getAuthor().equals(author1)) {
assertEquals(REVEALED_BY_CONTACT, m.getVisibility());
}
}
// assert that join messages reflect revealed relationship
Collection<GroupMessageHeader> headers1 =
groupManager1.getHeaders(groupId0);
for (GroupMessageHeader h : headers1) {
if (h instanceof JoinMessageHeader) {
JoinMessageHeader j = (JoinMessageHeader) h;
if (h.getAuthor().equals(author2))
// 1 revealed the relationship to 2
assertEquals(REVEALED_BY_US, j.getVisibility());
else
// 1's other relationship (to 1 and creator) are visible
assertEquals(VISIBLE, j.getVisibility());
}
}
Collection<GroupMessageHeader> headers2 =
groupManager2.getHeaders(groupId0);
for (GroupMessageHeader h : headers2) {
if (h instanceof JoinMessageHeader) {
JoinMessageHeader j = (JoinMessageHeader) h;
if (h.getAuthor().equals(author1))
// 2's relationship was revealed by 1
assertEquals(REVEALED_BY_CONTACT, j.getVisibility());
else
// 2's other relationship (to 2 and creator) are visible
assertEquals(VISIBLE, j.getVisibility());
}
}
}
@Test
public void testDissolveGroup() throws Exception {
defaultInit();
// group is not dissolved initially
assertFalse(groupManager1.isDissolved(groupId0));
// creator dissolves group
Transaction txn1 = db1.startTransaction(false);
groupManager1.markGroupDissolved(txn1, groupId0);
db1.commitTransaction(txn1);
db1.endTransaction(txn1);
// group is dissolved now
assertTrue(groupManager1.isDissolved(groupId0));
}
@After
public void tearDown() throws Exception {
stopLifecycles();
TestUtils.deleteTestDirectory(testDir);
}
private class Listener implements EventListener {
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (!event.isLocal()) {
if (event.getState() == DELIVERED) {
LOG.info("Delivered new message");
deliveryWaiter.resume();
} else if (event.getState() == INVALID ||
event.getState() == PENDING) {
LOG.info("Validated new " + event.getState().name() +
" message");
validationWaiter.resume();
}
}
}
}
}
private void defaultInit() throws Exception {
addDefaultIdentities();
addDefaultContacts();
listenToEvents();
addGroup();
}
private void addDefaultIdentities() throws DbException {
KeyPair keyPair0 = crypto.generateSignatureKeyPair();
byte[] publicKey0 = keyPair0.getPublic().getEncoded();
byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
author0 = authorFactory
.createLocalAuthor(AUTHOR0, publicKey0, privateKey0);
identityManager0.registerLocalAuthor(author0);
privateGroup0 =
privateGroupFactory.createPrivateGroup("Testgroup", author0);
groupId0 = privateGroup0.getId();
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
byte[] publicKey1 = keyPair1.getPublic().getEncoded();
byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
author1 = authorFactory
.createLocalAuthor(AUTHOR1, publicKey1, privateKey1);
identityManager1.registerLocalAuthor(author1);
KeyPair keyPair2 = crypto.generateSignatureKeyPair();
byte[] publicKey2 = keyPair2.getPublic().getEncoded();
byte[] privateKey2 = keyPair2.getPrivate().getEncoded();
author2 = authorFactory
.createLocalAuthor(AUTHOR2, publicKey2, privateKey2);
identityManager2.registerLocalAuthor(author2);
}
private void addDefaultContacts() throws DbException {
// creator adds invitee as contact
contactId1 = contactManager0
.addContact(author1, author0.getId(), master,
clock.currentTimeMillis(), true, true, true);
// invitee adds creator back
contactId01 = contactManager1
.addContact(author0, author1.getId(), master,
clock.currentTimeMillis(), true, true, true);
// creator adds invitee as contact
contactId2 = contactManager0
.addContact(author2, author0.getId(), master,
clock.currentTimeMillis(), true, true, true);
// invitee adds creator back
contactId02 = contactManager2
.addContact(author0, author2.getId(), master,
clock.currentTimeMillis(), true, true, true);
}
private void listenToEvents() {
Listener listener0 = new Listener();
t0.getEventBus().addListener(listener0);
Listener listener1 = new Listener();
t1.getEventBus().addListener(listener1);
Listener listener2 = new Listener();
t2.getEventBus().addListener(listener2);
}
private void addGroup() throws Exception {
// author0 joins privateGroup0
long joinTime = clock.currentTimeMillis();
GroupMessage joinMsg0 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author0);
groupManager0.addPrivateGroup(privateGroup0, joinMsg0, true);
assertEquals(joinMsg0.getMessage().getId(),
groupManager0.getPreviousMsgId(groupId0));
// share the group with 1
Transaction txn0 = db0.startTransaction(false);
db0.setGroupVisibility(txn0, contactId1, privateGroup0.getId(), SHARED);
db0.commitTransaction(txn0);
db0.endTransaction(txn0);
// author1 joins privateGroup0
joinTime = clock.currentTimeMillis();
long inviteTime = joinTime - 1;
Contact c1 = contactManager0.getContact(contactId1);
byte[] creatorSignature = groupInvitationFactory
.signInvitation(c1, privateGroup0.getId(), inviteTime,
author0.getPrivateKey());
GroupMessage joinMsg1 = groupMessageFactory
.createJoinMessage(privateGroup0.getId(), joinTime, author1,
inviteTime, creatorSignature);
groupManager1.addPrivateGroup(privateGroup0, joinMsg1, false);
// share the group with 0
Transaction txn1 = db1.startTransaction(false);
db1.setGroupVisibility(txn1, contactId01, privateGroup0.getId(),
SHARED);
db1.commitTransaction(txn1);
db1.endTransaction(txn1);
assertEquals(joinMsg1.getMessage().getId(),
groupManager1.getPreviousMsgId(groupId0));
// sync join messages
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
sync1To0();
deliveryWaiter.await(TIMEOUT, 1);
}
private void sync0To1() throws IOException, TimeoutException {
deliverMessage(sync0, contactId01, sync1, contactId1, "0 to 1");
}
private void sync1To0() throws IOException, TimeoutException {
deliverMessage(sync1, contactId1, sync0, contactId01, "1 to 0");
}
private void deliverMessage(SyncSessionFactory fromSync, ContactId fromId,
SyncSessionFactory toSync, ContactId toId, String debug)
throws IOException, TimeoutException {
if (debug != null) LOG.info("TEST: Sending message from " + debug);
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Create an outgoing sync session
SyncSession sessionFrom =
fromSync.createSimplexOutgoingSession(toId, MAX_LATENCY, out);
// Write whatever needs to be written
sessionFrom.run();
out.close();
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
// Create an incoming sync session
SyncSession sessionTo = toSync.createIncomingSession(fromId, in);
// Read whatever needs to be read
sessionTo.run();
in.close();
}
private void startLifecycles() throws InterruptedException {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager2 = t2.getLifecycleManager();
lifecycleManager0.startServices(AUTHOR0);
lifecycleManager1.startServices(AUTHOR1);
lifecycleManager2.startServices(AUTHOR2);
lifecycleManager0.waitForStartup();
lifecycleManager1.waitForStartup();
lifecycleManager2.waitForStartup();
}
private void stopLifecycles() throws InterruptedException {
// Clean up
lifecycleManager0.stopServices();
lifecycleManager1.stopServices();
lifecycleManager2.stopServices();
lifecycleManager0.waitForShutdown();
lifecycleManager1.waitForShutdown();
lifecycleManager2.waitForShutdown();
}
private void injectEagerSingletons(
PrivateGroupManagerTestComponent component) {
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new PrivateGroupModule.EagerSingletons());
component.inject(new CryptoModule.EagerSingletons());
component.inject(new ContactModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new PropertiesModule.EagerSingletons());
}
}

View File

@@ -0,0 +1,85 @@
package org.briarproject;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.privategroup.PrivateGroupManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.privategroup.PrivateGroupModule;
import org.briarproject.privategroup.invitation.GroupInvitationModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestDatabaseModule.class,
TestPluginsModule.class,
TestSeedProviderModule.class,
ClientsModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
MessagingModule.class,
PrivateGroupModule.class,
GroupInvitationModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
})
interface PrivateGroupManagerTestComponent {
void inject(PrivateGroupManagerTest testCase);
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(PrivateGroupModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(PropertiesModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
IdentityManager getIdentityManager();
ContactManager getContactManager();
PrivateGroupManager getPrivateGroupManager();
SyncSessionFactory getSyncSessionFactory();
DatabaseComponent getDatabaseComponent();
}

View File

@@ -43,6 +43,9 @@ import static org.junit.Assert.assertTrue;
public class SimplexMessagingIntegrationTest extends BriarTestCase {
private final static String ALICE = "Alice";
private final static String BOB = "Bob";
private final File testDir = TestUtils.getTestDirectory();
private final File aliceDir = new File(testDir, "alice");
private final File bobDir = new File(testDir, "bob");
@@ -69,6 +72,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
read(write());
}
private byte[] write() throws Exception {
// Instantiate Alice's services
LifecycleManager lifecycleManager = alice.getLifecycleManager();
@@ -83,23 +87,23 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
SyncSessionFactory syncSessionFactory = alice.getSyncSessionFactory();
// Start the lifecycle manager
lifecycleManager.startServices();
lifecycleManager.startServices(null);
lifecycleManager.waitForStartup();
// Add an identity for Alice
LocalAuthor aliceAuthor = new LocalAuthor(aliceId, "Alice",
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
identityManager.addLocalAuthor(aliceAuthor);
identityManager.registerLocalAuthor(aliceAuthor);
// Add Bob as a contact
Author bobAuthor = new Author(bobId, "Bob",
Author bobAuthor = new Author(bobId, BOB,
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = contactManager.addContact(bobAuthor, aliceId,
master, timestamp, true, true);
ContactId contactId = contactManager.addContact(bobAuthor,
aliceAuthor.getId(), master, timestamp, true, true, true);
// Send Bob a message
GroupId groupId = messagingManager.getConversationId(contactId);
byte[] body = "Hi Bob!".getBytes("UTF-8");
String body = "Hi Bob!";
PrivateMessage message = privateMessageFactory.createPrivateMessage(
groupId, timestamp, null, "text/plain", body);
groupId, timestamp, body);
messagingManager.addLocalMessage(message);
// Get a stream context
StreamContext ctx = keyManager.getStreamContext(contactId,
@@ -136,18 +140,17 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
bob.getMessagingManager();
// Start the lifecyle manager
lifecycleManager.startServices();
lifecycleManager.startServices(null);
lifecycleManager.waitForStartup();
// Add an identity for Bob
LocalAuthor bobAuthor = new LocalAuthor(bobId, "Bob",
LocalAuthor bobAuthor = new LocalAuthor(bobId, BOB,
new byte[MAX_PUBLIC_KEY_LENGTH], new byte[123], timestamp);
identityManager.addLocalAuthor(bobAuthor);
identityManager.registerLocalAuthor(bobAuthor);
// Add Alice as a contact
Author aliceAuthor = new Author(aliceId, "Alice",
Author aliceAuthor = new Author(aliceId, ALICE,
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = contactManager.addContact(aliceAuthor, bobId,
master, timestamp, false, true);
ContactId contactId = contactManager.addContact(aliceAuthor,
bobAuthor.getId(), master, timestamp, false, true, true);
// Set up an event listener
MessageListener listener = new MessageListener();
bob.getEventBus().addListener(listener);

View File

@@ -31,7 +31,6 @@ import java.util.Collection;
import javax.inject.Inject;
import static org.briarproject.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
import static org.briarproject.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
@@ -75,8 +74,8 @@ public class SyncIntegrationTest extends BriarTestCase {
headerKey = TestUtils.getSecretKey();
streamNumber = 123;
// Create a group
ClientId clientId = new ClientId(TestUtils.getRandomId());
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
ClientId clientId = new ClientId(TestUtils.getRandomString(5));
byte[] descriptor = new byte[0];
Group group = groupFactory.createGroup(clientId, descriptor);
// Add two messages to the group
long timestamp = System.currentTimeMillis();
@@ -123,7 +122,7 @@ public class SyncIntegrationTest extends BriarTestCase {
// Create the readers
StreamContext ctx = new StreamContext(contactId, transportId, tagKey,
headerKey, 0);
headerKey, streamNumber);
InputStream streamReader = streamReaderFactory.createStreamReader(in,
ctx);
PacketReader packetReader = packetReaderFactory.createPacketReader(

View File

@@ -1,5 +1,10 @@
package org.briarproject;
package org.briarproject.introduction;
import org.briarproject.TestDatabaseModule;
import org.briarproject.TestPluginsModule;
import org.briarproject.TestSeedProviderModule;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageTracker;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
@@ -15,10 +20,8 @@ import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.introduction.IntroductionGroupFactory;
import org.briarproject.introduction.IntroductionModule;
import org.briarproject.introduction.MessageSender;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.messaging.MessagingModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
@@ -45,9 +48,10 @@ import dagger.Component;
SyncModule.class,
SystemModule.class,
DataModule.class,
PropertiesModule.class
PropertiesModule.class,
MessagingModule.class
})
public interface IntroductionIntegrationTestComponent {
interface IntroductionIntegrationTestComponent {
void inject(IntroductionIntegrationTest testCase);
@@ -79,12 +83,16 @@ public interface IntroductionIntegrationTestComponent {
TransportPropertyManager getTransportPropertyManager();
MessageTracker getMessageTracker();
SyncSessionFactory getSyncSessionFactory();
/* the following methods are only needed to manually construct messages */
DatabaseComponent getDatabaseComponent();
ClientHelper getClientHelper();
MessageSender getMessageSender();
IntroductionGroupFactory getIntroductionGroupFactory();

11
briar-android/.tx/config Normal file
View File

@@ -0,0 +1,11 @@
[main]
host = https://www.transifex.com
lang_map = pt_BR: pt-rBR, fr_FR: fr
[briar.stringsxml-5]
file_filter = res/values-<lang>/strings.xml
source_file = res/values/strings.xml
source_lang = en
type = ANDROID
minimum_perc = 25

View File

@@ -7,8 +7,7 @@
android:versionName="0.12">
<uses-sdk
android:minSdkVersion="9"
android:minSdkVersion="14"
android:targetSdkVersion="22"
tools:overrideLibrary="android.support.v14.preference"
/>
@@ -51,7 +50,9 @@
android:finishOnTaskLaunch="true"
android:label="@string/crash_report_title"
android:launchMode="singleInstance"
android:process=":briar_error_handler">
android:process=":briar_error_handler"
android:theme="@style/BriarThemeNoActionBar.Default"
android:windowSoftInputMode="stateHidden">
</activity>
<activity
@@ -67,7 +68,8 @@
<activity
android:name=".android.SetupActivity"
android:label="@string/setup_title">
android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
@@ -91,7 +93,7 @@
android:label="@string/app_name"
android:theme="@style/BriarThemeNoActionBar.Default"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="stateHidden">
android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
@@ -99,8 +101,71 @@
</activity>
<activity
android:name=".android.forum.AvailableForumsActivity"
android:label="@string/available_forums_title"
android:name=".android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.privategroup.conversation.GroupActivity"
android:label="@string/app_name"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.privategroup.invitation.GroupInvitationActivity"
android:label="@string/groups_invitations_title"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"/>
</activity>
<activity
android:name=".android.privategroup.memberlist.GroupMemberListActivity"
android:label="@string/groups_member_list"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name=".android.privategroup.reveal.RevealContactsActivity"
android:label="@string/groups_reveal_contacts"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"
/>
</activity>
<activity
android:name=".android.privategroup.creation.GroupInviteActivity"
android:label="@string/groups_invite_members"
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"/>
</activity>
<activity
android:name=".android.sharing.ForumInvitationActivity"
android:label="@string/forum_invitations_title"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -108,6 +173,16 @@
/>
</activity>
<activity
android:name=".android.sharing.BlogInvitationActivity"
android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName=".android.contact.ConversationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity"
/>
</activity>
<activity
android:name=".android.forum.CreateForumActivity"
android:label="@string/create_forum_title"
@@ -122,7 +197,8 @@
<activity
android:name=".android.forum.ForumActivity"
android:label="@string/app_name"
android:parentActivityName=".android.NavDrawerActivity">
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
@@ -130,18 +206,30 @@
</activity>
<activity
android:name=".android.forum.ReadForumPostActivity"
android:label="@string/app_name"
android:parentActivityName=".android.NavDrawerActivity">
android:name=".android.sharing.ShareForumActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.forum.ForumActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
android:value=".android.forum.ForumActivity"
/>
</activity>
<activity
android:name=".android.forum.ShareForumActivity"
android:label="@string/forums_share_toolbar_header"
android:name=".android.sharing.ShareBlogActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.blogs.BlogActivity"
android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.sharing.ForumSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.forum.ForumActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -150,10 +238,18 @@
</activity>
<activity
android:name=".android.forum.WriteForumPostActivity"
android:label="@string/app_name"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="stateVisible">
android:name=".android.sharing.BlogSharingStatusActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.blogs.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.blogs.BlogActivity"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
@@ -161,9 +257,46 @@
</activity>
<activity
android:name=".android.identity.CreateIdentityActivity"
android:label="@string/new_identity_title"
android:windowSoftInputMode="stateVisible">
android:name=".android.blogs.WriteBlogPostActivity"
android:label="@string/blogs_write_blog_post"
android:parentActivityName=".android.blogs.BlogActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.blogs.ReblogActivity"
android:label="@string/blogs_reblog_button"
android:parentActivityName=".android.blogs.BlogActivity"
android:windowSoftInputMode="stateHidden">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.blogs.RssFeedImportActivity"
android:label="@string/blogs_rss_feeds_import"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.blogs.RssFeedManageActivity"
android:label="@string/blogs_rss_feeds_manage"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
@@ -204,7 +337,7 @@
<activity
android:name=".android.SettingsActivity"
android:label="@string/settings_title"
android:label="@string/settings_button"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -216,6 +349,16 @@
</intent-filter>
</activity>
<activity
android:name=".android.ChangePasswordActivity"
android:label="@string/change_password"
android:parentActivityName=".android.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.SettingsActivity"
/>
</activity>
<activity
android:name=".android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting">

View File

@@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
width="24"
height="24"
viewBox="0 0 24 24"
sodipodi:docname="ic_emoji_emoticons.svg">
<metadata
id="metadata8">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs6" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1021"
id="namedview4"
showgrid="false"
inkscape:zoom="4.6354778"
inkscape:cx="47.926788"
inkscape:cy="24.127496"
inkscape:window-x="1440"
inkscape:window-y="23"
inkscape:window-maximized="0"
inkscape:current-layer="svg2" />
<path
style="fill:#000000;fill-opacity:1"
d="m 15.483903,3.8556996 c -0.661546,0.040406 -0.536253,1.2125273 -0.08054,1.6240791 1.361771,1.4519837 1.747379,3.5080793 1.895646,5.4253553 0.109142,2.216286 -0.0846,4.555699 -1.171466,6.533591 -0.361828,0.731167 -1.339597,1.273078 -1.15283,2.195835 0.287109,1.037426 1.187031,0.242862 1.620751,-0.183708 1.991711,-1.742024 2.867744,-4.428018 2.93133,-7.013492 0.02009,-1.918049 -0.231841,-3.9213035 -1.212735,-5.6044037 -0.664187,-1.0906817 -1.39072,-2.2339438 -2.497355,-2.9193489 -0.127976,-0.045915 -0.238296,-0.06368 -0.332802,-0.057908 z M 5.9118212,7.6583077 A 1.3631614,1.3631614 0 0 0 4.54866,9.0214691 1.3631614,1.3631614 0 0 0 5.9118212,10.38463 1.3631614,1.3631614 0 0 0 7.2749824,9.0214691 1.3631614,1.3631614 0 0 0 5.9118212,7.6583077 Z m 3.0731032,3.0012183 0,2.044742 4.7710646,0 0,-2.044742 -4.7710646,0 z m -3.1496485,3.471136 a 1.3631614,1.3631614 0 0 0 -1.3631612,1.363161 1.3631614,1.3631614 0 0 0 1.3631612,1.363161 1.3631614,1.3631614 0 0 0 1.3631612,-1.363161 1.3631614,1.3631614 0 0 0 -1.3631612,-1.363161 z"
id="path4142"
inkscape:connector-curvature="0" />
</svg>

After

Width:  |  Height:  |  Size: 2.6 KiB

View File

@@ -0,0 +1,92 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<!-- Created with Inkscape (http://www.inkscape.org/) -->
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
width="49"
height="20"
viewBox="0 0 49.000004 20"
id="svg2"
version="1.1"
inkscape:version="0.91 r13725"
sodipodi:docname="trust-indicator.svg">
<defs
id="defs4" />
<sodipodi:namedview
id="base"
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1.0"
inkscape:pageopacity="0.0"
inkscape:pageshadow="2"
inkscape:zoom="15.839192"
inkscape:cx="19.828141"
inkscape:cy="4.1791031"
inkscape:document-units="px"
inkscape:current-layer="layer1"
showgrid="false"
units="px"
fit-margin-top="0"
fit-margin-left="0"
fit-margin-right="0"
fit-margin-bottom="0"
inkscape:window-width="1920"
inkscape:window-height="993"
inkscape:window-x="1440"
inkscape:window-y="0"
inkscape:window-maximized="1" />
<metadata
id="metadata7">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title />
</cc:Work>
</rdf:RDF>
</metadata>
<g
inkscape:label="Layer 1"
inkscape:groupmode="layer"
id="layer1"
transform="translate(-216.17711,-507.04154)">
<g
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="text4136"
transform="matrix(1,0,0,0.90497738,-18.96574,55.694085)">
<path
d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
id="path4745"
inkscape:connector-curvature="0" />
</g>
<g
transform="matrix(1,0,0,0.90497738,-3.4657389,55.694085)"
id="g4755"
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1">
<path
inkscape:connector-curvature="0"
id="path4757"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z" />
</g>
<g
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:25px;line-height:125%;font-family:FreeSans;-inkscape-font-specification:FreeSans;letter-spacing:0px;word-spacing:0px;fill:#b7b7b7;fill-opacity:1;stroke:none;stroke-width:1px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
id="g4139"
transform="matrix(1,0,0,0.90497738,12.034262,55.694085)">
<path
d="m 250.64285,514.07648 0,-2.275 -2.55,0 0,-3.8 2.55,0 0,-2.275 -2.55,0 0,-4.225 -2.375,0 0,4.225 -3.15,0 0,-4.225 -2.375,0 0,4.225 -2.55,0 0,2.275 2.55,0 0,3.8 -2.55,0 0,2.275 2.55,0 0,4 2.375,0 0,-4 3.15,0 0,4 2.375,0 0,-4 2.55,0 z m -4.925,-2.275 -3.15,0 0,-3.8 3.15,0 0,3.8 z"
style="font-style:normal;font-variant:normal;font-weight:600;font-stretch:normal;font-family:Titillium;-inkscape-font-specification:'Titillium Semi-Bold';fill:#b7b7b7;fill-opacity:1"
id="path4141"
inkscape:connector-curvature="0" />
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 4.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 901 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 650 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 412 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 KiB

View File

@@ -6,45 +6,39 @@ apply plugin: 'witness'
apply plugin: 'com.neenbedankt.android-apt'
apply plugin: 'de.undercouch.download'
repositories {
jcenter()
maven { url "https://oss.sonatype.org/content/repositories/snapshots" }
}
dependencies {
def supportVersion = '23.2.1'
compile project(':briar-api')
compile project(':briar-core')
compile fileTree(dir: 'libs', include: '*.jar')
// This shouldn't be necessary; per section 23.4.4 of the Gradle docs:
// "file dependencies are included in transitive project dependencies within the same build".
compile files('../briar-core/libs/jsocks.jar')
compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4'
}
compile("com.android.support:preference-v7:$supportVersion") {
exclude module: 'support-v4'
}
compile("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4'
exclude module: 'preference-v7'
exclude module: 'recyclerview-v7'
}
compile("com.android.support:design:$supportVersion") {
exclude module: 'support-v4'
exclude module: 'recyclerview-v7'
}
compile "com.android.support:cardview-v7:$supportVersion"
compile 'com.android.support:support-annotations:23.4.0'
compile('ch.acra:acra:4.8.5') {
exclude module: 'support-v4'
exclude module: 'support-annotations'
}
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'
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.7.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0'
testCompile 'junit:junit:4.12'
testCompile 'net.jodah:concurrentunit:0.4.2'
@@ -52,11 +46,6 @@ dependencies {
testCompile project(path: ':briar-tests')
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'org.robolectric:robolectric:3.0'
androidTestCompile 'org.mockito:mockito-core:1.10.19'
androidTestApt 'com.google.dagger:dagger-compiler:2.0.2'
androidTestCompile 'junit:junit:4.12'
}
dependencyVerification {
@@ -64,17 +53,20 @@ 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',
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f',
'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b',
'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a',
]
}
@@ -82,6 +74,10 @@ android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
resValue "string", "app_package", "org.briarproject"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
@@ -105,24 +101,16 @@ android {
unitTests.returnDefaultValues = true
}
}
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
// This moves them out of them default location under src/<type>/... which would
// conflict with src/ being used by the main source set.
// Adding new build types or product flavors should be accompanied
// by a similar customization.
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
buildTypes {
debug {
shrinkResources false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
@@ -134,10 +122,8 @@ android {
}
lintOptions {
abortOnError false
}
dexOptions {
incremental true
warning 'MissingTranslation'
warning 'ImpliedQuantity'
}
}

View File

@@ -4,17 +4,16 @@
-dontobfuscate
-verbose
-useuniqueclassmembernames
-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
#-optimizations !code/simplification/arithmetic,!field/*,!class/merging/*
# For comfortability in case we do obfuscate
# -renamesourcefileattribute SourceFile
-keepattributes SourceFile,LineNumberTable,*Annotation*,Signature, InnerClasses, EnclosingMethod
-keepattributes SourceFile, LineNumberTable, *Annotation*, Signature, InnerClasses, EnclosingMethod
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Application
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class com.android.vending.licensing.ILicensingService
-keepclasseswithmembers class * { native <methods>; }
-keepclasseswithmembers class * {
public <init> (android.content.Context, android.util.AttributeSet);
@@ -32,31 +31,41 @@
-keepclassmembers class * extends android.app.Activity {
public void *(android.view.View);
}
-keepclassmembers class * {
@com.google.inject.Inject <init>(...);
@com.google.inject.Inject <fields>;
}
# Just in case Roboguice events are used
-keepclassmembers class * {
void *(**On*Event);
}
-keep class org.h2.** { *; }
-keep class org.briarproject.** { *; }
-keep class com.google.inject.** { *; }
-keep class javax.inject.** { *; }
-keep class javax.annotation.** { *; }
-keep class roboguice.** { *; }
-keep class dagger.** { *; }
-keep class com.google.** { *; }
-keep class com.google.zxing.Result
-dontwarn org.h2.**
-dontnote org.h2.**
-dontwarn net.sf.cglib.**
-dontwarn org.briarproject.plugins.tcp.**
-dontwarn roboguice.**
-dontnote org.briarproject.crypto.**
-dontnote org.spongycastle.crypto.parsers.ECIESPublicKeyParser
-dontwarn net.sourceforge.jsocks.**
-dontnote android.support.**
-dontnote dagger.**
-dontwarn dagger.**
-dontwarn com.google.common.**
-dontnote com.google.common.**
-dontwarn com.google.common.**
# RSS libraries
-keep class com.rometools.rome.feed.synd.impl.** { *;}
-keep class com.rometools.rome.io.impl.** { *;}
-keep class org.jsoup.safety.Whitelist
-dontnote com.rometools.rome.**
-dontwarn javax.xml.stream.**
-dontwarn org.jaxen.**
-dontwarn java.nio.**
-dontwarn org.codehaus.mojo.animal_sniffer.**
-dontwarn org.slf4j.impl.**
# Emoji
-keep class org.thoughtcrime.securesms.**
-keep class com.astuetz.PagerSlidingTabStrip$OnTabReselectedListener
-keep public enum com.bumptech.glide.load.resource.bitmap.ImageHeaderParser$** {
**[] $VALUES;
public *;
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 975 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1009 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 366 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 2.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 28 KiB

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 210 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 362 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 708 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 531 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 175 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1014 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 27 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@@ -1,16 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
A FAB does not work, because even with fabSize="mini" it will be too big due to shadow drawing
on lower API levels
-->
<ripple
xmlns:android="http://schemas.android.com/apk/res/android"
android:color="@color/briar_primary_dark">
<item>
<shape android:shape="oval">
<solid android:color="@color/briar_primary"/>
</shape>
</item>
</ripple>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 853 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Some files were not shown because too many files have changed in this diff Show More