Compare commits

...

109 Commits

Author SHA1 Message Date
akwizgran
531e555b52 Bumped version number for beta release. 2017-07-25 18:43:19 +01:00
akwizgran
a9024aa34b Merge branch '955-shared-with-update' into 'master'
Fix "shared with" counter not being updated

Closes #955

See merge request !569
2017-07-25 17:40:40 +00:00
akwizgran
d4e3b7842c Merge branch 'blog-sharing-tests' into 'master'
Add unit tests for BlogSharingManager

See merge request !567
2017-07-25 17:40:29 +00:00
Torsten Grote
167fddfbcc Add unit tests for BlogSharingManager 2017-07-25 12:45:36 -03:00
Torsten Grote
a48d642648 Fix UI bug in CreateForumActivity and adapt group creation 2017-07-25 12:32:53 -03:00
Torsten Grote
9a70f054c7 Use proper GroupId when reacting to accepted invitations
Fixes #955
2017-07-25 10:03:13 -03:00
Torsten Grote
ca43d13bd6 Merge branch 'inject-properties-module-eager-singletons' into 'master'
Inject properties module's eager singletons

See merge request !568
2017-07-25 12:55:59 +00:00
akwizgran
5b71004179 Inject properties module's eager singletons. 2017-07-25 13:49:15 +01:00
akwizgran
63befccdbf Bumped expiry time and version number for beta release. 2017-07-21 11:52:09 +01:00
akwizgran
4ecf7c02d0 Merge branch '979-duplicate-blog-session' into 'master'
Fix Blog Sharing Sessions

Closes #979

See merge request !566
2017-07-21 10:27:21 +00:00
Torsten Grote
f25badc18c Move responsibility for pre-sharing blogs to sharing manager
to have all the code related to that in one place,
so it is easier to maintain and to spot bugs.

This also checks that only blogs without an existing sharing session
are shared and initialized again.
It extends an existing test to catch the missing check.

This removes some debugging information from the previous commit
to not leak private information via the sharing sessions.

Fixes #979
2017-07-17 14:07:47 -03:00
akwizgran
6e931e9ba5 Bump version number and expiry date for beta release. 2017-07-17 10:32:46 +01:00
akwizgran
7e749124bf Merge branch '617-protocol-versioning' into 'master'
Protocol versioning for BTP

See merge request !557
2017-07-17 09:24:35 +00:00
akwizgran
5822eb7808 Remove 'this'. 2017-07-17 10:16:50 +01:00
Torsten Grote
7a7e086541 Merge branch '982-name-not-found-exception' into 'master'
Use fully-qualified class names in manifest

Closes #982

See merge request !565
2017-07-14 12:19:50 +00:00
akwizgran
abab3167c2 Use fully-qualified class names in manifest.
This prevents a crash on Android 4 when the package name in build.gradle differs from the name in the manifest.
2017-07-14 12:01:55 +01:00
Torsten Grote
8d08570568 Merge branch '977-crash-when-opening-rss-blog' into 'master'
Create correct shareable for RSS blogs

Closes #977

See merge request !564
2017-07-07 17:42:54 +00:00
akwizgran
2007078f13 Added test for sharing an RSS blog. 2017-07-07 18:02:15 +01:00
akwizgran
dfb71a7978 Merge branch '942-block-blog-notifications' into 'master'
Block blog notifications when viewing combined feed

See merge request !563
2017-07-07 16:32:07 +00:00
akwizgran
480b0e3a03 Create correct shareable for RSS blogs.
Also removed "personal blog" wording that doesn't apply to RSS blogs.
2017-07-07 17:30:44 +01:00
akwizgran
8f8751f4ac Block blog notifications when viewing combined feed. 2017-07-07 15:34:00 +01:00
akwizgran
de2ea112ee Merge branch '933-beta-warning' into 'master'
Show Beta Expiry Warning

Closes #933

See merge request !559
2017-07-07 12:19:04 +00:00
Torsten Grote
6f99a53fd9 Show beta expiry warning in main activity 2017-07-07 09:12:07 -03:00
akwizgran
a8a9b9032d Merge branch 'warn_on_extra_translation' into 'master'
Show a warning instead of throwing an error for unused translations

See merge request !561
2017-07-07 12:01:23 +00:00
goapunk
6b15fb89de Show a warning instead of throwing an error for unused translations
Signed-off-by: goapunk <noobie@goapunks.net>
2017-07-07 12:19:44 +02:00
Torsten Grote
a711d6b8a1 Merge branch '106-fix-package-name' into 'master'
Set applicationId to match app_package

See merge request !560
2017-07-05 13:46:30 +00:00
akwizgran
5678f8aaa4 Update Robolectric tests so they can find resources. 2017-07-05 14:40:03 +01:00
akwizgran
2fe37f6c26 Set applicationId to match app_package. 2017-07-05 14:17:35 +01:00
Torsten Grote
a879747968 Translation Update 2017-07-05 09:50:14 -03:00
akwizgran
95e8fd7ee0 Merge branch 'notify_more_agressively' into 'master'
Notify more aggressively

See merge request !555
2017-07-05 11:36:37 +00:00
goapunk
4416aaaa4c Notify more aggressively
* Add setting to show notification on the lockscreen
* Don't block notifications in the contact-/-group/-forum/-bloglist

Signed-off-by: goapunk <noobie@goapunks.net>
2017-07-05 13:05:21 +02:00
Torsten Grote
500d5f0efe Merge branch '822-group-creation-workflow' into 'master'
Revisit private group creation workflow

Closes #822

See merge request !553
2017-07-04 17:19:11 +00:00
akwizgran
fc8978fd90 Create forum when button is clicked. 2017-07-04 16:17:59 +01:00
akwizgran
73df126bd4 Create and share private groups separately, as with forums. 2017-07-04 16:17:58 +01:00
akwizgran
9146488c7d Use same layout and behaviour for creating groups and forums. 2017-07-04 16:17:58 +01:00
akwizgran
613a7fe376 Merge branch '962-removing-contacts' into 'master'
Add test where two contacts remove each other

Closes #962

See merge request !558
2017-07-04 15:14:27 +00:00
akwizgran
ecb62f00d4 Code cleanup. 2017-07-04 16:09:32 +01:00
akwizgran
c4540a03cd Protocol versioning for BTP. 2017-07-04 16:09:32 +01:00
akwizgran
3e31da99b5 Merge branch '106-beta-namespace' into 'master'
Change app name and package name for beta release

Closes #106

See merge request !545
2017-07-04 14:40:47 +00:00
Torsten Grote
098c1d0b1e Add test where two contacts remove each other 2017-07-04 11:30:17 -03:00
akwizgran
178e908c86 Use a different package name and app name for beta builds. 2017-07-03 18:20:05 +01:00
akwizgran
ecf7cf14ae Merge branch '957-rss-url-case-sensitivity' into 'master'
Handle RSS URLs case-insensitively

Closes #957

See merge request !544
2017-07-03 16:22:52 +00:00
akwizgran
09e2a15a73 Merge branch '951-remove-visibility-indicators' into 'master'
Remove visibility indicators from private group join messages

Closes #951

See merge request !546
2017-07-03 16:22:31 +00:00
akwizgran
ab387860a6 Removed visibility indicators from private group member list. 2017-07-03 16:37:29 +01:00
akwizgran
f63fc94f2b Removed visibility indicators from private group join messages. 2017-07-03 16:37:29 +01:00
Michael Rogers
41e5928cca Validate and normalise RSS URLs. 2017-07-03 16:36:50 +01:00
Torsten Grote
8303175494 Merge branch 'use-f-droid-base-docker-image' into 'master'
Use F-Droid's base Docker image

See merge request !556
2017-07-03 15:35:28 +00:00
akwizgran
151eb6935b Use F-Droid's base Docker image. 2017-07-03 16:17:03 +01:00
akwizgran
6a419c0c7b Merge branch '968-downgrade-h2' into 'master'
Downgrade H2 to 1.4.192

Closes #968

See merge request !550
2017-07-03 11:51:35 +00:00
akwizgran
1795b32121 Downgrade H2 to 1.4.192. 2017-07-03 12:36:45 +01:00
akwizgran
01971768ce Merge branch '970_settings_use_summaries' into 'master'
Split notification settings into title and summary.

Closes #970

See merge request !554
2017-07-03 11:19:15 +00:00
akwizgran
ef7483ab01 Merge branch '787-tapping-blog-author-opens-same-blog' into 'master'
Don't reopen the same blog when the author is tapped

Closes #787

See merge request !551
2017-07-03 10:43:15 +00:00
akwizgran
527d11473d Merge branch '805-introduction-message-layout' into 'master'
Use smaller layout for introduction message screen

Closes #805

See merge request !552
2017-07-03 10:40:10 +00:00
akwizgran
775dadc9a0 Merge branch '904-notify-tor-controller' into 'master'
Notify Tor controller if Tor has crashed

Closes #904

See merge request !543
2017-07-03 09:29:04 +00:00
akwizgran
800b10a988 Merge branch '956-tap-protection-ux' into 'master'
Don't show tap protection dialog until it's needed

Closes #956

See merge request !548
2017-07-03 09:22:31 +00:00
akwizgran
c977bf047d Removed overrides of showScreenFilterWarning. 2017-07-03 10:08:36 +01:00
goapunk
660a25f21d Split notification settings into title and summary.
Signed-off-by: goapunk <noobie@goapunks.net>
2017-07-03 09:27:07 +02:00
Torsten Grote
e7fd6d23af Merge branch '962-check-blog-subscription-when-removing-contact' into 'master'
Check personal blog subscription when removing contact

See merge request !549
2017-06-30 20:57:52 +00:00
Torsten Grote
46982897f0 Merge branch '963-load-thread-list-messages-on-start' into 'master'
Load messages each time activity starts

See merge request !547
2017-06-30 20:53:13 +00:00
Torsten Grote
d24de68d64 Merge branch '574-upgrade-tor' into 'master'
Upgrade Tor to 0.2.9

Closes #574

See merge request !542
2017-06-30 20:36:22 +00:00
akwizgran
7514c46a3f Use smaller layout for introduction message screen. 2017-06-30 16:23:16 +01:00
akwizgran
6632c0f8e3 Don't reopen the same blog when the author is tapped. 2017-06-30 14:24:32 +01:00
akwizgran
79aafcda69 Fixed a test, added a regression test. 2017-06-30 12:58:44 +01:00
akwizgran
05af21e8dc Check personal blog subscription when removing contact. 2017-06-30 12:04:34 +01:00
akwizgran
0dc62cbbdc Fixed a test. 2017-06-30 10:01:35 +01:00
akwizgran
f3a084cfd2 Removed option to remember shown overlay apps. 2017-06-29 18:47:36 +01:00
akwizgran
8b32f82566 Don't show tap protection dialog until it's needed. 2017-06-29 18:18:39 +01:00
akwizgran
d598b6ed44 Load messages each time activity starts. 2017-06-26 16:21:25 +01:00
akwizgran
f5dc6f24b9 Bumped expiry date to 1 September 2017. 2017-06-26 14:50:58 +01:00
akwizgran
37454392da Update Tor binaries to 0.2.9.11. 2017-06-23 22:06:00 +01:00
akwizgran
de7f9111d3 Update Tor patch to 0.2.9.11. 2017-06-23 15:23:26 +01:00
akwizgran
96d2889a6c Notify Tor controller if Tor has crashed. 2017-06-23 14:57:00 +01:00
Torsten Grote
f6412d1e9a Merge branch 'spongy-castle-1-56' into 'master'
Upgrade Spongy Castle and some other dependencies

See merge request !541
2017-06-15 17:37:22 +00:00
akwizgran
b377cd6b1c Upgrade Spongy Castle and some other dependencies. 2017-06-12 17:44:08 +01:00
Ernir Erlingsson
f6cdbda5bb Merge branch 'master' of https://code.briarproject.org/akwizgran/briar 2017-05-29 11:06:16 +02:00
Ernir Erlingsson
855c600a3e hotfix list restore NPE for user testing 2017-05-29 11:05:46 +02:00
akwizgran
ea6e8303b0 Merge branch '954_dont_show_on_startup_failure' into 'master'
Don't show screenfilter warning in StartupFailureActivity

See merge request !539
2017-05-29 09:05:18 +00:00
goapunk
d4934040d9 Don't show screenfilter warning in StartupFailureActivity
Signed-off-by: goapunk <noobie@goapunks.net>
2017-05-22 12:40:33 +02:00
akwizgran
3449677b24 Bumped version number and expiry date. 2017-05-19 12:07:29 +01:00
akwizgran
1ad3a6646e Merge branch '941-store-correct-parent-id' into 'master'
Store correct original parent ID when rewrapping blog posts

See merge request !534
2017-05-12 09:53:27 +00:00
akwizgran
2d10f6b2bd Merge branch '884-emoji-text-view-layout-bug' into 'master'
Remove ellipsizing support from EmojiTextView

Closes #884

See merge request !533
2017-05-12 09:35:33 +00:00
akwizgran
5b05424d83 Merge branch 'master' into '941-store-correct-parent-id'
# Conflicts:
#   briar-core/src/test/java/org/briarproject/briar/blog/BlogManagerImplTest.java
2017-05-12 09:34:24 +00:00
akwizgran
0826022d82 Merge branch 'bring_annotations_in_line' into 'master'
Bring nullable annotation imports in line

See merge request !536
2017-05-12 09:33:00 +00:00
akwizgran
a901bfb9cb Merge branch '948-vector-crash' into 'master'
Remove scientific notation from vector drawables to prevent crashes

Closes #948

See merge request !537
2017-05-12 09:28:59 +00:00
akwizgran
03cdce122a Merge branch '947-bluetooth-address-crash' into 'master'
Don't crash on empty bluetooth addresses

See merge request !538
2017-05-12 09:26:57 +00:00
goapunk
f2e0e16969 Bring nullable annotation imports in line
Signed-off-by: goapunk <noobie@goapunks.net>
2017-05-12 10:06:56 +02:00
Torsten Grote
0c441e2ff3 Don't crash on empty bluetooth addresses 2017-05-10 15:06:09 -03:00
Torsten Grote
21302304a5 Remove scientific notation from vector drawables to prevent crashes
Details: http://stackoverflow.com/a/40829348
2017-05-10 14:56:59 -03:00
Torsten Grote
6839d8b844 Merge branch 'wifi-manager-memory-leak' into 'master'
Use application context to get WifiManager

See merge request !535
2017-05-10 17:01:52 +00:00
Torsten Grote
aee65a716c Merge branch '798-remove-contact-blogs' into 'master'
Allow to remove pre-shared blogs of our contacts

Closes #798

See merge request !529
2017-05-10 16:58:38 +00:00
Torsten Grote
6a07d8f2c9 Allow to remove pre-shared blogs of our contacts 2017-05-10 13:50:07 -03:00
Ernir Erlingsson
3c1ea81cd0 Merge branch '853-disabled-menu-items' into 'master'
Remove theme default color override

Closes #853

See merge request !527
2017-05-06 20:26:15 +00:00
Ernir Erlingsson
025f417bc7 Merge branch '894-list-position-restore' into 'master'
save and restore list position for threaded lists

Closes #894 and #946

See merge request !528
2017-05-06 19:37:02 +00:00
Ernir Erlingsson
c9dcd906c9 final pre-merge fixes 2017-05-06 21:36:25 +02:00
Ernir Erlingsson
7024e04d15 fixed final akwizgran comments 2017-05-06 21:31:53 +02:00
akwizgran
0b8ac947db Use application context to get WifiManager. 2017-05-05 15:43:27 +01:00
Ernir Erlingsson
948410a064 fixed unread buttons for threaded lists and akwizgran's comments 2017-05-05 14:49:53 +02:00
akwizgran
2841339cac Merge branch '468-ci' into 'master'
Set up basic CI

Closes #468

See merge request !530
2017-05-05 09:11:06 +00:00
Torsten Grote
e8e82bd805 Update Translations 2017-05-04 10:26:49 -03:00
Ernir Erlingsson
6876f40a0e Merge branch 'fix_groupname_validation' into 'master'
Fix groupname validation

See merge request !531
2017-05-04 07:26:55 +00:00
Ernir Erlingsson
5f4e1ecdfd improvements after code review #1
fix
2017-05-02 11:42:55 +02:00
Ernir Erlingsson
044719432a list position save and restore now implemented for threaded lists 2017-05-02 11:42:55 +02:00
Ernir Erlingsson
d1a929da85 bumped expire date 2017-05-02 11:42:15 +02:00
goapunk
2a8978a60d fix group name validation
Signed-off-by: goapunk <noobie@goapunks.net>
2017-04-29 16:49:37 +02:00
Torsten Grote
c0afad7a26 Set up basic CI 2017-04-28 13:24:41 -03:00
akwizgran
37281c6c23 Remove ellipsizing support from EmojiTextView.
This is a workaround for a layout bug.
2017-04-28 15:39:24 +01:00
akwizgran
76a5e25656 Added tests for wrapping and rewrapping blog posts. 2017-04-19 12:16:18 +01:00
akwizgran
3575b74837 Store correct original parent ID when rewrapping blog posts. 2017-04-19 12:15:34 +01:00
Torsten Grote
f1c7996960 Remove theme default color override 2017-04-18 09:14:00 -03:00
135 changed files with 3123 additions and 1778 deletions

20
.gitlab-ci.yml Normal file
View File

@@ -0,0 +1,20 @@
image: registry.gitlab.com/fdroid/ci-images-base:latest
cache:
paths:
- .gradle/wrapper
- .gradle/caches
before_script:
- export GRADLE_USER_HOME=$PWD/.gradle
# - export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
# - echo y | android --silent update sdk --no-ui --filter android-${ANDROID_COMPILE_SDK}
test:
script:
- ./gradlew test
after_script:
# this file changes every time but should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/

View File

@@ -12,8 +12,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 22 targetSdkVersion 22
versionCode 1 versionCode 14
versionName "1.0" versionName "0.14"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
} }
@@ -25,38 +25,38 @@ android {
dependencies { dependencies {
compile project(':bramble-core') compile project(':bramble-core')
compile fileTree(dir: 'libs', include: ['*.jar']) compile fileTree(dir: 'libs', include: '*.jar')
provided 'javax.annotation:jsr250-api:1.0' provided 'javax.annotation:jsr250-api:1.0'
} }
def torBinaryDir = 'src/main/res/raw' def torBinaryDir = 'src/main/res/raw'
task downloadTorGeoIp(type: Download) { task downloadTorGeoIp(type: Download) {
src 'https://briarproject.org/build/geoip-2015-12-01.zip' src 'https://briarproject.org/build/geoip-2017-05-02.zip'
dest "$torBinaryDir/geoip.zip" dest "$torBinaryDir/geoip.zip"
onlyIfNewer true onlyIfNewer true
} }
task downloadTorBinaryArm(type: Download) { task downloadTorBinaryArm(type: Download) {
src 'https://briarproject.org/build/tor-0.2.7.6-arm.zip' src 'https://briarproject.org/build/tor-0.2.9.11-arm.zip'
dest "$torBinaryDir/tor_arm.zip" dest "$torBinaryDir/tor_arm.zip"
onlyIfNewer true onlyIfNewer true
} }
task downloadTorBinaryArmPie(type: Download) { task downloadTorBinaryArmPie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.7.6-arm-pie.zip' src 'https://briarproject.org/build/tor-0.2.9.11-arm-pie.zip'
dest "$torBinaryDir/tor_arm_pie.zip" dest "$torBinaryDir/tor_arm_pie.zip"
onlyIfNewer true onlyIfNewer true
} }
task downloadTorBinaryX86(type: Download) { task downloadTorBinaryX86(type: Download) {
src 'https://briarproject.org/build/tor-0.2.7.6-x86.zip' src 'https://briarproject.org/build/tor-0.2.9.11-x86.zip'
dest "$torBinaryDir/tor_x86.zip" dest "$torBinaryDir/tor_x86.zip"
onlyIfNewer true onlyIfNewer true
} }
task downloadTorBinaryX86Pie(type: Download) { task downloadTorBinaryX86Pie(type: Download) {
src 'https://briarproject.org/build/tor-0.2.7.6-x86-pie.zip' src 'https://briarproject.org/build/tor-0.2.9.11-x86-pie.zip'
dest "$torBinaryDir/tor_x86_pie.zip" dest "$torBinaryDir/tor_x86_pie.zip"
onlyIfNewer true onlyIfNewer true
} }
@@ -64,31 +64,31 @@ task downloadTorBinaryX86Pie(type: Download) {
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') { task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
src "$torBinaryDir/geoip.zip" src "$torBinaryDir/geoip.zip"
algorithm 'SHA-256' algorithm 'SHA-256'
checksum '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162' checksum '51f4d1272fb867e1f3b36b67a584e2a33c40b40f62305457d799fd399cd77c9b'
} }
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') { task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
src "$torBinaryDir/tor_arm.zip" src "$torBinaryDir/tor_arm.zip"
algorithm 'SHA-256' algorithm 'SHA-256'
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146' checksum '1da6008663a8ad98b349e62acbbf42c379f65ec504fa467cb119c187cd5a4c6b'
} }
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') { task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
src "$torBinaryDir/tor_arm_pie.zip" src "$torBinaryDir/tor_arm_pie.zip"
algorithm 'SHA-256' algorithm 'SHA-256'
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646' checksum 'eb061f880829e05f104690ac744848133f2dacef04759d425a2cff0df32c271e'
} }
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') { task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
src "$torBinaryDir/tor_x86.zip" src "$torBinaryDir/tor_x86.zip"
algorithm 'SHA-256' algorithm 'SHA-256'
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e' checksum 'f5308aff8303daca082f82227d02b51ddedba4ab1d1420739ada0427ae5dbb41'
} }
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') { task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
src "$torBinaryDir/tor_x86_pie.zip" src "$torBinaryDir/tor_x86_pie.zip"
algorithm 'SHA-256' algorithm 'SHA-256'
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e' checksum '889a6c81ac73d05d35ed610ca5a913cee44d333e4ae1749c2a107f2f7dd8197b'
} }
project.afterEvaluate { project.afterEvaluate {

View File

@@ -7,12 +7,12 @@ apply plugin: 'witness'
dependencies { dependencies {
compile "com.google.dagger:dagger:2.0.2" compile "com.google.dagger:dagger:2.0.2"
compile 'com.google.dagger:dagger-compiler:2.0.2' compile 'com.google.dagger:dagger-compiler:2.0.2'
compile 'com.google.code.findbugs:jsr305:3.0.1' compile 'com.google.code.findbugs:jsr305:3.0.2'
testCompile 'junit:junit:4.12' testCompile 'junit:junit:4.12'
testCompile "org.jmock:jmock:2.8.1" testCompile "org.jmock:jmock:2.8.2"
testCompile "org.jmock:jmock-junit4:2.8.1" testCompile "org.jmock:jmock-junit4:2.8.2"
testCompile "org.jmock:jmock-legacy:2.8.1" testCompile "org.jmock:jmock-legacy:2.8.2"
testCompile "org.hamcrest:hamcrest-library:1.3" testCompile "org.hamcrest:hamcrest-library:1.3"
testCompile "org.hamcrest:hamcrest-core:1.3" testCompile "org.hamcrest:hamcrest-core:1.3"
} }
@@ -21,7 +21,7 @@ dependencyVerification {
verify = [ verify = [
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9', 'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3', 'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd', 'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b', 'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99', 'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',

View File

@@ -1,6 +1,7 @@
package org.briarproject.bramble.api; package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
@@ -53,6 +54,12 @@ public class Bytes implements Comparable<Bytes> {
return aBytes.length - bBytes.length; return aBytes.length - bBytes.length;
} }
@Override
public String toString() {
return getClass().getSimpleName() +
"(" + StringUtils.toHexString(getBytes()) + ")";
}
public static class BytesComparator implements Comparator<Bytes> { public static class BytesComparator implements Comparator<Bytes> {
@Override @Override

View File

@@ -137,7 +137,8 @@ public interface CryptoComponent {
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod); TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
/** Encodes the pseudo-random tag that is used to recognise a stream. */ /** Encodes the pseudo-random tag that is used to recognise a stream. */
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber); void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber);
/** /**
* Signs the given byte[] with the given PrivateKey. * Signs the given byte[] with the given PrivateKey.

View File

@@ -4,6 +4,11 @@ import org.briarproject.bramble.api.crypto.SecretKey;
public interface TransportConstants { public interface TransportConstants {
/**
* The current version of the transport protocol.
*/
int PROTOCOL_VERSION = 3;
/** /**
* The length of the pseudo-random tag in bytes. * The length of the pseudo-random tag in bytes.
*/ */
@@ -14,21 +19,22 @@ public interface TransportConstants {
*/ */
int STREAM_HEADER_NONCE_LENGTH = 24; int STREAM_HEADER_NONCE_LENGTH = 24;
/**
* The length of the stream header initialisation vector (IV) in bytes.
*/
int STREAM_HEADER_IV_LENGTH = STREAM_HEADER_NONCE_LENGTH - 8;
/** /**
* The length of the message authentication code (MAC) in bytes. * The length of the message authentication code (MAC) in bytes.
*/ */
int MAC_LENGTH = 16; int MAC_LENGTH = 16;
/**
* The length of the stream header plaintext in bytes. The stream header
* contains the protocol version, stream number and frame key.
*/
int STREAM_HEADER_PLAINTEXT_LENGTH = 2 + 8 + SecretKey.LENGTH;
/** /**
* The length of the stream header in bytes. * The length of the stream header in bytes.
*/ */
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH int STREAM_HEADER_LENGTH = STREAM_HEADER_NONCE_LENGTH
+ MAC_LENGTH; + STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH;
/** /**
* The length of the frame nonce in bytes. * The length of the frame nonce in bytes.

View File

@@ -19,7 +19,7 @@ public class PrivacyUtils {
@Nullable @Nullable
public static String scrubMacAddress(@Nullable String address) { public static String scrubMacAddress(@Nullable String address) {
if (address == null) return null; if (address == null || address.length() == 0) return null;
// this is a fake address we need to know about // this is a fake address we need to know about
if (address.equals("02:00:00:00:00:00")) return address; if (address.equals("02:00:00:00:00:00")) return address;
// keep first and last octet of MAC address // keep first and last octet of MAC address

View File

@@ -1,8 +1,9 @@
plugins { plugins {
id "java" id 'java'
id "net.ltgt.apt" version "0.9" id 'net.ltgt.apt' version '0.9'
id "idea" id 'idea'
} }
sourceCompatibility = 1.6 sourceCompatibility = 1.6
targetCompatibility = 1.6 targetCompatibility = 1.6
@@ -10,17 +11,18 @@ apply plugin: 'witness'
dependencies { dependencies {
compile project(':bramble-api') compile project(':bramble-api')
compile fileTree(dir: 'libs', include: '*.jar') compile 'com.madgag.spongycastle:core:1.56.0.0'
compile 'com.madgag.spongycastle:core:1.54.0.0' compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
compile 'com.h2database:h2:1.4.190' compile 'org.bitlet:weupnp:0.1.4'
testCompile project(path: ':bramble-api', configuration: 'testOutput') testCompile project(path: ':bramble-api', configuration: 'testOutput')
} }
dependencyVerification { dependencyVerification {
verify = [ verify = [
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e', 'com.madgag.spongycastle:core:5e791b0eaa9e0c4594231b44f616a52adddb7dccedeb0ad9ad74887e19499a23',
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c' 'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
] ]
} }

View File

@@ -54,6 +54,7 @@ public class BrambleCoreModule {
c.inject(new IdentityModule.EagerSingletons()); c.inject(new IdentityModule.EagerSingletons());
c.inject(new LifecycleModule.EagerSingletons()); c.inject(new LifecycleModule.EagerSingletons());
c.inject(new PluginModule.EagerSingletons()); c.inject(new PluginModule.EagerSingletons());
c.inject(new PropertiesModule.EagerSingletons());
c.inject(new SyncModule.EagerSingletons()); c.inject(new SyncModule.EagerSingletons());
c.inject(new SystemModule.EagerSingletons()); c.inject(new SystemModule.EagerSingletons());
c.inject(new TransportModule.EagerSingletons()); c.inject(new TransportModule.EagerSingletons());

View File

@@ -45,8 +45,10 @@ import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_B
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH; import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS; import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
class CryptoComponentImpl implements CryptoComponent { class CryptoComponentImpl implements CryptoComponent {
@@ -412,8 +414,11 @@ class CryptoComponentImpl implements CryptoComponent {
} }
@Override @Override
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) { public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
long streamNumber) {
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException(); if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
throw new IllegalArgumentException();
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED) if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
throw new IllegalArgumentException(); throw new IllegalArgumentException();
// Initialise the PRF // Initialise the PRF
@@ -421,10 +426,14 @@ class CryptoComponentImpl implements CryptoComponent {
// The output of the PRF must be long enough to use as a tag // The output of the PRF must be long enough to use as a tag
int macLength = prf.getDigestSize(); int macLength = prf.getDigestSize();
if (macLength < TAG_LENGTH) throw new IllegalStateException(); if (macLength < TAG_LENGTH) throw new IllegalStateException();
// The input is the stream number as a 64-bit integer // The input is the protocol version as a 16-bit integer, followed by
byte[] input = new byte[INT_64_BYTES]; // the stream number as a 64-bit integer
ByteUtils.writeUint64(streamNumber, input, 0); byte[] protocolVersionBytes = new byte[INT_16_BYTES];
prf.update(input, 0, input.length); ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
byte[] streamNumberBytes = new byte[INT_64_BYTES];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
byte[] mac = new byte[macLength]; byte[] mac = new byte[macLength];
prf.doFinal(mac, 0); prf.doFinal(mac, 0);
// The output is the first TAG_LENGTH bytes of the MAC // The output is the first TAG_LENGTH bytes of the MAC

View File

@@ -20,9 +20,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
@NotThreadSafe @NotThreadSafe
@@ -117,7 +119,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
private void readStreamHeader() throws IOException { private void readStreamHeader() throws IOException {
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH]; byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
byte[] streamHeaderPlaintext = new byte[SecretKey.LENGTH]; byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
// Read the stream header // Read the stream header
int offset = 0; int offset = 0;
while (offset < STREAM_HEADER_LENGTH) { while (offset < STREAM_HEADER_LENGTH) {
@@ -126,21 +128,35 @@ class StreamDecrypterImpl implements StreamDecrypter {
if (read == -1) throw new EOFException(); if (read == -1) throw new EOFException();
offset += read; offset += read;
} }
// The nonce consists of the stream number followed by the IV // Extract the nonce
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH]; byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0); System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, 0,
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, STREAM_HEADER_NONCE_LENGTH);
INT_64_BYTES, STREAM_HEADER_IV_LENGTH);
// Decrypt and authenticate the stream header // Decrypt and authenticate the stream header
try { try {
cipher.init(false, streamHeaderKey, streamHeaderNonce); cipher.init(false, streamHeaderKey, streamHeaderNonce);
int decrypted = cipher.process(streamHeaderCiphertext, int decrypted = cipher.process(streamHeaderCiphertext,
STREAM_HEADER_IV_LENGTH, SecretKey.LENGTH + MAC_LENGTH, STREAM_HEADER_NONCE_LENGTH,
STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH,
streamHeaderPlaintext, 0); streamHeaderPlaintext, 0);
if (decrypted != SecretKey.LENGTH) throw new RuntimeException(); if (decrypted != STREAM_HEADER_PLAINTEXT_LENGTH)
throw new RuntimeException();
} catch (GeneralSecurityException e) { } catch (GeneralSecurityException e) {
throw new FormatException(); throw new FormatException();
} }
frameKey = new SecretKey(streamHeaderPlaintext); // Check the protocol version
int receivedProtocolVersion =
ByteUtils.readUint16(streamHeaderPlaintext, 0);
if (receivedProtocolVersion != PROTOCOL_VERSION)
throw new FormatException();
// Check the stream number
long receivedStreamNumber = ByteUtils.readUint64(streamHeaderPlaintext,
INT_16_BYTES);
if (receivedStreamNumber != streamNumber) throw new FormatException();
// Extract the frame key
byte[] frameKeyBytes = new byte[SecretKey.LENGTH];
System.arraycopy(streamHeaderPlaintext, INT_16_BYTES + INT_64_BYTES,
frameKeyBytes, 0, SecretKey.LENGTH);
frameKey = new SecretKey(frameKeyBytes);
} }
} }

View File

@@ -13,7 +13,8 @@ import javax.annotation.concurrent.Immutable;
import javax.inject.Inject; import javax.inject.Inject;
import javax.inject.Provider; import javax.inject.Provider;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
@Immutable @Immutable
@@ -36,22 +37,22 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
AuthenticatedCipher cipher = cipherProvider.get(); AuthenticatedCipher cipher = cipherProvider.get();
long streamNumber = ctx.getStreamNumber(); long streamNumber = ctx.getStreamNumber();
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, ctx.getTagKey(), streamNumber); crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH]; byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv); crypto.getSecureRandom().nextBytes(streamHeaderNonce);
SecretKey frameKey = crypto.generateSecretKey(); SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, streamNumber, tag, return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
streamHeaderIv, ctx.getHeaderKey(), frameKey); streamHeaderNonce, ctx.getHeaderKey(), frameKey);
} }
@Override @Override
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out, public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
SecretKey headerKey) { SecretKey headerKey) {
AuthenticatedCipher cipher = cipherProvider.get(); AuthenticatedCipher cipher = cipherProvider.get();
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH]; byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
crypto.getSecureRandom().nextBytes(streamHeaderIv); crypto.getSecureRandom().nextBytes(streamHeaderNonce);
SecretKey frameKey = crypto.generateSecretKey(); SecretKey frameKey = crypto.generateSecretKey();
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderIv, return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
headerKey, frameKey); headerKey, frameKey);
} }
} }

View File

@@ -18,9 +18,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
@NotThreadSafe @NotThreadSafe
@@ -33,7 +35,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
private final long streamNumber; private final long streamNumber;
@Nullable @Nullable
private final byte[] tag; private final byte[] tag;
private final byte[] streamHeaderIv; private final byte[] streamHeaderNonce;
private final byte[] frameNonce, frameHeader; private final byte[] frameNonce, frameHeader;
private final byte[] framePlaintext, frameCiphertext; private final byte[] framePlaintext, frameCiphertext;
@@ -41,13 +43,13 @@ class StreamEncrypterImpl implements StreamEncrypter {
private boolean writeTag, writeStreamHeader; private boolean writeTag, writeStreamHeader;
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher, StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderIv, long streamNumber, @Nullable byte[] tag, byte[] streamHeaderNonce,
SecretKey streamHeaderKey, SecretKey frameKey) { SecretKey streamHeaderKey, SecretKey frameKey) {
this.out = out; this.out = out;
this.cipher = cipher; this.cipher = cipher;
this.streamNumber = streamNumber; this.streamNumber = streamNumber;
this.tag = tag; this.tag = tag;
this.streamHeaderIv = streamHeaderIv; this.streamHeaderNonce = streamHeaderNonce;
this.streamHeaderKey = streamHeaderKey; this.streamHeaderKey = streamHeaderKey;
this.frameKey = frameKey; this.frameKey = frameKey;
frameNonce = new byte[FRAME_NONCE_LENGTH]; frameNonce = new byte[FRAME_NONCE_LENGTH];
@@ -114,22 +116,23 @@ class StreamEncrypterImpl implements StreamEncrypter {
} }
private void writeStreamHeader() throws IOException { private void writeStreamHeader() throws IOException {
// The nonce consists of the stream number followed by the IV // The header contains the protocol version, stream number and frame key
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH]; byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0); ByteUtils.writeUint16(PROTOCOL_VERSION, streamHeaderPlaintext, 0);
System.arraycopy(streamHeaderIv, 0, streamHeaderNonce, INT_64_BYTES, ByteUtils.writeUint64(streamNumber, streamHeaderPlaintext,
STREAM_HEADER_IV_LENGTH); INT_16_BYTES);
byte[] streamHeaderPlaintext = frameKey.getBytes(); System.arraycopy(frameKey.getBytes(), 0, streamHeaderPlaintext,
INT_16_BYTES + INT_64_BYTES, SecretKey.LENGTH);
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH]; byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0, System.arraycopy(streamHeaderNonce, 0, streamHeaderCiphertext, 0,
STREAM_HEADER_IV_LENGTH); STREAM_HEADER_NONCE_LENGTH);
// Encrypt and authenticate the frame key // Encrypt and authenticate the stream header key
try { try {
cipher.init(true, streamHeaderKey, streamHeaderNonce); cipher.init(true, streamHeaderKey, streamHeaderNonce);
int encrypted = cipher.process(streamHeaderPlaintext, 0, int encrypted = cipher.process(streamHeaderPlaintext, 0,
SecretKey.LENGTH, streamHeaderCiphertext, STREAM_HEADER_PLAINTEXT_LENGTH, streamHeaderCiphertext,
STREAM_HEADER_IV_LENGTH); STREAM_HEADER_NONCE_LENGTH);
if (encrypted != SecretKey.LENGTH + MAC_LENGTH) if (encrypted != STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH)
throw new RuntimeException(); throw new RuntimeException();
} catch (GeneralSecurityException badCipher) { } catch (GeneralSecurityException badCipher) {
throw new RuntimeException(badCipher); throw new RuntimeException(badCipher);

View File

@@ -70,25 +70,7 @@ class XSalsa20Poly1305AuthenticatedCipher implements AuthenticatedCipher {
byte[] subKey = new byte[SUBKEY_LENGTH]; byte[] subKey = new byte[SUBKEY_LENGTH];
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0); xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
// Reverse the order of the Poly130 subkey // Clamp the subkey
//
// NaCl and libsodium use the first 32 bytes of XSalsa20 as the
// subkey for crypto_onetimeauth_poly1305, which interprets it
// as r[0] ... r[15], k[0] ... k[15]. See section 9 of the NaCl
// paper (http://cr.yp.to/highspeed/naclcrypto-20090310.pdf),
// where the XSalsa20 output is defined as (r, s, t, ...).
//
// BC's Poly1305 implementation interprets the subkey as
// k[0] ... k[15], r[0] ... r[15] (per poly1305_aes_clamp in
// the reference implementation).
//
// To be NaCl-compatible, we reverse the subkey.
System.arraycopy(subKey, 0, zero, 0, SUBKEY_LENGTH / 2);
System.arraycopy(subKey, SUBKEY_LENGTH / 2, subKey, 0,
SUBKEY_LENGTH / 2);
System.arraycopy(zero, 0, subKey, SUBKEY_LENGTH / 2,
SUBKEY_LENGTH / 2);
// Now we can clamp the correct part of the subkey
Poly1305KeyGenerator.clamp(subKey); Poly1305KeyGenerator.clamp(subKey);
// Initialize Poly1305 with the subkey // Initialize Poly1305 with the subkey

View File

@@ -29,6 +29,7 @@ import javax.annotation.concurrent.ThreadSafe;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -126,7 +127,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : inKeys.getWindow().getUnseen()) { for (long streamNumber : inKeys.getWindow().getUnseen()) {
TagContext tagCtx = new TagContext(c, inKeys, streamNumber); TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
byte[] tag = new byte[TAG_LENGTH]; byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, inKeys.getTagKey(), streamNumber); crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
inContexts.put(new Bytes(tag), tagCtx); inContexts.put(new Bytes(tag), tagCtx);
} }
} }
@@ -242,7 +244,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
// Add tags for any stream numbers added to the window // Add tags for any stream numbers added to the window
for (long streamNumber : change.getAdded()) { for (long streamNumber : change.getAdded()) {
byte[] addTag = new byte[TAG_LENGTH]; byte[] addTag = new byte[TAG_LENGTH];
crypto.encodeTag(addTag, inKeys.getTagKey(), streamNumber); crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
streamNumber);
inContexts.put(new Bytes(addTag), new TagContext( inContexts.put(new Bytes(addTag), new TagContext(
tagCtx.contactId, inKeys, streamNumber)); tagCtx.contactId, inKeys, streamNumber));
} }
@@ -250,7 +253,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
for (long streamNumber : change.getRemoved()) { for (long streamNumber : change.getRemoved()) {
if (streamNumber == tagCtx.streamNumber) continue; if (streamNumber == tagCtx.streamNumber) continue;
byte[] removeTag = new byte[TAG_LENGTH]; byte[] removeTag = new byte[TAG_LENGTH];
crypto.encodeTag(removeTag, inKeys.getTagKey(), streamNumber); crypto.encodeTag(removeTag, inKeys.getTagKey(),
PROTOCOL_VERSION, streamNumber);
inContexts.remove(new Bytes(removeTag)); inContexts.remove(new Bytes(removeTag));
} }
// Write the window back to the DB // Write the window back to the DB

View File

@@ -14,7 +14,8 @@ import static junit.framework.Assert.assertEquals;
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES; import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
@@ -22,7 +23,8 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher; private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey; private final SecretKey streamHeaderKey, frameKey;
private final byte[] streamHeaderIv, payload; private final byte[] streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final int payloadLength = 123, paddingLength = 234; private final int payloadLength = 123, paddingLength = 234;
private final long streamNumber = 1234; private final long streamNumber = 1234;
@@ -30,7 +32,12 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
cipher = new TestAuthenticatedCipher(); // Null cipher cipher = new TestAuthenticatedCipher(); // Null cipher
streamHeaderKey = TestUtils.getSecretKey(); streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey(); frameKey = TestUtils.getSecretKey();
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH); streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
payload = TestUtils.getRandomBytes(payloadLength); payload = TestUtils.getRandomBytes(payloadLength);
} }
@@ -47,7 +54,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1); byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -76,6 +85,85 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
assertEquals(-1, s.readFrame(buffer)); assertEquals(-1, s.readFrame(buffer));
} }
@Test(expected = IOException.class)
public void testWrongProtocolVersionThrowsException() throws Exception {
byte[] wrongProtocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION + 1, wrongProtocolVersionBytes,
0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(wrongProtocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class)
public void testWrongStreamNumberThrowsException() throws Exception {
byte[] wrongStreamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber + 1, wrongStreamNumberBytes, 0);
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
paddingLength);
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
int payloadLength1 = 345, paddingLength1 = 456;
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
paddingLength1);
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(wrongStreamNumberBytes);
out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader);
out.write(payload);
out.write(new byte[paddingLength]);
out.write(new byte[MAC_LENGTH]);
out.write(frameHeader1);
out.write(payload1);
out.write(new byte[paddingLength1]);
out.write(new byte[MAC_LENGTH]);
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
streamNumber, streamHeaderKey);
// Try to read the first frame
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
s.readFrame(buffer);
}
@Test(expected = IOException.class) @Test(expected = IOException.class)
public void testTruncatedFrameThrowsException() throws Exception { public void testTruncatedFrameThrowsException() throws Exception {
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -83,7 +171,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength); paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -111,7 +201,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
byte[] payload = TestUtils.getRandomBytes(payloadLength); byte[] payload = TestUtils.getRandomBytes(payloadLength);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -138,7 +230,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
padding[paddingLength - 1] = 1; padding[paddingLength - 1] = 1;
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);
@@ -162,7 +256,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
paddingLength); paddingLength);
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
out.write(streamHeaderIv); out.write(streamHeaderNonce);
out.write(protocolVersionBytes);
out.write(streamNumberBytes);
out.write(frameKey.getBytes()); out.write(frameKey.getBytes());
out.write(new byte[MAC_LENGTH]); out.write(new byte[MAC_LENGTH]);
out.write(frameHeader); out.write(frameHeader);

View File

@@ -3,6 +3,7 @@ package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestUtils; import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.util.ByteUtils;
import org.junit.Test; import org.junit.Test;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
@@ -11,8 +12,9 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HE
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -21,7 +23,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
private final AuthenticatedCipher cipher; private final AuthenticatedCipher cipher;
private final SecretKey streamHeaderKey, frameKey; private final SecretKey streamHeaderKey, frameKey;
private final byte[] tag, streamHeaderIv, payload; private final byte[] tag, streamHeaderNonce, protocolVersionBytes;
private final byte[] streamNumberBytes, payload;
private final long streamNumber = 1234; private final long streamNumber = 1234;
private final int payloadLength = 123, paddingLength = 234; private final int payloadLength = 123, paddingLength = 234;
@@ -30,7 +33,12 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
streamHeaderKey = TestUtils.getSecretKey(); streamHeaderKey = TestUtils.getSecretKey();
frameKey = TestUtils.getSecretKey(); frameKey = TestUtils.getSecretKey();
tag = TestUtils.getRandomBytes(TAG_LENGTH); tag = TestUtils.getRandomBytes(TAG_LENGTH);
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH); streamHeaderNonce =
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
protocolVersionBytes = new byte[2];
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
streamNumberBytes = new byte[8];
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
payload = TestUtils.getRandomBytes(payloadLength); payload = TestUtils.getRandomBytes(payloadLength);
} }
@@ -38,7 +46,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePayloadLength() throws Exception { public void testRejectsNegativePayloadLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, -1, 0, false); s.writeFrame(payload, -1, 0, false);
} }
@@ -47,7 +56,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsNegativePaddingLength() throws Exception { public void testRejectsNegativePaddingLength() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, 0, -1, false); s.writeFrame(payload, 0, -1, false);
} }
@@ -56,7 +66,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testRejectsMaxPayloadPlusPadding() throws Exception { public void testRejectsMaxPayloadPlusPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1]; byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false); s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
@@ -66,7 +77,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadIncludingPadding() throws Exception { public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH]; byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false); s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
@@ -78,7 +90,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testAcceptsMaxPayloadWithoutPadding() throws Exception { public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH]; byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false); s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
@@ -90,14 +103,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception { public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, false); s.writeFrame(payload, payloadLength, 0, false);
// Expect the tag, stream header, frame header, payload and MAC // Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -113,14 +129,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithTag() throws Exception { public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, true); s.writeFrame(payload, payloadLength, 0, true);
// Expect the tag, stream header, frame header, payload and MAC // Expect the tag, stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -136,13 +155,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception { public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, false); s.writeFrame(payload, payloadLength, 0, false);
// Expect the stream header, frame header, payload and MAC // Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -158,13 +180,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception { public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, 0, true); s.writeFrame(payload, payloadLength, 0, true);
// Expect the stream header, frame header, payload and MAC // Expect the stream header, frame header, payload and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -180,14 +205,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithTag() throws Exception { public void testWritePaddedNonFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false); s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the tag, stream header, frame header, payload, padding and MAC // Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -205,14 +233,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithTag() throws Exception { public void testWritePaddedFinalFrameWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true); s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the tag, stream header, frame header, payload, padding and MAC // Expect the tag, stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -230,13 +261,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception { public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, false); s.writeFrame(payload, payloadLength, paddingLength, false);
// Expect the stream header, frame header, payload, padding and MAC // Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -254,13 +288,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWritePaddedFinalFrameWithoutTag() throws Exception { public void testWritePaddedFinalFrameWithoutTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
s.writeFrame(payload, payloadLength, paddingLength, true); s.writeFrame(payload, payloadLength, paddingLength, true);
// Expect the stream header, frame header, payload, padding and MAC // Expect the stream header, frame header, payload, padding and MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -278,7 +315,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testWriteTwoFramesWithTag() throws Exception { public void testWriteTwoFramesWithTag() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
int payloadLength1 = 345, paddingLength1 = 456; int payloadLength1 = 345, paddingLength1 = 456;
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1); byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
@@ -289,7 +327,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// MAC, second frame header, payload, padding, MAC // MAC, second frame header, payload, padding, MAC
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH]; byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
@@ -315,7 +355,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception { throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream once // Flush the stream once
s.flush(); s.flush();
@@ -323,7 +364,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header // Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
@@ -335,7 +378,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
throws Exception { throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, tag, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream twice // Flush the stream twice
s.flush(); s.flush();
@@ -344,7 +388,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
// Expect the tag and stream header // Expect the tag and stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(tag); expected.write(tag);
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);
@@ -355,14 +401,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
public void testFlushDoesNotWriteTagIfNull() throws Exception { public void testFlushDoesNotWriteTagIfNull() throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher, StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey); streamNumber, null, streamHeaderNonce, streamHeaderKey,
frameKey);
// Flush the stream once // Flush the stream once
s.flush(); s.flush();
// Expect the stream header // Expect the stream header
ByteArrayOutputStream expected = new ByteArrayOutputStream(); ByteArrayOutputStream expected = new ByteArrayOutputStream();
expected.write(streamHeaderIv); expected.write(streamHeaderNonce);
expected.write(protocolVersionBytes);
expected.write(streamNumberBytes);
expected.write(frameKey.getBytes()); expected.write(frameKey.getBytes());
expected.write(new byte[MAC_LENGTH]); expected.write(new byte[MAC_LENGTH]);

View File

@@ -0,0 +1,59 @@
package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.TestSecureRandomProvider;
import org.briarproject.bramble.test.TestUtils;
import org.junit.Test;
import java.util.HashSet;
import java.util.Set;
import static junit.framework.TestCase.assertTrue;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
public class TagEncodingTest extends BrambleTestCase {
private final CryptoComponent crypto;
private final SecretKey tagKey;
private final long streamNumber = 1234567890;
public TagEncodingTest() {
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
tagKey = TestUtils.getSecretKey();
}
@Test
public void testKeyAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
SecretKey tagKey = TestUtils.getSecretKey();
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testProtocolVersionAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
assertTrue(set.add(new Bytes(tag)));
}
}
@Test
public void testStreamNumberAffectsTag() throws Exception {
Set<Bytes> set = new HashSet<Bytes>();
for (int i = 0; i < 100; i++) {
byte[] tag = new byte[TAG_LENGTH];
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
assertTrue(set.add(new Bytes(tag)));
}
}
}

View File

@@ -34,6 +34,7 @@ import java.util.Collection;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH; import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.junit.Assert.assertArrayEquals; import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
@@ -115,7 +116,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
private void read(byte[] connectionData) throws Exception { private void read(byte[] connectionData) throws Exception {
// Calculate the expected tag // Calculate the expected tag
byte[] expectedTag = new byte[TAG_LENGTH]; byte[] expectedTag = new byte[TAG_LENGTH];
crypto.encodeTag(expectedTag, tagKey, streamNumber); crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
// Read the tag // Read the tag
InputStream in = new ByteArrayInputStream(connectionData); InputStream in = new ByteArrayInputStream(connectionData);

View File

@@ -33,6 +33,7 @@ import java.util.concurrent.ScheduledExecutorService;
import static java.util.concurrent.TimeUnit.MILLISECONDS; import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE; import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH; import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED; import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
@@ -86,7 +87,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets per contact) // Encode the tags (3 sets per contact)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(6).of(crypto).encodeTag(with(any(byte[].class)), exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated
@@ -133,7 +134,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys // Save the keys
@@ -199,7 +200,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -247,7 +248,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -306,7 +307,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -355,7 +356,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
} }
// Rotate the transport keys (the keys are unaffected) // Rotate the transport keys (the keys are unaffected)
@@ -365,7 +366,8 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
oneOf(db).addTransportKeys(txn, contactId, transportKeys); oneOf(db).addTransportKeys(txn, contactId, transportKeys);
// Encode a new tag after sliding the window // Encode a new tag after sliding the window
oneOf(crypto).encodeTag(with(any(byte[].class)), oneOf(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with((long) REORDERING_WINDOW_SIZE)); with(tagKey), with(PROTOCOL_VERSION),
with((long) REORDERING_WINDOW_SIZE));
will(new EncodeTagAction(tags)); will(new EncodeTagAction(tags));
// Save the reordering window (previous rotation period, base 1) // Save the reordering window (previous rotation period, base 1)
oneOf(db).setReorderingWindow(txn, contactId, transportId, 999, oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
@@ -428,7 +430,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Schedule key rotation at the start of the next rotation period // Schedule key rotation at the start of the next rotation period
@@ -450,7 +452,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
// Encode the tags (3 sets) // Encode the tags (3 sets)
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) { for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
exactly(3).of(crypto).encodeTag(with(any(byte[].class)), exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
with(tagKey), with(i)); with(tagKey), with(PROTOCOL_VERSION), with(i));
will(new EncodeTagAction()); will(new EncodeTagAction());
} }
// Save the keys that were rotated // Save the keys that were rotated

View File

@@ -7,10 +7,19 @@ apply plugin: 'witness'
dependencies { dependencies {
compile project(':bramble-core') compile project(':bramble-core')
compile fileTree(dir: 'libs', include: '*.jar') compile fileTree(dir: 'libs', include: '*.jar')
compile 'net.java.dev.jna:jna:4.4.0'
compile 'net.java.dev.jna:jna-platform:4.4.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput') testCompile project(path: ':bramble-core', configuration: 'testOutput')
} }
dependencyVerification {
verify = [
'net.java.dev.jna:jna:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
'net.java.dev.jna:jna-platform:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
]
}
tasks.withType(Test) { tasks.withType(Test) {
systemProperty 'java.library.path', 'libs' systemProperty 'java.library.path', 'libs'
} }

Binary file not shown.

View File

@@ -5,13 +5,10 @@ dependencies {
def supportVersion = '23.2.1' def supportVersion = '23.2.1'
compile project(':briar-core') compile project(':briar-core')
compile project(':bramble-android') compile project(':bramble-android')
compile fileTree(dir: 'libs', include: '*.jar')
compile "com.android.support:support-v4:$supportVersion" compile "com.android.support:support-v4:$supportVersion"
compile("com.android.support:appcompat-v7:$supportVersion") { compile("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4' exclude module: 'support-v4'
} }
compile("com.android.support:preference-v14:$supportVersion") { compile("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4' exclude module: 'support-v4'
} }
@@ -20,7 +17,7 @@ dependencies {
exclude module: 'recyclerview-v7' exclude module: 'recyclerview-v7'
} }
compile "com.android.support:cardview-v7:$supportVersion" compile "com.android.support:cardview-v7:$supportVersion"
compile 'com.android.support:support-annotations:23.4.0' compile "com.android.support:support-annotations:$supportVersion"
compile('ch.acra:acra:4.8.5') { compile('ch.acra:acra:4.8.5') {
exclude module: 'support-v4' exclude module: 'support-v4'
exclude module: 'support-annotations' exclude module: 'support-annotations'
@@ -28,15 +25,16 @@ dependencies {
compile 'info.guardianproject.panic:panic:0.5' compile 'info.guardianproject.panic:panic:0.5'
compile 'info.guardianproject.trustedintents:trustedintents:0.2' compile 'info.guardianproject.trustedintents:trustedintents:0.2'
compile 'de.hdodenhof:circleimageview:2.1.0' compile 'de.hdodenhof:circleimageview:2.1.0'
compile 'com.google.zxing:core:3.2.1' compile 'com.google.zxing:core:3.3.0'
provided 'javax.annotation:jsr250-api:1.0'
compile 'com.jpardogo.materialtabstrip:library:1.1.0' compile 'com.jpardogo.materialtabstrip:library:1.1.0'
compile 'com.github.bumptech.glide:glide:3.7.0' compile 'com.github.bumptech.glide:glide:3.8.0'
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0' compile 'uk.co.samuelwall:material-tap-target-prompt:1.9.2'
provided 'javax.annotation:jsr250-api:1.0'
testCompile project(path: ':bramble-core', configuration: 'testOutput') testCompile project(path: ':bramble-core', configuration: 'testOutput')
testCompile 'org.robolectric:robolectric:3.0' testCompile 'org.robolectric:robolectric:3.0'
testCompile 'org.mockito:mockito-core:1.10.19' testCompile 'org.mockito:mockito-core:2.8.9'
} }
dependencyVerification { dependencyVerification {
@@ -45,20 +43,18 @@ dependencyVerification {
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2', 'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e', 'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503', 'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259', 'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448', 'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:750d9e7b940dc0ee48f8680623b55d46e14e8727acc922d7b156e57e7c549655',
'uk.co.samuelwall:material-tap-target-prompt:5d4951124366bc5c52e57beaa294db7611f0aa2a8d80e0163e1383e1966ba5b2',
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d', 'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7', 'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686', 'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01', 'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1', 'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1', 'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe', 'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025', 'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b',
'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a',
] ]
} }
@@ -82,7 +78,10 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 14 minSdkVersion 14
targetSdkVersion 22 targetSdkVersion 22
resValue "string", "app_package", "org.briarproject.briar" versionCode 1603
versionName "0.16.3"
applicationId "org.briarproject.briar.beta"
resValue "string", "app_package", "org.briarproject.briar.beta"
buildConfigField "String", "GitHash", "\"${getGitHash()}\"" buildConfigField "String", "GitHash", "\"${getGitHash()}\""
} }
@@ -107,5 +106,6 @@ android {
lintOptions { lintOptions {
warning 'MissingTranslation' warning 'MissingTranslation'
warning 'ImpliedQuantity' warning 'ImpliedQuantity'
warning 'ExtraTranslation'
} }
} }

View File

@@ -1,9 +1,7 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<manifest <manifest
package="org.briarproject.briar" package="org.briarproject.briar"
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android">
android:versionCode="13"
android:versionName="0.13">
<uses-feature android:name="android.hardware.bluetooth"/> <uses-feature android:name="android.hardware.bluetooth"/>
<uses-feature android:name="android.hardware.camera" /> <uses-feature android:name="android.hardware.camera" />
@@ -21,7 +19,7 @@
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" /> <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<application <application
android:name=".android.BriarApplicationImpl" android:name="org.briarproject.briar.android.BriarApplicationImpl"
android:allowBackup="false" android:allowBackup="false"
android:icon="@mipmap/ic_launcher_round" android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name" android:label="@string/app_name"
@@ -29,7 +27,7 @@
android:theme="@style/BriarTheme"> android:theme="@style/BriarTheme">
<service <service
android:name=".android.BriarService" android:name="org.briarproject.briar.android.BriarService"
android:exported="false"> android:exported="false">
<intent-filter> <intent-filter>
<action android:name="org.briarproject.briar.android.BriarService"/> <action android:name="org.briarproject.briar.android.BriarService"/>
@@ -37,7 +35,7 @@
</service> </service>
<activity <activity
android:name=".android.reporting.DevReportActivity" android:name="org.briarproject.briar.android.reporting.DevReportActivity"
android:excludeFromRecents="true" android:excludeFromRecents="true"
android:exported="false" android:exported="false"
android:finishOnTaskLaunch="true" android:finishOnTaskLaunch="true"
@@ -49,24 +47,24 @@
</activity> </activity>
<activity <activity
android:name=".android.splash.ExpiredActivity" android:name="org.briarproject.briar.android.splash.ExpiredActivity"
android:label="@string/app_name"> android:label="@string/app_name">
</activity> </activity>
<activity <activity
android:name=".android.login.PasswordActivity" android:name="org.briarproject.briar.android.login.PasswordActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:windowSoftInputMode="stateVisible"> android:windowSoftInputMode="stateVisible">
</activity> </activity>
<activity <activity
android:name=".android.login.SetupActivity" android:name="org.briarproject.briar.android.login.SetupActivity"
android:label="@string/setup_title" android:label="@string/setup_title"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
</activity> </activity>
<activity <activity
android:name=".android.splash.SplashScreenActivity" android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:label="@string/app_name"> android:label="@string/app_name">
<intent-filter> <intent-filter>
@@ -76,268 +74,268 @@
</activity> </activity>
<activity <activity
android:name=".android.navdrawer.NavDrawerActivity" android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:launchMode="singleTop"> android:launchMode="singleTop">
</activity> </activity>
<activity <activity
android:name=".android.contact.ConversationActivity" android:name="org.briarproject.briar.android.contact.ConversationActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateHidden|adjustResize"> android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.creation.CreateGroupActivity" android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
android:label="@string/groups_create_group_title" android:label="@string/groups_create_group_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="adjustResize"> android:windowSoftInputMode="adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.conversation.GroupActivity" android:name="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.invitation.GroupInvitationActivity" android:name="org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity"
android:label="@string/groups_invitations_title" android:label="@string/groups_invitations_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.privategroup.memberlist.GroupMemberListActivity" android:name="org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity"
android:label="@string/groups_member_list" android:label="@string/groups_member_list"
android:parentActivityName=".android.privategroup.conversation.GroupActivity" android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity" android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.reveal.RevealContactsActivity" android:name="org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity"
android:label="@string/groups_reveal_contacts" android:label="@string/groups_reveal_contacts"
android:parentActivityName=".android.privategroup.conversation.GroupActivity" android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateAlwaysHidden"> android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity" android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.privategroup.creation.GroupInviteActivity" android:name="org.briarproject.briar.android.privategroup.creation.GroupInviteActivity"
android:label="@string/groups_invite_members" android:label="@string/groups_invite_members"
android:parentActivityName=".android.privategroup.conversation.GroupActivity" android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.privategroup.conversation.GroupActivity"/> android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.sharing.ForumInvitationActivity" android:name="org.briarproject.briar.android.sharing.ForumInvitationActivity"
android:label="@string/forum_invitations_title" android:label="@string/forum_invitations_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.BlogInvitationActivity" android:name="org.briarproject.briar.android.sharing.BlogInvitationActivity"
android:label="@string/blogs_sharing_invitations_title" android:label="@string/blogs_sharing_invitations_title"
android:parentActivityName=".android.contact.ConversationActivity"> android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity" android:value="org.briarproject.briar.android.contact.ConversationActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.forum.CreateForumActivity" android:name="org.briarproject.briar.android.forum.CreateForumActivity"
android:label="@string/create_forum_title" android:label="@string/create_forum_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible"> android:windowSoftInputMode="adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.forum.ForumActivity" android:name="org.briarproject.briar.android.forum.ForumActivity"
android:label="@string/app_name" android:label="@string/app_name"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.ShareForumActivity" android:name="org.briarproject.briar.android.sharing.ShareForumActivity"
android:label="@string/activity_share_toolbar_header" android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.forum.ForumActivity" android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.forum.ForumActivity" android:value="org.briarproject.briar.android.forum.ForumActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.ShareBlogActivity" android:name="org.briarproject.briar.android.sharing.ShareBlogActivity"
android:label="@string/activity_share_toolbar_header" android:label="@string/activity_share_toolbar_header"
android:parentActivityName=".android.blog.BlogActivity" android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="adjustResize|stateHidden"> android:windowSoftInputMode="adjustResize|stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.ForumSharingStatusActivity" android:name="org.briarproject.briar.android.sharing.ForumSharingStatusActivity"
android:label="@string/sharing_status" android:label="@string/sharing_status"
android:parentActivityName=".android.forum.ForumActivity"> android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.forum.ForumActivity" android:value="org.briarproject.briar.android.forum.ForumActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.sharing.BlogSharingStatusActivity" android:name="org.briarproject.briar.android.sharing.BlogSharingStatusActivity"
android:label="@string/sharing_status" android:label="@string/sharing_status"
android:parentActivityName=".android.blog.BlogActivity"> android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.BlogActivity" android:name="org.briarproject.briar.android.blog.BlogActivity"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"> android:theme="@style/BriarTheme.NoActionBar">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.blog.WriteBlogPostActivity" android:name="org.briarproject.briar.android.blog.WriteBlogPostActivity"
android:label="@string/blogs_write_blog_post" android:label="@string/blogs_write_blog_post"
android:parentActivityName=".android.blog.BlogActivity" android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="stateVisible|adjustResize"> android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.ReblogActivity" android:name="org.briarproject.briar.android.blog.ReblogActivity"
android:label="@string/blogs_reblog_button" android:label="@string/blogs_reblog_button"
android:parentActivityName=".android.blog.BlogActivity" android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
android:windowSoftInputMode="stateHidden"> android:windowSoftInputMode="stateHidden">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.blog.BlogActivity" android:value="org.briarproject.briar.android.blog.BlogActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.RssFeedImportActivity" android:name="org.briarproject.briar.android.blog.RssFeedImportActivity"
android:label="@string/blogs_rss_feeds_import" android:label="@string/blogs_rss_feeds_import"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:windowSoftInputMode="stateVisible|adjustResize"> android:windowSoftInputMode="stateVisible|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.blog.RssFeedManageActivity" android:name="org.briarproject.briar.android.blog.RssFeedManageActivity"
android:label="@string/blogs_rss_feeds_manage" android:label="@string/blogs_rss_feeds_manage"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.invitation.AddContactActivity" android:name="org.briarproject.briar.android.invitation.AddContactActivity"
android:label="@string/add_contact_title" android:label="@string/add_contact_title"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.keyagreement.KeyAgreementActivity" android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
android:label="@string/add_contact_title" android:label="@string/add_contact_title"
android:theme="@style/BriarTheme.NoActionBar" android:theme="@style/BriarTheme.NoActionBar"
android:parentActivityName=".android.navdrawer.NavDrawerActivity"> android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity"/> android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
</activity> </activity>
<activity <activity
android:name=".android.introduction.IntroductionActivity" android:name="org.briarproject.briar.android.introduction.IntroductionActivity"
android:label="@string/introduction_activity_title" android:label="@string/introduction_activity_title"
android:parentActivityName=".android.contact.ConversationActivity" android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity"
android:windowSoftInputMode="stateHidden|adjustResize"> android:windowSoftInputMode="stateHidden|adjustResize">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.contact.ConversationActivity" android:value="org.briarproject.briar.android.contact.ConversationActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.StartupFailureActivity" android:name="org.briarproject.briar.android.StartupFailureActivity"
android:label="@string/startup_failed_activity_title"> android:label="@string/startup_failed_activity_title">
</activity> </activity>
<activity <activity
android:name=".android.settings.SettingsActivity" android:name="org.briarproject.briar.android.settings.SettingsActivity"
android:label="@string/settings_button" android:label="@string/settings_button"
android:parentActivityName=".android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY"> android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.navdrawer.NavDrawerActivity" android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
/> />
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/> <action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
@@ -346,27 +344,27 @@
</activity> </activity>
<activity <activity
android:name=".android.login.ChangePasswordActivity" android:name="org.briarproject.briar.android.login.ChangePasswordActivity"
android:label="@string/change_password" android:label="@string/change_password"
android:parentActivityName=".android.settings.SettingsActivity"> android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.settings.SettingsActivity" android:value="org.briarproject.briar.android.settings.SettingsActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.panic.PanicPreferencesActivity" android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity"
android:label="@string/panic_setting" android:label="@string/panic_setting"
android:parentActivityName=".android.settings.SettingsActivity"> android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data <meta-data
android:name="android.support.PARENT_ACTIVITY" android:name="android.support.PARENT_ACTIVITY"
android:value=".android.settings.SettingsActivity" android:value="org.briarproject.briar.android.settings.SettingsActivity"
/> />
</activity> </activity>
<activity <activity
android:name=".android.panic.PanicResponderActivity" android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
android:noHistory="true" android:noHistory="true"
android:theme="@android:style/Theme.NoDisplay"> android:theme="@android:style/Theme.NoDisplay">
<!-- this can never have launchMode singleTask or singleInstance! --> <!-- this can never have launchMode singleTask or singleInstance! -->
@@ -377,7 +375,7 @@
</activity> </activity>
<activity <activity
android:name=".android.panic.ExitActivity" android:name="org.briarproject.briar.android.panic.ExitActivity"
android:theme="@android:style/Theme.NoDisplay"> android:theme="@android:style/Theme.NoDisplay">
</activity> </activity>

View File

@@ -32,6 +32,7 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
import org.briarproject.briar.api.blog.BlogManager; import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostFactory; import org.briarproject.briar.api.blog.BlogPostFactory;
import org.briarproject.briar.api.blog.BlogSharingManager; import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.feed.FeedManager; import org.briarproject.briar.api.feed.FeedManager;
import org.briarproject.briar.api.forum.ForumManager; import org.briarproject.briar.api.forum.ForumManager;
import org.briarproject.briar.api.forum.ForumSharingManager; import org.briarproject.briar.api.forum.ForumSharingManager;
@@ -78,6 +79,8 @@ public interface AndroidComponent
@DatabaseExecutor @DatabaseExecutor
Executor databaseExecutor(); Executor databaseExecutor();
MessageTracker messageTracker();
LifecycleManager lifecycleManager(); LifecycleManager lifecycleManager();
IdentityManager identityManager(); IdentityManager identityManager();

View File

@@ -61,6 +61,7 @@ import static android.content.Context.NOTIFICATION_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE; import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL; import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
import static android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET; import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@@ -327,6 +328,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE); b.setCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET); b.setVisibility(VISIBILITY_SECRET);
} }
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
@@ -347,17 +353,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
return defaults; return defaults;
} }
@Override
public void clearAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearContactNotification();
clearIntroductionSuccessNotification();
}
});
}
@UiThread @UiThread
private void showGroupMessageNotification(final GroupId g) { private void showGroupMessageNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
@@ -432,6 +427,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL); b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET); b.setVisibility(VISIBILITY_SECRET);
} }
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
@@ -440,16 +440,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
} }
@Override
public void clearAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearGroupMessageNotification();
}
});
}
@UiThread @UiThread
private void showForumPostNotification(final GroupId g) { private void showForumPostNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
@@ -524,6 +514,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL); b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET); b.setVisibility(VISIBILITY_SECRET);
} }
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
@@ -532,16 +527,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
} }
@Override
public void clearAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
clearForumPostNotification();
}
});
}
@UiThread @UiThread
private void showBlogPostNotification(final GroupId g) { private void showBlogPostNotification(final GroupId g) {
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
@@ -602,6 +587,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_SOCIAL); b.setCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET); b.setVisibility(VISIBILITY_SECRET);
} }
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
@@ -658,6 +648,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0)); b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
if (Build.VERSION.SDK_INT >= 21) { if (Build.VERSION.SDK_INT >= 21) {
b.setCategory(CATEGORY_MESSAGE); b.setCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
if (showOnLockScreen)
b.setVisibility(VISIBILITY_PRIVATE);
else
b.setVisibility(VISIBILITY_SECRET); b.setVisibility(VISIBILITY_SECRET);
} }
Object o = appContext.getSystemService(NOTIFICATION_SERVICE); Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
@@ -705,68 +700,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
}); });
} }
@Override
public void blockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = true;
blockIntroductions = true;
}
});
}
@Override
public void unblockAllContactNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockContacts = false;
blockIntroductions = false;
}
});
}
@Override
public void blockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = true;
}
});
}
@Override
public void unblockAllGroupMessageNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockGroups = false;
}
});
}
@Override
public void blockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = true;
}
});
}
@Override
public void unblockAllForumPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
blockForums = false;
}
});
}
@Override @Override
public void blockAllBlogPostNotifications() { public void blockAllBlogPostNotifications() {
androidExecutor.runOnUiThread(new Runnable() { androidExecutor.runOnUiThread(new Runnable() {
@@ -786,5 +719,4 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} }
}); });
} }
} }

View File

@@ -38,8 +38,6 @@ public class AppModule {
static class EagerSingletons { static class EagerSingletons {
@Inject @Inject
AndroidNotificationManager androidNotificationManager; AndroidNotificationManager androidNotificationManager;
@Inject
ScreenFilterMonitor screenFilterMonitor;
} }
private final Application application; private final Application application;
@@ -171,10 +169,8 @@ public class AppModule {
} }
@Provides @Provides
@Singleton
ScreenFilterMonitor provideScreenFilterMonitor( ScreenFilterMonitor provideScreenFilterMonitor(
LifecycleManager lifecycleManager, ScreenFilterMonitorImpl sfm) { ScreenFilterMonitorImpl screenFilterMonitor) {
lifecycleManager.registerService(sfm); return screenFilterMonitor;
return sfm;
} }
} }

View File

@@ -6,5 +6,9 @@ package org.briarproject.briar.android;
*/ */
public interface BriarApplication { public interface BriarApplication {
// This build expires on 21 October 2017
long EXPIRY_DATE = 1508544000 * 1000L;
AndroidComponent getApplicationComponent(); AndroidComponent getApplicationComponent();
} }

View File

@@ -1,24 +1,13 @@
package org.briarproject.briar.android; package org.briarproject.briar.android;
import android.app.Application; import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.content.pm.PackageInfo; import android.content.pm.PackageInfo;
import android.content.pm.PackageManager; import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.Signature; import android.content.pm.Signature;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.preference.PreferenceManager;
import org.briarproject.bramble.api.lifecycle.Service; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.lifecycle.ServiceException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.util.StringUtils; import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
@@ -27,37 +16,26 @@ import java.io.InputStream;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory; import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Collection;
import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.TreeSet; import java.util.TreeSet;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW; import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
import static android.content.Intent.ACTION_PACKAGE_ADDED;
import static android.content.Intent.EXTRA_REPLACING;
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM; import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP; import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
import static android.content.pm.PackageManager.GET_PERMISSIONS; import static android.content.pm.PackageManager.GET_PERMISSIONS;
import static android.content.pm.PackageManager.GET_SIGNATURES; import static android.content.pm.PackageManager.GET_SIGNATURES;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@MethodsNotNullByDefault @NotNullByDefault
@ParametersNotNullByDefault class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
public class ScreenFilterMonitorImpl extends BroadcastReceiver
implements Service, ScreenFilterMonitor {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(ScreenFilterMonitorImpl.class.getName()); Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
private static final String PREF_SCREEN_FILTER_APPS =
"shownScreenFilterApps";
/* /*
* Ignore Play Services if it uses this package name and public key - it's * Ignore Play Services if it uses this package name and public key - it's
@@ -78,124 +56,17 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" + "82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
"0B145B6AA192858E79020103"; "0B145B6AA192858E79020103";
private final Context appContext;
private final AndroidExecutor androidExecutor;
private final PackageManager pm; private final PackageManager pm;
private final SharedPreferences prefs;
private final AtomicBoolean used = new AtomicBoolean(false);
// The following must only be accessed on the UI thread
private final Set<String> apps = new HashSet<>();
private final Set<String> shownApps;
private boolean serviceStarted = false;
@Inject @Inject
ScreenFilterMonitorImpl(AndroidExecutor executor, Application app) { ScreenFilterMonitorImpl(Application app) {
this.androidExecutor = executor; pm = app.getPackageManager();
this.appContext = app;
pm = appContext.getPackageManager();
prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
shownApps = getShownScreenFilterApps();
}
@Override
public void startService() throws ServiceException {
if (used.getAndSet(true)) throw new IllegalStateException();
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_PACKAGE_ADDED);
intentFilter.addDataScheme("package");
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
intentFilter);
apps.addAll(getInstalledScreenFilterApps());
serviceStarted = true;
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
@Override
public void stopService() throws ServiceException {
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
@Override
public Void call() {
serviceStarted = false;
appContext.unregisterReceiver(ScreenFilterMonitorImpl.this);
return null;
}
});
try {
f.get();
} catch (InterruptedException | ExecutionException e) {
throw new ServiceException(e);
}
}
private Set<String> getShownScreenFilterApps() {
// Result must not be modified
Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
HashSet<String> result = new HashSet<>();
if (s != null) {
result.addAll(s);
}
return result;
}
@Override
public void onReceive(Context context, Intent intent) {
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
final String packageName =
intent.getData().getEncodedSchemeSpecificPart();
androidExecutor.runOnUiThread(new Runnable() {
@Override
public void run() {
String pkg = isOverlayApp(packageName);
if (pkg == null) {
return;
}
apps.add(pkg);
}
});
}
} }
@Override @Override
@UiThread @UiThread
public Set<String> getApps() { public Set<String> getApps() {
if (!serviceStarted) { Set<String> screenFilterApps = new TreeSet<>();
apps.addAll(getInstalledScreenFilterApps());
}
TreeSet<String> buf = new TreeSet<>();
if (apps.isEmpty()) {
return buf;
}
buf.addAll(apps);
buf.removeAll(shownApps);
return buf;
}
@Override
@UiThread
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
HashSet<String> buf = new HashSet<>(s);
shownApps.addAll(buf);
if (persistent && !s.isEmpty()) {
buf.addAll(getShownScreenFilterApps());
prefs.edit()
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
.apply();
}
}
private Set<String> getInstalledScreenFilterApps() {
HashSet<String> screenFilterApps = new HashSet<>();
List<PackageInfo> packageInfos = List<PackageInfo> packageInfos =
pm.getInstalledPackages(GET_PERMISSIONS); pm.getInstalledPackages(GET_PERMISSIONS);
for (PackageInfo packageInfo : packageInfos) { for (PackageInfo packageInfo : packageInfos) {
@@ -209,21 +80,6 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
return screenFilterApps; return screenFilterApps;
} }
// Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
// returns the app name.
@Nullable
private String isOverlayApp(String pkg) {
try {
PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
if (isOverlayApp(pkgInfo)) {
return pkgToString(pkgInfo);
}
} catch (NameNotFoundException e) {
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
}
return null;
}
// Fetches the application name for a given package. // Fetches the application name for a given package.
@Nullable @Nullable
private String pkgToString(PackageInfo pkgInfo) { private String pkgToString(PackageInfo pkgInfo) {

View File

@@ -39,7 +39,7 @@ import org.briarproject.briar.android.privategroup.conversation.GroupConversatio
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity; import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment; import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment;
import org.briarproject.briar.android.privategroup.creation.CreateGroupMessageFragment; import org.briarproject.briar.android.privategroup.creation.CreateGroupMessageFragment;
import org.briarproject.briar.android.privategroup.creation.GroupCreateModule; import org.briarproject.briar.android.privategroup.creation.CreateGroupModule;
import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity; import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity;
import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment; import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment;
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity; import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
@@ -71,7 +71,7 @@ import dagger.Component;
@Component( @Component(
modules = {ActivityModule.class, ForumModule.class, SharingModule.class, modules = {ActivityModule.class, ForumModule.class, SharingModule.class,
BlogModule.class, ContactModule.class, GroupListModule.class, BlogModule.class, ContactModule.class, GroupListModule.class,
GroupCreateModule.class, GroupInvitationModule.class, CreateGroupModule.class, GroupInvitationModule.class,
GroupConversationModule.class, GroupMemberModule.class, GroupConversationModule.class, GroupMemberModule.class,
GroupRevealModule.class}, GroupRevealModule.class},
dependencies = AndroidComponent.class) dependencies = AndroidComponent.class)

View File

@@ -2,9 +2,13 @@ package org.briarproject.briar.android.activity;
import android.os.Bundle; import android.os.Bundle;
import android.os.IBinder; import android.os.IBinder;
import android.support.annotation.LayoutRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.app.AppCompatActivity; import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View; import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.inputmethod.InputMethodManager; import android.view.inputmethod.InputMethodManager;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -13,7 +17,9 @@ import org.briarproject.briar.android.BriarApplication;
import org.briarproject.briar.android.DestroyableContext; import org.briarproject.briar.android.DestroyableContext;
import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.forum.ForumModule; import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.fragment.SFDialogFragment; import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
import org.briarproject.briar.android.widget.TapSafeFrameLayout;
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.util.ArrayList; import java.util.ArrayList;
@@ -23,21 +29,23 @@ import java.util.Set;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.WindowManager.LayoutParams.FLAG_SECURE; import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT; import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS; import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
public abstract class BaseActivity extends AppCompatActivity public abstract class BaseActivity extends AppCompatActivity
implements DestroyableContext { implements DestroyableContext, OnTapFilteredListener {
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
protected ActivityComponent activityComponent; protected ActivityComponent activityComponent;
private final List<ActivityLifecycleController> lifecycleControllers = private final List<ActivityLifecycleController> lifecycleControllers =
new ArrayList<>(); new ArrayList<>();
private boolean destroyed = false; private boolean destroyed = false;
private ScreenFilterDialogFragment dialogFrag;
@Inject
protected ScreenFilterMonitor screenFilterMonitor;
private SFDialogFragment dialogFrag;
public abstract void injectActivity(ActivityComponent component); public abstract void injectActivity(ActivityComponent component);
@@ -65,7 +73,6 @@ public abstract class BaseActivity extends AppCompatActivity
for (ActivityLifecycleController alc : lifecycleControllers) { for (ActivityLifecycleController alc : lifecycleControllers) {
alc.onActivityCreate(this); alc.onActivityCreate(this);
} }
} }
public ActivityComponent getActivityComponent() { public ActivityComponent getActivityComponent() {
@@ -97,12 +104,6 @@ public abstract class BaseActivity extends AppCompatActivity
} }
} }
@Override
protected void onPostResume() {
super.onPostResume();
showNewScreenFilterWarning();
}
@Override @Override
protected void onPause() { protected void onPause() {
super.onPause(); super.onPause();
@@ -112,18 +113,14 @@ public abstract class BaseActivity extends AppCompatActivity
} }
} }
protected void showNewScreenFilterWarning() { private void showScreenFilterWarning() {
final Set<String> apps = screenFilterMonitor.getApps(); if (dialogFrag != null && dialogFrag.isVisible()) return;
if (apps.isEmpty()) { Set<String> apps = screenFilterMonitor.getApps();
return; if (apps.isEmpty()) return;
} dialogFrag =
dialogFrag = SFDialogFragment.newInstance(new ArrayList<>(apps)); ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
dialogFrag.setCancelable(false); dialogFrag.setCancelable(false);
dialogFrag.show(getSupportFragmentManager(), "SFDialog"); dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
}
public void rememberShownApps(ArrayList<String> s, boolean permanent) {
screenFilterMonitor.storeAppsAsShown(s, permanent);
} }
@Override @Override
@@ -161,4 +158,70 @@ public abstract class BaseActivity extends AppCompatActivity
supportFinishAfterTransition(); supportFinishAfterTransition();
} }
/*
* Wraps the given view in a wrapper that notifies this activity when an
* obscured touch has been filtered, and returns the wrapper.
*/
private View makeTapSafeWrapper(View v) {
TapSafeFrameLayout wrapper = new TapSafeFrameLayout(this);
wrapper.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
wrapper.setOnTapFilteredListener(this);
wrapper.addView(v);
return wrapper;
}
/*
* Finds the AppCompat toolbar, if any, and configures it to filter
* obscured touches. If a custom toolbar is used, it will be part of the
* content view and thus protected by the wrapper. But the default toolbar
* is outside the wrapper.
*/
private void protectToolbar() {
View decorView = getWindow().getDecorView();
if (decorView instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) decorView);
if (toolbar != null) toolbar.setFilterTouchesWhenObscured(true);
}
}
@Nullable
private Toolbar findToolbar(ViewGroup vg) {
for (int i = 0, len = vg.getChildCount(); i < len; i++) {
View child = vg.getChildAt(i);
if (child instanceof Toolbar) return (Toolbar) child;
if (child instanceof ViewGroup) {
Toolbar toolbar = findToolbar((ViewGroup) child);
if (toolbar != null) return toolbar;
}
}
return null;
}
@Override
public void setContentView(@LayoutRes int layoutRes) {
setContentView(getLayoutInflater().inflate(layoutRes, null));
}
@Override
public void setContentView(View v) {
super.setContentView(makeTapSafeWrapper(v));
protectToolbar();
}
@Override
public void setContentView(View v, LayoutParams layoutParams) {
super.setContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void addContentView(View v, LayoutParams layoutParams) {
super.addContentView(makeTapSafeWrapper(v), layoutParams);
protectToolbar();
}
@Override
public void onTapFiltered() {
showScreenFilterWarning();
}
} }

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.activity;
import android.annotation.SuppressLint; import android.annotation.SuppressLint;
import android.content.Intent; import android.content.Intent;
import android.os.Build; import android.os.Build;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar; import android.support.v7.app.ActionBar;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.transition.Slide; import android.transition.Slide;
@@ -21,6 +20,7 @@ import org.briarproject.briar.android.panic.ExitActivity;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.android.blog; package org.briarproject.briar.android.blog;
import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@@ -20,8 +21,10 @@ import java.util.logging.Logger;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.View.INVISIBLE; import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE; import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION; import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
@UiThread @UiThread
@@ -58,6 +61,20 @@ abstract class BasePostFragment extends BaseFragment {
progressBar = (ProgressBar) view.findViewById(R.id.progressBar); progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
progressBar.setVisibility(VISIBLE); progressBar.setVisibility(VISIBLE);
ui = new BlogPostViewHolder(view); ui = new BlogPostViewHolder(view);
ui.setOnBlogPostClickListener(new OnBlogPostClickListener() {
@Override
public void onBlogPostClick(BlogPostItem post) {
// We're already there
}
@Override
public void onAuthorClick(BlogPostItem post) {
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
});
return view; return view;
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.blog;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.view.View; import android.view.View;
@@ -15,6 +14,7 @@ import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity; import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
@MethodsNotNullByDefault @MethodsNotNullByDefault

View File

@@ -106,7 +106,7 @@ class BlogControllerImpl extends BaseControllerImpl
BlogInvitationResponseReceivedEvent b = BlogInvitationResponseReceivedEvent b =
(BlogInvitationResponseReceivedEvent) e; (BlogInvitationResponseReceivedEvent) e;
InvitationResponse r = b.getResponse(); InvitationResponse r = b.getResponse();
if (r.getGroupId().equals(groupId) && r.wasAccepted()) { if (r.getShareableId().equals(groupId) && r.wasAccepted()) {
LOG.info("Blog invitation accepted"); LOG.info("Blog invitation accepted");
onBlogInvitationAccepted(b.getContactId()); onBlogInvitationAccepted(b.getContactId());
} }
@@ -169,7 +169,7 @@ class BlogControllerImpl extends BaseControllerImpl
LocalAuthor a = identityManager.getLocalAuthor(); LocalAuthor a = identityManager.getLocalAuthor();
Blog b = blogManager.getBlog(groupId); Blog b = blogManager.getBlog(groupId);
boolean ours = a.getId().equals(b.getAuthor().getId()); boolean ours = a.getId().equals(b.getAuthor().getId());
boolean removable = blogManager.canBeRemoved(groupId); boolean removable = blogManager.canBeRemoved(b);
BlogItem blog = new BlogItem(b, ours, removable); BlogItem blog = new BlogItem(b, ours, removable);
long duration = System.currentTimeMillis() - now; long duration = System.currentTimeMillis() - now;
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))

View File

@@ -27,7 +27,6 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.blog.BlogController.BlogSharingListener; import org.briarproject.briar.android.blog.BlogController.BlogSharingListener;
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.briar.android.controller.SharingController; import org.briarproject.briar.android.controller.SharingController;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
@@ -216,10 +215,19 @@ public class BlogFragment extends BaseFragment
showNextFragment(f); showNextFragment(f);
} }
@Override
public void onAuthorClick(BlogPostItem post) {
if (post.getGroupId().equals(groupId)) return; // We're already there
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
private void loadBlogPosts(final boolean reload) { private void loadBlogPosts(final boolean reload) {
blogController.loadBlogPosts( blogController.loadBlogPosts(
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>( new UiResultExceptionHandler<Collection<BlogPostItem>,
this) { DbException>(this) {
@Override @Override
public void onResultUi(Collection<BlogPostItem> posts) { public void onResultUi(Collection<BlogPostItem> posts) {
if (posts.isEmpty()) { if (posts.isEmpty()) {
@@ -257,13 +265,13 @@ public class BlogFragment extends BaseFragment
} }
private void setToolbarTitle(Author a) { private void setToolbarTitle(Author a) {
String title = getString(R.string.blogs_personal_blog, a.getName()); getActivity().setTitle(a.getName());
getActivity().setTitle(title);
} }
private void loadSharedContacts() { private void loadSharedContacts() {
blogController.loadSharingContacts( blogController.loadSharingContacts(
new UiResultExceptionHandler<Collection<ContactId>, DbException>(this) { new UiResultExceptionHandler<Collection<ContactId>,
DbException>(this) {
@Override @Override
public void onResultUi(Collection<ContactId> contacts) { public void onResultUi(Collection<ContactId> contacts) {
sharingController.addAll(contacts); sharingController.addAll(contacts);

View File

@@ -48,8 +48,4 @@ class BlogPostAdapter
return a.getId().equals(b.getId()); return a.getId().equals(b.getId());
} }
interface OnBlogPostClickListener {
void onBlogPostClick(BlogPostItem post);
}
} }

View File

@@ -20,7 +20,6 @@ import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.briar.android.view.AuthorView; import org.briarproject.briar.android.view.AuthorView;
import org.briarproject.briar.api.blog.BlogCommentHeader; import org.briarproject.briar.api.blog.BlogCommentHeader;
import org.briarproject.briar.api.blog.BlogPostHeader; import org.briarproject.briar.api.blog.BlogPostHeader;
@@ -49,6 +48,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
private final TextView body; private final TextView body;
private final ViewGroup commentContainer; private final ViewGroup commentContainer;
@Nullable
private OnBlogPostClickListener listener; private OnBlogPostClickListener listener;
BlogPostViewHolder(View v) { BlogPostViewHolder(View v) {
@@ -111,10 +111,15 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
author.setPersona( author.setPersona(
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL); item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
// TODO make author clickable more often #624 // TODO make author clickable more often #624
if (item.getHeader().getType() == POST) { if (listener != null && item.getHeader().getType() == POST) {
author.setBlogLink(post.getGroupId()); author.setAuthorClickable(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onAuthorClick(item);
}
});
} else { } else {
author.unsetBlogLink(); author.setAuthorNotClickable();
} }
// post body // post body
@@ -165,7 +170,14 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
reblogger.setAuthor(item.getAuthor()); reblogger.setAuthor(item.getAuthor());
reblogger.setAuthorStatus(item.getAuthorStatus()); reblogger.setAuthorStatus(item.getAuthorStatus());
reblogger.setDate(item.getTimestamp()); reblogger.setDate(item.getTimestamp());
reblogger.setBlogLink(item.getGroupId()); if (listener != null) {
reblogger.setAuthorClickable(new OnClickListener() {
@Override
public void onClick(View v) {
listener.onAuthorClick(item);
}
});
}
reblogger.setVisibility(VISIBLE); reblogger.setVisibility(VISIBLE);
reblogger.setPersona(AuthorView.REBLOGGER); reblogger.setPersona(AuthorView.REBLOGGER);

View File

@@ -19,7 +19,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
import org.briarproject.briar.android.blog.FeedController.FeedListener; import org.briarproject.briar.android.blog.FeedController.FeedListener;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
@@ -34,6 +33,7 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.app.Activity.RESULT_OK; import static android.app.Activity.RESULT_OK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.support.design.widget.Snackbar.LENGTH_LONG; import static android.support.design.widget.Snackbar.LENGTH_LONG;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID; import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_WRITE_BLOG_POST; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_WRITE_BLOG_POST;
@@ -223,6 +223,14 @@ public class FeedFragment extends BaseFragment implements
showNextFragment(f); showNextFragment(f);
} }
@Override
public void onAuthorClick(BlogPostItem post) {
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
@Override @Override
public String getUniqueTag() { public String getUniqueTag() {
return TAG; return TAG;

View File

@@ -0,0 +1,8 @@
package org.briarproject.briar.android.blog;
interface OnBlogPostClickListener {
void onBlogPostClick(BlogPostItem post);
void onAuthorClick(BlogPostItem post);
}

View File

@@ -5,6 +5,7 @@ import android.os.Bundle;
import android.support.v7.app.AlertDialog; import android.support.v7.app.AlertDialog;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.util.Patterns;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@@ -20,9 +21,12 @@ import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.api.feed.FeedManager; import org.briarproject.briar.api.feed.FeedManager;
import java.io.IOException; import java.io.IOException;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.view.View.GONE; import static android.view.View.GONE;
@@ -98,10 +102,17 @@ public class RssFeedImportActivity extends BriarActivity {
private void enableOrDisableImportButton() { private void enableOrDisableImportButton() {
String url = urlInput.getText().toString(); String url = urlInput.getText().toString();
if (url.startsWith("http://") || url.startsWith("https://")) importButton.setEnabled(validateAndNormaliseUrl(url) != null);
importButton.setEnabled(true); }
else
importButton.setEnabled(false); @Nullable
private String validateAndNormaliseUrl(String url) {
if (!Patterns.WEB_URL.matcher(url).matches()) return null;
try {
return new URL(url).toString();
} catch (MalformedURLException e) {
return null;
}
} }
private void publish() { private void publish() {
@@ -109,7 +120,9 @@ public class RssFeedImportActivity extends BriarActivity {
importButton.setVisibility(GONE); importButton.setVisibility(GONE);
progressBar.setVisibility(VISIBLE); progressBar.setVisibility(VISIBLE);
importFeed(urlInput.getText().toString()); String url = validateAndNormaliseUrl(urlInput.getText().toString());
if (url == null) throw new AssertionError();
importFeed(url);
} }
private void importFeed(final String url) { private void importFeed(final String url) {

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.contact; package org.briarproject.briar.android.contact;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
import android.view.View; import android.view.View;
@@ -13,6 +12,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener; import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
import javax.annotation.Nullable;
import im.delight.android.identicons.IdenticonDrawable; import im.delight.android.identicons.IdenticonDrawable;
@UiThread @UiThread

View File

@@ -178,8 +178,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
notificationManager.blockAllContactNotifications();
notificationManager.clearAllContactNotifications();
eventBus.addListener(this); eventBus.addListener(this);
loadContacts(); loadContacts();
list.startPeriodicUpdate(); list.startPeriodicUpdate();
@@ -189,7 +187,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
eventBus.removeListener(this); eventBus.removeListener(this);
notificationManager.unblockAllContactNotifications();
adapter.clear(); adapter.clear();
list.showProgressBar(); list.showProgressBar();
list.stopPeriodicUpdate(); list.stopPeriodicUpdate();

View File

@@ -17,7 +17,7 @@ public class DbControllerImpl implements DbController {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(DbControllerImpl.class.getName()); Logger.getLogger(DbControllerImpl.class.getName());
private final Executor dbExecutor; protected final Executor dbExecutor;
private final LifecycleManager lifecycleManager; private final LifecycleManager lifecycleManager;
@Inject @Inject

View File

@@ -1,7 +1,5 @@
package org.briarproject.briar.android.controller; package org.briarproject.briar.android.controller;
import android.support.annotation.Nullable;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -15,6 +13,7 @@ import java.util.Collection;
import java.util.HashSet; import java.util.HashSet;
import java.util.Set; import java.util.Set;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
@NotNullByDefault @NotNullByDefault

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.android.forum;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.KeyEvent; import android.view.KeyEvent;
@@ -38,16 +39,15 @@ import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LEN
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class CreateForumActivity extends BriarActivity public class CreateForumActivity extends BriarActivity {
implements OnEditorActionListener, OnClickListener {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(CreateForumActivity.class.getName()); Logger.getLogger(CreateForumActivity.class.getName());
private TextInputLayout nameEntryLayout;
private EditText nameEntry; private EditText nameEntry;
private Button createForumButton; private Button createForumButton;
private ProgressBar progress; private ProgressBar progress;
private TextView feedback;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@Inject @Inject
@@ -59,12 +59,10 @@ public class CreateForumActivity extends BriarActivity
setContentView(R.layout.activity_create_forum); setContentView(R.layout.activity_create_forum);
nameEntryLayout =
(TextInputLayout) findViewById(R.id.createForumNameLayout);
nameEntry = (EditText) findViewById(R.id.createForumNameEntry); nameEntry = (EditText) findViewById(R.id.createForumNameEntry);
TextWatcher nameEntryWatcher = new TextWatcher() { nameEntry.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable s) {
}
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, public void beforeTextChanged(CharSequence s, int start, int count,
@@ -72,21 +70,39 @@ public class CreateForumActivity extends BriarActivity
} }
@Override @Override
public void onTextChanged(CharSequence text, int start, public void onTextChanged(CharSequence s, int start,
int lengthBefore, int lengthAfter) { int lengthBefore, int lengthAfter) {
enableOrDisableCreateButton(); enableOrDisableCreateButton();
} }
};
nameEntry.setOnEditorActionListener(this);
nameEntry.addTextChangedListener(nameEntryWatcher);
feedback = (TextView) findViewById(R.id.createForumFeedback); @Override
public void afterTextChanged(Editable s) {
}
});
nameEntry.setOnEditorActionListener(new OnEditorActionListener() {
@Override
public boolean onEditorAction(TextView v, int actionId,
KeyEvent e) {
createForum();
return true;
}
});
createForumButton = (Button) findViewById(R.id.createForumButton); createForumButton = (Button) findViewById(R.id.createForumButton);
createForumButton.setOnClickListener(this); createForumButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
createForum();
}
});
progress = (ProgressBar) findViewById(R.id.createForumProgressBar); progress = (ProgressBar) findViewById(R.id.createForumProgressBar);
}
@Override
public void onStart() {
super.onStart();
showSoftKeyboard(nameEntry);
} }
@Override @Override
@@ -95,37 +111,28 @@ public class CreateForumActivity extends BriarActivity
} }
private void enableOrDisableCreateButton() { private void enableOrDisableCreateButton() {
if (progress == null) return; // Not created yet if (createForumButton == null) return; // Not created yet
createForumButton.setEnabled(validateName()); createForumButton.setEnabled(validateName());
} }
@Override
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
hideSoftKeyboard(textView);
return true;
}
private boolean validateName() { private boolean validateName() {
String name = nameEntry.getText().toString(); String name = nameEntry.getText().toString();
int length = StringUtils.toUtf8(name).length; int length = StringUtils.toUtf8(name).length;
if (length > MAX_FORUM_NAME_LENGTH) { if (length > MAX_FORUM_NAME_LENGTH) {
feedback.setText(R.string.name_too_long); nameEntryLayout.setError(getString(R.string.name_too_long));
return false; return false;
} }
feedback.setText(""); nameEntryLayout.setError(null);
return length > 0; return length > 0;
} }
@Override private void createForum() {
public void onClick(View view) {
if (view == createForumButton) {
hideSoftKeyboard(view);
if (!validateName()) return; if (!validateName()) return;
hideSoftKeyboard(nameEntry);
createForumButton.setVisibility(GONE); createForumButton.setVisibility(GONE);
progress.setVisibility(VISIBLE); progress.setVisibility(VISIBLE);
storeForum(nameEntry.getText().toString()); storeForum(nameEntry.getText().toString());
} }
}
private void storeForum(final String name) { private void storeForum(final String name) {
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {

View File

@@ -17,6 +17,7 @@ import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.android.forum.ForumController.ForumListener; import org.briarproject.briar.android.forum.ForumController.ForumListener;
import org.briarproject.briar.android.threaded.ThreadListControllerImpl; import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.forum.Forum; import org.briarproject.briar.api.forum.Forum;
import org.briarproject.briar.api.forum.ForumInvitationResponse; import org.briarproject.briar.api.forum.ForumInvitationResponse;
@@ -55,10 +56,10 @@ class ForumControllerImpl extends
LifecycleManager lifecycleManager, IdentityManager identityManager, LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor, @CryptoExecutor Executor cryptoExecutor,
ForumManager forumManager, ForumSharingManager forumSharingManager, ForumManager forumManager, ForumSharingManager forumSharingManager,
EventBus eventBus, Clock clock, EventBus eventBus, Clock clock, MessageTracker messageTracker,
AndroidNotificationManager notificationManager) { AndroidNotificationManager notificationManager) {
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
eventBus, clock, notificationManager); eventBus, clock, notificationManager, messageTracker);
this.forumManager = forumManager; this.forumManager = forumManager;
this.forumSharingManager = forumSharingManager; this.forumSharingManager = forumSharingManager;
} }
@@ -84,7 +85,7 @@ class ForumControllerImpl extends
(ForumInvitationResponseReceivedEvent) e; (ForumInvitationResponseReceivedEvent) e;
ForumInvitationResponse r = ForumInvitationResponse r =
(ForumInvitationResponse) f.getResponse(); (ForumInvitationResponse) f.getResponse();
if (r.getGroupId().equals(getGroupId()) && r.wasAccepted()) { if (r.getShareableId().equals(getGroupId()) && r.wasAccepted()) {
LOG.info("Forum invitation was accepted"); LOG.info("Forum invitation was accepted");
onForumInvitationAccepted(r.getContactId()); onForumInvitationAccepted(r.getContactId());
} }

View File

@@ -119,8 +119,6 @@ public class ForumListFragment extends BaseEventFragment implements
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
notificationManager.blockAllForumPostNotifications();
notificationManager.clearAllForumPostNotifications();
loadForums(); loadForums();
loadAvailableForums(); loadAvailableForums();
list.startPeriodicUpdate(); list.startPeriodicUpdate();
@@ -129,7 +127,6 @@ public class ForumListFragment extends BaseEventFragment implements
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
notificationManager.unblockAllForumPostNotifications();
adapter.clear(); adapter.clear();
list.showProgressBar(); list.showProgressBar();
list.stopPeriodicUpdate(); list.stopPeriodicUpdate();

View File

@@ -1,66 +0,0 @@
package org.briarproject.briar.android.fragment;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.BaseActivity;
import java.util.ArrayList;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class SFDialogFragment extends DialogFragment {
public static SFDialogFragment newInstance(ArrayList<String> apps) {
SFDialogFragment frag = new SFDialogFragment();
Bundle args = new Bundle();
args.putStringArrayList("apps", apps);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder =
new AlertDialog.Builder(
getActivity(),
R.style.BriarDialogThemeNoFilter);
builder.setTitle(R.string.screen_filter_title);
LayoutInflater li = getActivity().getLayoutInflater();
//Pass null here because it's an AlertDialog
View v =
li.inflate(R.layout.alert_dialog_checkbox, null,
false);
TextView t = (TextView) v.findViewById(R.id.alert_dialog_text);
final ArrayList<String> apps =
getArguments().getStringArrayList("apps");
t.setText(getString(R.string.screen_filter_body, TextUtils
.join("\n", apps)));
final CheckBox cb =
(CheckBox) v.findViewById(
R.id.checkBox_screen_filter_reminder);
builder.setNeutralButton(R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog,
int which) {
((BaseActivity) getActivity())
.rememberShownApps(apps, cb.isChecked());
}
});
builder.setView(v);
return builder.create();
}
}

View File

@@ -0,0 +1,46 @@
package org.briarproject.briar.android.fragment;
import android.app.Dialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v7.app.AlertDialog;
import android.text.TextUtils;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import java.util.ArrayList;
import javax.annotation.Nullable;
@NotNullByDefault
public class ScreenFilterDialogFragment extends DialogFragment {
public static ScreenFilterDialogFragment newInstance(
ArrayList<String> apps) {
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
Bundle args = new Bundle();
args.putStringArrayList("apps", apps);
frag.setArguments(args);
return frag;
}
@Override
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
R.style.BriarDialogThemeNoFilter);
builder.setTitle(R.string.screen_filter_title);
ArrayList<String> apps = getArguments().getStringArrayList("apps");
builder.setMessage(getString(R.string.screen_filter_body,
TextUtils.join("\n", apps)));
builder.setNeutralButton(R.string.continue_button,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
dialog.dismiss();
}
});
return builder.create();
}
}

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.fragment; package org.briarproject.briar.android.fragment;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -9,6 +8,8 @@ import android.view.ViewGroup;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import javax.annotation.Nullable;
public class SignOutFragment extends BaseFragment { public class SignOutFragment extends BaseFragment {
private static final String TAG = SignOutFragment.class.getName(); private static final String TAG = SignOutFragment.class.getName();

View File

@@ -33,7 +33,6 @@ import im.delight.android.identicons.IdenticonDrawable;
import static android.app.Activity.RESULT_OK; import static android.app.Activity.RESULT_OK;
import static android.view.View.GONE; import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static android.widget.Toast.LENGTH_SHORT; import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH; import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
@@ -94,7 +93,6 @@ public class IntroductionMessageFragment extends BaseFragment
View v = inflater.inflate(R.layout.introduction_message, container, View v = inflater.inflate(R.layout.introduction_message, container,
false); false);
ui = new ViewHolder(v); ui = new ViewHolder(v);
ui.text.setVisibility(GONE);
ui.message.setSendButtonEnabled(false); ui.message.setSendButtonEnabled(false);
return v; return v;
@@ -156,17 +154,11 @@ public class IntroductionMessageFragment extends BaseFragment
ui.contactName1.setText(c1.getAuthor().getName()); ui.contactName1.setText(c1.getAuthor().getName());
ui.contactName2.setText(c2.getAuthor().getName()); ui.contactName2.setText(c2.getAuthor().getName());
// set introduction text
ui.text.setText(String.format(
getString(R.string.introduction_message_text),
c1.getAuthor().getName(), c2.getAuthor().getName()));
// set button action // set button action
ui.message.setListener(IntroductionMessageFragment.this); ui.message.setListener(IntroductionMessageFragment.this);
// hide progress bar and show views // hide progress bar and show views
ui.progressBar.setVisibility(GONE); ui.progressBar.setVisibility(GONE);
ui.text.setVisibility(VISIBLE);
ui.message.setSendButtonEnabled(true); ui.message.setSendButtonEnabled(true);
ui.message.showSoftKeyboard(); ui.message.showSoftKeyboard();
} }
@@ -234,7 +226,6 @@ public class IntroductionMessageFragment extends BaseFragment
private final ProgressBar progressBar; private final ProgressBar progressBar;
private final CircleImageView avatar1, avatar2; private final CircleImageView avatar1, avatar2;
private final TextView contactName1, contactName2; private final TextView contactName1, contactName2;
private final TextView text;
private final TextInputView message; private final TextInputView message;
private ViewHolder(View v) { private ViewHolder(View v) {
@@ -243,7 +234,6 @@ public class IntroductionMessageFragment extends BaseFragment
avatar2 = (CircleImageView) v.findViewById(R.id.avatarContact2); avatar2 = (CircleImageView) v.findViewById(R.id.avatarContact2);
contactName1 = (TextView) v.findViewById(R.id.nameContact1); contactName1 = (TextView) v.findViewById(R.id.nameContact1);
contactName2 = (TextView) v.findViewById(R.id.nameContact2); contactName2 = (TextView) v.findViewById(R.id.nameContact2);
text = (TextView) v.findViewById(R.id.introductionText);
message = (TextInputView) v message = (TextInputView) v
.findViewById(R.id.introductionMessageView); .findViewById(R.id.introductionMessageView);
} }

View File

@@ -29,6 +29,7 @@ import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity; import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.blog.FeedFragment; import org.briarproject.briar.android.blog.FeedFragment;
import org.briarproject.briar.android.contact.ContactListFragment; import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.controller.handler.UiResultHandler;
import org.briarproject.briar.android.forum.ForumListFragment; import org.briarproject.briar.android.forum.ForumListFragment;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
@@ -45,6 +46,9 @@ import javax.inject.Inject;
import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE; import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
import static android.support.v4.view.GravityCompat.START; import static android.support.v4.view.GravityCompat.START;
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED; import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
public class NavDrawerActivity extends BriarActivity implements public class NavDrawerActivity extends BriarActivity implements
BaseFragmentListener, TransportStateListener, BaseFragmentListener, TransportStateListener,
@@ -128,6 +132,12 @@ public class NavDrawerActivity extends BriarActivity implements
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
updateTransports(); updateTransports();
controller.showExpiryWarning(new UiResultHandler<Boolean>(this) {
@Override
public void onResultUi(Boolean showWarning) {
if (showWarning) showExpiryWarning();
}
});
} }
private void exitIfStartupFailed(Intent intent) { private void exitIfStartupFailed(Intent intent) {
@@ -254,6 +264,34 @@ public class NavDrawerActivity extends BriarActivity implements
// Do nothing for now // Do nothing for now
} }
@SuppressWarnings("ConstantConditions")
private void showExpiryWarning() {
int daysUntilExpiry = getDaysUntilExpiry();
if (daysUntilExpiry < 0) signOut();
// show expiry warning text
final ViewGroup
expiryWarning = (ViewGroup) findViewById(R.id.expiryWarning);
TextView expiryWarningText =
(TextView) expiryWarning.findViewById(R.id.expiryWarningText);
expiryWarningText.setText(getResources()
.getQuantityString(R.plurals.expiry_warning, daysUntilExpiry,
daysUntilExpiry));
// make close button functional
ImageView expiryWarningClose =
(ImageView) expiryWarning.findViewById(R.id.expiryWarningClose);
expiryWarningClose.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
controller.expiryWarningDismissed();
expiryWarning.setVisibility(GONE);
}
});
expiryWarning.setVisibility(VISIBLE);
}
private void initializeTransports(final LayoutInflater inflater) { private void initializeTransports(final LayoutInflater inflater) {
transports = new ArrayList<>(3); transports = new ArrayList<>(3);

View File

@@ -3,10 +3,15 @@ package org.briarproject.briar.android.navdrawer;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.controller.handler.ResultHandler;
@NotNullByDefault @NotNullByDefault
public interface NavDrawerController extends ActivityLifecycleController { public interface NavDrawerController extends ActivityLifecycleController {
boolean isTransportRunning(TransportId transportId); boolean isTransportRunning(TransportId transportId);
void showExpiryWarning(final ResultHandler<Boolean> handler);
void expiryWarningDismissed();
} }

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.android.navdrawer;
import android.app.Activity; import android.app.Activity;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event; import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener; import org.briarproject.bramble.api.event.EventListener;
@@ -14,7 +15,10 @@ import org.briarproject.bramble.api.plugin.PluginManager;
import org.briarproject.bramble.api.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent; import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent; import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.briar.android.controller.DbControllerImpl; import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ResultHandler;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import java.util.logging.Logger; import java.util.logging.Logger;
@@ -22,6 +26,9 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.INFO; import static java.util.logging.Level.INFO;
import static java.util.logging.Level.WARNING;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -30,18 +37,21 @@ public class NavDrawerControllerImpl extends DbControllerImpl
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(NavDrawerControllerImpl.class.getName()); Logger.getLogger(NavDrawerControllerImpl.class.getName());
private static final String EXPIRY_DATE_WARNING = "expiryDateWarning";
private final PluginManager pluginManager; private final PluginManager pluginManager;
private final SettingsManager settingsManager;
private final EventBus eventBus; private final EventBus eventBus;
private volatile TransportStateListener listener; private volatile TransportStateListener listener;
@Inject @Inject
NavDrawerControllerImpl(@DatabaseExecutor Executor dbExecutor, NavDrawerControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, LifecycleManager lifecycleManager, PluginManager pluginManager,
PluginManager pluginManager, EventBus eventBus) { SettingsManager settingsManager, EventBus eventBus) {
super(dbExecutor, lifecycleManager); super(dbExecutor, lifecycleManager);
this.pluginManager = pluginManager; this.pluginManager = pluginManager;
this.settingsManager = settingsManager;
this.eventBus = eventBus; this.eventBus = eventBus;
} }
@@ -92,6 +102,63 @@ public class NavDrawerControllerImpl extends DbControllerImpl
}); });
} }
@Override
public void showExpiryWarning(final ResultHandler<Boolean> handler) {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Settings settings =
settingsManager.getSettings(SETTINGS_NAMESPACE);
int warningInt = settings.getInt(EXPIRY_DATE_WARNING, 0);
if (warningInt == 0) {
// we have not warned before
handler.onResult(true);
} else {
long warningLong = warningInt * 1000L;
long now = System.currentTimeMillis();
long daysSinceLastWarning =
(now - warningLong) / 1000 / 60 / 60 / 24;
long daysBeforeExpiry =
(EXPIRY_DATE - now) / 1000 / 60 / 60 / 24;
if (daysSinceLastWarning >= 30) {
handler.onResult(true);
return;
}
if (daysBeforeExpiry <= 3 && daysSinceLastWarning > 0) {
handler.onResult(true);
return;
}
handler.onResult(false);
}
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@Override
public void expiryWarningDismissed() {
runOnDbThread(new Runnable() {
@Override
public void run() {
try {
Settings settings = new Settings();
int date = (int) (System.currentTimeMillis() / 1000L);
settings.putInt(EXPIRY_DATE_WARNING, date);
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
@Override @Override
public boolean isTransportRunning(TransportId transportId) { public boolean isTransportRunning(TransportId transportId) {
Plugin plugin = pluginManager.getPlugin(transportId); Plugin plugin = pluginManager.getPlugin(transportId);

View File

@@ -17,6 +17,7 @@ import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.android.privategroup.conversation.GroupController.GroupListener; import org.briarproject.briar.android.privategroup.conversation.GroupController.GroupListener;
import org.briarproject.briar.android.threaded.ThreadListControllerImpl; import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.MessageTracker.GroupCount; import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.privategroup.GroupMember; import org.briarproject.briar.api.privategroup.GroupMember;
import org.briarproject.briar.api.privategroup.GroupMessage; import org.briarproject.briar.api.privategroup.GroupMessage;
@@ -60,9 +61,10 @@ class GroupControllerImpl extends
@CryptoExecutor Executor cryptoExecutor, @CryptoExecutor Executor cryptoExecutor,
PrivateGroupManager privateGroupManager, PrivateGroupManager privateGroupManager,
GroupMessageFactory groupMessageFactory, EventBus eventBus, GroupMessageFactory groupMessageFactory, EventBus eventBus,
Clock clock, AndroidNotificationManager notificationManager) { MessageTracker messageTracker, Clock clock,
AndroidNotificationManager notificationManager) {
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor, super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
eventBus, clock, notificationManager); eventBus, clock, notificationManager, messageTracker);
this.privateGroupManager = privateGroupManager; this.privateGroupManager = privateGroupManager;
this.groupMessageFactory = groupMessageFactory; this.groupMessageFactory = groupMessageFactory;
} }
@@ -106,7 +108,7 @@ class GroupControllerImpl extends
(GroupInvitationResponseReceivedEvent) e; (GroupInvitationResponseReceivedEvent) e;
final GroupInvitationResponse r = final GroupInvitationResponse r =
(GroupInvitationResponse) g.getResponse(); (GroupInvitationResponse) g.getResponse();
if (getGroupId().equals(r.getGroupId()) && r.wasAccepted()) { if (getGroupId().equals(r.getShareableId()) && r.wasAccepted()) {
listener.runOnUiThreadUnlessDestroyed(new Runnable() { listener.runOnUiThreadUnlessDestroyed(new Runnable() {
@Override @Override
public void run() { public void run() {

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.privategroup.conversation; package org.briarproject.briar.android.privategroup.conversation;
import android.support.annotation.LayoutRes; import android.support.annotation.LayoutRes;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
@@ -12,6 +11,7 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.threaded.ThreadItem; import org.briarproject.briar.android.threaded.ThreadItem;
import org.briarproject.briar.api.privategroup.GroupMessageHeader; import org.briarproject.briar.api.privategroup.GroupMessageHeader;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
@UiThread @UiThread

View File

@@ -1,25 +1,15 @@
package org.briarproject.briar.android.privategroup.conversation; package org.briarproject.briar.android.privategroup.conversation;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.view.View; import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder; import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener; import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES; import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -27,16 +17,10 @@ class JoinMessageItemViewHolder
extends BaseThreadItemViewHolder<GroupMessageItem> { extends BaseThreadItemViewHolder<GroupMessageItem> {
private final boolean isCreator; private final boolean isCreator;
private final ImageView icon;
private final TextView info;
private final Button options;
JoinMessageItemViewHolder(View v, boolean isCreator) { JoinMessageItemViewHolder(View v, boolean isCreator) {
super(v); super(v);
this.isCreator = isCreator; this.isCreator = isCreator;
icon = (ImageView) v.findViewById(R.id.icon);
info = (TextView) v.findViewById(R.id.info);
options = (Button) v.findViewById(R.id.optionsButton);
} }
@Override @Override
@@ -56,9 +40,6 @@ class JoinMessageItemViewHolder
getContext().getString(R.string.groups_member_joined, getContext().getString(R.string.groups_member_joined,
item.getAuthor().getName())); item.getAuthor().getName()));
} }
icon.setVisibility(View.GONE);
info.setVisibility(View.GONE);
options.setVisibility(View.GONE);
} }
private void bind(final JoinMessageItem item) { private void bind(final JoinMessageItem item) {
@@ -75,32 +56,6 @@ class JoinMessageItemViewHolder
item.getAuthor().getName())); item.getAuthor().getName()));
} }
} }
if (item.getStatus() == OURSELVES || item.getStatus() == UNKNOWN) {
icon.setVisibility(View.GONE);
info.setVisibility(View.GONE);
options.setVisibility(View.GONE);
} else {
icon.setVisibility(View.VISIBLE);
icon.setImageResource(getVisibilityIcon(item.getVisibility()));
info.setVisibility(View.VISIBLE);
info.setText(getVisibilityString(getContext(), item.getVisibility(),
item.getAuthor().getName()));
if (item.getVisibility() == INVISIBLE) {
options.setVisibility(View.VISIBLE);
options.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent i =
new Intent(ctx, RevealContactsActivity.class);
i.putExtra(GROUP_ID, item.getGroupId().getBytes());
ctx.startActivity(i);
}
});
} else {
options.setVisibility(View.GONE);
}
}
} }
} }

View File

@@ -1,58 +0,0 @@
package org.briarproject.briar.android.privategroup.creation;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.briar.android.contactselection.ContactSelectorActivity;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
import java.util.Collection;
import javax.inject.Inject;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public abstract class BaseGroupInviteActivity
extends ContactSelectorActivity implements MessageFragmentListener {
@Inject
CreateGroupController controller;
@Override
public void contactsSelected(Collection<ContactId> contacts) {
super.contactsSelected(contacts);
showNextFragment(new CreateGroupMessageFragment());
}
@Override
public boolean onButtonClick(String message) {
if (groupId == null)
throw new IllegalStateException("GroupId was not initialized");
controller.sendInvitation(groupId, contacts, message,
new UiResultExceptionHandler<Void, DbException>(this) {
@Override
public void onResultUi(Void result) {
setResult(RESULT_OK);
supportFinishAfterTransition();
}
@Override
public void onExceptionUi(DbException exception) {
setResult(RESULT_CANCELED);
handleDbException(exception);
}
});
return true;
}
@Override
public int getMaximumMessageLength() {
return MAX_GROUP_INVITATION_MSG_LENGTH;
}
}

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.privategroup.creation;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
@@ -10,14 +9,20 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity; import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
import javax.annotation.Nullable;
import javax.inject.Inject;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class CreateGroupActivity extends BaseGroupInviteActivity implements public class CreateGroupActivity extends BriarActivity
CreateGroupListener, MessageFragmentListener { implements CreateGroupListener {
@Inject
CreateGroupController controller;
@Override @Override
public void injectActivity(ActivityComponent component) { public void injectActivity(ActivityComponent component) {
@@ -28,32 +33,20 @@ public class CreateGroupActivity extends BaseGroupInviteActivity implements
public void onCreate(@Nullable Bundle bundle) { public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
setContentView(R.layout.activity_fragment_container);
if (bundle == null) { if (bundle == null) {
showInitialFragment(new CreateGroupFragment()); showInitialFragment(new CreateGroupFragment());
} }
} }
@Override
public void onBackPressed() {
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
// At this point, the group had been created already,
// so don't allow to create it again.
openNewGroup();
overridePendingTransition(R.anim.screen_old_in,
R.anim.screen_new_out);
} else {
super.onBackPressed();
}
}
@Override @Override
public void onGroupNameChosen(String name) { public void onGroupNameChosen(String name) {
controller.createGroup(name, controller.createGroup(name,
new UiResultExceptionHandler<GroupId, DbException>(this) { new UiResultExceptionHandler<GroupId, DbException>(this) {
@Override @Override
public void onResultUi(GroupId g) { public void onResultUi(GroupId g) {
groupId = g; openNewGroup(g);
switchToContactSelectorFragment(g);
} }
@Override @Override
@@ -63,16 +56,10 @@ public class CreateGroupActivity extends BaseGroupInviteActivity implements
}); });
} }
private void switchToContactSelectorFragment(GroupId g) { private void openNewGroup(GroupId g) {
showNextFragment(GroupInviteFragment.newInstance(g));
}
private void openNewGroup() {
Intent i = new Intent(this, GroupActivity.class); Intent i = new Intent(this, GroupActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes()); i.putExtra(GROUP_ID, g.getBytes());
startActivity(i); startActivity(i);
// finish this activity, so we can't come back to it
finish(); finish();
} }
} }

View File

@@ -2,18 +2,27 @@ package org.briarproject.briar.android.privategroup.creation;
import android.content.Context; import android.content.Context;
import android.os.Bundle; import android.os.Bundle;
import android.support.design.widget.TextInputLayout;
import android.text.Editable; import android.text.Editable;
import android.text.TextWatcher; import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ProgressBar;
import android.widget.TextView;
import android.widget.TextView.OnEditorActionListener;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import static android.view.View.GONE;
import static android.view.View.VISIBLE;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH; import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_NAME_LENGTH;
public class CreateGroupFragment extends BaseFragment { public class CreateGroupFragment extends BaseFragment {
@@ -21,8 +30,10 @@ public class CreateGroupFragment extends BaseFragment {
public final static String TAG = CreateGroupFragment.class.getName(); public final static String TAG = CreateGroupFragment.class.getName();
private CreateGroupListener listener; private CreateGroupListener listener;
private EditText name; private EditText nameEntry;
private Button button; private Button createGroupButton;
private TextInputLayout nameLayout;
private ProgressBar progress;
@Override @Override
public void onAttach(Context context) { public void onAttach(Context context) {
@@ -34,42 +45,54 @@ public class CreateGroupFragment extends BaseFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) { Bundle savedInstanceState) {
// inflate view
View v = inflater.inflate(R.layout.fragment_create_group, container, View v = inflater.inflate(R.layout.fragment_create_group, container,
false); false);
name = (EditText) v.findViewById(R.id.name); nameEntry = (EditText) v.findViewById(R.id.name);
name.addTextChangedListener(new TextWatcher() { nameEntry.addTextChangedListener(new TextWatcher() {
@Override @Override
public void beforeTextChanged(CharSequence s, int start, int count, public void beforeTextChanged(CharSequence s, int start, int count,
int after) { int after) {
} }
@Override @Override
public void onTextChanged(CharSequence s, int start, int before, public void onTextChanged(CharSequence s, int start,
int count) { int lengthBefore, int lengthAfter) {
validateName(); enableOrDisableCreateButton();
} }
@Override @Override
public void afterTextChanged(Editable s) { public void afterTextChanged(Editable s) {
} }
}); });
button = (Button) v.findViewById(R.id.button); nameEntry.setOnEditorActionListener(new OnEditorActionListener() {
button.setOnClickListener(new View.OnClickListener() {
@Override @Override
public void onClick(View v) { public boolean onEditorAction(TextView v, int actionId,
listener.hideSoftKeyboard(name); KeyEvent e) {
listener.onGroupNameChosen(name.getText().toString()); createGroup();
return true;
} }
}); });
nameLayout = (TextInputLayout) v.findViewById(R.id.nameLayout);
createGroupButton = (Button) v.findViewById(R.id.button);
createGroupButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
createGroup();
}
});
progress = (ProgressBar) v.findViewById(R.id.progressBar);
return v; return v;
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
listener.showSoftKeyboard(name); listener.showSoftKeyboard(nameEntry);
} }
@Override @Override
@@ -82,12 +105,27 @@ public class CreateGroupFragment extends BaseFragment {
return TAG; return TAG;
} }
private void validateName() { private void enableOrDisableCreateButton() {
String name = this.name.getText().toString(); if (createGroupButton == null) return; // Not created yet
if (name.length() < 1 || name.length() > MAX_GROUP_NAME_LENGTH) createGroupButton.setEnabled(validateName());
button.setEnabled(false);
else if(!button.isEnabled())
button.setEnabled(true);
} }
private boolean validateName() {
String name = nameEntry.getText().toString();
int length = StringUtils.toUtf8(name).length;
if (length > MAX_GROUP_NAME_LENGTH) {
nameLayout.setError(getString(R.string.name_too_long));
return false;
}
nameLayout.setError(null);
return length > 0;
}
private void createGroup() {
if (!validateName()) return;
listener.hideSoftKeyboard(nameEntry);
createGroupButton.setVisibility(GONE);
progress.setVisibility(VISIBLE);
listener.onGroupNameChosen(nameEntry.getText().toString());
}
} }

View File

@@ -6,7 +6,7 @@ import dagger.Module;
import dagger.Provides; import dagger.Provides;
@Module @Module
public class GroupCreateModule { public class CreateGroupModule {
@ActivityScope @ActivityScope
@Provides @Provides

View File

@@ -3,25 +3,43 @@ package org.briarproject.briar.android.privategroup.creation;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.android.activity.ActivityComponent; import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.contactselection.ContactSelectorActivity;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener; import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
public class GroupInviteActivity extends BaseGroupInviteActivity import java.util.Collection;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class GroupInviteActivity extends ContactSelectorActivity
implements MessageFragmentListener { implements MessageFragmentListener {
@Inject
CreateGroupController controller;
@Override @Override
public void injectActivity(ActivityComponent component) { public void injectActivity(ActivityComponent component) {
component.inject(this); component.inject(this);
} }
@Override @Override
public void onCreate(Bundle bundle) { public void onCreate(@Nullable Bundle bundle) {
super.onCreate(bundle); super.onCreate(bundle);
Intent i = getIntent(); Intent i = getIntent();
byte[] g = i.getByteArrayExtra(GROUP_ID); byte[] g = i.getByteArrayExtra(GROUP_ID);
if (g == null) throw new IllegalStateException("No GroupId in intent."); if (g == null) throw new IllegalStateException("No GroupId in intent");
groupId = new GroupId(g); groupId = new GroupId(g);
if (bundle == null) { if (bundle == null) {
@@ -29,4 +47,36 @@ public class GroupInviteActivity extends BaseGroupInviteActivity
} }
} }
@Override
public void contactsSelected(Collection<ContactId> contacts) {
super.contactsSelected(contacts);
showNextFragment(new CreateGroupMessageFragment());
}
@Override
public boolean onButtonClick(String message) {
if (groupId == null)
throw new IllegalStateException("GroupId was not initialized");
controller.sendInvitation(groupId, contacts, message,
new UiResultExceptionHandler<Void, DbException>(this) {
@Override
public void onResultUi(Void result) {
setResult(RESULT_OK);
supportFinishAfterTransition();
}
@Override
public void onExceptionUi(DbException exception) {
setResult(RESULT_CANCELED);
handleDbException(exception);
}
});
return true;
}
@Override
public int getMaximumMessageLength() {
return MAX_GROUP_INVITATION_MSG_LENGTH;
}
} }

View File

@@ -79,15 +79,12 @@ class GroupListControllerImpl extends DbControllerImpl
throw new IllegalStateException( throw new IllegalStateException(
"GroupListListener needs to be attached"); "GroupListListener needs to be attached");
eventBus.addListener(this); eventBus.addListener(this);
notificationManager.blockAllGroupMessageNotifications();
notificationManager.clearAllGroupMessageNotifications();
} }
@Override @Override
@CallSuper @CallSuper
public void onStop() { public void onStop() {
eventBus.removeListener(this); eventBus.removeListener(this);
notificationManager.unblockAllGroupMessageNotifications();
} }
@Override @Override

View File

@@ -36,7 +36,6 @@ class MemberListAdapter extends
@Override @Override
public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) { public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
if (m1.isOnline() != m2.isOnline()) return false; if (m1.isOnline() != m2.isOnline()) return false;
if (m1.getVisibility() != m2.getVisibility()) return false;
if (m1.getContactId() != m2.getContactId()) return false; if (m1.getContactId() != m2.getContactId()) return false;
if (m1.getStatus() != m2.getStatus()) return false; if (m1.getStatus() != m2.getStatus()) return false;
return true; return true;

View File

@@ -1,14 +1,12 @@
package org.briarproject.briar.android.privategroup.memberlist; package org.briarproject.briar.android.privategroup.memberlist;
import android.support.annotation.Nullable;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status; import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.privategroup.GroupMember; import org.briarproject.briar.api.privategroup.GroupMember;
import org.briarproject.briar.api.privategroup.Visibility;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe; import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe @NotThreadSafe
@@ -40,10 +38,6 @@ class MemberListItem {
return groupMember.getContactId(); return groupMember.getContactId();
} }
Visibility getVisibility() {
return groupMember.getVisibility();
}
boolean isOnline() { boolean isOnline() {
return online; return online;
} }

View File

@@ -11,9 +11,6 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.view.AuthorView; import org.briarproject.briar.android.view.AuthorView;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES; import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
@@ -22,16 +19,12 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
private final AuthorView author; private final AuthorView author;
private final ImageView bulb; private final ImageView bulb;
private final TextView creator; private final TextView creator;
private final ImageView icon;
private final TextView info;
MemberListItemHolder(View v) { MemberListItemHolder(View v) {
super(v); super(v);
author = (AuthorView) v.findViewById(R.id.authorView); author = (AuthorView) v.findViewById(R.id.authorView);
bulb = (ImageView) v.findViewById(R.id.bulbView); bulb = (ImageView) v.findViewById(R.id.bulbView);
creator = (TextView) v.findViewById(R.id.creatorView); creator = (TextView) v.findViewById(R.id.creatorView);
icon = (ImageView) v.findViewById(R.id.icon);
info = (TextView) v.findViewById(R.id.info);
} }
protected void bind(MemberListItem item) { protected void bind(MemberListItem item) {
@@ -64,19 +57,6 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
} else { } else {
creator.setVisibility(View.GONE); creator.setVisibility(View.GONE);
} }
// visibility information
if (item.getStatus() == OURSELVES || item.getStatus() == UNKNOWN) {
icon.setVisibility(View.GONE);
info.setVisibility(View.GONE);
} else {
icon.setVisibility(View.VISIBLE);
icon.setImageResource(getVisibilityIcon(item.getVisibility()));
info.setVisibility(View.VISIBLE);
info.setText(
getVisibilityString(info.getContext(), item.getVisibility(),
item.getMember().getName()));
}
} }
} }

View File

@@ -146,7 +146,7 @@ public class BriarReportPrimer implements ReportPrimer {
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI); NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
boolean wifiAvailable = wifi != null && wifi.isAvailable(); boolean wifiAvailable = wifi != null && wifi.isAvailable();
// Is wifi enabled? // Is wifi enabled?
o = ctx.getSystemService(WIFI_SERVICE); o = ctx.getApplicationContext().getSystemService(WIFI_SERVICE);
WifiManager wm = (WifiManager) o; WifiManager wm = (WifiManager) o;
boolean wifiEnabled = wm != null && boolean wifiEnabled = wm != null &&
wm.getWifiState() == WIFI_STATE_ENABLED; wm.getWifiState() == WIFI_STATE_ENABLED;

View File

@@ -6,6 +6,7 @@ import android.content.Intent;
import android.media.Ringtone; import android.media.Ringtone;
import android.media.RingtoneManager; import android.media.RingtoneManager;
import android.net.Uri; import android.net.Uri;
import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.support.v7.preference.CheckBoxPreference; import android.support.v7.preference.CheckBoxPreference;
import android.support.v7.preference.ListPreference; import android.support.v7.preference.ListPreference;
@@ -54,6 +55,7 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGT
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG; import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM; import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP; import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_LOCK_SCREEN;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE; import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME; import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI; import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
@@ -81,6 +83,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
private CheckBoxPreference notifyForumPosts; private CheckBoxPreference notifyForumPosts;
private CheckBoxPreference notifyBlogPosts; private CheckBoxPreference notifyBlogPosts;
private CheckBoxPreference notifyVibration; private CheckBoxPreference notifyVibration;
private CheckBoxPreference notifyLockscreen;
private Preference notifySound; private Preference notifySound;
// Fields that are accessed from background threads must be volatile // Fields that are accessed from background threads must be volatile
@@ -114,6 +118,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
"pref_key_notify_blog_posts"); "pref_key_notify_blog_posts");
notifyVibration = (CheckBoxPreference) findPreference( notifyVibration = (CheckBoxPreference) findPreference(
"pref_key_notify_vibration"); "pref_key_notify_vibration");
notifyLockscreen = (CheckBoxPreference) findPreference(
"pref_key_notify_lock_screen");
notifySound = findPreference("pref_key_notify_sound"); notifySound = findPreference("pref_key_notify_sound");
enableBluetooth.setOnPreferenceChangeListener(this); enableBluetooth.setOnPreferenceChangeListener(this);
@@ -123,7 +129,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyForumPosts.setOnPreferenceChangeListener(this); notifyForumPosts.setOnPreferenceChangeListener(this);
notifyBlogPosts.setOnPreferenceChangeListener(this); notifyBlogPosts.setOnPreferenceChangeListener(this);
notifyVibration.setOnPreferenceChangeListener(this); notifyVibration.setOnPreferenceChangeListener(this);
if (Build.VERSION.SDK_INT >= 21) {
notifyLockscreen.setVisible(true);
notifyLockscreen.setOnPreferenceChangeListener(this);
}
notifySound.setOnPreferenceClickListener( notifySound.setOnPreferenceClickListener(
new Preference.OnPreferenceClickListener() { new Preference.OnPreferenceClickListener() {
@Override @Override
@@ -234,6 +243,9 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyVibration.setChecked(settings.getBoolean( notifyVibration.setChecked(settings.getBoolean(
PREF_NOTIFY_VIBRATION, true)); PREF_NOTIFY_VIBRATION, true));
notifyLockscreen.setChecked(settings.getBoolean(
PREF_NOTIFY_LOCK_SCREEN, false));
String text; String text;
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) { if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
String ringtoneName = String ringtoneName =
@@ -290,6 +302,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
Settings s = new Settings(); Settings s = new Settings();
s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) o); s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) o);
storeSettings(s); storeSettings(s);
} else if (preference == notifyLockscreen) {
Settings s = new Settings();
s.putBoolean(PREF_NOTIFY_LOCK_SCREEN, (Boolean) o);
storeSettings(s);
} }
return true; return true;
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.sharing;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.view.MenuItem; import android.view.MenuItem;
@@ -25,6 +24,7 @@ import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;

View File

@@ -22,6 +22,7 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL; import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
import static org.briarproject.briar.android.TestingConstants.TESTING; import static org.briarproject.briar.android.TestingConstants.TESTING;
@@ -30,9 +31,6 @@ public class SplashScreenActivity extends BaseActivity {
private static final Logger LOG = private static final Logger LOG =
Logger.getLogger(SplashScreenActivity.class.getName()); Logger.getLogger(SplashScreenActivity.class.getName());
// This build expires on 1 May 2017
private static final long EXPIRY_DATE = 1493593200 * 1000L;
@Inject @Inject
protected ConfigController configController; protected ConfigController configController;
@Inject @Inject
@@ -83,10 +81,6 @@ public class SplashScreenActivity extends BaseActivity {
} }
} }
@Override
protected void showNewScreenFilterWarning() {
}
private void enableStrictMode() { private void enableStrictMode() {
if (TESTING) { if (TESTING) {
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder(); ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();

View File

@@ -1,6 +1,6 @@
package org.briarproject.briar.android.threaded; package org.briarproject.briar.android.threaded;
import android.support.annotation.Nullable; import android.os.Handler;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView;
@@ -14,6 +14,8 @@ import org.briarproject.briar.android.util.VersionedAdapter;
import java.util.Collection; import java.util.Collection;
import javax.annotation.Nullable;
import static android.support.v7.widget.RecyclerView.NO_POSITION; import static android.support.v7.widget.RecyclerView.NO_POSITION;
@UiThread @UiThread
@@ -26,6 +28,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
protected final NestedTreeList<I> items = new NestedTreeList<>(); protected final NestedTreeList<I> items = new NestedTreeList<>();
private final ThreadItemListener<I> listener; private final ThreadItemListener<I> listener;
private final LinearLayoutManager layoutManager; private final LinearLayoutManager layoutManager;
private final Handler handler = new Handler();
private volatile int revision = 0; private volatile int revision = 0;
@@ -64,6 +67,17 @@ public class ThreadItemAdapter<I extends ThreadItem>
revision++; revision++;
} }
void setItemWithIdVisible(MessageId messageId) {
int pos = 0;
for (I item : items) {
if (item.getId().equals(messageId)) {
layoutManager.scrollToPosition(pos);
break;
}
pos++;
}
}
public void setItems(Collection<I> items) { public void setItems(Collection<I> items) {
this.items.clear(); this.items.clear();
this.items.addAll(items); this.items.addAll(items);
@@ -144,7 +158,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
/** /**
* Returns the position of the first unread item below the current viewport * Returns the position of the first unread item below the current viewport
*/ */
public int getVisibleUnreadPosBottom() { int getVisibleUnreadPosBottom() {
final int positionBottom = layoutManager.findLastVisibleItemPosition(); final int positionBottom = layoutManager.findLastVisibleItemPosition();
if (positionBottom == NO_POSITION) return NO_POSITION; if (positionBottom == NO_POSITION) return NO_POSITION;
for (int i = positionBottom + 1; i < items.size(); i++) { for (int i = positionBottom + 1; i < items.size(); i++) {
@@ -156,7 +170,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
/** /**
* Returns the position of the first unread item above the current viewport * Returns the position of the first unread item above the current viewport
*/ */
public int getVisibleUnreadPosTop() { int getVisibleUnreadPosTop() {
final int positionTop = layoutManager.findFirstVisibleItemPosition(); final int positionTop = layoutManager.findFirstVisibleItemPosition();
int position = NO_POSITION; int position = NO_POSITION;
for (int i = 0; i < items.size(); i++) { for (int i = 0; i < items.size(); i++) {

View File

@@ -0,0 +1,15 @@
package org.briarproject.briar.android.threaded;
import org.briarproject.bramble.api.sync.MessageId;
import java.util.List;
import javax.annotation.Nullable;
public interface ThreadItemList<I extends ThreadItem> extends List<I> {
@Nullable
MessageId getFirstVisibleItemId();
void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId);
}

View File

@@ -0,0 +1,22 @@
package org.briarproject.briar.android.threaded;
import org.briarproject.bramble.api.sync.MessageId;
import java.util.ArrayList;
import javax.annotation.Nullable;
public class ThreadItemListImpl<I extends ThreadItem> extends ArrayList<I>
implements ThreadItemList<I> {
private MessageId bottomVisibleItemId;
@Override
public MessageId getFirstVisibleItemId() {
return bottomVisibleItemId;
}
public void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId) {
this.bottomVisibleItemId = bottomVisibleItemId;
}
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.threaded;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.support.annotation.CallSuper; import android.support.annotation.CallSuper;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes; import android.support.annotation.StringRes;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar; import android.support.design.widget.Snackbar;
@@ -26,6 +25,7 @@ import org.briarproject.briar.android.controller.SharingController;
import org.briarproject.briar.android.controller.SharingController.SharingListener; import org.briarproject.briar.android.controller.SharingController.SharingListener;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener; import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListDataSource;
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener; import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.android.view.TextInputView; import org.briarproject.briar.android.view.TextInputView;
@@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
import java.util.Collection; import java.util.Collection;
import java.util.logging.Logger; import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import static android.support.design.widget.Snackbar.make; import static android.support.design.widget.Snackbar.make;
@@ -51,7 +52,7 @@ import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCo
public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader> public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader>
extends BriarActivity extends BriarActivity
implements ThreadListListener<H>, TextInputListener, SharingListener, implements ThreadListListener<H>, TextInputListener, SharingListener,
ThreadItemListener<I> { ThreadItemListener<I>, ThreadListDataSource {
protected static final String KEY_REPLY_ID = "replyId"; protected static final String KEY_REPLY_ID = "replyId";
@@ -68,6 +69,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
private MessageId replyId; private MessageId replyId;
protected abstract ThreadListController<G, I, H> getController(); protected abstract ThreadListController<G, I, H> getController();
@Inject @Inject
protected SharingController sharingController; protected SharingController sharingController;
@@ -104,6 +106,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
updateUnreadCount(); updateUnreadCount();
} }
} }
@Override @Override
public void onScrollStateChanged(RecyclerView recyclerView, public void onScrollStateChanged(RecyclerView recyclerView,
int newState) { int newState) {
@@ -139,11 +142,22 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
if (replyIdBytes != null) replyId = new MessageId(replyIdBytes); if (replyIdBytes != null) replyId = new MessageId(replyIdBytes);
} }
loadItems();
sharingController.setSharingListener(this); sharingController.setSharingListener(this);
loadSharingContacts(); loadSharingContacts();
} }
@Override
@Nullable
public MessageId getFirstVisibleMessageId() {
if (layoutManager != null && adapter != null) {
int position =
layoutManager.findFirstVisibleItemPosition();
I i = adapter.getItemAt(position);
return i == null ? null : i.getId();
}
return null;
}
protected abstract A createAdapter(LinearLayoutManager layoutManager); protected abstract A createAdapter(LinearLayoutManager layoutManager);
protected void loadNamedGroup() { protected void loadNamedGroup() {
@@ -167,16 +181,16 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
protected void loadItems() { protected void loadItems() {
final int revision = adapter.getRevision(); final int revision = adapter.getRevision();
getController().loadItems( getController().loadItems(
new UiResultExceptionHandler<Collection<I>, DbException>(this) { new UiResultExceptionHandler<ThreadItemList<I>, DbException>(
this) {
@Override @Override
public void onResultUi(Collection<I> items) { public void onResultUi(ThreadItemList<I> items) {
if (revision == adapter.getRevision()) { if (revision == adapter.getRevision()) {
adapter.incrementRevision(); adapter.incrementRevision();
if (items.isEmpty()) { if (items.isEmpty()) {
list.showData(); list.showData();
} else { } else {
adapter.setItems(items); initList(items);
list.showData();
updateTextInput(replyId); updateTextInput(replyId);
} }
} else { } else {
@@ -192,6 +206,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
}); });
} }
private void initList(final ThreadItemList<I> items) {
adapter.setItems(items);
MessageId messageId = items.getFirstVisibleItemId();
if (messageId != null)
adapter.setItemWithIdVisible(messageId);
updateUnreadCount();
list.showData();
}
protected void loadSharingContacts() { protected void loadSharingContacts() {
getController().loadSharingContacts( getController().loadSharingContacts(
new UiResultExceptionHandler<Collection<ContactId>, DbException>( new UiResultExceptionHandler<Collection<ContactId>, DbException>(
@@ -215,6 +238,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
sharingController.onStart(); sharingController.onStart();
loadItems();
list.startPeriodicUpdate(); list.startPeriodicUpdate();
} }

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.DestroyableContext; import org.briarproject.briar.android.DestroyableContext;
import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.controller.handler.ExceptionHandler; import org.briarproject.briar.android.controller.handler.ExceptionHandler;
@@ -30,7 +31,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
void loadItem(H header, ResultExceptionHandler<I, DbException> handler); void loadItem(H header, ResultExceptionHandler<I, DbException> handler);
void loadItems(ResultExceptionHandler<Collection<I>, DbException> handler); void loadItems(ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
void markItemRead(I item); void markItemRead(I item);
@@ -41,7 +42,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
void deleteNamedGroup(ExceptionHandler<DbException> handler); void deleteNamedGroup(ExceptionHandler<DbException> handler);
interface ThreadListListener<H> extends DestroyableContext { interface ThreadListListener<H> extends ThreadListDataSource {
@UiThread @UiThread
void onHeaderReceived(H header); void onHeaderReceived(H header);
@@ -52,4 +53,10 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
void onInvitationAccepted(ContactId c); void onInvitationAccepted(ContactId c);
} }
interface ThreadListDataSource extends DestroyableContext {
@UiThread @Nullable
MessageId getFirstVisibleMessageId();
}
} }

View File

@@ -22,14 +22,13 @@ import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener; import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.client.NamedGroup; import org.briarproject.briar.api.client.NamedGroup;
import org.briarproject.briar.api.client.PostHeader; import org.briarproject.briar.api.client.PostHeader;
import org.briarproject.briar.api.client.ThreadedMessage; import org.briarproject.briar.api.client.ThreadedMessage;
import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -55,18 +54,21 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
protected final AndroidNotificationManager notificationManager; protected final AndroidNotificationManager notificationManager;
protected final Executor cryptoExecutor; protected final Executor cryptoExecutor;
protected final Clock clock; protected final Clock clock;
private final MessageTracker messageTracker;
protected volatile L listener; protected volatile L listener;
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor, protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, IdentityManager identityManager, LifecycleManager lifecycleManager, IdentityManager identityManager,
@CryptoExecutor Executor cryptoExecutor, EventBus eventBus, @CryptoExecutor Executor cryptoExecutor, EventBus eventBus,
Clock clock, AndroidNotificationManager notificationManager) { Clock clock, AndroidNotificationManager notificationManager,
MessageTracker messageTracker) {
super(dbExecutor, lifecycleManager); super(dbExecutor, lifecycleManager);
this.identityManager = identityManager; this.identityManager = identityManager;
this.cryptoExecutor = cryptoExecutor; this.cryptoExecutor = cryptoExecutor;
this.notificationManager = notificationManager; this.notificationManager = notificationManager;
this.clock = clock; this.clock = clock;
this.eventBus = eventBus; this.eventBus = eventBus;
this.messageTracker = messageTracker;
} }
@Override @Override
@@ -97,6 +99,21 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
@Override @Override
public void onActivityDestroy() { public void onActivityDestroy() {
final MessageId messageId = listener.getFirstVisibleMessageId();
if (messageId != null) {
dbExecutor.execute(new Runnable() {
@Override
public void run() {
try {
messageTracker
.storeMessageId(groupId, messageId);
} catch (DbException e) {
if (LOG.isLoggable(WARNING))
LOG.log(WARNING, e.toString(), e);
}
}
});
}
} }
@CallSuper @CallSuper
@@ -144,7 +161,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
@Override @Override
public void loadItems( public void loadItems(
final ResultExceptionHandler<Collection<I>, DbException> handler) { final ResultExceptionHandler<ThreadItemList<I>, DbException> handler) {
checkGroupId(); checkGroupId();
runOnDbThread(new Runnable() { runOnDbThread(new Runnable() {
@Override @Override
@@ -293,11 +310,16 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
@DatabaseExecutor @DatabaseExecutor
protected abstract void deleteNamedGroup(G groupItem) throws DbException; protected abstract void deleteNamedGroup(G groupItem) throws DbException;
private List<I> buildItems(Collection<H> headers) { private ThreadItemList<I> buildItems(Collection<H> headers)
List<I> items = new ArrayList<>(); throws DbException {
ThreadItemList<I> items = new ThreadItemListImpl<>();
for (H h : headers) { for (H h : headers) {
items.add(buildItem(h, bodyCache.get(h.getId()))); items.add(buildItem(h, bodyCache.get(h.getId())));
} }
MessageId msgId = messageTracker.loadStoredMessageId(groupId);
if (LOG.isLoggable(INFO))
LOG.info("Loaded last top visible message id " + msgId);
items.setFirstVisibleId(msgId);
return items; return items;
} }

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.util; package org.briarproject.briar.android.util;
import android.content.Context; import android.content.Context;
import android.support.annotation.Nullable;
import android.support.design.widget.TextInputLayout; import android.support.design.widget.TextInputLayout;
import android.support.v4.app.FragmentManager; import android.support.v4.app.FragmentManager;
import android.support.v4.content.ContextCompat; import android.support.v4.content.ContextCompat;
@@ -23,6 +22,8 @@ import org.briarproject.briar.R;
import org.briarproject.briar.android.view.ArticleMovementMethod; import org.briarproject.briar.android.view.ArticleMovementMethod;
import org.briarproject.briar.android.widget.LinkDialogFragment; import org.briarproject.briar.android.widget.LinkDialogFragment;
import javax.annotation.Nullable;
import static android.text.format.DateUtils.DAY_IN_MILLIS; import static android.text.format.DateUtils.DAY_IN_MILLIS;
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH; import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE; import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
@@ -30,6 +31,7 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
import static android.text.format.DateUtils.FORMAT_SHOW_DATE; import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS; import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS; import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
public class UiUtils { public class UiUtils {
@@ -63,6 +65,12 @@ public class UiUtils {
MIN_DATE_RESOLUTION, flags).toString(); MIN_DATE_RESOLUTION, flags).toString();
} }
public static int getDaysUntilExpiry() {
long now = System.currentTimeMillis();
long daysBeforeExpiry = (EXPIRY_DATE - now) / 1000 / 60 / 60 / 24;
return (int) daysBeforeExpiry;
}
public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) { public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) {
if (body.length() < TEASER_LENGTH) if (body.length() < TEASER_LENGTH)
throw new IllegalArgumentException( throw new IllegalArgumentException(

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.view; package org.briarproject.briar.android.view;
import android.content.Context; import android.content.Context;
import android.content.Intent;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Typeface; import android.graphics.Typeface;
import android.support.annotation.DimenRes; import android.support.annotation.DimenRes;
@@ -16,9 +15,7 @@ import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author; import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.Author.Status; import org.briarproject.bramble.api.identity.Author.Status;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.util.UiUtils; import org.briarproject.briar.android.util.UiUtils;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@@ -27,12 +24,10 @@ import de.hdodenhof.circleimageview.CircleImageView;
import im.delight.android.identicons.IdenticonDrawable; import im.delight.android.identicons.IdenticonDrawable;
import static android.content.Context.LAYOUT_INFLATER_SERVICE; import static android.content.Context.LAYOUT_INFLATER_SERVICE;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.graphics.Typeface.BOLD; import static android.graphics.Typeface.BOLD;
import static android.util.TypedValue.COMPLEX_UNIT_PX; import static android.util.TypedValue.COMPLEX_UNIT_PX;
import static org.briarproject.bramble.api.identity.Author.Status.NONE; import static org.briarproject.bramble.api.identity.Author.Status.NONE;
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES; import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@UiThread @UiThread
public class AuthorView extends RelativeLayout { public class AuthorView extends RelativeLayout {
@@ -110,24 +105,16 @@ public class AuthorView extends RelativeLayout {
requestLayout(); requestLayout();
} }
public void setBlogLink(final GroupId groupId) { public void setAuthorClickable(OnClickListener listener) {
setClickable(true); setClickable(true);
TypedValue outValue = new TypedValue(); TypedValue outValue = new TypedValue();
getContext().getTheme().resolveAttribute( getContext().getTheme().resolveAttribute(
android.R.attr.selectableItemBackground, outValue, true); android.R.attr.selectableItemBackground, outValue, true);
setBackgroundResource(outValue.resourceId); setBackgroundResource(outValue.resourceId);
setOnClickListener(new OnClickListener() { setOnClickListener(listener);
@Override
public void onClick(View v) {
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
});
} }
public void unsetBlogLink() { public void setAuthorNotClickable() {
setClickable(false); setClickable(false);
setBackgroundResource(android.R.color.transparent); setBackgroundResource(android.R.color.transparent);
setOnClickListener(null); setOnClickListener(null);

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.view;
import android.content.Context; import android.content.Context;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.support.design.widget.FloatingActionButton; import android.support.design.widget.FloatingActionButton;
import android.util.AttributeSet; import android.util.AttributeSet;
@@ -13,6 +12,8 @@ import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import javax.annotation.Nullable;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
public class UnreadMessageButton extends FrameLayout { public class UnreadMessageButton extends FrameLayout {
@@ -36,8 +37,7 @@ public class UnreadMessageButton extends FrameLayout {
LayoutInflater inflater = (LayoutInflater) context LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE); .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater inflater.inflate(R.layout.unread_message_button, this, true);
.inflate(R.layout.unread_message_button, this, true);
fab = (FloatingActionButton) findViewById(R.id.fab); fab = (FloatingActionButton) findViewById(R.id.fab);
unread = (TextView) findViewById(R.id.unreadCountView); unread = (TextView) findViewById(R.id.unreadCountView);
@@ -64,15 +64,11 @@ public class UnreadMessageButton extends FrameLayout {
public void setUnreadCount(int count) { public void setUnreadCount(int count) {
if (count == 0) { if (count == 0) {
fab.setVisibility(GONE); setVisibility(INVISIBLE);
// fab.hide();
unread.setVisibility(GONE);
} else { } else {
// FIXME: Use animations when upgrading to support library 24.2.0 // FIXME: Use animations when upgrading to support library 24.2.0
// https://code.google.com/p/android/issues/detail?id=216469 // https://code.google.com/p/android/issues/detail?id=216469
fab.setVisibility(VISIBLE); setVisibility(VISIBLE);
// if (!fab.isShown()) fab.show();
unread.setVisibility(VISIBLE);
unread.setText(String.valueOf(count)); unread.setText(String.valueOf(count));
} }
} }

View File

@@ -0,0 +1,51 @@
package org.briarproject.briar.android.widget;
import android.content.Context;
import android.support.annotation.AttrRes;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.widget.FrameLayout;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import javax.annotation.Nullable;
import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED;
@NotNullByDefault
public class TapSafeFrameLayout extends FrameLayout {
@Nullable
private OnTapFilteredListener listener;
public TapSafeFrameLayout(Context context) {
super(context);
setFilterTouchesWhenObscured(false);
}
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
setFilterTouchesWhenObscured(false);
}
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs,
@AttrRes int defStyleAttr) {
super(context, attrs, defStyleAttr);
setFilterTouchesWhenObscured(false);
}
public void setOnTapFilteredListener(OnTapFilteredListener listener) {
this.listener = listener;
}
@Override
public boolean onFilterTouchEventForSecurity(MotionEvent e) {
boolean filter = (e.getFlags() & FLAG_WINDOW_IS_OBSCURED) != 0;
if (filter && listener != null) listener.onTapFiltered();
return !filter;
}
public interface OnTapFilteredListener {
void onTapFiltered();
}
}

View File

@@ -18,19 +18,14 @@ public interface AndroidNotificationManager {
String PREF_NOTIFY_RINGTONE_NAME = "notifyRingtoneName"; String PREF_NOTIFY_RINGTONE_NAME = "notifyRingtoneName";
String PREF_NOTIFY_RINGTONE_URI = "notifyRingtoneUri"; String PREF_NOTIFY_RINGTONE_URI = "notifyRingtoneUri";
String PREF_NOTIFY_VIBRATION = "notifyVibration"; String PREF_NOTIFY_VIBRATION = "notifyVibration";
String PREF_NOTIFY_LOCK_SCREEN = "notifyLockScreen";
void clearContactNotification(ContactId c); void clearContactNotification(ContactId c);
void clearAllContactNotifications();
void clearGroupMessageNotification(GroupId g); void clearGroupMessageNotification(GroupId g);
void clearAllGroupMessageNotifications();
void clearForumPostNotification(GroupId g); void clearForumPostNotification(GroupId g);
void clearAllForumPostNotifications();
void clearBlogPostNotification(GroupId g); void clearBlogPostNotification(GroupId g);
void clearAllBlogPostNotifications(); void clearAllBlogPostNotifications();
@@ -43,18 +38,6 @@ public interface AndroidNotificationManager {
void unblockNotification(GroupId g); void unblockNotification(GroupId g);
void blockAllContactNotifications();
void unblockAllContactNotifications();
void blockAllGroupMessageNotifications();
void unblockAllGroupMessageNotifications();
void blockAllForumPostNotifications();
void unblockAllForumPostNotifications();
void blockAllBlogPostNotifications(); void blockAllBlogPostNotifications();
void unblockAllBlogPostNotifications(); void unblockAllBlogPostNotifications();

View File

@@ -2,14 +2,10 @@ package org.briarproject.briar.api.android;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import java.util.Collection;
import java.util.Set; import java.util.Set;
public interface ScreenFilterMonitor { public interface ScreenFilterMonitor {
@UiThread @UiThread
Set<String> getApps(); Set<String> getApps();
@UiThread
void storeAppsAsShown(Collection<String> s, boolean persistent);
} }

View File

@@ -1,29 +1,21 @@
package org.thoughtcrime.securesms.components.emoji; package org.thoughtcrime.securesms.components.emoji;
import android.content.Context; import android.content.Context;
import android.graphics.Paint.FontMetricsInt;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.UiThread; import android.support.annotation.UiThread;
import android.text.TextUtils; import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet; import android.util.AttributeSet;
import android.view.ViewConfiguration; import android.view.ViewConfiguration;
import android.widget.TextView;
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable; import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import static android.text.TextUtils.TruncateAt.END;
import static android.view.View.MeasureSpec.AT_MOST;
import static android.view.View.MeasureSpec.EXACTLY;
import static android.widget.TextView.BufferType.SPANNABLE; import static android.widget.TextView.BufferType.SPANNABLE;
@UiThread @UiThread
public class EmojiTextView extends TextView { public class EmojiTextView extends AppCompatTextView {
private CharSequence source;
private boolean needsEllipsizing;
public EmojiTextView(Context context) { public EmojiTextView(Context context) {
this(context, null); this(context, null);
@@ -42,13 +34,9 @@ public class EmojiTextView extends TextView {
@Override @Override
public void setText(@Nullable CharSequence text, BufferType type) { public void setText(@Nullable CharSequence text, BufferType type) {
source = EmojiProvider.getInstance(getContext()).emojify(text, this); CharSequence source =
EmojiProvider.getInstance(getContext()).emojify(text, this);
setTextEllipsized(source); super.setText(source, SPANNABLE);
}
private void setTextEllipsized(final @Nullable CharSequence source) {
super.setText(needsEllipsizing ? ellipsize(source) : source, SPANNABLE);
} }
@Override @Override
@@ -57,26 +45,6 @@ public class EmojiTextView extends TextView {
else super.invalidateDrawable(drawable); else super.invalidateDrawable(drawable);
} }
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
final int size = MeasureSpec.getSize(widthMeasureSpec);
final int mode = MeasureSpec.getMode(widthMeasureSpec);
if (getEllipsize() == END &&
!TextUtils.isEmpty(source) &&
(mode == AT_MOST || mode == EXACTLY) &&
getPaint().breakText(source, 0, source.length() - 1, true, size,
null) != source.length()) {
needsEllipsizing = true;
FontMetricsInt font = getPaint().getFontMetricsInt();
int height = Math.abs(font.top - font.bottom);
super.onMeasure(MeasureSpec.makeMeasureSpec(size, EXACTLY),
MeasureSpec.makeMeasureSpec(height, EXACTLY));
} else {
needsEllipsizing = false;
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
}
@Override @Override
protected void onLayout(boolean changed, int left, int top, int right, protected void onLayout(boolean changed, int left, int top, int right,
int bottom) { int bottom) {
@@ -89,20 +57,6 @@ public class EmojiTextView extends TextView {
if (size > drawingCacheSize) { if (size > drawingCacheSize) {
setLayerType(LAYER_TYPE_NONE, null); setLayerType(LAYER_TYPE_NONE, null);
} }
if (changed) setTextEllipsized(source);
super.onLayout(changed, left, top, right, bottom); super.onLayout(changed, left, top, right, bottom);
} }
@Nullable
public CharSequence ellipsize(@Nullable CharSequence text) {
if (TextUtils.isEmpty(text) || getWidth() == 0 ||
getEllipsize() != END) {
return text;
} else {
return TextUtils.ellipsize(text, getPaint(),
getWidth() - getPaddingRight() - getPaddingLeft(), END);
}
}
} }

View File

@@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="18dp"
android:height="18dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
</vector>

View File

@@ -7,7 +7,7 @@
<path <path
android:fillColor="#ffa500" android:fillColor="#ffa500"
android:pathData="M0,8.88178e-16 L30,8.88178e-16 L30,30 L0,30 L0,8.88178e-16 Z"/> android:pathData="M0,0 L30,0 L30,30 L0,30 L0,0 Z"/>
<path <path
android:fillColor="#ffffff" android:fillColor="#ffffff"
android:pathData="M8.9322,18.0339 C10.6078,18.0339,11.9661,19.3922,11.9661,21.0678 android:pathData="M8.9322,18.0339 C10.6078,18.0339,11.9661,19.3922,11.9661,21.0678

View File

@@ -12,9 +12,9 @@
android:fillColor="#87c214" android:fillColor="#87c214"
android:pathData="M64.9004,0 C55.2004,0,47.1992,7.99922,47.1992,17.6992 L47.1992,40.1992 android:pathData="M64.9004,0 C55.2004,0,47.1992,7.99922,47.1992,17.6992 L47.1992,40.1992
L90.8008,40.1992 L90.8008,17.6992 L90.8008,40.1992 L90.8008,17.6992
C90.8008,7.99922,82.8992,-4.73695e-15,73.1992,0 L64.9004,0 Z M161.9,0 C90.8008,7.99922,82.8992,0,73.1992,0 L64.9004,0 Z M161.9,0
C152.2,0,144.199,7.99922,144.199,17.6992 L144.199,137.199 L187.801,137.199 C152.2,0,144.199,7.99922,144.199,17.6992 L144.199,137.199 L187.801,137.199
L187.801,17.6992 C187.801,7.99922,179.899,-4.73695e-15,170.199,0 L161.9,0 Z L187.801,17.6992 C187.801,7.99922,179.899,0,170.199,0 L161.9,0 Z
M47.1992,97.8008 L47.1992,217.301 C47.1992,227.001,55.1004,235,64.9004,235 M47.1992,97.8008 L47.1992,217.301 C47.1992,227.001,55.1004,235,64.9004,235
L73.1992,235 C82.8992,235,90.9004,227.001,90.9004,217.301 L90.9004,97.8008 L73.1992,235 C82.8992,235,90.9004,227.001,90.9004,217.301 L90.9004,97.8008
L47.1992,97.8008 Z M144.199,194.801 L144.199,217.301 L47.1992,97.8008 Z M144.199,194.801 L144.199,217.301
@@ -25,12 +25,12 @@ C179.899,235,187.9,227.001,187.9,217.301 L187.9,194.801 L144.199,194.801 Z"/>
android:pathData="M144.2,144.2 L187.9,144.2 L187.9,187.9 L144.2,187.9 L144.2,144.2 Z"/> android:pathData="M144.2,144.2 L187.9,144.2 L187.9,187.9 L144.2,187.9 L144.2,144.2 Z"/>
<path <path
android:fillColor="#95d220" android:fillColor="#95d220"
android:pathData="M17.6992,47.1992 C7.99922,47.1992,2.36848e-15,55.1004,0,64.9004 L0,73.1992 android:pathData="M17.6992,47.1992 C7.99922,47.1992,0,55.1004,0,64.9004 L0,73.1992
C0,82.8992,7.89922,90.9004,17.6992,90.9004 L137.199,90.9004 L137.199,47.1992 C0,82.8992,7.89922,90.9004,17.6992,90.9004 L137.199,90.9004 L137.199,47.1992
L17.6992,47.1992 Z M194.801,47.1992 L194.801,90.9004 L217.301,90.9004 L17.6992,47.1992 Z M194.801,47.1992 L194.801,90.9004 L217.301,90.9004
C227.001,90.9004,235,82.9992,235,73.1992 L235,64.9004 C227.001,90.9004,235,82.9992,235,73.1992 L235,64.9004
C235,55.1004,227.001,47.1992,217.301,47.1992 L194.801,47.1992 Z M17.6992,144.199 C235,55.1004,227.001,47.1992,217.301,47.1992 L194.801,47.1992 Z M17.6992,144.199
C7.99922,144.199,2.36848e-15,152.1,0,161.9 L0,170.199 C7.99922,144.199,0,152.1,0,161.9 L0,170.199
C0,179.899,7.89922,187.9,17.6992,187.9 L40.1992,187.9 L40.1992,144.199 C0,179.899,7.89922,187.9,17.6992,187.9 L40.1992,187.9 L40.1992,144.199
L17.6992,144.199 Z M97.8008,144.199 L97.8008,187.9 L217.301,187.9 L17.6992,144.199 Z M97.8008,144.199 L97.8008,187.9 L217.301,187.9
C227.001,187.9,235,179.899,235,170.199 L235,161.9 C227.001,187.9,235,179.899,235,170.199 L235,161.9

View File

@@ -1,44 +1,42 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:gravity="center_horizontal" android:orientation="vertical"
android:padding="20dp" > android:padding="@dimen/margin_large">
<TextView <android.support.design.widget.TextInputLayout
android:layout_width="wrap_content" android:id="@+id/createForumNameLayout"
android:layout_height="wrap_content"
android:gravity="center"
android:textSize="@dimen/text_size_medium"
android:text="@string/choose_forum_name" />
<EditText
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/createForumNameEntry" app:errorEnabled="true">
android:maxLines="1"
android:inputType="text|textCapSentences" />
<TextView <EditText
android:layout_width="wrap_content" android:id="@+id/createForumNameEntry"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:id="@+id/createForumFeedback" android:hint="@string/choose_forum_hint"
android:gravity="center" android:inputType="text|textCapSentences"
android:paddingLeft="50dp" android:maxLines="1"/>
android:paddingRight="50dp" />
</android.support.design.widget.TextInputLayout>
<Button <Button
style="@style/BriarButton"
android:id="@+id/createForumButton" android:id="@+id/createForumButton"
style="@style/BriarButton"
android:enabled="false"
android:text="@string/create_forum_button"/> android:text="@string/create_forum_button"/>
<ProgressBar <ProgressBar
android:id="@+id/createForumProgressBar" android:id="@+id/createForumProgressBar"
android:layout_height="wrap_content"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true" android:indeterminate="true"
android:visibility="gone" /> android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout> </LinearLayout>

View File

@@ -5,5 +5,5 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_gravity="center" android:layout_gravity="center"
android:gravity="center" android:gravity="center"
android:text="@string/expiry_warning" android:text="@string/expiry_date_reached"
android:textSize="@dimen/text_size_large"/> android:textSize="@dimen/text_size_large"/>

View File

@@ -15,6 +15,38 @@
<include layout="@layout/toolbar"/> <include layout="@layout/toolbar"/>
<RelativeLayout
android:id="@+id/expiryWarning"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/briar_warning_background"
android:orientation="horizontal"
android:padding="@dimen/margin_medium"
android:visibility="gone"
tools:visibility="visible">
<TextView
android:id="@+id/expiryWarningText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/expiryWarningClose"
android:text="@string/expiry_warning"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/text_size_small"/>
<ImageView
android:id="@+id/expiryWarningClose"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:contentDescription="@string/close"
android:scaleType="center"
android:src="@drawable/ic_close"
android:tint="@color/briar_text_tertiary_inverse"/>
</RelativeLayout>
<FrameLayout <FrameLayout
android:id="@+id/fragmentContainer" android:id="@+id/fragmentContainer"
android:layout_width="match_parent" android:layout_width="match_parent"

View File

@@ -15,7 +15,7 @@
android:layout_weight="1" android:layout_weight="1"
android:gravity="top" android:gravity="top"
android:hint="@string/blogs_rss_feeds_import_hint" android:hint="@string/blogs_rss_feeds_import_hint"
android:inputType="textMultiLine|textUri"/> android:inputType="textUri"/>
<Button <Button
android:id="@+id/importButton" android:id="@+id/importButton"

View File

@@ -1,40 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:filterTouchesWhenObscured="false"
android:orientation="vertical">
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:fadeScrollbars="false"
android:paddingLeft="20dp"
android:paddingRight="20dp"
android:paddingStart="20dp"
android:paddingTop="20dp"
android:theme="@style/BriarTheme">
<TextView
android:id="@+id/alert_dialog_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingLeft="3dp"
android:paddingRight="6dp"
android:text="TextView"
android:textAppearance="@style/BriarTextBody"
android:theme="@+theme/BriarDialogTheme"/>
</ScrollView>
<CheckBox
android:id="@+id/checkBox_screen_filter_reminder"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="15dp"
android:layout_marginStart="15dp"
android:layout_weight="0"
android:filterTouchesWhenObscured="false"
android:text="@string/checkbox_dont_show_again"
android:textAppearance="@style/BriarTextBody"/>
</LinearLayout>

View File

@@ -1,20 +1,28 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android" 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_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical" android:orientation="vertical"
android:padding="@dimen/margin_medium"> android:padding="@dimen/margin_large">
<android.support.design.widget.TextInputLayout
android:id="@+id/nameLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<EditText <EditText
android:id="@+id/name" android:id="@+id/name"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:layout_weight="1" android:hint="@string/groups_create_group_hint"
android:gravity="bottom"
android:maxLines="1"
android:inputType="text|textCapSentences" android:inputType="text|textCapSentences"
android:hint="@string/groups_create_group_hint"/> android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<Button <Button
android:id="@+id/button" android:id="@+id/button"
@@ -22,4 +30,13 @@
android:enabled="false" android:enabled="false"
android:text="@string/groups_create_group_button"/> android:text="@string/groups_create_group_button"/>
<ProgressBar
android:id="@+id/progressBar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:indeterminate="true"
android:visibility="gone"
tools:visibility="visible"/>
</LinearLayout> </LinearLayout>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<LinearLayout <ScrollView
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
@@ -7,25 +7,21 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:orientation="vertical"> android:orientation="vertical">
<ScrollView <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="0dp" android:layout_height="wrap_content"
android:layout_weight="1"> android:orientation="vertical">
<LinearLayout <LinearLayout
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_activity_horizontal" android:layout_margin="@dimen/margin_activity_horizontal"
android:orientation="vertical"> android:gravity="center"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/introductionIcon"
android:gravity="top|center_horizontal" android:gravity="top|center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
@@ -47,7 +43,7 @@
android:layout_marginTop="@dimen/margin_small" android:layout_marginTop="@dimen/margin_small"
android:gravity="center" android:gravity="center"
android:textColor="@color/briar_text_primary" android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny" android:textSize="@dimen/text_size_small"
tools:text="Contact 1"/> tools:text="Contact 1"/>
</LinearLayout> </LinearLayout>
@@ -56,15 +52,12 @@
android:id="@+id/introductionIcon" android:id="@+id/introductionIcon"
android:layout_width="@dimen/listitem_picture_size" android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size" android:layout_height="@dimen/listitem_picture_size"
android:layout_centerHorizontal="true"
android:src="@drawable/ic_contact_introduction" android:src="@drawable/ic_contact_introduction"
tools:ignore="ContentDescription"/> tools:ignore="ContentDescription"/>
<LinearLayout <LinearLayout
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_toEndOf="@+id/introductionIcon"
android:layout_toRightOf="@+id/introductionIcon"
android:gravity="top|center_horizontal" android:gravity="top|center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
@@ -86,12 +79,12 @@
android:layout_marginTop="@dimen/margin_small" android:layout_marginTop="@dimen/margin_small"
android:gravity="center" android:gravity="center"
android:textColor="@color/briar_text_primary" android:textColor="@color/briar_text_primary"
android:textSize="@dimen/text_size_tiny" android:textSize="@dimen/text_size_small"
tools:text="Contact 2"/> tools:text="Contact 2"/>
</LinearLayout> </LinearLayout>
</RelativeLayout> </LinearLayout>
<ProgressBar <ProgressBar
android:id="@+id/progressBar" android:id="@+id/progressBar"
@@ -101,19 +94,6 @@
android:layout_gravity="center" android:layout_gravity="center"
tools:visibility="gone"/> tools:visibility="gone"/>
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
android:id="@+id/introductionText"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_activity_horizontal"
android:textColor="@color/briar_text_secondary"
android:textSize="@dimen/text_size_medium"
tools:text="@string/introduction_message_text"/>
</LinearLayout>
</ScrollView>
<org.briarproject.briar.android.view.LargeTextInputView <org.briarproject.briar.android.view.LargeTextInputView
android:id="@+id/introductionMessageView" android:id="@+id/introductionMessageView"
android:layout_width="match_parent" android:layout_width="match_parent"
@@ -123,3 +103,4 @@
app:maxLines="5"/> app:maxLines="5"/>
</LinearLayout> </LinearLayout>
</ScrollView>

View File

@@ -19,57 +19,15 @@
android:textStyle="italic" android:textStyle="italic"
tools:text="@string/groups_member_joined"/> tools:text="@string/groups_member_joined"/>
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/info"
android:layout_alignLeft="@+id/text"
android:layout_alignTop="@+id/info"
android:layout_below="@+id/text"
android:layout_marginRight="@dimen/margin_medium"
android:scaleType="center"
tools:ignore="ContentDescription"
tools:src="@drawable/ic_visibility"/>
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignEnd="@+id/text"
android:layout_alignRight="@+id/text"
android:layout_below="@+id/text"
android:layout_marginBottom="@dimen/margin_medium"
android:layout_toRightOf="@+id/icon"
android:gravity="center_vertical"
android:minHeight="24dp"
android:textColor="@color/briar_text_secondary"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_tiny"
android:textStyle="italic"
tools:text="@string/groups_reveal_visible_revealed_by_contact"/>
<org.briarproject.briar.android.view.AuthorView <org.briarproject.briar.android.view.AuthorView
android:id="@+id/author" android:id="@+id/author"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignLeft="@+id/text" android:layout_alignLeft="@+id/text"
android:layout_below="@+id/info" android:layout_alignStart="@+id/text"
android:layout_toLeftOf="@+id/optionsButton" android:layout_below="@+id/text"
app:persona="commenter"/> app:persona="commenter"/>
<Button
android:id="@+id/optionsButton"
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/author"
android:layout_alignEnd="@+id/text"
android:layout_alignRight="@+id/text"
android:layout_below="@+id/info"
android:gravity="right|center_vertical"
android:text="@string/options"/>
<View <View
style="@style/Divider.ThreadItem" style="@style/Divider.ThreadItem"
android:layout_below="@+id/author" android:layout_below="@+id/author"

View File

@@ -13,11 +13,13 @@
android:id="@+id/authorView" android:id="@+id/authorView"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true" android:layout_alignParentLeft="true"
android:layout_alignParentTop="true" android:layout_alignParentTop="true"
android:layout_marginBottom="@dimen/margin_small" android:layout_marginBottom="@dimen/margin_small"
android:layout_marginEnd="@dimen/listitem_horizontal_margin" android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin" android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toStartOf="@+id/bulbView"
android:layout_toLeftOf="@+id/bulbView" android:layout_toLeftOf="@+id/bulbView"
app:persona="list"/> app:persona="list"/>
@@ -29,6 +31,7 @@
android:layout_alignParentEnd="true" android:layout_alignParentEnd="true"
android:layout_alignParentRight="true" android:layout_alignParentRight="true"
android:layout_alignTop="@+id/authorView" android:layout_alignTop="@+id/authorView"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin" android:layout_marginRight="@dimen/listitem_horizontal_margin"
tools:src="@drawable/contact_connected"/> tools:src="@drawable/contact_connected"/>
@@ -39,40 +42,16 @@
android:layout_below="@+id/authorView" android:layout_below="@+id/authorView"
android:layout_marginBottom="@dimen/margin_small" android:layout_marginBottom="@dimen/margin_small"
android:layout_marginLeft="@dimen/listitem_group_member_indentation" android:layout_marginLeft="@dimen/listitem_group_member_indentation"
android:layout_marginStart="@dimen/listitem_group_member_indentation"
android:text="@string/groups_member_created_you" android:text="@string/groups_member_created_you"
android:textColor="@color/briar_text_secondary" android:textColor="@color/briar_text_secondary"
tools:visibility="visible"/> tools:visibility="visible"/>
<ImageView
android:id="@+id/icon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/creatorView"
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
android:layout_marginRight="@dimen/margin_small"
android:contentDescription="@string/forum_invitation_already_sharing"
android:src="@drawable/ic_visibility"/>
<TextView
android:id="@+id/info"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/creatorView"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_toRightOf="@+id/icon"
android:gravity="center_vertical"
android:minHeight="24dp"
android:textColor="@color/briar_text_secondary"
android:textIsSelectable="true"
android:textSize="@dimen/text_size_tiny"
android:textStyle="italic"
tools:text="@string/groups_reveal_visible_revealed_by_us"/>
<View <View
android:id="@+id/divider" android:id="@+id/divider"
style="@style/Divider.ContactList" style="@style/Divider.ContactList"
android:layout_below="@+id/info" android:layout_below="@+id/creatorView"
android:layout_marginStart="@dimen/listitem_group_member_indentation"
android:layout_marginLeft="@dimen/listitem_group_member_indentation" android:layout_marginLeft="@dimen/listitem_group_member_indentation"
android:layout_marginTop="@dimen/margin_medium"/> android:layout_marginTop="@dimen/margin_medium"/>

View File

@@ -10,16 +10,16 @@
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/action_group_reveal" android:id="@+id/action_group_invite"
android:icon="@drawable/ic_visibility_white" android:icon="@drawable/social_share_white"
android:title="@string/groups_reveal_contacts" android:title="@string/groups_invite_members"
app:showAsAction="ifRoom"/> app:showAsAction="ifRoom"/>
<item <item
android:id="@+id/action_group_invite" android:id="@+id/action_group_reveal"
android:icon="@drawable/ic_add_white" android:icon="@drawable/ic_visibility_white"
android:title="@string/groups_invite_members" android:title="@string/groups_reveal_contacts"
app:showAsAction="ifRoom"/> app:showAsAction="never"/>
<item <item
android:id="@+id/action_group_leave" android:id="@+id/action_group_leave"

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