Compare commits

...

132 Commits

Author SHA1 Message Date
str4d
b3722ad840 Remove unused variable 2016-07-13 05:48:12 +00:00
str4d
a6de1f7144 Update conversation timestamps for outgoing messages 2016-07-13 05:14:20 +00:00
str4d
2698e4c181 Use a checked exception instead of a boolean to indicate an invalid readable msg 2016-07-13 05:14:20 +00:00
str4d
db52b2c29f Implement conversation data persistence 2016-07-13 05:14:20 +00:00
str4d
f750280845 Pass MessageId inside *InvitationReceivedEvent so we get the right one 2016-07-13 05:14:18 +00:00
str4d
1e8784efe9 Add a ConversationItemReceivedEvent 2016-07-13 05:03:37 +00:00
str4d
7a3bd86522 Use ConversationManager for timestamps and unread counts 2016-07-13 05:03:36 +00:00
str4d
c333052396 Wrap messages, introductions and forum invites in a ConversationManager 2016-07-13 05:02:52 +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
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
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
360 changed files with 16334 additions and 6050 deletions

View File

@@ -2,10 +2,6 @@ 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"

View File

@@ -0,0 +1,387 @@
package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.blogs.Blog;
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.KeyPair;
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.Test;
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.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.briarproject.api.sync.ValidationManager.State.VALID;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class BlogManagerTest {
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 final String CONTENT_TYPE = "text/plain";
private static final Logger LOG =
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
private BlogManagerTestComponent t0, t1;
@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 byte[] body = TestUtils.getRandomBytes(42);
Collection<BlogPostHeader> headers0 =
blogManager0.getPostHeaders(blog0.getId());
assertEquals(0, headers0.size());
// add a post to blog0
BlogPost p = blogPostFactory
.createBlogPost(blog0.getId(), null, clock.currentTimeMillis(),
null, author0, CONTENT_TYPE, 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
assertArrayEquals(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());
// check that body is there
assertArrayEquals(body,
blogManager1.getPostBody(p.getMessage().getId()));
stopLifecycles();
}
@Test
public void testBlogPostInWrongBlog() throws Exception {
startLifecycles();
defaultInit();
// add a post to blog1
final byte[] body = TestUtils.getRandomBytes(42);
BlogPost p = blogPostFactory
.createBlogPost(blog1.getId(), null, clock.currentTimeMillis(),
null, author0, CONTENT_TYPE, 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 testAddAndRemoveBlog() throws Exception {
startLifecycles();
defaultInit();
String name = "Test Blog";
String desc = "Description";
// add blog
Blog blog = blogManager0.addBlog(author0, name, desc);
Collection<Blog> blogs0 = blogManager0.getBlogs();
assertEquals(3, blogs0.size());
assertTrue(blogs0.contains(blog));
assertEquals(2, blogManager0.getBlogs(author0).size());
// remove blog
blogManager0.removeBlog(blog);
blogs0 = blogManager0.getBlogs();
assertEquals(2, blogs0.size());
assertFalse(blogs0.contains(blog));
assertEquals(1, blogManager0.getBlogs(author0).size());
stopLifecycles();
}
@After
public void tearDown() throws Exception {
TestUtils.deleteTestDirectory(testDir);
}
private class Listener implements EventListener {
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() == VALID ||
event.getState() == INVALID ||
event.getState() == PENDING) {
validationWaiter.resume();
}
}
}
}
}
private void defaultInit() throws DbException {
addDefaultIdentities();
addDefaultContacts();
listenToEvents();
}
private void addDefaultIdentities() throws DbException {
KeyPair keyPair0 = crypto.generateSignatureKeyPair();
byte[] publicKey0 = keyPair0.getPublic().getEncoded();
byte[] privateKey0 = keyPair0.getPrivate().getEncoded();
author0 = authorFactory
.createLocalAuthor(AUTHOR1, publicKey0, privateKey0);
identityManager0.addLocalAuthor(author0);
blog0 = blogFactory.createPersonalBlog(author0);
KeyPair keyPair1 = crypto.generateSignatureKeyPair();
byte[] publicKey1 = keyPair1.getPublic().getEncoded();
byte[] privateKey1 = keyPair1.getPrivate().getEncoded();
author1 = authorFactory
.createLocalAuthor(AUTHOR2, publicKey1, privateKey1);
identityManager1.addLocalAuthor(author1);
blog1 = blogFactory.createPersonalBlog(author1);
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
// invitee adds sharer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), 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();
lifecycleManager1.startServices();
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,447 @@
package org.briarproject;
import junit.framework.Assert;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.contact.Contact;
import org.briarproject.api.contact.ContactId;
import org.briarproject.api.contact.ContactManager;
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.briarproject.util.StringUtils;
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.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
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.briarproject.api.sync.ValidationManager.State.VALID;
import static org.junit.Assert.assertTrue;
public class ForumManagerTest {
private LifecycleManager lifecycleManager0, lifecycleManager1;
private SyncSessionFactory sync0, sync1;
private ForumManager forumManager0, forumManager1;
private ContactManager contactManager0, contactManager1;
private ContactId contactId0,contactId1;
private IdentityManager identityManager0, identityManager1;
private LocalAuthor author0, author1;
private Forum forum0;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@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();
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, ForumPost parent,
String body, long ms) throws Exception {
return forumPostFactory.createAnonymousPost(groupId, ms,
parent == null ? null : parent.getMessage().getId(),
"text/plain", StringUtils.toUtf8(body));
}
@Test
public void testForumPost() throws Exception {
startLifecycles();
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(post1.getMessage().getId(), true);
forumManager0.addLocalPost(post2);
forumManager0.setReadFlag(post2.getMessage().getId(), false);
Collection<ForumPostHeader> headers =
forumManager0.getPostHeaders(forum.getGroup().getId());
assertEquals(2, headers.size());
for (ForumPostHeader h : headers) {
final String hBody =
StringUtils.fromUtf8(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());
// send post to 1
sync0To1();
deliveryWaiter.await(TIMEOUT, 1);
assertEquals(1, forumManager1.getPostHeaders(g).size());
// add another forum post
time = clock.currentTimeMillis();
ForumPost post2 = createForumPost(g, null, "b", time);
forumManager1.addLocalPost(post2);
assertEquals(1, forumManager0.getPostHeaders(g).size());
assertEquals(2, forumManager1.getPostHeaders(g).size());
// send post to 0
sync1To0();
deliveryWaiter.await(TIMEOUT, 1);
assertEquals(2, forumManager1.getPostHeaders(g).size());
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 posts 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 {
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() == VALID ||
event.getState() == INVALID ||
event.getState() == PENDING) {
validationWaiter.resume();
}
}
}
}
}
private void defaultInit() throws DbException {
addDefaultIdentities();
addDefaultContacts();
addForum();
listenToEvents();
}
private void addDefaultIdentities() throws DbException {
author0 = authorFactory.createLocalAuthor(SHARER,
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
TestUtils.getRandomBytes(123));
identityManager0.addLocalAuthor(author0);
author1 = authorFactory.createLocalAuthor(INVITEE,
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
TestUtils.getRandomBytes(123));
identityManager1.addLocalAuthor(author1);
}
private void addDefaultContacts() throws DbException {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
// invitee adds sharer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), 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();
lifecycleManager1.startServices();
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,83 @@
package org.briarproject;
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.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.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,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
})
public 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();
ForumSharingManager getForumSharingManager();
ForumManager getForumManager();
SyncSessionFactory getSyncSessionFactory();
}

View File

@@ -2,12 +2,17 @@ package org.briarproject;
import net.jodah.concurrentunit.Waiter;
import org.briarproject.api.Bytes;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.clients.SessionId;
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.KeyParser;
import org.briarproject.api.crypto.PrivateKey;
import org.briarproject.api.crypto.SecretKey;
import org.briarproject.api.data.BdfList;
import org.briarproject.api.db.DatabaseComponent;
@@ -18,24 +23,30 @@ import org.briarproject.api.event.Event;
import org.briarproject.api.event.EventListener;
import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumInvitationMessage;
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.ClientId;
import org.briarproject.api.sync.Group;
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.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.junit.After;
@@ -49,6 +60,7 @@ 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;
@@ -57,33 +69,39 @@ import javax.inject.Inject;
import static org.briarproject.TestPluginsModule.MAX_LATENCY;
import static org.briarproject.api.forum.ForumConstants.FORUM_SALT_LENGTH;
import static org.briarproject.api.forum.ForumConstants.SHARE_MSG_TYPE_INVITATION;
import static org.briarproject.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.api.sharing.SharingConstants.SHARE_MSG_TYPE_INVITATION;
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 ForumSharingIntegrationTest extends BriarTestCase {
LifecycleManager lifecycleManager0, lifecycleManager1;
SyncSessionFactory sync0, sync1;
ForumManager forumManager0, forumManager1;
ContactManager contactManager0, contactManager1;
ContactId contactId0, contactId1;
IdentityManager identityManager0, identityManager1;
LocalAuthor author0, author1;
Forum forum0;
SharerListener listener0;
InviteeListener listener1;
private LifecycleManager lifecycleManager0, lifecycleManager1, lifecycleManager2;
private SyncSessionFactory sync0, sync1, sync2;
private ForumManager forumManager0, forumManager1;
private ContactManager contactManager0, contactManager1, contactManager2;
private ContactId contactId0, contactId2, contactId1, contactId21;
private IdentityManager identityManager0, identityManager1, identityManager2;
private LocalAuthor author0, author1, author2;
private Forum forum0;
private SharerListener listener0, listener2;
private InviteeListener listener1;
@Inject
Clock clock;
@Inject
AuthorFactory authorFactory;
@Inject
ForumPostFactory forumPostFactory;
@Inject
CryptoComponent cryptoComponent;
// objects accessed from background threads need to be volatile
private volatile ForumSharingManager forumSharingManager0;
private volatile ForumSharingManager forumSharingManager1;
private volatile ForumSharingManager forumSharingManager2;
private volatile Waiter eventWaiter;
private volatile Waiter msgWaiter;
@@ -92,11 +110,12 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
private final int TIMEOUT = 15000;
private final String SHARER = "Sharer";
private final String INVITEE = "Invitee";
private final String SHARER2 = "Sharer2";
private static final Logger LOG =
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
private ForumSharingIntegrationTestComponent t0, t1;
private ForumSharingIntegrationTestComponent t0, t1, t2;
@Rule
public ExpectedException thrown=ExpectedException.none();
@@ -117,17 +136,25 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
t1 = DaggerForumSharingIntegrationTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(t1Dir)).build();
injectEagerSingletons(t1);
File t2Dir = new File(testDir, SHARER2);
t2 = DaggerForumSharingIntegrationTestComponent.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();
forumManager0 = t0.getForumManager();
forumManager1 = t1.getForumManager();
forumSharingManager0 = t0.getForumSharingManager();
forumSharingManager1 = t1.getForumSharingManager();
forumSharingManager2 = t2.getForumSharingManager();
sync0 = t0.getSyncSessionFactory();
sync1 = t1.getSyncSessionFactory();
sync2 = t2.getSyncSessionFactory();
// initialize waiters fresh for each test
eventWaiter = new Waiter();
@@ -143,7 +170,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
@@ -156,13 +183,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getAvailableForums().size());
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(1, forumManager1.getForums().size());
// invitee has one invitation message from sharer
List<ForumInvitationMessage> list =
new ArrayList<>(forumSharingManager1
.getForumInvitationMessages(contactId0));
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
// check other things are alright with the forum message
ForumInvitationMessage invitation = list.get(0);
@@ -172,7 +199,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals("Hi!", invitation.getMessage());
// sharer has own invitation message
assertEquals(1,
forumSharingManager0.getForumInvitationMessages(contactId1)
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// forum can not be shared again
Contact c1 = contactManager0.getContact(contactId1);
@@ -193,7 +220,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, null);
.sendInvitation(forum0.getId(), contactId1, null);
// sync first request message
syncToInvitee();
@@ -206,15 +233,15 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was not added
assertEquals(0, forumSharingManager0.getAvailableForums().size());
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(0, forumManager1.getForums().size());
// forum is no longer available to invitee who declined
assertEquals(0, forumSharingManager1.getAvailableForums().size());
assertEquals(0, forumSharingManager1.getInvited().size());
// invitee has one invitation message from sharer
List<ForumInvitationMessage> list =
new ArrayList<>(forumSharingManager1
.getForumInvitationMessages(contactId0));
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
// check other things are alright with the forum message
ForumInvitationMessage invitation = list.get(0);
@@ -224,7 +251,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals(null, invitation.getMessage());
// sharer has own invitation message
assertEquals(1,
forumSharingManager0.getForumInvitationMessages(contactId1)
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// forum can be shared again
Contact c1 = contactManager0.getContact(contactId1);
@@ -243,7 +270,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
@@ -256,13 +283,14 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getAvailableForums().size());
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0));
// sharer shares forum with invitee
Contact c1 = contactManager0.getContact(contactId1);
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
.contains(contactId1));
.contains(c1));
// invitee gets forum shared by sharer
Contact contact0 = contactManager1.getContact(contactId1);
assertTrue(forumSharingManager1.getSharedBy(forum0.getId())
@@ -275,17 +303,16 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
syncToSharer();
// forum is gone
assertEquals(0, forumSharingManager0.getAvailableForums().size());
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(0, forumManager1.getForums().size());
// sharer no longer shares forum with invitee
assertFalse(forumSharingManager0.getSharedWith(forum0.getId())
.contains(contactId1));
.contains(c1));
// invitee no longer gets forum shared by sharer
assertFalse(forumSharingManager1.getSharedBy(forum0.getId())
.contains(contact0));
// forum can be shared again
Contact c1 = contactManager0.getContact(contactId1);
assertTrue(forumSharingManager0.canBeShared(forum0.getId(), c1));
Contact c0 = contactManager1.getContact(contactId0);
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
@@ -303,7 +330,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, null);
.sendInvitation(forum0.getId(), contactId1, null);
// sync first request message
syncToInvitee();
@@ -316,13 +343,14 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getAvailableForums().size());
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0));
// sharer shares forum with invitee
Contact c1 = contactManager0.getContact(contactId1);
assertTrue(forumSharingManager0.getSharedWith(forum0.getId())
.contains(contactId1));
.contains(c1));
// invitee gets forum shared by sharer
Contact contact0 = contactManager1.getContact(contactId1);
assertTrue(forumSharingManager1.getSharedBy(forum0.getId())
@@ -339,13 +367,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals(1, forumManager1.getForums().size());
// invitee no longer shares forum with sharer
Contact c0 = contactManager1.getContact(contactId0);
assertFalse(forumSharingManager1.getSharedWith(forum0.getId())
.contains(contactId0));
.contains(c0));
// sharer no longer gets forum shared by invitee
assertFalse(forumSharingManager1.getSharedBy(forum0.getId())
.contains(contact0));
// forum can be shared again
Contact c0 = contactManager1.getContact(contactId0);
assertTrue(forumSharingManager1.canBeShared(forum0.getId(), c0));
} finally {
stopLifecycles();
@@ -361,13 +389,14 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, null);
.sendInvitation(forum0.getId(), contactId1, null);
// sharer un-subscribes from forum
forumManager0.removeForum(forum0);
// from her on expect the response to fail with a DbException
thrown.expect(DbException.class);
// from here on expect the response to fail with an AssertionError,
// because there is in fact no invited forum available anymore
thrown.expect(AssertionError.class);
// sync first request message and leave message
syncToInvitee();
@@ -375,7 +404,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener1.requestReceived);
// invitee has no forums available
assertEquals(0, forumSharingManager1.getAvailableForums().size());
assertEquals(0, forumSharingManager1.getInvited().size());
} finally {
stopLifecycles();
}
@@ -390,7 +419,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
@@ -411,7 +440,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// get SessionId from invitation
List<ForumInvitationMessage> list = new ArrayList<>(
forumSharingManager1
.getForumInvitationMessages(contactId0));
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
ForumInvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
@@ -458,7 +487,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
@@ -474,7 +503,8 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertEquals(1, forumManager1.getForums().size());
// invitee now shares same forum back
forumSharingManager1.sendForumInvitation(forum0.getId(), contactId0,
forumSharingManager1.sendInvitation(forum0.getId(),
contactId0,
"I am re-sharing this forum with you.");
// sync re-share invitation
@@ -483,13 +513,82 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// make sure that no new request was received
assertFalse(listener0.requestReceived);
assertEquals(1,
forumSharingManager0.getForumInvitationMessages(contactId1)
forumSharingManager0.getInvitationMessages(contactId1)
.size());
} finally {
stopLifecycles();
}
}
@Test
public void testSharingSameForumWithEachOtherAtSameTime() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// invitee adds the same forum
DatabaseComponent db1 = t1.getDatabaseComponent();
Transaction txn = db1.startTransaction(false);
db1.addGroup(txn, forum0.getGroup());
txn.setComplete();
db1.endTransaction(txn);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// invitee now shares same forum back
forumSharingManager1.sendInvitation(forum0.getId(),
contactId0, "I am re-sharing this forum with you.");
// find out who should be Alice, because of random keys
Bytes key0 = new Bytes(author0.getPublicKey());
Bytes key1 = new Bytes(author1.getPublicKey());
// only now sync first request message
boolean alice = key1.compareTo(key0) < 0;
syncToInvitee();
if (alice) {
assertFalse(listener1.requestReceived);
} else {
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
}
// sync second invitation
alice = key0.compareTo(key1) < 0;
syncToSharer();
if (alice) {
assertFalse(listener0.requestReceived);
// sharer did not receive request, but response to own request
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
assertEquals(1, forumSharingManager0
.getInvitationMessages(contactId1).size());
assertEquals(2, forumSharingManager1
.getInvitationMessages(contactId0).size());
} else {
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.requestReceived);
// send response from sharer to invitee and make sure it arrived
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.responseReceived);
assertEquals(2, forumSharingManager0
.getInvitationMessages(contactId1).size());
assertEquals(1, forumSharingManager1
.getInvitationMessages(contactId0).size());
}
} finally {
stopLifecycles();
}
}
@Test
public void testContactRemoved() throws Exception {
startLifecycles();
@@ -499,7 +598,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// send invitation
forumSharingManager0
.sendForumInvitation(forum0.getId(), contactId1, "Hi!");
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
@@ -519,14 +618,16 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// remember SessionId from invitation
List<ForumInvitationMessage> list = new ArrayList<>(
forumSharingManager1
.getForumInvitationMessages(contactId0));
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
ForumInvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
// contacts now remove each other
contactManager0.removeContact(contactId1);
contactManager2.removeContact(contactId21);
contactManager1.removeContact(contactId0);
contactManager1.removeContact(contactId2);
// make sure sharer does share the forum with nobody now
assertEquals(0,
@@ -572,6 +673,189 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
}
}
@Test
public void testTwoContactsShareSameForum() throws Exception {
startLifecycles();
try {
// initialize
addDefaultIdentities();
addDefaultContacts();
addForumForSharer();
// second sharer adds the same forum
DatabaseComponent db2 = t2.getDatabaseComponent();
Transaction txn = db2.startTransaction(false);
db2.addGroup(txn, forum0.getGroup());
txn.setComplete();
db2.endTransaction(txn);
// add listeners
listener0 = new SharerListener();
t0.getEventBus().addListener(listener0);
listener1 = new InviteeListener(true, false);
t1.getEventBus().addListener(listener1);
listener2 = new SharerListener();
t2.getEventBus().addListener(listener2);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
// second sharer sends invitation for same forum
forumSharingManager2
.sendInvitation(forum0.getId(), contactId1, null);
// sync second request message
deliverMessage(sync2, contactId2, sync1, contactId1,
"Sharer2 to Invitee");
// make sure we now have two invitations to the same forum available
Collection<Forum> forums = forumSharingManager1.getInvited();
assertEquals(1, forums.size());
assertEquals(2,
forumSharingManager1.getSharedBy(forum0.getId()).size());
// make sure both sharers actually share the forum
Collection<Contact> contacts =
forumSharingManager1.getSharedBy(forum0.getId());
assertEquals(2, contacts.size());
// answer second request
Contact c2 = contactManager1.getContact(contactId2);
forumSharingManager1.respondToInvitation(forum0, c2, true);
// sync response
deliverMessage(sync1, contactId21, sync2, contactId2,
"Invitee to Sharer2");
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener2.responseReceived);
// answer first request
Contact c0 =
contactManager1.getContact(contactId0);
forumSharingManager1.respondToInvitation(forum0, c0, true);
// sync response
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
} finally {
stopLifecycles();
}
}
@Test
public void testSyncAfterReSharing() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
defaultInit(true);
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
// sharer posts into the forum
long time = clock.currentTimeMillis();
byte[] body = TestUtils.getRandomBytes(42);
KeyParser keyParser = cryptoComponent.getSignatureKeyParser();
PrivateKey key = keyParser.parsePrivateKey(author0.getPrivateKey());
ForumPost p = forumPostFactory
.createPseudonymousPost(forum0.getId(), time, null, author0,
"text/plain", body, key);
forumManager0.addLocalPost(p);
// sync forum post
syncToInvitee();
// make sure forum post arrived
Collection<ForumPostHeader> headers =
forumManager1.getPostHeaders(forum0.getId());
assertEquals(1, headers.size());
ForumPostHeader header = headers.iterator().next();
assertEquals(p.getMessage().getId(), header.getId());
assertEquals(author0, header.getAuthor());
// now invitee creates a post
time = clock.currentTimeMillis();
body = TestUtils.getRandomBytes(42);
key = keyParser.parsePrivateKey(author1.getPrivateKey());
p = forumPostFactory
.createPseudonymousPost(forum0.getId(), time, null, author1,
"text/plain", body, key);
forumManager1.addLocalPost(p);
// sync forum post
syncToSharer();
// make sure forum post arrived
headers = forumManager1.getPostHeaders(forum0.getId());
assertEquals(2, headers.size());
boolean found = false;
for (ForumPostHeader h : headers) {
if (p.getMessage().getId().equals(h.getId())) {
found = true;
assertEquals(author1, h.getAuthor());
}
}
assertTrue(found);
// contacts remove each other
contactManager0.removeContact(contactId1);
contactManager1.removeContact(contactId0);
contactManager1.removeContact(contactId2);
contactManager2.removeContact(contactId21);
// contacts add each other back
addDefaultContacts();
// send invitation again
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, "Hi!");
// sync first request message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
// sync response back
syncToSharer();
eventWaiter.await(TIMEOUT, 1);
// now invitee creates a post
time = clock.currentTimeMillis();
body = TestUtils.getRandomBytes(42);
key = keyParser.parsePrivateKey(author1.getPrivateKey());
p = forumPostFactory
.createPseudonymousPost(forum0.getId(), time, null, author1,
"text/plain", body, key);
forumManager1.addLocalPost(p);
// sync forum post
syncToSharer();
// make sure forum post arrived
headers = forumManager1.getPostHeaders(forum0.getId());
assertEquals(3, headers.size());
found = false;
for (ForumPostHeader h : headers) {
if (p.getMessage().getId().equals(h.getId())) {
found = true;
assertEquals(author1, h.getAuthor());
}
}
assertTrue(found);
} finally {
stopLifecycles();
}
}
@After
public void tearDown() throws InterruptedException {
@@ -580,18 +864,23 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
private class SharerListener implements EventListener {
public volatile boolean requestReceived = false;
public volatile boolean responseReceived = false;
volatile boolean requestReceived = false;
volatile boolean responseReceived = false;
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
.equals(forumSharingManager0.getClientId()) &&
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
ClientId c = event.getClientId();
if ((s == DELIVERED || s == INVALID) &&
c.equals(forumSharingManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Sharer received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
} else if (s == DELIVERED && !event.isLocal() &&
c.equals(forumManager0.getClientId())) {
LOG.info("TEST: Sharer received forum post");
msgWaiter.resume();
}
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
@@ -609,7 +898,8 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
requestReceived = true;
Forum f = event.getForum();
try {
forumSharingManager0.respondToInvitation(f, true);
Contact c = contactManager0.getContact(contactId1);
forumSharingManager0.respondToInvitation(f, c, true);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
@@ -621,36 +911,48 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
private class InviteeListener implements EventListener {
public volatile boolean requestReceived = false;
public volatile boolean responseReceived = false;
volatile boolean requestReceived = false;
volatile boolean responseReceived = false;
private final boolean accept;
private final boolean accept, answer;
InviteeListener(boolean accept, boolean answer) {
this.accept = accept;
this.answer = answer;
}
InviteeListener(boolean accept) {
this.accept = accept;
this(accept, true);
}
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
.equals(forumSharingManager1.getClientId()) &&
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
ClientId c = event.getClientId();
if ((s == DELIVERED || s == INVALID) &&
c.equals(forumSharingManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Invitee received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
} else if (s == DELIVERED && !event.isLocal() &&
c.equals(forumManager0.getClientId())) {
LOG.info("TEST: Invitee received forum post");
msgWaiter.resume();
}
} else if (e instanceof ForumInvitationReceivedEvent) {
ForumInvitationReceivedEvent event =
(ForumInvitationReceivedEvent) e;
eventWaiter.assertEquals(contactId0, event.getContactId());
requestReceived = true;
if (!answer) return;
Forum f = event.getForum();
// work-around because the forum does not contain the group
f = forumManager1.createForum(f.getName(), f.getSalt());
try {
forumSharingManager1.respondToInvitation(f, accept);
eventWaiter.assertEquals(1,
forumSharingManager1.getInvited().size());
Contact c =
contactManager1.getContact(event.getContactId());
forumSharingManager1.respondToInvitation(f, c, accept);
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
@@ -672,18 +974,23 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// Start the lifecycle manager and wait for it to finish
lifecycleManager0 = t0.getLifecycleManager();
lifecycleManager1 = t1.getLifecycleManager();
lifecycleManager2 = t2.getLifecycleManager();
lifecycleManager0.startServices();
lifecycleManager1.startServices();
lifecycleManager2.startServices();
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 {
@@ -694,14 +1001,23 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
}
private void addDefaultIdentities() throws DbException {
KeyPair keyPair = cryptoComponent.generateSignatureKeyPair();
author0 = authorFactory.createLocalAuthor(SHARER,
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
TestUtils.getRandomBytes(123));
keyPair.getPublic().getEncoded(),
keyPair.getPrivate().getEncoded());
identityManager0.addLocalAuthor(author0);
keyPair = cryptoComponent.generateSignatureKeyPair();
author1 = authorFactory.createLocalAuthor(INVITEE,
TestUtils.getRandomBytes(MAX_PUBLIC_KEY_LENGTH),
TestUtils.getRandomBytes(123));
keyPair.getPublic().getEncoded(),
keyPair.getPrivate().getEncoded());
identityManager1.addLocalAuthor(author1);
keyPair = cryptoComponent.generateSignatureKeyPair();
author2 = authorFactory.createLocalAuthor(SHARER2,
keyPair.getPublic().getEncoded(),
keyPair.getPrivate().getEncoded());
identityManager2.addLocalAuthor(author2);
}
private void addDefaultContacts() throws DbException {
@@ -710,17 +1026,25 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
// invitee adds sharer back
// second sharer does the same
contactId21 = contactManager2.addContact(author1,
author2.getId(), master, clock.currentTimeMillis(), true,
true
);
// invitee adds sharers back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
contactId2 = contactManager1.addContact(author2,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
}
private void addForumForSharer() throws DbException {
// sharer creates forum
forum0 = forumManager0.createForum("Test Forum");
forumManager0.addForum(forum0);
forum0 = forumManager0.addForum("Test Forum");
}
private void listenToEvents(boolean accept) {
@@ -728,6 +1052,8 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
t0.getEventBus().addListener(listener0);
listener1 = new InviteeListener(accept);
t1.getEventBus().addListener(listener1);
listener2 = new SharerListener();
t2.getEventBus().addListener(listener2);
}
private void syncToInvitee() throws IOException, TimeoutException {
@@ -773,6 +1099,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
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

@@ -21,6 +21,7 @@ import org.briarproject.forum.ForumModule;
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;
@@ -44,6 +45,7 @@ import dagger.Component;
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
@@ -62,6 +64,8 @@ public interface ForumSharingIntegrationTestComponent {
void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);

View File

@@ -19,7 +19,7 @@ import org.briarproject.api.event.IntroductionAbortedEvent;
import org.briarproject.api.event.IntroductionRequestReceivedEvent;
import org.briarproject.api.event.IntroductionResponseReceivedEvent;
import org.briarproject.api.event.IntroductionSucceededEvent;
import org.briarproject.api.event.MessageValidatedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
@@ -29,10 +29,13 @@ import org.briarproject.api.introduction.IntroductionRequest;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.properties.TransportProperties;
import org.briarproject.api.properties.TransportPropertyManager;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.MessageId;
import org.briarproject.api.sync.SyncSession;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.api.sync.ValidationManager;
import org.briarproject.api.sync.ValidationManager.State;
import org.briarproject.api.system.Clock;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
@@ -70,6 +73,8 @@ import static org.briarproject.api.introduction.IntroductionConstants.PUBLIC_KEY
import static org.briarproject.api.introduction.IntroductionConstants.SESSION_ID;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE;
import static org.briarproject.api.introduction.IntroductionConstants.TYPE_REQUEST;
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;
@@ -832,6 +837,106 @@ public class IntroductionIntegrationTest extends BriarTestCase {
}
}
@Test
public void testIntroduceesRemovedCleanup() throws Exception {
startLifecycles();
try {
// Add Identities
addDefaultIdentities();
// Add Transport Properties
addTransportProperties();
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
);
assertTrue(contactId0.equals(contactId02));
// listen to events
IntroducerListener listener0 = new IntroducerListener();
t0.getEventBus().addListener(listener0);
IntroduceeListener listener1 = new IntroduceeListener(1, true);
t1.getEventBus().addListener(listener1);
IntroduceeListener listener2 = new IntroduceeListener(2, true);
t2.getEventBus().addListener(listener2);
// make introduction
long time = clock.currentTimeMillis();
Contact introducee1 = contactManager0.getContact(contactId1);
Contact introducee2 = contactManager0.getContact(contactId2);
introductionManager0
.makeIntroduction(introducee1, introducee2, "Hi!", time);
// sync first request message
deliverMessage(sync0, contactId0, sync1, contactId1, "0 to 1");
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// get database and local group for introducee
DatabaseComponent db0 = t0.getDatabaseComponent();
IntroductionGroupFactory groupFactory0 =
t0.getIntroductionGroupFactory();
Group group1 = groupFactory0.createLocalGroup();
// get local session state messages
Map<MessageId, Metadata> map;
Transaction txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
txn.setComplete();
} finally {
db0.endTransaction(txn);
}
// check that we have one session state
assertEquals(1, map.size());
// introducer removes introducee1
contactManager0.removeContact(contactId1);
// get local session state messages again
txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
txn.setComplete();
} finally {
db0.endTransaction(txn);
}
// make sure local state is still there
assertEquals(1, map.size());
// introducer removes other introducee
contactManager0.removeContact(contactId2);
// get local session state messages again
txn = db0.startTransaction(false);
try {
map = db0.getMessageMetadata(txn, group1.getId());
txn.setComplete();
} finally {
db0.endTransaction(txn);
}
// make sure local state is gone now
assertEquals(0, map.size());
} finally {
stopLifecycles();
}
}
// TODO add a test for faking responses when #256 is implemented
@After
@@ -947,15 +1052,16 @@ public class IntroductionIntegrationTest extends BriarTestCase {
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
.equals(introductionManager0.getClientId()) &&
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
State s = event.getState();
ClientId c = event.getClientId();
if ((s == DELIVERED || s == INVALID) &&
c.equals(introductionManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Introducee" + introducee +
" received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
}
} else if (e instanceof IntroductionRequestReceivedEvent) {
@@ -1014,14 +1120,13 @@ public class IntroductionIntegrationTest extends BriarTestCase {
@Override
public void eventOccurred(Event e) {
if (e instanceof MessageValidatedEvent) {
MessageValidatedEvent event = (MessageValidatedEvent) e;
if (event.getClientId()
if (e instanceof MessageStateChangedEvent) {
MessageStateChangedEvent event = (MessageStateChangedEvent) e;
if (event.getState() == DELIVERED && event.getClientId()
.equals(introductionManager0.getClientId()) &&
!event.isLocal()) {
LOG.info("TEST: Introducer received message in group " +
((MessageValidatedEvent) e).getMessage()
.getGroupId().hashCode());
event.getMessage().getGroupId().hashCode());
msgWaiter.resume();
}
} else if (e instanceof IntroductionResponseReceivedEvent) {

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;
@@ -76,7 +75,7 @@ public class SyncIntegrationTest extends BriarTestCase {
streamNumber = 123;
// Create a group
ClientId clientId = new ClientId(TestUtils.getRandomId());
byte[] descriptor = new byte[MAX_GROUP_DESCRIPTOR_LENGTH];
byte[] descriptor = new byte[0];
Group group = groupFactory.createGroup(clientId, descriptor);
// Add two messages to the group
long timestamp = System.currentTimeMillis();

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
[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

@@ -67,7 +67,8 @@
<activity
android:name=".android.SetupActivity"
android:label="@string/setup_title">
android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize">
</activity>
<activity
@@ -99,8 +100,8 @@
</activity>
<activity
android:name=".android.forum.AvailableForumsActivity"
android:label="@string/available_forums_title"
android:name=".android.forum.ForumInvitationsActivity"
android:label="@string/forum_invitations_title"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -129,16 +130,6 @@
/>
</activity>
<activity
android:name=".android.forum.ReadForumPostActivity"
android:label="@string/app_name"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.forum.ShareForumActivity"
android:label="@string/forums_share_toolbar_header"
@@ -150,13 +141,12 @@
</activity>
<activity
android:name=".android.forum.WriteForumPostActivity"
android:label="@string/app_name"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="stateVisible">
android:name=".android.forum.ForumSharingStatusActivity"
android:label="@string/forum_sharing_status"
android:parentActivityName=".android.forum.ForumActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
android:value=".android.forum.ForumActivity"
/>
</activity>
@@ -216,6 +206,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,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

View File

@@ -6,11 +6,6 @@ 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')

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.5 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: 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: 1.4 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: 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: 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

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.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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M13,12h7v1.5h-7zM13,9.5h7L20,11h-7zM13,14.5h7L20,16h-7zM21,4L3,4c-1.1,0 -2,0.9 -2,2v13c0,1.1 0.9,2 2,2h18c1.1,0 2,-0.9 2,-2L23,6c0,-1.1 -0.9,-2 -2,-2zM21,19h-9L12,6h9v13z"/>
</vector>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="@dimen/unread_bubble_size"/>
<padding
android:left="@dimen/unread_bubble_padding_horizontal"
android:right="@dimen/unread_bubble_padding_horizontal"
android:bottom="1px"/>
<solid
android:color="@color/briar_primary"/>
<stroke
android:color="@color/briar_text_primary_inverse"
android:width="@dimen/avatar_border_width"/>
</shape>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners
android:radius="@dimen/unread_bubble_size"/>
<padding
android:left="@dimen/unread_bubble_padding_horizontal"
android:right="@dimen/unread_bubble_padding_horizontal"/>
<solid
android:color="@color/briar_gold"/>
<stroke
android:color="@color/briar_primary"
android:width="@dimen/avatar_border_width"/>
</shape>

View File

@@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="48.0"
android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M9.1,19.3l14.9,11.8l14.9,-11.8l-1.9,-2.4l-13,10.4l-13,-10.4z"/>
</vector>

View File

@@ -0,0 +1,4 @@
<vector android:height="24dp" android:viewportHeight="48.0"
android:viewportWidth="48.0" android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M38.9,28.7l-14.9,-11.8l-14.9,11.8l1.9,2.4l13,-10.4l13,10.4z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16,11c1.66,0 2.99,-1.34 2.99,-3S17.66,5 16,5c-1.66,0 -3,1.34 -3,3s1.34,3 3,3zM8,11c1.66,0 2.99,-1.34 2.99,-3S9.66,5 8,5C6.34,5 5,6.34 5,8s1.34,3 3,3zM8,13c-2.33,0 -7,1.17 -7,3.5L1,19h14v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5zM16,13c-0.29,0 -0.62,0.02 -0.97,0.05 1.16,0.84 1.97,1.97 1.97,3.45L17,19h6v-2.5c0,-2.33 -4.67,-3.5 -7,-3.5z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M21,6h-2v9L6,15v2c0,0.55 0.45,1 1,1h11l4,4L22,7c0,-0.55 -0.45,-1 -1,-1zM17,12L17,3c0,-0.55 -0.45,-1 -1,-1L3,2c-0.55,0 -1,0.45 -1,1v14l4,-4h10c0.55,0 1,-0.45 1,-1z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98s-0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.12,-0.22 -0.39,-0.3 -0.61,-0.22l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.23,-0.09 -0.49,0 -0.61,0.22l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98s0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.12,0.22 0.39,0.3 0.61,0.22l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.23,0.09 0.49,0 0.61,-0.22l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM12,15.5c-1.93,0 -3.5,-1.57 -3.5,-3.5s1.57,-3.5 3.5,-3.5 3.5,1.57 3.5,3.5 -1.57,3.5 -3.5,3.5z"/>
</vector>

View File

@@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:alpha="0.54"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M13,8.2l-1,-1 -4,4 -4,-4 -1,1 4,4 -4,4 1,1 4,-4 4,4 1,-1 -4,-4 4,-4zM19,1H9c-1.1,0 -2,0.9 -2,2v3h2V4h10v16H9v-2H7v3c0,1.1 0.9,2 2,2h10c1.1,0 2,-0.9 2,-2V3c0,-1.1 -0.9,-2 -2,-2z"/>
</vector>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android" android:shape="oval">
<solid android:color="@color/window_background"/>
<stroke
android:width="2dp"
android:color="@color/forum_discussion_nested_line"/>
</shape>

View File

@@ -1,13 +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
-->
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item>
<shape android:shape="oval">
<solid android:color="@color/briar_primary"/>
</shape>
</item>
</selector>

View File

@@ -0,0 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/chevron48dp_down"/>
<item android:drawable="@drawable/chevron48dp_up"/>
</selector>

View File

@@ -1,6 +1,6 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:width="38dp"
android:height="38dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path
android:fillColor="#b7b7b7"
android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
L10.5752,8.38194 L10.5752,11.8208 Z"/>
<path
android:fillColor="#b7b7b7"
android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
L26.0752,8.38194 L26.0752,11.8208 Z"/>
<path
android:fillColor="#b7b7b7"
android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
L41.5752,8.38194 L41.5752,11.8208 Z"/>
</vector>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path
android:fillColor="#c34032"
android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
L10.5752,8.38194 L10.5752,11.8208 Z"/>
<path
android:fillColor="#b7b7b7"
android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
L26.0752,8.38194 L26.0752,11.8208 Z"/>
<path
android:fillColor="#b7b7b7"
android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
L41.5752,8.38194 L41.5752,11.8208 Z"/>
</vector>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path
android:fillColor="#fcd53a"
android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
L10.5752,8.38194 L10.5752,11.8208 Z"/>
<path
android:fillColor="#fcd53a"
android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
L26.0752,8.38194 L26.0752,11.8208 Z"/>
<path
android:fillColor="#b7b7b7"
android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
L41.5752,8.38194 L41.5752,11.8208 Z"/>
</vector>

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path
android:fillColor="#7fac49"
android:pathData="M15.5002,13.8797 L15.5002,11.8208 L12.9502,11.8208 L12.9502,8.38194
L15.5002,8.38194 L15.5002,6.32312 L12.9502,6.32312 L12.9502,2.49959
L10.5752,2.49959 L10.5752,6.32312 L7.42514,6.32312 L7.42514,2.49959
L5.05014,2.49959 L5.05014,6.32312 L2.50016,6.32312 L2.50016,8.38194
L5.05014,8.38194 L5.05014,11.8208 L2.50016,11.8208 L2.50016,13.8797
L5.05014,13.8797 L5.05014,17.4996 L7.42514,17.4996 L7.42514,13.8797
L10.5752,13.8797 L10.5752,17.4996 L12.9502,17.4996 L12.9502,13.8797
L15.5002,13.8797 Z M10.5752,11.8208 L7.42514,11.8208 L7.42514,8.38194
L10.5752,8.38194 L10.5752,11.8208 Z"/>
<path
android:fillColor="#7fac49"
android:pathData="M31.0002,13.8797 L31.0002,11.8208 L28.4502,11.8208 L28.4502,8.38194
L31.0002,8.38194 L31.0002,6.32312 L28.4502,6.32312 L28.4502,2.49959
L26.0752,2.49959 L26.0752,6.32312 L22.9251,6.32312 L22.9251,2.49959
L20.5501,2.49959 L20.5501,6.32312 L18.0002,6.32312 L18.0002,8.38194
L20.5501,8.38194 L20.5501,11.8208 L18.0002,11.8208 L18.0002,13.8797
L20.5501,13.8797 L20.5501,17.4996 L22.9251,17.4996 L22.9251,13.8797
L26.0752,13.8797 L26.0752,17.4996 L28.4502,17.4996 L28.4502,13.8797
L31.0002,13.8797 Z M26.0752,11.8208 L22.9251,11.8208 L22.9251,8.38194
L26.0752,8.38194 L26.0752,11.8208 Z"/>
<path
android:fillColor="#7fac49"
android:pathData="M46.5002,13.8797 L46.5002,11.8208 L43.9502,11.8208 L43.9502,8.38194
L46.5002,8.38194 L46.5002,6.32312 L43.9502,6.32312 L43.9502,2.49959
L41.5752,2.49959 L41.5752,6.32312 L38.4251,6.32312 L38.4251,2.49959
L36.0501,2.49959 L36.0501,6.32312 L33.5002,6.32312 L33.5002,8.38194
L36.0501,8.38194 L36.0501,11.8208 L33.5002,11.8208 L33.5002,13.8797
L36.0501,13.8797 L36.0501,17.4996 L38.4251,17.4996 L38.4251,13.8797
L41.5752,13.8797 L41.5752,17.4996 L43.9502,17.4996 L43.9502,13.8797
L46.5002,13.8797 Z M41.5752,11.8208 L38.4251,11.8208 L38.4251,8.38194
L41.5752,8.38194 L41.5752,11.8208 Z"/>
</vector>

View File

@@ -38,7 +38,8 @@
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/your_nickname"/>
android:text="@string/your_nickname"
android:visibility="gone"/>
<Spinner
android:id="@+id/spinner"
@@ -46,7 +47,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/spinner_border"
android:spinnerMode="dropdown"/>
android:spinnerMode="dropdown"
android:visibility="gone"/>
<TextView
android:id="@+id/faceToFaceView"

View File

@@ -38,7 +38,8 @@
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/your_nickname"/>
android:text="@string/your_nickname"
android:visibility="gone"/>
<Spinner
android:id="@+id/spinner"
@@ -46,7 +47,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/spinner_border"
android:spinnerMode="dropdown"/>
android:spinnerMode="dropdown"
android:visibility="gone"/>
<TextView
android:id="@+id/faceToFaceView"

View File

@@ -0,0 +1,128 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".android.ChangePasswordActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingLeft="@dimen/margin_activity_horizontal"
android:paddingRight="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
android:id="@+id/current_password_title"
style="@style/BriarTextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="@string/current_password"
android:textSize="@dimen/text_size_medium"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/current_password_entry_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/current_password_title"
android:layout_centerHorizontal="true"
app:errorEnabled="true">
<EditText
android:id="@+id/current_password_entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/new_password_title"
style="@style/BriarTextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/current_password_entry_wrapper"
android:layout_centerHorizontal="true"
android:text="@string/choose_new_password"
android:textSize="@dimen/text_size_medium"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/new_password_entry_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/new_password_title"
android:layout_centerHorizontal="true"
app:errorEnabled="true">
<EditText
android:id="@+id/new_password_entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/new_password_confirm_title"
style="@style/BriarTextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/new_password_entry_wrapper"
android:layout_centerHorizontal="true"
android:text="@string/confirm_new_password"
android:textSize="@dimen/text_size_medium"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/new_password_confirm_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/new_password_confirm_title"
android:layout_centerHorizontal="true"
app:errorEnabled="true">
<EditText
android:id="@+id/new_password_confirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<org.briarproject.android.util.StrengthMeter
android:id="@+id/strength_meter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/new_password_confirm_wrapper"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
<Button
android:id="@+id/change_password"
style="@style/BriarButton.Default"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/strength_meter"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/margin_medium"
android:enabled="false"
android:text="@string/change_password"/>
<ProgressBar
android:id="@+id/progress_wheel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/change_password"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
</RelativeLayout>
</ScrollView>

View File

@@ -46,33 +46,9 @@
<View style="@style/Divider.Horizontal"/>
<LinearLayout
<include
layout="@layout/text_input_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/button_bar_background"
android:orientation="horizontal"
android:paddingLeft="@dimen/margin_medium"
android:paddingStart="@dimen/margin_medium">
<EditText
android:id="@+id/contentView"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:hint="@string/private_message_hint"
android:inputType="text|textMultiLine|textCapSentences"/>
<ImageButton
android:id="@+id/sendButton"
android:layout_width="38dp"
android:layout_height="38dp"
android:layout_margin="@dimen/margin_small"
android:background="@drawable/round_button"
android:src="@drawable/social_send_now_white"
android:contentDescription="@string/send"
android:elevation="@dimen/margin_tiny"
/>
</LinearLayout>
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_activity_horizontal">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="@string/choose_nickname"
android:textSize="@dimen/text_size_large"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/nicknameInputLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<EditText
android:id="@+id/nicknameEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:ems="10"
android:inputType="textPersonName"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/createIdentityButton"
style="@style/BriarButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:enabled="false"
android:text="@string/create_identity_button"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:indeterminate="true"
android:visibility="gone"/>
</LinearLayout>

View File

@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="@string/expiry_warning"
android:textSize="@dimen/text_size_large"/>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<org.briarproject.android.util.BriarRecyclerView
android:id="@+id/forum_discussion_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:scrollToEnd="false"/>
<include
layout="@layout/text_input_field"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>

View File

@@ -0,0 +1,48 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/default_separator_inverted"
android:padding="@dimen/margin_medium"
android:text="@string/forum_shared_by"
android:textSize="@dimen/text_size_large"/>
<View style="@style/Divider.ForumList"/>
<org.briarproject.android.util.BriarRecyclerView
android:id="@+id/sharedByView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_medium"
android:paddingTop="@dimen/margin_medium"/>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/default_separator_inverted"
android:padding="@dimen/margin_medium"
android:text="@string/forum_shared_with"
android:textSize="@dimen/text_size_large"/>
<View style="@style/Divider.ForumList"/>
<org.briarproject.android.util.BriarRecyclerView
android:id="@+id/sharedWithView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_medium"
android:paddingTop="@dimen/margin_medium"/>
</LinearLayout>
</ScrollView>

View File

@@ -13,7 +13,7 @@
<include
android:id="@+id/navigation_menu_drawer"
layout="@layout/navigation_menu"
android:layout_width="@dimen/nav_drawer_width"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"/>

View File

@@ -2,8 +2,10 @@
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
tools:context=".android.SetupActivity">
<RelativeLayout
android:layout_width="match_parent"
@@ -17,109 +19,93 @@
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
android:id="@+id/nickname_title"
style="@style/BriarTextTitle"
android:layout_width="wrap_content"
android:id="@+id/setup_explanation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="@string/choose_nickname"
android:textSize="@dimen/text_size_medium"/>
android:text="@string/setup_explanation"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/nickname_entry_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/setup_explanation"
android:layout_centerHorizontal="true"
android:layout_below="@id/nickname_title"
android:layout_marginTop="@dimen/margin_medium"
app:errorEnabled="true">
<EditText
<android.support.design.widget.TextInputEditText
android:id="@+id/nickname_entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/choose_nickname"
android:imeOptions="actionNext"
android:inputType="text|textCapWords"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/password_title"
style="@style/BriarTextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/nickname_entry_wrapper"
android:text="@string/choose_password"
android:textSize="@dimen/text_size_medium"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/password_entry_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/nickname_entry_wrapper"
android:layout_centerHorizontal="true"
android:layout_below="@id/password_title"
app:errorEnabled="true">
<EditText
<android.support.design.widget.TextInputEditText
android:id="@+id/password_entry"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/choose_password"
android:imeOptions="actionNext"
android:inputType="textPassword"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<TextView
android:id="@+id/password_confirm_title"
style="@style/BriarTextTitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/password_entry_wrapper"
android:text="@string/confirm_password"
android:textSize="@dimen/text_size_medium"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/password_confirm_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_below="@id/password_confirm_title"
app:errorEnabled="true">
<EditText
android:id="@+id/password_confirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textPassword"
android:imeOptions="actionDone"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<org.briarproject.android.util.StrengthMeter
android:id="@+id/strength_meter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/password_entry_wrapper"
android:layout_centerHorizontal="true"
android:layout_below="@id/password_confirm_wrapper"
android:visibility="invisible"/>
android:layout_marginBottom="@dimen/margin_medium"
android:visibility="gone"/>
<android.support.design.widget.TextInputLayout
android:id="@+id/password_confirm_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/strength_meter"
android:layout_centerHorizontal="true"
app:errorEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="@+id/password_confirm"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/confirm_password"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/create_account"
style="@style/BriarButton.Default"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/password_confirm_wrapper"
android:layout_centerHorizontal="true"
android:layout_below="@id/strength_meter"
android:layout_marginTop="@dimen/margin_medium"
android:enabled="false"
android:text="@string/create_account_button"/>
<ProgressBar
android:id="@+id/progress_wheel"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/create_account"
android:layout_centerHorizontal="true"
android:visibility="invisible" />
android:visibility="invisible"/>
</RelativeLayout>

View File

@@ -1,45 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
app:civ_border_width="@dimen/avatar_border_width"
app:civ_border_color="@color/briar_primary"/>
<ImageView
android:id="@+id/statusView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:src="@drawable/identity_anonymous"/>
<TextView
android:id="@+id/nameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginEnd="@dimen/margin_small"
android:layout_marginLeft="@dimen/listitem_text_left_margin"
android:layout_marginRight="@dimen/margin_small"
android:layout_marginStart="@dimen/listitem_text_left_margin"
android:layout_toLeftOf="@id/statusView"
android:layout_toStartOf="@id/statusView"
android:ellipsize="end"
android:singleLine="true"
android:text="@string/anonymous"
android:textSize="@dimen/text_size_medium"/>
</RelativeLayout>

View File

@@ -24,8 +24,10 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:padding="@dimen/margin_activity_horizontal"
android:text="@string/no_data"
android:textSize="@dimen/text_size_large"
android:text="@string/no_data"/>
tools:text="@string/no_contacts"/>
</RelativeLayout>

View File

@@ -9,11 +9,10 @@
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/contactAvatar"
style="@style/BriarAvatar"
android:layout_width="30dp"
android:layout_height="30dp"
android:transitionName="avatar"
app:civ_border_color="@color/action_bar_text"
app:civ_border_width="@dimen/avatar_border_width"
tools:src="@drawable/ic_launcher"/>
<ImageView
@@ -22,7 +21,7 @@
android:layout_height="15dp"
android:layout_gravity="bottom|right"
android:scaleType="fitCenter"
tools:src="@drawable/contact_online"
tools:ignore="ContentDescription"/>
tools:ignore="ContentDescription"
tools:src="@drawable/contact_online"/>
</FrameLayout>

View File

@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -9,11 +8,11 @@
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
style="@style/BriarAvatar"
android:layout_width="@dimen/dropdown_picture_size"
android:layout_height="@dimen/dropdown_picture_size"
android:layout_margin="@dimen/margin_small"
app:civ_border_color="@color/briar_primary"
app:civ_border_width="@dimen/avatar_border_width"/>
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/nameView"

View File

@@ -0,0 +1,193 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/forum_cell"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
<View
android:id="@+id/nested_line_1"
style="@style/DiscussionLevelIndicator"
android:layout_width="@dimen/forum_nested_line_width"
android:layout_height="match_parent"
android:visibility="gone"
tools:visibility="visible"/>
<View
android:id="@+id/nested_line_2"
style="@style/DiscussionLevelIndicator"
android:layout_width="@dimen/forum_nested_line_width"
android:layout_height="match_parent"
android:layout_toRightOf="@id/nested_line_1"
android:visibility="gone"/>
<View
android:id="@+id/nested_line_3"
style="@style/DiscussionLevelIndicator"
android:layout_width="@dimen/forum_nested_line_width"
android:layout_height="match_parent"
android:layout_toRightOf="@id/nested_line_2"
android:visibility="gone"/>
<View
android:id="@+id/nested_line_4"
style="@style/DiscussionLevelIndicator"
android:layout_width="@dimen/forum_nested_line_width"
android:layout_height="match_parent"
android:layout_toRightOf="@id/nested_line_3"
android:visibility="gone"/>
<View
android:id="@+id/nested_line_5"
style="@style/DiscussionLevelIndicator"
android:layout_width="@dimen/forum_nested_line_width"
android:layout_height="match_parent"
android:layout_toRightOf="@id/nested_line_4"
android:visibility="gone"/>
<TextView
android:id="@+id/nested_line_text"
android:layout_width="@dimen/forum_nested_indicator"
android:layout_height="@dimen/forum_nested_indicator"
android:layout_centerInParent="true"
android:background="@drawable/level_indicator_circle"
android:gravity="center"
android:textSize="@dimen/text_size_small"
android:visibility="gone"
/>
</RelativeLayout>
<RelativeLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_weight="1">
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_small"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:layout_marginTop="@dimen/margin_medium"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textColor="@color/briar_text_primary"
tools:text="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua."/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
android:layout_width="@dimen/forum_avatar_size"
android:layout_height="@dimen/forum_avatar_size"
android:layout_alignLeft="@id/text"
android:layout_below="@id/text"
android:layout_marginRight="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_small"
android:src="@drawable/ic_launcher"
app:civ_border_color="@color/briar_primary"
app:civ_border_width="@dimen/avatar_border_width"
tools:src="@drawable/ic_launcher"
/>
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toRightOf="@id/avatar"
android:layout_alignBottom="@+id/avatar"
android:layout_alignTop="@+id/avatar"
android:gravity="center"
android:ellipsize="end"
android:maxLines="1"
android:textSize="@dimen/text_size_tiny"
tools:text="John Smith"/>
<org.briarproject.android.util.TrustIndicatorView
android:id="@+id/trustIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/avatar"
android:layout_alignTop="@+id/avatar"
android:scaleType="center"
android:layout_marginLeft="@dimen/margin_small"
android:layout_marginStart="@dimen/margin_small"
android:layout_toRightOf="@+id/author"
tools:src="@drawable/trust_indicator_verified"/>
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/avatar"
android:layout_alignTop="@+id/avatar"
android:gravity="center"
android:layout_toRightOf="@+id/trustIndicator"
android:layout_marginLeft="@dimen/margin_small"
android:layout_marginStart="@dimen/margin_small"
android:ellipsize="end"
android:maxLines="1"
android:textSize="@dimen/text_size_tiny"
tools:text="09:09"/>
<ImageView
android:id="@+id/chevron"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@id/text"
android:layout_marginRight="@dimen/margin_medium"
android:layout_marginTop="@dimen/margin_small"
android:clickable="true"
android:src="@drawable/selector_chevron"
android:tint="@color/briar_button_positive"
/>
<TextView
android:id="@+id/btn_reply"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/text"
android:layout_marginRight="@dimen/margin_medium"
android:layout_toLeftOf="@id/chevron"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:padding="@dimen/margin_medium"
android:text="@string/btn_reply"
android:textColor="@color/briar_button_positive"
android:textSize="@dimen/text_size_tiny"/>
<TextView
android:id="@+id/replies"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="@id/btn_reply"
android:layout_toLeftOf="@id/btn_reply"
android:layout_toRightOf="@+id/date"
android:gravity="right|end"
android:maxLines="1"
android:padding="@dimen/margin_medium"
android:textSize="@dimen/text_size_tiny"
tools:text="2 replies"/>
<View
android:id="@+id/top_divider"
style="@style/Divider.ForumList"
android:layout_width="match_parent"
android:layout_height="@dimen/margin_separator"
android:layout_alignLeft="@id/text"
android:layout_alignParentTop="true"/>
</RelativeLayout>
</LinearLayout>

View File

@@ -0,0 +1,46 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.TabLayout
android:id="@+id/tabLayout"
style="@style/BriarTabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/blogs_feed"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/blogs_my_blogs"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/blogs_blog_list"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/blogs_available_blogs"/>
<android.support.design.widget.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/blogs_drafts"/>
</android.support.design.widget.TabLayout>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This is just a placeholder to be replaced by the real My Blogs list -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="@+id/num"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:padding="@dimen/margin_activity_horizontal"
android:textSize="128sp"
tools:text="1"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="@dimen/margin_activity_horizontal"
android:text="There is nothing for you to see here.\n\nMove along and come back later."
android:textSize="@dimen/text_size_large"/>
</LinearLayout>

View File

@@ -1,27 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.briarproject.android.util.BriarRecyclerView
<org.briarproject.android.util.BriarRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/contactList"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/addContactFAB"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="@dimen/margin_activity_horizontal"
android:src="@drawable/ic_add_white"
app:fabSize="normal"
app:elevation="4dp"
app:layout_anchor="@id/contactList"
app:layout_anchorGravity="bottom|right|end"
app:layout_behavior="org.briarproject.android.util.HideFabOnScrollBehavior"/>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -20,7 +20,8 @@
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/your_nickname"/>
android:text="@string/your_nickname"
android:visibility="gone"/>
<Spinner
android:id="@+id/spinner"
@@ -28,7 +29,8 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/spinner_border"
android:spinnerMode="dropdown"/>
android:spinnerMode="dropdown"
android:visibility="gone"/>
<ImageView
android:id="@+id/imageView"

View File

@@ -24,6 +24,7 @@
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact1"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_centerHorizontal="true"
@@ -31,8 +32,6 @@
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toLeftOf="@+id/introductionIcon"
android:layout_toStartOf="@+id/introductionIcon"
app:civ_border_color="@color/briar_primary"
app:civ_border_width="@dimen/avatar_border_width"
tools:src="@drawable/ic_launcher"/>
<ImageView
@@ -45,6 +44,7 @@
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact2"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_centerHorizontal="true"
@@ -53,8 +53,6 @@
android:layout_toEndOf="@+id/introductionIcon"
android:layout_toRightOf="@+id/introductionIcon"
android:transitionName="avatar"
app:civ_border_color="@color/briar_primary"
app:civ_border_width="@dimen/avatar_border_width"
tools:src="@drawable/ic_launcher"/>
</RelativeLayout>
@@ -75,6 +73,7 @@
android:layout_weight="1"
android:gravity="top"
android:textSize="@dimen/text_size_medium"
android:textColor="@color/briar_text_primary"
tools:text="@string/introduction_message_text"/>
<EditText

View File

@@ -47,6 +47,7 @@
android:layout_height="wrap_content"
android:padding="@dimen/margin_medium"
android:textSize="50sp"
android:textColor="@color/briar_text_secondary"
android:layout_below="@+id/yourConfirmationCodeView"
android:layout_centerHorizontal="true"
tools:text="1337"/>

View File

@@ -33,6 +33,7 @@
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:textSize="50sp"
android:textColor="@color/briar_text_secondary"
android:layout_below="@+id/yourCodeView"
android:layout_centerHorizontal="true"
tools:text="1337"/>

View File

@@ -20,7 +20,8 @@
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/your_nickname"/>
android:text="@string/your_nickname"
android:visibility="gone"/>
<Spinner
android:id="@+id/spinner"
@@ -28,7 +29,8 @@
android:layout_height="wrap_content"
android:background="@drawable/spinner_border"
android:layout_marginTop="@dimen/margin_medium"
android:spinnerMode="dropdown"/>
android:spinnerMode="dropdown"
android:visibility="gone"/>
<ImageView
android:id="@+id/imageView"

View File

@@ -16,6 +16,7 @@
android:text="@string/connection_failed"
android:layout_gravity="center_horizontal"
android:textSize="@dimen/text_size_large"
android:textColor="@color/briar_text_primary"
android:drawableStart="@drawable/alerts_and_states_error"
android:drawableLeft="@drawable/alerts_and_states_error"
android:gravity="center_vertical"
@@ -26,6 +27,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/interfering"
android:textColor="@color/briar_text_primary"
android:layout_gravity="center_horizontal"
android:padding="@dimen/margin_medium"/>

View File

@@ -6,13 +6,13 @@
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/listitem_horizontal_margin"
android:background="?attr/selectableItemBackground">
android:background="?attr/selectableItemBackground"
android:paddingTop="@dimen/listitem_horizontal_margin">
<org.briarproject.android.util.TextAvatarView
android:id="@+id/avatarView"
android:layout_width="@dimen/avatar_forum_size"
android:layout_height="@dimen/avatar_forum_size"
android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
@@ -22,10 +22,12 @@
android:id="@+id/forumNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:maxLines="2"
android:textColor="@android:color/primary_text_light"
android:textColor="@color/briar_primary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a forum that is available"/>
@@ -34,38 +36,55 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/forumNameView"
android:layout_marginBottom="-8dp"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:paddingTop="@dimen/margin_medium"
android:textColor="@android:color/secondary_text_light"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="Shared by Megalox"/>
<TextView
android:id="@+id/forumSubscribedView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/sharedByView"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/margin_medium"
android:text="@string/forum_invitation_exists"
android:textColor="@color/briar_text_tertiary"
android:textSize="@dimen/text_size_small"
tools:visibility="visible"/>
<Button
android:id="@+id/acceptButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_button_accept"
android:layout_below="@+id/sharedByView"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
android:layout_below="@+id/forumSubscribedView"
android:layout_marginTop="-8dp"
android:text="@string/dialog_button_accept"/>
<Button
android:id="@+id/declineButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_button_decline"
android:layout_below="@+id/sharedByView"
android:layout_below="@+id/forumSubscribedView"
android:layout_marginTop="-8dp"
android:layout_toLeftOf="@+id/acceptButton"
android:layout_toStartOf="@+id/acceptButton"/>
android:layout_toStartOf="@+id/acceptButton"
android:text="@string/dialog_button_decline"/>
<View style="@style/Divider.ForumList"
android:layout_below="@+id/acceptButton"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<View
style="@style/Divider.ForumList"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/acceptButton"/>
</RelativeLayout>

View File

@@ -10,43 +10,62 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="@dimen/listitem_horizontal_margin"
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:background="?attr/selectableItemBackground"
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/listitem_horizontal_margin"
>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
<FrameLayout
android:id="@+id/avatarFrameView"
android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:transitionName="avatar"
app:civ_border_color="@color/briar_primary"
app:civ_border_width="@dimen/avatar_border_width"
tools:src="@drawable/ic_launcher"/>
android:layout_marginStart="@dimen/listitem_horizontal_margin">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_gravity="bottom|left"
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/unreadCountView"
android:layout_width="wrap_content"
android:layout_height="@dimen/unread_bubble_size"
android:layout_gravity="right|top"
android:background="@drawable/bubble"
android:gravity="center"
android:minWidth="@dimen/unread_bubble_size"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/unread_bubble_text_size"
android:textStyle="bold"
tools:text="123"/>
</FrameLayout>
<LinearLayout
android:id="@+id/textViews"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:layout_toEndOf="@+id/avatarFrameView"
android:layout_toLeftOf="@+id/bulbView"
android:layout_toRightOf="@+id/avatarView"
android:layout_toEndOf="@+id/avatarView">
android:layout_toRightOf="@+id/avatarFrameView"
android:orientation="vertical">
<TextView
android:id="@+id/nameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:textColor="@android:color/primary_text_light"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a contact"/>
@@ -54,7 +73,7 @@
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="@android:color/secondary_text_light"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="Dec 24"/>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingBottom="@dimen/margin_small"
android:paddingTop="@dimen/margin_small">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size_small"
android:layout_height="@dimen/listitem_picture_size_small"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/nameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:maxLines="2"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a contact"/>
</LinearLayout>

View File

@@ -19,9 +19,11 @@
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_height="match_parent"
android:layout_marginBottom="@dimen/margin_small"
android:gravity="bottom"
android:textSize="@dimen/text_size_large"
android:textColor="@color/briar_text_primary"
tools:text="Crash log entry title"/>
</LinearLayout>
@@ -30,6 +32,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/margin_medium"
android:textColor="@color/briar_text_secondary"
tools:text="Crash log entry value"/>
</LinearLayout>

View File

@@ -1,68 +1,68 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/listitem_horizontal_margin"
android:background="?attr/selectableItemBackground">
<org.briarproject.android.util.TextAvatarView
android:id="@+id/avatarView"
android:layout_height="@dimen/avatar_forum_size"
android:layout_width="@dimen/avatar_forum_size"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
/>
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/listitem_horizontal_margin"/>
<TextView
android:id="@+id/forumNameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_marginTop="@dimen/listitem_horizontal_margin"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:maxLines="2"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a forum"
android:layout_alignParentTop="true"
android:layout_toRightOf="@+id/avatarView"
android:layout_toEndOf="@+id/avatarView"/>
tools:text="This is a name of a forum"/>
<TextView
android:id="@+id/unreadView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingTop="@dimen/margin_medium"
android:layout_below="@+id/forumNameView"
android:layout_toEndOf="@+id/avatarView"
android:layout_toRightOf="@+id/avatarView"
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/margin_medium"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
android:text="@string/no_unread_posts"
android:layout_below="@+id/forumNameView"
android:layout_toRightOf="@+id/avatarView"
android:layout_toEndOf="@+id/avatarView"/>
tools:text="1337 posts"/>
<TextView
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_below="@+id/forumNameView"
android:paddingTop="@dimen/margin_medium"
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/margin_medium"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="Dec 24"/>
<View style="@style/Divider.ForumList"
android:layout_below="@+id/unreadView"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"/>
<View
style="@style/Divider.ForumList"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/unreadView"/>
</RelativeLayout>

View File

@@ -27,6 +27,7 @@
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
android:textColor="@color/briar_text_secondary"
tools:text="@string/forum_invitation_received"/>
<TextView
@@ -50,7 +51,7 @@
android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/introductionText"
android:text="@string/forum_show_available"/>
android:text="@string/forum_show_invitations"/>
</RelativeLayout>

View File

@@ -26,6 +26,7 @@
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
android:textColor="@color/briar_text_secondary"
tools:text="@string/introduction_request_received"/>
<TextView

View File

@@ -27,6 +27,7 @@
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
android:textColor="@color/briar_text_secondary"
tools:text="@string/introduction_request_received"/>
<TextView

View File

@@ -26,6 +26,7 @@
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
android:textColor="@color/briar_text_secondary"
tools:text="@string/introduction_request_received"/>
<TextView

View File

@@ -16,6 +16,7 @@
android:layout_height="wrap_content"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textColor="@color/briar_text_primary"
tools:text="Short message"/>
<TextView

View File

@@ -17,6 +17,7 @@
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
android:textColor="@color/briar_text_secondary"
tools:text="@string/introduction_response_accepted_received"/>
<TextView

View File

@@ -22,6 +22,7 @@
android:textIsSelectable="true"
android:textSize="@dimen/text_size_medium"
android:textStyle="italic"
android:textColor="@color/briar_text_secondary"
tools:text="@string/introduction_response_accepted_sent"/>
<TextView

View File

@@ -14,6 +14,7 @@
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_selectable_picture_size"
android:layout_height="@dimen/listitem_selectable_picture_size"
android:layout_alignParentLeft="true"
@@ -22,8 +23,6 @@
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:transitionName="avatar"
app:civ_border_color="@color/briar_primary"
app:civ_border_width="@dimen/avatar_border_width"
tools:src="@drawable/ic_launcher"/>
<TextView
@@ -38,6 +37,7 @@
android:layout_toRightOf="@+id/avatarView"
android:maxLines="2"
android:textSize="@dimen/text_size_large"
android:textColor="@color/briar_text_primary"
tools:text="This is a name of a contact"/>
<CheckBox

View File

@@ -20,7 +20,7 @@
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:padding="@dimen/margin_small"
android:textColor="@android:color/tertiary_text_light"
android:textColor="@color/briar_text_secondary"
tools:text="@string/transport_tor"/>
</LinearLayout>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:showIn="@layout/navigation_menu">
<ImageView
android:id="@+id/imageView2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="top|center_horizontal"
android:layout_margin="@dimen/margin_medium"
android:contentDescription="@string/app_name"
android:src="@drawable/briar_logo_large"/>
<View
style="@style/Divider.Horizontal"
android:layout_gravity="bottom"/>
</FrameLayout>

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