Compare commits

...

150 Commits

Author SHA1 Message Date
akwizgran
84b3670624 Create hidden service via conf file so we can use Tor 0.2.6. 2016-08-27 08:45:01 +01:00
akwizgran
749695187e Disable verification of Tor binaries so we can use old ones. 2016-08-26 23:30:58 +01:00
akwizgran
c4db72abf2 Revert Tor performance improvements for measurement. 2016-08-26 22:22:44 +01:00
Torsten Grote
55602ed76a Merge branch '614-dev-reporter-does-not-close-stream' into 'master'
Use Briar's IoUtils.copy(), not H2's IOUtils.copy()

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

Closes #614.

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

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

Closes #555.

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

Closes #597

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

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

Closes #311

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

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

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

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

Closes #592

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

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

Closes #483

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



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

Closes #573

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

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


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

Closes #566

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

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

Closes #583

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

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

Closes #580

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

Closes #591

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

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

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

Closes #595

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

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

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

Closes #342

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

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

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

Closes #587

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

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

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

Closes #582.

Depends on !272.

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

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

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

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

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

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

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

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

Closes #579

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

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

Fixes #470

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

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

Closes #429

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

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

Closes #514

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

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

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

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

Closes #576

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

Closes #575

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

Closes #565

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

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

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

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

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

Closes #498,  #497

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

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

Closes #571

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

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

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

Closes #517

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

Closes #486

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

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

Closes #485

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

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

Closes #484

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

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

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

Closes #562

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

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

Closes #335

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

Closes #543

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

Closes #559

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

Closes #472

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

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

Closes #553

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

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

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

Closes #488

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

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

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

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

Closes #415

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

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

Closes #535

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

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

Closes #544

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

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

Closes #310

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

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

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

Closes #533

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

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

Closes #515

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

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

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

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

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

Closes #436 

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

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

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

Closes #356

See merge request !242
2016-07-13 18:47:09 +00:00
str4d
8504cfa88b Re-enable UI if blog post fails to publish 2016-07-13 03:16:59 +00:00
str4d
dd1eed5aa7 Cleanup after review 2016-07-13 03:08:37 +00:00
str4d
39d91e026b Update introduction message text colours 2016-07-12 23:45:36 +00:00
Torsten Grote
412bf162ce Show contact names under avatars before sending introduction
to make it clearer who will be introduced.
2016-07-08 12:39:26 -03:00
Torsten Grote
d8b5710a42 Blog Feed: Show 'Scroll To' link only if scrolling possible 2016-07-04 16:28:57 -03:00
Torsten Grote
fd7278b488 Combined Blog Feed
This commit addes a combined blog feed that shows all posts of all
subscribed blogs in the order the blog posts have been received.

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

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

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

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

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

The actual list of blogs in the My Blogs tab will be done in the next
commit.
2016-07-04 14:35:07 -03:00
304 changed files with 11305 additions and 3214 deletions

View File

@@ -37,37 +37,15 @@
<JavaCodeStyleSettings>
<option name="ANNOTATION_PARAMETER_WRAP" value="1" />
</JavaCodeStyleSettings>
<Objective-C-extensions>
<option name="GENERATE_INSTANCE_VARIABLES_FOR_PROPERTIES" value="ASK" />
<option name="RELEASE_STYLE" value="IVAR" />
<option name="TYPE_QUALIFIERS_PLACEMENT" value="BEFORE" />
<file>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Import" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Macro" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Typedef" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Enum" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Constant" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Global" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Struct" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="FunctionPredecl" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Function" />
</file>
<class>
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Property" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="Synthesize" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InitMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="StaticMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="InstanceMethod" />
<option name="com.jetbrains.cidr.lang.util.OCDeclarationKind" value="DeallocMethod" />
</class>
<extensions>
<pair source="cpp" header="h" />
<pair source="c" header="h" />
</extensions>
</Objective-C-extensions>
<XML>
<option name="XML_LEGACY_SETTINGS_IMPORTED" value="true" />
</XML>
<codeStyleSettings language="Groovy">
<indentOptions>
<option name="USE_TAB_CHARACTER" value="true" />
<option name="SMART_TABS" value="true" />
</indentOptions>
</codeStyleSettings>
<codeStyleSettings language="JAVA">
<option name="RIGHT_MARGIN" value="80" />
<option name="ALIGN_MULTILINE_PARAMETERS" value="false" />

View File

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

View File

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

View File

@@ -33,7 +33,9 @@ import org.briarproject.sync.SyncModule;
import org.briarproject.transport.TransportModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
@@ -94,6 +96,9 @@ public class BlogManagerTest {
private BlogManagerTestComponent t0, t1;
@Rule
public ExpectedException thrown = ExpectedException.none();
@Before
public void setUp() throws Exception {
BlogManagerTestComponent component =
@@ -249,6 +254,7 @@ public class BlogManagerTest {
assertEquals(3, blogs0.size());
assertTrue(blogs0.contains(blog));
assertEquals(2, blogManager0.getBlogs(author0).size());
assertTrue(blogManager0.canBeRemoved(blog.getId()));
// remove blog
blogManager0.removeBlog(blog);
@@ -260,6 +266,27 @@ public class BlogManagerTest {
stopLifecycles();
}
@Test
public void testCanNotRemoveContactsPersonalBlog() throws Exception {
startLifecycles();
defaultInit();
assertFalse(blogManager0.canBeRemoved(blog1.getId()));
assertFalse(blogManager1.canBeRemoved(blog0.getId()));
// the following two calls should throw a DbException now
thrown.expect(DbException.class);
blogManager0.removeBlog(blog1);
blogManager1.removeBlog(blog0);
// blogs have not been removed
assertEquals(2, blogManager0.getBlogs().size());
assertEquals(2, blogManager1.getBlogs().size());
stopLifecycles();
}
@After
public void tearDown() throws Exception {
TestUtils.deleteTestDirectory(testDir);
@@ -310,12 +337,12 @@ public class BlogManagerTest {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// invitee adds sharer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
}

View File

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

View File

@@ -0,0 +1,100 @@
package org.briarproject;
import org.briarproject.api.blogs.BlogManager;
import org.briarproject.api.blogs.BlogSharingManager;
import org.briarproject.api.clients.ClientHelper;
import org.briarproject.api.clients.MessageQueueManager;
import org.briarproject.api.clients.PrivateGroupFactory;
import org.briarproject.api.contact.ContactManager;
import org.briarproject.api.db.DatabaseComponent;
import org.briarproject.api.event.EventBus;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
import org.briarproject.data.DataModule;
import org.briarproject.db.DatabaseModule;
import org.briarproject.event.EventModule;
import org.briarproject.forum.ForumModule;
import org.briarproject.identity.IdentityModule;
import org.briarproject.lifecycle.LifecycleModule;
import org.briarproject.properties.PropertiesModule;
import org.briarproject.sharing.SharingModule;
import org.briarproject.sync.SyncModule;
import org.briarproject.system.SystemModule;
import org.briarproject.transport.TransportModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestDatabaseModule.class,
TestPluginsModule.class,
TestSeedProviderModule.class,
ClientsModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
BlogsModule.class,
ForumModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
SharingModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class
})
interface BlogSharingIntegrationTestComponent {
void inject(BlogSharingIntegrationTest testCase);
void inject(ContactModule.EagerSingletons init);
void inject(CryptoModule.EagerSingletons init);
void inject(BlogsModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(PropertiesModule.EagerSingletons init);
void inject(SharingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
EventBus getEventBus();
IdentityManager getIdentityManager();
ContactManager getContactManager();
BlogSharingManager getBlogSharingManager();
BlogManager getBlogManager();
SyncSessionFactory getSyncSessionFactory();
/* the following methods are only needed to manually construct messages */
DatabaseComponent getDatabaseComponent();
PrivateGroupFactory getPrivateGroupFactory();
ClientHelper getClientHelper();
MessageQueueManager getMessageQueueManager();
}

View File

@@ -365,12 +365,12 @@ public class ForumManagerTest {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// invitee adds sharer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
}

View File

@@ -7,6 +7,7 @@ import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
@@ -38,6 +39,7 @@ import dagger.Component;
DatabaseModule.class,
EventModule.class,
ForumModule.class,
BlogsModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
@@ -46,7 +48,7 @@ import dagger.Component;
SystemModule.class,
TransportModule.class
})
public interface ForumManagerTestComponent {
interface ForumManagerTestComponent {
void inject(ForumManagerTest testCase);

View File

@@ -25,7 +25,8 @@ import org.briarproject.api.event.ForumInvitationReceivedEvent;
import org.briarproject.api.event.ForumInvitationResponseReceivedEvent;
import org.briarproject.api.event.MessageStateChangedEvent;
import org.briarproject.api.forum.Forum;
import org.briarproject.api.forum.ForumInvitationMessage;
import org.briarproject.api.forum.ForumInvitationRequest;
import org.briarproject.api.forum.ForumInvitationResponse;
import org.briarproject.api.forum.ForumManager;
import org.briarproject.api.forum.ForumPost;
import org.briarproject.api.forum.ForumPostFactory;
@@ -35,6 +36,8 @@ import org.briarproject.api.identity.AuthorFactory;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.identity.LocalAuthor;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sharing.InvitationItem;
import org.briarproject.api.sharing.InvitationMessage;
import org.briarproject.api.sync.ClientId;
import org.briarproject.api.sync.Group;
import org.briarproject.api.sync.SyncSession;
@@ -111,6 +114,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
private final String SHARER = "Sharer";
private final String INVITEE = "Invitee";
private final String SHARER2 = "Sharer2";
private boolean respond = true;
private static final Logger LOG =
Logger.getLogger(ForumSharingIntegrationTest.class.getName());
@@ -183,22 +187,33 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
// invitee has one invitation message from sharer
List<ForumInvitationMessage> list =
List<InvitationMessage> list =
new ArrayList<>(forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
assertEquals(2, list.size());
// check other things are alright with the forum message
ForumInvitationMessage invitation = list.get(0);
assertFalse(invitation.isAvailable());
assertEquals(forum0.getName(), invitation.getForumName());
assertEquals(contactId1, invitation.getContactId());
assertEquals("Hi!", invitation.getMessage());
// sharer has own invitation message
assertEquals(1,
for (InvitationMessage m : list) {
if (m instanceof ForumInvitationRequest) {
ForumInvitationRequest invitation =
(ForumInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(forum0.getName(), invitation.getForumName());
assertEquals(contactId1, invitation.getContactId());
assertEquals("Hi!", invitation.getMessage());
} else {
ForumInvitationResponse response =
(ForumInvitationResponse) m;
assertEquals(contactId0, response.getContactId());
assertTrue(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// forum can not be shared again
@@ -233,24 +248,35 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was not added
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(0, forumManager1.getForums().size());
// forum is no longer available to invitee who declined
assertEquals(0, forumSharingManager1.getInvited().size());
assertEquals(0, forumSharingManager1.getInvitations().size());
// invitee has one invitation message from sharer
List<ForumInvitationMessage> list =
// invitee has one invitation message from sharer and one response
List<InvitationMessage> list =
new ArrayList<>(forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
// check other things are alright with the forum message
ForumInvitationMessage invitation = list.get(0);
assertFalse(invitation.isAvailable());
assertEquals(forum0.getName(), invitation.getForumName());
assertEquals(contactId1, invitation.getContactId());
assertEquals(null, invitation.getMessage());
// sharer has own invitation message
assertEquals(1,
assertEquals(2, list.size());
// check things are alright with the forum message
for (InvitationMessage m : list) {
if (m instanceof ForumInvitationRequest) {
ForumInvitationRequest invitation =
(ForumInvitationRequest) m;
assertFalse(invitation.isAvailable());
assertEquals(forum0.getName(), invitation.getForumName());
assertEquals(contactId1, invitation.getContactId());
assertEquals(null, invitation.getMessage());
} else {
ForumInvitationResponse response =
(ForumInvitationResponse) m;
assertEquals(contactId0, response.getContactId());
assertFalse(response.wasAccepted());
assertTrue(response.isLocal());
}
}
// sharer has own invitation message and response
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// forum can be shared again
@@ -283,7 +309,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0));
@@ -303,7 +329,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
syncToSharer();
// forum is gone
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(0, forumManager1.getForums().size());
// sharer no longer shares forum with invitee
@@ -343,7 +369,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
assertTrue(listener0.responseReceived);
// forum was added successfully
assertEquals(0, forumSharingManager0.getInvited().size());
assertEquals(0, forumSharingManager0.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
assertTrue(forumManager1.getForums().contains(forum0));
@@ -384,7 +410,7 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
public void testSharerLeavesBeforeResponse() throws Exception {
startLifecycles();
try {
// initialize and let invitee accept all requests
// initialize except event listeners
defaultInit(true);
// send invitation
@@ -394,17 +420,43 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// sharer un-subscribes from forum
forumManager0.removeForum(forum0);
// 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);
// prevent invitee response before syncing messages
respond = false;
// sync first request message and leave message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// invitee has no forums available
assertEquals(0, forumSharingManager1.getInvited().size());
// wait also for second message to arrive
msgWaiter.await(TIMEOUT, 1);
// ensure that invitee has no forum invitations available
assertEquals(0, forumSharingManager1.getInvitations().size());
assertEquals(0, forumManager1.getForums().size());
// Try again, this time allow the response
addForumForSharer();
respond = true;
// send invitation
forumSharingManager0
.sendInvitation(forum0.getId(), contactId1, null);
// sharer un-subscribes from forum
forumManager0.removeForum(forum0);
// sync first request message and leave message
syncToInvitee();
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.requestReceived);
// wait also for second message to arrive
msgWaiter.await(TIMEOUT, 1);
// ensure that invitee has no forum invitations available
assertEquals(0, forumSharingManager1.getInvitations().size());
assertEquals(1, forumManager1.getForums().size());
} finally {
stopLifecycles();
}
@@ -438,12 +490,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
listener1.requestReceived = false;
// get SessionId from invitation
List<ForumInvitationMessage> list = new ArrayList<>(
List<InvitationMessage> list = new ArrayList<>(
forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
ForumInvitationMessage msg = list.get(0);
assertEquals(2, list.size());
InvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
assertEquals(sessionId, list.get(1).getSessionId());
// get all sorts of stuff needed to send a message
DatabaseComponent db = t0.getDatabaseComponent();
@@ -501,6 +554,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// forum was added successfully
assertEquals(1, forumManager1.getForums().size());
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
// invitee now shares same forum back
forumSharingManager1.sendInvitation(forum0.getId(),
@@ -512,9 +568,10 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// make sure that no new request was received
assertFalse(listener0.requestReceived);
assertEquals(1,
assertEquals(2,
forumSharingManager0.getInvitationMessages(contactId1)
.size());
assertEquals(0, forumSharingManager0.getInvitations().size());
} finally {
stopLifecycles();
}
@@ -566,9 +623,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener0.responseReceived);
assertEquals(1, forumSharingManager0
assertEquals(2, forumSharingManager0
.getInvitationMessages(contactId1).size());
assertEquals(2, forumSharingManager1
assertEquals(3, forumSharingManager1
.getInvitationMessages(contactId0).size());
} else {
eventWaiter.await(TIMEOUT, 1);
@@ -579,9 +636,9 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
eventWaiter.await(TIMEOUT, 1);
assertTrue(listener1.responseReceived);
assertEquals(2, forumSharingManager0
assertEquals(3, forumSharingManager0
.getInvitationMessages(contactId1).size());
assertEquals(1, forumSharingManager1
assertEquals(2, forumSharingManager1
.getInvitationMessages(contactId0).size());
}
} finally {
@@ -616,12 +673,13 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
forumSharingManager0.getSharedWith(forum0.getId()).size());
// remember SessionId from invitation
List<ForumInvitationMessage> list = new ArrayList<>(
List<InvitationMessage> list = new ArrayList<>(
forumSharingManager1
.getInvitationMessages(contactId0));
assertEquals(1, list.size());
ForumInvitationMessage msg = list.get(0);
assertEquals(2, list.size());
InvitationMessage msg = list.get(0);
SessionId sessionId = msg.getSessionId();
assertEquals(sessionId, list.get(1).getSessionId());
// contacts now remove each other
contactManager0.removeContact(contactId1);
@@ -711,8 +769,11 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
"Sharer2 to Invitee");
// make sure we now have two invitations to the same forum available
Collection<Forum> forums = forumSharingManager1.getInvited();
Collection<InvitationItem> forums =
forumSharingManager1.getInvitations();
assertEquals(1, forums.size());
assertEquals(2, forums.iterator().next().getNewSharers().size());
assertEquals(forum0, forums.iterator().next().getShareable());
assertEquals(2,
forumSharingManager1.getSharedBy(forum0.getId()).size());
@@ -949,10 +1010,16 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
Forum f = event.getForum();
try {
eventWaiter.assertEquals(1,
forumSharingManager1.getInvited().size());
forumSharingManager1.getInvitations().size());
InvitationItem invitation =
forumSharingManager1.getInvitations().iterator()
.next();
eventWaiter.assertEquals(f, invitation.getShareable());
if (respond) {
Contact c =
contactManager1.getContact(event.getContactId());
forumSharingManager1.respondToInvitation(f, c, accept);
}
} catch (DbException ex) {
eventWaiter.rethrow(ex);
} finally {
@@ -1024,21 +1091,21 @@ public class ForumSharingIntegrationTest extends BriarTestCase {
// sharer adds invitee as contact
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// second sharer does the same
contactId21 = contactManager2.addContact(author1,
author2.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// invitee adds sharers back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
contactId2 = contactManager1.addContact(author2,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
}

View File

@@ -11,6 +11,7 @@ import org.briarproject.api.forum.ForumSharingManager;
import org.briarproject.api.identity.IdentityManager;
import org.briarproject.api.lifecycle.LifecycleManager;
import org.briarproject.api.sync.SyncSessionFactory;
import org.briarproject.blogs.BlogsModule;
import org.briarproject.clients.ClientsModule;
import org.briarproject.contact.ContactModule;
import org.briarproject.crypto.CryptoModule;
@@ -42,6 +43,7 @@ import dagger.Component;
DatabaseModule.class,
EventModule.class,
ForumModule.class,
BlogsModule.class,
IdentityModule.class,
LifecycleModule.class,
PropertiesModule.class,
@@ -50,7 +52,7 @@ import dagger.Component;
SystemModule.class,
TransportModule.class
})
public interface ForumSharingIntegrationTestComponent {
interface ForumSharingIntegrationTestComponent {
void inject(ForumSharingIntegrationTest testCase);

View File

@@ -164,20 +164,20 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
assertTrue(contactId0.equals(contactId02));
@@ -238,6 +238,18 @@ public class IntroductionIntegrationTest extends BriarTestCase {
assertTrue(contactManager2
.contactExists(author1.getId(), author2.getId()));
// make sure that introduced contacts are not verified
for (Contact c : contactManager1.getActiveContacts()) {
if (c.getAuthor().equals(author2)) {
assertFalse(c.isVerified());
}
}
for (Contact c : contactManager2.getActiveContacts()) {
if (c.getAuthor().equals(author1)) {
assertFalse(c.isVerified());
}
}
assertDefaultUiMessages();
} finally {
stopLifecycles();
@@ -256,18 +268,18 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1, author0.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
contactId2 = contactManager0.addContact(author2, author0.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0, author1.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
assertTrue(contactId0.equals(contactId02));
@@ -349,18 +361,18 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1, author0.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
contactId2 = contactManager0.addContact(author2, author0.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0, author1.getId(),
master, clock.currentTimeMillis(), false, true
master, clock.currentTimeMillis(), false, true, true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), false,
true
true, true
);
assertTrue(contactId0.equals(contactId02));
@@ -437,18 +449,18 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1, author0.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
contactId2 = contactManager0.addContact(author2, author0.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0, author1.getId(),
master, clock.currentTimeMillis(), false, true
master, clock.currentTimeMillis(), false, true, true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), false,
true
true, true
);
assertTrue(contactId0.equals(contactId02));
@@ -516,11 +528,11 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducee as contact
contactId1 = contactManager0.addContact(author1, author0.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0, author1.getId(),
master, clock.currentTimeMillis(), true, true
master, clock.currentTimeMillis(), true, true, true
);
// listen to events
@@ -573,21 +585,21 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees' authors as contacts
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// Add introducer back
contactId0 = null;
ContactId contactId01 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), false,
true
true, true
);
ContactId contactId02 = contactManager1.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), false,
true
true, true
);
// listen to events
@@ -667,20 +679,20 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
assertTrue(contactId0.equals(contactId02));
@@ -764,20 +776,20 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
assertTrue(contactId0.equals(contactId02));
@@ -850,20 +862,20 @@ public class IntroductionIntegrationTest extends BriarTestCase {
// Add introducees as contacts
contactId1 = contactManager0.addContact(author1,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
contactId2 = contactManager0.addContact(author2,
author0.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
// Add introducer back
contactId0 = contactManager1.addContact(author0,
author1.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
ContactId contactId02 = contactManager2.addContact(author0,
author2.getId(), master, clock.currentTimeMillis(), true,
true
true, true
);
assertTrue(contactId0.equals(contactId02));

View File

@@ -93,7 +93,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
Author bobAuthor = new Author(bobId, "Bob",
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = contactManager.addContact(bobAuthor, aliceId,
master, timestamp, true, true);
master, timestamp, true, true, true);
// Send Bob a message
GroupId groupId = messagingManager.getConversationId(contactId);
@@ -146,7 +146,7 @@ public class SimplexMessagingIntegrationTest extends BriarTestCase {
Author aliceAuthor = new Author(aliceId, "Alice",
new byte[MAX_PUBLIC_KEY_LENGTH]);
ContactId contactId = contactManager.addContact(aliceAuthor, bobId,
master, timestamp, false, true);
master, timestamp, false, true, true);
// Set up an event listener
MessageListener listener = new MessageListener();

View File

@@ -7,8 +7,7 @@
android:versionName="0.12">
<uses-sdk
android:minSdkVersion="9"
android:minSdkVersion="14"
android:targetSdkVersion="22"
tools:overrideLibrary="android.support.v14.preference"
/>
@@ -51,7 +50,9 @@
android:finishOnTaskLaunch="true"
android:label="@string/crash_report_title"
android:launchMode="singleInstance"
android:process=":briar_error_handler">
android:process=":briar_error_handler"
android:theme="@style/BriarThemeNoActionBar.Default"
android:windowSoftInputMode="stateHidden">
</activity>
<activity
@@ -100,7 +101,7 @@
</activity>
<activity
android:name=".android.forum.ForumInvitationsActivity"
android:name=".android.sharing.InvitationsForumActivity"
android:label="@string/forum_invitations_title"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
@@ -109,6 +110,16 @@
/>
</activity>
<activity
android:name=".android.sharing.InvitationsBlogActivity"
android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName=".android.contact.ConversationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity"
/>
</activity>
<activity
android:name=".android.forum.CreateForumActivity"
android:label="@string/create_forum_title"
@@ -131,8 +142,8 @@
</activity>
<activity
android:name=".android.forum.ShareForumActivity"
android:label="@string/forums_share_toolbar_header"
android:name=".android.sharing.ShareForumActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.forum.ForumActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -141,8 +152,18 @@
</activity>
<activity
android:name=".android.forum.ForumSharingStatusActivity"
android:label="@string/forum_sharing_status"
android:name=".android.sharing.ShareBlogActivity"
android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.blogs.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.sharing.SharingStatusForumActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.forum.ForumActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
@@ -150,6 +171,67 @@
/>
</activity>
<activity
android:name=".android.sharing.SharingStatusBlogActivity"
android:label="@string/sharing_status"
android:parentActivityName=".android.blogs.BlogActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.blogs.CreateBlogActivity"
android:label="@string/blogs_my_blogs_label"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.blogs.BlogActivity"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.blogs.WriteBlogPostActivity"
android:label="@string/blogs_write_blog_post"
android:parentActivityName=".android.blogs.BlogActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blogs.BlogActivity"
/>
</activity>
<activity
android:name=".android.blogs.RssFeedImportActivity"
android:label="@string/blogs_rss_feeds_import"
android:parentActivityName=".android.NavDrawerActivity"
android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.blogs.RssFeedManageActivity"
android:label="@string/blogs_rss_feeds_manage"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value=".android.NavDrawerActivity"
/>
</activity>
<activity
android:name=".android.identity.CreateIdentityActivity"
android:label="@string/new_identity_title"
@@ -194,7 +276,7 @@
<activity
android:name=".android.SettingsActivity"
android:label="@string/settings_title"
android:label="@string/settings_button"
android:parentActivityName=".android.NavDrawerActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"

View File

@@ -14,17 +14,14 @@ dependencies {
// This shouldn't be necessary; per section 23.4.4 of the Gradle docs:
// "file dependencies are included in transitive project dependencies within the same build".
compile files('../briar-core/libs/jsocks.jar')
compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4'
}
compile("com.android.support:preference-v7:$supportVersion") {
exclude module: 'support-v4'
}
compile("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4'
exclude module: 'preference-v7'
exclude module: 'recyclerview-v7'
}
compile("com.android.support:design:$supportVersion") {
exclude module: 'support-v4'
@@ -47,11 +44,6 @@ dependencies {
testCompile project(path: ':briar-tests')
testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:1.10.19'
androidTestCompile 'org.robolectric:robolectric:3.0'
androidTestCompile 'org.mockito:mockito-core:1.10.19'
androidTestApt 'com.google.dagger:dagger-compiler:2.0.2'
androidTestCompile 'junit:junit:4.12'
}
dependencyVerification {
@@ -63,7 +55,6 @@ dependencyVerification {
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
'com.android.support:support-annotations:786ab0d060774fb95cfdaf4878771e14b85733b1af9d72a4aae762dc7c1dff9f',
@@ -77,6 +68,10 @@ android {
compileSdkVersion 23
buildToolsVersion "23.0.3"
defaultConfig {
resValue "string", "app_package", "org.briarproject"
}
sourceSets {
main {
manifest.srcFile 'AndroidManifest.xml'
@@ -100,24 +95,16 @@ android {
unitTests.returnDefaultValues = true
}
}
// Move the build types to build-types/<type>
// For instance, build-types/debug/java, build-types/debug/AndroidManifest.xml, ...
// This moves them out of them default location under src/<type>/... which would
// conflict with src/ being used by the main source set.
// Adding new build types or product flavors should be accompanied
// by a similar customization.
debug.setRoot('build-types/debug')
release.setRoot('build-types/release')
}
buildTypes {
debug {
shrinkResources false
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
release {
shrinkResources true
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
}
@@ -129,8 +116,10 @@ android {
}
lintOptions {
abortOnError false
warning 'MissingTranslation'
warning 'ImpliedQuantity'
}
dexOptions {
incremental true
}
@@ -200,8 +189,8 @@ project.afterEvaluate {
preBuild.dependsOn {
[
'verifyTorGeoIp',
'verifyTorBinaryArm',
'verifyTorBinaryArmPie',
// 'verifyTorBinaryArm',
// 'verifyTorBinaryArmPie',
'verifyTorBinaryX86',
'verifyTorBinaryX86Pie'
]

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 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: 1014 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

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

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 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.56"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#000000"
android:pathData="M6,19c0,1.1 0.9,2 2,2h8c1.1,0 2,-0.9 2,-2V7H6v12zM19,4h-3.5l-1,-1h-5l-1,1H5v2h14V4z"/>
</vector>

View File

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

View File

@@ -1,4 +1,9 @@
<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 xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="48.0"
android:viewportWidth="48.0">
<path
android:fillColor="#06b9ff"
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

@@ -1,4 +1,9 @@
<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 xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="48.0"
android:viewportWidth="48.0">
<path
android:fillColor="#06b9ff"
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="M20,2L4,2c-1.1,0 -1.99,0.9 -1.99,2L2,22l4,-4h14c1.1,0 2,-0.9 2,-2L22,4c0,-1.1 -0.9,-2 -2,-2zM6,9h12v2L6,11L6,9zM14,14L6,14v-2h8v2zM18,8L6,8L6,6h12v2z"/>
</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="M7,7h10v3l4,-4 -4,-4v3L5,5v6h2L7,7zM17,17L7,17v-3l-4,4 4,4v-3h12v-6h-2v4z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M1,21h22L12,2 1,21zM13,18h-2v-2h2v2zM13,14h-2v-4h2v4z"/>
</vector>

View File

@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="100dp"
android:viewportHeight="49.5"
android:viewportWidth="194.8">
<path
android:fillColor="#000000"
android:pathData="M30.1 16.5l-9 0 0 -5c0 -2.4 -2 -4.4 -4.4 -4.4L4.4 7.1C2 7.1 0 9.1 0 11.5l0 24.2c0 2.4 2 4.4 4.4 4.4l9 0 0 5c0 2.4 2 4.4 4.4 4.4l12.2 0c2.4 0 4.4 -2 4.4 -4.4l0 -24.2c0.1 -2.4 -1.9 -4.4 -4.3 -4.4zm-27.4 16.1l0 -20.9 15.8 0 0 20.9 -15.8 0zm10.7 4.6l-5.8 0 0 -1.5 5.8 0 0 1.5zm13.5 9.4l-5.8 0 0 -1.5 5.8 0 0 1.5zm5 -4.6l-15.8 0 0 -1.9 0.5 0c2.4 0 4.4 -2 4.4 -4.4l0 -14.6 10.8 0 0 20.9z"/>
<path
android:fillColor="#000000"
android:pathData="M101.2 16.5l-8.3 0 0 -4.4c0 -1.4 -1.2 -2.6 -2.6 -2.6l-3.9 0 -2.1 -2.5 -6.9 0 -2.2 2.5 -3.8 0c-1.4 0 -2.6 1.2 -2.6 2.6l0 13.3c0 1.4 1.2 2.6 2.6 2.6l13.1 0 0 17.2c0 2.4 2 4.4 4.4 4.4l12.2 0c2.4 0 4.4 -2 4.4 -4.4l0 -24.3c0.2 -2.4 -1.8 -4.4 -4.3 -4.4zm-26.4 2.4c0 -3.3 2.7 -6 6 -6 3.3 0 6 2.7 6 6 0 3.3 -2.7 6 -6 6 -3.3 0 -6 -2.7 -6 -6zm23.2 27.7l-5.8 0 0 -1.5 5.8 0 0 1.5zm5 -4.6l-15.8 0 0 -14.1 3.1 0c1.4 0 2.6 -1.2 2.6 -2.6l0 -4.2 10.1 0 0 20.9z"/>
<path
android:fillColor="#000000"
android:pathData="M84.600003 18.9a3.8 3.8 0 0 1 -3.8 3.8 3.8 3.8 0 0 1 -3.8 -3.8 3.8 3.8 0 0 1 3.8 -3.8 3.8 3.8 0 0 1 3.8 3.8z"/>
<path
android:fillColor="#000000"
android:pathData="M175.3 16.5l-9.8 0 0 -5.7c0 -1.4 -1.2 -2.6 -2.6 -2.6l-19.3 0c-1.4 0 -2.6 1.2 -2.6 2.6l0 14.4c0 1.4 1.2 2.6 2.6 2.6l15.1 0 0 17.3c0 2.4 2 4.4 4.4 4.4l12.2 0c2.4 0 4.4 -2 4.4 -4.4l0 -24.2c0.1 -2.4 -1.9 -4.4 -4.4 -4.4zm-12.4 -5.9l-9.6 6 -9.6 -6 19.2 0zm-19.4 14.8l0 -12.3 9.8 6.1 9.8 -6.1 0 12.3 -19.6 0zm28.6 21.2l-5.8 0 0 -1.5 5.8 0 0 1.5zm5 -4.6l-15.8 0 0 -14.2 1.6 0c1.4 0 2.6 -1.2 2.6 -2.6l0 -4.1 11.6 0 0 20.9z"/>
<path
android:fillColor="#ff0000"
android:pathData="M101.4 17.8l2 2 7.4 -7.3 7.3 7.3 2.1 -2 -7.4 -7.4 7.4 -7.3 -2.1 -2.1 -7.3 7.4 -7.4 -7.4 -2 2.1 7.3 7.3z"/>
<path
android:fillColor="#ff0000"
android:pathData="M176 17.8l2.1 2 7.3 -7.3 7.4 7.3 2 -2 -7.3 -7.4 7.3 -7.3 -2 -2.1 -7.4 7.4 -7.3 -7.4 -2.1 2.1 7.3 7.3z"/>
<path
android:fillColor="#08b124"
android:pathData="M35.8 18.8l0 0L52.5 2.1 50.5 0 35.6 14.8 28.5 7.7l-2.1 2.1 9.2 9.1z"/>
</vector>

View File

@@ -1,5 +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_down" android:state_selected="true"/>
<item android:drawable="@drawable/chevron48dp_up"/>
</selector>

View File

@@ -1,5 +0,0 @@
<vector android:alpha="0.56" android:height="24dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M2.01,21L23,12 2.01,3 2,10l15,2 -15,2z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24.0"
android:viewportHeight="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -1,7 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="40dp"
android:height="15dp"
android:width="31dp"
android:height="12dp"
android:viewportHeight="20"
android:viewportWidth="49">
<path

View File

@@ -3,23 +3,21 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
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">
android:orientation="horizontal">
<ImageView
android:id="@+id/imageView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:layout_weight="1"
android:adjustViewBounds="true"
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"
android:scaleType="fitCenter"
android:src="@drawable/qr_code_intro"/>
@@ -31,10 +29,15 @@
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
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/yourNicknameView"
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -46,17 +49,32 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/spinner_border"
android:background="@drawable/border_spinner"
android:spinnerMode="dropdown"
android:visibility="gone"/>
<TextView
android:id="@+id/faceToFaceView"
style="@style/BriarTextBody"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:text="@string/face_to_face"/>
android:background="@drawable/border_explanation"
android:orientation="vertical"
android:padding="@dimen/margin_large">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:padding="@dimen/margin_medium"
android:src="@drawable/qr_code_explanation"/>
<TextView
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:text="@string/face_to_face"/>
</LinearLayout>
<Button
android:id="@+id/continueButton"

View File

@@ -34,7 +34,6 @@
android:orientation="vertical">
<TextView
android:id="@+id/yourNicknameView"
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -46,12 +45,11 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/spinner_border"
android:background="@drawable/border_spinner"
android:spinnerMode="dropdown"
android:visibility="gone"/>
<TextView
android:id="@+id/faceToFaceView"
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -0,0 +1,21 @@
<?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="match_parent">
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".android.blogs.BlogActivity"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"/>
</FrameLayout>

View File

@@ -0,0 +1,69 @@
<?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">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="@dimen/margin_activity_horizontal"
tools:context=".android.blogs.CreateBlogActivity">
<android.support.design.widget.TextInputLayout
android:id="@+id/titleLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterEnabled="true"
app:counterOverflowTextAppearance="@style/BriarTextCounter.Overflow">
<android.support.design.widget.TextInputEditText
android:id="@+id/titleInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/blogs_my_blogs_create_hint_title"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/descLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:counterEnabled="true"
app:counterOverflowTextAppearance="@style/BriarTextCounter.Overflow">
<android.support.design.widget.TextInputEditText
android:id="@+id/descInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/blogs_my_blogs_create_hint_desc"/>
</android.support.design.widget.TextInputLayout>
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/blogs_my_blogs_create_hint_desc_explanation"/>
<Button
android:id="@+id/createBlogButton"
style="@style/BriarButton"
android:layout_marginTop="@dimen/margin_activity_vertical"
android:enabled="false"
android:text="@string/blogs_my_blogs_create"/>
<ProgressBar
android:id="@+id/createBlogProgressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_activity_vertical"
android:indeterminate="true"
android:visibility="gone"/>
</LinearLayout>
</ScrollView>

View File

@@ -32,9 +32,6 @@
<Button
style="@style/BriarButton"
android:id="@+id/createForumButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:text="@string/create_forum_button" />
<ProgressBar
@@ -42,8 +39,6 @@
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:indeterminate="true"
android:layout_centerHorizontal="true"
android:visibility="gone" />
</LinearLayout>

View File

@@ -34,8 +34,6 @@
<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"/>

View File

@@ -1,72 +1,85 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<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="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
style="@style/BriarToolbar"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".android.report.DevReportActivity">
<TextView
android:id="@+id/title"
style="@style/TextAppearance.AppCompat.Large.Inverse"
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
style="@style/BriarToolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
tools:text="@string/crash_report_title"/>
</android.support.v7.widget.Toolbar>
android:layout_height="wrap_content"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="@dimen/margin_medium">
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large">
<EditText
android:id="@+id/user_comment"
<android.support.design.widget.TextInputLayout
android:id="@+id/user_comment_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/user_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine|textCapSentences"
tools:hint="@string/describe_crash"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/user_email_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
android:inputType="textMultiLine|textCapSentences"
tools:hint="@string/describe_crash"/>
android:layout_below="@+id/user_comment_layout"
android:layout_marginTop="@dimen/margin_small">
<EditText
android:id="@+id/user_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
android:layout_marginTop="@dimen/margin_small"
android:hint="@string/optional_contact_email"
android:inputType="textEmailAddress"
android:maxLines="1"/>
<android.support.design.widget.TextInputEditText
android:id="@+id/user_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/optional_contact_email"
android:inputType="textEmailAddress"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<CheckBox
android:id="@+id/include_debug_report"
android:layout_width="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
android:layout_alignParentLeft="true"
android:layout_below="@+id/user_email_layout"
android:layout_marginTop="@dimen/margin_small"
android:text="@string/include_debug_report"/>
android:layout_toLeftOf="@+id/chevron"
android:checked="false"
android:text="@string/include_debug_report_crash"/>
<Button
android:id="@+id/chevron"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/include_debug_report"
android:text="@string/show"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/include_debug_report"
android:layout_marginTop="@dimen/margin_small">
<LinearLayout
@@ -80,33 +93,97 @@
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@dimen/margin_large"
android:paddingStart="@dimen/margin_large"
android:paddingTop="@dimen/margin_small"/>
android:paddingTop="@dimen/margin_small"
android:visibility="gone"/>
</ScrollView>
</LinearLayout>
<ProgressBar
android:id="@+id/progress_wheel"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:indeterminate="true"
android:visibility="gone"/>
<ProgressBar
android:id="@+id/progress_wheel"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/include_debug_report"
android:layout_centerHorizontal="true"
android:indeterminate="true"
android:visibility="gone"/>
<android.support.design.widget.FloatingActionButton
android:id="@+id/share_dev_report"
android:layout_width="wrap_content"
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:id="@+id/request_report"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/window_background"
android:clickable="true"
android:gravity="center"
android:padding="@dimen/margin_large"
android:visibility="gone">
<TextView
android:id="@+id/crashed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/briar_crashed"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_large"/>
<TextView
android:id="@+id/fault"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/crashed"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/not_your_fault"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_large"/>
<TextView
android:id="@+id/pleaseSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/fault"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/please_send_report"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_large"/>
<TextView
android:id="@+id/encrypted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/pleaseSend"
android:layout_marginBottom="@dimen/margin_large"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:background="@color/briar_accent"
android:src="@drawable/social_share"
android:tint="@color/action_bar_text"/>
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/report_is_encrypted"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_large"/>
<Button
android:id="@+id/declineButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/encrypted"
android:text="@string/close"/>
<Button
android:id="@+id/acceptButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/encrypted"
android:text="@string/send_report"/>
</RelativeLayout>
</LinearLayout>
</FrameLayout>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<org.briarproject.android.util.BriarRecyclerView
android:id="@+id/availableForumsView"
android:id="@+id/invitationsView"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View File

@@ -11,7 +11,6 @@
<!-- The second child is the menu -->
<include
android:id="@+id/navigation_menu_drawer"
layout="@layout/navigation_menu"
android:layout_width="wrap_content"
android:layout_height="match_parent"

View File

@@ -0,0 +1,34 @@
<?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="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_small"
tools:context=".android.blogs.RssFeedImportActivity">
<EditText
android:id="@+id/urlInput"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="top"
android:hint="@string/blogs_rss_feeds_import_hint"
android:inputType="textMultiLine|textUri"/>
<Button
android:id="@+id/importButton"
style="@style/BriarButton"
android:enabled="false"
android:text="@string/blogs_rss_feeds_import_button"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
</LinearLayout>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<org.briarproject.android.util.BriarRecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/feedList"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:scrollToEnd="false"
app:emptyText="@string/blogs_rss_feeds_manage_empty_state"
tools:listitem="@layout/list_item_rss_feed"/>

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/shareForumContainer"
android:id="@+id/shareContainer"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"/>

View File

@@ -7,7 +7,6 @@
android:padding="@dimen/margin_activity_horizontal">
<TextView
android:id="@+id/headlineView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="@style/BriarTextTitle"

View File

@@ -0,0 +1,56 @@
<?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"
android:orientation="vertical"
android:padding="@dimen/margin_small"
tools:context=".android.blogs.WriteBlogPostActivity">
<android.support.design.widget.TextInputLayout
android:id="@+id/titleLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="gone"
app:counterEnabled="true"
app:counterOverflowTextAppearance="@style/BriarTextCounter.Overflow">
<android.support.design.widget.TextInputEditText
android:id="@+id/titleInput"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/blogs_write_blog_post_title_hint"
android:inputType="textCapWords|textCapSentences|textAutoCorrect"/>
</android.support.design.widget.TextInputLayout>
<EditText
android:id="@+id/bodyInput"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="bottom"
android:hint="@string/blogs_write_blog_post_body_hint"
android:inputType="textMultiLine|textLongMessage|textCapSentences|textAutoCorrect">
<requestFocus/>
</EditText>
<Button
android:id="@+id/publishButton"
style="@style/BriarButton"
android:enabled="false"
android:text="@string/blogs_publish_blog_post"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
</LinearLayout>

View File

@@ -148,9 +148,7 @@
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"
/>
android:src="@drawable/selector_chevron"/>
<TextView
android:id="@+id/btn_reply"

View File

@@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<org.briarproject.android.util.BriarRecyclerView
android:id="@+id/postList"
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"
app:scrollToEnd="false"
tools:context=".android.blogs.BlogActivity"/>

View File

@@ -0,0 +1,78 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/margin_activity_horizontal">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
style="@style/BriarAvatar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginRight="@dimen/margin_medium"
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/authorName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/avatar"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="Author Name"/>
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/avatar"
android:layout_below="@+id/authorName"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:gravity="bottom"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="yesterday"/>
<org.briarproject.android.util.TrustIndicatorView
android:id="@+id/trustIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_small"
android:layout_toRightOf="@+id/authorName"
tools:src="@drawable/trust_indicator_verified"/>
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/avatar"
android:layout_marginTop="@dimen/margin_medium"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_large"
android:textStyle="bold"
tools:text="This Is A Blog Post Title"/>
<TextView
android:id="@+id/body"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/title"
android:layout_marginTop="@dimen/margin_medium"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_medium"
tools:text="Body of Blog Post. This could be insanely large or just a short text as well."/>
</RelativeLayout>
</ScrollView>

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,26 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- This is just a placeholder to be replaced by the real My Blogs list -->
<LinearLayout
<org.briarproject.android.util.BriarRecyclerView
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>
tools:listitem="@layout/list_item_blog"/>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
android:id="@+id/coordinatorLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"

View File

@@ -16,7 +16,6 @@
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
android:id="@+id/yourNicknameView"
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -28,7 +27,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:background="@drawable/spinner_border"
android:background="@drawable/border_spinner"
android:spinnerMode="dropdown"
android:visibility="gone"/>
@@ -41,13 +40,31 @@
android:scaleType="fitCenter"
android:src="@drawable/qr_code_intro"/>
<TextView
android:id="@+id/faceToFaceView"
style="@style/BriarTextBody"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_small"
android:layout_marginRight="@dimen/margin_small"
android:layout_marginTop="@dimen/margin_xlarge"
android:text="@string/face_to_face"/>
android:background="@drawable/border_explanation"
android:orientation="vertical"
android:padding="@dimen/margin_large">
<ImageView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:padding="@dimen/margin_medium"
android:src="@drawable/qr_code_explanation"/>
<TextView
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_medium"
android:text="@string/face_to_face"/>
</LinearLayout>
<Button
android:id="@+id/continueButton"

View File

@@ -34,10 +34,7 @@
<Button
android:id="@+id/shareForumButton"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/forum_share_button"
/>
android:text="@string/forum_share_button"/>
</LinearLayout>

View File

@@ -1,38 +1,50 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.NestedScrollView
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:fillViewport="true">
<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"
android:padding="@dimen/margin_activity_horizontal"
android:orientation="vertical">
android:orientation="vertical"
android:padding="@dimen/margin_activity_horizontal">
<RelativeLayout
android:id="@+id/introductionHeader"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/margin_medium">
android:layout_height="wrap_content">
<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"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/introductionIcon"
android:layout_toStartOf="@+id/introductionIcon"
tools:src="@drawable/ic_launcher"/>
android:gravity="top|center_horizontal"
android:orientation="vertical">
<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_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/nameContact1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="Contact 1"/>
</LinearLayout>
<ImageView
android:id="@+id/introductionIcon"
@@ -42,18 +54,37 @@
android:src="@drawable/ic_contact_introduction"
tools:ignore="ContentDescription"/>
<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"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/introductionIcon"
android:layout_toRightOf="@+id/introductionIcon"
android:transitionName="avatar"
tools:src="@drawable/ic_launcher"/>
android:gravity="top|center_horizontal"
android:orientation="vertical">
<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_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:transitionName="avatar"
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/nameContact2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="Contact 2"/>
</LinearLayout>
</RelativeLayout>
@@ -72,8 +103,8 @@
android:layout_marginTop="@dimen/margin_medium"
android:layout_weight="1"
android:gravity="top"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_medium"
android:textColor="@color/briar_text_primary"
tools:text="@string/introduction_message_text"/>
<EditText
@@ -83,15 +114,14 @@
android:layout_marginTop="@dimen/margin_medium"
android:gravity="bottom"
android:hint="@string/introduction_message_hint"
android:inputType="text|textMultiLine|textCapSentences"/>
android:inputType="text|textMultiLine|textCapSentences"
android:textColor="@color/briar_text_primary"
android:textColorHint="@color/briar_text_tertiary"/>
<Button
android:id="@+id/makeIntroductionButton"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/introduction_button"
/>
android:text="@string/introduction_button"/>
</LinearLayout>

View File

@@ -6,8 +6,6 @@
android:layout_height="match_parent">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -6,8 +6,6 @@
android:layout_height="match_parent">
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

@@ -16,7 +16,6 @@
android:paddingTop="@dimen/margin_activity_vertical">
<TextView
android:id="@+id/yourNicknameView"
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"
@@ -27,7 +26,7 @@
android:id="@+id/spinner"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@drawable/spinner_border"
android:background="@drawable/border_spinner"
android:layout_marginTop="@dimen/margin_medium"
android:spinnerMode="dropdown"
android:visibility="gone"/>
@@ -42,7 +41,6 @@
android:src="@drawable/bluetooth"/>
<TextView
android:id="@+id/faceToFaceView"
style="@style/BriarTextBody"
android:layout_width="match_parent"
android:layout_height="wrap_content"

View File

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

View File

@@ -0,0 +1,118 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:layout_marginTop="@dimen/listitem_vertical_margin"
android:background="?attr/selectableItemBackground">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatar"
style="@style/BriarAvatar"
android:layout_width="30dp"
android:layout_height="30dp"
android:layout_marginBottom="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/authorName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@+id/avatar"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="Author Name"/>
<TextView
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@id/avatar"
android:layout_below="@+id/authorName"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:gravity="bottom"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny"
tools:text="yesterday"/>
<TextView
android:id="@+id/newView"
style="@style/BriarTag"
android:layout_alignBottom="@+id/dateView"
android:layout_marginLeft="@dimen/margin_small"
android:layout_toRightOf="@+id/dateView"
android:text="@string/tag_new"
android:visibility="gone"/>
<org.briarproject.android.util.TrustIndicatorView
android:id="@+id/trustIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/authorName"
android:layout_alignTop="@+id/authorName"
android:layout_marginLeft="@dimen/margin_small"
android:layout_toRightOf="@+id/authorName"
android:scaleType="center"
tools:src="@drawable/trust_indicator_verified"/>
<ImageView
android:id="@+id/chatView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/commentView"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_chat"
android:visibility="gone"/>
<ImageView
android:id="@+id/commentView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_repeat"
android:visibility="gone"/>
<TextView
android:id="@+id/titleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/avatar"
android:layout_marginBottom="@dimen/margin_medium"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:ellipsize="end"
android:maxLines="3"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_large"
android:visibility="gone"
tools:text="This is a blog post title which can also be longer"/>
<TextView
android:id="@+id/bodyView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/titleView"
android:layout_marginEnd="@dimen/margin_medium"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a body text that shows the content of a blog post. This one is not short, but it is also not too long."/>
<View
style="@style/Divider.ForumList"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/bodyView"
android:layout_marginTop="@dimen/listitem_vertical_margin"/>
</RelativeLayout>

View File

@@ -49,7 +49,6 @@
</FrameLayout>
<LinearLayout
android:id="@+id/textViews"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"

View File

@@ -31,7 +31,7 @@
tools:text="This is a name of a forum"/>
<TextView
android:id="@+id/unreadView"
android:id="@+id/postCountView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/forumNameView"
@@ -62,7 +62,7 @@
style="@style/Divider.ForumList"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/unreadView"/>
android:layout_below="@+id/postCountView"/>
</RelativeLayout>

View File

@@ -11,7 +11,6 @@
layout="@layout/list_item_msg_in"/>
<RelativeLayout
android:id="@+id/introductionLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|start"
@@ -50,7 +49,7 @@
android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/introductionText"
android:text="@string/dialog_button_accept"/>
android:text="@string/accept"/>
<Button
android:id="@+id/declineButton"
@@ -61,7 +60,7 @@
android:layout_below="@+id/introductionText"
android:layout_toLeftOf="@+id/acceptButton"
android:layout_toStartOf="@+id/acceptButton"
android:text="@string/dialog_button_decline"/>
android:text="@string/decline"/>
</RelativeLayout>

View File

@@ -1,57 +0,0 @@
<?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="vertical">
<include
android:id="@+id/messageLayout"
layout="@layout/list_item_msg_out"/>
<RelativeLayout
android:id="@+id/introductionLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|end"
android:background="@drawable/notice_out"
android:layout_marginLeft="@dimen/message_bubble_margin_non_tail"
android:layout_marginRight="@dimen/message_bubble_margin_tail">
<TextView
android:id="@+id/introductionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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
android:id="@+id/introductionTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/introductionText"
android:textColor="@color/private_message_date"
android:textSize="@dimen/text_size_tiny"
tools:text="Dec 24, 13:37"/>
<ImageView
android:id="@+id/introductionStatus"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/introductionTime"
android:layout_toRightOf="@+id/introductionTime"
android:layout_alignBottom="@+id/introductionTime"
android:layout_marginLeft="@dimen/margin_medium"
tools:ignore="ContentDescription"
tools:src="@drawable/message_delivered"/>
</RelativeLayout>
</LinearLayout>

View File

@@ -67,7 +67,7 @@
android:layout_alignParentRight="true"
android:layout_below="@+id/forumSubscribedView"
android:layout_marginTop="-8dp"
android:text="@string/dialog_button_accept"/>
android:text="@string/accept"/>
<Button
android:id="@+id/declineButton"
@@ -78,7 +78,7 @@
android:layout_marginTop="-8dp"
android:layout_toLeftOf="@+id/acceptButton"
android:layout_toStartOf="@+id/acceptButton"
android:text="@string/dialog_button_decline"/>
android:text="@string/decline"/>
<View
style="@style/Divider.ForumList"

View File

@@ -11,7 +11,6 @@
layout="@layout/list_item_msg_out"/>
<RelativeLayout
android:id="@+id/introductionLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|end"

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/noticeLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"

View File

@@ -7,7 +7,6 @@
android:orientation="vertical">
<RelativeLayout
android:id="@+id/noticeLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|end"

View File

@@ -0,0 +1,123 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:layout_marginTop="@dimen/listitem_horizontal_margin"
android:background="?attr/selectableItemBackground">
<TextView
android:id="@+id/titleView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:maxLines="2"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a RSS Feed"/>
<ImageView
android:id="@+id/deleteButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:contentDescription="@string/delete"
android:src="@drawable/action_delete_black"/>
<TextView
android:id="@+id/author"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/titleView"
android:layout_marginRight="@dimen/margin_small"
android:paddingTop="@dimen/margin_tiny"
android:text="@string/blogs_rss_feeds_manage_author"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"/>
<TextView
android:id="@+id/authorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/author"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toRightOf="@+id/author"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="Bruce Schneier"/>
<TextView
android:id="@+id/imported"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/author"
android:layout_marginRight="@dimen/margin_small"
android:paddingTop="@dimen/margin_tiny"
android:text="@string/blogs_rss_feeds_manage_imported"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"/>
<TextView
android:id="@+id/importedView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/imported"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toRightOf="@+id/imported"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="July 4"/>
<TextView
android:id="@+id/updated"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/imported"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/margin_small"
android:paddingTop="@dimen/margin_tiny"
android:text="@string/blogs_rss_feeds_manage_updated"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"/>
<TextView
android:id="@+id/updatedView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/updated"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toRightOf="@+id/updated"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="5 min. ago"/>
<TextView
android:id="@+id/descriptionView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/updated"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/margin_medium"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_small"
tools:text="This is a description of the RSS feed. It can be several lines long, but it can also not exist at all if it is not present in the feed itself."/>
<View
style="@style/Divider.ForumList"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_below="@+id/descriptionView"
android:layout_marginTop="@dimen/listitem_horizontal_margin"/>
</RelativeLayout>

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="wrap_content"
@@ -9,8 +8,9 @@
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="@dimen/listitem_height_contact_selector"
android:background="?attr/selectableItemBackground">
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:padding="@dimen/listitem_horizontal_margin">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
@@ -20,13 +20,10 @@
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"
tools:src="@drawable/ic_launcher"/>
<TextView
android:id="@+id/nameView"
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
@@ -35,10 +32,28 @@
android:layout_toEndOf="@+id/avatarView"
android:layout_toLeftOf="@+id/checkBox"
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"/>
android:orientation="vertical">
<TextView
android:id="@+id/nameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_large"
tools:text="This is a name of a contact"/>
<TextView
android:id="@+id/infoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:text="@string/forum_invitation_already_sharing"
android:textColor="@color/briar_text_tertiary"
android:textSize="@dimen/text_size_small"
tools:visibility="visible"/>
</LinearLayout>
<CheckBox
android:id="@+id/checkBox"
@@ -47,7 +62,6 @@
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:clickable="false"/>
</RelativeLayout>

View File

@@ -11,7 +11,6 @@
layout="@layout/list_item_msg_in"/>
<RelativeLayout
android:id="@+id/introductionLayout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left|start"
@@ -37,13 +36,13 @@
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/showForumsButton"
android:layout_below="@+id/showInvitationsButton"
android:textColor="@color/private_message_date"
android:textSize="@dimen/text_size_tiny"
tools:text="Dec 24, 13:37"/>
<Button
android:id="@+id/showForumsButton"
android:id="@+id/showInvitationsButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -51,7 +50,7 @@
android:layout_alignEnd="@+id/introductionText"
android:layout_alignRight="@+id/introductionText"
android:layout_below="@+id/introductionText"
android:text="@string/forum_show_invitations"/>
tools:text="@string/forum_show_invitations"/>
</RelativeLayout>

View File

@@ -7,7 +7,6 @@
tools:showIn="@layout/navigation_menu">
<ImageView
android:id="@+id/imageView2"
android:layout_width="100dp"
android:layout_height="100dp"
android:layout_gravity="top|center_horizontal"

View File

@@ -0,0 +1,23 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_blog_share"
android:icon="@drawable/social_share_white"
android:title="@string/blogs_sharing_share"
app:showAsAction="ifRoom"/>
<item
android:id="@+id/action_blog_sharing_status"
android:title="@string/sharing_status"
app:showAsAction="never"/>
<item
android:id="@+id/action_blog_delete"
android:icon="@drawable/action_delete_white"
android:title="@string/blogs_remove_blog"
app:showAsAction="never"/>
</menu>

View File

@@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_write_blog_post"
android:icon="@drawable/forum_item_create_white"
android:title="@string/blogs_write_blog_post"
app:showAsAction="always"/>
<item
android:id="@+id/action_rss_feeds_import"
android:title="@string/blogs_rss_feeds_import"
app:showAsAction="never"/>
<item
android:id="@+id/action_rss_feeds_manage"
android:title="@string/blogs_rss_feeds_manage"
app:showAsAction="never"/>
</menu>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_create_blog"
android:icon="@drawable/ic_add_white"
android:title="@string/blogs_my_blogs_create"
app:showAsAction="ifRoom"/>
</menu>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_write_blog_post"
android:icon="@drawable/forum_item_create_white"
android:title="@string/blogs_write_blog_post"
app:showAsAction="ifRoom"/>
</menu>

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_send_report"
android:icon="@drawable/social_send_now_white"
android:title="@string/send_report"
app:showAsAction="always"/>
</menu>

View File

@@ -17,7 +17,7 @@
<item
android:id="@+id/action_forum_sharing_status"
android:title="@string/forum_sharing_status"
android:title="@string/sharing_status"
app:showAsAction="never"/>
<item

View File

@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<menu
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_rss_feeds_import"
android:icon="@drawable/ic_add_white"
android:title="@string/blogs_rss_feeds_import"
app:showAsAction="always"/>
</menu>

View File

@@ -1,66 +1,46 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">Briar</string>
<string name="crash_report_title">Briar-Absturzberichte</string>
<string name="feedback_title">Feedback</string>
<string name="optional_contact_email">Optionale Kontaktemail</string>
<string name="include_debug_report">Absturzbericht anhängen?</string>
<string name="could_not_load_report_data">Konnte Daten des Berichts nicht laden</string>
<string name="ongoing_notification_title">In Briar angemeldet</string>
<string name="setup_title">Briar einrichten</string>
<!--Setup-->
<string name="setup_title">Fehler beim Starten von Briar</string>
<string name="setup_explanation">Dein Briar-Konto wird verschlüsselt auf deinem Gerät gespeichert und nicht mit der \"Cloud\" synchronisiert. Wenn du Briar deinstallierst oder dein Passwort vergisst, kannst du dein Konto und deine Daten nicht wiederherstellen.</string>
<string name="choose_nickname">Wähle deinen Benutzernamen</string>
<string name="choose_password">Wähle dein Passwort</string>
<string name="confirm_password">Passwort bestätigen</string>
<string name="name_too_long">Name ist zu lang</string>
<string name="password_too_weak">Passwort ist zu schwach</string>
<string name="passwords_do_not_match">Passwörter stimmen nicht überein</string>
<string name="create_account_button">Konto anlegen</string>
<string name="password_hint">Passwort</string>
<string name="forgotten_password">Ich habe mein Passwort vergessen</string>
<string name="progress_title_please_wait">Bitte warten...</string>
<!--Login-->
<string name="enter_password">Passwort eingeben:</string>
<string name="try_again">Passwort falsch, bitte erneut versuchen</string>
<string name="sign_in_button">Anmelden</string>
<string name="forgotten_password">Ich habe mein Passwort vergessen</string>
<string name="dialog_title_lost_password">Passwort vergessen</string>
<string name="dialog_message_lost_password">Dein Briar-Konto ist verschlüsselt auf deinem Gerät gespeichert und nicht mit der \"Cloud\" synchronisiert. Deshalb können wir dein Passwort nicht zurücksetzen. Willst du dein Konto löschen und neu beginnen?\n\nAchtung: Deine bestehenden Identitäten, Kontakte und Nachrichten gehen dann für immer verloren.</string>
<string name="startup_failed_notification_title">Briar konnte nicht gestartet werden</string>
<string name="startup_failed_notification_text">Möglicherweise hilft eine Neuinstallation von Briar.</string>
<string name="startup_failed_activity_title">Fehler beim Starten von Briar</string>
<string name="startup_failed_db_error">Deine Briar-Datenbank ist korrupt. Briar-Konto, Daten und alle Verbindungen zu Kontakten können nicht mehr wiederhergestellt werden. Deinstalliere Briar und erstelle nach Installation der aktuellen Briar-Version ein neues Konto.</string>
<string name="startup_failed_service_error">Briar konnte ein benötigtes Plugin nicht starten. Normalerweise kann das Problem durch eine Neuinstallation von Briar gelöst werden. Eine Neuinstallation führt jedoch zum Verlust des Kontos und aller dazugehörigen Daten, da Briar deine Daten nicht auf zentralen Servern speichert</string>
<string name="expiry_warning">Diese Version von Briar ist nicht mehr aktuell.\nBitte installiere eine neuere Version.</string>
<string name="dialog_title_welcome">Willkommen bei Briar</string>
<string name="dialog_welcome_message">Füge einen Kontakt hinzu, um mit verschlüsselter Kommunikation zu beginnen oder wähle das Icon in der oberen linken Ecke für mehr Optionen.</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Navigationsleiste öffnen</string>
<string name="nav_drawer_close_description">Navigationsleiste schliessen</string>
<string name="contact_list_button">Kontakte</string>
<string name="delete_contact">Kontakt löschen</string>
<string name="contact_deleted_toast">Kontakt gelöscht</string>
<string name="forums_button">Foren</string>
<string name="blogs_button">Mikroblogs</string>
<string name="settings_button">Einstellungen</string>
<string name="sign_out_button">Abmelden</string>
<string name="contact_list_title">Kontakte</string>
<string name="no_contacts">Keine Kontakte</string>
<string name="add_contact_title">Kontakt hinzufügen</string>
<string name="continue_button">Weiter</string>
<string name="connection_failed">Verbindung fehlgeschlagen</string>
<string name="try_again_button">Noch einmal versuchen</string>
<string name="connected_to_contact">Mit Kontakt verbunden</string>
<string name="codes_do_not_match">Codes stimmen nicht überein</string>
<string name="contact_added_toast">Kontakt hinzugefügt: %s</string>
<string name="contact_already_exists">Kontakt %s existiert bereits</string>
<string name="scan_qr_code">QR-Code scannen</string>
<string name="qr_code_invalid">Der QR-Code ist ungültig</string>
<string name="no_private_messages">Keine Nachrichten</string>
<string name="message_sent_toast">Nachricht wurde versendet</string>
<string name="show_forums">Anzeigen</string>
<string name="forum_leave">Forum verlassen</string>
<string name="forum_left_toast">Forum wurde verlassen</string>
<string name="create_forum_title">Neues Forum</string>
<string name="forum_created_toast">Forum wurde erstellt</string>
<string name="forum_share_button">Forum teilen</string>
<string name="forum_compose_post">Neuer Forenbeitrag</string>
<string name="from">Von:</string>
<string name="anonymous">Anonym</string>
<string name="new_identity_item">Neue Identität\u2026</string>
<string name="new_identity_title">Neue Identität</string>
<string name="create_identity_button">Identität erstellen</string>
<string name="identity_created_toast">Identität wurde erstellt</string>
<string name="forum_post_hint">Forenbeitrag eingeben</string>
<string name="available_forums_title">Verfügbare Foren</string>
<string name="forum_declined_toast">Foreneinladung abgelehnt</string>
<string name="shared_by_format">Geteilt durch %s</string>
<string name="forum_shared_by">Geteilt durch</string>
<string name="forum_shared_with">Geteilt mit</string>
<string name="nobody">Niemand</string>
<string name="add_button">Hinzufügen</string>
<string name="cancel_button">Abbrechen</string>
<string name="done_button">Fertig</string>
<string name="delete_button">Löschen</string>
<string name="post_sent_toast">Forenbeitrag wurde versendet</string>
<!--Transports-->
<string name="transport_tor">Internet</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">In Briar angemeldet</string>
<string name="ongoing_notification_text">Berühren, um Briar zu öffnen</string>
<plurals name="private_message_notification_text">
<item quantity="one">Neue private Nachricht.</item>
<item quantity="other">%d neue private Nachrichten</item>
@@ -69,8 +49,200 @@
<item quantity="one">Neuer Forenbeitrag</item>
<item quantity="other">%d neue Forenbeiträge</item>
</plurals>
<!--Settings-->
<string name="settings_title">Einstellungen</string>
<plurals name="blog_post_notification_text">
<item quantity="one">Neuer Blogeintrag.</item>
<item quantity="other">%d neue Blogbeiträge.</item>
</plurals>
<!--Misc-->
<string name="now">jetzt</string>
<string name="show">Anzeigen</string>
<string name="hide">Ausblenden</string>
<string name="ok">OK</string>
<string name="cancel">Abbrechen</string>
<string name="delete">Löschen</string>
<string name="accept">Annehmen</string>
<string name="decline">Ablehnen</string>
<string name="online">Online</string>
<string name="offline">Offline</string>
<string name="send">Senden</string>
<string name="no_data">Keine Daten</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nTipp das \"+\"-Icon am oberen Bildschirmrand an und folge den Anweisungen, um Freunde zu deiner Kontaktliste hinzuzufügen.\n\nDenk daran: Du kannst neue Kontakte nur dann hinzufügen, wenn du ihnen physisch begegnest. Das hindert andere daran, sich als dich auszugeben oder deine Nachrichten abzufangen.</string>
<string name="date_no_private_messages">Keine Nachrichten.</string>
<string name="no_private_messages">Das ist die Gesprächsansicht.\n\nEs scheint hier einen Mangel an Gesprächsthemen zu geben.\n\nTipp einfach auf das Eingabefeld am unteren Bildschirmrand, um ein Gespräch zu beginnen.</string>
<string name="delete_contact">Kontakt löschen</string>
<string name="dialog_title_delete_contact">Löschen des Kontakts bestätigen</string>
<string name="dialog_message_delete_contact">Bist du sicher, dass du diesen Kontakt und alle dazugehörigen Nachrichten löschen möchtest?</string>
<string name="contact_deleted_toast">Kontakt gelöscht</string>
<!--Adding Contacts-->
<string name="add_contact_title">Kontakt hinzufügen</string>
<string name="your_nickname">Wähle die zu verwendende Identität:</string>
<string name="face_to_face">Du kannst eine Person nur als Kontakt hinzufügen, wenn du ihr physisch begegnest. Das hindert andere daran, sich als dich auszugeben oder deine Nachrichten abzufangen.</string>
<string name="continue_button">Weiter</string>
<string name="your_invitation_code">Dein Einladungs-Code ist</string>
<string name="enter_invitation_code">Bitte gib den Einladungs-Code deines Kontakts an:</string>
<string name="searching_format">Such nach Kontakt mit Einladungs-Code %06d\u2026</string>
<string name="connection_failed">Verbindung fehlgeschlagen</string>
<string name="could_not_find_contact">Briar kann Deinen Kontakt in der Nähe nicht finden</string>
<string name="could_not_open_camera">Es konnte nicht auf die Kamera zugegriffen werden. Bitte versuche es erneut.</string>
<string name="try_again_button">Noch einmal versuchen</string>
<string name="connected_to_contact">Mit Kontakt verbunden</string>
<string name="calculating_confirmation_code">Bestätigungscode wird berechnet\u2026</string>
<string name="your_confirmation_code">Dein Bestätigungscode ist</string>
<string name="enter_confirmation_code">Bitte gebe den Bestätigungscode Deines Kontakts ein:</string>
<string name="waiting_for_contact">Warte auf Kontakt\u2026</string>
<string name="exchanging_contact_details">Kontaktdetails werden ausgetauscht\u2026</string>
<string name="codes_do_not_match">Codes stimmen nicht überein</string>
<string name="interfering">Das könnte daran liegen, dass jemand deine Verbindung stört</string>
<string name="contact_added_toast">Kontakt hinzugefügt: %s</string>
<string name="contact_already_exists">Kontakt %s existiert bereits</string>
<string name="contact_exchange_failed">Kontaktaustausch fehlgeschlagen</string>
<string name="qr_code_invalid">Der QR-Code ist ungültig</string>
<string name="connecting_to_device">Verbinde mit Gerät\u2026</string>
<string name="authenticating_with_device">Authentifiziere Gerät\u2026</string>
<string name="connection_aborted_local">Verbindung wurde durch uns abgebrochen! Das könnte daran liegen, dass jemand deine Verbindung stört.</string>
<string name="connection_aborted_remote">Verbindung wurde durch deinen Kontakt abgebrochen! Das könnte daran liegen, dass jemand deine Verbindung stört.</string>
<!--Introductions-->
<string name="introduction_activity_title">Kontakt auswählen</string>
<string name="introduction_message_title">Kontakte einander empfehlen</string>
<string name="introduction_message_text">Du kannst eine Nachrircht verfassen, welche mit deiner Kontaktempfehlung an %1$s und %2$s mitgesendet wird.</string>
<string name="introduction_message_hint">Gib eine Nachricht ein (optional)</string>
<string name="introduction_button">Kontaktempfehlung abgeben</string>
<string name="introduction_sent">Deine Kontaktempfehlung wurde verschickt</string>
<string name="introduction_error">Es gab einen Fehler beim Versuch, die Kontaktempfehlung zu verschicken</string>
<string name="introduction_response_error">Fehler bei Antwort auf Kontaktempfehlung</string>
<string name="introduction_warn_different_identities_title">Vorsicht: Verschiedene Identitäten</string>
<string name="introduction_warn_different_identities_text">Du versucht zwei Kontakte zu empfehlen, welche du mit zwei verschiedenen Identitäten hinzugefügt hast. Es bedeutet wahrscheinlich, dass beide Identitäten zu dir gehören.</string>
<string name="dialog_button_introduce">Als Kontakt empfehlen</string>
<string name="introduction_request_sent">Du wolltest %1$s an %2$s als Kontakt empfehlen</string>
<string name="introduction_request_received">%1$s schlägt vor, dich als Kontakt an %2$s zu empfehlen. Möchtest Du %2$s zu deiner Kontaktliste hinzufügen?</string>
<string name="introduction_request_exists_received">%1$s schlägt vor, dich als Kontakt an %2$s zu empfehlen. %2$s ist allerdings bereits in deiner Kontaktliste. Da %1$s das vielleicht nicht weiss, kannst du trotzdem antworten:</string>
<string name="introduction_request_for_our_identity_received">%1$s schlägt vor, dich als Kontakt an %2$s zu empfehlen, aber %2$s ist eine deiner eigenen Identitäten. Deshalb kannst du die Empfehlung nicht annehmen:</string>
<string name="introduction_request_answered_received">%1$s schlägt vor, dich als Kontakt an %2$s zu empfehlen. </string>
<string name="introduction_response_accepted_sent">Du hast die Kontaktempfehlung für %1$s angenommen.</string>
<string name="introduction_response_declined_sent">Du hast die Kontaktempfehlung von %1$s abgelehnt.</string>
<string name="introduction_response_accepted_received">%1$s hat die Kontaktempfehlung für %2$s angenommen.</string>
<string name="introduction_response_declined_received">%1$s hat deine Kontaktempfehlung von %2$s abgelehnt.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s meldet, dass %2$s die Kontaktempfehlung abgelehnt hat.</string>
<string name="introduction_success_title">Empfohlener Kontakt wurde hinzugefügt.</string>
<string name="introduction_success_text">%1$s wurde dir als Kontakt empfohlen.</string>
<!--Forums-->
<string name="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Icon am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
<string name="create_forum_title">Neues Forum</string>
<string name="choose_forum_name">Wähle einen Namen für dein Forum:</string>
<string name="create_forum_button">Forum erstellen</string>
<string name="forum_created_toast">Forum wurde erstellt</string>
<string name="no_forum_posts">Dieses Forum ist leer.\n\nBenutze das Stift-Icon am oberen Bildschirmrand um den ersten Beitrag zu verfassen.\n\nFühlst du dich einsam hier? Dann teile das Forum mit weiteren Kontakten!</string>
<string name="no_posts">Keine Beiträge</string>
<plurals name="posts">
<item quantity="one">%d Beitrag</item>
<item quantity="other">%d Beiträge</item>
</plurals>
<string name="forum_compose_post">Neuer Forenbeitrag</string>
<string name="forum_new_entry_posted">Forenbeitrag wurde veröffentlicht.</string>
<string name="forum_new_entry_received">Neuer Forenbeitrag</string>
<string name="forum_new_message_hint">Neuer Eintrag</string>
<string name="forum_message_reply_hint">Neue Antwort</string>
<string name="btn_reply">ANTWORTEN</string>
<plurals name="message_replies">
<item quantity="one">%1$d Antwort</item>
<item quantity="other">%1$d Antworten</item>
</plurals>
<string name="forum_leave">Forum verlassen</string>
<string name="dialog_title_leave_forum">Verlassen des Forums bestätigen</string>
<string name="dialog_message_leave_forum">Bist du sicher, dass du dieses Forum verlassen willst? Kontakte, die du mit diesem Forum geteilt hast, werden keine Updates von diesem Forum mehr bekommen.</string>
<string name="dialog_button_leave">Verlassen</string>
<string name="forum_left_toast">Forum wurde verlassen</string>
<!--Forum Sharing-->
<string name="forum_share_button">Forum teilen</string>
<string name="forum_share_action">Teile dieses Forum mit den gewählten Kontakten</string>
<string name="activity_share_toolbar_header">Kontakte auswählen</string>
<string name="no_contacts_selector">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nBitte komm zurück, wenn du deinen ersten Kontakt hinzugefügt hast.</string>
<string name="forum_shared_snackbar">Forum mit gewählten Kontakten geteilt</string>
<string name="forum_share_message">Du kannst eine optionale Einladungsnachricht verfassen, die an die ausgewählten Kontakte geschickt wird.</string>
<string name="forum_share_error">Es gab einen Fehler beim Versuch, dieses Forum zu teilen.</string>
<string name="forum_invitation_received">%1$s hat das Forum \"%2$s\" mit dir geteilt.</string>
<string name="forum_invitation_sent">Du hast das Forum \"%1$s\" mit %2$s geteilt.</string>
<string name="forum_show_invitations">Foreneinladungen anzeigen</string>
<string name="forum_invitations_title">Foreneinladungen</string>
<string name="forum_invitation_exists">Du hast bereits eine Einladung zu diesem Forum angenommen. Wenn du weitere Einladungen annimmst, wird die Kommunikation in dem Forum einfacher und das Forum wird wachsen.</string>
<string name="forum_joined_toast">Dem Forum beigetreten</string>
<string name="forum_declined_toast">Foreneinladung abgelehnt</string>
<string name="shared_by_format">Geteilt durch %s</string>
<string name="forum_invitation_already_sharing">Bereits geteilt.</string>
<string name="forum_invitation_response_accepted_sent">Du hast die Forumseinladung von %s akzeptiert.</string>
<string name="forum_invitation_response_declined_sent">Du hast die Forumeinladung von %s abgelehnt.</string>
<string name="forum_invitation_response_accepted_received">%s hat die Forumeinladung akzeptiert.</string>
<string name="forum_invitation_response_declined_received">%s hat die Forumseinladung abgelehnt.</string>
<string name="sharing_status">Sharing Status</string>
<plurals name="forums_shared">
<item quantity="one">%d Forum von Kontaken geteilt</item>
<item quantity="other">%d Foren von Kontakten geteilt</item>
</plurals>
<string name="forum_shared_by">Geteilt durch</string>
<string name="forum_shared_with">Geteilt mit</string>
<string name="nobody">Niemand</string>
<!--Blogs-->
<string name="blogs_feed">Feed</string>
<string name="blogs_my_blogs">Meine Blogs</string>
<string name="blogs_my_blogs_create">Blog erstellen</string>
<string name="blogs_my_blogs_label">Blog hinzufügen</string>
<string name="blogs_my_blogs_create_hint_title">Blog-Titel (kann nicht mehr geändert werden)</string>
<string name="blogs_my_blogs_create_hint_desc">Eine kurze Beschreibung deines neuen Blogs</string>
<string name="blogs_my_blogs_create_hint_desc_explanation">Der Inhalt der Beschreibung hilft möglichen Leserinnen und Lesern, sich zu entscheiden, ob sie dein Blog abonnieren möchten.</string>
<string name="blogs_my_blogs_empty_state">Du hast noch keine Blogs.\n\nWarum erstellst du nicht einfach selbst ein neues Blog, indem du auf das \"+\"-Icon am oberen Bildschirmrand tippst?</string>
<string name="blogs_my_blogs_blog_empty_state">Das ist der Bereich für deinen Bloginhalt.\n\nlt es scheint als hättest du noch nichts geschrieben.\n\nBenutze das Stiftsymbol um einen neuen Blogeintrag zu erstellen.\n\nVergiss nicht deinen Blog zu veröffentlichen.</string>
<string name="blogs_my_blogs_created">Blog wurde erstellt</string>
<string name="blogs_blog_is_empty">Dieses Blog ist leer</string>
<string name="blogs_other_blog_empty_state">Dieser Blog ist momentan leer.\n\nEntweder hat der Autor noch nichts geschrieben, oder die Person, die diesen Blog mit dir geteilt hat, muss online kommen, sodass Beiträge synchronisiert werden können.</string>
<string name="tag_new">NEU</string>
<string name="blogs_write_blog_post">Blogbeitrag erstellen</string>
<string name="blogs_write_blog_post_title_hint">Titel hinzufügen (optional)</string>
<string name="blogs_write_blog_post_body_hint">Gib hier deinen Blogbeitrag ein</string>
<string name="blogs_publish_blog_post">Veröffentlichen</string>
<string name="blogs_blog_post_created">Blogbeitrag erstellt</string>
<string name="blogs_blog_post_received">Neuen Blogbeitrag empfangen</string>
<string name="blogs_blog_post_scroll_to">Scrolle zu</string>
<string name="blogs_blog_failed_to_load">Blog konnte nicht geladen werden</string>
<string name="blogs_blog_post_failed_to_load">Blogbeitrag konnte nicht geladen werden</string>
<string name="blogs_feed_empty_state">Dies ist die globale Blog-Zeitleiste.\n\nOffensichtlich hat noch niemand etwas veröffentlicht.\n\nSei die oder der erste und tipp auf das Stift-Icon, um einen neuen Blogbeitrag zu verfassen.</string>
<string name="blogs_personal_blog">%ss persönliches Blog</string>
<string name="blogs_remove_blog">Blog entfernen</string>
<string name="blogs_remove_blog_dialog_message">Bist du sicher, dass du diesen Blog und alle dazugehörigen Beiträge löschen möchtest?\nBeachte, dass dies nicht den Blog auf Geräten anderer Leute löscht.</string>
<string name="blogs_remove_blog_ok">Blog entfernen</string>
<string name="blogs_blog_removed">Blog wurde entfernt</string>
<string name="blogs_blog_list">Blogliste</string>
<string name="blogs_available_blogs">Verfügbare Blogs</string>
<string name="blogs_drafts">Entwürfe</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Blog teilen</string>
<string name="blogs_sharing_error">Es gab einen Fehler beim Versuch, dieses Blog zu teilen</string>
<string name="blogs_sharing_button">Blog teilen</string>
<string name="blogs_sharing_snackbar">Blog wurde mit ausgewählten Kontakten geteilt</string>
<string name="blogs_sharing_response_accepted_sent">Du hast die Blogeinladung von %s akzeptiert.</string>
<string name="blogs_sharing_response_declined_sent">Du hast die Blogeinladung von %s abgelehnt.</string>
<string name="blogs_sharing_response_accepted_received">%s hat die Blogeinladung akzeptiert.</string>
<string name="blogs_sharing_response_declined_received">%s hat die Blogeinladung abgelehnt.</string>
<string name="blogs_sharing_invitation_received">%1$s hat das persönliche Blog von %2$s mit dir geteilt.</string>
<string name="blogs_sharing_invitation_sent">Du hast das persönliche Blog von %1$s mit %2$s geteilt.</string>
<string name="blogs_sharing_show_invitations">Blogeinladungen anzeigen</string>
<string name="blogs_sharing_invitations_title">Blogeinladungen</string>
<string name="blogs_sharing_exists">Du hast dieses Blog bereits abonniert. Ein neues Abo anzunehmen kann dazu führen, dass neue Einträge schneller verfügbar werden.</string>
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
<string name="blogs_sharing_declined_toast">Blogeinladung abgelehnt</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">RSS-Feed importieren</string>
<string name="blogs_rss_feeds_import_button">Importieren</string>
<string name="blogs_rss_feeds_import_hint">URL des RSS-Feeds eingeben</string>
<string name="blogs_rss_feeds_import_error">Es tut uns Leid! Es gab einen Fehler beim Importieren deines Feeds.</string>
<string name="blogs_rss_feeds_manage">RSS-Feeds verwalten</string>
<string name="blogs_rss_feeds_manage_imported">Importiert:</string>
<string name="blogs_rss_feeds_manage_author">Autor:</string>
<string name="blogs_rss_feeds_manage_updated">Zuletzt aktualisiert:</string>
<string name="blogs_rss_feeds_manage_delete_error">Der Feed konnte nicht gelöscht werden!</string>
<string name="blogs_rss_feeds_manage_empty_state">Du hast bisher noch keine RSS Feeds importiert. Tippe auf das \"+\"-Icon am oberen Bildschirmrand um einen neuen Feed hinzuzufügen.</string>
<string name="blogs_rss_feeds_manage_error">Es gab ein Problem beim Laden deiner Feeds. Bitte versuche es später erneut.</string>
<!--Settings Network-->
<string name="network_settings_title">Netzwerke</string>
<string name="bluetooth_setting">Mit Bluetooth verbinden</string>
<string name="bluetooth_setting_enabled">Sobald Kontakte in der Nähe sind</string>
@@ -78,57 +250,68 @@
<string name="tor_mobile_setting">Mit Tor verbinden</string>
<string name="tor_mobile_setting_enabled">Wenn Wi-Fi oder Mobildaten verwendet werden</string>
<string name="tor_mobile_setting_disabled">Nur wenn Wi-Fi verwendet wird</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Sicherheit</string>
<string name="change_password">Passwort ändern</string>
<string name="current_password">Jetziges Passwort eingeben:</string>
<string name="choose_new_password">Neues Passwort eingeben:</string>
<string name="confirm_new_password">Neues Passwort bestätigen:</string>
<string name="password_changed">Passwort wurde geändert.</string>
<string name="panic_setting">Panik-Button einrichten</string>
<string name="panic_setting_title">Panik-Button</string>
<string name="panic_setting_hint">Lege fest, wie Briar auf eine Panik-Button-App reagiert</string>
<string name="panic_app_setting_title">Panik-Button-App</string>
<string name="unknown_app">eine unbekannte App</string>
<string name="panic_app_setting_summary">Keine App wurde eingerichtet</string>
<string name="panic_app_setting_none">Keine</string>
<string name="dialog_title_connect_panic_app">Bestätige Panic App</string>
<string name="dialog_message_connect_panic_app">Bist du sicher, dass du %1$s ermöglichen willst, Panik-Button-Ereignisse auszulösen, die zum Löschen von Daten führen?</string>
<string name="lock_setting_title">Abmelden</string>
<string name="lock_setting_summary">Von Briar abmelden, wenn ein Panik-Button aktiviert wird</string>
<string name="purge_setting_title">Konto löschen</string>
<string name="purge_setting_summary">Briar-Konto löschen, wenn der Panik-Button gedrückt wird. Vorsicht: Dies wird alle Identitäten, Kontakte und Nachrichten für immer löschen.</string>
<string name="uninstall_setting_title">Briar deinstallieren</string>
<string name="uninstall_setting_summary">Diese Aktion benötigt manuelle Bestätigung im Falle eines Panik-Ereignisses</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Benachrichtigungen</string>
<string name="notify_private_messages_setting">Benachrichtigungen für private Nachrichten anzeigen</string>
<string name="notify_forum_posts_setting">Benachrichtigungen für Forenbeiträge anzeigen</string>
<string name="notify_blog_posts_setting">Benachrichtigungen für Blog-Beiträge anzeigen</string>
<string name="notify_vibration_setting">Vibrieren</string>
<string name="notify_sound_setting">Tonsignal</string>
<string name="notify_sound_setting_default">Standardklingelton</string>
<string name="notify_sound_setting_disabled">Kein</string>
<string name="choose_ringtone_title">Klingelton auswählen</string>
<!--Settings Crash reports and feedback-->
<string name="crash_report_settings_title">Absturzberichte</string>
<string name="enable_acra_setting">Absturzberichte aktivieren</string>
<string name="acra_syslog_setting">Systemlogdateien senden</string>
<string name="acra_user_email_setting">Optionale Kontaktemail</string>
<string name="acra_alwaysaccept_setting">Berichte immer versenden</string>
<string name="panic_app_setting_title">Panik-Button-App</string>
<string name="unknown_app">eine unbekannte App</string>
<string name="panic_app_setting_summary">Keine App wurde eingerichtet</string>
<string name="panic_app_setting_none">Keine</string>
<string name="lock_setting_title">Abmelden</string>
<string name="lock_setting_summary">Von Briar abmelden, wenn ein Panik-Button aktiviert wird</string>
<string name="purge_setting_title">Konto löschen</string>
<string name="uninstall_setting_title">Briar deinstallieren</string>
<string name="feedback_settings_title">Feedback</string>
<string name="online">Online</string>
<string name="offline">Offline</string>
<string name="send">Senden</string>
<string name="transport_tor">Internet</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wi-Fi</string>
<string name="no_data">Keine Daten</string>
<!--Introduction Client-->
<string name="introduction_activity_title">Kontakt auswählen</string>
<!--Forum-->
<!--Dialogs-->
<string name="dialog_title_lost_password">Passwort vergessen</string>
<string name="dialog_title_delete_contact">Löschen des Kontakts bestätigen</string>
<string name="dialog_title_welcome">Willkommen bei Briar</string>
<string name="dialog_title_share_crash_report">Briar ist abgestürzt</string>
<string name="dialog_title_leave_forum">Verlassen des Forums bestätigen</string>
<string name="dialog_button_ok">OK</string>
<string name="dialog_button_leave">Verlassen</string>
<string name="dialog_button_accept">Annehmen</string>
<string name="dialog_button_decline">Ablehnen</string>
<!--Toolbar headers-->
<string name="dashboard_toolbar_header">Briar</string>
<string name="settings_toolbar_header">Einstellungen</string>
<string name="contacts_toolbar_header">Kontakte</string>
<string name="forums_toolbar_header">Foren</string>
<string name="forums_share_toolbar_header">Kontakte auswählen</string>
<!--Progress titles-->
<string name="progress_title_please_wait">Bitte warten...</string>
<!--Blogs-->
<string name="send_feedback">Feedback abschicken</string>
<!--Multiple Identities-->
<string name="anonymous">Anonym</string>
<string name="new_identity_item">Neue Identität\u2026</string>
<string name="new_identity_title">Neue Identität</string>
<string name="create_identity_button">Identität erstellen</string>
<string name="identity_created_toast">Identität wurde erstellt</string>
<!--Crash Reporter-->
<string name="crash_report_title">Briar-Absturzbericht</string>
<string name="briar_crashed">Es tut uns leid, Briar ist abgestürzt.</string>
<string name="not_your_fault">Das ist nicht deine Schuld.</string>
<string name="please_send_report">Bitte hilf uns, Briar zu verbessern, indem du einen Absturzbericht sendest.</string>
<string name="report_is_encrypted">Wir versprechen, dass der Bericht verschlüsselt ist und über eine sichere Verbindung geschickt wird.</string>
<string name="feedback_title">Feedback</string>
<string name="describe_crash">Beschreibe was passiert ist (optional)</string>
<string name="enter_feedback">Gib dein Feedback ein</string>
<string name="optional_contact_email">Deine E-Mail-Adresse (optional)</string>
<string name="include_debug_report_crash">Anonymisierte Daten über den Absturz anhängen</string>
<string name="include_debug_report_feedback">Anonymisierte Daten über dieses Gerät anhängen</string>
<string name="could_not_load_report_data">Konnte Daten des Berichts nicht laden</string>
<string name="send_report">Bericht abschicken</string>
<string name="close">Schließen</string>
<string name="dev_report_saved">Der Bericht wurde gespeichert. Er wird verschickt, wenn du dich das nächste Mal bei Briar anmeldest</string>
<!--Sign Out-->
<string name="progress_title_logout">Von Briar abmelden...</string>
</resources>

View File

@@ -0,0 +1,316 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<!--Setup-->
<string name="setup_title">Configuración de Briar</string>
<string name="setup_explanation">Tu cuenta de Briar se almacena cifrada en tu teléfono, no en ninguna nube. Si desinstalas Briar u olvidas tu contraseña, no podrás recuperar ni tu cuenta, ni tus datos.</string>
<string name="choose_nickname">Elige tu nombre de usuario</string>
<string name="choose_password">Elige tu contraseña</string>
<string name="confirm_password">Confirma tu contraseña</string>
<string name="name_too_long">El nombre es demasiado largo</string>
<string name="password_too_weak">La contraseña es demasiado débil</string>
<string name="passwords_do_not_match">Las contraseñas no coinciden</string>
<string name="create_account_button">Registrar una nueva cuenta</string>
<string name="progress_title_please_wait">Espera, por favor</string>
<!--Login-->
<string name="enter_password">Introduce tu contraseña:</string>
<string name="try_again">Contraseña incorrecta, inténtalo de nuevo</string>
<string name="sign_in_button">Identificarse</string>
<string name="forgotten_password">Olvidé mi contraseña</string>
<string name="dialog_title_lost_password">Contraseña olvidada</string>
<string name="dialog_message_lost_password">Tu cuenta de Briar se almacena cifrada en tu teléfono, no en ninguna nube, así que no podemos reiniciar tu contraseña. ¿Deseas eliminar tu cuenta y empezar de nuevo?\n\nAdvertencia: Tus identidades, contactos y mensajes se perderán para siempre.</string>
<string name="startup_failed_notification_title">Briar no pudo iniciarse</string>
<string name="startup_failed_notification_text">Quizá tengas que reinstalar Briar.</string>
<string name="startup_failed_activity_title">Fallo al iniciar Briar</string>
<string name="startup_failed_db_error">Por alguna razón, la base de datos de Briar ha sufrido daños irreparables. Tu cuenta, tus datos y todos tus contactos se han perdido. Desafortunadamente, tendrás que reinstalar Briar y registrar una nueva cuenta.</string>
<string name="startup_failed_service_error">Briar no pudo iniciar un complemento necesario. Reinstalar Briar suele solucionar el problema. Sin embargo, ten en cuenta que perderás tu cuenta y todos los datos asociados ya que Briar no almacena esta información en ningún servidor centrar.</string>
<string name="expiry_warning">Esta versión ha caducado.\nInstala una más reciente, por favor.</string>
<string name="dialog_title_welcome">Bienvenido a Briar</string>
<string name="dialog_welcome_message">Añade un contacto para empezar a comunicarte de forma segura o pulsa el icono de la esquina superior izquierda de la pantalla para otras opciones.</string>
<!--Navigation Drawer-->
<string name="nav_drawer_open_description">Abrir el panel de navegación</string>
<string name="nav_drawer_close_description">Cierra el panel de navegación</string>
<string name="contact_list_button">Contactos</string>
<string name="forums_button">Foros</string>
<string name="blogs_button">Micro blogs</string>
<string name="settings_button">Configuración</string>
<string name="sign_out_button">Cerrar la sesión</string>
<!--Transports-->
<string name="transport_tor">Internet</string>
<string name="transport_bt">Bluetooth</string>
<string name="transport_lan">Wi-Fi</string>
<!--Notifications-->
<string name="ongoing_notification_title">Conectado a Briar</string>
<string name="ongoing_notification_text">Toca para abrir Briar.</string>
<plurals name="private_message_notification_text">
<item quantity="one">Tienes un nuevo mensaje privado.</item>
<item quantity="other">Tienes %d nuevos mensajes privados.</item>
</plurals>
<plurals name="forum_post_notification_text">
<item quantity="one">Hay una nueva publicación en el foro.</item>
<item quantity="other">Hay %d nuevas publicaciones en el foro.</item>
</plurals>
<plurals name="blog_post_notification_text">
<item quantity="one">Hay una nueva entrada de blog.</item>
<item quantity="other">Hay %d nuevas entradas de blog.</item>
</plurals>
<!--Misc-->
<string name="now">ahora</string>
<string name="show">Mostrar</string>
<string name="hide">Ocultar</string>
<string name="ok">De acuerdo</string>
<string name="cancel">Cancelar</string>
<string name="delete">Borrar</string>
<string name="accept">Aceptar</string>
<string name="decline">Rechazar</string>
<string name="online">En línea</string>
<string name="offline">Desconectado</string>
<string name="send">Enviar</string>
<string name="no_data">Sin datos</string>
<!--Contacts and Private Conversations-->
<string name="no_contacts">Parece que eres nuevo por aquí y no tienes aún contactos.\n\nPulsa el icono + en la parte superior y sigue las instrucciones para añadir amigos a tu lista.\n\nPor favor, recuerda: sólo puedes añadir nuevos contactos cara a cara para evitar que nadie suplante tu identidad o lea tus mensajes en el futuro. </string>
<string name="date_no_private_messages">Sin mensajes.</string>
<string name="no_private_messages">Esta es la vista de conversación.\n\nParece que aún no hay tal.\n\nPulsa el campo de texto en la parte inferior para empezar la conversación.</string>
<string name="delete_contact">Eliminar contacto</string>
<string name="dialog_title_delete_contact">Confirmar eliminación de contacto</string>
<string name="dialog_message_delete_contact">¿Seguro que quieres eliminar este contacto y todos los mensajes intercambiados entre vosotros?</string>
<string name="contact_deleted_toast">Contacto eliminado</string>
<!--Adding Contacts-->
<string name="add_contact_title">Añadir un contacto</string>
<string name="your_nickname">Elige la identidad que quieres usar:</string>
<string name="face_to_face">Debes estar cara a cara con la persona que quieres añadir como contacto. De esta manera se evita que nadie suplante tu identidad o lea tus mensajes en el futuro.</string>
<string name="continue_button">Continuar</string>
<string name="your_invitation_code">Tu código de invitación es</string>
<string name="enter_invitation_code">Por favor, introduce el código de invitación de tu contacto:</string>
<string name="searching_format">Buscando el contacto con el código de invitación %06d\u2026</string>
<string name="connection_failed">La conexión falló</string>
<string name="could_not_find_contact">Briar no pudo encontrar a tu contacto cerca</string>
<string name="try_again_button">Prueba de nuevo</string>
<string name="connected_to_contact">Conectado con el contacto</string>
<string name="calculating_confirmation_code">Calculando código de confirmación\u2026</string>
<string name="your_confirmation_code">Tu código de confirmación es</string>
<string name="enter_confirmation_code">Por favor, introduce el código de confirmación de tu contacto:</string>
<string name="waiting_for_contact">Esperando al contacto\u2026</string>
<string name="exchanging_contact_details">Intercambiando información de contacto\u2026</string>
<string name="codes_do_not_match">Los códigos no concuerdan</string>
<string name="interfering">Esto podría significar que alguien está intentando interceptar la conexión</string>
<string name="contact_added_toast">Contacto añadido: %s</string>
<string name="contact_already_exists">El contacto %s ya existe</string>
<string name="contact_exchange_failed">El intercambio del contacto falló</string>
<string name="qr_code_invalid">El código QR no es válido</string>
<string name="connecting_to_device">Conectado al dispositivo\u2026</string>
<string name="authenticating_with_device">Autentificándose con el dispositivo\u2026</string>
<string name="connection_aborted_local">¡Hemos interumpido la conexión! Esto podría significar que alguien está intentando interceptar la conexión</string>
<string name="connection_aborted_remote">¡La conexión ha sido interrumpida por tu contacto! Esto podría significar que alguien está intentando interceptar la conexión</string>
<!--Introductions-->
<string name="introduction_activity_title">Seleccionar contacto</string>
<string name="introduction_message_title">Presentar a dos contactos</string>
<string name="introduction_message_text">Puedes redactar un mensaje que se enviará a %1$s y a %2$s junto con tu presentación:</string>
<string name="introduction_message_hint">Escribe un mensaje (opcional)</string>
<string name="introduction_button">Realizar la presentación</string>
<string name="introduction_sent">Tu presentación se ha mandado.</string>
<string name="introduction_error">Ocurrió un error realizando la presentación.</string>
<string name="introduction_response_error">Error al responder a la presentación</string>
<string name="introduction_warn_different_identities_title">Advertencia: identidades diferentes</string>
<string name="introduction_warn_different_identities_text">Estás intentando presentar a dos contactos que añadiste con identidades distintas. Esto quizá pueda revelar que esas dos identidades te pertenecen.</string>
<string name="dialog_button_introduce">Presentar</string>
<string name="introduction_request_sent">Has pedido presentar a %1$s a %2$s.</string>
<string name="introduction_request_received">%1$s ha pedido presentarte a %2$s. ¿Quieres añadir a %2$s a tu lista de contactos?</string>
<string name="introduction_request_exists_received">%1$s ha pedido presentarte a %2$s, pero %2$s ya está en tu lista de contactos. %1$s puede no saberlo, así que puedes responderle de todas maneras:</string>
<string name="introduction_request_for_our_identity_received">%1$s ha pedido presentarte a %2$s, pero %2$s es una de tus otras identidades, así que no puedes aceptar la presentación:</string>
<string name="introduction_request_answered_received">%1$s ha pedido presentarte a %2$s.</string>
<string name="introduction_response_accepted_sent">Has aceptado la presentación a %1$s.</string>
<string name="introduction_response_declined_sent">Has rechazado la presentación a %1$s.</string>
<string name="introduction_response_accepted_received">%1$s aceptó la presentación de %2$s.</string>
<string name="introduction_response_declined_received">%1$s rechazó la presentación a %2$s.</string>
<string name="introduction_response_declined_received_by_introducee">%1$s dice que %2$s rechazó la presentación.</string>
<string name="introduction_success_title">Se ha añadido al contacto presentado</string>
<string name="introduction_success_text">Te han presentado a %1$s.</string>
<!--Forums-->
<string name="no_forums">No tienes ningún foro aún.\n\n¿Por qué no creas uno pulsando el icono + de la parte superior?\n\nTambién puedes pedirle a tus contactos que compartan foros contigo.</string>
<string name="create_forum_title">Nuevo foro</string>
<string name="choose_forum_name">Elige un nombre para tu foro:</string>
<string name="create_forum_button">Crear foro</string>
<string name="forum_created_toast">Foro creado</string>
<string name="no_forum_posts">Este foro está vacío.\n\nUsa el icono del lápiz de la parte superior para redactar una primera publicación.\n\n¿No te sientes un poco solo aquí? ¡Comparte este foro con más contactos!</string>
<string name="no_posts">Sin publicaciones</string>
<plurals name="posts">
<item quantity="one">%d publicación</item>
<item quantity="other">%d publicaciones</item>
</plurals>
<string name="forum_compose_post">Nueva publicación en el foro</string>
<string name="forum_new_entry_posted">Publicada entrada en el foro</string>
<string name="forum_new_entry_received">Nueva entrada en el foro</string>
<string name="forum_new_message_hint">Nueva entrada</string>
<string name="forum_message_reply_hint">Nueva respuesta</string>
<string name="btn_reply">RESPONDER</string>
<plurals name="message_replies">
<item quantity="one">%1$d respuesta</item>
<item quantity="other">%1$d respuestas</item>
</plurals>
<string name="forum_leave">Abandonar foro</string>
<string name="dialog_title_leave_forum">Confirmación abandono del foro</string>
<string name="dialog_message_leave_forum">¿Seguro que quieres abandonar el foro? Puede que los contactos que invitaste al foro dejen de recibir actualizaciones del mismo.</string>
<string name="dialog_button_leave">Abandonar</string>
<string name="forum_left_toast">Foro abandonado</string>
<!--Forum Sharing-->
<string name="forum_share_button">Compartir foro</string>
<string name="forum_share_action">Compartir este foro con los contactos seleccionados</string>
<string name="activity_share_toolbar_header">Elige contactos</string>
<string name="no_contacts_selector">Parece que eres nuevo aquí y no tienes contactos aún.\n\nPor favor, vuelve cuando hayas añadido tu primer contacto.</string>
<string name="forum_shared_snackbar">Foro compartido con los contactos seleccionados</string>
<string name="forum_share_message">Puedes redactar un mensaje de invitación opcional que se enviará a los contactos seleccionados.</string>
<string name="forum_share_error">Hubo un error compartiendo este foro.</string>
<string name="forum_invitation_received">%1$s ha compartido el foro \"%2$s\" contigo.</string>
<string name="forum_invitation_sent">Has compartido el foro \"%1$s\" con %2$s.</string>
<string name="forum_show_invitations">Mostrar invitaciones a foros</string>
<string name="forum_invitations_title">Invitaciones a foros</string>
<string name="forum_invitation_exists">Ya aceptaste la invitación a este foro. Aceptar más invitaciones aumentará y fortalecerá la comunicación con el foro.</string>
<string name="forum_joined_toast">Te uniste al foro</string>
<string name="forum_declined_toast">Rechazada la invitación al foro</string>
<string name="shared_by_format">Compartido por %s</string>
<string name="forum_invitation_already_sharing">Ya se está compartiendo</string>
<string name="forum_invitation_response_accepted_sent">Aceptaste la invitación al foro de %s.</string>
<string name="forum_invitation_response_declined_sent">Rechazaste la invitación al foro de %s.</string>
<string name="forum_invitation_response_accepted_received">%s aceptó la invitación al foro.</string>
<string name="forum_invitation_response_declined_received">%s rechazó la invitación al foro.</string>
<string name="sharing_status">Estado de la compartición</string>
<plurals name="forums_shared">
<item quantity="one">%d foro compartido por contactos</item>
<item quantity="other">%d foros compartidos por contactos</item>
</plurals>
<string name="forum_shared_by">Compartido por</string>
<string name="forum_shared_with">Compartido con</string>
<string name="nobody">Nadie</string>
<!--Blogs-->
<string name="blogs_feed">Lista de entradas</string>
<string name="blogs_my_blogs">Mis blogs</string>
<string name="blogs_my_blogs_create">Crear blog</string>
<string name="blogs_my_blogs_label">Añadir nuevo blog</string>
<string name="blogs_my_blogs_create_hint_title">Título del blog (no se podrá cambiar más tarde)</string>
<string name="blogs_my_blogs_create_hint_desc">Pequeña descripción de tu nuevo blog</string>
<string name="blogs_my_blogs_create_hint_desc_explanation">Los lectores en potencia podrán o no suscribirse a tu blog en función de la descripción</string>
<string name="blogs_my_blogs_empty_state">No tienes blogs.\n\n¿Por qué no creas uno ahora pulsando en el más de la esquina superior derecha de la pantalla?</string>
<string name="blogs_my_blogs_blog_empty_state">Este es el espacio para el contenido de tu blog.\n\nParece que no has escrito nada aún.\n\nPor favor, pulsa el icono del lápiz para redactar un nuevo artículo.\n\nNo olvides hacerlo público y compartir tu blog.</string>
<string name="blogs_my_blogs_created">Blog creado</string>
<string name="blogs_blog_is_empty">Este blog está vacío</string>
<string name="blogs_other_blog_empty_state">Este blog está vacío.\n\nO el autor no ha escrito aún, o la persona que compartió este blog contigo necesita conectarse para que se puedan sincronizar los artículos.</string>
<string name="tag_new">NUEVO</string>
<string name="blogs_write_blog_post">Escribir artículo del blog</string>
<string name="blogs_write_blog_post_title_hint">Añade un título (opcional)</string>
<string name="blogs_write_blog_post_body_hint">Escribe tu artículo aquí</string>
<string name="blogs_publish_blog_post">Publicar</string>
<string name="blogs_blog_post_created">Creado artículo del blog</string>
<string name="blogs_blog_post_received">Recibido nuevo artículo del blog</string>
<string name="blogs_blog_post_scroll_to">Desplazarse hasta</string>
<string name="blogs_blog_failed_to_load">Fallo al cargar el blog</string>
<string name="blogs_blog_post_failed_to_load">Fallo al cargar el artículo del blog</string>
<string name="blogs_feed_empty_state">Esta es la lista global de entradas de blog.\n\nParece que nadie ha publicado nada todavía.\n\nSé el primero: pulsa el icono del lápiz para escribir una nueva entrada de blog.</string>
<string name="blogs_personal_blog">Blog personal de %s</string>
<string name="blogs_remove_blog">Eliminar blog</string>
<string name="blogs_remove_blog_dialog_message">¿Seguro que quieres eliminar este blog y todos sus artículos?\nTen en cuenta que no se eliminará el blog de los dispositivos de otras personas.</string>
<string name="blogs_remove_blog_ok">Eliminar blog</string>
<string name="blogs_blog_removed">Blog eliminado</string>
<string name="blogs_blog_list">Lista de blogs</string>
<string name="blogs_available_blogs">Blogs disponibles</string>
<string name="blogs_drafts">Borradores</string>
<!--Blog Sharing-->
<string name="blogs_sharing_share">Compartir blog</string>
<string name="blogs_sharing_error">Hubo un error compartiendo este blog.</string>
<string name="blogs_sharing_button">Compartir blog</string>
<string name="blogs_sharing_snackbar">Blog compartido con los contactos seleccionados</string>
<string name="blogs_sharing_response_accepted_sent">Aceptaste la invitación al blog de %s.</string>
<string name="blogs_sharing_response_declined_sent">Rechazaste la invitación al blog de %s.</string>
<string name="blogs_sharing_response_accepted_received">%s aceptó la invitación al blog.</string>
<string name="blogs_sharing_response_declined_received">%s rechazó la invitación al blog.</string>
<string name="blogs_sharing_invitation_received">%1$s ha compartido el blog personal de %2$s contigo.</string>
<string name="blogs_sharing_invitation_sent">Has compartido el blog personal de %1$s con %2$s.</string>
<string name="blogs_sharing_show_invitations">Mostrar invitaciones a blogs</string>
<string name="blogs_sharing_invitations_title">Invitaciones a blogs</string>
<string name="blogs_sharing_exists">Ya estás suscrito a este blog. Aceptar la invitación de nuevo conllevará una mejora de la velocidad en la recepción de los artículos.</string>
<string name="blogs_sharing_joined_toast">Suscrito al blog</string>
<string name="blogs_sharing_declined_toast">Rechazada invitación al blog</string>
<!--RSS Feeds-->
<string name="blogs_rss_feeds_import">Importar canal RSS</string>
<string name="blogs_rss_feeds_import_button">Importar</string>
<string name="blogs_rss_feeds_import_hint">Introduce la URL del canal RSS</string>
<string name="blogs_rss_feeds_import_error">Lo sentimos, hubo un error importando tu canal.</string>
<string name="blogs_rss_feeds_manage">Administrar canales RSS</string>
<string name="blogs_rss_feeds_manage_imported">Importado:</string>
<string name="blogs_rss_feeds_manage_author">Autor:</string>
<string name="blogs_rss_feeds_manage_updated">Última actualización:</string>
<string name="blogs_rss_feeds_manage_delete_error">¡El canal no pudo ser eliminado!</string>
<string name="blogs_rss_feeds_manage_empty_state">No has importado ningún canal RSS.\n\n¿Por qué no pulsas el signo más de la esquina superior derecha para añadir el primero?</string>
<string name="blogs_rss_feeds_manage_error">Hubo un problema cargando tus canales RSS. Por favor, prueba más tarde.</string>
<!--Settings Network-->
<string name="network_settings_title">Redes</string>
<string name="bluetooth_setting">Connectar mediante Bluetooth</string>
<string name="bluetooth_setting_enabled">Cuando haya contactos cerca</string>
<string name="bluetooth_setting_disabled">Solo al añadir contactos</string>
<string name="tor_mobile_setting">Conectar mediante Tor</string>
<string name="tor_mobile_setting_enabled">Cuando se use Wi-Fi o datos móviles</string>
<string name="tor_mobile_setting_disabled">Solo usando Wi-Fi</string>
<!--Settings Security and Panic-->
<string name="security_settings_title">Seguridad</string>
<string name="change_password">Cambiar contraseña</string>
<string name="current_password">Introduce tu contraseña actual:</string>
<string name="choose_new_password">Elige tu nueva contraseña:</string>
<string name="confirm_new_password">Confirma tu nueva contraseña:</string>
<string name="password_changed">La contraseña ha sido cambiada.</string>
<string name="panic_setting">Configuración del botón de pánico</string>
<string name="panic_setting_title">Botón de pánico</string>
<string name="panic_setting_hint">Configura cómo reaccionará Briar cuando uses una aplicación botón de pánico</string>
<string name="panic_app_setting_title">Aplicación botón de pánico</string>
<string name="unknown_app">una aplicación desconocida</string>
<string name="panic_app_setting_summary">No se ha seleccionado aplicación</string>
<string name="panic_app_setting_none">Ninguna</string>
<string name="dialog_title_connect_panic_app">Confirmar aplicación de pánico</string>
<string name="dialog_message_connect_panic_app">¿Seguro que quieres permitir que %1$s lance acciones destructivas de botón de pánico?</string>
<string name="lock_setting_title">Cerrar sesión</string>
<string name="lock_setting_summary">Cerrar la sesión de Briar si se pulsa un botón de pánico</string>
<string name="purge_setting_title">Eliminar cuenta</string>
<string name="purge_setting_summary">Eliminar tu cuenta de Briar si se pulsa un botón de pánico. Advertencia: esto eliminará permanentemente tus identidades, contactos y mensajes</string>
<string name="uninstall_setting_title">Desinstalar Briar</string>
<string name="uninstall_setting_summary">Requerirá confirmación manual en el momento de pánico</string>
<!--Settings Notifications-->
<string name="notification_settings_title">Notificaciones</string>
<string name="notify_private_messages_setting">Mostrar alertar para mensajes privados</string>
<string name="notify_forum_posts_setting">Mostrar alertas para publicaciones de foros</string>
<string name="notify_blog_posts_setting">Mostrar alertas para artículos de blogs</string>
<string name="notify_vibration_setting">Vibrar</string>
<string name="notify_sound_setting">Sonar</string>
<string name="notify_sound_setting_default">Alerta sonora predeterminada</string>
<string name="notify_sound_setting_disabled">Ninguna</string>
<string name="choose_ringtone_title">Elegir alerta sonora</string>
<!--Settings Crash reports and feedback-->
<string name="crash_report_settings_title">Informes de error</string>
<string name="enable_acra_setting">Activar el envío de informes de error</string>
<string name="acra_syslog_setting">Enviar trazas del sistema</string>
<string name="acra_user_email_setting">Correo electrónico opcional de contacto</string>
<string name="acra_alwaysaccept_setting">Siempre enviar informes</string>
<string name="feedback_settings_title">Retroalimentación</string>
<string name="send_feedback">Enviar comentario</string>
<!--Multiple Identities-->
<string name="anonymous">Anónimo</string>
<string name="new_identity_item">Nueva identidad\u2026</string>
<string name="new_identity_title">Nueva identidad</string>
<string name="create_identity_button">Crear identidad</string>
<string name="identity_created_toast">Identidad creada</string>
<!--Crash Reporter-->
<string name="crash_report_title">Informe de error de Briar</string>
<string name="briar_crashed">Lo sentimos, Briar ha fallado.</string>
<string name="not_your_fault">No es tu culpa.</string>
<string name="please_send_report">Por favor, ayúdanos a construir mejor Briar enviándonos un informe de error.</string>
<string name="report_is_encrypted">Prometemos que el informe se cifrará y se enviará de forma segura.</string>
<string name="feedback_title">Danos tu opinión</string>
<string name="describe_crash">Describe qué ha ocurrido (opcional)</string>
<string name="enter_feedback">Escribe tu comentario</string>
<string name="optional_contact_email">Tu correo electrónico (opcional)</string>
<string name="include_debug_report_crash">Incluir información anónima sobre el error</string>
<string name="include_debug_report_feedback">Incluir información anónima sobre este dispositivo</string>
<string name="could_not_load_report_data">No se pudieron cargar los datos del informe.</string>
<string name="send_report">Enviar informe</string>
<string name="close">Cerrar</string>
<string name="dev_report_saved">Informe guardado. Se enviará la próxima vez que inicies Briar.</string>
<!--Sign Out-->
<string name="progress_title_logout">Saliendo de Briar…</string>
</resources>

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