Compare commits

..

39 Commits

Author SHA1 Message Date
akwizgran
62cca1335f Bump version numbers for 1.2.20 release. 2021-03-29 13:33:12 +01:00
akwizgran
11a18859fb Update translations. 2021-03-29 13:33:12 +01:00
akwizgran
1116a7e125 Merge branch 'update-bridges-again' into 'master'
Remove a failing bridge

See merge request briar/briar!1422
2021-03-29 12:32:11 +00:00
akwizgran
415b315292 Add a Tor Browser default bridge. 2021-03-29 13:01:27 +01:00
akwizgran
9818ec2b66 Remove a failing bridge. 2021-03-29 12:55:04 +01:00
akwizgran
372516646d Merge branch '1970-blog-bugs' into 'master'
Fix issues with blogs after refactoring

See merge request briar/briar!1421
2021-03-26 14:32:11 +00:00
Torsten Grote
72e721b0d3 Don't show snackbar about local blog post again after screen rotation 2021-03-26 10:56:57 -03:00
Torsten Grote
6599093611 Improve blog author clickability
resolves issue where clicking reblogged author opened reblogging author's blog
2021-03-26 10:40:51 -03:00
Torsten Grote
dceeecf1fe Open blog posts from blog feed in BlogActivity 2021-03-26 10:23:31 -03:00
Torsten Grote
ace0b9a3d8 Merge branch 'update-bridges' into 'master'
Replace a failing bridge with a Tor Browser default bridge

See merge request briar/briar!1420
2021-03-26 11:59:03 +00:00
akwizgran
7c45c90de9 Replace a failing bridge with a Tor Browser default bridge. 2021-03-26 09:27:36 +00:00
akwizgran
c2a4b5e26a Bump version numbers for 1.2.19 release. 2021-03-25 17:36:04 +00:00
akwizgran
feac0ad802 Update translations. 2021-03-25 17:34:02 +00:00
akwizgran
60478eba3f Merge branch '1866-blog-controller' into 'master'
Migrate BlogController and FeedController to ViewModel

Closes #1891 and #1866

See merge request briar/briar!1342
2021-03-25 17:25:43 +00:00
akwizgran
3639952612 Merge branch 'espresso-ci' into 'master'
Run instrumentation tests in CI when briar-android changes

Closes admin#20

See merge request briar/briar!1413
2021-03-25 15:47:18 +00:00
akwizgran
c4a654b267 Merge branch '1979-feedback-crash' into 'master'
Don't crash when pressing SHOW with user information when sending feedback

Closes #1979

See merge request briar/briar!1418
2021-03-25 13:29:43 +00:00
Torsten Grote
ecb31a4d32 Don't crash when pressing SHOW with user information when sending feedback 2021-03-25 08:47:18 -03:00
Torsten Grote
76f201bb2f Run Espresso tests manually as they are still too flaky 2021-03-24 16:10:33 -03:00
akwizgran
87799b743c Add Burmese translation to language chooser. 2021-03-24 15:28:33 +00:00
akwizgran
b898a7c370 Update translations, add Burmese translation. 2021-03-24 13:54:00 +00:00
Torsten Grote
f3210e3af2 Allow DbViewModel work on things other than lists. 2021-03-23 12:59:16 -03:00
akwizgran
225fd6fd49 Merge branch 'headless-remove-type-args-in-jar-sorting-algorithm' into 'master'
Remove redundant type args in briar-headless/build.gradle

See merge request briar/briar!1416
2021-03-23 12:38:53 +00:00
Sebastian Kürten
400d259a60 Remove redundant type args in briar-headless/build.gradle
The TreeMap<> doesn't need to repeat <String, JarEntry> from
Map<String, JarEntry>.
2021-03-23 07:44:02 +01:00
Torsten Grote
4074ac8578 Add handleException() to DbViewModel
and use it for blogs
2021-03-22 15:17:30 -03:00
Torsten Grote
b2e6dd4138 publish log files as artifacts when emulator job fails 2021-03-19 14:19:07 -03:00
Torsten Grote
b608b42174 Run instrumentation tests in CI when briar-android changes 2021-03-18 12:15:47 -03:00
Torsten Grote
f603254153 Fix instrumentation tests 2021-03-18 12:15:46 -03:00
Torsten Grote
c851dd228b Add a different (faster) way to exclude large/slow tests 2021-03-18 12:15:46 -03:00
Torsten Grote
e97478a21a Don't reload blog data when configuration changes 2021-03-17 14:16:02 -03:00
Torsten Grote
726ebcea3f Make blog post author clickable when not already in their blog 2021-03-17 14:16:02 -03:00
Torsten Grote
2f969775d8 Remove TransactionManager from blog's BaseViewModel 2021-03-17 14:16:02 -03:00
Torsten Grote
d3b855318c Anticipate review feedback for blog view models after re-basing 2021-03-17 14:16:01 -03:00
Torsten Grote
95104d3383 Clean up after migrating blog controllers to view model 2021-03-17 14:16:01 -03:00
Torsten Grote
6860a04e8b Don't use layoutManager hack to restore scrolling position of blogs
not needed anymore when posts are cached in viewmodels
2021-03-17 14:16:01 -03:00
Torsten Grote
33c24f8655 Migrate blogs to new SharingController
and get rid of the deprecated one
2021-03-17 14:16:00 -03:00
Torsten Grote
1fa4b78474 Migrate BlogController to BlogViewModel 2021-03-17 14:16:00 -03:00
Torsten Grote
b678de7529 Make BlogAdapter final and don't pass in a FragmentManager 2021-03-17 14:16:00 -03:00
Torsten Grote
ab1ed0ff5a Turn FeedController into FeedViewModel 2021-03-17 14:15:59 -03:00
Torsten Grote
ad20e5230a Allow blog posts to be loaded within one transaction 2021-03-17 14:15:59 -03:00
308 changed files with 3510 additions and 12502 deletions

View File

@@ -1,30 +1,52 @@
image: briar/ci-image-android:latest image: briar/ci-image-android:latest
stages: stages:
- test - test
- optional_tests - optional_tests
- check_reproducibility - check_reproducibility
test: .base-test:
stage: test
before_script: before_script:
- set -e - set -e
- export GRADLE_USER_HOME=$PWD/.gradle - export GRADLE_USER_HOME=$PWD/.gradle
cache: cache:
key: "$CI_COMMIT_REF_SLUG"
paths: paths:
- .gradle/wrapper - .gradle/wrapper
- .gradle/caches - .gradle/caches
script:
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom check compileOfficialDebugAndroidTestSources compileScreenshotDebugAndroidTestSources
after_script: after_script:
# these file change every time but should not be cached # these file change every time and should not be cached
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock - rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/ - rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
test:
extends: .base-test
stage: test
script:
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom animalSnifferMain animalSnifferTest
- ./gradlew --no-daemon -Djava.security.egd=file:/dev/urandom check
android test:
extends: .base-test
stage: optional_tests
image: briar/ci-image-android-emulator:latest
script:
# start emulator first, so it can fail early
- start-emulator.sh
# run normal and screenshot tests together (exclude Large tests)
- ./gradlew -Djava.security.egd=file:/dev/urandom connectedAndroidTest -Pandroid.testInstrumentationRunnerArguments.notAnnotation=androidx.test.filters.LargeTest
artifacts:
name: "${CI_PROJECT_PATH}_${CI_JOB_STAGE}_${CI_COMMIT_REF_NAME}_${CI_COMMIT_SHA}"
paths:
- kernel.log
- logcat.txt
expire_in: 3 days
when: on_failure
when: manual
except:
- tags
tags:
- kvm
test_reproducible: test_reproducible:
stage: check_reproducibility stage: check_reproducibility
@@ -40,6 +62,7 @@ test_reproducible:
- export GRADLE_USER_HOME=$PWD/.gradle - export GRADLE_USER_HOME=$PWD/.gradle
cache: cache:
key: "$CI_COMMIT_REF_SLUG"
paths: paths:
- .gradle/wrapper - .gradle/wrapper
- .gradle/caches - .gradle/caches

View File

@@ -31,6 +31,15 @@
<option name="PACKAGES_TO_USE_STAR_IMPORTS"> <option name="PACKAGES_TO_USE_STAR_IMPORTS">
<value /> <value />
</option> </option>
<option name="PACKAGES_IMPORT_LAYOUT">
<value>
<package name="" alias="false" withSubpackages="true" />
<package name="java" alias="false" withSubpackages="true" />
<package name="javax" alias="false" withSubpackages="true" />
<package name="kotlin" alias="false" withSubpackages="true" />
<package name="" alias="true" withSubpackages="true" />
</value>
</option>
<option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT" value="2147483647" />
<option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" /> <option name="NAME_COUNT_TO_USE_STAR_IMPORT_FOR_MEMBERS" value="2147483647" />
<option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" /> <option name="CODE_STYLE_DEFAULTS" value="KOTLIN_OFFICIAL" />

View File

@@ -0,0 +1,51 @@
<component name="ProjectRunConfigurationManager">
<configuration default="false" name="Instrumentation Tests (destroys DB)" type="AndroidTestRunConfigurationType" factoryName="Android Instrumented Tests">
<module name="briar.briar-android" />
<option name="TESTING_TYPE" value="1" />
<option name="METHOD_NAME" value="" />
<option name="CLASS_NAME" value="" />
<option name="PACKAGE_NAME" value="org.briarproject.briar.android" />
<option name="INSTRUMENTATION_RUNNER_CLASS" value="" />
<option name="EXTRA_OPTIONS" value="-e notAnnotation androidx.test.filters.LargeTest" />
<option name="INCLUDE_GRADLE_EXTRA_OPTIONS" value="true" />
<option name="CLEAR_LOGCAT" value="false" />
<option name="SHOW_LOGCAT_AUTOMATICALLY" value="false" />
<option name="SKIP_NOOP_APK_INSTALLATIONS" value="true" />
<option name="FORCE_STOP_RUNNING_APP" value="true" />
<option name="TARGET_SELECTION_MODE" value="DEVICE_AND_SNAPSHOT_COMBO_BOX" />
<option name="DEBUGGER_TYPE" value="Auto" />
<Auto>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Auto>
<Hybrid>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Hybrid>
<Java />
<Native>
<option name="USE_JAVA_AWARE_DEBUGGER" value="false" />
<option name="SHOW_STATIC_VARS" value="true" />
<option name="WORKING_DIR" value="" />
<option name="TARGET_LOGGING_CHANNELS" value="lldb process:gdb-remote packets" />
<option name="SHOW_OPTIMIZED_WARNING" value="true" />
</Native>
<Profilers>
<option name="ADVANCED_PROFILING_ENABLED" value="false" />
<option name="STARTUP_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_ENABLED" value="false" />
<option name="STARTUP_CPU_PROFILING_CONFIGURATION_NAME" value="Sample Java Methods" />
<option name="STARTUP_NATIVE_MEMORY_PROFILING_ENABLED" value="false" />
<option name="NATIVE_MEMORY_SAMPLE_RATE_BYTES" value="2048" />
</Profilers>
<method v="2">
<option name="Android.Gradle.BeforeRunTask" enabled="true" />
</method>
</configuration>
</component>

View File

@@ -15,8 +15,8 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 10218 versionCode 10220
versionName "1.2.18" versionName "1.2.20"
consumerProguardFiles 'proguard-rules.txt' consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -50,10 +50,10 @@ dependencies {
compileOnly 'javax.annotation:jsr250-api:1.0' compileOnly 'javax.annotation:jsr250-api:1.0'
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation "junit:junit:$junit_version" testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:$jmock_version" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-imposters:$jmock_version" testImplementation "org.jmock:jmock-legacy:2.8.2"
} }
def torBinariesDir = 'src/main/res/raw' def torBinariesDir = 'src/main/res/raw'

View File

@@ -9,7 +9,7 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.After; import org.junit.After;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -44,7 +44,7 @@ public class AndroidAccountManagerTest extends BrambleMockTestCase {
private AndroidAccountManager accountManager; private AndroidAccountManager accountManager;
public AndroidAccountManagerTest() { public AndroidAccountManagerTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE); context.setImposteriser(ClassImposteriser.INSTANCE);
app = context.mock(Application.class); app = context.mock(Application.class);
applicationInfo = new ApplicationInfo(); applicationInfo = new ApplicationInfo();
applicationInfo.dataDir = testDir.getAbsolutePath(); applicationInfo.dataDir = testDir.getAbsolutePath();

View File

@@ -1,6 +1,6 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.android.tools.analytics-library:protos:27.1.1:protos-27.1.1.jar:13f77e73762e58ab372d140b3a6be6903aea9775b62dd14fbc62d4cc7069c9a4', 'com.android.tools.analytics-library:protos:27.1.1:protos-27.1.1.jar:13f77e73762e58ab372d140b3a6be6903aea9775b62dd14fbc62d4cc7069c9a4',
'com.android.tools.analytics-library:shared:27.1.1:shared-27.1.1.jar:82930a52001410e97d809930b670f4de3002286975f046b9de5f6b777b06d366', 'com.android.tools.analytics-library:shared:27.1.1:shared-27.1.1.jar:82930a52001410e97d809930b670f4de3002286975f046b9de5f6b777b06d366',
'com.android.tools.analytics-library:tracker:27.1.1:tracker-27.1.1.jar:31bc5a00be0055bac89c9b2f34751883e987cd89e3ac1783720645c164f591d9', 'com.android.tools.analytics-library:tracker:27.1.1:tracker-27.1.1.jar:31bc5a00be0055bac89c9b2f34751883e987cd89e3ac1783720645c164f591d9',
@@ -31,7 +31,6 @@ dependencyVerification {
'com.android.tools:sdklib:27.1.1:sdklib-27.1.1.jar:08e6b83961ac9724b3c1e3d0eff971f13be6701292c77914b8794480f3391250', 'com.android.tools:sdklib:27.1.1:sdklib-27.1.1.jar:08e6b83961ac9724b3c1e3d0eff971f13be6701292c77914b8794480f3391250',
'com.android:signflinger:4.1.1:signflinger-4.1.1.jar:0c66825988873ec2d51057fa463f54a8f18fc7326ff4530b9da363b71e97ce60', 'com.android:signflinger:4.1.1:signflinger-4.1.1.jar:0c66825988873ec2d51057fa463f54a8f18fc7326ff4530b9da363b71e97ce60',
'com.android:zipflinger:4.1.1:zipflinger-4.1.1.jar:0a8c3e52ac13dd031236f9fb5ba4408b1d5dcd12325a05440b36da09d8881446', 'com.android:zipflinger:4.1.1:zipflinger-4.1.1.jar:0a8c3e52ac13dd031236f9fb5ba4408b1d5dcd12325a05440b36da09d8881446',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81', 'com.google.code.gson:gson:2.8.5:gson-2.8.5.jar:233a0149fc365c9f6edbd683cfe266b19bdc773be98eabdaf6b3c924b48e7d81',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
@@ -64,16 +63,16 @@ dependencyVerification {
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'javax.xml.bind:jaxb-api:2.3.1:jaxb-api-2.3.1.jar:88b955a0df57880a26a74708bc34f74dcaf8ebf4e78843a28b50eae945732b06', 'javax.xml.bind:jaxb-api:2.3.1:jaxb-api-2.3.1.jar:88b955a0df57880a26a74708bc34f74dcaf8ebf4e78843a28b50eae945732b06',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd', 'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5', 'net.sf.jopt-simple:jopt-simple:4.9:jopt-simple-4.9.jar:26c5856e954b5f864db76f13b86919b59c6eecf9fd930b96baa8884626baf2f5',
'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2', 'net.sf.kxml:kxml2:2.3.0:kxml2-2.3.0.jar:f264dd9f79a1fde10ce5ecc53221eff24be4c9331c830b7d52f2f08a7b633de2',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6', 'org.apache.commons:commons-compress:1.12:commons-compress-1.12.jar:2c1542faf343185b7cab9c3d55c8ae5471d6d095d3887a4adefdbdf2984dc0b6',
'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7', 'org.apache.httpcomponents:httpclient:4.5.6:httpclient-4.5.6.jar:c03f813195e7a80e3608d0ddd8da80b21696a4c92a6a2298865bf149071551c7',
'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd', 'org.apache.httpcomponents:httpcore:4.4.10:httpcore-4.4.10.jar:78ba1096561957db1b55200a159b648876430342d15d461277e62360da19f6fd',
'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e', 'org.apache.httpcomponents:httpmime:4.5.6:httpmime-4.5.6.jar:0b2b1102c18d3c7e05a77214b9b7501a6f6056174ae5604e0e256776eda7553e',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca', 'org.bouncycastle:bcpkix-jdk15on:1.56:bcpkix-jdk15on-1.56.jar:7043dee4e9e7175e93e0b36f45b1ec1ecb893c5f755667e8b916eb8dd201c6ca',
'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349', 'org.bouncycastle:bcprov-jdk15on:1.56:bcprov-jdk15on-1.56.jar:963e1ee14f808ffb99897d848ddcdb28fa91ddda867eb18d303e82728f878349',
'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a:obfs4proxy-android-0.0.12-dev-40245c4a.zip:8ab05a8f8391be2cb5ab2b665c281a06d9e3a756bd0f95a40a36ca927866ea82', 'org.briarproject:obfs4proxy-android:0.0.12-dev-40245c4a:obfs4proxy-android-0.0.12-dev-40245c4a.zip:8ab05a8f8391be2cb5ab2b665c281a06d9e3a756bd0f95a40a36ca927866ea82',
@@ -86,9 +85,8 @@ dependencyVerification {
'org.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d', 'org.codehaus.mojo:animal-sniffer-annotations:1.18:animal-sniffer-annotations-1.18.jar:47f05852b48ee9baefef80fa3d8cea60efa4753c0013121dd7fe5eef2e5c729d',
'org.glassfish.jaxb:jaxb-runtime:2.3.1:jaxb-runtime-2.3.1.jar:45fecfa5c8217ce1f3652ab95179790ec8cc0dec0384bca51cbeb94a293d9f2f', 'org.glassfish.jaxb:jaxb-runtime:2.3.1:jaxb-runtime-2.3.1.jar:45fecfa5c8217ce1f3652ab95179790ec8cc0dec0384bca51cbeb94a293d9f2f',
'org.glassfish.jaxb:txw2:2.3.1:txw2-2.3.1.jar:34975dde1c6920f1a39791142235689bc3cd357e24d05edd8ff93b885bd68d60', 'org.glassfish.jaxb:txw2:2.3.1:txw2-2.3.1.jar:34975dde1c6920f1a39791142235689bc3cd357e24d05edd8ff93b885bd68d60',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
'org.jetbrains.kotlin:kotlin-reflect:1.3.72:kotlin-reflect-1.3.72.jar:a188d9367de1c4ee9479db630985c0597b20709c83161b1430d24edb27e38c40', 'org.jetbrains.kotlin:kotlin-reflect:1.3.72:kotlin-reflect-1.3.72.jar:a188d9367de1c4ee9479db630985c0597b20709c83161b1430d24edb27e38c40',
'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72:kotlin-stdlib-common-1.3.72.jar:5e7d1552863e480c1628b1cc39ce230ef829f5b7230106215a05acda5172203a', 'org.jetbrains.kotlin:kotlin-stdlib-common:1.3.72:kotlin-stdlib-common-1.3.72.jar:5e7d1552863e480c1628b1cc39ce230ef829f5b7230106215a05acda5172203a',
'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72:kotlin-stdlib-jdk7-1.3.72.jar:40566c0c08d414b9413ba556ff7f8a0b04b98b9f0f424d122dd2088510efccc4', 'org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.3.72:kotlin-stdlib-jdk7-1.3.72.jar:40566c0c08d414b9413ba556ff7f8a0b04b98b9f0f424d122dd2088510efccc4',
@@ -96,18 +94,17 @@ dependencyVerification {
'org.jetbrains.kotlin:kotlin-stdlib:1.3.72:kotlin-stdlib-1.3.72.jar:3856a7349ebacd6d1be6802b2fed9c4dc2c5a564ea92b6b945ac988243d4b16b', 'org.jetbrains.kotlin:kotlin-stdlib:1.3.72:kotlin-stdlib-1.3.72.jar:3856a7349ebacd6d1be6802b2fed9c4dc2c5a564ea92b6b945ac988243d4b16b',
'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7', 'org.jetbrains.trove4j:trove4j:20160824:trove4j-20160824.jar:1917871c8deb468307a584680c87a44572f5a8b0b98c6d397fc0f5f86596dbe7',
'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478', 'org.jetbrains:annotations:13.0:annotations-13.0.jar:ace2a10dc8e2d5fd34925ecac03e4988b2c0f851650c94b8cef49ba1bd111478',
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b', 'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd', 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04', 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
'org.jvnet.staxex:stax-ex:1.8:stax-ex-1.8.jar:95b05d9590af4154c6513b9c5dc1fb2e55b539972ba0a9ef28e9a0c01d83ad77', 'org.jvnet.staxex:stax-ex:1.8:stax-ex-1.8.jar:95b05d9590af4154c6513b9c5dc1fb2e55b539972ba0a9ef28e9a0c01d83ad77',
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984', 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474', 'org.ow2.asm:asm-analysis:7.0:asm-analysis-7.0.jar:e981f8f650c4d900bb033650b18e122fa6b161eadd5f88978d08751f72ee8474',
'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d', 'org.ow2.asm:asm-commons:7.0:asm-commons-7.0.jar:fed348ef05958e3e846a3ac074a12af5f7936ef3d21ce44a62c4fa08a771927d',
'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c', 'org.ow2.asm:asm-tree:7.0:asm-tree-7.0.jar:cfd7a0874f9de36a999c127feeadfbfe6e04d4a71ee954d7af3d853f0be48a6c',
'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145', 'org.ow2.asm:asm-util:7.0:asm-util-7.0.jar:75fbbca440ef463f41c2b0ab1a80abe67e910ac486da60a7863cbcb5bae7e145',
'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf', 'org.ow2.asm:asm:7.0:asm-7.0.jar:b88ef66468b3c978ad0c97fd6e90979e56155b4ac69089ba7a44e9aa7ffe9acf',
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
] ]
} }

View File

@@ -10,9 +10,10 @@ dependencies {
implementation "com.google.dagger:dagger:2.24" implementation "com.google.dagger:dagger:2.24"
implementation 'com.google.code.findbugs:jsr305:3.0.2' implementation 'com.google.code.findbugs:jsr305:3.0.2'
testImplementation "junit:junit:$junit_version" testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:$jmock_version" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2"
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.api.account;
import org.briarproject.bramble.api.crypto.DecryptionException; import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.identity.Identity;
import org.briarproject.bramble.api.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@@ -44,17 +43,6 @@ public interface AccountManager {
*/ */
boolean createAccount(String name, String password); boolean createAccount(String name, String password);
/**
* Restores a given identity by registering it with the
* {@link IdentityManager}. Creates a database key, encrypts it with the
* given password and stores it on disk. {@link #accountExists()} will
* return true after this method returns true.
* @param identity
* @param password
* @return
*/
boolean restoreAccount(Identity identity, String password);
/** /**
* Deletes all account state from disk. {@link #accountExists()} will * Deletes all account state from disk. {@link #accountExists()} will
* return false after this method returns. * return false after this method returns.

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api.contact;
/**
* Record types for the contact exchange protocol.
*/
public interface ContactExchangeRecordTypes {
byte CONTACT_INFO = 0;
}

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.api.contact;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.UnsupportedVersionException; import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.ContactExistsException; import org.briarproject.bramble.api.db.ContactExistsException;
import org.briarproject.bramble.api.db.DbException; import org.briarproject.bramble.api.db.DbException;
@@ -49,10 +48,6 @@ public interface ContactManager {
SecretKey rootKey, long timestamp, boolean alice, boolean verified, SecretKey rootKey, long timestamp, boolean alice, boolean verified,
boolean active) throws DbException; boolean active) throws DbException;
ContactId addContact(Transaction txn, Author remote, AuthorId local,
PublicKey handshake, boolean verified)
throws DbException, GeneralSecurityException;
/** /**
* Stores a contact associated with the given local and remote pseudonyms, * Stores a contact associated with the given local and remote pseudonyms,
* replacing the given pending contact, derives and stores handshake mode * replacing the given pending contact, derives and stores handshake mode
@@ -210,19 +205,6 @@ public interface ContactManager {
void setContactAlias(ContactId c, @Nullable String alias) void setContactAlias(ContactId c, @Nullable String alias)
throws DbException; throws DbException;
/**
* Sets the contact's handshake public key
*/
void setHandshakePublicKey(Transaction txn, ContactId c,
PublicKey handshakePublicKey) throws DbException,
GeneralSecurityException;
/**
* Sets the contact's handshake public key
*/
void setHandshakePublicKey(ContactId c, PublicKey handshakePublicKey)
throws DbException, GeneralSecurityException;
/** /**
* Returns true if a contact with this {@code remoteAuthorId} belongs to * Returns true if a contact with this {@code remoteAuthorId} belongs to
* the local pseudonym with this {@code localAuthorId}. * the local pseudonym with this {@code localAuthorId}.

View File

@@ -24,19 +24,6 @@ public interface HandshakeManager {
HandshakeResult handshake(PendingContactId p, InputStream in, HandshakeResult handshake(PendingContactId p, InputStream in,
StreamWriter out) throws DbException, IOException; StreamWriter out) throws DbException, IOException;
/**
* Handshakes with the given contact. Returns an ephemeral master key
* authenticated with both parties' handshake key pairs and a flag
* indicating whether the local peer is Alice or Bob.
*
* @param in An incoming stream for the handshake, which must be secured in
* handshake mode
* @param out An outgoing stream for the handshake, which must be secured
* in handshake mode
*/
HandshakeResult handshake(ContactId c, InputStream in, StreamWriter out)
throws DbException, IOException;
class HandshakeResult { class HandshakeResult {
private final SecretKey masterKey; private final SecretKey masterKey;

View File

@@ -546,11 +546,6 @@ public interface DatabaseComponent extends TransactionManager {
void setContactAlias(Transaction txn, ContactId c, @Nullable String alias) void setContactAlias(Transaction txn, ContactId c, @Nullable String alias)
throws DbException; throws DbException;
/**
* Sets the remote handshake public key for a given contact
*/
void setHandshakePublicKey(Transaction txn, ContactId c, PublicKey handshakePublicKey) throws DbException;
/** /**
* Sets the given group's visibility to the given contact. * Sets the given group's visibility to the given contact.
*/ */

View File

@@ -26,11 +26,6 @@ public interface IdentityManager {
*/ */
void registerIdentity(Identity i); void registerIdentity(Identity i);
/**
* Returns the cached local identity or loads it from the database.
*/
Identity getIdentity(Transaction txn) throws DbException;
/** /**
* Returns the cached local identity or loads it from the database. * Returns the cached local identity or loads it from the database.
*/ */

View File

@@ -74,13 +74,6 @@ public interface TransportPropertyManager {
TransportProperties getRemoteProperties(ContactId c, TransportId t) TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException; throws DbException;
/**
* Returns the remote transport properties for the given contact and
* transport.
*/
TransportProperties getRemoteProperties(Transaction txn, ContactId c,
TransportId t) throws DbException;
/** /**
* Merges the given properties with the existing local properties for the * Merges the given properties with the existing local properties for the
* given transport. * given transport.

View File

@@ -35,20 +35,6 @@ public interface KeyManager {
ContactId c, SecretKey rootKey, long timestamp, boolean alice, ContactId c, SecretKey rootKey, long timestamp, boolean alice,
boolean active) throws DbException; boolean active) throws DbException;
/**
* Derives and stores a set of rotation mode transport keys for
* communicating with the given contact over each transport and returns the
* key set IDs.
* <p/>
* {@link StreamContext StreamContexts} for the contact can be created
* after this method has returned.
*
* @param alice True if the local party is Alice
* @param active Whether the derived keys can be used for outgoing streams
*/
Map<TransportId, KeySetId> addRotationKeys(ContactId c, SecretKey rootKey,
long timestamp, boolean alice, boolean active) throws DbException;
/** /**
* Informs the key manager that a new contact has been added. Derives and * Informs the key manager that a new contact has been added. Derives and
* stores a set of handshake mode transport keys for communicating with the * stores a set of handshake mode transport keys for communicating with the

View File

@@ -1,27 +1,24 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64', 'com.google.dagger:dagger:2.24:dagger-2.24.jar:550a6e46a6dfcdf1d764887b6090cea94f783327e50e5c73754f18facfc70b64',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0', 'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047', 'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619', 'org.codehaus.mojo.signature:java16:1.1:java16-1.1.signature:53799223a2c98dba2d0add810bed76315460df285c69e4f397ae6098f87dd619',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90', 'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52', 'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09', 'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b', 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd', 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04', 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be',
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d', 'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de', 'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
] ]
} }

View File

@@ -21,25 +21,16 @@ dependencies {
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6 testImplementation 'org.hsqldb:hsqldb:2.3.5' // The last version that supports Java 1.6
testImplementation 'net.jodah:concurrentunit:0.4.2' testImplementation 'junit:junit:4.12'
testImplementation "junit:junit:$junit_version" testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-junit4:$jmock_version" testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.jmock:jmock-imposters:$jmock_version"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24'
signature 'org.codehaus.mojo.signature:java16:1.1@signature' signature 'org.codehaus.mojo.signature:java16:1.1@signature'
} }
animalsniffer {
// Allow requireNonNull: Android desugaring rewrites it (so it's safe for us to use),
// and it gets used when passing method references instead of lambdas with Java 11.
// Note that this line allows *all* methods from java.util.Objects.
// That's the best that we can do with the configuration options that Animal Sniffer offers.
ignore 'java.util.Objects'
}
// needed to make test output available to bramble-java // needed to make test output available to bramble-java
configurations { configurations {
testOutput.extendsFrom(testCompile) testOutput.extendsFrom(testCompile)

View File

@@ -176,18 +176,6 @@ class AccountManagerImpl implements AccountManager {
} }
} }
public boolean restoreAccount(Identity identity, String password) {
synchronized (stateChangeLock) {
if (hasDatabaseKey())
throw new AssertionError("Already have a database key");
identityManager.registerIdentity(identity);
SecretKey key = crypto.generateSecretKey();
if (!encryptAndStoreDatabaseKey(key, password)) return false;
databaseKey = key;
return true;
}
}
@GuardedBy("stateChangeLock") @GuardedBy("stateChangeLock")
private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) { private boolean encryptAndStoreDatabaseKey(SecretKey key, String password) {
byte[] plaintext = key.getBytes(); byte[] plaintext = key.getBytes();

View File

@@ -76,7 +76,7 @@ class ConnectionManagerImpl implements ConnectionManager {
ioExecutor.execute(new IncomingDuplexSyncConnection(keyManager, ioExecutor.execute(new IncomingDuplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory, connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, ioExecutor, syncSessionFactory, transportPropertyManager, ioExecutor,
t, d, handshakeManager)); t, d));
} }
@Override @Override
@@ -101,7 +101,7 @@ class ConnectionManagerImpl implements ConnectionManager {
ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager, ioExecutor.execute(new OutgoingDuplexSyncConnection(keyManager,
connectionRegistry, streamReaderFactory, streamWriterFactory, connectionRegistry, streamReaderFactory, streamWriterFactory,
syncSessionFactory, transportPropertyManager, ioExecutor, syncSessionFactory, transportPropertyManager, ioExecutor,
secureRandom, handshakeManager, c, t, d)); secureRandom, c, t, d));
} }
@Override @Override

View File

@@ -3,7 +3,6 @@ package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry; import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.connection.InterruptibleConnection; import org.briarproject.bramble.api.connection.InterruptibleConnection;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.TransportConnectionReader; import org.briarproject.bramble.api.plugin.TransportConnectionReader;
import org.briarproject.bramble.api.plugin.TransportConnectionWriter; import org.briarproject.bramble.api.plugin.TransportConnectionWriter;

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry; import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
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.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -14,11 +13,9 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory; import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
import static java.util.logging.Level.WARNING; import static java.util.logging.Level.WARNING;
@@ -27,10 +24,6 @@ import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault @NotNullByDefault
class IncomingDuplexSyncConnection extends DuplexSyncConnection class IncomingDuplexSyncConnection extends DuplexSyncConnection
implements Runnable { implements Runnable {
private final HandshakeManager handshakeManager;
// FIXME: Exchange timestamp as part of handshake protocol?
private static final long TIMESTAMP = 1617235200; // 1 April 2021 00:00 UTC
IncomingDuplexSyncConnection(KeyManager keyManager, IncomingDuplexSyncConnection(KeyManager keyManager,
ConnectionRegistry connectionRegistry, ConnectionRegistry connectionRegistry,
@@ -39,18 +32,14 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
SyncSessionFactory syncSessionFactory, SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
Executor ioExecutor, TransportId transportId, Executor ioExecutor, TransportId transportId,
DuplexTransportConnection connection, DuplexTransportConnection connection) {
HandshakeManager handshakeManager) {
super(keyManager, connectionRegistry, streamReaderFactory, super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory, streamWriterFactory, syncSessionFactory,
transportPropertyManager, ioExecutor, transportId, connection); transportPropertyManager, ioExecutor, transportId, connection);
this.handshakeManager = handshakeManager;
} }
@Override @Override
public void run() { public void run() {
LOG.info("Running IncomingDuplexSyncConnection on transport " +
transportId.getString());
// Read and recognise the tag // Read and recognise the tag
StreamContext ctx = recogniseTag(reader, transportId); StreamContext ctx = recogniseTag(reader, transportId);
if (ctx == null) { if (ctx == null) {
@@ -65,22 +54,10 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
return; return;
} }
if (ctx.isHandshakeMode()) { if (ctx.isHandshakeMode()) {
if (!performHandshake(ctx, contactId)) { // TODO: Support handshake mode for contacts
LOG.warning("Handshake failed"); LOG.warning("Received handshake tag, expected rotation mode");
return; onReadError(true);
} return;
// Allocate a rotation mode stream context
ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
if (ctx.isHandshakeMode()) {
LOG.warning("Got handshake mode context after handshaking");
onWriteError();
return;
}
} }
connectionRegistry.registerIncomingConnection(contactId, transportId, connectionRegistry.registerIncomingConnection(contactId, transportId,
this); this);
@@ -126,35 +103,5 @@ class IncomingDuplexSyncConnection extends DuplexSyncConnection
onWriteError(); onWriteError();
} }
} }
private boolean performHandshake(StreamContext ctxIn, ContactId contactId) {
LOG.info("Performing handshake (Incoming)");
// Allocate the outgoing stream context
StreamContext ctxOut =
allocateStreamContext(contactId, transportId);
if (ctxOut == null) {
LOG.warning("Could not allocate stream context");
onReadError(true);
return false;
}
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
// Flush the output stream to send the outgoing stream header
StreamWriter out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
HandshakeManager.HandshakeResult result =
handshakeManager.handshake(contactId, in, out);
keyManager.addRotationKeys(contactId, result.getMasterKey(),
TIMESTAMP, result.isAlice(), true);
LOG.info("Rotation keys added");
return true;
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onReadError(true);
return false;
}
}
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.bramble.connection;
import org.briarproject.bramble.api.connection.ConnectionRegistry; import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.HandshakeManager;
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.plugin.TransportId; import org.briarproject.bramble.api.plugin.TransportId;
@@ -15,11 +14,9 @@ import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager; import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext; import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory; import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory; import org.briarproject.bramble.api.transport.StreamWriterFactory;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -31,38 +28,26 @@ import static org.briarproject.bramble.util.LogUtils.logException;
class OutgoingDuplexSyncConnection extends DuplexSyncConnection class OutgoingDuplexSyncConnection extends DuplexSyncConnection
implements Runnable { implements Runnable {
// FIXME: Exchange timestamp as part of handshake protocol?
private static final long TIMESTAMP = 1617235200; // 1 April 2021 00:00 UTC
private final SecureRandom secureRandom; private final SecureRandom secureRandom;
private final HandshakeManager handshakeManager;
private final ContactId contactId; private final ContactId contactId;
OutgoingDuplexSyncConnection( OutgoingDuplexSyncConnection(KeyManager keyManager,
KeyManager keyManager,
ConnectionRegistry connectionRegistry, ConnectionRegistry connectionRegistry,
StreamReaderFactory streamReaderFactory, StreamReaderFactory streamReaderFactory,
StreamWriterFactory streamWriterFactory, StreamWriterFactory streamWriterFactory,
SyncSessionFactory syncSessionFactory, SyncSessionFactory syncSessionFactory,
TransportPropertyManager transportPropertyManager, TransportPropertyManager transportPropertyManager,
Executor ioExecutor, Executor ioExecutor, SecureRandom secureRandom, ContactId contactId,
SecureRandom secureRandom, TransportId transportId, DuplexTransportConnection connection) {
HandshakeManager handshakeManager,
ContactId contactId,
TransportId transportId,
DuplexTransportConnection connection) {
super(keyManager, connectionRegistry, streamReaderFactory, super(keyManager, connectionRegistry, streamReaderFactory,
streamWriterFactory, syncSessionFactory, streamWriterFactory, syncSessionFactory,
transportPropertyManager, ioExecutor, transportId, connection); transportPropertyManager, ioExecutor, transportId, connection);
this.secureRandom = secureRandom; this.secureRandom = secureRandom;
this.handshakeManager = handshakeManager;
this.contactId = contactId; this.contactId = contactId;
} }
@Override @Override
public void run() { public void run() {
LOG.info("Running OutgoingDuplexSyncConnection on transport " +
transportId.getString());
// Allocate a stream context // Allocate a stream context
StreamContext ctx = allocateStreamContext(contactId, transportId); StreamContext ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) { if (ctx == null) {
@@ -71,22 +56,10 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection
return; return;
} }
if (ctx.isHandshakeMode()) { if (ctx.isHandshakeMode()) {
if (!performHandshake(ctx)) { // TODO: Support handshake mode for contacts
LOG.warning("Handshake failed"); LOG.warning("Cannot use handshake mode stream context");
return; onWriteError();
} return;
// Allocate a rotation mode stream context
ctx = allocateStreamContext(contactId, transportId);
if (ctx == null) {
LOG.warning("Could not allocate stream context");
onWriteError();
return;
}
if (ctx.isHandshakeMode()) {
LOG.warning("Got handshake mode context after handshaking");
onWriteError();
return;
}
} }
// Start the incoming session on another thread // Start the incoming session on another thread
Priority priority = generatePriority(); Priority priority = generatePriority();
@@ -154,59 +127,6 @@ class OutgoingDuplexSyncConnection extends DuplexSyncConnection
} }
} }
private boolean performHandshake(StreamContext ctxOut) {
LOG.info("Performing handshake (Outgoing)");
// Flush the output stream to send the outgoing stream header
StreamWriter out;
try {
out = streamWriterFactory.createStreamWriter(
writer.getOutputStream(), ctxOut);
out.getOutputStream().flush();
} catch (IOException e) {
logException(LOG, WARNING, e);
onWriteError();
return false;
}
// Read and recognise the tag
StreamContext ctxIn = recogniseTag(reader, transportId);
// Unrecognised tags are suspicious in this case
if (ctxIn == null) {
LOG.warning("Unrecognised tag for returning stream");
onReadError();
return false;
}
// Check that the stream comes from the expected contact
ContactId inContactId = ctxIn.getContactId();
if (contactId == null) {
LOG.warning("Expected contact tag, got rendezvous tag");
onReadError();
return false;
}
if (!inContactId.equals(contactId)) {
LOG.warning("Wrong contact ID for returning stream");
onReadError();
return false;
}
// TODO: Register the connection, close it if it's redundant
// Handshake and exchange contacts
try {
InputStream in = streamReaderFactory.createStreamReader(
reader.getInputStream(), ctxIn);
HandshakeManager.HandshakeResult result =
handshakeManager.handshake(contactId, in, out);
keyManager.addRotationKeys(contactId, result.getMasterKey(),
TIMESTAMP, result.isAlice(), true);
LOG.info("Rotation keys added");
return true;
} catch (IOException | DbException e) {
logException(LOG, WARNING, e);
onWriteError();
return false;
} finally {
// TODO: Unregister the connection
}
}
private void onReadError() { private void onReadError() {
// 'Recognised' is always true for outgoing connections // 'Recognised' is always true for outgoing connections
onReadError(true); onReadError(true);

View File

@@ -1,6 +1,6 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.contact;
public interface ContactExchangeConstants { interface ContactExchangeConstants {
/** /**
* The current version of the contact exchange protocol. * The current version of the contact exchange protocol.

View File

@@ -1,4 +1,4 @@
package org.briarproject.bramble.api.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
@@ -6,7 +6,7 @@ import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault @NotNullByDefault
public interface ContactExchangeCrypto { interface ContactExchangeCrypto {
/** /**
* Derives the header key for a contact exchange stream from the master key. * Derives the header key for a contact exchange stream from the master key.

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.contact.ContactExchangeCrypto;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PrivateKey; import org.briarproject.bramble.api.crypto.PrivateKey;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
@@ -11,12 +10,12 @@ import java.security.GeneralSecurityException;
import javax.inject.Inject; import javax.inject.Inject;
import static org.briarproject.bramble.api.contact.ContactExchangeConstants.ALICE_KEY_LABEL; import static org.briarproject.bramble.contact.ContactExchangeConstants.ALICE_KEY_LABEL;
import static org.briarproject.bramble.api.contact.ContactExchangeConstants.ALICE_NONCE_LABEL; import static org.briarproject.bramble.contact.ContactExchangeConstants.ALICE_NONCE_LABEL;
import static org.briarproject.bramble.api.contact.ContactExchangeConstants.BOB_KEY_LABEL; import static org.briarproject.bramble.contact.ContactExchangeConstants.BOB_KEY_LABEL;
import static org.briarproject.bramble.api.contact.ContactExchangeConstants.BOB_NONCE_LABEL; import static org.briarproject.bramble.contact.ContactExchangeConstants.BOB_NONCE_LABEL;
import static org.briarproject.bramble.api.contact.ContactExchangeConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.contact.ContactExchangeConstants.SIGNING_LABEL; import static org.briarproject.bramble.contact.ContactExchangeConstants.SIGNING_LABEL;
@NotNullByDefault @NotNullByDefault
class ContactExchangeCryptoImpl implements ContactExchangeCrypto { class ContactExchangeCryptoImpl implements ContactExchangeCrypto {

View File

@@ -4,7 +4,6 @@ import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Predicate; import org.briarproject.bramble.api.Predicate;
import org.briarproject.bramble.api.client.ClientHelper; import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactExchangeCrypto;
import org.briarproject.bramble.api.contact.ContactExchangeManager; import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
@@ -48,8 +47,8 @@ import javax.inject.Inject;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH; import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_SIGNATURE_LENGTH;
import static org.briarproject.bramble.api.contact.ContactExchangeConstants.PROTOCOL_VERSION; import static org.briarproject.bramble.contact.ContactExchangeConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.contact.ContactExchangeRecordTypes.CONTACT_INFO; import static org.briarproject.bramble.contact.ContactExchangeRecordTypes.CONTACT_INFO;
import static org.briarproject.bramble.util.ValidationUtils.checkLength; import static org.briarproject.bramble.util.ValidationUtils.checkLength;
import static org.briarproject.bramble.util.ValidationUtils.checkSize; import static org.briarproject.bramble.util.ValidationUtils.checkSize;
@@ -82,8 +81,7 @@ class ContactExchangeManagerImpl implements ContactExchangeManager {
private final ContactManager contactManager; private final ContactManager contactManager;
private final IdentityManager identityManager; private final IdentityManager identityManager;
private final TransportPropertyManager transportPropertyManager; private final TransportPropertyManager transportPropertyManager;
private final org.briarproject.bramble.api.contact.ContactExchangeCrypto private final ContactExchangeCrypto contactExchangeCrypto;
contactExchangeCrypto;
private final StreamReaderFactory streamReaderFactory; private final StreamReaderFactory streamReaderFactory;
private final StreamWriterFactory streamWriterFactory; private final StreamWriterFactory streamWriterFactory;

View File

@@ -0,0 +1,9 @@
package org.briarproject.bramble.contact;
/**
* Record types for the contact exchange protocol.
*/
interface ContactExchangeRecordTypes {
byte CONTACT_INFO = 0;
}

View File

@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.contact.PendingContact;
import org.briarproject.bramble.api.contact.PendingContactId; import org.briarproject.bramble.api.contact.PendingContactId;
import org.briarproject.bramble.api.contact.PendingContactState; import org.briarproject.bramble.api.contact.PendingContactState;
import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent; import org.briarproject.bramble.api.contact.event.PendingContactStateChangedEvent;
import org.briarproject.bramble.api.crypto.CryptoConstants;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;
import org.briarproject.bramble.api.crypto.PublicKey; import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
@@ -120,18 +119,6 @@ class ContactManagerImpl implements ContactManager, EventListener {
verified, active)); verified, active));
} }
@Override
public ContactId addContact(Transaction txn, Author remote, AuthorId local,
PublicKey handshake, boolean verified)
throws DbException, GeneralSecurityException {
ContactId c = db.addContact(txn, remote, local, handshake, verified);
Contact contact = db.getContact(txn, c);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addContact(txn, c, handshake, ourKeyPair);
for (ContactHook hook : hooks) hook.addingContact(txn, contact);
return c;
}
@Override @Override
public String getHandshakeLink() throws DbException { public String getHandshakeLink() throws DbException {
KeyPair keyPair = db.transactionWithResult(true, KeyPair keyPair = db.transactionWithResult(true,
@@ -247,25 +234,6 @@ class ContactManagerImpl implements ContactManager, EventListener {
db.transaction(false, txn -> setContactAlias(txn, c, alias)); db.transaction(false, txn -> setContactAlias(txn, c, alias));
} }
@Override
public void setHandshakePublicKey(Transaction txn, ContactId c,
PublicKey handshakePublicKey) throws DbException, GeneralSecurityException {
if (handshakePublicKey.getKeyType() !=
CryptoConstants.KEY_TYPE_AGREEMENT) {
throw new IllegalArgumentException();
}
db.setHandshakePublicKey(txn, c, handshakePublicKey);
KeyPair ourKeyPair = identityManager.getHandshakeKeys(txn);
keyManager.addContact(txn, c, handshakePublicKey, ourKeyPair);
}
@Override
public void setHandshakePublicKey(ContactId c, PublicKey handshakePublicKey)
throws DbException, GeneralSecurityException {
db.transaction(false,
txn -> setHandshakePublicKey(txn, c, handshakePublicKey));
}
@Override @Override
public boolean contactExists(Transaction txn, AuthorId remoteAuthorId, public boolean contactExists(Transaction txn, AuthorId remoteAuthorId,
AuthorId localAuthorId) throws DbException { AuthorId localAuthorId) throws DbException {

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.contact; package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.contact.ContactExchangeCrypto;
import org.briarproject.bramble.api.contact.ContactExchangeManager; import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.HandshakeManager; import org.briarproject.bramble.api.contact.HandshakeManager;

View File

@@ -3,8 +3,6 @@ package org.briarproject.bramble.contact;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.Pair; import org.briarproject.bramble.api.Pair;
import org.briarproject.bramble.api.Predicate; import org.briarproject.bramble.api.Predicate;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.HandshakeManager; import org.briarproject.bramble.api.contact.HandshakeManager;
import org.briarproject.bramble.api.contact.PendingContact; import org.briarproject.bramble.api.contact.PendingContact;
@@ -90,27 +88,6 @@ class HandshakeManagerImpl implements HandshakeManager {
}); });
PublicKey theirStaticPublicKey = keys.getFirst(); PublicKey theirStaticPublicKey = keys.getFirst();
KeyPair ourStaticKeyPair = keys.getSecond(); KeyPair ourStaticKeyPair = keys.getSecond();
return handshake(theirStaticPublicKey, ourStaticKeyPair, in, out);
}
@Override
public HandshakeResult handshake(ContactId c, InputStream in,
StreamWriter out) throws DbException, IOException {
Pair<PublicKey, KeyPair> keys = db.transactionWithResult(true, txn -> {
Contact contact = contactManager.getContact(txn, c);
PublicKey handshakePublicKey = contact.getHandshakePublicKey();
if (handshakePublicKey == null) throw new DbException();
KeyPair keyPair = identityManager.getHandshakeKeys(txn);
return new Pair<>(handshakePublicKey, keyPair);
});
PublicKey theirStaticPublicKey = keys.getFirst();
KeyPair ourStaticKeyPair = keys.getSecond();
return handshake(theirStaticPublicKey, ourStaticKeyPair, in, out);
}
private HandshakeResult handshake(PublicKey theirStaticPublicKey,
KeyPair ourStaticKeyPair, InputStream in, StreamWriter out)
throws IOException {
boolean alice = transportCrypto.isAlice(theirStaticPublicKey, boolean alice = transportCrypto.isAlice(theirStaticPublicKey,
ourStaticKeyPair); ourStaticKeyPair);
RecordReader recordReader = recordReaderFactory.createRecordReader(in); RecordReader recordReader = recordReaderFactory.createRecordReader(in);

View File

@@ -1,11 +1,12 @@
package org.briarproject.bramble.api.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@NotNullByDefault @NotNullByDefault
public interface AuthenticatedCipher { interface AuthenticatedCipher {
/** /**
* Initializes this cipher for encryption or decryption with a key and an * Initializes this cipher for encryption or decryption with a key and an

View File

@@ -6,7 +6,6 @@ import net.i2p.crypto.eddsa.KeyPairGenerator;
import org.briarproject.bramble.api.crypto.AgreementPrivateKey; import org.briarproject.bramble.api.crypto.AgreementPrivateKey;
import org.briarproject.bramble.api.crypto.AgreementPublicKey; import org.briarproject.bramble.api.crypto.AgreementPublicKey;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.DecryptionException; import org.briarproject.bramble.api.crypto.DecryptionException;
import org.briarproject.bramble.api.crypto.KeyPair; import org.briarproject.bramble.api.crypto.KeyPair;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.KeyAgreementCrypto; import org.briarproject.bramble.api.crypto.KeyAgreementCrypto;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamDecrypter; import org.briarproject.bramble.api.crypto.StreamDecrypter;
import org.briarproject.bramble.api.crypto.StreamDecrypterFactory; import org.briarproject.bramble.api.crypto.StreamDecrypterFactory;

View File

@@ -1,7 +1,6 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.FormatException; import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamDecrypter; import org.briarproject.bramble.api.crypto.StreamDecrypter;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.CryptoComponent; import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamEncrypter; import org.briarproject.bramble.api.crypto.StreamEncrypter;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.crypto.StreamEncrypter; import org.briarproject.bramble.api.crypto.StreamEncrypter;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.spongycastle.crypto.DataLengthException; import org.spongycastle.crypto.DataLengthException;

View File

@@ -32,7 +32,6 @@ import org.briarproject.bramble.api.transport.KeySetId;
import org.briarproject.bramble.api.transport.TransportKeySet; import org.briarproject.bramble.api.transport.TransportKeySet;
import org.briarproject.bramble.api.transport.TransportKeys; import org.briarproject.bramble.api.transport.TransportKeys;
import java.sql.Connection;
import java.util.Collection; import java.util.Collection;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@@ -696,11 +695,6 @@ interface Database<T> {
void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey, void setHandshakeKeyPair(T txn, AuthorId local, PublicKey publicKey,
PrivateKey privateKey) throws DbException; PrivateKey privateKey) throws DbException;
/**
* Sets the handshake public key for a given contact
*/
void setHandshakePublicKey(T txn, ContactId c, PublicKey handshakePublicKey) throws DbException;
/** /**
* Marks the given message as permanent, i.e. not temporary. * Marks the given message as permanent, i.e. not temporary.
*/ */

View File

@@ -1040,15 +1040,6 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
} }
} }
@Override
public void setHandshakePublicKey(Transaction transaction, ContactId c, PublicKey handshakePublicKey) throws DbException {
if (transaction.isReadOnly()) throw new IllegalArgumentException();
T txn = unbox(transaction);
if (!db.containsContact(txn, c))
throw new NoSuchContactException();
db.setHandshakePublicKey(txn, c, handshakePublicKey);
}
@Override @Override
public void setHandshakeKeyPair(Transaction transaction, AuthorId local, public void setHandshakeKeyPair(Transaction transaction, AuthorId local,
PublicKey publicKey, PrivateKey privateKey) throws DbException { PublicKey publicKey, PrivateKey privateKey) throws DbException {

View File

@@ -3058,23 +3058,6 @@ abstract class JdbcDatabase implements Database<Connection> {
} }
} }
@Override
public void setHandshakePublicKey(Connection txn, ContactId c, PublicKey handshakePublicKey) throws DbException {
PreparedStatement ps = null;
try {
String sql = "UPDATE contacts SET handshakePublicKey = ? WHERE contactId = ?";
ps = txn.prepareStatement(sql);
ps.setBytes(1, handshakePublicKey.getEncoded());
ps.setInt(2, c.getInt());
int affected = ps.executeUpdate();
if (affected < 0 || affected > 1) throw new DbStateException();
ps.close();
} catch (SQLException e) {
tryToClose(ps, LOG, WARNING);
throw new DbException(e);
}
}
@Override @Override
public void setGroupVisibility(Connection txn, ContactId c, GroupId g, public void setGroupVisibility(Connection txn, ContactId c, GroupId g,
boolean shared) throws DbException { boolean shared) throws DbException {

View File

@@ -118,11 +118,6 @@ class IdentityManagerImpl implements IdentityManager, OpenDatabaseHook {
return cached.getLocalAuthor(); return cached.getLocalAuthor();
} }
@Override
public Identity getIdentity(Transaction txn) throws DbException {
return getCachedIdentity(txn);
}
@Override @Override
public LocalAuthor getLocalAuthor(Transaction txn) throws DbException { public LocalAuthor getLocalAuthor(Transaction txn) throws DbException {
return getCachedIdentity(txn).getLocalAuthor(); return getCachedIdentity(txn).getLocalAuthor();

View File

@@ -127,7 +127,6 @@ class KeyAgreementConnector {
List<Pair<DuplexPlugin, BdfList>> transports = new ArrayList<>(); List<Pair<DuplexPlugin, BdfList>> transports = new ArrayList<>();
for (TransportId id : PREFERRED_TRANSPORTS) { for (TransportId id : PREFERRED_TRANSPORTS) {
TransportDescriptor d = descriptors.get(id); TransportDescriptor d = descriptors.get(id);
LOG.info("id: " + id + " d: " + d);
Plugin p = pluginManager.getPlugin(id); Plugin p = pluginManager.getPlugin(id);
if (d != null && p instanceof DuplexPlugin) { if (d != null && p instanceof DuplexPlugin) {
if (LOG.isLoggable(INFO)) if (LOG.isLoggable(INFO))
@@ -138,9 +137,6 @@ class KeyAgreementConnector {
// TODO: If we don't have any transports in common with the peer, // TODO: If we don't have any transports in common with the peer,
// warn the user and give up (#1224) // warn the user and give up (#1224)
if (transports.isEmpty()) {
LOG.info("No transports found");
}
if (!transports.isEmpty()) { if (!transports.isEmpty()) {
byte[] commitment = remotePayload.getCommitment(); byte[] commitment = remotePayload.getCommitment();

View File

@@ -34,12 +34,12 @@ class KeyAgreementTransport {
Logger.getLogger(KeyAgreementTransport.class.getName()); Logger.getLogger(KeyAgreementTransport.class.getName());
// Accept records with current protocol version, known record type // Accept records with current protocol version, known record type
private static final Predicate<Record> ACCEPT = r -> private static Predicate<Record> ACCEPT = r ->
r.getProtocolVersion() == PROTOCOL_VERSION && r.getProtocolVersion() == PROTOCOL_VERSION &&
isKnownRecordType(r.getRecordType()); isKnownRecordType(r.getRecordType());
// Ignore records with current protocol version, unknown record type // Ignore records with current protocol version, unknown record type
private static final Predicate<Record> IGNORE = r -> private static Predicate<Record> IGNORE = r ->
r.getProtocolVersion() == PROTOCOL_VERSION && r.getProtocolVersion() == PROTOCOL_VERSION &&
!isKnownRecordType(r.getRecordType()); !isKnownRecordType(r.getRecordType());

View File

@@ -294,13 +294,7 @@ class TransportPropertyManagerImpl implements TransportPropertyManager,
public TransportProperties getRemoteProperties(ContactId c, TransportId t) public TransportProperties getRemoteProperties(ContactId c, TransportId t)
throws DbException { throws DbException {
return db.transactionWithResult(true, txn -> return db.transactionWithResult(true, txn ->
getRemoteProperties(txn, c, t)); getRemoteProperties(txn, db.getContact(txn, c), t));
}
@Override
public TransportProperties getRemoteProperties(Transaction txn,
ContactId c, TransportId t) throws DbException {
return getRemoteProperties(txn, db.getContact(txn, c), t);
} }
@Override @Override

View File

@@ -101,9 +101,9 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
} }
@Override @Override
public Map<TransportId, KeySetId> addRotationKeys(Transaction txn, public Map<TransportId, KeySetId> addRotationKeys(
ContactId c, SecretKey rootKey, long timestamp, boolean alice, Transaction txn, ContactId c, SecretKey rootKey, long timestamp,
boolean active) throws DbException { boolean alice, boolean active) throws DbException {
Map<TransportId, KeySetId> ids = new HashMap<>(); Map<TransportId, KeySetId> ids = new HashMap<>();
for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) { for (Entry<TransportId, TransportKeyManager> e : managers.entrySet()) {
TransportId t = e.getKey(); TransportId t = e.getKey();
@@ -114,14 +114,6 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
return ids; return ids;
} }
@Override
public Map<TransportId, KeySetId> addRotationKeys(ContactId c,
SecretKey rootKey, long timestamp, boolean alice, boolean active)
throws DbException {
return db.transactionWithResult(false, txn ->
addRotationKeys(txn, c, rootKey, timestamp, alice, active));
}
@Override @Override
public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c, public Map<TransportId, KeySetId> addContact(Transaction txn, ContactId c,
PublicKey theirPublicKey, KeyPair ourKeyPair) PublicKey theirPublicKey, KeyPair ourKeyPair)
@@ -145,7 +137,7 @@ class KeyManagerImpl implements KeyManager, Service, EventListener {
PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair) PendingContactId p, PublicKey theirPublicKey, KeyPair ourKeyPair)
throws DbException, GeneralSecurityException { throws DbException, GeneralSecurityException {
SecretKey staticMasterKey = transportCrypto SecretKey staticMasterKey = transportCrypto
.deriveStaticMasterKey(theirPublicKey, ourKeyPair); .deriveStaticMasterKey(theirPublicKey, ourKeyPair);
SecretKey rootKey = SecretKey rootKey =
transportCrypto.deriveHandshakeRootKey(staticMasterKey, true); transportCrypto.deriveHandshakeRootKey(staticMasterKey, true);
boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair); boolean alice = transportCrypto.isAlice(theirPublicKey, ourKeyPair);

View File

@@ -1,8 +1,8 @@
Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1 Bridge obfs4 192.95.36.142:443 CDF2E852BF539B82BD10E27E9115A31734E378C2 cert=qUVQ0srL1JI/vO6V6m/24anYXiJD3QP2HgzUKQtQ7GRqqUvs7P+tG43RtAqdhLOALP7DJQ iat-mode=1
Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1 Bridge obfs4 38.229.1.78:80 C8CBDB2464FC9804A69531437BCF2BE31FDD2EE4 cert=Hmyfd2ev46gGY7NoVxA9ngrPF2zCZtzskRTzoWXbxNkzeVnGFPWmrTtILRyqCTjHR+s9dg iat-mode=1
Bridge obfs4 38.229.33.83:80 0BAC39417268B96B9F514E7F63FA6FBA1A788955 cert=VwEFpk9F/UN9JED7XpG1XOjm/O8ZCXK80oPecgWnNDZDv5pdkhq1OpbAH0wNqOT6H6BmRQ iat-mode=1
Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0 Bridge obfs4 37.218.245.14:38224 D9A82D2F9C2F65A18407B1D2B764F130847F8B5D cert=bjRaMrr1BRiAW8IE9U5z27fQaYgOhX1UCmOpg2pFpoMvo6ZgQMzLsaTzzQNTlm7hNcb+Sg iat-mode=0
Bridge obfs4 85.31.186.98:443 011F2599C0E9B27EE74B353155E244813763C3E5 cert=ayq0XzCwhpdysn5o0EyDUbmSOx3X/oTEbzDMvczHOdBJKlvIdHHLJGkZARtT4dcBFArPPg iat-mode=0 Bridge obfs4 85.31.186.26:443 91A6354697E6B02A386312F68D82CF86824D3606 cert=PBwr+S8JTVZo6MPdHnkTwXJPILWADLqfMGoVvhZClMq/Urndyd42BwX9YFJHZnBB3H0XCw iat-mode=0
Bridge obfs4 144.217.20.138:80 FB70B257C162BF1038CA669D568D76F5B7F0BABB cert=vYIV5MgrghGQvZPIi1tJwnzorMgqgmlKaB77Y3Z9Q/v94wZBOAXkW+fdx4aSxLVnKO+xNw iat-mode=0
Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0 Bridge obfs4 193.11.166.194:27015 2D82C2E354D531A68469ADF7F878FA6060C6BACA cert=4TLQPJrTSaDffMK7Nbao6LC7G9OW/NHkUwIdjLSS3KYf0Nv4/nQiiI8dY2TcsQx01NniOg iat-mode=0
Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0 Bridge obfs4 193.11.166.194:27020 86AC7B8D430DAC4117E9F42C9EAED18133863AAF cert=0LDeJH4JzMDtkJJrFphJCiPqKx7loozKN7VNfuukMGfHO0Z8OGdzHVkhVAOfo1mUdv9cMg iat-mode=0
Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0 Bridge obfs4 193.11.166.194:27025 1AE2C08904527FEA90C4C4F8C1083EA59FBC6FAF cert=ItvYZzW5tn6v3G4UnQa6Qz04Npro6e81AP70YujmK/KXwDFPTs3aHXcHp4n8Vt6w/bv8cA iat-mode=0

View File

@@ -13,7 +13,7 @@ import org.briarproject.bramble.api.sync.Message;
import org.briarproject.bramble.api.sync.MessageContext; import org.briarproject.bramble.api.sync.MessageContext;
import org.briarproject.bramble.test.ValidatorTestCase; import org.briarproject.bramble.test.ValidatorTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test; import org.junit.Test;
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE; import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
@@ -38,7 +38,7 @@ public class BdfMessageValidatorTest extends ValidatorTestCase {
private final Metadata meta = new Metadata(); private final Metadata meta = new Metadata();
public BdfMessageValidatorTest() { public BdfMessageValidatorTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE); context.setImposteriser(ClassImposteriser.INSTANCE);
} }
@Test(expected = InvalidMessageException.class) @Test(expected = InvalidMessageException.class)

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
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;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
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;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
import org.briarproject.bramble.api.crypto.SecretKey; import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;

View File

@@ -1,6 +1,5 @@
package org.briarproject.bramble.crypto; package org.briarproject.bramble.crypto;
import org.briarproject.bramble.api.crypto.AuthenticatedCipher;
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.util.StringUtils; import org.briarproject.bramble.util.StringUtils;

View File

@@ -11,8 +11,8 @@ import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.test.BrambleTestCase; import org.briarproject.bramble.test.BrambleTestCase;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.auto.Mock; import org.jmock.auto.Mock;
import org.jmock.imposters.ByteBuddyClassImposteriser;
import org.jmock.integration.junit4.JUnitRuleMockery; import org.jmock.integration.junit4.JUnitRuleMockery;
import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Rule; import org.junit.Rule;
import org.junit.Test; import org.junit.Test;
@@ -34,7 +34,7 @@ public class KeyAgreementProtocolTest extends BrambleTestCase {
@Rule @Rule
public JUnitRuleMockery context = new JUnitRuleMockery() {{ public JUnitRuleMockery context = new JUnitRuleMockery() {{
// So we can mock concrete classes like KeyAgreementTransport // So we can mock concrete classes like KeyAgreementTransport
setImposteriser(ByteBuddyClassImposteriser.INSTANCE); setImposteriser(ClassImposteriser.INSTANCE);
}}; }};
private final PublicKey alicePubKey = getAgreementPublicKey(); private final PublicKey alicePubKey = getAgreementPublicKey();

View File

@@ -14,7 +14,7 @@ import org.briarproject.bramble.api.record.RecordWriterFactory;
import org.briarproject.bramble.test.BrambleMockTestCase; import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.CaptureArgumentAction; import org.briarproject.bramble.test.CaptureArgumentAction;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Test; import org.junit.Test;
import java.io.InputStream; import java.io.InputStream;
@@ -58,7 +58,7 @@ public class KeyAgreementTransportTest extends BrambleMockTestCase {
private KeyAgreementTransport kat; private KeyAgreementTransport kat;
public KeyAgreementTransportTest() { public KeyAgreementTransportTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE); context.setImposteriser(ClassImposteriser.INSTANCE);
inputStream = context.mock(InputStream.class); inputStream = context.mock(InputStream.class);
outputStream = context.mock(OutputStream.class); outputStream = context.mock(OutputStream.class);
} }

View File

@@ -25,7 +25,7 @@ import org.briarproject.bramble.test.BrambleMockTestCase;
import org.briarproject.bramble.test.ImmediateExecutor; import org.briarproject.bramble.test.ImmediateExecutor;
import org.briarproject.bramble.test.RunAction; import org.briarproject.bramble.test.RunAction;
import org.jmock.Expectations; import org.jmock.Expectations;
import org.jmock.imposters.ByteBuddyClassImposteriser; import org.jmock.lib.legacy.ClassImposteriser;
import org.junit.Before; import org.junit.Before;
import org.junit.Test; import org.junit.Test;
@@ -69,7 +69,7 @@ public class PollerImplTest extends BrambleMockTestCase {
private PollerImpl poller; private PollerImpl poller;
public PollerImplTest() { public PollerImplTest() {
context.setImposteriser(ByteBuddyClassImposteriser.INSTANCE); context.setImposteriser(ClassImposteriser.INSTANCE);
random = context.mock(SecureRandom.class); random = context.mock(SecureRandom.class);
} }

View File

@@ -1,7 +1,6 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a', 'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a',
@@ -20,12 +19,11 @@ dependencyVerification {
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140', 'net.i2p.crypto:eddsa:0.2.0:eddsa-0.2.0.jar:a7cb1b85c16e2f0730b9204106929a1d9aaae1df728adc7041a8b8b605692140',
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
'net.jodah:concurrentunit:0.4.2:concurrentunit-0.4.2.jar:5583078e1acf91734939e985bc9e7ee947b0e93a8eef679da6bb07bbeb47ced3',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd', 'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb', 'org.bitlet:weupnp:0.1.4:weupnp-0.1.4.jar:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864', 'org.briarproject:jtorctl:0.3:jtorctl-0.3.jar:f2939238a097898998432effe93b0334d97a787972ab3a91a8973a1d309fc864',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
@@ -34,18 +32,16 @@ dependencyVerification {
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53', 'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90', 'org.codehaus.mojo:animal-sniffer-ant-tasks:1.16:animal-sniffer-ant-tasks-1.16.jar:890040976fbe2d584619a6a61b1fd2e925b3b5eb342a85eb2762c467c0d64e90',
'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52', 'org.codehaus.mojo:animal-sniffer:1.16:animal-sniffer-1.16.jar:72be8bcc226ba43b937c722a08a07852bfa1b11400089265d5df0ee7b38b1d52',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050',
'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad', 'org.hsqldb:hsqldb:2.3.5:hsqldb-2.3.5.jar:6676a6977ac98997a80f827ddbd3fe8ca1e0853dad1492512135fd1a222ccfad',
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b', 'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd', 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04', 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be', 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d', 'org.ow2.asm:asm-all:5.2:asm-all-5.2.jar:7fbffbc1db3422e2101689fd88df8384b15817b52b9b2b267b9f6d2511dc198d',
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de', 'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.whispersystems:curve25519-java:0.5.0:curve25519-java-0.5.0.jar:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d', 'org.whispersystems:curve25519-java:0.5.0:curve25519-java-0.5.0.jar:0aadd43cf01d11e9b58f867b3c4f25c3194e8b0623d1953d32dfbfbee009e38d',
] ]
} }

View File

@@ -23,9 +23,10 @@ dependencies {
testImplementation project(path: ':bramble-api', configuration: 'testOutput') testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput') testImplementation project(path: ':bramble-core', configuration: 'testOutput')
testImplementation "junit:junit:$junit_version" testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:$jmock_version" testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2"
testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24' testAnnotationProcessor 'com.google.dagger:dagger-compiler:2.24'
} }

View File

@@ -1,7 +1,6 @@
dependencyVerification { dependencyVerification {
verify = [ verify = [
'cglib:cglib:3.2.8:cglib-3.2.8.jar:3f64de999ecc5595dc84ca8ff0879d8a34c8623f9ef3c517a53ed59023fcb9db', 'cglib:cglib:3.2.0:cglib-3.2.0.jar:adb13bab79712ad6bdf1bd59f2a3918018a8016e722e8a357065afb9e6690861',
'com.google.code.findbugs:annotations:3.0.1:annotations-3.0.1.jar:6b47ff0a6de0ce17cbedc3abb0828ca5bce3009d53ea47b3723ff023c4742f79',
'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7', 'com.google.code.findbugs:jsr305:3.0.2:jsr305-3.0.2.jar:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f', 'com.google.dagger:dagger-compiler:2.24:dagger-compiler-2.24.jar:3c5afb955fb188da485cb2c048eff37dce0e1530b9780a0f2f7187d16d1ccc1f',
'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a', 'com.google.dagger:dagger-producers:2.24:dagger-producers-2.24.jar:f10f45b95191954d5d6b043fca9e62fb621d21bf70634b8f8476c7988b504c3a',
@@ -18,26 +17,24 @@ dependencyVerification {
'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f', 'javax.annotation:jsr250-api:1.0:jsr250-api-1.0.jar:a1a922d0d9b6d183ed3800dfac01d1e1eb159f0e8c6f94736931c1def54a941f',
'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff', 'javax.inject:javax.inject:1:javax.inject-1.jar:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a', 'junit:junit:4.12:junit-4.12.jar:59721f0805e223d84b90677887d9ff567dc534d7c502ca903c0c2b17f05c116a',
'net.bytebuddy:byte-buddy:1.9.12:byte-buddy-1.9.12.jar:3688c3d434bebc3edc5516296a2ed0f47b65e451071b4afecad84f902f0efc11',
'net.java.dev.jna:jna-platform:4.5.2:jna-platform-4.5.2.jar:f1d00c167d8921c6e23c626ef9f1c3ae0be473c95c68ffa012bc7ae55a87e2d6', 'net.java.dev.jna:jna-platform:4.5.2:jna-platform-4.5.2.jar:f1d00c167d8921c6e23c626ef9f1c3ae0be473c95c68ffa012bc7ae55a87e2d6',
'net.java.dev.jna:jna:4.5.2:jna-4.5.2.jar:0c8eb7acf67261656d79005191debaba3b6bf5dd60a43735a245429381dbecff', 'net.java.dev.jna:jna:4.5.2:jna-4.5.2.jar:0c8eb7acf67261656d79005191debaba3b6bf5dd60a43735a245429381dbecff',
'net.jcip:jcip-annotations:1.0:jcip-annotations-1.0.jar:be5805392060c71474bf6c9a67a099471274d30b83eef84bfc4e0889a4f1dcc0',
'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd', 'net.ltgt.gradle.incap:incap:0.2:incap-0.2.jar:b625b9806b0f1e4bc7a2e3457119488de3cd57ea20feedd513db070a573a4ffd',
'org.apache-extras.beanshell:bsh:2.0b6:bsh-2.0b6.jar:a17955976070c0573235ee662f2794a78082758b61accffce8d3f8aedcd91047', 'org.apache.ant:ant-launcher:1.9.4:ant-launcher-1.9.4.jar:7bccea20b41801ca17bcbc909a78c835d0f443f12d639c77bd6ae3d05861608d',
'org.apache.ant:ant:1.9.4:ant-1.9.4.jar:649ae0730251de07b8913f49286d46bba7b92d47c5f332610aa426c4f02161d8',
'org.beanshell:bsh:1.3.0:bsh-1.3.0.jar:9b04edc75d19db54f1b4e8b5355e9364384c6cf71eb0a1b9724c159d779879f8',
'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a:obfs4proxy-0.0.12-dev-40245c4a.zip:172029e7058b3a83ac93ac4991a44bf76e16ce8d46f558f5836d57da3cb3a766', 'org.briarproject:obfs4proxy:0.0.12-dev-40245c4a:obfs4proxy-0.0.12-dev-40245c4a.zip:172029e7058b3a83ac93ac4991a44bf76e16ce8d46f558f5836d57da3cb3a766',
'org.briarproject:tor:0.3.5.13-1:tor-0.3.5.13-1.zip:ef35c16bf8dc1f4c75ed71d9f55e4514f383d124ec96b859aca647c990927c99', 'org.briarproject:tor:0.3.5.13-1:tor-0.3.5.13-1.zip:ef35c16bf8dc1f4c75ed71d9f55e4514f383d124ec96b859aca647c990927c99',
'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d', 'org.checkerframework:checker-compat-qual:2.5.3:checker-compat-qual-2.5.3.jar:d76b9afea61c7c082908023f0cbc1427fab9abd2df915c8b8a3e7a509bccbc6d',
'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a', 'org.checkerframework:checker-qual:2.5.2:checker-qual-2.5.2.jar:64b02691c8b9d4e7700f8ee2e742dce7ea2c6e81e662b7522c9ee3bf568c040a',
'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53', 'org.codehaus.mojo:animal-sniffer-annotations:1.17:animal-sniffer-annotations-1.17.jar:92654f493ecfec52082e76354f0ebf87648dc3d5cec2e3c3cdb947c016747a53',
'org.hamcrest:hamcrest-core:2.1:hamcrest-core-2.1.jar:e09109e54a289d88506b9bfec987ddd199f4217c9464132668351b9a4f00bee9', 'org.hamcrest:hamcrest-core:1.3:hamcrest-core-1.3.jar:66fdef91e9739348df7a096aa384a5685f4e875584cce89386a7a47251c4d8e9',
'org.hamcrest:hamcrest-library:2.1:hamcrest-library-2.1.jar:b7e2b6895b3b679f0e47b6380fda391b225e9b78505db9d8bdde8d3cc8d52a21', 'org.hamcrest:hamcrest-library:1.3:hamcrest-library-1.3.jar:711d64522f9ec410983bd310934296da134be4254a125080a0416ec178dfad1c',
'org.hamcrest:hamcrest:2.1:hamcrest-2.1.jar:ba93b2e3a562322ba432f0a1b53addcc55cb188253319a020ed77f824e692050', 'org.jmock:jmock-junit4:2.8.2:jmock-junit4-2.8.2.jar:f7ee4df4f7bd7b7f1cafad3b99eb74d579f109d5992ff625347352edb55e674c',
'org.jmock:jmock-imposters:2.12.0:jmock-imposters-2.12.0.jar:3b836269745a137c9b2347e8d7c2104845b126ef04f012d6bfd94f1a7dea7b09', 'org.jmock:jmock-legacy:2.8.2:jmock-legacy-2.8.2.jar:f2b985a5c08a9edb7f37612330c058809da3f6a6d63ce792426ebf8ff0d6d31b',
'org.jmock:jmock-junit4:2.12.0:jmock-junit4-2.12.0.jar:3233062fc889637c151a24f1ee086bad04321ab7d8264fef279daff0fa27205b', 'org.jmock:jmock-testjar:2.8.2:jmock-testjar-2.8.2.jar:8900860f72c474e027cf97fe78dcbf154a1aa7fc62b6845c5fb4e4f3c7bc8760',
'org.jmock:jmock-legacy:2.12.0:jmock-legacy-2.12.0.jar:dea3a9cca653d082e2fe7e40232e982fe03a9984c7d67ceff24f3e03fe580dcd', 'org.jmock:jmock:2.8.2:jmock-2.8.2.jar:6c73cb4a2e6dbfb61fd99c9a768539c170ab6568e57846bd60dbf19596b65b16',
'org.jmock:jmock-testjar:2.12.0:jmock-testjar-2.12.0.jar:efefbcf6cd294d0e29f0c46eb2a3380d4ca4e1763ff719c69e2f2ac62f564a04', 'org.objenesis:objenesis:2.1:objenesis-2.1.jar:c74330cc6b806c804fd37e74487b4fe5d7c2750c5e15fbc6efa13bdee1bdef80',
'org.jmock:jmock:2.12.0:jmock-2.12.0.jar:266d07314c0cd343c46ff8a55601272de8cf406807caf55e6f313295f83d10be', 'org.ow2.asm:asm:5.0.4:asm-5.0.4.jar:896618ed8ae62702521a78bc7be42b7c491a08e6920a15f89a3ecdec31e9a220',
'org.objenesis:objenesis:3.0.1:objenesis-3.0.1.jar:7a8ff780b9ff48415d7c705f60030b0acaa616e7f823c98eede3b63508d4e984',
'org.ow2.asm:asm:7.1:asm-7.1.jar:4ab2fa2b6d2cc9ccb1eaa05ea329c407b47b13ed2915f62f8c4b8cc96258d4de',
] ]
} }

View File

@@ -26,9 +26,9 @@ android {
defaultConfig { defaultConfig {
minSdkVersion 16 minSdkVersion 16
targetSdkVersion 29 targetSdkVersion 29
versionCode 10218 versionCode 10220
versionName "1.2.18" versionName "1.2.20"
applicationId "org.briarproject.briar.socialbackup" applicationId "org.briarproject.briar.android"
vectorDrawables.useSupportLibrary = true vectorDrawables.useSupportLibrary = true
buildConfigField "String", "GitHash", buildConfigField "String", "GitHash",
@@ -106,7 +106,6 @@ dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'com.google.android.material:material:1.2.1' implementation 'com.google.android.material:material:1.2.1'
implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc03' implementation 'androidx.recyclerview:recyclerview-selection:1.1.0-rc03'
implementation 'org.magmacollective.darkcrystal:dark-crystal-secret-sharing-wrapper:1.1.0'
implementation 'info.guardianproject.panic:panic:1.0' implementation 'info.guardianproject.panic:panic:1.0'
implementation 'info.guardianproject.trustedintents:trustedintents:0.2' implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
@@ -137,12 +136,11 @@ dependencies {
testImplementation "androidx.arch.core:core-testing:2.1.0" testImplementation "androidx.arch.core:core-testing:2.1.0"
testImplementation "androidx.test.espresso:espresso-core:$espressoVersion" testImplementation "androidx.test.espresso:espresso-core:$espressoVersion"
testImplementation 'org.robolectric:robolectric:4.3.1' testImplementation 'org.robolectric:robolectric:4.3.1'
testImplementation 'org.mockito:mockito-core:3.1.0'
testImplementation 'org.mockito:mockito-core:3.9.0' testImplementation 'junit:junit:4.13.1'
testImplementation "junit:junit:$junit_version" testImplementation "org.jmock:jmock:$jmockVersion"
testImplementation "org.jmock:jmock:$jmock_version" testImplementation "org.jmock:jmock-junit4:$jmockVersion"
testImplementation "org.jmock:jmock-junit4:$jmock_version" testImplementation "org.jmock:jmock-legacy:$jmockVersion"
testImplementation "org.jmock:jmock-imposters:$jmock_version"
testAnnotationProcessor "com.google.dagger:dagger-compiler:2.24" testAnnotationProcessor "com.google.dagger:dagger-compiler:2.24"
androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput') androidTestImplementation project(path: ':bramble-api', configuration: 'testOutput')

View File

@@ -40,5 +40,3 @@
# Dependency injection annotations, needed for UI tests on older API levels # Dependency injection annotations, needed for UI tests on older API levels
-keep class javax.inject.** -keep class javax.inject.**
-keep class com.sun.jna.** { *; }

View File

@@ -4,16 +4,20 @@ import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault; import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.settings.Settings;
import org.briarproject.bramble.api.settings.SettingsManager;
import org.briarproject.briar.R; import org.briarproject.briar.R;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.test.espresso.intent.rule.IntentsTestRule; import androidx.test.espresso.intent.rule.IntentsTestRule;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext; import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static org.briarproject.briar.android.controller.BriarControllerImpl.DOZE_ASK_AGAIN;
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
@SuppressWarnings("WeakerAccess") @SuppressWarnings("WeakerAccess")
@@ -27,6 +31,8 @@ public abstract class UiTest {
protected AccountManager accountManager; protected AccountManager accountManager;
@Inject @Inject
protected LifecycleManager lifecycleManager; protected LifecycleManager lifecycleManager;
@Inject
protected SettingsManager settingsManager;
public UiTest() { public UiTest() {
BriarTestComponentApplication app = getApplicationContext(); BriarTestComponentApplication app = getApplicationContext();
@@ -39,22 +45,8 @@ public abstract class UiTest {
protected class CleanAccountTestRule<A extends Activity> protected class CleanAccountTestRule<A extends Activity>
extends IntentsTestRule<A> { extends IntentsTestRule<A> {
@Nullable
private final Runnable runnable;
public CleanAccountTestRule(Class<A> activityClass) { public CleanAccountTestRule(Class<A> activityClass) {
super(activityClass); super(activityClass);
this.runnable = null;
}
/**
* Use this if you need to run code before launching the activity.
* Note: You need to use {@link #launchActivity(Intent)} yourself
* to start the activity.
*/
public CleanAccountTestRule(Class<A> activityClass, Runnable runnable) {
super(activityClass, false, false);
this.runnable = runnable;
} }
@Override @Override
@@ -62,16 +54,17 @@ public abstract class UiTest {
super.beforeActivityLaunched(); super.beforeActivityLaunched();
accountManager.deleteAccount(); accountManager.deleteAccount();
accountManager.createAccount(USERNAME, PASSWORD); accountManager.createAccount(USERNAME, PASSWORD);
if (runnable != null) { Intent serviceIntent =
Intent serviceIntent = new Intent(getApplicationContext(), BriarService.class);
new Intent(getApplicationContext(), BriarService.class); getApplicationContext().startService(serviceIntent);
getApplicationContext().startService(serviceIntent); try {
try { lifecycleManager.waitForStartup();
lifecycleManager.waitForStartup(); // do not show doze white-listing dialog
} catch (InterruptedException e) { Settings settings = new Settings();
throw new AssertionError(e); settings.putBoolean(DOZE_ASK_AGAIN, false);
} settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
runnable.run(); } catch (InterruptedException | DbException e) {
throw new AssertionError(e);
} }
} }
} }

View File

@@ -10,12 +10,15 @@ import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
import androidx.test.filters.LargeTest;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
import static org.junit.Assert.fail; import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
@LargeTest
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class PngSuiteImageCompressorTest public class PngSuiteImageCompressorTest
extends AbstractImageCompressorTest { extends AbstractImageCompressorTest {

View File

@@ -12,11 +12,14 @@ import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.logging.Logger; import java.util.logging.Logger;
import androidx.test.filters.LargeTest;
import static java.util.logging.Logger.getLogger; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull; import static org.briarproject.bramble.api.nullsafety.NullSafety.requireNonNull;
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled; import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
import static org.junit.Assume.assumeTrue; import static org.junit.Assume.assumeTrue;
@LargeTest
@RunWith(Parameterized.class) @RunWith(Parameterized.class)
public class PngSuiteImageSizeCalculatorTest public class PngSuiteImageSizeCalculatorTest
extends AbstractImageSizeCalculatorTest { extends AbstractImageSizeCalculatorTest {

View File

@@ -1,39 +0,0 @@
package org.briarproject.briar.android.conversation;
import android.content.Context;
import android.content.Intent;
import org.briarproject.briar.R;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.ActivityTestRule;
import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.matcher.ViewMatchers.isDisplayed;
import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
import static org.briarproject.briar.android.ViewActions.waitUntilMatches;
import static org.briarproject.briar.android.conversation.ConversationActivity.CONTACT_ID;
@RunWith(AndroidJUnit4.class)
public class ConversationActivityNotSignedInTest {
@Rule
public ActivityTestRule<ConversationActivity> testRule =
new ActivityTestRule<>(ConversationActivity.class, false, false);
@Test
public void openWithoutSignedIn() {
Context targetContext = getInstrumentation().getTargetContext();
Intent intent = new Intent(targetContext, ConversationActivity.class);
intent.putExtra(CONTACT_ID, 1);
testRule.launchActivity(intent);
onView(withText(R.string.sign_in_button))
.perform(waitUntilMatches(isDisplayed()));
}
}

View File

@@ -16,6 +16,7 @@ import androidx.test.espresso.contrib.DrawerActions;
import androidx.test.ext.junit.runners.AndroidJUnit4; import androidx.test.ext.junit.runners.AndroidJUnit4;
import androidx.test.rule.ActivityTestRule; import androidx.test.rule.ActivityTestRule;
import static androidx.test.core.app.ApplicationProvider.getApplicationContext;
import static androidx.test.espresso.Espresso.onView; import static androidx.test.espresso.Espresso.onView;
import static androidx.test.espresso.action.ViewActions.click; import static androidx.test.espresso.action.ViewActions.click;
import static androidx.test.espresso.assertion.ViewAssertions.matches; import static androidx.test.espresso.assertion.ViewAssertions.matches;
@@ -29,7 +30,9 @@ import static androidx.test.espresso.matcher.ViewMatchers.withClassName;
import static androidx.test.espresso.matcher.ViewMatchers.withId; import static androidx.test.espresso.matcher.ViewMatchers.withId;
import static androidx.test.espresso.matcher.ViewMatchers.withText; import static androidx.test.espresso.matcher.ViewMatchers.withText;
import static org.briarproject.briar.android.ViewActions.waitUntilMatches; import static org.briarproject.briar.android.ViewActions.waitUntilMatches;
import static org.briarproject.briar.android.util.UiUtils.hasScreenLock;
import static org.hamcrest.CoreMatchers.is; import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assume.assumeTrue;
@RunWith(AndroidJUnit4.class) @RunWith(AndroidJUnit4.class)
public class SettingsActivityScreenshotTest extends ScreenshotTest { public class SettingsActivityScreenshotTest extends ScreenshotTest {
@@ -76,6 +79,8 @@ public class SettingsActivityScreenshotTest extends ScreenshotTest {
@Test @Test
public void appLock() { public void appLock() {
assumeTrue("device has no screen lock",
hasScreenLock(getApplicationContext()));
// scroll down // scroll down
onView(withClassName(is(RecyclerView.class.getName()))) onView(withClassName(is(RecyclerView.class.getName())))
.perform(scrollTo(hasDescendant( .perform(scrollTo(hasDescendant(

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string name="app_name" translatable="false">Briar SB Debug</string> <string name="app_name" translatable="false">Briar Debug</string>
<string name="app_package" translatable="false">org.briarproject.briar.socialbackup.debug</string> <string name="app_package" translatable="false">org.briarproject.briar.android.debug</string>
</resources> </resources>

View File

@@ -138,43 +138,6 @@
</intent-filter> </intent-filter>
</activity> </activity>
<activity
android:name="org.briarproject.briar.android.socialbackup.SocialBackupSetupActivity"
android:label="@string/activity_name_distributed_backup"
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.settings.SettingsActivity" />
</activity>
<activity
android:name="org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardActivity"
android:label="@string/activity_name_custodian_help_recovery"
android:parentActivityName="org.briarproject.briar.android.conversation.ConversationActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.conversation.ConversationActivity" />
</activity>
<activity
android:name="org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity"
android:label="@string/activity_name_recovery"
android:parentActivityName="org.briarproject.briar.android.account.SetupActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.account.SetupActivity" />
</activity>
<activity
android:name="org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity"
android:label="@string/activity_name_restore_account"
android:parentActivityName="org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity" />
</activity>
<activity <activity
android:name="org.briarproject.briar.android.conversation.ConversationActivity" android:name="org.briarproject.briar.android.conversation.ConversationActivity"
android:label="@string/app_name" android:label="@string/app_name"
@@ -379,7 +342,7 @@
</activity> </activity>
<activity <activity
android:name="org.briarproject.briar.android.contact.add.nearby.AddNearbyContactActivity" android:name="org.briarproject.briar.android.keyagreement.ContactExchangeActivity"
android:label="@string/add_contact_title" android:label="@string/add_contact_title"
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity" android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
android:theme="@style/BriarTheme.NoActionBar"> android:theme="@style/BriarTheme.NoActionBar">

View File

@@ -8,13 +8,11 @@ import org.briarproject.bramble.BrambleCoreModule;
import org.briarproject.bramble.account.BriarAccountModule; import org.briarproject.bramble.account.BriarAccountModule;
import org.briarproject.bramble.api.FeatureFlags; import org.briarproject.bramble.api.FeatureFlags;
import org.briarproject.bramble.api.account.AccountManager; import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.connection.ConnectionRegistry; import org.briarproject.bramble.api.connection.ConnectionRegistry;
import org.briarproject.bramble.api.contact.ContactExchangeManager; import org.briarproject.bramble.api.contact.ContactExchangeManager;
import org.briarproject.bramble.api.contact.ContactManager; import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.crypto.CryptoExecutor; import org.briarproject.bramble.api.crypto.CryptoExecutor;
import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator; import org.briarproject.bramble.api.crypto.PasswordStrengthEstimator;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DatabaseExecutor; import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.TransactionManager; import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.EventBus; import org.briarproject.bramble.api.event.EventBus;
@@ -61,7 +59,6 @@ import org.briarproject.briar.api.privategroup.PrivateGroupFactory;
import org.briarproject.briar.api.privategroup.PrivateGroupManager; import org.briarproject.briar.api.privategroup.PrivateGroupManager;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationFactory;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager; import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.briar.api.socialbackup.SocialBackupManager;
import org.briarproject.briar.api.test.TestDataCreator; import org.briarproject.briar.api.test.TestDataCreator;
import java.util.concurrent.Executor; import java.util.concurrent.Executor;
@@ -187,12 +184,6 @@ public interface AndroidComponent
Thread.UncaughtExceptionHandler exceptionHandler(); Thread.UncaughtExceptionHandler exceptionHandler();
SocialBackupManager socialBackupManager();
DatabaseComponent databaseComponent();
ClientHelper clientHelper();
void inject(SignInReminderReceiver briarService); void inject(SignInReminderReceiver briarService);
void inject(BriarService briarService); void inject(BriarService briarService);

View File

@@ -30,10 +30,11 @@ import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.android.account.DozeHelperModule; import org.briarproject.briar.android.account.DozeHelperModule;
import org.briarproject.briar.android.account.LockManagerImpl; import org.briarproject.briar.android.account.LockManagerImpl;
import org.briarproject.briar.android.account.SetupModule; import org.briarproject.briar.android.account.SetupModule;
import org.briarproject.briar.android.blog.BlogModule;
import org.briarproject.briar.android.contact.ContactListModule; import org.briarproject.briar.android.contact.ContactListModule;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactModule;
import org.briarproject.briar.android.forum.ForumModule; import org.briarproject.briar.android.forum.ForumModule;
import org.briarproject.briar.android.introduction.IntroductionModule; import org.briarproject.briar.android.introduction.IntroductionModule;
import org.briarproject.briar.android.keyagreement.ContactExchangeModule;
import org.briarproject.briar.android.logging.LoggingModule; import org.briarproject.briar.android.logging.LoggingModule;
import org.briarproject.briar.android.login.LoginModule; import org.briarproject.briar.android.login.LoginModule;
import org.briarproject.briar.android.navdrawer.NavDrawerModule; import org.briarproject.briar.android.navdrawer.NavDrawerModule;
@@ -42,9 +43,6 @@ import org.briarproject.briar.android.privategroup.list.GroupListModule;
import org.briarproject.briar.android.reporting.DevReportModule; import org.briarproject.briar.android.reporting.DevReportModule;
import org.briarproject.briar.android.settings.SettingsModule; import org.briarproject.briar.android.settings.SettingsModule;
import org.briarproject.briar.android.sharing.SharingModule; import org.briarproject.briar.android.sharing.SharingModule;
import org.briarproject.briar.android.socialbackup.SocialBackupSetupModule;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardModule;
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardModule;
import org.briarproject.briar.android.test.TestAvatarCreatorImpl; import org.briarproject.briar.android.test.TestAvatarCreatorImpl;
import org.briarproject.briar.android.viewmodel.ViewModelModule; import org.briarproject.briar.android.viewmodel.ViewModelModule;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
@@ -52,7 +50,6 @@ import org.briarproject.briar.api.android.DozeWatchdog;
import org.briarproject.briar.api.android.LockManager; import org.briarproject.briar.api.android.LockManager;
import org.briarproject.briar.api.android.ScreenFilterMonitor; import org.briarproject.briar.api.android.ScreenFilterMonitor;
import org.briarproject.briar.api.test.TestAvatarCreator; import org.briarproject.briar.api.test.TestAvatarCreator;
import org.briarproject.briar.socialbackup.AndroidDarkCrystalModule;
import java.io.File; import java.io.File;
import java.security.GeneralSecurityException; import java.security.GeneralSecurityException;
@@ -79,7 +76,7 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
@Module(includes = { @Module(includes = {
SetupModule.class, SetupModule.class,
DozeHelperModule.class, DozeHelperModule.class,
AddNearbyContactModule.class, ContactExchangeModule.class,
LoggingModule.class, LoggingModule.class,
LoginModule.class, LoginModule.class,
NavDrawerModule.class, NavDrawerModule.class,
@@ -87,16 +84,13 @@ import static org.briarproject.briar.android.TestingConstants.IS_DEBUG_BUILD;
SettingsModule.class, SettingsModule.class,
DevReportModule.class, DevReportModule.class,
ContactListModule.class, ContactListModule.class,
AndroidDarkCrystalModule.class,
IntroductionModule.class, IntroductionModule.class,
// below need to be within same scope as ViewModelProvider.Factory // below need to be within same scope as ViewModelProvider.Factory
BlogModule.class,
ForumModule.class, ForumModule.class,
GroupListModule.class, GroupListModule.class,
GroupConversationModule.class, GroupConversationModule.class,
SharingModule.class, SharingModule.class,
OwnerReturnShardModule.class,
CustodianReturnShardModule.class,
SocialBackupSetupModule.class
}) })
public class AppModule { public class AppModule {

View File

@@ -30,7 +30,6 @@ public class AuthorNameFragment extends SetupFragment {
private TextInputLayout authorNameWrapper; private TextInputLayout authorNameWrapper;
private TextInputEditText authorNameInput; private TextInputEditText authorNameInput;
private Button nextButton; private Button nextButton;
private Button recoverButton;
public static AuthorNameFragment newInstance() { public static AuthorNameFragment newInstance() {
return new AuthorNameFragment(); return new AuthorNameFragment();
@@ -55,9 +54,6 @@ public class AuthorNameFragment extends SetupFragment {
authorNameInput.addTextChangedListener(this); authorNameInput.addTextChangedListener(this);
nextButton.setOnClickListener(this); nextButton.setOnClickListener(this);
recoverButton = v.findViewById(R.id.buttonRestoreAccount);
recoverButton.setOnClickListener(e -> viewModel.recoverClicked());
return v; return v;
} }
@@ -79,8 +75,6 @@ public class AuthorNameFragment extends SetupFragment {
boolean enabled = authorNameLength > 0 && !error; boolean enabled = authorNameLength > 0 && !error;
authorNameInput.setOnEditorActionListener(enabled ? this : null); authorNameInput.setOnEditorActionListener(enabled ? this : null);
nextButton.setEnabled(enabled); nextButton.setEnabled(enabled);
recoverButton.setEnabled(!enabled);
// recoverButton.setVisibility(enabled ? View.INVISIBLE : View.VISIBLE);
} }
@Override @Override

View File

@@ -2,7 +2,7 @@ package org.briarproject.briar.android.account;
import android.content.Context; import android.content.Context;
public interface DozeHelper { interface DozeHelper {
boolean needToShowDozeFragment(Context context); boolean needToShowDozeFragment(Context context);
} }

View File

@@ -14,7 +14,7 @@ import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
public class DozeView extends PowerView { class DozeView extends PowerView {
@Nullable @Nullable
private Runnable onButtonClickListener; private Runnable onButtonClickListener;

View File

@@ -21,7 +21,7 @@ import static android.os.Build.VERSION.SDK_INT;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
public class HuaweiView extends PowerView { class HuaweiView extends PowerView {
private final static String PACKAGE_NAME = "com.huawei.systemmanager"; private final static String PACKAGE_NAME = "com.huawei.systemmanager";
private final static String CLASS_NAME = private final static String CLASS_NAME =

View File

@@ -24,7 +24,7 @@ import static org.briarproject.briar.android.util.UiUtils.showOnboardingDialog;
@UiThread @UiThread
@NotNullByDefault @NotNullByDefault
public abstract class PowerView extends ConstraintLayout { abstract class PowerView extends ConstraintLayout {
private final TextView textView; private final TextView textView;
private final ImageView checkImage; private final ImageView checkImage;
@@ -156,7 +156,7 @@ public abstract class PowerView extends ConstraintLayout {
}; };
} }
public interface OnCheckedChangedListener { interface OnCheckedChangedListener {
void onCheckedChanged(); void onCheckedChanged();
} }

View File

@@ -1,8 +0,0 @@
package org.briarproject.briar.android.account;
import androidx.annotation.UiThread;
public interface RecoverAccountListener {
@UiThread
void recoverAccountChosen();
}

View File

@@ -10,7 +10,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.BaseActivity; import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener; import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
@@ -26,7 +25,6 @@ import static org.briarproject.briar.android.account.SetupViewModel.State.AUTHOR
import static org.briarproject.briar.android.account.SetupViewModel.State.CREATED; import static org.briarproject.briar.android.account.SetupViewModel.State.CREATED;
import static org.briarproject.briar.android.account.SetupViewModel.State.DOZE; import static org.briarproject.briar.android.account.SetupViewModel.State.DOZE;
import static org.briarproject.briar.android.account.SetupViewModel.State.FAILED; import static org.briarproject.briar.android.account.SetupViewModel.State.FAILED;
import static org.briarproject.briar.android.account.SetupViewModel.State.RECOVER;
import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PASSWORD; import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PASSWORD;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@@ -62,8 +60,6 @@ public class SetupActivity extends BaseActivity
showPasswordFragment(); showPasswordFragment();
} else if (state == DOZE) { } else if (state == DOZE) {
showDozeFragment(); showDozeFragment();
} else if (state == RECOVER) {
recover();
} else if (state == CREATED || state == FAILED) { } else if (state == CREATED || state == FAILED) {
// TODO: Show an error if failed // TODO: Show an error if failed
showApp(); showApp();
@@ -88,13 +84,6 @@ public class SetupActivity extends BaseActivity
overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out); overridePendingTransition(R.anim.screen_new_in, R.anim.screen_old_out);
} }
void recover () {
// finish();
Intent i = new Intent(this, OwnerReturnShardActivity.class);
i.addFlags(FLAG_ACTIVITY_NEW_TASK);
startActivity(i);
}
@Override @Override
@Deprecated @Deprecated
public void runOnDbThread(Runnable runnable) { public void runOnDbThread(Runnable runnable) {

View File

@@ -1,8 +0,0 @@
package org.briarproject.briar.android.account;
import androidx.annotation.UiThread;
public interface SetupNewAccountChosenListener {
@UiThread
void setupNewAccountChosen();
}

View File

@@ -25,14 +25,12 @@ import static org.briarproject.briar.android.account.SetupViewModel.State.AUTHOR
import static org.briarproject.briar.android.account.SetupViewModel.State.CREATED; import static org.briarproject.briar.android.account.SetupViewModel.State.CREATED;
import static org.briarproject.briar.android.account.SetupViewModel.State.DOZE; import static org.briarproject.briar.android.account.SetupViewModel.State.DOZE;
import static org.briarproject.briar.android.account.SetupViewModel.State.FAILED; import static org.briarproject.briar.android.account.SetupViewModel.State.FAILED;
import static org.briarproject.briar.android.account.SetupViewModel.State.RECOVER;
import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PASSWORD; import static org.briarproject.briar.android.account.SetupViewModel.State.SET_PASSWORD;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public
class SetupViewModel extends AndroidViewModel { class SetupViewModel extends AndroidViewModel {
enum State {AUTHOR_NAME, SET_PASSWORD, DOZE, CREATED, FAILED, RECOVER} enum State {AUTHOR_NAME, SET_PASSWORD, DOZE, CREATED, FAILED}
private static final Logger LOG = private static final Logger LOG =
getLogger(SetupActivity.class.getName()); getLogger(SetupActivity.class.getName());
@@ -118,9 +116,4 @@ class SetupViewModel extends AndroidViewModel {
} }
}); });
} }
public void recoverClicked() {
LOG.info("RECOVER CLICKED ***");
state.postEvent(RECOVER);
}
} }

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.activity;
import android.app.Activity; import android.app.Activity;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.briar.android.AndroidComponent; import org.briarproject.briar.android.AndroidComponent;
import org.briarproject.briar.android.StartupFailureActivity; import org.briarproject.briar.android.StartupFailureActivity;
import org.briarproject.briar.android.account.AuthorNameFragment; import org.briarproject.briar.android.account.AuthorNameFragment;
@@ -12,20 +11,14 @@ import org.briarproject.briar.android.account.SetupActivity;
import org.briarproject.briar.android.account.UnlockActivity; import org.briarproject.briar.android.account.UnlockActivity;
import org.briarproject.briar.android.blog.BlogActivity; import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.blog.BlogFragment; import org.briarproject.briar.android.blog.BlogFragment;
import org.briarproject.briar.android.blog.BlogModule;
import org.briarproject.briar.android.blog.BlogPostFragment; import org.briarproject.briar.android.blog.BlogPostFragment;
import org.briarproject.briar.android.blog.FeedFragment; import org.briarproject.briar.android.blog.FeedFragment;
import org.briarproject.briar.android.blog.FeedPostFragment;
import org.briarproject.briar.android.blog.ReblogActivity; import org.briarproject.briar.android.blog.ReblogActivity;
import org.briarproject.briar.android.blog.ReblogFragment; import org.briarproject.briar.android.blog.ReblogFragment;
import org.briarproject.briar.android.blog.RssFeedImportActivity; import org.briarproject.briar.android.blog.RssFeedImportActivity;
import org.briarproject.briar.android.blog.RssFeedManageActivity; import org.briarproject.briar.android.blog.RssFeedManageActivity;
import org.briarproject.briar.android.blog.WriteBlogPostActivity; import org.briarproject.briar.android.blog.WriteBlogPostActivity;
import org.briarproject.briar.android.contact.ContactListFragment; import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactActivity;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactErrorFragment;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactFragment;
import org.briarproject.briar.android.contact.add.nearby.AddNearbyContactIntroFragment;
import org.briarproject.briar.android.contact.add.remote.AddContactActivity; import org.briarproject.briar.android.contact.add.remote.AddContactActivity;
import org.briarproject.briar.android.contact.add.remote.LinkExchangeFragment; import org.briarproject.briar.android.contact.add.remote.LinkExchangeFragment;
import org.briarproject.briar.android.contact.add.remote.NicknameFragment; import org.briarproject.briar.android.contact.add.remote.NicknameFragment;
@@ -41,6 +34,10 @@ import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
import org.briarproject.briar.android.introduction.ContactChooserFragment; import org.briarproject.briar.android.introduction.ContactChooserFragment;
import org.briarproject.briar.android.introduction.IntroductionActivity; import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.introduction.IntroductionMessageFragment; import org.briarproject.briar.android.introduction.IntroductionMessageFragment;
import org.briarproject.briar.android.keyagreement.ContactExchangeActivity;
import org.briarproject.briar.android.keyagreement.ContactExchangeErrorFragment;
import org.briarproject.briar.android.keyagreement.KeyAgreementActivity;
import org.briarproject.briar.android.keyagreement.KeyAgreementFragment;
import org.briarproject.briar.android.login.ChangePasswordActivity; import org.briarproject.briar.android.login.ChangePasswordActivity;
import org.briarproject.briar.android.login.OpenDatabaseFragment; import org.briarproject.briar.android.login.OpenDatabaseFragment;
import org.briarproject.briar.android.login.PasswordFragment; import org.briarproject.briar.android.login.PasswordFragment;
@@ -78,40 +75,19 @@ import org.briarproject.briar.android.sharing.ShareBlogFragment;
import org.briarproject.briar.android.sharing.ShareForumActivity; import org.briarproject.briar.android.sharing.ShareForumActivity;
import org.briarproject.briar.android.sharing.ShareForumFragment; import org.briarproject.briar.android.sharing.ShareForumFragment;
import org.briarproject.briar.android.sharing.SharingModule; import org.briarproject.briar.android.sharing.SharingModule;
import org.briarproject.briar.android.socialbackup.SetupExplainerFragment;
import org.briarproject.briar.android.socialbackup.recover.CustodianRecoveryModeExplainerFragment;
import org.briarproject.briar.android.socialbackup.CustodianSelectorFragment;
import org.briarproject.briar.android.socialbackup.SocialBackupSetupActivity;
import org.briarproject.briar.android.socialbackup.ExistingBackupFragment;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardActivity;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardErrorFragment;
import org.briarproject.briar.android.socialbackup.recover.CustodianReturnShardFragment;
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeErrorFragment;
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeExplainerFragment;
import org.briarproject.briar.android.socialbackup.recover.OwnerRecoveryModeMainFragment;
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardActivity;
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardFragment;
import org.briarproject.briar.android.socialbackup.ThresholdSelectorFragment;
import org.briarproject.briar.android.socialbackup.creation.CreateBackupModule;
import org.briarproject.briar.android.socialbackup.recover.OwnerReturnShardSuccessFragment;
import org.briarproject.briar.android.socialbackup.recover.RestoreAccountActivity;
import org.briarproject.briar.android.socialbackup.recover.RestoreAccountDozeFragment;
import org.briarproject.briar.android.socialbackup.recover.RestoreAccountSetPasswordFragment;
import org.briarproject.briar.android.splash.SplashScreenActivity; import org.briarproject.briar.android.splash.SplashScreenActivity;
import org.briarproject.briar.android.test.TestDataActivity; import org.briarproject.briar.android.test.TestDataActivity;
import dagger.Component; import dagger.Component;
@ActivityScope @ActivityScope
@Component(modules ={ @Component(modules = {
ActivityModule.class, ActivityModule.class,
BlogModule.class,
CreateGroupModule.class, CreateGroupModule.class,
GroupInvitationModule.class, GroupInvitationModule.class,
GroupMemberModule.class, GroupMemberModule.class,
GroupRevealModule.class, GroupRevealModule.class,
SharingModule.SharingLegacyModule.class, SharingModule.SharingLegacyModule.class
CreateBackupModule.class
}, dependencies = AndroidComponent.class) }, dependencies = AndroidComponent.class)
public interface ActivityComponent { public interface ActivityComponent {
@@ -129,7 +105,9 @@ public interface ActivityComponent {
void inject(PanicPreferencesActivity activity); void inject(PanicPreferencesActivity activity);
void inject(AddNearbyContactActivity activity); void inject(ContactExchangeActivity activity);
void inject(KeyAgreementActivity activity);
void inject(ConversationActivity activity); void inject(ConversationActivity activity);
@@ -171,8 +149,6 @@ public interface ActivityComponent {
void inject(BlogPostFragment fragment); void inject(BlogPostFragment fragment);
void inject(FeedPostFragment fragment);
void inject(ReblogFragment fragment); void inject(ReblogFragment fragment);
void inject(ReblogActivity activity); void inject(ReblogActivity activity);
@@ -201,14 +177,6 @@ public interface ActivityComponent {
void inject(CrashReportActivity crashReportActivity); void inject(CrashReportActivity crashReportActivity);
void inject(CustodianReturnShardActivity custodianReturnShardActivity);
void inject(OwnerReturnShardActivity ownerReturnShardActivity);
void inject(OwnerRecoveryModeMainFragment ownerRecoveryModeMainFragment);
void inject(RestoreAccountActivity restoreAccountActivity);
// Fragments // Fragments
void inject(AuthorNameFragment fragment); void inject(AuthorNameFragment fragment);
@@ -235,9 +203,7 @@ public interface ActivityComponent {
void inject(FeedFragment fragment); void inject(FeedFragment fragment);
void inject(AddNearbyContactIntroFragment fragment); void inject(KeyAgreementFragment fragment);
void inject(AddNearbyContactFragment fragment);
void inject(LinkExchangeFragment fragment); void inject(LinkExchangeFragment fragment);
@@ -255,7 +221,7 @@ public interface ActivityComponent {
void inject(ScreenFilterDialogFragment fragment); void inject(ScreenFilterDialogFragment fragment);
void inject(AddNearbyContactErrorFragment fragment); void inject(ContactExchangeErrorFragment fragment);
void inject(AliasDialogFragment aliasDialogFragment); void inject(AliasDialogFragment aliasDialogFragment);
@@ -267,33 +233,4 @@ public interface ActivityComponent {
void inject(ConfirmAvatarDialogFragment fragment); void inject(ConfirmAvatarDialogFragment fragment);
void inject(ThresholdSelectorFragment thresholdSelectorFragment);
void inject(SocialBackupSetupActivity distributedBackupActivity);
void inject(DatabaseComponent databaseComponent);
void inject(CustodianSelectorFragment custodianSelectorFragment);
void inject(OwnerRecoveryModeExplainerFragment ownerRecoveryModeExplainerFragment);
void inject(ExistingBackupFragment existingBackupFragment);
void inject(CustodianRecoveryModeExplainerFragment custodianRecoveryModeExplainerFragment);
void inject(CustodianReturnShardFragment custodianReturnShardFragment);
void inject(OwnerReturnShardFragment ownerReturnShardFragment);
void inject(RestoreAccountSetPasswordFragment restoreAccountSetPasswordFragment);
void inject(RestoreAccountDozeFragment restoreAccountDozeFragment);
void inject(OwnerReturnShardSuccessFragment ownerReturnShardSuccessFragment);
void inject(OwnerRecoveryModeErrorFragment ownerRecoveryModeErrorFragment);
void inject(CustodianReturnShardErrorFragment custodianReturnShardErrorFragment);
void inject(SetupExplainerFragment setupExplainerFragment);
} }

View File

@@ -6,10 +6,11 @@ public interface RequestCodes {
int REQUEST_INTRODUCTION = 2; int REQUEST_INTRODUCTION = 2;
int REQUEST_GROUP_INVITE = 3; int REQUEST_GROUP_INVITE = 3;
int REQUEST_SHARE_FORUM = 4; int REQUEST_SHARE_FORUM = 4;
int REQUEST_WRITE_BLOG_POST = 5;
int REQUEST_SHARE_BLOG = 6; int REQUEST_SHARE_BLOG = 6;
int REQUEST_RINGTONE = 7; int REQUEST_RINGTONE = 7;
int REQUEST_PERMISSION_CAMERA_LOCATION = 8;
int REQUEST_DOZE_WHITELISTING = 9; int REQUEST_DOZE_WHITELISTING = 9;
int REQUEST_BLUETOOTH_DISCOVERABLE = 10;
int REQUEST_UNLOCK = 11; int REQUEST_UNLOCK = 11;
int REQUEST_KEYGUARD_UNLOCK = 12; int REQUEST_KEYGUARD_UNLOCK = 12;
int REQUEST_ATTACH_IMAGE = 13; int REQUEST_ATTACH_IMAGE = 13;

View File

@@ -1,48 +0,0 @@
package org.briarproject.briar.android.blog;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.blog.BlogPostHeader;
import java.util.Collection;
import javax.annotation.Nullable;
import androidx.annotation.UiThread;
@NotNullByDefault
interface BaseController {
@UiThread
void onStart();
@UiThread
void onStop();
void loadBlogPosts(GroupId g,
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler);
void loadBlogPost(BlogPostHeader header,
ResultExceptionHandler<BlogPostItem, DbException> handler);
void loadBlogPost(GroupId g, MessageId m,
ResultExceptionHandler<BlogPostItem, DbException> handler);
void repeatPost(BlogPostItem item, @Nullable String comment,
ExceptionHandler<DbException> handler);
@NotNullByDefault
interface BlogListener {
@UiThread
void onBlogPostAdded(BlogPostHeader header, boolean local);
@UiThread
void onBlogRemoved();
}
}

View File

@@ -1,209 +0,0 @@
package org.briarproject.briar.android.blog;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
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.MessageId;
import org.briarproject.briar.android.controller.DbControllerImpl;
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogCommentHeader;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostHeader;
import org.briarproject.briar.util.HtmlUtils;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import androidx.annotation.CallSuper;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.util.HtmlUtils.ARTICLE;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
abstract class BaseControllerImpl extends DbControllerImpl
implements BaseController, EventListener {
private static final Logger LOG =
Logger.getLogger(BaseControllerImpl.class.getName());
protected final EventBus eventBus;
protected final AndroidNotificationManager notificationManager;
protected final IdentityManager identityManager;
protected final BlogManager blogManager;
private final Map<MessageId, String> textCache = new ConcurrentHashMap<>();
private final Map<MessageId, BlogPostHeader> headerCache =
new ConcurrentHashMap<>();
BaseControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, EventBus eventBus,
AndroidNotificationManager notificationManager,
IdentityManager identityManager, BlogManager blogManager) {
super(dbExecutor, lifecycleManager);
this.eventBus = eventBus;
this.notificationManager = notificationManager;
this.identityManager = identityManager;
this.blogManager = blogManager;
}
@Override
@CallSuper
public void onStart() {
eventBus.addListener(this);
}
@Override
@CallSuper
public void onStop() {
eventBus.removeListener(this);
}
@Override
public void loadBlogPosts(GroupId groupId,
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {
runOnDbThread(() -> {
try {
Collection<BlogPostItem> items = loadItems(groupId);
handler.onResult(items);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
Collection<BlogPostItem> loadItems(GroupId groupId) throws DbException {
long start = now();
Collection<BlogPostHeader> headers =
blogManager.getPostHeaders(groupId);
logDuration(LOG, "Loading headers", start);
Collection<BlogPostItem> items = new ArrayList<>(headers.size());
start = now();
for (BlogPostHeader h : headers) {
headerCache.put(h.getId(), h);
BlogPostItem item = getItem(h);
items.add(item);
}
logDuration(LOG, "Loading bodies", start);
return items;
}
@Override
public void loadBlogPost(BlogPostHeader header,
ResultExceptionHandler<BlogPostItem, DbException> handler) {
String text = textCache.get(header.getId());
if (text != null) {
LOG.info("Loaded text from cache");
handler.onResult(new BlogPostItem(header, text));
return;
}
runOnDbThread(() -> {
try {
long start = now();
BlogPostItem item = getItem(header);
logDuration(LOG, "Loading text", start);
handler.onResult(item);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
@Override
public void loadBlogPost(GroupId g, MessageId m,
ResultExceptionHandler<BlogPostItem, DbException> handler) {
BlogPostHeader header = headerCache.get(m);
if (header != null) {
LOG.info("Loaded header from cache");
loadBlogPost(header, handler);
return;
}
runOnDbThread(() -> {
try {
long start = now();
BlogPostHeader header1 = getPostHeader(g, m);
BlogPostItem item = getItem(header1);
logDuration(LOG, "Loading post", start);
handler.onResult(item);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
@Override
public void repeatPost(BlogPostItem item, @Nullable String comment,
ExceptionHandler<DbException> handler) {
runOnDbThread(() -> {
try {
LocalAuthor a = identityManager.getLocalAuthor();
Blog b = blogManager.getPersonalBlog(a);
BlogPostHeader h = item.getHeader();
blogManager.addLocalComment(a, b.getId(), comment, h);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
private BlogPostHeader getPostHeader(GroupId g, MessageId m)
throws DbException {
BlogPostHeader header = headerCache.get(m);
if (header == null) {
header = blogManager.getPostHeader(g, m);
headerCache.put(m, header);
}
return header;
}
@DatabaseExecutor
private BlogPostItem getItem(BlogPostHeader h) throws DbException {
String text;
if (h instanceof BlogCommentHeader) {
BlogCommentHeader c = (BlogCommentHeader) h;
BlogCommentItem item = new BlogCommentItem(c);
text = getPostText(item.getPostHeader().getId());
item.setText(text);
return item;
} else {
text = getPostText(h.getId());
return new BlogPostItem(h, text);
}
}
@DatabaseExecutor
private String getPostText(MessageId m) throws DbException {
String text = textCache.get(m);
if (text == null) {
text = HtmlUtils.clean(blogManager.getPostText(m), ARTICLE);
textCache.put(m, text);
}
return text;
}
}

View File

@@ -1,121 +0,0 @@
package org.briarproject.briar.android.blog;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.fragment.BaseFragment;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import androidx.annotation.CallSuper;
import androidx.annotation.UiThread;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
@UiThread
@MethodsNotNullByDefault
@ParametersNotNullByDefault
abstract class BasePostFragment extends BaseFragment {
static final String POST_ID = "briar.POST_ID";
private static final Logger LOG =
getLogger(BasePostFragment.class.getName());
private final Handler handler = new Handler(Looper.getMainLooper());
protected MessageId postId;
private ProgressBar progressBar;
private BlogPostViewHolder ui;
private BlogPostItem post;
private Runnable refresher;
@CallSuper
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
// retrieve MessageId of blog post from arguments
byte[] p = requireArguments().getByteArray(POST_ID);
if (p == null) throw new IllegalStateException("No post ID in args");
postId = new MessageId(p);
View view = inflater.inflate(R.layout.fragment_blog_post, container,
false);
progressBar = view.findViewById(R.id.progressBar);
progressBar.setVisibility(VISIBLE);
ui = new BlogPostViewHolder(view, true, new OnBlogPostClickListener() {
@Override
public void onBlogPostClick(BlogPostItem post) {
// We're already there
}
@Override
public void onAuthorClick(BlogPostItem post) {
if (getContext() == null) return;
Intent i = new Intent(getContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
getContext().startActivity(i);
}
}, getFragmentManager());
return view;
}
@CallSuper
@Override
public void onStart() {
super.onStart();
startPeriodicUpdate();
}
@CallSuper
@Override
public void onStop() {
super.onStop();
stopPeriodicUpdate();
}
@UiThread
protected void onBlogPostLoaded(BlogPostItem post) {
progressBar.setVisibility(INVISIBLE);
this.post = post;
ui.bindItem(post);
}
private void startPeriodicUpdate() {
refresher = () -> {
LOG.info("Updating Content...");
ui.updateDate(post.getTimestamp());
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
};
LOG.info("Adding Handler Callback");
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
}
private void stopPeriodicUpdate() {
if (refresher != null) {
LOG.info("Removing Handler Callback");
handler.removeCallbacks(refresher);
}
}
}

View File

@@ -0,0 +1,216 @@
package org.briarproject.briar.android.blog;
import android.app.Application;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.viewmodel.DbViewModel;
import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogCommentHeader;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogPostHeader;
import org.briarproject.briar.util.HtmlUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Level.WARNING;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.util.HtmlUtils.ARTICLE;
@NotNullByDefault
abstract class BaseViewModel extends DbViewModel implements EventListener {
private static final Logger LOG = getLogger(BaseViewModel.class.getName());
private final EventBus eventBus;
protected final IdentityManager identityManager;
protected final AndroidNotificationManager notificationManager;
protected final BlogManager blogManager;
protected final MutableLiveData<LiveResult<ListUpdate>> blogPosts =
new MutableLiveData<>();
BaseViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager,
TransactionManager db,
AndroidExecutor androidExecutor,
EventBus eventBus,
IdentityManager identityManager,
AndroidNotificationManager notificationManager,
BlogManager blogManager) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor);
this.eventBus = eventBus;
this.identityManager = identityManager;
this.notificationManager = notificationManager;
this.blogManager = blogManager;
eventBus.addListener(this);
}
@Override
protected void onCleared() {
super.onCleared();
eventBus.removeListener(this);
}
@DatabaseExecutor
protected List<BlogPostItem> loadBlogPosts(Transaction txn, GroupId groupId)
throws DbException {
long start = now();
List<BlogPostHeader> headers =
blogManager.getPostHeaders(txn, groupId);
logDuration(LOG, "Loading headers", start);
List<BlogPostItem> items = new ArrayList<>(headers.size());
start = now();
for (BlogPostHeader h : headers) {
BlogPostItem item = getItem(txn, h);
items.add(item);
}
logDuration(LOG, "Loading bodies", start);
return items;
}
@DatabaseExecutor
protected BlogPostItem getItem(Transaction txn, BlogPostHeader h)
throws DbException {
String text;
if (h instanceof BlogCommentHeader) {
BlogCommentHeader c = (BlogCommentHeader) h;
BlogCommentItem item = new BlogCommentItem(c);
text = getPostText(txn, item.getPostHeader().getId());
item.setText(text);
return item;
} else {
text = getPostText(txn, h.getId());
return new BlogPostItem(h, text);
}
}
@DatabaseExecutor
private String getPostText(Transaction txn, MessageId m)
throws DbException {
return HtmlUtils.clean(blogManager.getPostText(txn, m), ARTICLE);
}
LiveData<LiveResult<BlogPostItem>> loadBlogPost(GroupId g, MessageId m) {
MutableLiveData<LiveResult<BlogPostItem>> result =
new MutableLiveData<>();
runOnDbThread(true, txn -> {
long start = now();
BlogPostHeader header = blogManager.getPostHeader(txn, g, m);
BlogPostItem item = getItem(txn, header);
logDuration(LOG, "Loading post", start);
result.postValue(new LiveResult<>(item));
}, e -> {
logException(LOG, WARNING, e);
result.postValue(new LiveResult<>(e));
});
return result;
}
protected void onBlogPostAdded(BlogPostHeader header, boolean local) {
runOnDbThread(true, txn -> {
BlogPostItem item = getItem(txn, header);
txn.attach(() -> onBlogPostItemAdded(item, local));
}, this::handleException);
}
@UiThread
private void onBlogPostItemAdded(BlogPostItem item, boolean local) {
List<BlogPostItem> items = addListItem(getBlogPostItems(), item);
if (items != null) {
Collections.sort(items);
blogPosts.setValue(new LiveResult<>(new ListUpdate(local, items)));
}
}
void repeatPost(BlogPostItem item, @Nullable String comment) {
runOnDbThread(() -> {
try {
LocalAuthor a = identityManager.getLocalAuthor();
Blog b = blogManager.getPersonalBlog(a);
BlogPostHeader h = item.getHeader();
blogManager.addLocalComment(a, b.getId(), comment, h);
} catch (DbException e) {
handleException(e);
}
});
}
LiveData<LiveResult<ListUpdate>> getBlogPosts() {
return blogPosts;
}
@UiThread
@Nullable
protected List<BlogPostItem> getBlogPostItems() {
LiveResult<ListUpdate> value = blogPosts.getValue();
if (value == null) return null;
ListUpdate result = value.getResultOrNull();
return result == null ? null : result.getItems();
}
/**
* Call this after {@link ListUpdate#getPostAddedWasLocal()} was processed.
* This prevents it from getting processed again.
*/
@UiThread
void resetLocalUpdate() {
LiveResult<ListUpdate> value = blogPosts.getValue();
if (value == null) return;
ListUpdate result = value.getResultOrNull();
result.postAddedWasLocal = null;
}
static class ListUpdate {
@Nullable
private Boolean postAddedWasLocal;
private final List<BlogPostItem> items;
ListUpdate(@Nullable Boolean postAddedWasLocal,
List<BlogPostItem> items) {
this.postAddedWasLocal = postAddedWasLocal;
this.items = items;
}
/**
* @return null when not a single post was added with this update.
* true when a single post was added locally and false if remotely.
*/
@Nullable
public Boolean getPostAddedWasLocal() {
return postAddedWasLocal;
}
public List<BlogPostItem> getItems() {
return items;
}
}
}

View File

@@ -6,9 +6,11 @@ import android.os.Bundle;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
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.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.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment;
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;
@@ -16,6 +18,10 @@ import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.appcompat.widget.Toolbar; import androidx.appcompat.widget.Toolbar;
import androidx.lifecycle.ViewModelProvider;
import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.blog.BlogPostFragment.POST_ID;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
@@ -23,7 +29,16 @@ public class BlogActivity extends BriarActivity
implements BaseFragmentListener { implements BaseFragmentListener {
@Inject @Inject
BlogController blogController; ViewModelProvider.Factory viewModelFactory;
private BlogViewModel viewModel;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(this, viewModelFactory)
.get(BlogViewModel.class);
}
@Override @Override
public void onCreate(@Nullable Bundle state) { public void onCreate(@Nullable Bundle state) {
@@ -31,32 +46,46 @@ public class BlogActivity extends BriarActivity
// GroupId from Intent // GroupId from Intent
Intent i = getIntent(); Intent i = getIntent();
byte[] b = i.getByteArrayExtra(GROUP_ID); GroupId groupId =
if (b == null) throw new IllegalStateException("No group ID in intent"); new GroupId(requireNonNull(i.getByteArrayExtra(GROUP_ID)));
GroupId groupId = new GroupId(b); // Get post info from intent
blogController.setGroupId(groupId); @Nullable byte[] postId = i.getByteArrayExtra(POST_ID);
viewModel.setGroupId(groupId, postId == null);
setContentView(R.layout.activity_fragment_container_toolbar); setContentView(R.layout.activity_fragment_container_toolbar);
Toolbar toolbar = setUpCustomToolbar(false); Toolbar toolbar = setUpCustomToolbar(false);
// Open Sharing Status on Toolbar click // Open Sharing Status on Toolbar click
if (toolbar != null) { toolbar.setOnClickListener(v -> {
toolbar.setOnClickListener(v -> { Intent i1 = new Intent(BlogActivity.this,
Intent i1 = new Intent(BlogActivity.this, BlogSharingStatusActivity.class);
BlogSharingStatusActivity.class); i1.putExtra(GROUP_ID, groupId.getBytes());
i1.putExtra(GROUP_ID, groupId.getBytes()); startActivity(i1);
startActivity(i1); });
});
} viewModel.getBlog().observe(this, blog ->
setTitle(blog.getBlog().getAuthor().getName())
);
viewModel.getSharingInfo().observe(this, info ->
setToolbarSubTitle(info.total, info.online)
);
if (state == null) { if (state == null) {
showInitialFragment(BlogFragment.newInstance(groupId)); if (postId == null) {
showInitialFragment(BlogFragment.newInstance(groupId));
} else {
MessageId messageId = new MessageId(postId);
BaseFragment f =
BlogPostFragment.newInstance(groupId, messageId);
showInitialFragment(f);
}
} }
} }
@Override private void setToolbarSubTitle(int total, int online) {
public void injectActivity(ActivityComponent component) { requireNonNull(getSupportActionBar())
component.inject(this); .setSubtitle(getString(R.string.shared_with, total, online));
} }
} }

View File

@@ -8,7 +8,9 @@ import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.List; import java.util.List;
// This class is not thread-safe import javax.annotation.concurrent.NotThreadSafe;
@NotThreadSafe
class BlogCommentItem extends BlogPostItem { class BlogCommentItem extends BlogPostItem {
private static final BlogCommentComparator COMPARATOR = private static final BlogCommentComparator COMPARATOR =

View File

@@ -1,46 +0,0 @@
package org.briarproject.briar.android.blog;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import java.util.Collection;
import androidx.annotation.UiThread;
@NotNullByDefault
public interface BlogController extends BaseController {
void setGroupId(GroupId g);
@UiThread
void setBlogSharingListener(BlogSharingListener listener);
@UiThread
void unsetBlogSharingListener(BlogSharingListener listener);
void loadBlogPosts(
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler);
void loadBlogPost(MessageId m,
ResultExceptionHandler<BlogPostItem, DbException> handler);
void loadBlog(ResultExceptionHandler<BlogItem, DbException> handler);
void deleteBlog(ResultExceptionHandler<Void, DbException> handler);
void loadSharingContacts(
ResultExceptionHandler<Collection<ContactId>, DbException> handler);
interface BlogSharingListener extends BlogListener {
@UiThread
void onBlogInvitationAccepted(ContactId c);
@UiThread
void onBlogLeft(ContactId c);
}
}

View File

@@ -1,9 +1,7 @@
package org.briarproject.briar.android.blog; package org.briarproject.briar.android.blog;
import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.os.Parcelable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -12,33 +10,25 @@ import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; 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.blog.BaseViewModel.ListUpdate;
import org.briarproject.briar.android.blog.BlogController.BlogSharingListener;
import org.briarproject.briar.android.controller.SharingController;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity; import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
import org.briarproject.briar.android.sharing.ShareBlogActivity; import org.briarproject.briar.android.sharing.ShareBlogActivity;
import org.briarproject.briar.android.util.BriarSnackbarBuilder; import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.blog.BlogPostHeader; import org.briarproject.briar.android.widget.LinkDialogFragment;
import java.util.Collection;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView.LayoutManager; import androidx.recyclerview.widget.RecyclerView.LayoutManager;
@@ -48,31 +38,22 @@ import static android.widget.Toast.LENGTH_SHORT;
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG; import static com.google.android.material.snackbar.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_SHARE_BLOG; import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_BLOG;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_WRITE_BLOG_POST;
import static org.briarproject.briar.android.controller.SharingController.SharingListener;
@UiThread @UiThread
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class BlogFragment extends BaseFragment public class BlogFragment extends BaseFragment
implements BlogSharingListener, SharingListener, implements OnBlogPostClickListener {
OnBlogPostClickListener {
private final static String TAG = BlogFragment.class.getName(); private final static String TAG = BlogFragment.class.getName();
@Inject @Inject
BlogController blogController; ViewModelProvider.Factory viewModelFactory;
@Inject
SharingController sharingController;
@Nullable
private Parcelable layoutManagerState;
private GroupId groupId; private GroupId groupId;
private BlogPostAdapter adapter; private BlogViewModel viewModel;
private LayoutManager layoutManager; private final BlogPostAdapter adapter = new BlogPostAdapter(false, this);
private BriarRecyclerView list; private BriarRecyclerView list;
private MenuItem writeButton, deleteButton;
private boolean isMyBlog = false, canDeleteBlog = false;
static BlogFragment newInstance(GroupId groupId) { static BlogFragment newInstance(GroupId groupId) {
BlogFragment f = new BlogFragment(); BlogFragment f = new BlogFragment();
@@ -87,8 +68,8 @@ public class BlogFragment extends BaseFragment
@Override @Override
public void injectFragment(ActivityComponent component) { public void injectFragment(ActivityComponent component) {
component.inject(this); component.inject(this);
blogController.setBlogSharingListener(this); viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
sharingController.setSharingListener(this); .get(BlogViewModel.class);
} }
@Nullable @Nullable
@@ -103,106 +84,82 @@ public class BlogFragment extends BaseFragment
View v = inflater.inflate(R.layout.fragment_blog, container, false); View v = inflater.inflate(R.layout.fragment_blog, container, false);
adapter = new BlogPostAdapter(requireActivity(), this,
getFragmentManager());
list = v.findViewById(R.id.postList); list = v.findViewById(R.id.postList);
layoutManager = new LinearLayoutManager(getActivity()); LayoutManager layoutManager = new LinearLayoutManager(getActivity());
list.setLayoutManager(layoutManager); list.setLayoutManager(layoutManager);
list.setAdapter(adapter); list.setAdapter(adapter);
list.showProgressBar(); list.showProgressBar();
list.setEmptyText(getString(R.string.blogs_other_blog_empty_state)); list.setEmptyText(getString(R.string.blogs_other_blog_empty_state));
if (savedInstanceState != null) { viewModel.getBlogPosts().observe(getViewLifecycleOwner(), result ->
layoutManagerState = result.onError(this::handleException)
savedInstanceState.getParcelable("layoutManager"); .onSuccess(this::onBlogPostsLoaded)
} );
viewModel.getBlogRemoved().observe(getViewLifecycleOwner(), removed -> {
if (removed) finish();
});
return v; return v;
} }
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
sharingController.onStart(); viewModel.blockAndClearNotifications();
loadBlog();
loadSharedContacts();
loadBlogPosts(false);
list.startPeriodicUpdate(); list.startPeriodicUpdate();
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
sharingController.onStop(); viewModel.unblockNotifications();
list.stopPeriodicUpdate(); list.stopPeriodicUpdate();
} }
@Override
public void onDestroy() {
super.onDestroy();
blogController.unsetBlogSharingListener(this);
sharingController.unsetSharingListener(this);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
if (layoutManager != null) {
layoutManagerState = layoutManager.onSaveInstanceState();
outState.putParcelable("layoutManager", layoutManagerState);
}
}
@Override @Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.blogs_blog_actions, menu); inflater.inflate(R.menu.blogs_blog_actions, menu);
writeButton = menu.findItem(R.id.action_write_blog_post); MenuItem writeButton = menu.findItem(R.id.action_write_blog_post);
if (isMyBlog) writeButton.setVisible(true); MenuItem deleteButton = menu.findItem(R.id.action_blog_delete);
deleteButton = menu.findItem(R.id.action_blog_delete); viewModel.getBlog().observe(getViewLifecycleOwner(), blog -> {
if (canDeleteBlog) deleteButton.setEnabled(true); if (blog.isOurs()) writeButton.setVisible(true);
if (blog.canBeRemoved()) deleteButton.setEnabled(true);
});
super.onCreateOptionsMenu(menu, inflater); super.onCreateOptionsMenu(menu, inflater);
} }
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) { int itemId = item.getItemId();
case R.id.action_write_blog_post: if (itemId == R.id.action_write_blog_post) {
Intent i = new Intent(getActivity(), Intent i = new Intent(getActivity(), WriteBlogPostActivity.class);
WriteBlogPostActivity.class); i.putExtra(GROUP_ID, groupId.getBytes());
i.putExtra(GROUP_ID, groupId.getBytes()); startActivity(i);
startActivityForResult(i, REQUEST_WRITE_BLOG_POST); return true;
return true; } else if (itemId == R.id.action_blog_share) {
case R.id.action_blog_share: Intent i = new Intent(getActivity(), ShareBlogActivity.class);
Intent i2 = new Intent(getActivity(), ShareBlogActivity.class); i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP); i.putExtra(GROUP_ID, groupId.getBytes());
i2.putExtra(GROUP_ID, groupId.getBytes()); startActivityForResult(i, REQUEST_SHARE_BLOG);
startActivityForResult(i2, REQUEST_SHARE_BLOG); return true;
return true; } else if (itemId == R.id.action_blog_sharing_status) {
case R.id.action_blog_sharing_status: Intent i =
Intent i3 = new Intent(getActivity(), new Intent(getActivity(), BlogSharingStatusActivity.class);
BlogSharingStatusActivity.class); i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP); i.putExtra(GROUP_ID, groupId.getBytes());
i3.putExtra(GROUP_ID, groupId.getBytes()); startActivity(i);
startActivity(i3); return true;
return true; } else if (itemId == R.id.action_blog_delete) {
case R.id.action_blog_delete: showDeleteDialog();
showDeleteDialog(); return true;
return true;
default:
return super.onOptionsItemSelected(item);
} }
return super.onOptionsItemSelected(item);
} }
@Override @Override
public void onActivityResult(int request, int result, public void onActivityResult(int request, int result,
@Nullable Intent data) { @Nullable Intent data) {
super.onActivityResult(request, result, data); super.onActivityResult(request, result, data);
if (request == REQUEST_SHARE_BLOG && result == RESULT_OK) {
if (request == REQUEST_WRITE_BLOG_POST && result == RESULT_OK) {
displaySnackbar(R.string.blogs_blog_post_created, true);
loadBlogPosts(true);
} else if (request == REQUEST_SHARE_BLOG && result == RESULT_OK) {
displaySnackbar(R.string.blogs_sharing_snackbar, false); displaySnackbar(R.string.blogs_sharing_snackbar, false);
} }
} }
@@ -212,35 +169,26 @@ public class BlogFragment extends BaseFragment
return TAG; return TAG;
} }
@Override private void onBlogPostsLoaded(ListUpdate update) {
public void onBlogPostAdded(BlogPostHeader header, boolean local) { adapter.submitList(update.getItems(), () -> {
blogController.loadBlogPost(header, Boolean wasLocal = update.getPostAddedWasLocal();
new UiResultExceptionHandler<BlogPostItem, DbException>( if (wasLocal != null && wasLocal) {
this) { list.scrollToPosition(0);
@Override displaySnackbar(R.string.blogs_blog_post_created,
public void onResultUi(BlogPostItem post) { false);
adapter.add(post); } else if (wasLocal != null) {
if (local) { displaySnackbar(R.string.blogs_blog_post_received,
list.scrollToPosition(0); true);
displaySnackbar(R.string.blogs_blog_post_created, }
false); viewModel.resetLocalUpdate();
} else { list.showData();
displaySnackbar(R.string.blogs_blog_post_received, });
true);
}
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
}
);
} }
@Override @Override
public void onBlogPostClick(BlogPostItem post) { public void onBlogPostClick(BlogPostItem post) {
BlogPostFragment f = BlogPostFragment.newInstance(post.getId()); BlogPostFragment f =
BlogPostFragment.newInstance(groupId, post.getId());
showNextFragment(f); showNextFragment(f);
} }
@@ -256,111 +204,10 @@ public class BlogFragment extends BaseFragment
getContext().startActivity(i); getContext().startActivity(i);
} }
private void loadBlogPosts(boolean reload) {
blogController.loadBlogPosts(
new UiResultExceptionHandler<Collection<BlogPostItem>,
DbException>(this) {
@Override
public void onResultUi(Collection<BlogPostItem> posts) {
if (posts.isEmpty()) {
list.showData();
} else {
adapter.addAll(posts);
if (reload || layoutManagerState == null) {
list.scrollToPosition(0);
} else {
layoutManager.onRestoreInstanceState(
layoutManagerState);
}
}
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
}
private void loadBlog() {
blogController.loadBlog(
new UiResultExceptionHandler<BlogItem, DbException>(this) {
@Override
public void onResultUi(BlogItem blog) {
setToolbarTitle(blog.getBlog().getAuthor());
if (blog.isOurs())
showWriteButton();
if (blog.canBeRemoved())
enableDeleteButton();
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
}
private void setToolbarTitle(Author a) {
getActivity().setTitle(a.getName());
}
private void loadSharedContacts() {
blogController.loadSharingContacts(
new UiResultExceptionHandler<Collection<ContactId>,
DbException>(this) {
@Override
public void onResultUi(Collection<ContactId> contacts) {
sharingController.addAll(contacts);
int online = sharingController.getOnlineCount();
setToolbarSubTitle(contacts.size(), online);
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
}
@Override @Override
public void onBlogInvitationAccepted(ContactId c) { public void onLinkClick(String url) {
sharingController.add(c); LinkDialogFragment f = LinkDialogFragment.newInstance(url);
setToolbarSubTitle(sharingController.getTotalCount(), f.show(getParentFragmentManager(), f.getUniqueTag());
sharingController.getOnlineCount());
}
@Override
public void onBlogLeft(ContactId c) {
sharingController.remove(c);
setToolbarSubTitle(sharingController.getTotalCount(),
sharingController.getOnlineCount());
}
@Override
public void onSharingInfoUpdated(int total, int online) {
setToolbarSubTitle(total, online);
}
private void setToolbarSubTitle(int total, int online) {
ActionBar actionBar =
((BriarActivity) getActivity()).getSupportActionBar();
if (actionBar != null) {
actionBar.setSubtitle(
getString(R.string.shared_with, total, online));
}
}
private void showWriteButton() {
isMyBlog = true;
if (writeButton != null)
writeButton.setVisible(true);
}
private void enableDeleteButton() {
canDeleteBlog = true;
if (deleteButton != null)
deleteButton.setEnabled(true);
} }
private void displaySnackbar(int stringId, boolean scroll) { private void displaySnackbar(int stringId, boolean scroll) {
@@ -373,38 +220,21 @@ public class BlogFragment extends BaseFragment
} }
private void showDeleteDialog() { private void showDeleteDialog() {
DialogInterface.OnClickListener okListener = AlertDialog.Builder builder = new AlertDialog.Builder(requireContext(),
(dialog, which) -> deleteBlog();
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
R.style.BriarDialogTheme); R.style.BriarDialogTheme);
builder.setTitle(getString(R.string.blogs_remove_blog)); builder.setTitle(getString(R.string.blogs_remove_blog));
builder.setMessage( builder.setMessage(
getString(R.string.blogs_remove_blog_dialog_message)); getString(R.string.blogs_remove_blog_dialog_message));
builder.setPositiveButton(R.string.cancel, null); builder.setPositiveButton(R.string.cancel, null);
builder.setNegativeButton(R.string.blogs_remove_blog_ok, okListener); builder.setNegativeButton(R.string.blogs_remove_blog_ok,
(dialog, which) -> deleteBlog());
builder.show(); builder.show();
} }
private void deleteBlog() { private void deleteBlog() {
blogController.deleteBlog( viewModel.deleteBlog();
new UiResultExceptionHandler<Void, DbException>(this) { Toast.makeText(getActivity(), R.string.blogs_blog_removed, LENGTH_SHORT)
@Override .show();
public void onResultUi(Void result) {
Toast.makeText(getActivity(),
R.string.blogs_blog_removed, LENGTH_SHORT)
.show();
finish();
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
}
@Override
public void onBlogRemoved() {
finish(); finish();
} }

View File

@@ -1,35 +1,23 @@
package org.briarproject.briar.android.blog; package org.briarproject.briar.android.blog;
import org.briarproject.briar.android.activity.ActivityScope; import org.briarproject.briar.android.viewmodel.ViewModelKey;
import org.briarproject.briar.android.activity.BaseActivity;
import org.briarproject.briar.android.controller.SharingController;
import org.briarproject.briar.android.controller.SharingControllerImpl;
import androidx.lifecycle.ViewModel;
import dagger.Binds;
import dagger.Module; import dagger.Module;
import dagger.Provides; import dagger.multibindings.IntoMap;
@Module @Module
public class BlogModule { public interface BlogModule {
@ActivityScope @Binds
@Provides @IntoMap
BlogController provideBlogController(BaseActivity activity, @ViewModelKey(FeedViewModel.class)
BlogControllerImpl blogController) { ViewModel bindFeedViewModel(FeedViewModel feedViewModel);
activity.addLifecycleController(blogController);
return blogController;
}
@ActivityScope @Binds
@Provides @IntoMap
FeedController provideFeedController(FeedControllerImpl feedController) { @ViewModelKey(BlogViewModel.class)
return feedController; ViewModel bindBlogViewModel(BlogViewModel blogViewModel);
}
@ActivityScope
@Provides
SharingController provideSharingController(
SharingControllerImpl sharingController) {
return sharingController;
}
} }

View File

@@ -1,6 +1,5 @@
package org.briarproject.briar.android.blog; package org.briarproject.briar.android.blog;
import android.content.Context;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
@@ -8,52 +7,44 @@ import android.view.ViewGroup;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; 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.util.BriarAdapter;
import androidx.annotation.Nullable; import androidx.recyclerview.widget.DiffUtil;
import androidx.fragment.app.FragmentManager; import androidx.recyclerview.widget.ListAdapter;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class BlogPostAdapter extends BriarAdapter<BlogPostItem, BlogPostViewHolder> { class BlogPostAdapter extends ListAdapter<BlogPostItem, BlogPostViewHolder> {
private final boolean authorClickable;
private final OnBlogPostClickListener listener; private final OnBlogPostClickListener listener;
@Nullable
private final FragmentManager fragmentManager;
BlogPostAdapter(Context ctx, OnBlogPostClickListener listener, BlogPostAdapter(boolean authorClickable, OnBlogPostClickListener listener) {
@Nullable FragmentManager fragmentManager) { super(new DiffUtil.ItemCallback<BlogPostItem>() {
super(ctx, BlogPostItem.class); @Override
public boolean areItemsTheSame(BlogPostItem a, BlogPostItem b) {
return a.getId().equals(b.getId());
}
@Override
public boolean areContentsTheSame(BlogPostItem a, BlogPostItem b) {
return a.isRead() == b.isRead();
}
});
this.authorClickable = authorClickable;
this.listener = listener; this.listener = listener;
this.fragmentManager = fragmentManager;
} }
@Override @Override
public BlogPostViewHolder onCreateViewHolder(ViewGroup parent, public BlogPostViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) { int viewType) {
View v = LayoutInflater.from(ctx).inflate( View v = LayoutInflater.from(parent.getContext()).inflate(
R.layout.list_item_blog_post, parent, false); R.layout.list_item_blog_post, parent, false);
return new BlogPostViewHolder(v, false, listener, fragmentManager); return new BlogPostViewHolder(v, false, listener, authorClickable);
} }
@Override @Override
public void onBindViewHolder(BlogPostViewHolder ui, int position) { public void onBindViewHolder(BlogPostViewHolder ui, int position) {
ui.bindItem(getItemAt(position)); ui.bindItem(getItem(position));
}
@Override
public int compare(BlogPostItem a, BlogPostItem b) {
return a.compareTo(b);
}
@Override
public boolean areContentsTheSame(BlogPostItem a, BlogPostItem b) {
return a.isRead() == b.isRead();
}
@Override
public boolean areItemsTheSame(BlogPostItem a, BlogPostItem b) {
return a.getId().equals(b.getId());
} }
} }

View File

@@ -1,76 +1,159 @@
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.Looper;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ProgressBar;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId; import org.briarproject.bramble.api.sync.MessageId;
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.BaseController.BlogListener; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler; import org.briarproject.briar.android.widget.LinkDialogFragment;
import org.briarproject.briar.api.blog.BlogPostHeader;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModelProvider;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static android.view.View.INVISIBLE;
import static android.view.View.VISIBLE;
import static java.util.Objects.requireNonNull;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
@UiThread
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class BlogPostFragment extends BasePostFragment implements BlogListener { public class BlogPostFragment extends BaseFragment
implements OnBlogPostClickListener {
private static final String TAG = BlogPostFragment.class.getName(); private static final String TAG = BlogPostFragment.class.getName();
private static final Logger LOG = getLogger(TAG);
static final String POST_ID = "briar.POST_ID";
protected BlogViewModel viewModel;
private final Handler handler = new Handler(Looper.getMainLooper());
private ProgressBar progressBar;
private BlogPostViewHolder ui;
private BlogPostItem post;
private Runnable refresher;
@Inject @Inject
BlogController blogController; ViewModelProvider.Factory viewModelFactory;
static BlogPostFragment newInstance(MessageId postId) { static BlogPostFragment newInstance(GroupId blogId, MessageId postId) {
BlogPostFragment f = new BlogPostFragment(); BlogPostFragment f = new BlogPostFragment();
Bundle bundle = new Bundle(); Bundle bundle = new Bundle();
bundle.putByteArray(GROUP_ID, blogId.getBytes());
bundle.putByteArray(POST_ID, postId.getBytes()); bundle.putByteArray(POST_ID, postId.getBytes());
f.setArguments(bundle); f.setArguments(bundle);
return f; return f;
} }
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
viewModel = new ViewModelProvider(requireActivity(), viewModelFactory)
.get(BlogViewModel.class);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
Bundle args = requireArguments();
GroupId groupId =
new GroupId(requireNonNull(args.getByteArray(GROUP_ID)));
MessageId postId =
new MessageId(requireNonNull(args.getByteArray(POST_ID)));
View view = inflater.inflate(R.layout.fragment_blog_post, container,
false);
progressBar = view.findViewById(R.id.progressBar);
progressBar.setVisibility(VISIBLE);
ui = new BlogPostViewHolder(view, true, this, false);
LifecycleOwner owner = getViewLifecycleOwner();
viewModel.loadBlogPost(groupId, postId).observe(owner, result ->
result.onError(this::handleException)
.onSuccess(this::onBlogPostLoaded)
);
return view;
}
@Override
public void onStart() {
super.onStart();
startPeriodicUpdate();
}
@Override
public void onStop() {
super.onStop();
stopPeriodicUpdate();
}
@UiThread
private void onBlogPostLoaded(BlogPostItem post) {
progressBar.setVisibility(INVISIBLE);
this.post = post;
ui.bindItem(post);
}
@Override
public void onBlogPostClick(BlogPostItem post) {
// We're already there
}
@Override
public void onAuthorClick(BlogPostItem post) {
Intent i = new Intent(requireContext(), BlogActivity.class);
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
requireContext().startActivity(i);
}
@Override
public void onLinkClick(String url) {
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
f.show(getParentFragmentManager(), f.getUniqueTag());
}
private void startPeriodicUpdate() {
refresher = () -> {
LOG.info("Updating Content...");
ui.updateDate(post.getTimestamp());
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
};
LOG.info("Adding Handler Callback");
handler.postDelayed(refresher, MIN_DATE_RESOLUTION);
}
private void stopPeriodicUpdate() {
if (refresher != null) {
LOG.info("Removing Handler Callback");
handler.removeCallbacks(refresher);
}
}
@Override @Override
public String getUniqueTag() { public String getUniqueTag() {
return TAG; return TAG;
} }
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
public void onStart() {
super.onStart();
blogController.loadBlogPost(postId,
new UiResultExceptionHandler<BlogPostItem, DbException>(
this) {
@Override
public void onResultUi(BlogPostItem post) {
onBlogPostLoaded(post);
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
}
@Override
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
// doesn't matter here
}
@Override
public void onBlogRemoved() {
finish();
}
} }

View File

@@ -17,7 +17,7 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
private final BlogPostHeader header; private final BlogPostHeader header;
@Nullable @Nullable
protected String text; protected String text;
private boolean read; private final boolean read;
BlogPostItem(BlogPostHeader header, @Nullable String text) { BlogPostItem(BlogPostHeader header, @Nullable String text) {
this.header = header; this.header = header;
@@ -74,9 +74,6 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
protected static int compare(BlogPostHeader h1, BlogPostHeader h2) { protected static int compare(BlogPostHeader h1, BlogPostHeader h2) {
// The newest post comes first // The newest post comes first
long aTime = h1.getTimeReceived(), bTime = h2.getTimeReceived(); return Long.compare(h2.getTimeReceived(), h1.getTimeReceived());
if (aTime > bTime) return -1;
if (aTime < bTime) return 1;
return 0;
} }
} }

View File

@@ -9,31 +9,31 @@ import android.view.ViewGroup;
import android.widget.ImageButton; import android.widget.ImageButton;
import android.widget.TextView; import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
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.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;
import javax.annotation.Nullable;
import androidx.annotation.NonNull;
import androidx.annotation.UiThread; import androidx.annotation.UiThread;
import androidx.core.view.ViewCompat; import androidx.core.view.ViewCompat;
import androidx.fragment.app.FragmentManager;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import static android.view.View.GONE; import static android.view.View.GONE;
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.activity.BriarActivity.GROUP_ID;
import static org.briarproject.briar.android.blog.BasePostFragment.POST_ID; import static org.briarproject.briar.android.blog.BlogPostFragment.POST_ID;
import static org.briarproject.briar.android.util.UiUtils.TEASER_LENGTH; import static org.briarproject.briar.android.util.UiUtils.TEASER_LENGTH;
import static org.briarproject.briar.android.util.UiUtils.getSpanned; import static org.briarproject.briar.android.util.UiUtils.getSpanned;
import static org.briarproject.briar.android.util.UiUtils.getTeaser; import static org.briarproject.briar.android.util.UiUtils.getTeaser;
import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable; import static org.briarproject.briar.android.util.UiUtils.makeLinksClickable;
import static org.briarproject.briar.api.blog.MessageType.POST; import static org.briarproject.briar.android.view.AuthorView.COMMENTER;
import static org.briarproject.briar.android.view.AuthorView.REBLOGGER;
import static org.briarproject.briar.android.view.AuthorView.RSS_FEED_REBLOGGED;
@UiThread @UiThread
@NotNullByDefault
class BlogPostViewHolder extends RecyclerView.ViewHolder { class BlogPostViewHolder extends RecyclerView.ViewHolder {
private final Context ctx; private final Context ctx;
@@ -43,20 +43,16 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
private final ImageButton reblogButton; private final ImageButton reblogButton;
private final TextView text; private final TextView text;
private final ViewGroup commentContainer; private final ViewGroup commentContainer;
private final boolean fullText; private final boolean fullText, authorClickable;
@NonNull
private final OnBlogPostClickListener listener; private final OnBlogPostClickListener listener;
@Nullable
private final FragmentManager fragmentManager;
BlogPostViewHolder(View v, boolean fullText, BlogPostViewHolder(View v, boolean fullText,
@NonNull OnBlogPostClickListener listener, OnBlogPostClickListener listener, boolean authorClickable) {
@Nullable FragmentManager fragmentManager) {
super(v); super(v);
this.fullText = fullText; this.fullText = fullText;
this.listener = listener; this.listener = listener;
this.fragmentManager = fragmentManager; this.authorClickable = authorClickable;
ctx = v.getContext(); ctx = v.getContext();
layout = v.findViewById(R.id.postLayout); layout = v.findViewById(R.id.postLayout);
@@ -67,10 +63,6 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
commentContainer = v.findViewById(R.id.commentContainer); commentContainer = v.findViewById(R.id.commentContainer);
} }
void setVisibility(int visibility) {
layout.setVisibility(visibility);
}
void hideReblogButton() { void hideReblogButton() {
reblogButton.setVisibility(GONE); reblogButton.setVisibility(GONE);
} }
@@ -87,15 +79,15 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
return "blogPost" + id.hashCode(); return "blogPost" + id.hashCode();
} }
void bindItem(@Nullable BlogPostItem item) { void bindItem(BlogPostItem item) {
if (item == null) return;
setTransitionName(item.getId()); setTransitionName(item.getId());
if (!fullText) { if (!fullText) {
layout.setClickable(true); layout.setClickable(true);
layout.setOnClickListener(v -> listener.onBlogPostClick(item)); layout.setOnClickListener(v -> listener.onBlogPostClick(item));
} }
boolean isReblog = item instanceof BlogCommentItem;
// author and date // author and date
BlogPostHeader post = item.getPostHeader(); BlogPostHeader post = item.getPostHeader();
author.setAuthor(post.getAuthor(), post.getAuthorInfo()); author.setAuthor(post.getAuthor(), post.getAuthorInfo());
@@ -103,7 +95,7 @@ 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 (!fullText && item.getHeader().getType() == POST) { if (authorClickable && !isReblog) {
author.setAuthorClickable(v -> listener.onAuthorClick(item)); author.setAuthorClickable(v -> listener.onAuthorClick(item));
} else { } else {
author.setAuthorNotClickable(); author.setAuthorNotClickable();
@@ -114,7 +106,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
if (fullText) { if (fullText) {
text.setText(postText); text.setText(postText);
text.setTextIsSelectable(true); text.setTextIsSelectable(true);
makeLinksClickable(text, fragmentManager); makeLinksClickable(text, listener::onLinkClick);
} else { } else {
text.setTextIsSelectable(false); text.setTextIsSelectable(false);
if (postText.length() > TEASER_LENGTH) if (postText.length() > TEASER_LENGTH)
@@ -132,32 +124,33 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
// comments // comments
commentContainer.removeAllViews(); commentContainer.removeAllViews();
if (item instanceof BlogCommentItem) { if (isReblog) {
onBindComment((BlogCommentItem) item); onBindComment((BlogCommentItem) item, authorClickable);
} else { } else {
reblogger.setVisibility(GONE); reblogger.setVisibility(GONE);
} }
} }
private void onBindComment(BlogCommentItem item) { private void onBindComment(BlogCommentItem item, boolean authorClickable) {
// reblogger // reblogger
reblogger.setAuthor(item.getAuthor(), item.getAuthorInfo()); reblogger.setAuthor(item.getAuthor(), item.getAuthorInfo());
reblogger.setDate(item.getTimestamp()); reblogger.setDate(item.getTimestamp());
if (!fullText) { if (authorClickable) {
reblogger.setAuthorClickable(v -> listener.onAuthorClick(item)); reblogger.setAuthorClickable(v -> listener.onAuthorClick(item));
} else {
reblogger.setAuthorNotClickable();
} }
reblogger.setVisibility(VISIBLE); reblogger.setVisibility(VISIBLE);
reblogger.setPersona(AuthorView.REBLOGGER); reblogger.setPersona(REBLOGGER);
author.setPersona(item.getHeader().getRootPost().isRssFeed() ? author.setPersona(item.getHeader().getRootPost().isRssFeed() ?
AuthorView.RSS_FEED_REBLOGGED : RSS_FEED_REBLOGGED : COMMENTER);
AuthorView.COMMENTER);
// comments // comments
// TODO use nested RecyclerView instead like we do for Image Attachments
for (BlogCommentHeader c : item.getComments()) { for (BlogCommentHeader c : item.getComments()) {
View v = LayoutInflater.from(ctx) View v = LayoutInflater.from(ctx).inflate(
.inflate(R.layout.list_item_blog_comment, R.layout.list_item_blog_comment, commentContainer, false);
commentContainer, false);
AuthorView author = v.findViewById(R.id.authorView); AuthorView author = v.findViewById(R.id.authorView);
TextView text = v.findViewById(R.id.textView); TextView text = v.findViewById(R.id.textView);

View File

@@ -1,24 +1,24 @@
package org.briarproject.briar.android.blog; package org.briarproject.briar.android.blog;
import android.app.Activity; import android.app.Application;
import org.briarproject.bramble.api.contact.Contact; import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId; import org.briarproject.bramble.api.contact.ContactId;
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.db.DbException;
import org.briarproject.bramble.api.db.TransactionManager;
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.identity.IdentityManager; import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.identity.LocalAuthor; import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager; import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId; import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent; import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
import org.briarproject.briar.android.controller.ActivityLifecycleController; import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler; import org.briarproject.briar.android.sharing.SharingController;
import org.briarproject.briar.android.sharing.SharingController.SharingInfo;
import org.briarproject.briar.api.android.AndroidNotificationManager; import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogInvitationResponse; import org.briarproject.briar.api.blog.BlogInvitationResponse;
@@ -35,85 +35,54 @@ import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.Nullable; import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Level.WARNING; import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration; import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now; import static org.briarproject.bramble.util.LogUtils.now;
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
class BlogControllerImpl extends BaseControllerImpl class BlogViewModel extends BaseViewModel {
implements ActivityLifecycleController, BlogController, EventListener {
private static final Logger LOG = private static final Logger LOG = getLogger(BlogViewModel.class.getName());
Logger.getLogger(BlogControllerImpl.class.getName());
private final BlogSharingManager blogSharingManager; private final BlogSharingManager blogSharingManager;
private final SharingController sharingController;
// UI thread private volatile GroupId groupId;
@Nullable
private BlogSharingListener listener;
private volatile GroupId groupId = null; private final MutableLiveData<BlogItem> blog = new MutableLiveData<>();
private final MutableLiveData<Boolean> blogRemoved =
new MutableLiveData<>();
@Inject @Inject
BlogControllerImpl(@DatabaseExecutor Executor dbExecutor, BlogViewModel(Application application,
LifecycleManager lifecycleManager, EventBus eventBus, @DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager,
TransactionManager db,
AndroidExecutor androidExecutor,
EventBus eventBus,
IdentityManager identityManager,
AndroidNotificationManager notificationManager, AndroidNotificationManager notificationManager,
IdentityManager identityManager, BlogManager blogManager, BlogManager blogManager,
BlogSharingManager blogSharingManager) { BlogSharingManager blogSharingManager,
super(dbExecutor, lifecycleManager, eventBus, notificationManager, SharingController sharingController) {
identityManager, blogManager); super(application, dbExecutor, lifecycleManager, db, androidExecutor,
eventBus, identityManager, notificationManager, blogManager);
this.blogSharingManager = blogSharingManager; this.blogSharingManager = blogSharingManager;
} this.sharingController = sharingController;
@Override
public void onActivityCreate(Activity activity) {
}
@Override
public void onActivityStart() {
super.onStart();
notificationManager.blockNotification(groupId);
notificationManager.clearBlogPostNotification(groupId);
}
@Override
public void onActivityStop() {
super.onStop();
notificationManager.unblockNotification(groupId);
}
@Override
public void onActivityDestroy() {
}
@Override
public void setGroupId(GroupId g) {
groupId = g;
}
@Override
public void setBlogSharingListener(BlogSharingListener listener) {
this.listener = listener;
}
@Override
public void unsetBlogSharingListener(BlogSharingListener listener) {
if (this.listener == listener) this.listener = null;
} }
@Override @Override
public void eventOccurred(Event e) { public void eventOccurred(Event e) {
if (groupId == null || listener == null)
throw new IllegalStateException();
if (e instanceof BlogPostAddedEvent) { if (e instanceof BlogPostAddedEvent) {
BlogPostAddedEvent b = (BlogPostAddedEvent) e; BlogPostAddedEvent b = (BlogPostAddedEvent) e;
if (b.getGroupId().equals(groupId)) { if (b.getGroupId().equals(groupId)) {
LOG.info("Blog post added"); LOG.info("Blog post added");
listener.onBlogPostAdded(b.getHeader(), b.isLocal()); onBlogPostAdded(b.getHeader(), b.isLocal());
} }
} else if (e instanceof BlogInvitationResponseReceivedEvent) { } else if (e instanceof BlogInvitationResponseReceivedEvent) {
BlogInvitationResponseReceivedEvent b = BlogInvitationResponseReceivedEvent b =
@@ -121,41 +90,36 @@ class BlogControllerImpl extends BaseControllerImpl
BlogInvitationResponse r = b.getMessageHeader(); BlogInvitationResponse r = b.getMessageHeader();
if (r.getShareableId().equals(groupId) && r.wasAccepted()) { if (r.getShareableId().equals(groupId) && r.wasAccepted()) {
LOG.info("Blog invitation accepted"); LOG.info("Blog invitation accepted");
listener.onBlogInvitationAccepted(b.getContactId()); sharingController.add(b.getContactId());
} }
} else if (e instanceof ContactLeftShareableEvent) { } else if (e instanceof ContactLeftShareableEvent) {
ContactLeftShareableEvent s = (ContactLeftShareableEvent) e; ContactLeftShareableEvent s = (ContactLeftShareableEvent) e;
if (s.getGroupId().equals(groupId)) { if (s.getGroupId().equals(groupId)) {
LOG.info("Blog left by contact"); LOG.info("Blog left by contact");
listener.onBlogLeft(s.getContactId()); sharingController.remove(s.getContactId());
} }
} else if (e instanceof GroupRemovedEvent) { } else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e; GroupRemovedEvent g = (GroupRemovedEvent) e;
if (g.getGroup().getId().equals(groupId)) { if (g.getGroup().getId().equals(groupId)) {
LOG.info("Blog removed"); LOG.info("Blog removed");
listener.onBlogRemoved(); blogRemoved.setValue(true);
} }
} }
} }
@Override /**
public void loadBlogPosts( * Set this before calling any other methods.
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) { */
if (groupId == null) throw new IllegalStateException(); @UiThread
loadBlogPosts(groupId, handler); public void setGroupId(GroupId groupId, boolean loadAllPosts) {
if (this.groupId == groupId) return; // configuration change
this.groupId = groupId;
loadBlog(groupId);
if (loadAllPosts) loadBlogPosts(groupId);
loadSharingContacts(groupId);
} }
@Override private void loadBlog(GroupId groupId) {
public void loadBlogPost(MessageId m,
ResultExceptionHandler<BlogPostItem, DbException> handler) {
if (groupId == null) throw new IllegalStateException();
loadBlogPost(groupId, m, handler);
}
@Override
public void loadBlog(
ResultExceptionHandler<BlogItem, DbException> handler) {
if (groupId == null) throw new IllegalStateException();
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
long start = now(); long start = now();
@@ -163,50 +127,65 @@ class BlogControllerImpl extends BaseControllerImpl
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(b); boolean removable = blogManager.canBeRemoved(b);
BlogItem blog = new BlogItem(b, ours, removable); blog.postValue(new BlogItem(b, ours, removable));
logDuration(LOG, "Loading blog", start); logDuration(LOG, "Loading blog", start);
handler.onResult(blog);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); handleException(e);
handler.onException(e);
} }
}); });
} }
@Override void blockAndClearNotifications() {
public void deleteBlog(ResultExceptionHandler<Void, DbException> handler) { notificationManager.blockNotification(groupId);
if (groupId == null) throw new IllegalStateException(); notificationManager.clearBlogPostNotification(groupId);
}
void unblockNotifications() {
notificationManager.unblockNotification(groupId);
}
private void loadBlogPosts(GroupId groupId) {
loadFromDb(txn -> new ListUpdate(null, loadBlogPosts(txn, groupId)),
blogPosts::setValue);
}
private void loadSharingContacts(GroupId groupId) {
runOnDbThread(true, txn -> {
Collection<Contact> contacts =
blogSharingManager.getSharedWith(txn, groupId);
txn.attach(() -> onSharingContactsLoaded(contacts));
}, this::handleException);
}
@UiThread
private void onSharingContactsLoaded(Collection<Contact> contacts) {
Collection<ContactId> contactIds = new ArrayList<>(contacts.size());
for (Contact c : contacts) contactIds.add(c.getId());
sharingController.addAll(contactIds);
}
void deleteBlog() {
runOnDbThread(() -> { runOnDbThread(() -> {
try { try {
long start = now(); long start = now();
Blog b = blogManager.getBlog(groupId); Blog b = blogManager.getBlog(groupId);
blogManager.removeBlog(b); blogManager.removeBlog(b);
logDuration(LOG, "Removing blog", start); logDuration(LOG, "Removing blog", start);
handler.onResult(null);
} catch (DbException e) { } catch (DbException e) {
logException(LOG, WARNING, e); handleException(e);
handler.onException(e);
} }
}); });
} }
@Override LiveData<BlogItem> getBlog() {
public void loadSharingContacts( return blog;
ResultExceptionHandler<Collection<ContactId>, DbException> handler) {
if (groupId == null) throw new IllegalStateException();
runOnDbThread(() -> {
try {
Collection<Contact> contacts =
blogSharingManager.getSharedWith(groupId);
Collection<ContactId> contactIds =
new ArrayList<>(contacts.size());
for (Contact c : contacts) contactIds.add(c.getId());
handler.onResult(contactIds);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
} }
LiveData<Boolean> getBlogRemoved() {
return blogRemoved;
}
LiveData<SharingInfo> getSharingInfo() {
return sharingController.getSharingInfo();
}
} }

View File

@@ -1,32 +0,0 @@
package org.briarproject.briar.android.blog;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.blog.Blog;
import java.util.Collection;
import androidx.annotation.UiThread;
@NotNullByDefault
public interface FeedController extends BaseController {
void loadBlogPosts(
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler);
void loadPersonalBlog(ResultExceptionHandler<Blog, DbException> handler);
@UiThread
void setFeedListener(FeedListener listener);
@UiThread
void unsetFeedListener(FeedListener listener);
@NotNullByDefault
interface FeedListener extends BlogListener {
@UiThread
void onBlogAdded();
}
}

View File

@@ -1,143 +0,0 @@
package org.briarproject.briar.android.blog;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.NoSuchGroupException;
import org.briarproject.bramble.api.db.NoSuchMessageException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
import org.briarproject.bramble.api.sync.event.GroupAddedEvent;
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import androidx.annotation.Nullable;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
@MethodsNotNullByDefault
@ParametersNotNullByDefault
class FeedControllerImpl extends BaseControllerImpl implements FeedController {
private static final Logger LOG =
Logger.getLogger(FeedControllerImpl.class.getName());
// UI thread
@Nullable
private FeedListener listener;
@Inject
FeedControllerImpl(@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager, EventBus eventBus,
AndroidNotificationManager notificationManager,
IdentityManager identityManager, BlogManager blogManager) {
super(dbExecutor, lifecycleManager, eventBus, notificationManager,
identityManager, blogManager);
}
@Override
public void onStart() {
super.onStart();
if (listener == null) throw new IllegalStateException();
notificationManager.blockAllBlogPostNotifications();
notificationManager.clearAllBlogPostNotifications();
}
@Override
public void onStop() {
super.onStop();
notificationManager.unblockAllBlogPostNotifications();
}
@Override
public void setFeedListener(FeedListener listener) {
this.listener = listener;
}
@Override
public void unsetFeedListener(FeedListener listener) {
if (this.listener == listener) this.listener = null;
}
@Override
public void eventOccurred(Event e) {
if (listener == null) throw new IllegalStateException();
if (e instanceof BlogPostAddedEvent) {
BlogPostAddedEvent b = (BlogPostAddedEvent) e;
LOG.info("Blog post added");
listener.onBlogPostAdded(b.getHeader(), b.isLocal());
} else if (e instanceof GroupAddedEvent) {
GroupAddedEvent g = (GroupAddedEvent) e;
if (g.getGroup().getClientId().equals(CLIENT_ID)) {
LOG.info("Blog added");
listener.onBlogAdded();
}
} else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e;
if (g.getGroup().getClientId().equals(CLIENT_ID)) {
LOG.info("Blog removed");
listener.onBlogRemoved();
}
}
}
@Override
public void loadBlogPosts(
ResultExceptionHandler<Collection<BlogPostItem>, DbException> handler) {
runOnDbThread(() -> {
try {
long start = now();
Collection<BlogPostItem> posts = new ArrayList<>();
for (Blog b : blogManager.getBlogs()) {
try {
posts.addAll(loadItems(b.getId()));
} catch (NoSuchGroupException | NoSuchMessageException e) {
logException(LOG, WARNING, e);
}
}
logDuration(LOG, "Loading all posts", start);
handler.onResult(posts);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
@Override
public void loadPersonalBlog(
ResultExceptionHandler<Blog, DbException> handler) {
runOnDbThread(() -> {
try {
long start = now();
Author a = identityManager.getLocalAuthor();
Blog b = blogManager.getPersonalBlog(a);
logDuration(LOG, "Loading personal blog", start);
handler.onResult(b);
} catch (DbException e) {
logException(LOG, WARNING, e);
handler.onException(e);
}
});
}
}

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.os.Parcelable;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.Menu; import android.view.Menu;
import android.view.MenuInflater; import android.view.MenuInflater;
@@ -10,53 +9,43 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault; import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault; import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
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.blog.FeedController.FeedListener; import org.briarproject.briar.android.blog.BaseViewModel.ListUpdate;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import org.briarproject.briar.android.fragment.BaseFragment; import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.util.BriarSnackbarBuilder; import org.briarproject.briar.android.util.BriarSnackbarBuilder;
import org.briarproject.briar.android.view.BriarRecyclerView; import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.android.widget.LinkDialogFragment;
import org.briarproject.briar.api.blog.Blog; import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogPostHeader;
import java.util.Collection;
import java.util.logging.Logger;
import javax.inject.Inject; import javax.inject.Inject;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.UiThread; import androidx.lifecycle.ViewModelProvider;
import androidx.recyclerview.widget.LinearLayoutManager; import androidx.recyclerview.widget.LinearLayoutManager;
import static android.app.Activity.RESULT_OK;
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP; import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
import static com.google.android.material.snackbar.Snackbar.LENGTH_LONG; import static com.google.android.material.snackbar.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.blog.BlogPostFragment.POST_ID;
@UiThread
@MethodsNotNullByDefault @MethodsNotNullByDefault
@ParametersNotNullByDefault @ParametersNotNullByDefault
public class FeedFragment extends BaseFragment implements public class FeedFragment extends BaseFragment
OnBlogPostClickListener, FeedListener { implements OnBlogPostClickListener {
public final static String TAG = FeedFragment.class.getName(); public final static String TAG = FeedFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
@Inject @Inject
FeedController feedController; ViewModelProvider.Factory viewModelFactory;
private BlogPostAdapter adapter; private FeedViewModel viewModel;
private final BlogPostAdapter adapter = new BlogPostAdapter(true, this);
private LinearLayoutManager layoutManager; private LinearLayoutManager layoutManager;
private BriarRecyclerView list; private BriarRecyclerView list;
@Nullable
private Blog personalBlog;
@Nullable
private Parcelable layoutManagerState;
public static FeedFragment newInstance() { public static FeedFragment newInstance() {
FeedFragment f = new FeedFragment(); FeedFragment f = new FeedFragment();
@@ -70,7 +59,8 @@ public class FeedFragment extends BaseFragment implements
@Override @Override
public void injectFragment(ActivityComponent component) { public void injectFragment(ActivityComponent component) {
component.inject(this); component.inject(this);
feedController.setFeedListener(this); viewModel = new ViewModelProvider(this, viewModelFactory)
.get(FeedViewModel.class);
} }
@Nullable @Nullable
@@ -82,9 +72,6 @@ public class FeedFragment extends BaseFragment implements
View v = inflater.inflate(R.layout.fragment_blog, container, false); View v = inflater.inflate(R.layout.fragment_blog, container, false);
adapter =
new BlogPostAdapter(getActivity(), this, getFragmentManager());
layoutManager = new LinearLayoutManager(getActivity()); layoutManager = new LinearLayoutManager(getActivity());
list = v.findViewById(R.id.postList); list = v.findViewById(R.id.postList);
list.setLayoutManager(layoutManager); list.setLayoutManager(layoutManager);
@@ -93,103 +80,39 @@ public class FeedFragment extends BaseFragment implements
list.setEmptyText(R.string.blogs_feed_empty_state); list.setEmptyText(R.string.blogs_feed_empty_state);
list.setEmptyAction(R.string.blogs_feed_empty_state_action); list.setEmptyAction(R.string.blogs_feed_empty_state_action);
if (savedInstanceState != null) { viewModel.getBlogPosts().observe(getViewLifecycleOwner(), result ->
layoutManagerState = result.onError(this::handleException)
savedInstanceState.getParcelable("layoutManager"); .onSuccess(this::onBlogPostsLoaded)
} );
return v; return v;
} }
@Override
public void onActivityResult(int requestCode, int resultCode,
@Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
// The BlogPostAddedEvent arrives when the controller is not listening
if (requestCode == REQUEST_WRITE_BLOG_POST && resultCode == RESULT_OK) {
showSnackBar(R.string.blogs_blog_post_created);
}
}
@Override @Override
public void onStart() { public void onStart() {
super.onStart(); super.onStart();
feedController.onStart(); viewModel.blockAndClearAllBlogPostNotifications();
list.startPeriodicUpdate(); list.startPeriodicUpdate();
loadPersonalBlog();
loadBlogPosts(false);
} }
@Override @Override
public void onStop() { public void onStop() {
super.onStop(); super.onStop();
feedController.onStop(); viewModel.unblockAllBlogPostNotifications();
adapter.clear();
list.showProgressBar();
list.stopPeriodicUpdate(); list.stopPeriodicUpdate();
// TODO save list position in database/preferences?
} }
@Override private void onBlogPostsLoaded(ListUpdate update) {
public void onDestroy() { adapter.submitList(update.getItems(), () -> {
super.onDestroy(); Boolean wasLocal = update.getPostAddedWasLocal();
feedController.unsetFeedListener(this); if (wasLocal != null && wasLocal) {
} showSnackBar(R.string.blogs_blog_post_created);
} else if (wasLocal != null) {
@Override showSnackBar(R.string.blogs_blog_post_received);
public void onSaveInstanceState(Bundle outState) { }
super.onSaveInstanceState(outState); viewModel.resetLocalUpdate();
if (layoutManager != null) { list.showData();
layoutManagerState = layoutManager.onSaveInstanceState(); });
outState.putParcelable("layoutManager", layoutManagerState);
}
}
private void loadPersonalBlog() {
feedController.loadPersonalBlog(
new UiResultExceptionHandler<Blog, DbException>(this) {
@Override
public void onResultUi(Blog b) {
personalBlog = b;
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
}
private void loadBlogPosts(boolean clear) {
int revision = adapter.getRevision();
feedController.loadBlogPosts(
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
this) {
@Override
public void onResultUi(Collection<BlogPostItem> posts) {
if (revision == adapter.getRevision()) {
adapter.incrementRevision();
if (clear) adapter.setItems(posts);
else adapter.addAll(posts);
if (posts.isEmpty()) list.showData();
if (layoutManagerState == null) {
list.scrollToPosition(0); // Scroll to the top
} else {
layoutManager.onRestoreInstanceState(
layoutManagerState);
}
} else {
LOG.info("Concurrent update, reloading");
loadBlogPosts(clear);
}
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
} }
@Override @Override
@@ -200,67 +123,46 @@ public class FeedFragment extends BaseFragment implements
@Override @Override
public boolean onOptionsItemSelected(MenuItem item) { public boolean onOptionsItemSelected(MenuItem item) {
if (personalBlog == null) return false; int itemId = item.getItemId();
switch (item.getItemId()) { if (itemId == R.id.action_write_blog_post) {
case R.id.action_write_blog_post: Blog personalBlog = viewModel.getPersonalBlog().getValue();
Intent i1 = if (personalBlog == null) return false;
new Intent(getActivity(), WriteBlogPostActivity.class); Intent i = new Intent(getActivity(), WriteBlogPostActivity.class);
i1.putExtra(GROUP_ID, personalBlog.getId().getBytes()); i.putExtra(GROUP_ID, personalBlog.getId().getBytes());
startActivityForResult(i1, REQUEST_WRITE_BLOG_POST); startActivity(i);
return true; return true;
case R.id.action_rss_feeds_import: } else if (itemId == R.id.action_rss_feeds_import) {
Intent i2 = Intent i = new Intent(getActivity(), RssFeedImportActivity.class);
new Intent(getActivity(), RssFeedImportActivity.class); startActivity(i);
startActivity(i2); return true;
return true; } else if (itemId == R.id.action_rss_feeds_manage) {
case R.id.action_rss_feeds_manage: Blog personalBlog = viewModel.getPersonalBlog().getValue();
Intent i3 = if (personalBlog == null) return false;
new Intent(getActivity(), RssFeedManageActivity.class); Intent i = new Intent(getActivity(), RssFeedManageActivity.class);
i3.putExtra(GROUP_ID, personalBlog.getId().getBytes()); i.putExtra(GROUP_ID, personalBlog.getId().getBytes());
startActivity(i3); startActivity(i);
return true; return true;
default:
return super.onOptionsItemSelected(item);
} }
} return super.onOptionsItemSelected(item);
@Override
public void onBlogPostAdded(BlogPostHeader header, boolean local) {
feedController.loadBlogPost(header,
new UiResultExceptionHandler<BlogPostItem, DbException>(
this) {
@Override
public void onResultUi(BlogPostItem post) {
adapter.incrementRevision();
adapter.add(post);
if (local) {
showSnackBar(R.string.blogs_blog_post_created);
} else {
showSnackBar(R.string.blogs_blog_post_received);
}
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
}
);
} }
@Override @Override
public void onBlogPostClick(BlogPostItem post) { public void onBlogPostClick(BlogPostItem post) {
FeedPostFragment f = Intent i = getBlogActivityIntent(post.getGroupId());
FeedPostFragment.newInstance(post.getGroupId(), post.getId()); i.putExtra(POST_ID, post.getId().getBytes());
showNextFragment(f); requireContext().startActivity(i);
} }
@Override @Override
public void onAuthorClick(BlogPostItem post) { public void onAuthorClick(BlogPostItem post) {
Intent i = new Intent(getContext(), BlogActivity.class); Intent i = getBlogActivityIntent(post.getGroupId());
i.putExtra(GROUP_ID, post.getGroupId().getBytes()); requireContext().startActivity(i);
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP); }
getContext().startActivity(i);
@Override
public void onLinkClick(String url) {
LinkDialogFragment f = LinkDialogFragment.newInstance(url);
f.show(getParentFragmentManager(), f.getUniqueTag());
} }
@Override @Override
@@ -268,6 +170,13 @@ public class FeedFragment extends BaseFragment implements
return TAG; return TAG;
} }
private Intent getBlogActivityIntent(GroupId groupId) {
Intent i = new Intent(requireContext(), BlogActivity.class);
i.putExtra(GROUP_ID, groupId.getBytes());
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
return i;
}
private void showSnackBar(int stringRes) { private void showSnackBar(int stringRes) {
int firstVisible = int firstVisible =
layoutManager.findFirstCompletelyVisibleItemPosition(); layoutManager.findFirstCompletelyVisibleItemPosition();
@@ -283,14 +192,4 @@ public class FeedFragment extends BaseFragment implements
sb.make(list, stringRes, LENGTH_LONG).show(); sb.make(list, stringRes, LENGTH_LONG).show();
} }
@Override
public void onBlogAdded() {
loadBlogPosts(false);
}
@Override
public void onBlogRemoved() {
loadBlogPosts(true);
}
} }

View File

@@ -1,87 +0,0 @@
package org.briarproject.briar.android.blog;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
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.MessageId;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
import javax.annotation.Nullable;
import javax.inject.Inject;
import androidx.annotation.UiThread;
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
@UiThread
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class FeedPostFragment extends BasePostFragment {
private static final String TAG = FeedPostFragment.class.getName();
private GroupId blogId;
@Inject
FeedController feedController;
static FeedPostFragment newInstance(GroupId blogId, MessageId postId) {
FeedPostFragment f = new FeedPostFragment();
Bundle bundle = new Bundle();
bundle.putByteArray(GROUP_ID, blogId.getBytes());
bundle.putByteArray(POST_ID, postId.getBytes());
f.setArguments(bundle);
return f;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
Bundle args = requireArguments();
byte[] b = args.getByteArray(GROUP_ID);
if (b == null) throw new IllegalStateException("No group ID in args");
blogId = new GroupId(b);
return super.onCreateView(inflater, container, savedInstanceState);
}
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void onStart() {
super.onStart();
feedController.loadBlogPost(blogId, postId,
new UiResultExceptionHandler<BlogPostItem, DbException>(
this) {
@Override
public void onResultUi(BlogPostItem post) {
onBlogPostLoaded(post);
}
@Override
public void onExceptionUi(DbException exception) {
handleException(exception);
}
});
}
}

View File

@@ -0,0 +1,133 @@
package org.briarproject.briar.android.blog;
import android.app.Application;
import org.briarproject.bramble.api.db.DatabaseExecutor;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.db.TransactionManager;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.event.GroupRemovedEvent;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.viewmodel.LiveResult;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import javax.inject.Inject;
import androidx.annotation.UiThread;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import static java.util.logging.Logger.getLogger;
import static org.briarproject.bramble.util.LogUtils.logDuration;
import static org.briarproject.bramble.util.LogUtils.now;
import static org.briarproject.briar.api.blog.BlogManager.CLIENT_ID;
@NotNullByDefault
class FeedViewModel extends BaseViewModel {
private static final Logger LOG = getLogger(FeedViewModel.class.getName());
private final MutableLiveData<Blog> personalBlog = new MutableLiveData<>();
@Inject
FeedViewModel(Application application,
@DatabaseExecutor Executor dbExecutor,
LifecycleManager lifecycleManager,
TransactionManager db,
AndroidExecutor androidExecutor,
EventBus eventBus,
IdentityManager identityManager,
AndroidNotificationManager notificationManager,
BlogManager blogManager) {
super(application, dbExecutor, lifecycleManager, db, androidExecutor,
eventBus, identityManager, notificationManager, blogManager);
loadPersonalBlog();
loadAllBlogPosts();
}
@Override
public void eventOccurred(Event e) {
if (e instanceof BlogPostAddedEvent) {
BlogPostAddedEvent b = (BlogPostAddedEvent) e;
LOG.info("Blog post added");
onBlogPostAdded(b.getHeader(), b.isLocal());
} else if (e instanceof GroupRemovedEvent) {
GroupRemovedEvent g = (GroupRemovedEvent) e;
if (g.getGroup().getClientId().equals(CLIENT_ID)) {
LOG.info("Blog removed");
onBlogRemoved(g.getGroup().getId());
}
}
}
void blockAndClearAllBlogPostNotifications() {
notificationManager.blockAllBlogPostNotifications();
notificationManager.clearAllBlogPostNotifications();
}
void unblockAllBlogPostNotifications() {
notificationManager.unblockAllBlogPostNotifications();
}
private void loadPersonalBlog() {
runOnDbThread(() -> {
try {
long start = now();
Author a = identityManager.getLocalAuthor();
Blog b = blogManager.getPersonalBlog(a);
logDuration(LOG, "Loading personal blog", start);
personalBlog.postValue(b);
} catch (DbException e) {
handleException(e);
}
});
}
LiveData<Blog> getPersonalBlog() {
return personalBlog;
}
private void loadAllBlogPosts() {
loadFromDb(this::loadAllBlogPosts, blogPosts::setValue);
}
@DatabaseExecutor
private ListUpdate loadAllBlogPosts(Transaction txn)
throws DbException {
long start = now();
List<BlogPostItem> posts = new ArrayList<>();
for (GroupId g : blogManager.getBlogIds(txn)) {
posts.addAll(loadBlogPosts(txn, g));
}
Collections.sort(posts);
logDuration(LOG, "Loading all posts", start);
return new ListUpdate(null, posts);
}
@UiThread
private void onBlogRemoved(GroupId g) {
List<BlogPostItem> items = removeListItems(getBlogPostItems(), item ->
item.getGroupId().equals(g)
);
if (items != null) {
blogPosts.setValue(new LiveResult<>(new ListUpdate(null, items)));
}
}
}

View File

@@ -5,4 +5,6 @@ interface OnBlogPostClickListener {
void onBlogPostClick(BlogPostItem post); void onBlogPostClick(BlogPostItem post);
void onAuthorClick(BlogPostItem post); void onAuthorClick(BlogPostItem post);
void onLinkClick(String url);
} }

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