mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 18:59:06 +01:00
Compare commits
98 Commits
831_refact
...
beta-0.16.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6e931e9ba5 | ||
|
|
7e749124bf | ||
|
|
5822eb7808 | ||
|
|
7a7e086541 | ||
|
|
abab3167c2 | ||
|
|
8d08570568 | ||
|
|
2007078f13 | ||
|
|
dfb71a7978 | ||
|
|
480b0e3a03 | ||
|
|
8f8751f4ac | ||
|
|
de2ea112ee | ||
|
|
6f99a53fd9 | ||
|
|
a8a9b9032d | ||
|
|
6b15fb89de | ||
|
|
a711d6b8a1 | ||
|
|
5678f8aaa4 | ||
|
|
2fe37f6c26 | ||
|
|
a879747968 | ||
|
|
95e8fd7ee0 | ||
|
|
4416aaaa4c | ||
|
|
500d5f0efe | ||
|
|
fc8978fd90 | ||
|
|
73df126bd4 | ||
|
|
9146488c7d | ||
|
|
613a7fe376 | ||
|
|
ecb62f00d4 | ||
|
|
c4540a03cd | ||
|
|
3e31da99b5 | ||
|
|
098c1d0b1e | ||
|
|
178e908c86 | ||
|
|
ecf7cf14ae | ||
|
|
09e2a15a73 | ||
|
|
ab387860a6 | ||
|
|
f63fc94f2b | ||
|
|
41e5928cca | ||
|
|
8303175494 | ||
|
|
151eb6935b | ||
|
|
6a419c0c7b | ||
|
|
1795b32121 | ||
|
|
01971768ce | ||
|
|
ef7483ab01 | ||
|
|
527d11473d | ||
|
|
775dadc9a0 | ||
|
|
800b10a988 | ||
|
|
c977bf047d | ||
|
|
660a25f21d | ||
|
|
e7fd6d23af | ||
|
|
46982897f0 | ||
|
|
d24de68d64 | ||
|
|
7514c46a3f | ||
|
|
6632c0f8e3 | ||
|
|
79aafcda69 | ||
|
|
05af21e8dc | ||
|
|
0dc62cbbdc | ||
|
|
f3a084cfd2 | ||
|
|
8b32f82566 | ||
|
|
d598b6ed44 | ||
|
|
f5dc6f24b9 | ||
|
|
37454392da | ||
|
|
de7f9111d3 | ||
|
|
96d2889a6c | ||
|
|
f6412d1e9a | ||
|
|
b377cd6b1c | ||
|
|
f6cdbda5bb | ||
|
|
855c600a3e | ||
|
|
ea6e8303b0 | ||
|
|
d4934040d9 | ||
|
|
3449677b24 | ||
|
|
1ad3a6646e | ||
|
|
2d10f6b2bd | ||
|
|
5b05424d83 | ||
|
|
0826022d82 | ||
|
|
a901bfb9cb | ||
|
|
03cdce122a | ||
|
|
f2e0e16969 | ||
|
|
0c441e2ff3 | ||
|
|
21302304a5 | ||
|
|
6839d8b844 | ||
|
|
aee65a716c | ||
|
|
6a07d8f2c9 | ||
|
|
3c1ea81cd0 | ||
|
|
025f417bc7 | ||
|
|
c9dcd906c9 | ||
|
|
7024e04d15 | ||
|
|
0b8ac947db | ||
|
|
948410a064 | ||
|
|
2841339cac | ||
|
|
e8e82bd805 | ||
|
|
6876f40a0e | ||
|
|
5f4e1ecdfd | ||
|
|
044719432a | ||
|
|
d1a929da85 | ||
|
|
2a8978a60d | ||
|
|
c0afad7a26 | ||
|
|
37281c6c23 | ||
|
|
76a5e25656 | ||
|
|
3575b74837 | ||
|
|
f1c7996960 |
20
.gitlab-ci.yml
Normal file
20
.gitlab-ci.yml
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
image: registry.gitlab.com/fdroid/ci-images-base:latest
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- .gradle/wrapper
|
||||||
|
- .gradle/caches
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||||
|
# - export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
|
||||||
|
# - echo y | android --silent update sdk --no-ui --filter android-${ANDROID_COMPILE_SDK}
|
||||||
|
|
||||||
|
test:
|
||||||
|
script:
|
||||||
|
- ./gradlew test
|
||||||
|
|
||||||
|
after_script:
|
||||||
|
# this file changes every time but should not be cached
|
||||||
|
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||||
|
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||||
@@ -12,8 +12,8 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 22
|
targetSdkVersion 22
|
||||||
versionCode 1
|
versionCode 14
|
||||||
versionName "1.0"
|
versionName "0.14"
|
||||||
consumerProguardFiles 'proguard-rules.txt'
|
consumerProguardFiles 'proguard-rules.txt'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -25,38 +25,38 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':bramble-core')
|
compile project(':bramble-core')
|
||||||
compile fileTree(dir: 'libs', include: ['*.jar'])
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
provided 'javax.annotation:jsr250-api:1.0'
|
provided 'javax.annotation:jsr250-api:1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
def torBinaryDir = 'src/main/res/raw'
|
def torBinaryDir = 'src/main/res/raw'
|
||||||
|
|
||||||
task downloadTorGeoIp(type: Download) {
|
task downloadTorGeoIp(type: Download) {
|
||||||
src 'https://briarproject.org/build/geoip-2015-12-01.zip'
|
src 'https://briarproject.org/build/geoip-2017-05-02.zip'
|
||||||
dest "$torBinaryDir/geoip.zip"
|
dest "$torBinaryDir/geoip.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryArm(type: Download) {
|
task downloadTorBinaryArm(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-arm.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.11-arm.zip'
|
||||||
dest "$torBinaryDir/tor_arm.zip"
|
dest "$torBinaryDir/tor_arm.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryArmPie(type: Download) {
|
task downloadTorBinaryArmPie(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-arm-pie.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.11-arm-pie.zip'
|
||||||
dest "$torBinaryDir/tor_arm_pie.zip"
|
dest "$torBinaryDir/tor_arm_pie.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryX86(type: Download) {
|
task downloadTorBinaryX86(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-x86.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.11-x86.zip'
|
||||||
dest "$torBinaryDir/tor_x86.zip"
|
dest "$torBinaryDir/tor_x86.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
|
|
||||||
task downloadTorBinaryX86Pie(type: Download) {
|
task downloadTorBinaryX86Pie(type: Download) {
|
||||||
src 'https://briarproject.org/build/tor-0.2.7.6-x86-pie.zip'
|
src 'https://briarproject.org/build/tor-0.2.9.11-x86-pie.zip'
|
||||||
dest "$torBinaryDir/tor_x86_pie.zip"
|
dest "$torBinaryDir/tor_x86_pie.zip"
|
||||||
onlyIfNewer true
|
onlyIfNewer true
|
||||||
}
|
}
|
||||||
@@ -64,31 +64,31 @@ task downloadTorBinaryX86Pie(type: Download) {
|
|||||||
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
|
task verifyTorGeoIp(type: Verify, dependsOn: 'downloadTorGeoIp') {
|
||||||
src "$torBinaryDir/geoip.zip"
|
src "$torBinaryDir/geoip.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum '9bcdaf0a7ba0933735328d8ec466c25c25dbb459efc2bce9e55c774eabea5162'
|
checksum '51f4d1272fb867e1f3b36b67a584e2a33c40b40f62305457d799fd399cd77c9b'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
|
task verifyTorBinaryArm(type: Verify, dependsOn: 'downloadTorBinaryArm') {
|
||||||
src "$torBinaryDir/tor_arm.zip"
|
src "$torBinaryDir/tor_arm.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum '83272962eda701cd5d74d2418651c4ff0f0b1dff51f558a292d1a1c42bf12146'
|
checksum '1da6008663a8ad98b349e62acbbf42c379f65ec504fa467cb119c187cd5a4c6b'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
|
task verifyTorBinaryArmPie(type: Verify, dependsOn: 'downloadTorBinaryArmPie') {
|
||||||
src "$torBinaryDir/tor_arm_pie.zip"
|
src "$torBinaryDir/tor_arm_pie.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum 'd0300d1e45de11ebb24ed62b9c492be9c2e88590b7822195ab38c7a76ffcf646'
|
checksum 'eb061f880829e05f104690ac744848133f2dacef04759d425a2cff0df32c271e'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
|
task verifyTorBinaryX86(type: Verify, dependsOn: 'downloadTorBinaryX86') {
|
||||||
src "$torBinaryDir/tor_x86.zip"
|
src "$torBinaryDir/tor_x86.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum 'b8813d97b01ee1b9c9a4233c1b9bbe9f9f6b494ae6f9cbd84de8a3911911615e'
|
checksum 'f5308aff8303daca082f82227d02b51ddedba4ab1d1420739ada0427ae5dbb41'
|
||||||
}
|
}
|
||||||
|
|
||||||
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
|
task verifyTorBinaryX86Pie(type: Verify, dependsOn: 'downloadTorBinaryX86Pie') {
|
||||||
src "$torBinaryDir/tor_x86_pie.zip"
|
src "$torBinaryDir/tor_x86_pie.zip"
|
||||||
algorithm 'SHA-256'
|
algorithm 'SHA-256'
|
||||||
checksum '9c66e765aa196dc089951a1b2140cc8290305c2fcbf365121f99e01a233baf4e'
|
checksum '889a6c81ac73d05d35ed610ca5a913cee44d333e4ae1749c2a107f2f7dd8197b'
|
||||||
}
|
}
|
||||||
|
|
||||||
project.afterEvaluate {
|
project.afterEvaluate {
|
||||||
|
|||||||
Binary file not shown.
@@ -7,12 +7,12 @@ apply plugin: 'witness'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile "com.google.dagger:dagger:2.0.2"
|
compile "com.google.dagger:dagger:2.0.2"
|
||||||
compile 'com.google.dagger:dagger-compiler:2.0.2'
|
compile 'com.google.dagger:dagger-compiler:2.0.2'
|
||||||
compile 'com.google.code.findbugs:jsr305:3.0.1'
|
compile 'com.google.code.findbugs:jsr305:3.0.2'
|
||||||
|
|
||||||
testCompile 'junit:junit:4.12'
|
testCompile 'junit:junit:4.12'
|
||||||
testCompile "org.jmock:jmock:2.8.1"
|
testCompile "org.jmock:jmock:2.8.2"
|
||||||
testCompile "org.jmock:jmock-junit4:2.8.1"
|
testCompile "org.jmock:jmock-junit4:2.8.2"
|
||||||
testCompile "org.jmock:jmock-legacy:2.8.1"
|
testCompile "org.jmock:jmock-legacy:2.8.2"
|
||||||
testCompile "org.hamcrest:hamcrest-library:1.3"
|
testCompile "org.hamcrest:hamcrest-library:1.3"
|
||||||
testCompile "org.hamcrest:hamcrest-core:1.3"
|
testCompile "org.hamcrest:hamcrest-core:1.3"
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,7 @@ dependencyVerification {
|
|||||||
verify = [
|
verify = [
|
||||||
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
'com.google.dagger:dagger:84c0282ed8be73a29e0475d639da030b55dee72369e58dd35ae7d4fe6243dcf9',
|
||||||
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
'com.google.dagger:dagger-compiler:b74bc9de063dd4c6400b232231f2ef5056145b8fbecbf5382012007dd1c071b3',
|
||||||
'com.google.code.findbugs:jsr305:c885ce34249682bc0236b4a7d56efcc12048e6135a5baf7a9cde8ad8cda13fcd',
|
'com.google.code.findbugs:jsr305:766ad2a0783f2687962c8ad74ceecc38a28b9f72a2d085ee438b7813e928d0c7',
|
||||||
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
'javax.inject:javax.inject:91c77044a50c481636c32d916fd89c9118a72195390452c81065080f957de7ff',
|
||||||
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
'com.google.dagger:dagger-producers:99ec15e8a0507ba569e7655bc1165ee5e5ca5aa914b3c8f7e2c2458f724edd6b',
|
||||||
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
'com.google.guava:guava:d664fbfc03d2e5ce9cab2a44fb01f1d0bf9dfebeccc1a473b1f9ea31f79f6f99',
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package org.briarproject.bramble.api;
|
package org.briarproject.bramble.api;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
|
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
@@ -53,6 +54,12 @@ public class Bytes implements Comparable<Bytes> {
|
|||||||
return aBytes.length - bBytes.length;
|
return aBytes.length - bBytes.length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return getClass().getSimpleName() +
|
||||||
|
"(" + StringUtils.toHexString(getBytes()) + ")";
|
||||||
|
}
|
||||||
|
|
||||||
public static class BytesComparator implements Comparator<Bytes> {
|
public static class BytesComparator implements Comparator<Bytes> {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -137,7 +137,8 @@ public interface CryptoComponent {
|
|||||||
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
TransportKeys rotateTransportKeys(TransportKeys k, long rotationPeriod);
|
||||||
|
|
||||||
/** Encodes the pseudo-random tag that is used to recognise a stream. */
|
/** Encodes the pseudo-random tag that is used to recognise a stream. */
|
||||||
void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber);
|
void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||||
|
long streamNumber);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Signs the given byte[] with the given PrivateKey.
|
* Signs the given byte[] with the given PrivateKey.
|
||||||
|
|||||||
@@ -4,6 +4,11 @@ import org.briarproject.bramble.api.crypto.SecretKey;
|
|||||||
|
|
||||||
public interface TransportConstants {
|
public interface TransportConstants {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The current version of the transport protocol.
|
||||||
|
*/
|
||||||
|
int PROTOCOL_VERSION = 3;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the pseudo-random tag in bytes.
|
* The length of the pseudo-random tag in bytes.
|
||||||
*/
|
*/
|
||||||
@@ -14,21 +19,22 @@ public interface TransportConstants {
|
|||||||
*/
|
*/
|
||||||
int STREAM_HEADER_NONCE_LENGTH = 24;
|
int STREAM_HEADER_NONCE_LENGTH = 24;
|
||||||
|
|
||||||
/**
|
|
||||||
* The length of the stream header initialisation vector (IV) in bytes.
|
|
||||||
*/
|
|
||||||
int STREAM_HEADER_IV_LENGTH = STREAM_HEADER_NONCE_LENGTH - 8;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the message authentication code (MAC) in bytes.
|
* The length of the message authentication code (MAC) in bytes.
|
||||||
*/
|
*/
|
||||||
int MAC_LENGTH = 16;
|
int MAC_LENGTH = 16;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The length of the stream header plaintext in bytes. The stream header
|
||||||
|
* contains the protocol version, stream number and frame key.
|
||||||
|
*/
|
||||||
|
int STREAM_HEADER_PLAINTEXT_LENGTH = 2 + 8 + SecretKey.LENGTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the stream header in bytes.
|
* The length of the stream header in bytes.
|
||||||
*/
|
*/
|
||||||
int STREAM_HEADER_LENGTH = STREAM_HEADER_IV_LENGTH + SecretKey.LENGTH
|
int STREAM_HEADER_LENGTH = STREAM_HEADER_NONCE_LENGTH
|
||||||
+ MAC_LENGTH;
|
+ STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The length of the frame nonce in bytes.
|
* The length of the frame nonce in bytes.
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ public class PrivacyUtils {
|
|||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static String scrubMacAddress(@Nullable String address) {
|
public static String scrubMacAddress(@Nullable String address) {
|
||||||
if (address == null) return null;
|
if (address == null || address.length() == 0) return null;
|
||||||
// this is a fake address we need to know about
|
// this is a fake address we need to know about
|
||||||
if (address.equals("02:00:00:00:00:00")) return address;
|
if (address.equals("02:00:00:00:00:00")) return address;
|
||||||
// keep first and last octet of MAC address
|
// keep first and last octet of MAC address
|
||||||
|
|||||||
@@ -1,8 +1,9 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "java"
|
id 'java'
|
||||||
id "net.ltgt.apt" version "0.9"
|
id 'net.ltgt.apt' version '0.9'
|
||||||
id "idea"
|
id 'idea'
|
||||||
}
|
}
|
||||||
|
|
||||||
sourceCompatibility = 1.6
|
sourceCompatibility = 1.6
|
||||||
targetCompatibility = 1.6
|
targetCompatibility = 1.6
|
||||||
|
|
||||||
@@ -10,17 +11,18 @@ apply plugin: 'witness'
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
compile project(':bramble-api')
|
compile project(':bramble-api')
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
compile 'com.madgag.spongycastle:core:1.56.0.0'
|
||||||
compile 'com.madgag.spongycastle:core:1.54.0.0'
|
compile 'com.h2database:h2:1.4.192' // This is the last version that supports Java 1.6
|
||||||
compile 'com.h2database:h2:1.4.190'
|
compile 'org.bitlet:weupnp:0.1.4'
|
||||||
|
|
||||||
testCompile project(path: ':bramble-api', configuration: 'testOutput')
|
testCompile project(path: ':bramble-api', configuration: 'testOutput')
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
verify = [
|
verify = [
|
||||||
'com.madgag.spongycastle:core:1e7fa4b19ccccd1011364ab838d0b4702470c178bbbdd94c5c90b2d4d749ea1e',
|
'com.madgag.spongycastle:core:5e791b0eaa9e0c4594231b44f616a52adddb7dccedeb0ad9ad74887e19499a23',
|
||||||
'com.h2database:h2:23ba495a07bbbb3bd6c3084d10a96dad7a23741b8b6d64b213459a784195a98c'
|
'com.h2database:h2:225b22e9857235c46c93861410b60b8c81c10dc8985f4faf188985ba5445126c',
|
||||||
|
'org.bitlet:weupnp:88df7e6504929d00bdb832863761385c68ab92af945b04f0770b126270a444fb',
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
@@ -45,8 +45,10 @@ import static org.briarproject.bramble.api.invitation.InvitationConstants.CODE_B
|
|||||||
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
import static org.briarproject.bramble.crypto.EllipticCurveConstants.PARAMETERS;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_32_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.MAX_16_BIT_UNSIGNED;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
class CryptoComponentImpl implements CryptoComponent {
|
class CryptoComponentImpl implements CryptoComponent {
|
||||||
@@ -412,8 +414,11 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void encodeTag(byte[] tag, SecretKey tagKey, long streamNumber) {
|
public void encodeTag(byte[] tag, SecretKey tagKey, int protocolVersion,
|
||||||
|
long streamNumber) {
|
||||||
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
if (tag.length < TAG_LENGTH) throw new IllegalArgumentException();
|
||||||
|
if (protocolVersion < 0 || protocolVersion > MAX_16_BIT_UNSIGNED)
|
||||||
|
throw new IllegalArgumentException();
|
||||||
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
if (streamNumber < 0 || streamNumber > MAX_32_BIT_UNSIGNED)
|
||||||
throw new IllegalArgumentException();
|
throw new IllegalArgumentException();
|
||||||
// Initialise the PRF
|
// Initialise the PRF
|
||||||
@@ -421,10 +426,14 @@ class CryptoComponentImpl implements CryptoComponent {
|
|||||||
// The output of the PRF must be long enough to use as a tag
|
// The output of the PRF must be long enough to use as a tag
|
||||||
int macLength = prf.getDigestSize();
|
int macLength = prf.getDigestSize();
|
||||||
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
if (macLength < TAG_LENGTH) throw new IllegalStateException();
|
||||||
// The input is the stream number as a 64-bit integer
|
// The input is the protocol version as a 16-bit integer, followed by
|
||||||
byte[] input = new byte[INT_64_BYTES];
|
// the stream number as a 64-bit integer
|
||||||
ByteUtils.writeUint64(streamNumber, input, 0);
|
byte[] protocolVersionBytes = new byte[INT_16_BYTES];
|
||||||
prf.update(input, 0, input.length);
|
ByteUtils.writeUint16(protocolVersion, protocolVersionBytes, 0);
|
||||||
|
prf.update(protocolVersionBytes, 0, protocolVersionBytes.length);
|
||||||
|
byte[] streamNumberBytes = new byte[INT_64_BYTES];
|
||||||
|
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||||
|
prf.update(streamNumberBytes, 0, streamNumberBytes.length);
|
||||||
byte[] mac = new byte[macLength];
|
byte[] mac = new byte[macLength];
|
||||||
prf.doFinal(mac, 0);
|
prf.doFinal(mac, 0);
|
||||||
// The output is the first TAG_LENGTH bytes of the MAC
|
// The output is the first TAG_LENGTH bytes of the MAC
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -117,7 +119,7 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
|
|
||||||
private void readStreamHeader() throws IOException {
|
private void readStreamHeader() throws IOException {
|
||||||
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
||||||
byte[] streamHeaderPlaintext = new byte[SecretKey.LENGTH];
|
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
|
||||||
// Read the stream header
|
// Read the stream header
|
||||||
int offset = 0;
|
int offset = 0;
|
||||||
while (offset < STREAM_HEADER_LENGTH) {
|
while (offset < STREAM_HEADER_LENGTH) {
|
||||||
@@ -126,21 +128,35 @@ class StreamDecrypterImpl implements StreamDecrypter {
|
|||||||
if (read == -1) throw new EOFException();
|
if (read == -1) throw new EOFException();
|
||||||
offset += read;
|
offset += read;
|
||||||
}
|
}
|
||||||
// The nonce consists of the stream number followed by the IV
|
// Extract the nonce
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
|
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce, 0,
|
||||||
System.arraycopy(streamHeaderCiphertext, 0, streamHeaderNonce,
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
INT_64_BYTES, STREAM_HEADER_IV_LENGTH);
|
|
||||||
// Decrypt and authenticate the stream header
|
// Decrypt and authenticate the stream header
|
||||||
try {
|
try {
|
||||||
cipher.init(false, streamHeaderKey, streamHeaderNonce);
|
cipher.init(false, streamHeaderKey, streamHeaderNonce);
|
||||||
int decrypted = cipher.process(streamHeaderCiphertext,
|
int decrypted = cipher.process(streamHeaderCiphertext,
|
||||||
STREAM_HEADER_IV_LENGTH, SecretKey.LENGTH + MAC_LENGTH,
|
STREAM_HEADER_NONCE_LENGTH,
|
||||||
|
STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH,
|
||||||
streamHeaderPlaintext, 0);
|
streamHeaderPlaintext, 0);
|
||||||
if (decrypted != SecretKey.LENGTH) throw new RuntimeException();
|
if (decrypted != STREAM_HEADER_PLAINTEXT_LENGTH)
|
||||||
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException e) {
|
} catch (GeneralSecurityException e) {
|
||||||
throw new FormatException();
|
throw new FormatException();
|
||||||
}
|
}
|
||||||
frameKey = new SecretKey(streamHeaderPlaintext);
|
// Check the protocol version
|
||||||
|
int receivedProtocolVersion =
|
||||||
|
ByteUtils.readUint16(streamHeaderPlaintext, 0);
|
||||||
|
if (receivedProtocolVersion != PROTOCOL_VERSION)
|
||||||
|
throw new FormatException();
|
||||||
|
// Check the stream number
|
||||||
|
long receivedStreamNumber = ByteUtils.readUint64(streamHeaderPlaintext,
|
||||||
|
INT_16_BYTES);
|
||||||
|
if (receivedStreamNumber != streamNumber) throw new FormatException();
|
||||||
|
// Extract the frame key
|
||||||
|
byte[] frameKeyBytes = new byte[SecretKey.LENGTH];
|
||||||
|
System.arraycopy(streamHeaderPlaintext, INT_16_BYTES + INT_64_BYTES,
|
||||||
|
frameKeyBytes, 0, SecretKey.LENGTH);
|
||||||
|
frameKey = new SecretKey(frameKeyBytes);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -13,7 +13,8 @@ import javax.annotation.concurrent.Immutable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
import javax.inject.Provider;
|
import javax.inject.Provider;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
@Immutable
|
@Immutable
|
||||||
@@ -36,22 +37,22 @@ class StreamEncrypterFactoryImpl implements StreamEncrypterFactory {
|
|||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
long streamNumber = ctx.getStreamNumber();
|
long streamNumber = ctx.getStreamNumber();
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, ctx.getTagKey(), streamNumber);
|
crypto.encodeTag(tag, ctx.getTagKey(), PROTOCOL_VERSION, streamNumber);
|
||||||
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderIv);
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
|
return new StreamEncrypterImpl(out, cipher, streamNumber, tag,
|
||||||
streamHeaderIv, ctx.getHeaderKey(), frameKey);
|
streamHeaderNonce, ctx.getHeaderKey(), frameKey);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
public StreamEncrypter createInvitationStreamEncrypter(OutputStream out,
|
||||||
SecretKey headerKey) {
|
SecretKey headerKey) {
|
||||||
AuthenticatedCipher cipher = cipherProvider.get();
|
AuthenticatedCipher cipher = cipherProvider.get();
|
||||||
byte[] streamHeaderIv = new byte[STREAM_HEADER_IV_LENGTH];
|
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
||||||
crypto.getSecureRandom().nextBytes(streamHeaderIv);
|
crypto.getSecureRandom().nextBytes(streamHeaderNonce);
|
||||||
SecretKey frameKey = crypto.generateSecretKey();
|
SecretKey frameKey = crypto.generateSecretKey();
|
||||||
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderIv,
|
return new StreamEncrypterImpl(out, cipher, 0, null, streamHeaderNonce,
|
||||||
headerKey, frameKey);
|
headerKey, frameKey);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,9 +18,11 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_NO
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_PLAINTEXT_LENGTH;
|
||||||
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_64_BYTES;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -33,7 +35,7 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
private final long streamNumber;
|
private final long streamNumber;
|
||||||
@Nullable
|
@Nullable
|
||||||
private final byte[] tag;
|
private final byte[] tag;
|
||||||
private final byte[] streamHeaderIv;
|
private final byte[] streamHeaderNonce;
|
||||||
private final byte[] frameNonce, frameHeader;
|
private final byte[] frameNonce, frameHeader;
|
||||||
private final byte[] framePlaintext, frameCiphertext;
|
private final byte[] framePlaintext, frameCiphertext;
|
||||||
|
|
||||||
@@ -41,13 +43,13 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
private boolean writeTag, writeStreamHeader;
|
private boolean writeTag, writeStreamHeader;
|
||||||
|
|
||||||
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
|
StreamEncrypterImpl(OutputStream out, AuthenticatedCipher cipher,
|
||||||
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderIv,
|
long streamNumber, @Nullable byte[] tag, byte[] streamHeaderNonce,
|
||||||
SecretKey streamHeaderKey, SecretKey frameKey) {
|
SecretKey streamHeaderKey, SecretKey frameKey) {
|
||||||
this.out = out;
|
this.out = out;
|
||||||
this.cipher = cipher;
|
this.cipher = cipher;
|
||||||
this.streamNumber = streamNumber;
|
this.streamNumber = streamNumber;
|
||||||
this.tag = tag;
|
this.tag = tag;
|
||||||
this.streamHeaderIv = streamHeaderIv;
|
this.streamHeaderNonce = streamHeaderNonce;
|
||||||
this.streamHeaderKey = streamHeaderKey;
|
this.streamHeaderKey = streamHeaderKey;
|
||||||
this.frameKey = frameKey;
|
this.frameKey = frameKey;
|
||||||
frameNonce = new byte[FRAME_NONCE_LENGTH];
|
frameNonce = new byte[FRAME_NONCE_LENGTH];
|
||||||
@@ -114,22 +116,23 @@ class StreamEncrypterImpl implements StreamEncrypter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void writeStreamHeader() throws IOException {
|
private void writeStreamHeader() throws IOException {
|
||||||
// The nonce consists of the stream number followed by the IV
|
// The header contains the protocol version, stream number and frame key
|
||||||
byte[] streamHeaderNonce = new byte[STREAM_HEADER_NONCE_LENGTH];
|
byte[] streamHeaderPlaintext = new byte[STREAM_HEADER_PLAINTEXT_LENGTH];
|
||||||
ByteUtils.writeUint64(streamNumber, streamHeaderNonce, 0);
|
ByteUtils.writeUint16(PROTOCOL_VERSION, streamHeaderPlaintext, 0);
|
||||||
System.arraycopy(streamHeaderIv, 0, streamHeaderNonce, INT_64_BYTES,
|
ByteUtils.writeUint64(streamNumber, streamHeaderPlaintext,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
INT_16_BYTES);
|
||||||
byte[] streamHeaderPlaintext = frameKey.getBytes();
|
System.arraycopy(frameKey.getBytes(), 0, streamHeaderPlaintext,
|
||||||
|
INT_16_BYTES + INT_64_BYTES, SecretKey.LENGTH);
|
||||||
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
byte[] streamHeaderCiphertext = new byte[STREAM_HEADER_LENGTH];
|
||||||
System.arraycopy(streamHeaderIv, 0, streamHeaderCiphertext, 0,
|
System.arraycopy(streamHeaderNonce, 0, streamHeaderCiphertext, 0,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
// Encrypt and authenticate the frame key
|
// Encrypt and authenticate the stream header key
|
||||||
try {
|
try {
|
||||||
cipher.init(true, streamHeaderKey, streamHeaderNonce);
|
cipher.init(true, streamHeaderKey, streamHeaderNonce);
|
||||||
int encrypted = cipher.process(streamHeaderPlaintext, 0,
|
int encrypted = cipher.process(streamHeaderPlaintext, 0,
|
||||||
SecretKey.LENGTH, streamHeaderCiphertext,
|
STREAM_HEADER_PLAINTEXT_LENGTH, streamHeaderCiphertext,
|
||||||
STREAM_HEADER_IV_LENGTH);
|
STREAM_HEADER_NONCE_LENGTH);
|
||||||
if (encrypted != SecretKey.LENGTH + MAC_LENGTH)
|
if (encrypted != STREAM_HEADER_PLAINTEXT_LENGTH + MAC_LENGTH)
|
||||||
throw new RuntimeException();
|
throw new RuntimeException();
|
||||||
} catch (GeneralSecurityException badCipher) {
|
} catch (GeneralSecurityException badCipher) {
|
||||||
throw new RuntimeException(badCipher);
|
throw new RuntimeException(badCipher);
|
||||||
|
|||||||
@@ -70,25 +70,7 @@ class XSalsa20Poly1305AuthenticatedCipher implements AuthenticatedCipher {
|
|||||||
byte[] subKey = new byte[SUBKEY_LENGTH];
|
byte[] subKey = new byte[SUBKEY_LENGTH];
|
||||||
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
|
xSalsa20Engine.processBytes(zero, 0, SUBKEY_LENGTH, subKey, 0);
|
||||||
|
|
||||||
// Reverse the order of the Poly130 subkey
|
// Clamp the subkey
|
||||||
//
|
|
||||||
// NaCl and libsodium use the first 32 bytes of XSalsa20 as the
|
|
||||||
// subkey for crypto_onetimeauth_poly1305, which interprets it
|
|
||||||
// as r[0] ... r[15], k[0] ... k[15]. See section 9 of the NaCl
|
|
||||||
// paper (http://cr.yp.to/highspeed/naclcrypto-20090310.pdf),
|
|
||||||
// where the XSalsa20 output is defined as (r, s, t, ...).
|
|
||||||
//
|
|
||||||
// BC's Poly1305 implementation interprets the subkey as
|
|
||||||
// k[0] ... k[15], r[0] ... r[15] (per poly1305_aes_clamp in
|
|
||||||
// the reference implementation).
|
|
||||||
//
|
|
||||||
// To be NaCl-compatible, we reverse the subkey.
|
|
||||||
System.arraycopy(subKey, 0, zero, 0, SUBKEY_LENGTH / 2);
|
|
||||||
System.arraycopy(subKey, SUBKEY_LENGTH / 2, subKey, 0,
|
|
||||||
SUBKEY_LENGTH / 2);
|
|
||||||
System.arraycopy(zero, 0, subKey, SUBKEY_LENGTH / 2,
|
|
||||||
SUBKEY_LENGTH / 2);
|
|
||||||
// Now we can clamp the correct part of the subkey
|
|
||||||
Poly1305KeyGenerator.clamp(subKey);
|
Poly1305KeyGenerator.clamp(subKey);
|
||||||
|
|
||||||
// Initialize Poly1305 with the subkey
|
// Initialize Poly1305 with the subkey
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import javax.annotation.concurrent.ThreadSafe;
|
|||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
|
|
||||||
@@ -126,7 +127,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
for (long streamNumber : inKeys.getWindow().getUnseen()) {
|
||||||
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
TagContext tagCtx = new TagContext(c, inKeys, streamNumber);
|
||||||
byte[] tag = new byte[TAG_LENGTH];
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(tag, inKeys.getTagKey(), streamNumber);
|
crypto.encodeTag(tag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
inContexts.put(new Bytes(tag), tagCtx);
|
inContexts.put(new Bytes(tag), tagCtx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -242,7 +244,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
// Add tags for any stream numbers added to the window
|
// Add tags for any stream numbers added to the window
|
||||||
for (long streamNumber : change.getAdded()) {
|
for (long streamNumber : change.getAdded()) {
|
||||||
byte[] addTag = new byte[TAG_LENGTH];
|
byte[] addTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(addTag, inKeys.getTagKey(), streamNumber);
|
crypto.encodeTag(addTag, inKeys.getTagKey(), PROTOCOL_VERSION,
|
||||||
|
streamNumber);
|
||||||
inContexts.put(new Bytes(addTag), new TagContext(
|
inContexts.put(new Bytes(addTag), new TagContext(
|
||||||
tagCtx.contactId, inKeys, streamNumber));
|
tagCtx.contactId, inKeys, streamNumber));
|
||||||
}
|
}
|
||||||
@@ -250,7 +253,8 @@ class TransportKeyManagerImpl implements TransportKeyManager {
|
|||||||
for (long streamNumber : change.getRemoved()) {
|
for (long streamNumber : change.getRemoved()) {
|
||||||
if (streamNumber == tagCtx.streamNumber) continue;
|
if (streamNumber == tagCtx.streamNumber) continue;
|
||||||
byte[] removeTag = new byte[TAG_LENGTH];
|
byte[] removeTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(removeTag, inKeys.getTagKey(), streamNumber);
|
crypto.encodeTag(removeTag, inKeys.getTagKey(),
|
||||||
|
PROTOCOL_VERSION, streamNumber);
|
||||||
inContexts.remove(new Bytes(removeTag));
|
inContexts.remove(new Bytes(removeTag));
|
||||||
}
|
}
|
||||||
// Write the window back to the DB
|
// Write the window back to the DB
|
||||||
|
|||||||
@@ -14,7 +14,8 @@ import static junit.framework.Assert.assertEquals;
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HEADER_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
import static org.briarproject.bramble.util.ByteUtils.INT_16_BYTES;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
|
|
||||||
@@ -22,7 +23,8 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
private final AuthenticatedCipher cipher;
|
private final AuthenticatedCipher cipher;
|
||||||
private final SecretKey streamHeaderKey, frameKey;
|
private final SecretKey streamHeaderKey, frameKey;
|
||||||
private final byte[] streamHeaderIv, payload;
|
private final byte[] streamHeaderNonce, protocolVersionBytes;
|
||||||
|
private final byte[] streamNumberBytes, payload;
|
||||||
private final int payloadLength = 123, paddingLength = 234;
|
private final int payloadLength = 123, paddingLength = 234;
|
||||||
private final long streamNumber = 1234;
|
private final long streamNumber = 1234;
|
||||||
|
|
||||||
@@ -30,7 +32,12 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
cipher = new TestAuthenticatedCipher(); // Null cipher
|
cipher = new TestAuthenticatedCipher(); // Null cipher
|
||||||
streamHeaderKey = TestUtils.getSecretKey();
|
streamHeaderKey = TestUtils.getSecretKey();
|
||||||
frameKey = TestUtils.getSecretKey();
|
frameKey = TestUtils.getSecretKey();
|
||||||
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH);
|
streamHeaderNonce =
|
||||||
|
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
|
||||||
|
protocolVersionBytes = new byte[2];
|
||||||
|
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
|
||||||
|
streamNumberBytes = new byte[8];
|
||||||
|
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||||
payload = TestUtils.getRandomBytes(payloadLength);
|
payload = TestUtils.getRandomBytes(payloadLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -47,7 +54,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
|
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(streamHeaderIv);
|
out.write(streamHeaderNonce);
|
||||||
|
out.write(protocolVersionBytes);
|
||||||
|
out.write(streamNumberBytes);
|
||||||
out.write(frameKey.getBytes());
|
out.write(frameKey.getBytes());
|
||||||
out.write(new byte[MAC_LENGTH]);
|
out.write(new byte[MAC_LENGTH]);
|
||||||
out.write(frameHeader);
|
out.write(frameHeader);
|
||||||
@@ -76,6 +85,85 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
assertEquals(-1, s.readFrame(buffer));
|
assertEquals(-1, s.readFrame(buffer));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testWrongProtocolVersionThrowsException() throws Exception {
|
||||||
|
byte[] wrongProtocolVersionBytes = new byte[2];
|
||||||
|
ByteUtils.writeUint16(PROTOCOL_VERSION + 1, wrongProtocolVersionBytes,
|
||||||
|
0);
|
||||||
|
|
||||||
|
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
|
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
|
||||||
|
paddingLength);
|
||||||
|
|
||||||
|
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
|
||||||
|
int payloadLength1 = 345, paddingLength1 = 456;
|
||||||
|
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
|
||||||
|
paddingLength1);
|
||||||
|
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(streamHeaderNonce);
|
||||||
|
out.write(wrongProtocolVersionBytes);
|
||||||
|
out.write(streamNumberBytes);
|
||||||
|
out.write(frameKey.getBytes());
|
||||||
|
out.write(new byte[MAC_LENGTH]);
|
||||||
|
out.write(frameHeader);
|
||||||
|
out.write(payload);
|
||||||
|
out.write(new byte[paddingLength]);
|
||||||
|
out.write(new byte[MAC_LENGTH]);
|
||||||
|
out.write(frameHeader1);
|
||||||
|
out.write(payload1);
|
||||||
|
out.write(new byte[paddingLength1]);
|
||||||
|
out.write(new byte[MAC_LENGTH]);
|
||||||
|
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
|
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
|
||||||
|
streamNumber, streamHeaderKey);
|
||||||
|
|
||||||
|
// Try to read the first frame
|
||||||
|
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
|
||||||
|
s.readFrame(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test(expected = IOException.class)
|
||||||
|
public void testWrongStreamNumberThrowsException() throws Exception {
|
||||||
|
byte[] wrongStreamNumberBytes = new byte[8];
|
||||||
|
ByteUtils.writeUint64(streamNumber + 1, wrongStreamNumberBytes, 0);
|
||||||
|
|
||||||
|
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
|
FrameEncoder.encodeHeader(frameHeader, false, payloadLength,
|
||||||
|
paddingLength);
|
||||||
|
|
||||||
|
byte[] frameHeader1 = new byte[FRAME_HEADER_LENGTH];
|
||||||
|
int payloadLength1 = 345, paddingLength1 = 456;
|
||||||
|
FrameEncoder.encodeHeader(frameHeader1, true, payloadLength1,
|
||||||
|
paddingLength1);
|
||||||
|
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
|
||||||
|
|
||||||
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
|
out.write(streamHeaderNonce);
|
||||||
|
out.write(protocolVersionBytes);
|
||||||
|
out.write(wrongStreamNumberBytes);
|
||||||
|
out.write(frameKey.getBytes());
|
||||||
|
out.write(new byte[MAC_LENGTH]);
|
||||||
|
out.write(frameHeader);
|
||||||
|
out.write(payload);
|
||||||
|
out.write(new byte[paddingLength]);
|
||||||
|
out.write(new byte[MAC_LENGTH]);
|
||||||
|
out.write(frameHeader1);
|
||||||
|
out.write(payload1);
|
||||||
|
out.write(new byte[paddingLength1]);
|
||||||
|
out.write(new byte[MAC_LENGTH]);
|
||||||
|
|
||||||
|
ByteArrayInputStream in = new ByteArrayInputStream(out.toByteArray());
|
||||||
|
StreamDecrypterImpl s = new StreamDecrypterImpl(in, cipher,
|
||||||
|
streamNumber, streamHeaderKey);
|
||||||
|
|
||||||
|
// Try to read the first frame
|
||||||
|
byte[] buffer = new byte[MAX_PAYLOAD_LENGTH];
|
||||||
|
s.readFrame(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
@Test(expected = IOException.class)
|
@Test(expected = IOException.class)
|
||||||
public void testTruncatedFrameThrowsException() throws Exception {
|
public void testTruncatedFrameThrowsException() throws Exception {
|
||||||
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] frameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -83,7 +171,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
paddingLength);
|
paddingLength);
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(streamHeaderIv);
|
out.write(streamHeaderNonce);
|
||||||
|
out.write(protocolVersionBytes);
|
||||||
|
out.write(streamNumberBytes);
|
||||||
out.write(frameKey.getBytes());
|
out.write(frameKey.getBytes());
|
||||||
out.write(new byte[MAC_LENGTH]);
|
out.write(new byte[MAC_LENGTH]);
|
||||||
out.write(frameHeader);
|
out.write(frameHeader);
|
||||||
@@ -111,7 +201,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
byte[] payload = TestUtils.getRandomBytes(payloadLength);
|
byte[] payload = TestUtils.getRandomBytes(payloadLength);
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(streamHeaderIv);
|
out.write(streamHeaderNonce);
|
||||||
|
out.write(protocolVersionBytes);
|
||||||
|
out.write(streamNumberBytes);
|
||||||
out.write(frameKey.getBytes());
|
out.write(frameKey.getBytes());
|
||||||
out.write(new byte[MAC_LENGTH]);
|
out.write(new byte[MAC_LENGTH]);
|
||||||
out.write(frameHeader);
|
out.write(frameHeader);
|
||||||
@@ -138,7 +230,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
padding[paddingLength - 1] = 1;
|
padding[paddingLength - 1] = 1;
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(streamHeaderIv);
|
out.write(streamHeaderNonce);
|
||||||
|
out.write(protocolVersionBytes);
|
||||||
|
out.write(streamNumberBytes);
|
||||||
out.write(frameKey.getBytes());
|
out.write(frameKey.getBytes());
|
||||||
out.write(new byte[MAC_LENGTH]);
|
out.write(new byte[MAC_LENGTH]);
|
||||||
out.write(frameHeader);
|
out.write(frameHeader);
|
||||||
@@ -162,7 +256,9 @@ public class StreamDecrypterImplTest extends BrambleTestCase {
|
|||||||
paddingLength);
|
paddingLength);
|
||||||
|
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
out.write(streamHeaderIv);
|
out.write(streamHeaderNonce);
|
||||||
|
out.write(protocolVersionBytes);
|
||||||
|
out.write(streamNumberBytes);
|
||||||
out.write(frameKey.getBytes());
|
out.write(frameKey.getBytes());
|
||||||
out.write(new byte[MAC_LENGTH]);
|
out.write(new byte[MAC_LENGTH]);
|
||||||
out.write(frameHeader);
|
out.write(frameHeader);
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.bramble.crypto;
|
|||||||
import org.briarproject.bramble.api.crypto.SecretKey;
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
import org.briarproject.bramble.test.BrambleTestCase;
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
import org.briarproject.bramble.test.TestUtils;
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
import org.briarproject.bramble.util.ByteUtils;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@@ -11,8 +12,9 @@ import static org.briarproject.bramble.api.transport.TransportConstants.FRAME_HE
|
|||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAC_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_FRAME_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_PAYLOAD_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_IV_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.STREAM_HEADER_NONCE_LENGTH;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -21,7 +23,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
|
|
||||||
private final AuthenticatedCipher cipher;
|
private final AuthenticatedCipher cipher;
|
||||||
private final SecretKey streamHeaderKey, frameKey;
|
private final SecretKey streamHeaderKey, frameKey;
|
||||||
private final byte[] tag, streamHeaderIv, payload;
|
private final byte[] tag, streamHeaderNonce, protocolVersionBytes;
|
||||||
|
private final byte[] streamNumberBytes, payload;
|
||||||
private final long streamNumber = 1234;
|
private final long streamNumber = 1234;
|
||||||
private final int payloadLength = 123, paddingLength = 234;
|
private final int payloadLength = 123, paddingLength = 234;
|
||||||
|
|
||||||
@@ -30,7 +33,12 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
streamHeaderKey = TestUtils.getSecretKey();
|
streamHeaderKey = TestUtils.getSecretKey();
|
||||||
frameKey = TestUtils.getSecretKey();
|
frameKey = TestUtils.getSecretKey();
|
||||||
tag = TestUtils.getRandomBytes(TAG_LENGTH);
|
tag = TestUtils.getRandomBytes(TAG_LENGTH);
|
||||||
streamHeaderIv = TestUtils.getRandomBytes(STREAM_HEADER_IV_LENGTH);
|
streamHeaderNonce =
|
||||||
|
TestUtils.getRandomBytes(STREAM_HEADER_NONCE_LENGTH);
|
||||||
|
protocolVersionBytes = new byte[2];
|
||||||
|
ByteUtils.writeUint16(PROTOCOL_VERSION, protocolVersionBytes, 0);
|
||||||
|
streamNumberBytes = new byte[8];
|
||||||
|
ByteUtils.writeUint64(streamNumber, streamNumberBytes, 0);
|
||||||
payload = TestUtils.getRandomBytes(payloadLength);
|
payload = TestUtils.getRandomBytes(payloadLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -38,7 +46,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testRejectsNegativePayloadLength() throws Exception {
|
public void testRejectsNegativePayloadLength() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, -1, 0, false);
|
s.writeFrame(payload, -1, 0, false);
|
||||||
}
|
}
|
||||||
@@ -47,7 +56,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testRejectsNegativePaddingLength() throws Exception {
|
public void testRejectsNegativePaddingLength() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, 0, -1, false);
|
s.writeFrame(payload, 0, -1, false);
|
||||||
}
|
}
|
||||||
@@ -56,7 +66,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testRejectsMaxPayloadPlusPadding() throws Exception {
|
public void testRejectsMaxPayloadPlusPadding() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
|
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH + 1];
|
||||||
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
|
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 1, false);
|
||||||
@@ -66,7 +77,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
|
public void testAcceptsMaxPayloadIncludingPadding() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
|
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
|
||||||
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
|
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH - 1, 1, false);
|
||||||
@@ -78,7 +90,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
|
public void testAcceptsMaxPayloadWithoutPadding() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
|
byte[] bigPayload = new byte[MAX_PAYLOAD_LENGTH];
|
||||||
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
|
s.writeFrame(bigPayload, MAX_PAYLOAD_LENGTH, 0, false);
|
||||||
@@ -90,14 +103,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
|
public void testWriteUnpaddedNonFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, false);
|
s.writeFrame(payload, payloadLength, 0, false);
|
||||||
|
|
||||||
// Expect the tag, stream header, frame header, payload and MAC
|
// Expect the tag, stream header, frame header, payload and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(tag);
|
expected.write(tag);
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -113,14 +129,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
|
public void testWriteUnpaddedFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, true);
|
s.writeFrame(payload, payloadLength, 0, true);
|
||||||
|
|
||||||
// Expect the tag, stream header, frame header, payload and MAC
|
// Expect the tag, stream header, frame header, payload and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(tag);
|
expected.write(tag);
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -136,13 +155,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
|
public void testWriteUnpaddedNonFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, null, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, false);
|
s.writeFrame(payload, payloadLength, 0, false);
|
||||||
|
|
||||||
// Expect the stream header, frame header, payload and MAC
|
// Expect the stream header, frame header, payload and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -158,13 +180,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
|
public void testWriteUnpaddedFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, null, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, 0, true);
|
s.writeFrame(payload, payloadLength, 0, true);
|
||||||
|
|
||||||
// Expect the stream header, frame header, payload and MAC
|
// Expect the stream header, frame header, payload and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -180,14 +205,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWritePaddedNonFinalFrameWithTag() throws Exception {
|
public void testWritePaddedNonFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, false);
|
s.writeFrame(payload, payloadLength, paddingLength, false);
|
||||||
|
|
||||||
// Expect the tag, stream header, frame header, payload, padding and MAC
|
// Expect the tag, stream header, frame header, payload, padding and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(tag);
|
expected.write(tag);
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -205,14 +233,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWritePaddedFinalFrameWithTag() throws Exception {
|
public void testWritePaddedFinalFrameWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, true);
|
s.writeFrame(payload, payloadLength, paddingLength, true);
|
||||||
|
|
||||||
// Expect the tag, stream header, frame header, payload, padding and MAC
|
// Expect the tag, stream header, frame header, payload, padding and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(tag);
|
expected.write(tag);
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -230,13 +261,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
|
public void testWritePaddedNonFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, null, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, false);
|
s.writeFrame(payload, payloadLength, paddingLength, false);
|
||||||
|
|
||||||
// Expect the stream header, frame header, payload, padding and MAC
|
// Expect the stream header, frame header, payload, padding and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -254,13 +288,16 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWritePaddedFinalFrameWithoutTag() throws Exception {
|
public void testWritePaddedFinalFrameWithoutTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, null, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
s.writeFrame(payload, payloadLength, paddingLength, true);
|
s.writeFrame(payload, payloadLength, paddingLength, true);
|
||||||
|
|
||||||
// Expect the stream header, frame header, payload, padding and MAC
|
// Expect the stream header, frame header, payload, padding and MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -278,7 +315,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testWriteTwoFramesWithTag() throws Exception {
|
public void testWriteTwoFramesWithTag() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
int payloadLength1 = 345, paddingLength1 = 456;
|
int payloadLength1 = 345, paddingLength1 = 456;
|
||||||
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
|
byte[] payload1 = TestUtils.getRandomBytes(payloadLength1);
|
||||||
|
|
||||||
@@ -289,7 +327,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
// MAC, second frame header, payload, padding, MAC
|
// MAC, second frame header, payload, padding, MAC
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(tag);
|
expected.write(tag);
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
byte[] expectedFrameHeader = new byte[FRAME_HEADER_LENGTH];
|
||||||
@@ -315,7 +355,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
// Flush the stream once
|
// Flush the stream once
|
||||||
s.flush();
|
s.flush();
|
||||||
@@ -323,7 +364,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
// Expect the tag and stream header
|
// Expect the tag and stream header
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(tag);
|
expected.write(tag);
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
|
|
||||||
@@ -335,7 +378,8 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
throws Exception {
|
throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, tag, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, tag, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
// Flush the stream twice
|
// Flush the stream twice
|
||||||
s.flush();
|
s.flush();
|
||||||
@@ -344,7 +388,9 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
// Expect the tag and stream header
|
// Expect the tag and stream header
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(tag);
|
expected.write(tag);
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
|
|
||||||
@@ -355,14 +401,17 @@ public class StreamEncrypterImplTest extends BrambleTestCase {
|
|||||||
public void testFlushDoesNotWriteTagIfNull() throws Exception {
|
public void testFlushDoesNotWriteTagIfNull() throws Exception {
|
||||||
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
ByteArrayOutputStream out = new ByteArrayOutputStream();
|
||||||
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
StreamEncrypterImpl s = new StreamEncrypterImpl(out, cipher,
|
||||||
streamNumber, null, streamHeaderIv, streamHeaderKey, frameKey);
|
streamNumber, null, streamHeaderNonce, streamHeaderKey,
|
||||||
|
frameKey);
|
||||||
|
|
||||||
// Flush the stream once
|
// Flush the stream once
|
||||||
s.flush();
|
s.flush();
|
||||||
|
|
||||||
// Expect the stream header
|
// Expect the stream header
|
||||||
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
ByteArrayOutputStream expected = new ByteArrayOutputStream();
|
||||||
expected.write(streamHeaderIv);
|
expected.write(streamHeaderNonce);
|
||||||
|
expected.write(protocolVersionBytes);
|
||||||
|
expected.write(streamNumberBytes);
|
||||||
expected.write(frameKey.getBytes());
|
expected.write(frameKey.getBytes());
|
||||||
expected.write(new byte[MAC_LENGTH]);
|
expected.write(new byte[MAC_LENGTH]);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,59 @@
|
|||||||
|
package org.briarproject.bramble.crypto;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.Bytes;
|
||||||
|
import org.briarproject.bramble.api.crypto.CryptoComponent;
|
||||||
|
import org.briarproject.bramble.api.crypto.SecretKey;
|
||||||
|
import org.briarproject.bramble.test.BrambleTestCase;
|
||||||
|
import org.briarproject.bramble.test.TestSecureRandomProvider;
|
||||||
|
import org.briarproject.bramble.test.TestUtils;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import static junit.framework.TestCase.assertTrue;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
|
|
||||||
|
public class TagEncodingTest extends BrambleTestCase {
|
||||||
|
|
||||||
|
private final CryptoComponent crypto;
|
||||||
|
private final SecretKey tagKey;
|
||||||
|
private final long streamNumber = 1234567890;
|
||||||
|
|
||||||
|
public TagEncodingTest() {
|
||||||
|
crypto = new CryptoComponentImpl(new TestSecureRandomProvider());
|
||||||
|
tagKey = TestUtils.getSecretKey();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testKeyAffectsTag() throws Exception {
|
||||||
|
Set<Bytes> set = new HashSet<Bytes>();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
|
SecretKey tagKey = TestUtils.getSecretKey();
|
||||||
|
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber);
|
||||||
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testProtocolVersionAffectsTag() throws Exception {
|
||||||
|
Set<Bytes> set = new HashSet<Bytes>();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
|
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION + i, streamNumber);
|
||||||
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testStreamNumberAffectsTag() throws Exception {
|
||||||
|
Set<Bytes> set = new HashSet<Bytes>();
|
||||||
|
for (int i = 0; i < 100; i++) {
|
||||||
|
byte[] tag = new byte[TAG_LENGTH];
|
||||||
|
crypto.encodeTag(tag, tagKey, PROTOCOL_VERSION, streamNumber + i);
|
||||||
|
assertTrue(set.add(new Bytes(tag)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -34,6 +34,7 @@ import java.util.Collection;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
import static org.briarproject.bramble.api.sync.SyncConstants.MAX_GROUP_DESCRIPTOR_LENGTH;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.junit.Assert.assertArrayEquals;
|
import static org.junit.Assert.assertArrayEquals;
|
||||||
import static org.junit.Assert.assertEquals;
|
import static org.junit.Assert.assertEquals;
|
||||||
@@ -115,7 +116,7 @@ public class SyncIntegrationTest extends BrambleTestCase {
|
|||||||
private void read(byte[] connectionData) throws Exception {
|
private void read(byte[] connectionData) throws Exception {
|
||||||
// Calculate the expected tag
|
// Calculate the expected tag
|
||||||
byte[] expectedTag = new byte[TAG_LENGTH];
|
byte[] expectedTag = new byte[TAG_LENGTH];
|
||||||
crypto.encodeTag(expectedTag, tagKey, streamNumber);
|
crypto.encodeTag(expectedTag, tagKey, PROTOCOL_VERSION, streamNumber);
|
||||||
|
|
||||||
// Read the tag
|
// Read the tag
|
||||||
InputStream in = new ByteArrayInputStream(connectionData);
|
InputStream in = new ByteArrayInputStream(connectionData);
|
||||||
|
|||||||
@@ -33,6 +33,7 @@ import java.util.concurrent.ScheduledExecutorService;
|
|||||||
|
|
||||||
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
import static java.util.concurrent.TimeUnit.MILLISECONDS;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.MAX_CLOCK_DIFFERENCE;
|
||||||
|
import static org.briarproject.bramble.api.transport.TransportConstants.PROTOCOL_VERSION;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
import static org.briarproject.bramble.api.transport.TransportConstants.REORDERING_WINDOW_SIZE;
|
||||||
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
|
||||||
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
import static org.briarproject.bramble.util.ByteUtils.MAX_32_BIT_UNSIGNED;
|
||||||
@@ -86,7 +87,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets per contact)
|
// Encode the tags (3 sets per contact)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(6).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were rotated
|
||||||
@@ -133,7 +134,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys
|
// Save the keys
|
||||||
@@ -199,7 +200,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
@@ -247,7 +248,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
@@ -306,7 +307,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
@@ -355,7 +356,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
}
|
}
|
||||||
// Rotate the transport keys (the keys are unaffected)
|
// Rotate the transport keys (the keys are unaffected)
|
||||||
@@ -365,7 +366,8 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
oneOf(db).addTransportKeys(txn, contactId, transportKeys);
|
||||||
// Encode a new tag after sliding the window
|
// Encode a new tag after sliding the window
|
||||||
oneOf(crypto).encodeTag(with(any(byte[].class)),
|
oneOf(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with((long) REORDERING_WINDOW_SIZE));
|
with(tagKey), with(PROTOCOL_VERSION),
|
||||||
|
with((long) REORDERING_WINDOW_SIZE));
|
||||||
will(new EncodeTagAction(tags));
|
will(new EncodeTagAction(tags));
|
||||||
// Save the reordering window (previous rotation period, base 1)
|
// Save the reordering window (previous rotation period, base 1)
|
||||||
oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
|
oneOf(db).setReorderingWindow(txn, contactId, transportId, 999,
|
||||||
@@ -428,7 +430,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Schedule key rotation at the start of the next rotation period
|
// Schedule key rotation at the start of the next rotation period
|
||||||
@@ -450,7 +452,7 @@ public class TransportKeyManagerImplTest extends BrambleTestCase {
|
|||||||
// Encode the tags (3 sets)
|
// Encode the tags (3 sets)
|
||||||
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
for (long i = 0; i < REORDERING_WINDOW_SIZE; i++) {
|
||||||
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
exactly(3).of(crypto).encodeTag(with(any(byte[].class)),
|
||||||
with(tagKey), with(i));
|
with(tagKey), with(PROTOCOL_VERSION), with(i));
|
||||||
will(new EncodeTagAction());
|
will(new EncodeTagAction());
|
||||||
}
|
}
|
||||||
// Save the keys that were rotated
|
// Save the keys that were rotated
|
||||||
|
|||||||
@@ -7,10 +7,19 @@ apply plugin: 'witness'
|
|||||||
dependencies {
|
dependencies {
|
||||||
compile project(':bramble-core')
|
compile project(':bramble-core')
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
compile fileTree(dir: 'libs', include: '*.jar')
|
||||||
|
compile 'net.java.dev.jna:jna:4.4.0'
|
||||||
|
compile 'net.java.dev.jna:jna-platform:4.4.0'
|
||||||
|
|
||||||
testCompile project(path: ':bramble-core', configuration: 'testOutput')
|
testCompile project(path: ':bramble-core', configuration: 'testOutput')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
dependencyVerification {
|
||||||
|
verify = [
|
||||||
|
'net.java.dev.jna:jna:c4dadeeecaa90c8847902082aee5eb107fcf59c5d0e63a17fcaf273c0e2d2bd1',
|
||||||
|
'net.java.dev.jna:jna-platform:e9dda9e884fc107eb6367710540789a12dfa8ad28be9326b22ca6e352e325499',
|
||||||
|
]
|
||||||
|
}
|
||||||
|
|
||||||
tasks.withType(Test) {
|
tasks.withType(Test) {
|
||||||
systemProperty 'java.library.path', 'libs'
|
systemProperty 'java.library.path', 'libs'
|
||||||
}
|
}
|
||||||
|
|||||||
Binary file not shown.
Binary file not shown.
@@ -5,13 +5,10 @@ dependencies {
|
|||||||
def supportVersion = '23.2.1'
|
def supportVersion = '23.2.1'
|
||||||
compile project(':briar-core')
|
compile project(':briar-core')
|
||||||
compile project(':bramble-android')
|
compile project(':bramble-android')
|
||||||
compile fileTree(dir: 'libs', include: '*.jar')
|
|
||||||
|
|
||||||
compile "com.android.support:support-v4:$supportVersion"
|
compile "com.android.support:support-v4:$supportVersion"
|
||||||
compile("com.android.support:appcompat-v7:$supportVersion") {
|
compile("com.android.support:appcompat-v7:$supportVersion") {
|
||||||
exclude module: 'support-v4'
|
exclude module: 'support-v4'
|
||||||
}
|
}
|
||||||
|
|
||||||
compile("com.android.support:preference-v14:$supportVersion") {
|
compile("com.android.support:preference-v14:$supportVersion") {
|
||||||
exclude module: 'support-v4'
|
exclude module: 'support-v4'
|
||||||
}
|
}
|
||||||
@@ -20,7 +17,7 @@ dependencies {
|
|||||||
exclude module: 'recyclerview-v7'
|
exclude module: 'recyclerview-v7'
|
||||||
}
|
}
|
||||||
compile "com.android.support:cardview-v7:$supportVersion"
|
compile "com.android.support:cardview-v7:$supportVersion"
|
||||||
compile 'com.android.support:support-annotations:23.4.0'
|
compile "com.android.support:support-annotations:$supportVersion"
|
||||||
compile('ch.acra:acra:4.8.5') {
|
compile('ch.acra:acra:4.8.5') {
|
||||||
exclude module: 'support-v4'
|
exclude module: 'support-v4'
|
||||||
exclude module: 'support-annotations'
|
exclude module: 'support-annotations'
|
||||||
@@ -28,15 +25,16 @@ dependencies {
|
|||||||
compile 'info.guardianproject.panic:panic:0.5'
|
compile 'info.guardianproject.panic:panic:0.5'
|
||||||
compile 'info.guardianproject.trustedintents:trustedintents:0.2'
|
compile 'info.guardianproject.trustedintents:trustedintents:0.2'
|
||||||
compile 'de.hdodenhof:circleimageview:2.1.0'
|
compile 'de.hdodenhof:circleimageview:2.1.0'
|
||||||
compile 'com.google.zxing:core:3.2.1'
|
compile 'com.google.zxing:core:3.3.0'
|
||||||
provided 'javax.annotation:jsr250-api:1.0'
|
|
||||||
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
|
compile 'com.jpardogo.materialtabstrip:library:1.1.0'
|
||||||
compile 'com.github.bumptech.glide:glide:3.7.0'
|
compile 'com.github.bumptech.glide:glide:3.8.0'
|
||||||
compile 'uk.co.samuelwall:material-tap-target-prompt:1.3.0'
|
compile 'uk.co.samuelwall:material-tap-target-prompt:1.9.2'
|
||||||
|
|
||||||
|
provided 'javax.annotation:jsr250-api:1.0'
|
||||||
|
|
||||||
testCompile project(path: ':bramble-core', configuration: 'testOutput')
|
testCompile project(path: ':bramble-core', configuration: 'testOutput')
|
||||||
testCompile 'org.robolectric:robolectric:3.0'
|
testCompile 'org.robolectric:robolectric:3.0'
|
||||||
testCompile 'org.mockito:mockito-core:1.10.19'
|
testCompile 'org.mockito:mockito-core:2.8.9'
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencyVerification {
|
dependencyVerification {
|
||||||
@@ -45,20 +43,18 @@ dependencyVerification {
|
|||||||
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
|
'info.guardianproject.panic:panic:a7ed9439826db2e9901649892cf9afbe76f00991b768d8f4c26332d7c9406cb2',
|
||||||
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
|
'info.guardianproject.trustedintents:trustedintents:6221456d8821a8d974c2acf86306900237cf6afaaa94a4c9c44e161350f80f3e',
|
||||||
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
|
'de.hdodenhof:circleimageview:bcbc588e19e6dcf8c120b1957776bfe229efba5d2fbe5da7156372eeacf65503',
|
||||||
'com.google.zxing:core:b4d82452e7a6bf6ec2698904b332431717ed8f9a850224f295aec89de80f2259',
|
'com.google.zxing:core:bba7724e02a997cec38213af77133ee8e24b0d5cf5fa7ecbc16a4fa93f11ee0d',
|
||||||
'com.android.support:support-v4:81ce890f26d35c75ad17d0f998a7e3230330c3b41e0b629566bc744bee89e448',
|
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
|
||||||
|
'com.github.bumptech.glide:glide:750d9e7b940dc0ee48f8680623b55d46e14e8727acc922d7b156e57e7c549655',
|
||||||
|
'uk.co.samuelwall:material-tap-target-prompt:5d4951124366bc5c52e57beaa294db7611f0aa2a8d80e0163e1383e1966ba5b2',
|
||||||
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
|
'com.android.support:appcompat-v7:00f9d93acacd6731f309724054bf51492814b4b2869f16d7d5c0038dcb8c9a0d',
|
||||||
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
|
'com.android.support:preference-v14:44881bb46094e86d0bc2426f205419674a5b4eb514b44b5a4659b5de29f71eb7',
|
||||||
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
|
'com.android.support:design:003e0c0bea0a6891f8b2bc43f20ae7af2a49a17363e5bb10df5ee0bae12fa686',
|
||||||
'com.android.support:support-annotations:e91a88dd0c5e99069b7f09d4a46b5e06f1e9c4c72fc0a8e987e25d86af480f01',
|
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
|
||||||
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
|
'com.android.support:animated-vector-drawable:06d1963b85aa917099d7757e6a7b3e4dc06889413dc747f625ae8683606db3a1',
|
||||||
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
|
'com.android.support:support-vector-drawable:799bafe4c3de812386f0b291f744d5d6876452722dd40189b9ab87dbbf594ea1',
|
||||||
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
|
'com.android.support:recyclerview-v7:44040a888e23e0c93162a3377cfe06751080e3c22d369ab0d4301ef60d63b0fe',
|
||||||
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
|
'com.android.support:preference-v7:775101bd07bd052e455761c5c5d9523d7ad59f2f320e3e8cbde241fd6b1d6025',
|
||||||
'com.android.support:cardview-v7:4595f1c4a28cfa083b6c0920ad4d49e1c2ca4b8302a955e548f68eb63b74931b',
|
|
||||||
'com.jpardogo.materialtabstrip:library:24d19232b319f8c73e25793432357919a7ed972186f57a3b2c9093ea74ad8311',
|
|
||||||
'com.github.bumptech.glide:glide:76ef123957b5fbaebb05fcbe6606dd58c3bc3fcdadb257f99811d0ac9ea9b88b',
|
|
||||||
'uk.co.samuelwall:material-tap-target-prompt:f67e1caead12a914525b32cbf6da52a96b93ff89573f93cb41102ef3130fb64a',
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -82,7 +78,10 @@ android {
|
|||||||
defaultConfig {
|
defaultConfig {
|
||||||
minSdkVersion 14
|
minSdkVersion 14
|
||||||
targetSdkVersion 22
|
targetSdkVersion 22
|
||||||
resValue "string", "app_package", "org.briarproject.briar"
|
versionCode 1601
|
||||||
|
versionName "0.16.1"
|
||||||
|
applicationId "org.briarproject.briar.beta"
|
||||||
|
resValue "string", "app_package", "org.briarproject.briar.beta"
|
||||||
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
|
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -107,5 +106,6 @@ android {
|
|||||||
lintOptions {
|
lintOptions {
|
||||||
warning 'MissingTranslation'
|
warning 'MissingTranslation'
|
||||||
warning 'ImpliedQuantity'
|
warning 'ImpliedQuantity'
|
||||||
|
warning 'ExtraTranslation'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest
|
<manifest
|
||||||
package="org.briarproject.briar"
|
package="org.briarproject.briar"
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
android:versionCode="13"
|
|
||||||
android:versionName="0.13">
|
|
||||||
|
|
||||||
<uses-feature android:name="android.hardware.bluetooth"/>
|
<uses-feature android:name="android.hardware.bluetooth"/>
|
||||||
<uses-feature android:name="android.hardware.camera" />
|
<uses-feature android:name="android.hardware.camera" />
|
||||||
@@ -21,7 +19,7 @@
|
|||||||
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".android.BriarApplicationImpl"
|
android:name="org.briarproject.briar.android.BriarApplicationImpl"
|
||||||
android:allowBackup="false"
|
android:allowBackup="false"
|
||||||
android:icon="@mipmap/ic_launcher_round"
|
android:icon="@mipmap/ic_launcher_round"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
@@ -29,7 +27,7 @@
|
|||||||
android:theme="@style/BriarTheme">
|
android:theme="@style/BriarTheme">
|
||||||
|
|
||||||
<service
|
<service
|
||||||
android:name=".android.BriarService"
|
android:name="org.briarproject.briar.android.BriarService"
|
||||||
android:exported="false">
|
android:exported="false">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="org.briarproject.briar.android.BriarService"/>
|
<action android:name="org.briarproject.briar.android.BriarService"/>
|
||||||
@@ -37,7 +35,7 @@
|
|||||||
</service>
|
</service>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.reporting.DevReportActivity"
|
android:name="org.briarproject.briar.android.reporting.DevReportActivity"
|
||||||
android:excludeFromRecents="true"
|
android:excludeFromRecents="true"
|
||||||
android:exported="false"
|
android:exported="false"
|
||||||
android:finishOnTaskLaunch="true"
|
android:finishOnTaskLaunch="true"
|
||||||
@@ -49,24 +47,24 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.splash.ExpiredActivity"
|
android:name="org.briarproject.briar.android.splash.ExpiredActivity"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.login.PasswordActivity"
|
android:name="org.briarproject.briar.android.login.PasswordActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:windowSoftInputMode="stateVisible">
|
android:windowSoftInputMode="stateVisible">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.login.SetupActivity"
|
android:name="org.briarproject.briar.android.login.SetupActivity"
|
||||||
android:label="@string/setup_title"
|
android:label="@string/setup_title"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.splash.SplashScreenActivity"
|
android:name="org.briarproject.briar.android.splash.SplashScreenActivity"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"
|
android:theme="@style/BriarTheme.NoActionBar"
|
||||||
android:label="@string/app_name">
|
android:label="@string/app_name">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
@@ -76,268 +74,268 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.navdrawer.NavDrawerActivity"
|
android:name="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"
|
android:theme="@style/BriarTheme.NoActionBar"
|
||||||
android:launchMode="singleTop">
|
android:launchMode="singleTop">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.contact.ConversationActivity"
|
android:name="org.briarproject.briar.android.contact.ConversationActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"
|
android:theme="@style/BriarTheme.NoActionBar"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.privategroup.creation.CreateGroupActivity"
|
android:name="org.briarproject.briar.android.privategroup.creation.CreateGroupActivity"
|
||||||
android:label="@string/groups_create_group_title"
|
android:label="@string/groups_create_group_title"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:windowSoftInputMode="adjustResize">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.privategroup.conversation.GroupActivity"
|
android:name="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"
|
android:theme="@style/BriarTheme.NoActionBar"
|
||||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.privategroup.invitation.GroupInvitationActivity"
|
android:name="org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity"
|
||||||
android:label="@string/groups_invitations_title"
|
android:label="@string/groups_invitations_title"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"/>
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.privategroup.memberlist.GroupMemberListActivity"
|
android:name="org.briarproject.briar.android.privategroup.memberlist.GroupMemberListActivity"
|
||||||
android:label="@string/groups_member_list"
|
android:label="@string/groups_member_list"
|
||||||
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
|
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
|
||||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.privategroup.conversation.GroupActivity"
|
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.privategroup.reveal.RevealContactsActivity"
|
android:name="org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity"
|
||||||
android:label="@string/groups_reveal_contacts"
|
android:label="@string/groups_reveal_contacts"
|
||||||
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
|
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
|
||||||
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
|
android:windowSoftInputMode="adjustResize|stateAlwaysHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.privategroup.conversation.GroupActivity"
|
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.privategroup.creation.GroupInviteActivity"
|
android:name="org.briarproject.briar.android.privategroup.creation.GroupInviteActivity"
|
||||||
android:label="@string/groups_invite_members"
|
android:label="@string/groups_invite_members"
|
||||||
android:parentActivityName=".android.privategroup.conversation.GroupActivity"
|
android:parentActivityName="org.briarproject.briar.android.privategroup.conversation.GroupActivity"
|
||||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.privategroup.conversation.GroupActivity"/>
|
android:value="org.briarproject.briar.android.privategroup.conversation.GroupActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.sharing.ForumInvitationActivity"
|
android:name="org.briarproject.briar.android.sharing.ForumInvitationActivity"
|
||||||
android:label="@string/forum_invitations_title"
|
android:label="@string/forum_invitations_title"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.sharing.BlogInvitationActivity"
|
android:name="org.briarproject.briar.android.sharing.BlogInvitationActivity"
|
||||||
android:label="@string/blogs_sharing_invitations_title"
|
android:label="@string/blogs_sharing_invitations_title"
|
||||||
android:parentActivityName=".android.contact.ConversationActivity">
|
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.contact.ConversationActivity"
|
android:value="org.briarproject.briar.android.contact.ConversationActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.forum.CreateForumActivity"
|
android:name="org.briarproject.briar.android.forum.CreateForumActivity"
|
||||||
android:label="@string/create_forum_title"
|
android:label="@string/create_forum_title"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:windowSoftInputMode="stateVisible">
|
android:windowSoftInputMode="adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.forum.ForumActivity"
|
android:name="org.briarproject.briar.android.forum.ForumActivity"
|
||||||
android:label="@string/app_name"
|
android:label="@string/app_name"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"
|
android:theme="@style/BriarTheme.NoActionBar"
|
||||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.sharing.ShareForumActivity"
|
android:name="org.briarproject.briar.android.sharing.ShareForumActivity"
|
||||||
android:label="@string/activity_share_toolbar_header"
|
android:label="@string/activity_share_toolbar_header"
|
||||||
android:parentActivityName=".android.forum.ForumActivity"
|
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity"
|
||||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.forum.ForumActivity"
|
android:value="org.briarproject.briar.android.forum.ForumActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.sharing.ShareBlogActivity"
|
android:name="org.briarproject.briar.android.sharing.ShareBlogActivity"
|
||||||
android:label="@string/activity_share_toolbar_header"
|
android:label="@string/activity_share_toolbar_header"
|
||||||
android:parentActivityName=".android.blog.BlogActivity"
|
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
android:windowSoftInputMode="adjustResize|stateHidden">
|
android:windowSoftInputMode="adjustResize|stateHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.blog.BlogActivity"
|
android:value="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.sharing.ForumSharingStatusActivity"
|
android:name="org.briarproject.briar.android.sharing.ForumSharingStatusActivity"
|
||||||
android:label="@string/sharing_status"
|
android:label="@string/sharing_status"
|
||||||
android:parentActivityName=".android.forum.ForumActivity">
|
android:parentActivityName="org.briarproject.briar.android.forum.ForumActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.forum.ForumActivity"
|
android:value="org.briarproject.briar.android.forum.ForumActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.sharing.BlogSharingStatusActivity"
|
android:name="org.briarproject.briar.android.sharing.BlogSharingStatusActivity"
|
||||||
android:label="@string/sharing_status"
|
android:label="@string/sharing_status"
|
||||||
android:parentActivityName=".android.blog.BlogActivity">
|
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.blog.BlogActivity"
|
android:value="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.blog.BlogActivity"
|
android:name="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:theme="@style/BriarTheme.NoActionBar">
|
android:theme="@style/BriarTheme.NoActionBar">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"/>
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.blog.WriteBlogPostActivity"
|
android:name="org.briarproject.briar.android.blog.WriteBlogPostActivity"
|
||||||
android:label="@string/blogs_write_blog_post"
|
android:label="@string/blogs_write_blog_post"
|
||||||
android:parentActivityName=".android.blog.BlogActivity"
|
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
android:windowSoftInputMode="stateVisible|adjustResize">
|
android:windowSoftInputMode="stateVisible|adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.blog.BlogActivity"
|
android:value="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.blog.ReblogActivity"
|
android:name="org.briarproject.briar.android.blog.ReblogActivity"
|
||||||
android:label="@string/blogs_reblog_button"
|
android:label="@string/blogs_reblog_button"
|
||||||
android:parentActivityName=".android.blog.BlogActivity"
|
android:parentActivityName="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
android:windowSoftInputMode="stateHidden">
|
android:windowSoftInputMode="stateHidden">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.blog.BlogActivity"
|
android:value="org.briarproject.briar.android.blog.BlogActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.blog.RssFeedImportActivity"
|
android:name="org.briarproject.briar.android.blog.RssFeedImportActivity"
|
||||||
android:label="@string/blogs_rss_feeds_import"
|
android:label="@string/blogs_rss_feeds_import"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:windowSoftInputMode="stateVisible|adjustResize">
|
android:windowSoftInputMode="stateVisible|adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.blog.RssFeedManageActivity"
|
android:name="org.briarproject.briar.android.blog.RssFeedManageActivity"
|
||||||
android:label="@string/blogs_rss_feeds_manage"
|
android:label="@string/blogs_rss_feeds_manage"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.invitation.AddContactActivity"
|
android:name="org.briarproject.briar.android.invitation.AddContactActivity"
|
||||||
android:label="@string/add_contact_title"
|
android:label="@string/add_contact_title"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.keyagreement.KeyAgreementActivity"
|
android:name="org.briarproject.briar.android.keyagreement.KeyAgreementActivity"
|
||||||
android:label="@string/add_contact_title"
|
android:label="@string/add_contact_title"
|
||||||
android:theme="@style/BriarTheme.NoActionBar"
|
android:theme="@style/BriarTheme.NoActionBar"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity">
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"/>
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.introduction.IntroductionActivity"
|
android:name="org.briarproject.briar.android.introduction.IntroductionActivity"
|
||||||
android:label="@string/introduction_activity_title"
|
android:label="@string/introduction_activity_title"
|
||||||
android:parentActivityName=".android.contact.ConversationActivity"
|
android:parentActivityName="org.briarproject.briar.android.contact.ConversationActivity"
|
||||||
android:windowSoftInputMode="stateHidden|adjustResize">
|
android:windowSoftInputMode="stateHidden|adjustResize">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.contact.ConversationActivity"
|
android:value="org.briarproject.briar.android.contact.ConversationActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.StartupFailureActivity"
|
android:name="org.briarproject.briar.android.StartupFailureActivity"
|
||||||
android:label="@string/startup_failed_activity_title">
|
android:label="@string/startup_failed_activity_title">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.settings.SettingsActivity"
|
android:name="org.briarproject.briar.android.settings.SettingsActivity"
|
||||||
android:label="@string/settings_button"
|
android:label="@string/settings_button"
|
||||||
android:parentActivityName=".android.navdrawer.NavDrawerActivity"
|
android:parentActivityName="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
|
android:permission="android.permission.READ_NETWORK_USAGE_HISTORY">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.navdrawer.NavDrawerActivity"
|
android:value="org.briarproject.briar.android.navdrawer.NavDrawerActivity"
|
||||||
/>
|
/>
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
|
<action android:name="android.intent.action.MANAGE_NETWORK_USAGE"/>
|
||||||
@@ -346,27 +344,27 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.login.ChangePasswordActivity"
|
android:name="org.briarproject.briar.android.login.ChangePasswordActivity"
|
||||||
android:label="@string/change_password"
|
android:label="@string/change_password"
|
||||||
android:parentActivityName=".android.settings.SettingsActivity">
|
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.settings.SettingsActivity"
|
android:value="org.briarproject.briar.android.settings.SettingsActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.panic.PanicPreferencesActivity"
|
android:name="org.briarproject.briar.android.panic.PanicPreferencesActivity"
|
||||||
android:label="@string/panic_setting"
|
android:label="@string/panic_setting"
|
||||||
android:parentActivityName=".android.settings.SettingsActivity">
|
android:parentActivityName="org.briarproject.briar.android.settings.SettingsActivity">
|
||||||
<meta-data
|
<meta-data
|
||||||
android:name="android.support.PARENT_ACTIVITY"
|
android:name="android.support.PARENT_ACTIVITY"
|
||||||
android:value=".android.settings.SettingsActivity"
|
android:value="org.briarproject.briar.android.settings.SettingsActivity"
|
||||||
/>
|
/>
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.panic.PanicResponderActivity"
|
android:name="org.briarproject.briar.android.panic.PanicResponderActivity"
|
||||||
android:noHistory="true"
|
android:noHistory="true"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
<!-- this can never have launchMode singleTask or singleInstance! -->
|
<!-- this can never have launchMode singleTask or singleInstance! -->
|
||||||
@@ -377,7 +375,7 @@
|
|||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
<activity
|
<activity
|
||||||
android:name=".android.panic.ExitActivity"
|
android:name="org.briarproject.briar.android.panic.ExitActivity"
|
||||||
android:theme="@android:style/Theme.NoDisplay">
|
android:theme="@android:style/Theme.NoDisplay">
|
||||||
</activity>
|
</activity>
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,7 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
|||||||
import org.briarproject.briar.api.blog.BlogManager;
|
import org.briarproject.briar.api.blog.BlogManager;
|
||||||
import org.briarproject.briar.api.blog.BlogPostFactory;
|
import org.briarproject.briar.api.blog.BlogPostFactory;
|
||||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||||
|
import org.briarproject.briar.api.client.MessageTracker;
|
||||||
import org.briarproject.briar.api.feed.FeedManager;
|
import org.briarproject.briar.api.feed.FeedManager;
|
||||||
import org.briarproject.briar.api.forum.ForumManager;
|
import org.briarproject.briar.api.forum.ForumManager;
|
||||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||||
@@ -78,6 +79,8 @@ public interface AndroidComponent
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
Executor databaseExecutor();
|
Executor databaseExecutor();
|
||||||
|
|
||||||
|
MessageTracker messageTracker();
|
||||||
|
|
||||||
LifecycleManager lifecycleManager();
|
LifecycleManager lifecycleManager();
|
||||||
|
|
||||||
IdentityManager identityManager();
|
IdentityManager identityManager();
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ import static android.content.Context.NOTIFICATION_SERVICE;
|
|||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
|
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
|
||||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
||||||
|
import static android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE;
|
||||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||||
@@ -327,6 +328,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_MESSAGE);
|
b.setCategory(CATEGORY_MESSAGE);
|
||||||
|
boolean showOnLockScreen =
|
||||||
|
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
|
||||||
|
if (showOnLockScreen)
|
||||||
|
b.setVisibility(VISIBILITY_PRIVATE);
|
||||||
|
else
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
@@ -347,17 +353,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
return defaults;
|
return defaults;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearAllContactNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
clearContactNotification();
|
|
||||||
clearIntroductionSuccessNotification();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void showGroupMessageNotification(final GroupId g) {
|
private void showGroupMessageNotification(final GroupId g) {
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
androidExecutor.runOnUiThread(new Runnable() {
|
||||||
@@ -432,6 +427,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_SOCIAL);
|
b.setCategory(CATEGORY_SOCIAL);
|
||||||
|
boolean showOnLockScreen =
|
||||||
|
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
|
||||||
|
if (showOnLockScreen)
|
||||||
|
b.setVisibility(VISIBILITY_PRIVATE);
|
||||||
|
else
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
@@ -440,16 +440,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearAllGroupMessageNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
clearGroupMessageNotification();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void showForumPostNotification(final GroupId g) {
|
private void showForumPostNotification(final GroupId g) {
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
androidExecutor.runOnUiThread(new Runnable() {
|
||||||
@@ -524,6 +514,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
}
|
}
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_SOCIAL);
|
b.setCategory(CATEGORY_SOCIAL);
|
||||||
|
boolean showOnLockScreen =
|
||||||
|
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
|
||||||
|
if (showOnLockScreen)
|
||||||
|
b.setVisibility(VISIBILITY_PRIVATE);
|
||||||
|
else
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
@@ -532,16 +527,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void clearAllForumPostNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
clearForumPostNotification();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
private void showBlogPostNotification(final GroupId g) {
|
private void showBlogPostNotification(final GroupId g) {
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
androidExecutor.runOnUiThread(new Runnable() {
|
||||||
@@ -602,6 +587,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_SOCIAL);
|
b.setCategory(CATEGORY_SOCIAL);
|
||||||
|
boolean showOnLockScreen =
|
||||||
|
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
|
||||||
|
if (showOnLockScreen)
|
||||||
|
b.setVisibility(VISIBILITY_PRIVATE);
|
||||||
|
else
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
@@ -658,6 +648,11 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
b.setContentIntent(t.getPendingIntent(nextRequestId++, 0));
|
||||||
if (Build.VERSION.SDK_INT >= 21) {
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
b.setCategory(CATEGORY_MESSAGE);
|
b.setCategory(CATEGORY_MESSAGE);
|
||||||
|
boolean showOnLockScreen =
|
||||||
|
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
|
||||||
|
if (showOnLockScreen)
|
||||||
|
b.setVisibility(VISIBILITY_PRIVATE);
|
||||||
|
else
|
||||||
b.setVisibility(VISIBILITY_SECRET);
|
b.setVisibility(VISIBILITY_SECRET);
|
||||||
}
|
}
|
||||||
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
Object o = appContext.getSystemService(NOTIFICATION_SERVICE);
|
||||||
@@ -705,68 +700,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void blockAllContactNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
blockContacts = true;
|
|
||||||
blockIntroductions = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unblockAllContactNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
blockContacts = false;
|
|
||||||
blockIntroductions = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void blockAllGroupMessageNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
blockGroups = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unblockAllGroupMessageNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
blockGroups = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void blockAllForumPostNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
blockForums = true;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void unblockAllForumPostNotifications() {
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
blockForums = false;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void blockAllBlogPostNotifications() {
|
public void blockAllBlogPostNotifications() {
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
androidExecutor.runOnUiThread(new Runnable() {
|
||||||
@@ -786,5 +719,4 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -38,8 +38,6 @@ public class AppModule {
|
|||||||
static class EagerSingletons {
|
static class EagerSingletons {
|
||||||
@Inject
|
@Inject
|
||||||
AndroidNotificationManager androidNotificationManager;
|
AndroidNotificationManager androidNotificationManager;
|
||||||
@Inject
|
|
||||||
ScreenFilterMonitor screenFilterMonitor;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private final Application application;
|
private final Application application;
|
||||||
@@ -171,10 +169,8 @@ public class AppModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
|
||||||
ScreenFilterMonitor provideScreenFilterMonitor(
|
ScreenFilterMonitor provideScreenFilterMonitor(
|
||||||
LifecycleManager lifecycleManager, ScreenFilterMonitorImpl sfm) {
|
ScreenFilterMonitorImpl screenFilterMonitor) {
|
||||||
lifecycleManager.registerService(sfm);
|
return screenFilterMonitor;
|
||||||
return sfm;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,5 +6,9 @@ package org.briarproject.briar.android;
|
|||||||
*/
|
*/
|
||||||
public interface BriarApplication {
|
public interface BriarApplication {
|
||||||
|
|
||||||
|
// This build expires on 15 October 2017
|
||||||
|
long EXPIRY_DATE = 1508022000 * 1000L;
|
||||||
|
|
||||||
AndroidComponent getApplicationComponent();
|
AndroidComponent getApplicationComponent();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,24 +1,13 @@
|
|||||||
package org.briarproject.briar.android;
|
package org.briarproject.briar.android;
|
||||||
|
|
||||||
import android.app.Application;
|
import android.app.Application;
|
||||||
import android.content.BroadcastReceiver;
|
|
||||||
import android.content.Context;
|
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.IntentFilter;
|
|
||||||
import android.content.SharedPreferences;
|
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.content.pm.PackageManager.NameNotFoundException;
|
import android.content.pm.PackageManager.NameNotFoundException;
|
||||||
import android.content.pm.Signature;
|
import android.content.pm.Signature;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v7.preference.PreferenceManager;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.lifecycle.Service;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.lifecycle.ServiceException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
|
||||||
import org.briarproject.bramble.util.StringUtils;
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||||
|
|
||||||
@@ -27,37 +16,26 @@ import java.io.InputStream;
|
|||||||
import java.security.cert.CertificateException;
|
import java.security.cert.CertificateException;
|
||||||
import java.security.cert.CertificateFactory;
|
import java.security.cert.CertificateFactory;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.TreeSet;
|
import java.util.TreeSet;
|
||||||
import java.util.concurrent.Callable;
|
|
||||||
import java.util.concurrent.ExecutionException;
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.concurrent.atomic.AtomicBoolean;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
|
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
|
||||||
import static android.content.Intent.ACTION_PACKAGE_ADDED;
|
|
||||||
import static android.content.Intent.EXTRA_REPLACING;
|
|
||||||
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||||
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||||
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
||||||
import static android.content.pm.PackageManager.GET_SIGNATURES;
|
import static android.content.pm.PackageManager.GET_SIGNATURES;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@NotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
class ScreenFilterMonitorImpl implements ScreenFilterMonitor {
|
||||||
public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
|
||||||
implements Service, ScreenFilterMonitor {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
|
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
|
||||||
private static final String PREF_SCREEN_FILTER_APPS =
|
|
||||||
"shownScreenFilterApps";
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ignore Play Services if it uses this package name and public key - it's
|
* Ignore Play Services if it uses this package name and public key - it's
|
||||||
@@ -78,124 +56,17 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
|||||||
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
|
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
|
||||||
"0B145B6AA192858E79020103";
|
"0B145B6AA192858E79020103";
|
||||||
|
|
||||||
private final Context appContext;
|
|
||||||
private final AndroidExecutor androidExecutor;
|
|
||||||
private final PackageManager pm;
|
private final PackageManager pm;
|
||||||
private final SharedPreferences prefs;
|
|
||||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
|
||||||
|
|
||||||
// The following must only be accessed on the UI thread
|
|
||||||
private final Set<String> apps = new HashSet<>();
|
|
||||||
private final Set<String> shownApps;
|
|
||||||
private boolean serviceStarted = false;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
ScreenFilterMonitorImpl(AndroidExecutor executor, Application app) {
|
ScreenFilterMonitorImpl(Application app) {
|
||||||
this.androidExecutor = executor;
|
pm = app.getPackageManager();
|
||||||
this.appContext = app;
|
|
||||||
pm = appContext.getPackageManager();
|
|
||||||
prefs = PreferenceManager.getDefaultSharedPreferences(appContext);
|
|
||||||
shownApps = getShownScreenFilterApps();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void startService() throws ServiceException {
|
|
||||||
if (used.getAndSet(true)) throw new IllegalStateException();
|
|
||||||
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void call() {
|
|
||||||
IntentFilter intentFilter = new IntentFilter();
|
|
||||||
intentFilter.addAction(ACTION_PACKAGE_ADDED);
|
|
||||||
intentFilter.addDataScheme("package");
|
|
||||||
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
|
|
||||||
intentFilter);
|
|
||||||
apps.addAll(getInstalledScreenFilterApps());
|
|
||||||
serviceStarted = true;
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
f.get();
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
throw new ServiceException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void stopService() throws ServiceException {
|
|
||||||
Future<Void> f = androidExecutor.runOnUiThread(new Callable<Void>() {
|
|
||||||
@Override
|
|
||||||
public Void call() {
|
|
||||||
serviceStarted = false;
|
|
||||||
appContext.unregisterReceiver(ScreenFilterMonitorImpl.this);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
try {
|
|
||||||
f.get();
|
|
||||||
} catch (InterruptedException | ExecutionException e) {
|
|
||||||
throw new ServiceException(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getShownScreenFilterApps() {
|
|
||||||
// Result must not be modified
|
|
||||||
Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
|
|
||||||
HashSet<String> result = new HashSet<>();
|
|
||||||
if (s != null) {
|
|
||||||
result.addAll(s);
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onReceive(Context context, Intent intent) {
|
|
||||||
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
|
|
||||||
final String packageName =
|
|
||||||
intent.getData().getEncodedSchemeSpecificPart();
|
|
||||||
androidExecutor.runOnUiThread(new Runnable() {
|
|
||||||
@Override
|
|
||||||
public void run() {
|
|
||||||
String pkg = isOverlayApp(packageName);
|
|
||||||
if (pkg == null) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
apps.add(pkg);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@UiThread
|
@UiThread
|
||||||
public Set<String> getApps() {
|
public Set<String> getApps() {
|
||||||
if (!serviceStarted) {
|
Set<String> screenFilterApps = new TreeSet<>();
|
||||||
apps.addAll(getInstalledScreenFilterApps());
|
|
||||||
}
|
|
||||||
TreeSet<String> buf = new TreeSet<>();
|
|
||||||
if (apps.isEmpty()) {
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
buf.addAll(apps);
|
|
||||||
buf.removeAll(shownApps);
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
@UiThread
|
|
||||||
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
|
|
||||||
HashSet<String> buf = new HashSet<>(s);
|
|
||||||
shownApps.addAll(buf);
|
|
||||||
if (persistent && !s.isEmpty()) {
|
|
||||||
buf.addAll(getShownScreenFilterApps());
|
|
||||||
prefs.edit()
|
|
||||||
.putStringSet(PREF_SCREEN_FILTER_APPS, buf)
|
|
||||||
.apply();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getInstalledScreenFilterApps() {
|
|
||||||
HashSet<String> screenFilterApps = new HashSet<>();
|
|
||||||
List<PackageInfo> packageInfos =
|
List<PackageInfo> packageInfos =
|
||||||
pm.getInstalledPackages(GET_PERMISSIONS);
|
pm.getInstalledPackages(GET_PERMISSIONS);
|
||||||
for (PackageInfo packageInfo : packageInfos) {
|
for (PackageInfo packageInfo : packageInfos) {
|
||||||
@@ -209,21 +80,6 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
|||||||
return screenFilterApps;
|
return screenFilterApps;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
|
|
||||||
// returns the app name.
|
|
||||||
@Nullable
|
|
||||||
private String isOverlayApp(String pkg) {
|
|
||||||
try {
|
|
||||||
PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
|
|
||||||
if (isOverlayApp(pkgInfo)) {
|
|
||||||
return pkgToString(pkgInfo);
|
|
||||||
}
|
|
||||||
} catch (NameNotFoundException e) {
|
|
||||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
|
||||||
}
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Fetches the application name for a given package.
|
// Fetches the application name for a given package.
|
||||||
@Nullable
|
@Nullable
|
||||||
private String pkgToString(PackageInfo pkgInfo) {
|
private String pkgToString(PackageInfo pkgInfo) {
|
||||||
|
|||||||
@@ -39,7 +39,7 @@ import org.briarproject.briar.android.privategroup.conversation.GroupConversatio
|
|||||||
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
|
import org.briarproject.briar.android.privategroup.creation.CreateGroupActivity;
|
||||||
import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment;
|
import org.briarproject.briar.android.privategroup.creation.CreateGroupFragment;
|
||||||
import org.briarproject.briar.android.privategroup.creation.CreateGroupMessageFragment;
|
import org.briarproject.briar.android.privategroup.creation.CreateGroupMessageFragment;
|
||||||
import org.briarproject.briar.android.privategroup.creation.GroupCreateModule;
|
import org.briarproject.briar.android.privategroup.creation.CreateGroupModule;
|
||||||
import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity;
|
import org.briarproject.briar.android.privategroup.creation.GroupInviteActivity;
|
||||||
import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment;
|
import org.briarproject.briar.android.privategroup.creation.GroupInviteFragment;
|
||||||
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
|
import org.briarproject.briar.android.privategroup.invitation.GroupInvitationActivity;
|
||||||
@@ -71,7 +71,7 @@ import dagger.Component;
|
|||||||
@Component(
|
@Component(
|
||||||
modules = {ActivityModule.class, ForumModule.class, SharingModule.class,
|
modules = {ActivityModule.class, ForumModule.class, SharingModule.class,
|
||||||
BlogModule.class, ContactModule.class, GroupListModule.class,
|
BlogModule.class, ContactModule.class, GroupListModule.class,
|
||||||
GroupCreateModule.class, GroupInvitationModule.class,
|
CreateGroupModule.class, GroupInvitationModule.class,
|
||||||
GroupConversationModule.class, GroupMemberModule.class,
|
GroupConversationModule.class, GroupMemberModule.class,
|
||||||
GroupRevealModule.class},
|
GroupRevealModule.class},
|
||||||
dependencies = AndroidComponent.class)
|
dependencies = AndroidComponent.class)
|
||||||
|
|||||||
@@ -2,9 +2,13 @@ package org.briarproject.briar.android.activity;
|
|||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.IBinder;
|
import android.os.IBinder;
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v7.app.AppCompatActivity;
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewGroup.LayoutParams;
|
||||||
import android.view.inputmethod.InputMethodManager;
|
import android.view.inputmethod.InputMethodManager;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
@@ -13,7 +17,9 @@ import org.briarproject.briar.android.BriarApplication;
|
|||||||
import org.briarproject.briar.android.DestroyableContext;
|
import org.briarproject.briar.android.DestroyableContext;
|
||||||
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
||||||
import org.briarproject.briar.android.forum.ForumModule;
|
import org.briarproject.briar.android.forum.ForumModule;
|
||||||
import org.briarproject.briar.android.fragment.SFDialogFragment;
|
import org.briarproject.briar.android.fragment.ScreenFilterDialogFragment;
|
||||||
|
import org.briarproject.briar.android.widget.TapSafeFrameLayout;
|
||||||
|
import org.briarproject.briar.android.widget.TapSafeFrameLayout.OnTapFilteredListener;
|
||||||
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
@@ -23,21 +29,23 @@ import java.util.Set;
|
|||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
|
||||||
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
import static android.view.WindowManager.LayoutParams.FLAG_SECURE;
|
||||||
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
import static android.view.inputmethod.InputMethodManager.SHOW_IMPLICIT;
|
||||||
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
import static org.briarproject.briar.android.TestingConstants.PREVENT_SCREENSHOTS;
|
||||||
|
|
||||||
public abstract class BaseActivity extends AppCompatActivity
|
public abstract class BaseActivity extends AppCompatActivity
|
||||||
implements DestroyableContext {
|
implements DestroyableContext, OnTapFilteredListener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
protected ScreenFilterMonitor screenFilterMonitor;
|
||||||
|
|
||||||
protected ActivityComponent activityComponent;
|
protected ActivityComponent activityComponent;
|
||||||
|
|
||||||
private final List<ActivityLifecycleController> lifecycleControllers =
|
private final List<ActivityLifecycleController> lifecycleControllers =
|
||||||
new ArrayList<>();
|
new ArrayList<>();
|
||||||
private boolean destroyed = false;
|
private boolean destroyed = false;
|
||||||
|
private ScreenFilterDialogFragment dialogFrag;
|
||||||
@Inject
|
|
||||||
protected ScreenFilterMonitor screenFilterMonitor;
|
|
||||||
private SFDialogFragment dialogFrag;
|
|
||||||
|
|
||||||
public abstract void injectActivity(ActivityComponent component);
|
public abstract void injectActivity(ActivityComponent component);
|
||||||
|
|
||||||
@@ -65,7 +73,6 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
for (ActivityLifecycleController alc : lifecycleControllers) {
|
for (ActivityLifecycleController alc : lifecycleControllers) {
|
||||||
alc.onActivityCreate(this);
|
alc.onActivityCreate(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public ActivityComponent getActivityComponent() {
|
public ActivityComponent getActivityComponent() {
|
||||||
@@ -97,12 +104,6 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onPostResume() {
|
|
||||||
super.onPostResume();
|
|
||||||
showNewScreenFilterWarning();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPause() {
|
protected void onPause() {
|
||||||
super.onPause();
|
super.onPause();
|
||||||
@@ -112,18 +113,14 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void showNewScreenFilterWarning() {
|
private void showScreenFilterWarning() {
|
||||||
final Set<String> apps = screenFilterMonitor.getApps();
|
if (dialogFrag != null && dialogFrag.isVisible()) return;
|
||||||
if (apps.isEmpty()) {
|
Set<String> apps = screenFilterMonitor.getApps();
|
||||||
return;
|
if (apps.isEmpty()) return;
|
||||||
}
|
dialogFrag =
|
||||||
dialogFrag = SFDialogFragment.newInstance(new ArrayList<>(apps));
|
ScreenFilterDialogFragment.newInstance(new ArrayList<>(apps));
|
||||||
dialogFrag.setCancelable(false);
|
dialogFrag.setCancelable(false);
|
||||||
dialogFrag.show(getSupportFragmentManager(), "SFDialog");
|
dialogFrag.show(getSupportFragmentManager(), dialogFrag.getTag());
|
||||||
}
|
|
||||||
|
|
||||||
public void rememberShownApps(ArrayList<String> s, boolean permanent) {
|
|
||||||
screenFilterMonitor.storeAppsAsShown(s, permanent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -161,4 +158,70 @@ public abstract class BaseActivity extends AppCompatActivity
|
|||||||
supportFinishAfterTransition();
|
supportFinishAfterTransition();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Wraps the given view in a wrapper that notifies this activity when an
|
||||||
|
* obscured touch has been filtered, and returns the wrapper.
|
||||||
|
*/
|
||||||
|
private View makeTapSafeWrapper(View v) {
|
||||||
|
TapSafeFrameLayout wrapper = new TapSafeFrameLayout(this);
|
||||||
|
wrapper.setLayoutParams(new LayoutParams(MATCH_PARENT, MATCH_PARENT));
|
||||||
|
wrapper.setOnTapFilteredListener(this);
|
||||||
|
wrapper.addView(v);
|
||||||
|
return wrapper;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Finds the AppCompat toolbar, if any, and configures it to filter
|
||||||
|
* obscured touches. If a custom toolbar is used, it will be part of the
|
||||||
|
* content view and thus protected by the wrapper. But the default toolbar
|
||||||
|
* is outside the wrapper.
|
||||||
|
*/
|
||||||
|
private void protectToolbar() {
|
||||||
|
View decorView = getWindow().getDecorView();
|
||||||
|
if (decorView instanceof ViewGroup) {
|
||||||
|
Toolbar toolbar = findToolbar((ViewGroup) decorView);
|
||||||
|
if (toolbar != null) toolbar.setFilterTouchesWhenObscured(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private Toolbar findToolbar(ViewGroup vg) {
|
||||||
|
for (int i = 0, len = vg.getChildCount(); i < len; i++) {
|
||||||
|
View child = vg.getChildAt(i);
|
||||||
|
if (child instanceof Toolbar) return (Toolbar) child;
|
||||||
|
if (child instanceof ViewGroup) {
|
||||||
|
Toolbar toolbar = findToolbar((ViewGroup) child);
|
||||||
|
if (toolbar != null) return toolbar;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContentView(@LayoutRes int layoutRes) {
|
||||||
|
setContentView(getLayoutInflater().inflate(layoutRes, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContentView(View v) {
|
||||||
|
super.setContentView(makeTapSafeWrapper(v));
|
||||||
|
protectToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setContentView(View v, LayoutParams layoutParams) {
|
||||||
|
super.setContentView(makeTapSafeWrapper(v), layoutParams);
|
||||||
|
protectToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void addContentView(View v, LayoutParams layoutParams) {
|
||||||
|
super.addContentView(makeTapSafeWrapper(v), layoutParams);
|
||||||
|
protectToolbar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTapFiltered() {
|
||||||
|
showScreenFilterWarning();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.activity;
|
|||||||
import android.annotation.SuppressLint;
|
import android.annotation.SuppressLint;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.app.ActionBar;
|
import android.support.v7.app.ActionBar;
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.transition.Slide;
|
import android.transition.Slide;
|
||||||
@@ -21,6 +20,7 @@ import org.briarproject.briar.android.panic.ExitActivity;
|
|||||||
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
package org.briarproject.briar.android.blog;
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Handler;
|
import android.os.Handler;
|
||||||
import android.os.Looper;
|
import android.os.Looper;
|
||||||
@@ -20,8 +21,10 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.view.View.INVISIBLE;
|
import static android.view.View.INVISIBLE;
|
||||||
import static android.view.View.VISIBLE;
|
import static android.view.View.VISIBLE;
|
||||||
|
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||||
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
|
import static org.briarproject.briar.android.util.UiUtils.MIN_DATE_RESOLUTION;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -58,6 +61,20 @@ abstract class BasePostFragment extends BaseFragment {
|
|||||||
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
progressBar = (ProgressBar) view.findViewById(R.id.progressBar);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
ui = new BlogPostViewHolder(view);
|
ui = new BlogPostViewHolder(view);
|
||||||
|
ui.setOnBlogPostClickListener(new OnBlogPostClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onBlogPostClick(BlogPostItem post) {
|
||||||
|
// We're already there
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthorClick(BlogPostItem post) {
|
||||||
|
Intent i = new Intent(getContext(), BlogActivity.class);
|
||||||
|
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
|
||||||
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
getContext().startActivity(i);
|
||||||
|
}
|
||||||
|
});
|
||||||
return view;
|
return view;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.blog;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v7.widget.Toolbar;
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
|
||||||
@@ -15,6 +14,7 @@ import org.briarproject.briar.android.activity.BriarActivity;
|
|||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
|
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
|
|||||||
@@ -169,7 +169,7 @@ class BlogControllerImpl extends BaseControllerImpl
|
|||||||
LocalAuthor a = identityManager.getLocalAuthor();
|
LocalAuthor a = identityManager.getLocalAuthor();
|
||||||
Blog b = blogManager.getBlog(groupId);
|
Blog b = blogManager.getBlog(groupId);
|
||||||
boolean ours = a.getId().equals(b.getAuthor().getId());
|
boolean ours = a.getId().equals(b.getAuthor().getId());
|
||||||
boolean removable = blogManager.canBeRemoved(groupId);
|
boolean removable = blogManager.canBeRemoved(b);
|
||||||
BlogItem blog = new BlogItem(b, ours, removable);
|
BlogItem blog = new BlogItem(b, ours, removable);
|
||||||
long duration = System.currentTimeMillis() - now;
|
long duration = System.currentTimeMillis() - now;
|
||||||
if (LOG.isLoggable(INFO))
|
if (LOG.isLoggable(INFO))
|
||||||
|
|||||||
@@ -27,7 +27,6 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.blog.BlogController.BlogSharingListener;
|
import org.briarproject.briar.android.blog.BlogController.BlogSharingListener;
|
||||||
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
|
|
||||||
import org.briarproject.briar.android.controller.SharingController;
|
import org.briarproject.briar.android.controller.SharingController;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
@@ -216,10 +215,19 @@ public class BlogFragment extends BaseFragment
|
|||||||
showNextFragment(f);
|
showNextFragment(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthorClick(BlogPostItem post) {
|
||||||
|
if (post.getGroupId().equals(groupId)) return; // We're already there
|
||||||
|
Intent i = new Intent(getContext(), BlogActivity.class);
|
||||||
|
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
|
||||||
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
getContext().startActivity(i);
|
||||||
|
}
|
||||||
|
|
||||||
private void loadBlogPosts(final boolean reload) {
|
private void loadBlogPosts(final boolean reload) {
|
||||||
blogController.loadBlogPosts(
|
blogController.loadBlogPosts(
|
||||||
new UiResultExceptionHandler<Collection<BlogPostItem>, DbException>(
|
new UiResultExceptionHandler<Collection<BlogPostItem>,
|
||||||
this) {
|
DbException>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Collection<BlogPostItem> posts) {
|
public void onResultUi(Collection<BlogPostItem> posts) {
|
||||||
if (posts.isEmpty()) {
|
if (posts.isEmpty()) {
|
||||||
@@ -257,13 +265,13 @@ public class BlogFragment extends BaseFragment
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void setToolbarTitle(Author a) {
|
private void setToolbarTitle(Author a) {
|
||||||
String title = getString(R.string.blogs_personal_blog, a.getName());
|
getActivity().setTitle(a.getName());
|
||||||
getActivity().setTitle(title);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void loadSharedContacts() {
|
private void loadSharedContacts() {
|
||||||
blogController.loadSharingContacts(
|
blogController.loadSharingContacts(
|
||||||
new UiResultExceptionHandler<Collection<ContactId>, DbException>(this) {
|
new UiResultExceptionHandler<Collection<ContactId>,
|
||||||
|
DbException>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Collection<ContactId> contacts) {
|
public void onResultUi(Collection<ContactId> contacts) {
|
||||||
sharingController.addAll(contacts);
|
sharingController.addAll(contacts);
|
||||||
|
|||||||
@@ -48,8 +48,4 @@ class BlogPostAdapter
|
|||||||
return a.getId().equals(b.getId());
|
return a.getId().equals(b.getId());
|
||||||
}
|
}
|
||||||
|
|
||||||
interface OnBlogPostClickListener {
|
|
||||||
void onBlogPostClick(BlogPostItem post);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import android.widget.TextView;
|
|||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.sync.MessageId;
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
|
|
||||||
import org.briarproject.briar.android.view.AuthorView;
|
import org.briarproject.briar.android.view.AuthorView;
|
||||||
import org.briarproject.briar.api.blog.BlogCommentHeader;
|
import org.briarproject.briar.api.blog.BlogCommentHeader;
|
||||||
import org.briarproject.briar.api.blog.BlogPostHeader;
|
import org.briarproject.briar.api.blog.BlogPostHeader;
|
||||||
@@ -49,6 +48,7 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
private final TextView body;
|
private final TextView body;
|
||||||
private final ViewGroup commentContainer;
|
private final ViewGroup commentContainer;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
private OnBlogPostClickListener listener;
|
private OnBlogPostClickListener listener;
|
||||||
|
|
||||||
BlogPostViewHolder(View v) {
|
BlogPostViewHolder(View v) {
|
||||||
@@ -111,10 +111,15 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
author.setPersona(
|
author.setPersona(
|
||||||
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
|
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
|
||||||
// TODO make author clickable more often #624
|
// TODO make author clickable more often #624
|
||||||
if (item.getHeader().getType() == POST) {
|
if (listener != null && item.getHeader().getType() == POST) {
|
||||||
author.setBlogLink(post.getGroupId());
|
author.setAuthorClickable(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
listener.onAuthorClick(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
author.unsetBlogLink();
|
author.setAuthorNotClickable();
|
||||||
}
|
}
|
||||||
|
|
||||||
// post body
|
// post body
|
||||||
@@ -165,7 +170,14 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
|||||||
reblogger.setAuthor(item.getAuthor());
|
reblogger.setAuthor(item.getAuthor());
|
||||||
reblogger.setAuthorStatus(item.getAuthorStatus());
|
reblogger.setAuthorStatus(item.getAuthorStatus());
|
||||||
reblogger.setDate(item.getTimestamp());
|
reblogger.setDate(item.getTimestamp());
|
||||||
reblogger.setBlogLink(item.getGroupId());
|
if (listener != null) {
|
||||||
|
reblogger.setAuthorClickable(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
listener.onAuthorClick(item);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
reblogger.setVisibility(VISIBLE);
|
reblogger.setVisibility(VISIBLE);
|
||||||
reblogger.setPersona(AuthorView.REBLOGGER);
|
reblogger.setPersona(AuthorView.REBLOGGER);
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.blog.BlogPostAdapter.OnBlogPostClickListener;
|
|
||||||
import org.briarproject.briar.android.blog.FeedController.FeedListener;
|
import org.briarproject.briar.android.blog.FeedController.FeedListener;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
@@ -34,6 +33,7 @@ import javax.annotation.Nullable;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
|
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||||
import static android.support.design.widget.Snackbar.LENGTH_LONG;
|
import static android.support.design.widget.Snackbar.LENGTH_LONG;
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_WRITE_BLOG_POST;
|
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_WRITE_BLOG_POST;
|
||||||
@@ -223,6 +223,14 @@ public class FeedFragment extends BaseFragment implements
|
|||||||
showNextFragment(f);
|
showNextFragment(f);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAuthorClick(BlogPostItem post) {
|
||||||
|
Intent i = new Intent(getContext(), BlogActivity.class);
|
||||||
|
i.putExtra(GROUP_ID, post.getGroupId().getBytes());
|
||||||
|
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||||
|
getContext().startActivity(i);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String getUniqueTag() {
|
public String getUniqueTag() {
|
||||||
return TAG;
|
return TAG;
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
package org.briarproject.briar.android.blog;
|
||||||
|
|
||||||
|
interface OnBlogPostClickListener {
|
||||||
|
|
||||||
|
void onBlogPostClick(BlogPostItem post);
|
||||||
|
|
||||||
|
void onAuthorClick(BlogPostItem post);
|
||||||
|
}
|
||||||
@@ -5,6 +5,7 @@ import android.os.Bundle;
|
|||||||
import android.support.v7.app.AlertDialog;
|
import android.support.v7.app.AlertDialog;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
import android.util.Patterns;
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -20,9 +21,12 @@ import org.briarproject.briar.android.activity.BriarActivity;
|
|||||||
import org.briarproject.briar.api.feed.FeedManager;
|
import org.briarproject.briar.api.feed.FeedManager;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.net.URL;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
@@ -98,10 +102,17 @@ public class RssFeedImportActivity extends BriarActivity {
|
|||||||
|
|
||||||
private void enableOrDisableImportButton() {
|
private void enableOrDisableImportButton() {
|
||||||
String url = urlInput.getText().toString();
|
String url = urlInput.getText().toString();
|
||||||
if (url.startsWith("http://") || url.startsWith("https://"))
|
importButton.setEnabled(validateAndNormaliseUrl(url) != null);
|
||||||
importButton.setEnabled(true);
|
}
|
||||||
else
|
|
||||||
importButton.setEnabled(false);
|
@Nullable
|
||||||
|
private String validateAndNormaliseUrl(String url) {
|
||||||
|
if (!Patterns.WEB_URL.matcher(url).matches()) return null;
|
||||||
|
try {
|
||||||
|
return new URL(url).toString();
|
||||||
|
} catch (MalformedURLException e) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void publish() {
|
private void publish() {
|
||||||
@@ -109,7 +120,9 @@ public class RssFeedImportActivity extends BriarActivity {
|
|||||||
importButton.setVisibility(GONE);
|
importButton.setVisibility(GONE);
|
||||||
progressBar.setVisibility(VISIBLE);
|
progressBar.setVisibility(VISIBLE);
|
||||||
|
|
||||||
importFeed(urlInput.getText().toString());
|
String url = validateAndNormaliseUrl(urlInput.getText().toString());
|
||||||
|
if (url == null) throw new AssertionError();
|
||||||
|
importFeed(url);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void importFeed(final String url) {
|
private void importFeed(final String url) {
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
package org.briarproject.briar.android.contact;
|
package org.briarproject.briar.android.contact;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
@@ -13,6 +12,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import im.delight.android.identicons.IdenticonDrawable;
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
|
|||||||
@@ -178,8 +178,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
notificationManager.blockAllContactNotifications();
|
|
||||||
notificationManager.clearAllContactNotifications();
|
|
||||||
eventBus.addListener(this);
|
eventBus.addListener(this);
|
||||||
loadContacts();
|
loadContacts();
|
||||||
list.startPeriodicUpdate();
|
list.startPeriodicUpdate();
|
||||||
@@ -189,7 +187,6 @@ public class ContactListFragment extends BaseFragment implements EventListener {
|
|||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
eventBus.removeListener(this);
|
eventBus.removeListener(this);
|
||||||
notificationManager.unblockAllContactNotifications();
|
|
||||||
adapter.clear();
|
adapter.clear();
|
||||||
list.showProgressBar();
|
list.showProgressBar();
|
||||||
list.stopPeriodicUpdate();
|
list.stopPeriodicUpdate();
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ public class DbControllerImpl implements DbController {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(DbControllerImpl.class.getName());
|
Logger.getLogger(DbControllerImpl.class.getName());
|
||||||
|
|
||||||
private final Executor dbExecutor;
|
protected final Executor dbExecutor;
|
||||||
private final LifecycleManager lifecycleManager;
|
private final LifecycleManager lifecycleManager;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
|
|||||||
@@ -1,7 +1,5 @@
|
|||||||
package org.briarproject.briar.android.controller;
|
package org.briarproject.briar.android.controller;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
@@ -15,6 +13,7 @@ import java.util.Collection;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
|
|||||||
@@ -38,8 +38,7 @@ import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_NAME_LEN
|
|||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class CreateForumActivity extends BriarActivity
|
public class CreateForumActivity extends BriarActivity {
|
||||||
implements OnEditorActionListener, OnClickListener {
|
|
||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(CreateForumActivity.class.getName());
|
Logger.getLogger(CreateForumActivity.class.getName());
|
||||||
@@ -60,11 +59,7 @@ public class CreateForumActivity extends BriarActivity
|
|||||||
setContentView(R.layout.activity_create_forum);
|
setContentView(R.layout.activity_create_forum);
|
||||||
|
|
||||||
nameEntry = (EditText) findViewById(R.id.createForumNameEntry);
|
nameEntry = (EditText) findViewById(R.id.createForumNameEntry);
|
||||||
TextWatcher nameEntryWatcher = new TextWatcher() {
|
nameEntry.addTextChangedListener(new TextWatcher() {
|
||||||
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable s) {
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||||
@@ -72,21 +67,41 @@ public class CreateForumActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence text, int start,
|
public void onTextChanged(CharSequence s, int start,
|
||||||
int lengthBefore, int lengthAfter) {
|
int lengthBefore, int lengthAfter) {
|
||||||
enableOrDisableCreateButton();
|
enableOrDisableCreateButton();
|
||||||
}
|
}
|
||||||
};
|
|
||||||
nameEntry.setOnEditorActionListener(this);
|
@Override
|
||||||
nameEntry.addTextChangedListener(nameEntryWatcher);
|
public void afterTextChanged(Editable s) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
nameEntry.setOnEditorActionListener(new OnEditorActionListener() {
|
||||||
|
@Override
|
||||||
|
public boolean onEditorAction(TextView v, int actionId,
|
||||||
|
KeyEvent e) {
|
||||||
|
createForum();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
feedback = (TextView) findViewById(R.id.createForumFeedback);
|
feedback = (TextView) findViewById(R.id.createForumFeedback);
|
||||||
|
|
||||||
createForumButton = (Button) findViewById(R.id.createForumButton);
|
createForumButton = (Button) findViewById(R.id.createForumButton);
|
||||||
createForumButton.setOnClickListener(this);
|
createForumButton.setOnClickListener(new OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
createForum();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
progress = (ProgressBar) findViewById(R.id.createForumProgressBar);
|
progress = (ProgressBar) findViewById(R.id.createForumProgressBar);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStart() {
|
||||||
|
super.onStart();
|
||||||
|
showSoftKeyboard(nameEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -95,16 +110,10 @@ public class CreateForumActivity extends BriarActivity
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void enableOrDisableCreateButton() {
|
private void enableOrDisableCreateButton() {
|
||||||
if (progress == null) return; // Not created yet
|
if (createForumButton == null) return; // Not created yet
|
||||||
createForumButton.setEnabled(validateName());
|
createForumButton.setEnabled(validateName());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onEditorAction(TextView textView, int actionId, KeyEvent e) {
|
|
||||||
hideSoftKeyboard(textView);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private boolean validateName() {
|
private boolean validateName() {
|
||||||
String name = nameEntry.getText().toString();
|
String name = nameEntry.getText().toString();
|
||||||
int length = StringUtils.toUtf8(name).length;
|
int length = StringUtils.toUtf8(name).length;
|
||||||
@@ -116,16 +125,13 @@ public class CreateForumActivity extends BriarActivity
|
|||||||
return length > 0;
|
return length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
private void createForum() {
|
||||||
public void onClick(View view) {
|
|
||||||
if (view == createForumButton) {
|
|
||||||
hideSoftKeyboard(view);
|
|
||||||
if (!validateName()) return;
|
if (!validateName()) return;
|
||||||
|
hideSoftKeyboard(nameEntry);
|
||||||
createForumButton.setVisibility(GONE);
|
createForumButton.setVisibility(GONE);
|
||||||
progress.setVisibility(VISIBLE);
|
progress.setVisibility(VISIBLE);
|
||||||
storeForum(nameEntry.getText().toString());
|
storeForum(nameEntry.getText().toString());
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private void storeForum(final String name) {
|
private void storeForum(final String name) {
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
|||||||
import org.briarproject.briar.android.forum.ForumController.ForumListener;
|
import org.briarproject.briar.android.forum.ForumController.ForumListener;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
|
import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
|
import org.briarproject.briar.api.client.MessageTracker;
|
||||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||||
import org.briarproject.briar.api.forum.Forum;
|
import org.briarproject.briar.api.forum.Forum;
|
||||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||||
@@ -55,10 +56,10 @@ class ForumControllerImpl extends
|
|||||||
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
||||||
@CryptoExecutor Executor cryptoExecutor,
|
@CryptoExecutor Executor cryptoExecutor,
|
||||||
ForumManager forumManager, ForumSharingManager forumSharingManager,
|
ForumManager forumManager, ForumSharingManager forumSharingManager,
|
||||||
EventBus eventBus, Clock clock,
|
EventBus eventBus, Clock clock, MessageTracker messageTracker,
|
||||||
AndroidNotificationManager notificationManager) {
|
AndroidNotificationManager notificationManager) {
|
||||||
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
|
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
|
||||||
eventBus, clock, notificationManager);
|
eventBus, clock, notificationManager, messageTracker);
|
||||||
this.forumManager = forumManager;
|
this.forumManager = forumManager;
|
||||||
this.forumSharingManager = forumSharingManager;
|
this.forumSharingManager = forumSharingManager;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -119,8 +119,6 @@ public class ForumListFragment extends BaseEventFragment implements
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
notificationManager.blockAllForumPostNotifications();
|
|
||||||
notificationManager.clearAllForumPostNotifications();
|
|
||||||
loadForums();
|
loadForums();
|
||||||
loadAvailableForums();
|
loadAvailableForums();
|
||||||
list.startPeriodicUpdate();
|
list.startPeriodicUpdate();
|
||||||
@@ -129,7 +127,6 @@ public class ForumListFragment extends BaseEventFragment implements
|
|||||||
@Override
|
@Override
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
super.onStop();
|
super.onStop();
|
||||||
notificationManager.unblockAllForumPostNotifications();
|
|
||||||
adapter.clear();
|
adapter.clear();
|
||||||
list.showProgressBar();
|
list.showProgressBar();
|
||||||
list.stopPeriodicUpdate();
|
list.stopPeriodicUpdate();
|
||||||
|
|||||||
@@ -1,66 +0,0 @@
|
|||||||
package org.briarproject.briar.android.fragment;
|
|
||||||
|
|
||||||
import android.app.Dialog;
|
|
||||||
import android.content.DialogInterface;
|
|
||||||
import android.os.Bundle;
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.v4.app.DialogFragment;
|
|
||||||
import android.support.v7.app.AlertDialog;
|
|
||||||
import android.text.TextUtils;
|
|
||||||
import android.view.LayoutInflater;
|
|
||||||
import android.view.View;
|
|
||||||
import android.widget.CheckBox;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|
||||||
import org.briarproject.briar.R;
|
|
||||||
import org.briarproject.briar.android.activity.BaseActivity;
|
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
|
||||||
@ParametersNotNullByDefault
|
|
||||||
public class SFDialogFragment extends DialogFragment {
|
|
||||||
|
|
||||||
public static SFDialogFragment newInstance(ArrayList<String> apps) {
|
|
||||||
SFDialogFragment frag = new SFDialogFragment();
|
|
||||||
Bundle args = new Bundle();
|
|
||||||
args.putStringArrayList("apps", apps);
|
|
||||||
frag.setArguments(args);
|
|
||||||
return frag;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
|
||||||
AlertDialog.Builder builder =
|
|
||||||
new AlertDialog.Builder(
|
|
||||||
getActivity(),
|
|
||||||
R.style.BriarDialogThemeNoFilter);
|
|
||||||
builder.setTitle(R.string.screen_filter_title);
|
|
||||||
LayoutInflater li = getActivity().getLayoutInflater();
|
|
||||||
//Pass null here because it's an AlertDialog
|
|
||||||
View v =
|
|
||||||
li.inflate(R.layout.alert_dialog_checkbox, null,
|
|
||||||
false);
|
|
||||||
TextView t = (TextView) v.findViewById(R.id.alert_dialog_text);
|
|
||||||
final ArrayList<String> apps =
|
|
||||||
getArguments().getStringArrayList("apps");
|
|
||||||
t.setText(getString(R.string.screen_filter_body, TextUtils
|
|
||||||
.join("\n", apps)));
|
|
||||||
final CheckBox cb =
|
|
||||||
(CheckBox) v.findViewById(
|
|
||||||
R.id.checkBox_screen_filter_reminder);
|
|
||||||
builder.setNeutralButton(R.string.continue_button,
|
|
||||||
new DialogInterface.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(DialogInterface dialog,
|
|
||||||
int which) {
|
|
||||||
((BaseActivity) getActivity())
|
|
||||||
.rememberShownApps(apps, cb.isChecked());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
builder.setView(v);
|
|
||||||
return builder.create();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package org.briarproject.briar.android.fragment;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.DialogInterface;
|
||||||
|
import android.os.Bundle;
|
||||||
|
import android.support.v4.app.DialogFragment;
|
||||||
|
import android.support.v7.app.AlertDialog;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class ScreenFilterDialogFragment extends DialogFragment {
|
||||||
|
|
||||||
|
public static ScreenFilterDialogFragment newInstance(
|
||||||
|
ArrayList<String> apps) {
|
||||||
|
ScreenFilterDialogFragment frag = new ScreenFilterDialogFragment();
|
||||||
|
Bundle args = new Bundle();
|
||||||
|
args.putStringArrayList("apps", apps);
|
||||||
|
frag.setArguments(args);
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||||
|
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity(),
|
||||||
|
R.style.BriarDialogThemeNoFilter);
|
||||||
|
builder.setTitle(R.string.screen_filter_title);
|
||||||
|
ArrayList<String> apps = getArguments().getStringArrayList("apps");
|
||||||
|
builder.setMessage(getString(R.string.screen_filter_body,
|
||||||
|
TextUtils.join("\n", apps)));
|
||||||
|
builder.setNeutralButton(R.string.continue_button,
|
||||||
|
new DialogInterface.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(DialogInterface dialog, int which) {
|
||||||
|
dialog.dismiss();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return builder.create();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.fragment;
|
package org.briarproject.briar.android.fragment;
|
||||||
|
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@@ -9,6 +8,8 @@ import android.view.ViewGroup;
|
|||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
public class SignOutFragment extends BaseFragment {
|
public class SignOutFragment extends BaseFragment {
|
||||||
|
|
||||||
private static final String TAG = SignOutFragment.class.getName();
|
private static final String TAG = SignOutFragment.class.getName();
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ import im.delight.android.identicons.IdenticonDrawable;
|
|||||||
|
|
||||||
import static android.app.Activity.RESULT_OK;
|
import static android.app.Activity.RESULT_OK;
|
||||||
import static android.view.View.GONE;
|
import static android.view.View.GONE;
|
||||||
import static android.view.View.VISIBLE;
|
|
||||||
import static android.widget.Toast.LENGTH_SHORT;
|
import static android.widget.Toast.LENGTH_SHORT;
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
|
import static org.briarproject.briar.api.introduction.IntroductionConstants.MAX_INTRODUCTION_MESSAGE_LENGTH;
|
||||||
@@ -94,7 +93,6 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
View v = inflater.inflate(R.layout.introduction_message, container,
|
View v = inflater.inflate(R.layout.introduction_message, container,
|
||||||
false);
|
false);
|
||||||
ui = new ViewHolder(v);
|
ui = new ViewHolder(v);
|
||||||
ui.text.setVisibility(GONE);
|
|
||||||
ui.message.setSendButtonEnabled(false);
|
ui.message.setSendButtonEnabled(false);
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
@@ -156,17 +154,11 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
ui.contactName1.setText(c1.getAuthor().getName());
|
ui.contactName1.setText(c1.getAuthor().getName());
|
||||||
ui.contactName2.setText(c2.getAuthor().getName());
|
ui.contactName2.setText(c2.getAuthor().getName());
|
||||||
|
|
||||||
// set introduction text
|
|
||||||
ui.text.setText(String.format(
|
|
||||||
getString(R.string.introduction_message_text),
|
|
||||||
c1.getAuthor().getName(), c2.getAuthor().getName()));
|
|
||||||
|
|
||||||
// set button action
|
// set button action
|
||||||
ui.message.setListener(IntroductionMessageFragment.this);
|
ui.message.setListener(IntroductionMessageFragment.this);
|
||||||
|
|
||||||
// hide progress bar and show views
|
// hide progress bar and show views
|
||||||
ui.progressBar.setVisibility(GONE);
|
ui.progressBar.setVisibility(GONE);
|
||||||
ui.text.setVisibility(VISIBLE);
|
|
||||||
ui.message.setSendButtonEnabled(true);
|
ui.message.setSendButtonEnabled(true);
|
||||||
ui.message.showSoftKeyboard();
|
ui.message.showSoftKeyboard();
|
||||||
}
|
}
|
||||||
@@ -234,7 +226,6 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
private final ProgressBar progressBar;
|
private final ProgressBar progressBar;
|
||||||
private final CircleImageView avatar1, avatar2;
|
private final CircleImageView avatar1, avatar2;
|
||||||
private final TextView contactName1, contactName2;
|
private final TextView contactName1, contactName2;
|
||||||
private final TextView text;
|
|
||||||
private final TextInputView message;
|
private final TextInputView message;
|
||||||
|
|
||||||
private ViewHolder(View v) {
|
private ViewHolder(View v) {
|
||||||
@@ -243,7 +234,6 @@ public class IntroductionMessageFragment extends BaseFragment
|
|||||||
avatar2 = (CircleImageView) v.findViewById(R.id.avatarContact2);
|
avatar2 = (CircleImageView) v.findViewById(R.id.avatarContact2);
|
||||||
contactName1 = (TextView) v.findViewById(R.id.nameContact1);
|
contactName1 = (TextView) v.findViewById(R.id.nameContact1);
|
||||||
contactName2 = (TextView) v.findViewById(R.id.nameContact2);
|
contactName2 = (TextView) v.findViewById(R.id.nameContact2);
|
||||||
text = (TextView) v.findViewById(R.id.introductionText);
|
|
||||||
message = (TextInputView) v
|
message = (TextInputView) v
|
||||||
.findViewById(R.id.introductionMessageView);
|
.findViewById(R.id.introductionMessageView);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import org.briarproject.briar.android.activity.ActivityComponent;
|
|||||||
import org.briarproject.briar.android.activity.BriarActivity;
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.blog.FeedFragment;
|
import org.briarproject.briar.android.blog.FeedFragment;
|
||||||
import org.briarproject.briar.android.contact.ContactListFragment;
|
import org.briarproject.briar.android.contact.ContactListFragment;
|
||||||
|
import org.briarproject.briar.android.controller.handler.UiResultHandler;
|
||||||
import org.briarproject.briar.android.forum.ForumListFragment;
|
import org.briarproject.briar.android.forum.ForumListFragment;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||||
@@ -45,6 +46,9 @@ import javax.inject.Inject;
|
|||||||
import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
import static android.support.v4.app.FragmentManager.POP_BACK_STACK_INCLUSIVE;
|
||||||
import static android.support.v4.view.GravityCompat.START;
|
import static android.support.v4.view.GravityCompat.START;
|
||||||
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
import static android.support.v4.widget.DrawerLayout.LOCK_MODE_LOCKED_CLOSED;
|
||||||
|
import static android.view.View.GONE;
|
||||||
|
import static android.view.View.VISIBLE;
|
||||||
|
import static org.briarproject.briar.android.util.UiUtils.getDaysUntilExpiry;
|
||||||
|
|
||||||
public class NavDrawerActivity extends BriarActivity implements
|
public class NavDrawerActivity extends BriarActivity implements
|
||||||
BaseFragmentListener, TransportStateListener,
|
BaseFragmentListener, TransportStateListener,
|
||||||
@@ -128,6 +132,12 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
updateTransports();
|
updateTransports();
|
||||||
|
controller.showExpiryWarning(new UiResultHandler<Boolean>(this) {
|
||||||
|
@Override
|
||||||
|
public void onResultUi(Boolean showWarning) {
|
||||||
|
if (showWarning) showExpiryWarning();
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void exitIfStartupFailed(Intent intent) {
|
private void exitIfStartupFailed(Intent intent) {
|
||||||
@@ -254,6 +264,34 @@ public class NavDrawerActivity extends BriarActivity implements
|
|||||||
// Do nothing for now
|
// Do nothing for now
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
private void showExpiryWarning() {
|
||||||
|
int daysUntilExpiry = getDaysUntilExpiry();
|
||||||
|
if (daysUntilExpiry < 0) signOut();
|
||||||
|
|
||||||
|
// show expiry warning text
|
||||||
|
final ViewGroup
|
||||||
|
expiryWarning = (ViewGroup) findViewById(R.id.expiryWarning);
|
||||||
|
TextView expiryWarningText =
|
||||||
|
(TextView) expiryWarning.findViewById(R.id.expiryWarningText);
|
||||||
|
expiryWarningText.setText(getResources()
|
||||||
|
.getQuantityString(R.plurals.expiry_warning, daysUntilExpiry,
|
||||||
|
daysUntilExpiry));
|
||||||
|
|
||||||
|
// make close button functional
|
||||||
|
ImageView expiryWarningClose =
|
||||||
|
(ImageView) expiryWarning.findViewById(R.id.expiryWarningClose);
|
||||||
|
expiryWarningClose.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
controller.expiryWarningDismissed();
|
||||||
|
expiryWarning.setVisibility(GONE);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expiryWarning.setVisibility(VISIBLE);
|
||||||
|
}
|
||||||
|
|
||||||
private void initializeTransports(final LayoutInflater inflater) {
|
private void initializeTransports(final LayoutInflater inflater) {
|
||||||
transports = new ArrayList<>(3);
|
transports = new ArrayList<>(3);
|
||||||
|
|
||||||
|
|||||||
@@ -3,10 +3,15 @@ package org.briarproject.briar.android.navdrawer;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
||||||
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
|
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public interface NavDrawerController extends ActivityLifecycleController {
|
public interface NavDrawerController extends ActivityLifecycleController {
|
||||||
|
|
||||||
boolean isTransportRunning(TransportId transportId);
|
boolean isTransportRunning(TransportId transportId);
|
||||||
|
|
||||||
|
void showExpiryWarning(final ResultHandler<Boolean> handler);
|
||||||
|
|
||||||
|
void expiryWarningDismissed();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ package org.briarproject.briar.android.navdrawer;
|
|||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.event.Event;
|
import org.briarproject.bramble.api.event.Event;
|
||||||
import org.briarproject.bramble.api.event.EventBus;
|
import org.briarproject.bramble.api.event.EventBus;
|
||||||
import org.briarproject.bramble.api.event.EventListener;
|
import org.briarproject.bramble.api.event.EventListener;
|
||||||
@@ -14,7 +15,10 @@ import org.briarproject.bramble.api.plugin.PluginManager;
|
|||||||
import org.briarproject.bramble.api.plugin.TransportId;
|
import org.briarproject.bramble.api.plugin.TransportId;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportDisabledEvent;
|
||||||
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
|
import org.briarproject.bramble.api.plugin.event.TransportEnabledEvent;
|
||||||
|
import org.briarproject.bramble.api.settings.Settings;
|
||||||
|
import org.briarproject.bramble.api.settings.SettingsManager;
|
||||||
import org.briarproject.briar.android.controller.DbControllerImpl;
|
import org.briarproject.briar.android.controller.DbControllerImpl;
|
||||||
|
import org.briarproject.briar.android.controller.handler.ResultHandler;
|
||||||
|
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
@@ -22,6 +26,9 @@ import java.util.logging.Logger;
|
|||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.INFO;
|
import static java.util.logging.Level.INFO;
|
||||||
|
import static java.util.logging.Level.WARNING;
|
||||||
|
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
||||||
|
import static org.briarproject.briar.android.settings.SettingsFragment.SETTINGS_NAMESPACE;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
@@ -30,18 +37,21 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
|||||||
|
|
||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(NavDrawerControllerImpl.class.getName());
|
Logger.getLogger(NavDrawerControllerImpl.class.getName());
|
||||||
|
private static final String EXPIRY_DATE_WARNING = "expiryDateWarning";
|
||||||
|
|
||||||
private final PluginManager pluginManager;
|
private final PluginManager pluginManager;
|
||||||
|
private final SettingsManager settingsManager;
|
||||||
private final EventBus eventBus;
|
private final EventBus eventBus;
|
||||||
|
|
||||||
private volatile TransportStateListener listener;
|
private volatile TransportStateListener listener;
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
NavDrawerControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
NavDrawerControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager,
|
LifecycleManager lifecycleManager, PluginManager pluginManager,
|
||||||
PluginManager pluginManager, EventBus eventBus) {
|
SettingsManager settingsManager, EventBus eventBus) {
|
||||||
super(dbExecutor, lifecycleManager);
|
super(dbExecutor, lifecycleManager);
|
||||||
this.pluginManager = pluginManager;
|
this.pluginManager = pluginManager;
|
||||||
|
this.settingsManager = settingsManager;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -92,6 +102,63 @@ public class NavDrawerControllerImpl extends DbControllerImpl
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void showExpiryWarning(final ResultHandler<Boolean> handler) {
|
||||||
|
runOnDbThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Settings settings =
|
||||||
|
settingsManager.getSettings(SETTINGS_NAMESPACE);
|
||||||
|
int warningInt = settings.getInt(EXPIRY_DATE_WARNING, 0);
|
||||||
|
|
||||||
|
if (warningInt == 0) {
|
||||||
|
// we have not warned before
|
||||||
|
handler.onResult(true);
|
||||||
|
} else {
|
||||||
|
long warningLong = warningInt * 1000L;
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long daysSinceLastWarning =
|
||||||
|
(now - warningLong) / 1000 / 60 / 60 / 24;
|
||||||
|
long daysBeforeExpiry =
|
||||||
|
(EXPIRY_DATE - now) / 1000 / 60 / 60 / 24;
|
||||||
|
|
||||||
|
if (daysSinceLastWarning >= 30) {
|
||||||
|
handler.onResult(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (daysBeforeExpiry <= 3 && daysSinceLastWarning > 0) {
|
||||||
|
handler.onResult(true);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
handler.onResult(false);
|
||||||
|
}
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void expiryWarningDismissed() {
|
||||||
|
runOnDbThread(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
Settings settings = new Settings();
|
||||||
|
int date = (int) (System.currentTimeMillis() / 1000L);
|
||||||
|
settings.putInt(EXPIRY_DATE_WARNING, date);
|
||||||
|
settingsManager.mergeSettings(settings, SETTINGS_NAMESPACE);
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isTransportRunning(TransportId transportId) {
|
public boolean isTransportRunning(TransportId transportId) {
|
||||||
Plugin plugin = pluginManager.getPlugin(transportId);
|
Plugin plugin = pluginManager.getPlugin(transportId);
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
|||||||
import org.briarproject.briar.android.privategroup.conversation.GroupController.GroupListener;
|
import org.briarproject.briar.android.privategroup.conversation.GroupController.GroupListener;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
|
import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
|
import org.briarproject.briar.api.client.MessageTracker;
|
||||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMember;
|
import org.briarproject.briar.api.privategroup.GroupMember;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMessage;
|
import org.briarproject.briar.api.privategroup.GroupMessage;
|
||||||
@@ -60,9 +61,10 @@ class GroupControllerImpl extends
|
|||||||
@CryptoExecutor Executor cryptoExecutor,
|
@CryptoExecutor Executor cryptoExecutor,
|
||||||
PrivateGroupManager privateGroupManager,
|
PrivateGroupManager privateGroupManager,
|
||||||
GroupMessageFactory groupMessageFactory, EventBus eventBus,
|
GroupMessageFactory groupMessageFactory, EventBus eventBus,
|
||||||
Clock clock, AndroidNotificationManager notificationManager) {
|
MessageTracker messageTracker, Clock clock,
|
||||||
|
AndroidNotificationManager notificationManager) {
|
||||||
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
|
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
|
||||||
eventBus, clock, notificationManager);
|
eventBus, clock, notificationManager, messageTracker);
|
||||||
this.privateGroupManager = privateGroupManager;
|
this.privateGroupManager = privateGroupManager;
|
||||||
this.groupMessageFactory = groupMessageFactory;
|
this.groupMessageFactory = groupMessageFactory;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.privategroup.conversation;
|
package org.briarproject.briar.android.privategroup.conversation;
|
||||||
|
|
||||||
import android.support.annotation.LayoutRes;
|
import android.support.annotation.LayoutRes;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
@@ -12,6 +11,7 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.threaded.ThreadItem;
|
import org.briarproject.briar.android.threaded.ThreadItem;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
|
|||||||
@@ -1,25 +1,15 @@
|
|||||||
package org.briarproject.briar.android.privategroup.conversation;
|
package org.briarproject.briar.android.privategroup.conversation;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.Button;
|
|
||||||
import android.widget.ImageView;
|
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.privategroup.reveal.RevealContactsActivity;
|
|
||||||
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
|
import org.briarproject.briar.android.threaded.BaseThreadItemViewHolder;
|
||||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
|
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
|
||||||
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
|
|
||||||
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
|
|
||||||
import static org.briarproject.briar.api.privategroup.Visibility.INVISIBLE;
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -27,16 +17,10 @@ class JoinMessageItemViewHolder
|
|||||||
extends BaseThreadItemViewHolder<GroupMessageItem> {
|
extends BaseThreadItemViewHolder<GroupMessageItem> {
|
||||||
|
|
||||||
private final boolean isCreator;
|
private final boolean isCreator;
|
||||||
private final ImageView icon;
|
|
||||||
private final TextView info;
|
|
||||||
private final Button options;
|
|
||||||
|
|
||||||
JoinMessageItemViewHolder(View v, boolean isCreator) {
|
JoinMessageItemViewHolder(View v, boolean isCreator) {
|
||||||
super(v);
|
super(v);
|
||||||
this.isCreator = isCreator;
|
this.isCreator = isCreator;
|
||||||
icon = (ImageView) v.findViewById(R.id.icon);
|
|
||||||
info = (TextView) v.findViewById(R.id.info);
|
|
||||||
options = (Button) v.findViewById(R.id.optionsButton);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -56,9 +40,6 @@ class JoinMessageItemViewHolder
|
|||||||
getContext().getString(R.string.groups_member_joined,
|
getContext().getString(R.string.groups_member_joined,
|
||||||
item.getAuthor().getName()));
|
item.getAuthor().getName()));
|
||||||
}
|
}
|
||||||
icon.setVisibility(View.GONE);
|
|
||||||
info.setVisibility(View.GONE);
|
|
||||||
options.setVisibility(View.GONE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void bind(final JoinMessageItem item) {
|
private void bind(final JoinMessageItem item) {
|
||||||
@@ -75,32 +56,6 @@ class JoinMessageItemViewHolder
|
|||||||
item.getAuthor().getName()));
|
item.getAuthor().getName()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item.getStatus() == OURSELVES || item.getStatus() == UNKNOWN) {
|
|
||||||
icon.setVisibility(View.GONE);
|
|
||||||
info.setVisibility(View.GONE);
|
|
||||||
options.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
icon.setVisibility(View.VISIBLE);
|
|
||||||
icon.setImageResource(getVisibilityIcon(item.getVisibility()));
|
|
||||||
info.setVisibility(View.VISIBLE);
|
|
||||||
info.setText(getVisibilityString(getContext(), item.getVisibility(),
|
|
||||||
item.getAuthor().getName()));
|
|
||||||
if (item.getVisibility() == INVISIBLE) {
|
|
||||||
options.setVisibility(View.VISIBLE);
|
|
||||||
options.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Intent i =
|
|
||||||
new Intent(ctx, RevealContactsActivity.class);
|
|
||||||
i.putExtra(GROUP_ID, item.getGroupId().getBytes());
|
|
||||||
ctx.startActivity(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
options.setVisibility(View.GONE);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,58 +0,0 @@
|
|||||||
package org.briarproject.briar.android.privategroup.creation;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
|
||||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|
||||||
import org.briarproject.briar.android.contactselection.ContactSelectorActivity;
|
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
|
||||||
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
|
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
|
|
||||||
import javax.inject.Inject;
|
|
||||||
|
|
||||||
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
|
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
|
||||||
@ParametersNotNullByDefault
|
|
||||||
public abstract class BaseGroupInviteActivity
|
|
||||||
extends ContactSelectorActivity implements MessageFragmentListener {
|
|
||||||
|
|
||||||
@Inject
|
|
||||||
CreateGroupController controller;
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void contactsSelected(Collection<ContactId> contacts) {
|
|
||||||
super.contactsSelected(contacts);
|
|
||||||
|
|
||||||
showNextFragment(new CreateGroupMessageFragment());
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public boolean onButtonClick(String message) {
|
|
||||||
if (groupId == null)
|
|
||||||
throw new IllegalStateException("GroupId was not initialized");
|
|
||||||
controller.sendInvitation(groupId, contacts, message,
|
|
||||||
new UiResultExceptionHandler<Void, DbException>(this) {
|
|
||||||
@Override
|
|
||||||
public void onResultUi(Void result) {
|
|
||||||
setResult(RESULT_OK);
|
|
||||||
supportFinishAfterTransition();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onExceptionUi(DbException exception) {
|
|
||||||
setResult(RESULT_CANCELED);
|
|
||||||
handleDbException(exception);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public int getMaximumMessageLength() {
|
|
||||||
return MAX_GROUP_INVITATION_MSG_LENGTH;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.privategroup.creation;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
@@ -10,14 +9,20 @@ import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
|||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.activity.BriarActivity;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||||
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
@MethodsNotNullByDefault
|
@MethodsNotNullByDefault
|
||||||
@ParametersNotNullByDefault
|
@ParametersNotNullByDefault
|
||||||
public class CreateGroupActivity extends BaseGroupInviteActivity implements
|
public class CreateGroupActivity extends BriarActivity
|
||||||
CreateGroupListener, MessageFragmentListener {
|
implements CreateGroupListener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CreateGroupController controller;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
@@ -28,32 +33,20 @@ public class CreateGroupActivity extends BaseGroupInviteActivity implements
|
|||||||
public void onCreate(@Nullable Bundle bundle) {
|
public void onCreate(@Nullable Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
|
setContentView(R.layout.activity_fragment_container);
|
||||||
|
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
showInitialFragment(new CreateGroupFragment());
|
showInitialFragment(new CreateGroupFragment());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onBackPressed() {
|
|
||||||
if (getSupportFragmentManager().getBackStackEntryCount() == 1) {
|
|
||||||
// At this point, the group had been created already,
|
|
||||||
// so don't allow to create it again.
|
|
||||||
openNewGroup();
|
|
||||||
overridePendingTransition(R.anim.screen_old_in,
|
|
||||||
R.anim.screen_new_out);
|
|
||||||
} else {
|
|
||||||
super.onBackPressed();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onGroupNameChosen(String name) {
|
public void onGroupNameChosen(String name) {
|
||||||
controller.createGroup(name,
|
controller.createGroup(name,
|
||||||
new UiResultExceptionHandler<GroupId, DbException>(this) {
|
new UiResultExceptionHandler<GroupId, DbException>(this) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(GroupId g) {
|
public void onResultUi(GroupId g) {
|
||||||
groupId = g;
|
openNewGroup(g);
|
||||||
switchToContactSelectorFragment(g);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -63,16 +56,10 @@ public class CreateGroupActivity extends BaseGroupInviteActivity implements
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void switchToContactSelectorFragment(GroupId g) {
|
private void openNewGroup(GroupId g) {
|
||||||
showNextFragment(GroupInviteFragment.newInstance(g));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void openNewGroup() {
|
|
||||||
Intent i = new Intent(this, GroupActivity.class);
|
Intent i = new Intent(this, GroupActivity.class);
|
||||||
i.putExtra(GROUP_ID, groupId.getBytes());
|
i.putExtra(GROUP_ID, g.getBytes());
|
||||||
startActivity(i);
|
startActivity(i);
|
||||||
// finish this activity, so we can't come back to it
|
|
||||||
finish();
|
finish();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,12 +4,17 @@ import android.content.Context;
|
|||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
|
import android.view.KeyEvent;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
|
import android.view.View.OnClickListener;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.Button;
|
import android.widget.Button;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
import android.widget.TextView;
|
||||||
|
import android.widget.TextView.OnEditorActionListener;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.util.StringUtils;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||||
@@ -21,8 +26,9 @@ public class CreateGroupFragment extends BaseFragment {
|
|||||||
public final static String TAG = CreateGroupFragment.class.getName();
|
public final static String TAG = CreateGroupFragment.class.getName();
|
||||||
|
|
||||||
private CreateGroupListener listener;
|
private CreateGroupListener listener;
|
||||||
private EditText name;
|
private EditText nameEntry;
|
||||||
private Button button;
|
private Button createGroupButton;
|
||||||
|
private TextView feedback;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onAttach(Context context) {
|
public void onAttach(Context context) {
|
||||||
@@ -34,32 +40,42 @@ public class CreateGroupFragment extends BaseFragment {
|
|||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
public View onCreateView(LayoutInflater inflater, ViewGroup container,
|
||||||
Bundle savedInstanceState) {
|
Bundle savedInstanceState) {
|
||||||
|
|
||||||
// inflate view
|
|
||||||
View v = inflater.inflate(R.layout.fragment_create_group, container,
|
View v = inflater.inflate(R.layout.fragment_create_group, container,
|
||||||
false);
|
false);
|
||||||
name = (EditText) v.findViewById(R.id.name);
|
nameEntry = (EditText) v.findViewById(R.id.name);
|
||||||
name.addTextChangedListener(new TextWatcher() {
|
nameEntry.addTextChangedListener(new TextWatcher() {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void beforeTextChanged(CharSequence s, int start, int count,
|
public void beforeTextChanged(CharSequence s, int start, int count,
|
||||||
int after) {
|
int after) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onTextChanged(CharSequence s, int start, int before,
|
public void onTextChanged(CharSequence s, int start,
|
||||||
int count) {
|
int lengthBefore, int lengthAfter) {
|
||||||
validateName();
|
enableOrDisableCreateButton();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void afterTextChanged(Editable s) {
|
public void afterTextChanged(Editable s) {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
button = (Button) v.findViewById(R.id.button);
|
nameEntry.setOnEditorActionListener(new OnEditorActionListener() {
|
||||||
button.setOnClickListener(new View.OnClickListener() {
|
@Override
|
||||||
|
public boolean onEditorAction(TextView v, int actionId,
|
||||||
|
KeyEvent e) {
|
||||||
|
createGroup();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
feedback = (TextView) v.findViewById(R.id.feedback);
|
||||||
|
|
||||||
|
createGroupButton = (Button) v.findViewById(R.id.button);
|
||||||
|
createGroupButton.setOnClickListener(new OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
listener.hideSoftKeyboard(name);
|
createGroup();
|
||||||
listener.onGroupNameChosen(name.getText().toString());
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -69,7 +85,7 @@ public class CreateGroupFragment extends BaseFragment {
|
|||||||
@Override
|
@Override
|
||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
listener.showSoftKeyboard(name);
|
listener.showSoftKeyboard(nameEntry);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -82,12 +98,25 @@ public class CreateGroupFragment extends BaseFragment {
|
|||||||
return TAG;
|
return TAG;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void validateName() {
|
private void enableOrDisableCreateButton() {
|
||||||
String name = this.name.getText().toString();
|
if (createGroupButton == null) return; // Not created yet
|
||||||
if (name.length() < 1 || name.length() > MAX_GROUP_NAME_LENGTH)
|
createGroupButton.setEnabled(validateName());
|
||||||
button.setEnabled(false);
|
|
||||||
else if(!button.isEnabled())
|
|
||||||
button.setEnabled(true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean validateName() {
|
||||||
|
String name = nameEntry.getText().toString();
|
||||||
|
int length = StringUtils.toUtf8(name).length;
|
||||||
|
if (length > MAX_GROUP_NAME_LENGTH) {
|
||||||
|
feedback.setText(R.string.name_too_long);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
feedback.setText("");
|
||||||
|
return length > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void createGroup() {
|
||||||
|
if (!validateName()) return;
|
||||||
|
listener.hideSoftKeyboard(nameEntry);
|
||||||
|
listener.onGroupNameChosen(nameEntry.getText().toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import dagger.Module;
|
|||||||
import dagger.Provides;
|
import dagger.Provides;
|
||||||
|
|
||||||
@Module
|
@Module
|
||||||
public class GroupCreateModule {
|
public class CreateGroupModule {
|
||||||
|
|
||||||
@ActivityScope
|
@ActivityScope
|
||||||
@Provides
|
@Provides
|
||||||
@@ -3,25 +3,43 @@ package org.briarproject.briar.android.privategroup.creation;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||||
|
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||||
|
import org.briarproject.briar.android.contactselection.ContactSelectorActivity;
|
||||||
|
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
|
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
|
||||||
|
|
||||||
public class GroupInviteActivity extends BaseGroupInviteActivity
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_INVITATION_MSG_LENGTH;
|
||||||
|
|
||||||
|
@MethodsNotNullByDefault
|
||||||
|
@ParametersNotNullByDefault
|
||||||
|
public class GroupInviteActivity extends ContactSelectorActivity
|
||||||
implements MessageFragmentListener {
|
implements MessageFragmentListener {
|
||||||
|
|
||||||
|
@Inject
|
||||||
|
CreateGroupController controller;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void injectActivity(ActivityComponent component) {
|
public void injectActivity(ActivityComponent component) {
|
||||||
component.inject(this);
|
component.inject(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onCreate(Bundle bundle) {
|
public void onCreate(@Nullable Bundle bundle) {
|
||||||
super.onCreate(bundle);
|
super.onCreate(bundle);
|
||||||
|
|
||||||
Intent i = getIntent();
|
Intent i = getIntent();
|
||||||
byte[] g = i.getByteArrayExtra(GROUP_ID);
|
byte[] g = i.getByteArrayExtra(GROUP_ID);
|
||||||
if (g == null) throw new IllegalStateException("No GroupId in intent.");
|
if (g == null) throw new IllegalStateException("No GroupId in intent");
|
||||||
groupId = new GroupId(g);
|
groupId = new GroupId(g);
|
||||||
|
|
||||||
if (bundle == null) {
|
if (bundle == null) {
|
||||||
@@ -29,4 +47,36 @@ public class GroupInviteActivity extends BaseGroupInviteActivity
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void contactsSelected(Collection<ContactId> contacts) {
|
||||||
|
super.contactsSelected(contacts);
|
||||||
|
|
||||||
|
showNextFragment(new CreateGroupMessageFragment());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onButtonClick(String message) {
|
||||||
|
if (groupId == null)
|
||||||
|
throw new IllegalStateException("GroupId was not initialized");
|
||||||
|
controller.sendInvitation(groupId, contacts, message,
|
||||||
|
new UiResultExceptionHandler<Void, DbException>(this) {
|
||||||
|
@Override
|
||||||
|
public void onResultUi(Void result) {
|
||||||
|
setResult(RESULT_OK);
|
||||||
|
supportFinishAfterTransition();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onExceptionUi(DbException exception) {
|
||||||
|
setResult(RESULT_CANCELED);
|
||||||
|
handleDbException(exception);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getMaximumMessageLength() {
|
||||||
|
return MAX_GROUP_INVITATION_MSG_LENGTH;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -79,15 +79,12 @@ class GroupListControllerImpl extends DbControllerImpl
|
|||||||
throw new IllegalStateException(
|
throw new IllegalStateException(
|
||||||
"GroupListListener needs to be attached");
|
"GroupListListener needs to be attached");
|
||||||
eventBus.addListener(this);
|
eventBus.addListener(this);
|
||||||
notificationManager.blockAllGroupMessageNotifications();
|
|
||||||
notificationManager.clearAllGroupMessageNotifications();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@CallSuper
|
@CallSuper
|
||||||
public void onStop() {
|
public void onStop() {
|
||||||
eventBus.removeListener(this);
|
eventBus.removeListener(this);
|
||||||
notificationManager.unblockAllGroupMessageNotifications();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -36,7 +36,6 @@ class MemberListAdapter extends
|
|||||||
@Override
|
@Override
|
||||||
public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
|
public boolean areContentsTheSame(MemberListItem m1, MemberListItem m2) {
|
||||||
if (m1.isOnline() != m2.isOnline()) return false;
|
if (m1.isOnline() != m2.isOnline()) return false;
|
||||||
if (m1.getVisibility() != m2.getVisibility()) return false;
|
|
||||||
if (m1.getContactId() != m2.getContactId()) return false;
|
if (m1.getContactId() != m2.getContactId()) return false;
|
||||||
if (m1.getStatus() != m2.getStatus()) return false;
|
if (m1.getStatus() != m2.getStatus()) return false;
|
||||||
return true;
|
return true;
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
package org.briarproject.briar.android.privategroup.memberlist;
|
package org.briarproject.briar.android.privategroup.memberlist;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
|
|
||||||
import org.briarproject.bramble.api.contact.ContactId;
|
import org.briarproject.bramble.api.contact.ContactId;
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
import org.briarproject.bramble.api.identity.Author.Status;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.api.privategroup.GroupMember;
|
import org.briarproject.briar.api.privategroup.GroupMember;
|
||||||
import org.briarproject.briar.api.privategroup.Visibility;
|
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.annotation.concurrent.NotThreadSafe;
|
import javax.annotation.concurrent.NotThreadSafe;
|
||||||
|
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
@@ -40,10 +38,6 @@ class MemberListItem {
|
|||||||
return groupMember.getContactId();
|
return groupMember.getContactId();
|
||||||
}
|
}
|
||||||
|
|
||||||
Visibility getVisibility() {
|
|
||||||
return groupMember.getVisibility();
|
|
||||||
}
|
|
||||||
|
|
||||||
boolean isOnline() {
|
boolean isOnline() {
|
||||||
return online;
|
return online;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,9 +11,6 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.view.AuthorView;
|
import org.briarproject.briar.android.view.AuthorView;
|
||||||
|
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.UNKNOWN;
|
|
||||||
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityIcon;
|
|
||||||
import static org.briarproject.briar.android.privategroup.VisibilityHelper.getVisibilityString;
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
@@ -22,16 +19,12 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
|
|||||||
private final AuthorView author;
|
private final AuthorView author;
|
||||||
private final ImageView bulb;
|
private final ImageView bulb;
|
||||||
private final TextView creator;
|
private final TextView creator;
|
||||||
private final ImageView icon;
|
|
||||||
private final TextView info;
|
|
||||||
|
|
||||||
MemberListItemHolder(View v) {
|
MemberListItemHolder(View v) {
|
||||||
super(v);
|
super(v);
|
||||||
author = (AuthorView) v.findViewById(R.id.authorView);
|
author = (AuthorView) v.findViewById(R.id.authorView);
|
||||||
bulb = (ImageView) v.findViewById(R.id.bulbView);
|
bulb = (ImageView) v.findViewById(R.id.bulbView);
|
||||||
creator = (TextView) v.findViewById(R.id.creatorView);
|
creator = (TextView) v.findViewById(R.id.creatorView);
|
||||||
icon = (ImageView) v.findViewById(R.id.icon);
|
|
||||||
info = (TextView) v.findViewById(R.id.info);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void bind(MemberListItem item) {
|
protected void bind(MemberListItem item) {
|
||||||
@@ -64,19 +57,6 @@ class MemberListItemHolder extends RecyclerView.ViewHolder {
|
|||||||
} else {
|
} else {
|
||||||
creator.setVisibility(View.GONE);
|
creator.setVisibility(View.GONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
// visibility information
|
|
||||||
if (item.getStatus() == OURSELVES || item.getStatus() == UNKNOWN) {
|
|
||||||
icon.setVisibility(View.GONE);
|
|
||||||
info.setVisibility(View.GONE);
|
|
||||||
} else {
|
|
||||||
icon.setVisibility(View.VISIBLE);
|
|
||||||
icon.setImageResource(getVisibilityIcon(item.getVisibility()));
|
|
||||||
info.setVisibility(View.VISIBLE);
|
|
||||||
info.setText(
|
|
||||||
getVisibilityString(info.getContext(), item.getVisibility(),
|
|
||||||
item.getMember().getName()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ public class BriarReportPrimer implements ReportPrimer {
|
|||||||
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
|
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
|
||||||
boolean wifiAvailable = wifi != null && wifi.isAvailable();
|
boolean wifiAvailable = wifi != null && wifi.isAvailable();
|
||||||
// Is wifi enabled?
|
// Is wifi enabled?
|
||||||
o = ctx.getSystemService(WIFI_SERVICE);
|
o = ctx.getApplicationContext().getSystemService(WIFI_SERVICE);
|
||||||
WifiManager wm = (WifiManager) o;
|
WifiManager wm = (WifiManager) o;
|
||||||
boolean wifiEnabled = wm != null &&
|
boolean wifiEnabled = wm != null &&
|
||||||
wm.getWifiState() == WIFI_STATE_ENABLED;
|
wm.getWifiState() == WIFI_STATE_ENABLED;
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import android.content.Intent;
|
|||||||
import android.media.Ringtone;
|
import android.media.Ringtone;
|
||||||
import android.media.RingtoneManager;
|
import android.media.RingtoneManager;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.v7.preference.CheckBoxPreference;
|
import android.support.v7.preference.CheckBoxPreference;
|
||||||
import android.support.v7.preference.ListPreference;
|
import android.support.v7.preference.ListPreference;
|
||||||
@@ -54,6 +55,7 @@ import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_RINGT
|
|||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_BLOG;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_FORUM;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_GROUP;
|
||||||
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_LOCK_SCREEN;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_PRIVATE;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_NAME;
|
||||||
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
|
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
|
||||||
@@ -81,6 +83,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
private CheckBoxPreference notifyForumPosts;
|
private CheckBoxPreference notifyForumPosts;
|
||||||
private CheckBoxPreference notifyBlogPosts;
|
private CheckBoxPreference notifyBlogPosts;
|
||||||
private CheckBoxPreference notifyVibration;
|
private CheckBoxPreference notifyVibration;
|
||||||
|
private CheckBoxPreference notifyLockscreen;
|
||||||
|
|
||||||
private Preference notifySound;
|
private Preference notifySound;
|
||||||
|
|
||||||
// Fields that are accessed from background threads must be volatile
|
// Fields that are accessed from background threads must be volatile
|
||||||
@@ -114,6 +118,8 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
"pref_key_notify_blog_posts");
|
"pref_key_notify_blog_posts");
|
||||||
notifyVibration = (CheckBoxPreference) findPreference(
|
notifyVibration = (CheckBoxPreference) findPreference(
|
||||||
"pref_key_notify_vibration");
|
"pref_key_notify_vibration");
|
||||||
|
notifyLockscreen = (CheckBoxPreference) findPreference(
|
||||||
|
"pref_key_notify_lock_screen");
|
||||||
notifySound = findPreference("pref_key_notify_sound");
|
notifySound = findPreference("pref_key_notify_sound");
|
||||||
|
|
||||||
enableBluetooth.setOnPreferenceChangeListener(this);
|
enableBluetooth.setOnPreferenceChangeListener(this);
|
||||||
@@ -123,7 +129,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
notifyForumPosts.setOnPreferenceChangeListener(this);
|
notifyForumPosts.setOnPreferenceChangeListener(this);
|
||||||
notifyBlogPosts.setOnPreferenceChangeListener(this);
|
notifyBlogPosts.setOnPreferenceChangeListener(this);
|
||||||
notifyVibration.setOnPreferenceChangeListener(this);
|
notifyVibration.setOnPreferenceChangeListener(this);
|
||||||
|
if (Build.VERSION.SDK_INT >= 21) {
|
||||||
|
notifyLockscreen.setVisible(true);
|
||||||
|
notifyLockscreen.setOnPreferenceChangeListener(this);
|
||||||
|
}
|
||||||
notifySound.setOnPreferenceClickListener(
|
notifySound.setOnPreferenceClickListener(
|
||||||
new Preference.OnPreferenceClickListener() {
|
new Preference.OnPreferenceClickListener() {
|
||||||
@Override
|
@Override
|
||||||
@@ -234,6 +243,9 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
notifyVibration.setChecked(settings.getBoolean(
|
notifyVibration.setChecked(settings.getBoolean(
|
||||||
PREF_NOTIFY_VIBRATION, true));
|
PREF_NOTIFY_VIBRATION, true));
|
||||||
|
|
||||||
|
notifyLockscreen.setChecked(settings.getBoolean(
|
||||||
|
PREF_NOTIFY_LOCK_SCREEN, false));
|
||||||
|
|
||||||
String text;
|
String text;
|
||||||
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
|
if (settings.getBoolean(PREF_NOTIFY_SOUND, true)) {
|
||||||
String ringtoneName =
|
String ringtoneName =
|
||||||
@@ -290,6 +302,10 @@ public class SettingsFragment extends PreferenceFragmentCompat
|
|||||||
Settings s = new Settings();
|
Settings s = new Settings();
|
||||||
s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) o);
|
s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) o);
|
||||||
storeSettings(s);
|
storeSettings(s);
|
||||||
|
} else if (preference == notifyLockscreen) {
|
||||||
|
Settings s = new Settings();
|
||||||
|
s.putBoolean(PREF_NOTIFY_LOCK_SCREEN, (Boolean) o);
|
||||||
|
storeSettings(s);
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.sharing;
|
|||||||
|
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@@ -25,6 +24,7 @@ import java.util.Collection;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static java.util.logging.Level.WARNING;
|
import static java.util.logging.Level.WARNING;
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
|
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
||||||
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
|
import static org.briarproject.briar.android.TestingConstants.DEFAULT_LOG_LEVEL;
|
||||||
import static org.briarproject.briar.android.TestingConstants.TESTING;
|
import static org.briarproject.briar.android.TestingConstants.TESTING;
|
||||||
|
|
||||||
@@ -30,9 +31,6 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
private static final Logger LOG =
|
private static final Logger LOG =
|
||||||
Logger.getLogger(SplashScreenActivity.class.getName());
|
Logger.getLogger(SplashScreenActivity.class.getName());
|
||||||
|
|
||||||
// This build expires on 1 May 2017
|
|
||||||
private static final long EXPIRY_DATE = 1493593200 * 1000L;
|
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected ConfigController configController;
|
protected ConfigController configController;
|
||||||
@Inject
|
@Inject
|
||||||
@@ -83,10 +81,6 @@ public class SplashScreenActivity extends BaseActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void showNewScreenFilterWarning() {
|
|
||||||
}
|
|
||||||
|
|
||||||
private void enableStrictMode() {
|
private void enableStrictMode() {
|
||||||
if (TESTING) {
|
if (TESTING) {
|
||||||
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
|
ThreadPolicy.Builder threadPolicy = new ThreadPolicy.Builder();
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
package org.briarproject.briar.android.threaded;
|
package org.briarproject.briar.android.threaded;
|
||||||
|
|
||||||
import android.support.annotation.Nullable;
|
import android.os.Handler;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.v7.widget.LinearLayoutManager;
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
import android.support.v7.widget.RecyclerView;
|
import android.support.v7.widget.RecyclerView;
|
||||||
@@ -14,6 +14,8 @@ import org.briarproject.briar.android.util.VersionedAdapter;
|
|||||||
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@@ -26,6 +28,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
||||||
private final ThreadItemListener<I> listener;
|
private final ThreadItemListener<I> listener;
|
||||||
private final LinearLayoutManager layoutManager;
|
private final LinearLayoutManager layoutManager;
|
||||||
|
private final Handler handler = new Handler();
|
||||||
|
|
||||||
private volatile int revision = 0;
|
private volatile int revision = 0;
|
||||||
|
|
||||||
@@ -64,6 +67,17 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
revision++;
|
revision++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void setItemWithIdVisible(MessageId messageId) {
|
||||||
|
int pos = 0;
|
||||||
|
for (I item : items) {
|
||||||
|
if (item.getId().equals(messageId)) {
|
||||||
|
layoutManager.scrollToPosition(pos);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
pos++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public void setItems(Collection<I> items) {
|
public void setItems(Collection<I> items) {
|
||||||
this.items.clear();
|
this.items.clear();
|
||||||
this.items.addAll(items);
|
this.items.addAll(items);
|
||||||
@@ -144,7 +158,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
/**
|
/**
|
||||||
* Returns the position of the first unread item below the current viewport
|
* Returns the position of the first unread item below the current viewport
|
||||||
*/
|
*/
|
||||||
public int getVisibleUnreadPosBottom() {
|
int getVisibleUnreadPosBottom() {
|
||||||
final int positionBottom = layoutManager.findLastVisibleItemPosition();
|
final int positionBottom = layoutManager.findLastVisibleItemPosition();
|
||||||
if (positionBottom == NO_POSITION) return NO_POSITION;
|
if (positionBottom == NO_POSITION) return NO_POSITION;
|
||||||
for (int i = positionBottom + 1; i < items.size(); i++) {
|
for (int i = positionBottom + 1; i < items.size(); i++) {
|
||||||
@@ -156,7 +170,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
|||||||
/**
|
/**
|
||||||
* Returns the position of the first unread item above the current viewport
|
* Returns the position of the first unread item above the current viewport
|
||||||
*/
|
*/
|
||||||
public int getVisibleUnreadPosTop() {
|
int getVisibleUnreadPosTop() {
|
||||||
final int positionTop = layoutManager.findFirstVisibleItemPosition();
|
final int positionTop = layoutManager.findFirstVisibleItemPosition();
|
||||||
int position = NO_POSITION;
|
int position = NO_POSITION;
|
||||||
for (int i = 0; i < items.size(); i++) {
|
for (int i = 0; i < items.size(); i++) {
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
package org.briarproject.briar.android.threaded;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public interface ThreadItemList<I extends ThreadItem> extends List<I> {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
MessageId getFirstVisibleItemId();
|
||||||
|
|
||||||
|
void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId);
|
||||||
|
}
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
package org.briarproject.briar.android.threaded;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
public class ThreadItemListImpl<I extends ThreadItem> extends ArrayList<I>
|
||||||
|
implements ThreadItemList<I> {
|
||||||
|
|
||||||
|
private MessageId bottomVisibleItemId;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MessageId getFirstVisibleItemId() {
|
||||||
|
return bottomVisibleItemId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId) {
|
||||||
|
this.bottomVisibleItemId = bottomVisibleItemId;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.threaded;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.CallSuper;
|
import android.support.annotation.CallSuper;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.StringRes;
|
import android.support.annotation.StringRes;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.design.widget.Snackbar;
|
import android.support.design.widget.Snackbar;
|
||||||
@@ -26,6 +25,7 @@ import org.briarproject.briar.android.controller.SharingController;
|
|||||||
import org.briarproject.briar.android.controller.SharingController.SharingListener;
|
import org.briarproject.briar.android.controller.SharingController.SharingListener;
|
||||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||||
|
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListDataSource;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
||||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||||
import org.briarproject.briar.android.view.TextInputView;
|
import org.briarproject.briar.android.view.TextInputView;
|
||||||
@@ -38,6 +38,7 @@ import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
import javax.inject.Inject;
|
import javax.inject.Inject;
|
||||||
|
|
||||||
import static android.support.design.widget.Snackbar.make;
|
import static android.support.design.widget.Snackbar.make;
|
||||||
@@ -51,7 +52,7 @@ import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCo
|
|||||||
public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader>
|
public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader>
|
||||||
extends BriarActivity
|
extends BriarActivity
|
||||||
implements ThreadListListener<H>, TextInputListener, SharingListener,
|
implements ThreadListListener<H>, TextInputListener, SharingListener,
|
||||||
ThreadItemListener<I> {
|
ThreadItemListener<I>, ThreadListDataSource {
|
||||||
|
|
||||||
protected static final String KEY_REPLY_ID = "replyId";
|
protected static final String KEY_REPLY_ID = "replyId";
|
||||||
|
|
||||||
@@ -68,6 +69,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
private MessageId replyId;
|
private MessageId replyId;
|
||||||
|
|
||||||
protected abstract ThreadListController<G, I, H> getController();
|
protected abstract ThreadListController<G, I, H> getController();
|
||||||
|
|
||||||
@Inject
|
@Inject
|
||||||
protected SharingController sharingController;
|
protected SharingController sharingController;
|
||||||
|
|
||||||
@@ -104,6 +106,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
updateUnreadCount();
|
updateUnreadCount();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onScrollStateChanged(RecyclerView recyclerView,
|
public void onScrollStateChanged(RecyclerView recyclerView,
|
||||||
int newState) {
|
int newState) {
|
||||||
@@ -139,11 +142,22 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
if (replyIdBytes != null) replyId = new MessageId(replyIdBytes);
|
if (replyIdBytes != null) replyId = new MessageId(replyIdBytes);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadItems();
|
|
||||||
sharingController.setSharingListener(this);
|
sharingController.setSharingListener(this);
|
||||||
loadSharingContacts();
|
loadSharingContacts();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@Nullable
|
||||||
|
public MessageId getFirstVisibleMessageId() {
|
||||||
|
if (layoutManager != null && adapter != null) {
|
||||||
|
int position =
|
||||||
|
layoutManager.findFirstVisibleItemPosition();
|
||||||
|
I i = adapter.getItemAt(position);
|
||||||
|
return i == null ? null : i.getId();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
protected abstract A createAdapter(LinearLayoutManager layoutManager);
|
protected abstract A createAdapter(LinearLayoutManager layoutManager);
|
||||||
|
|
||||||
protected void loadNamedGroup() {
|
protected void loadNamedGroup() {
|
||||||
@@ -167,16 +181,16 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
protected void loadItems() {
|
protected void loadItems() {
|
||||||
final int revision = adapter.getRevision();
|
final int revision = adapter.getRevision();
|
||||||
getController().loadItems(
|
getController().loadItems(
|
||||||
new UiResultExceptionHandler<Collection<I>, DbException>(this) {
|
new UiResultExceptionHandler<ThreadItemList<I>, DbException>(
|
||||||
|
this) {
|
||||||
@Override
|
@Override
|
||||||
public void onResultUi(Collection<I> items) {
|
public void onResultUi(ThreadItemList<I> items) {
|
||||||
if (revision == adapter.getRevision()) {
|
if (revision == adapter.getRevision()) {
|
||||||
adapter.incrementRevision();
|
adapter.incrementRevision();
|
||||||
if (items.isEmpty()) {
|
if (items.isEmpty()) {
|
||||||
list.showData();
|
list.showData();
|
||||||
} else {
|
} else {
|
||||||
adapter.setItems(items);
|
initList(items);
|
||||||
list.showData();
|
|
||||||
updateTextInput(replyId);
|
updateTextInput(replyId);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -192,6 +206,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void initList(final ThreadItemList<I> items) {
|
||||||
|
adapter.setItems(items);
|
||||||
|
MessageId messageId = items.getFirstVisibleItemId();
|
||||||
|
if (messageId != null)
|
||||||
|
adapter.setItemWithIdVisible(messageId);
|
||||||
|
updateUnreadCount();
|
||||||
|
list.showData();
|
||||||
|
}
|
||||||
|
|
||||||
protected void loadSharingContacts() {
|
protected void loadSharingContacts() {
|
||||||
getController().loadSharingContacts(
|
getController().loadSharingContacts(
|
||||||
new UiResultExceptionHandler<Collection<ContactId>, DbException>(
|
new UiResultExceptionHandler<Collection<ContactId>, DbException>(
|
||||||
@@ -215,6 +238,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
|||||||
public void onStart() {
|
public void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
sharingController.onStart();
|
sharingController.onStart();
|
||||||
|
loadItems();
|
||||||
list.startPeriodicUpdate();
|
list.startPeriodicUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.ContactId;
|
|||||||
import org.briarproject.bramble.api.db.DbException;
|
import org.briarproject.bramble.api.db.DbException;
|
||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
import org.briarproject.bramble.api.sync.GroupId;
|
||||||
|
import org.briarproject.bramble.api.sync.MessageId;
|
||||||
import org.briarproject.briar.android.DestroyableContext;
|
import org.briarproject.briar.android.DestroyableContext;
|
||||||
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
||||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||||
@@ -30,7 +31,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
|||||||
|
|
||||||
void loadItem(H header, ResultExceptionHandler<I, DbException> handler);
|
void loadItem(H header, ResultExceptionHandler<I, DbException> handler);
|
||||||
|
|
||||||
void loadItems(ResultExceptionHandler<Collection<I>, DbException> handler);
|
void loadItems(ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
|
||||||
|
|
||||||
void markItemRead(I item);
|
void markItemRead(I item);
|
||||||
|
|
||||||
@@ -41,7 +42,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
|||||||
|
|
||||||
void deleteNamedGroup(ExceptionHandler<DbException> handler);
|
void deleteNamedGroup(ExceptionHandler<DbException> handler);
|
||||||
|
|
||||||
interface ThreadListListener<H> extends DestroyableContext {
|
interface ThreadListListener<H> extends ThreadListDataSource {
|
||||||
@UiThread
|
@UiThread
|
||||||
void onHeaderReceived(H header);
|
void onHeaderReceived(H header);
|
||||||
|
|
||||||
@@ -52,4 +53,10 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
|||||||
void onInvitationAccepted(ContactId c);
|
void onInvitationAccepted(ContactId c);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
interface ThreadListDataSource extends DestroyableContext {
|
||||||
|
|
||||||
|
@UiThread @Nullable
|
||||||
|
MessageId getFirstVisibleMessageId();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,14 +22,13 @@ import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
|||||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||||
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
||||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||||
|
import org.briarproject.briar.api.client.MessageTracker;
|
||||||
import org.briarproject.briar.api.client.NamedGroup;
|
import org.briarproject.briar.api.client.NamedGroup;
|
||||||
import org.briarproject.briar.api.client.PostHeader;
|
import org.briarproject.briar.api.client.PostHeader;
|
||||||
import org.briarproject.briar.api.client.ThreadedMessage;
|
import org.briarproject.briar.api.client.ThreadedMessage;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.concurrent.ConcurrentHashMap;
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
import java.util.concurrent.Executor;
|
import java.util.concurrent.Executor;
|
||||||
@@ -55,18 +54,21 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
protected final AndroidNotificationManager notificationManager;
|
protected final AndroidNotificationManager notificationManager;
|
||||||
protected final Executor cryptoExecutor;
|
protected final Executor cryptoExecutor;
|
||||||
protected final Clock clock;
|
protected final Clock clock;
|
||||||
|
private final MessageTracker messageTracker;
|
||||||
protected volatile L listener;
|
protected volatile L listener;
|
||||||
|
|
||||||
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||||
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
||||||
@CryptoExecutor Executor cryptoExecutor, EventBus eventBus,
|
@CryptoExecutor Executor cryptoExecutor, EventBus eventBus,
|
||||||
Clock clock, AndroidNotificationManager notificationManager) {
|
Clock clock, AndroidNotificationManager notificationManager,
|
||||||
|
MessageTracker messageTracker) {
|
||||||
super(dbExecutor, lifecycleManager);
|
super(dbExecutor, lifecycleManager);
|
||||||
this.identityManager = identityManager;
|
this.identityManager = identityManager;
|
||||||
this.cryptoExecutor = cryptoExecutor;
|
this.cryptoExecutor = cryptoExecutor;
|
||||||
this.notificationManager = notificationManager;
|
this.notificationManager = notificationManager;
|
||||||
this.clock = clock;
|
this.clock = clock;
|
||||||
this.eventBus = eventBus;
|
this.eventBus = eventBus;
|
||||||
|
this.messageTracker = messageTracker;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -97,6 +99,21 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onActivityDestroy() {
|
public void onActivityDestroy() {
|
||||||
|
final MessageId messageId = listener.getFirstVisibleMessageId();
|
||||||
|
if (messageId != null) {
|
||||||
|
dbExecutor.execute(new Runnable() {
|
||||||
|
@Override
|
||||||
|
public void run() {
|
||||||
|
try {
|
||||||
|
messageTracker
|
||||||
|
.storeMessageId(groupId, messageId);
|
||||||
|
} catch (DbException e) {
|
||||||
|
if (LOG.isLoggable(WARNING))
|
||||||
|
LOG.log(WARNING, e.toString(), e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@CallSuper
|
@CallSuper
|
||||||
@@ -144,7 +161,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void loadItems(
|
public void loadItems(
|
||||||
final ResultExceptionHandler<Collection<I>, DbException> handler) {
|
final ResultExceptionHandler<ThreadItemList<I>, DbException> handler) {
|
||||||
checkGroupId();
|
checkGroupId();
|
||||||
runOnDbThread(new Runnable() {
|
runOnDbThread(new Runnable() {
|
||||||
@Override
|
@Override
|
||||||
@@ -293,11 +310,16 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
|||||||
@DatabaseExecutor
|
@DatabaseExecutor
|
||||||
protected abstract void deleteNamedGroup(G groupItem) throws DbException;
|
protected abstract void deleteNamedGroup(G groupItem) throws DbException;
|
||||||
|
|
||||||
private List<I> buildItems(Collection<H> headers) {
|
private ThreadItemList<I> buildItems(Collection<H> headers)
|
||||||
List<I> items = new ArrayList<>();
|
throws DbException {
|
||||||
|
ThreadItemList<I> items = new ThreadItemListImpl<>();
|
||||||
for (H h : headers) {
|
for (H h : headers) {
|
||||||
items.add(buildItem(h, bodyCache.get(h.getId())));
|
items.add(buildItem(h, bodyCache.get(h.getId())));
|
||||||
}
|
}
|
||||||
|
MessageId msgId = messageTracker.loadStoredMessageId(groupId);
|
||||||
|
if (LOG.isLoggable(INFO))
|
||||||
|
LOG.info("Loaded last top visible message id " + msgId);
|
||||||
|
items.setFirstVisibleId(msgId);
|
||||||
return items;
|
return items;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.util;
|
package org.briarproject.briar.android.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.design.widget.TextInputLayout;
|
import android.support.design.widget.TextInputLayout;
|
||||||
import android.support.v4.app.FragmentManager;
|
import android.support.v4.app.FragmentManager;
|
||||||
import android.support.v4.content.ContextCompat;
|
import android.support.v4.content.ContextCompat;
|
||||||
@@ -23,6 +22,8 @@ import org.briarproject.briar.R;
|
|||||||
import org.briarproject.briar.android.view.ArticleMovementMethod;
|
import org.briarproject.briar.android.view.ArticleMovementMethod;
|
||||||
import org.briarproject.briar.android.widget.LinkDialogFragment;
|
import org.briarproject.briar.android.widget.LinkDialogFragment;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.text.format.DateUtils.DAY_IN_MILLIS;
|
import static android.text.format.DateUtils.DAY_IN_MILLIS;
|
||||||
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
|
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
|
||||||
import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
|
import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
|
||||||
@@ -30,6 +31,7 @@ import static android.text.format.DateUtils.FORMAT_ABBREV_TIME;
|
|||||||
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
|
||||||
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
|
||||||
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
|
||||||
|
import static org.briarproject.briar.android.BriarApplication.EXPIRY_DATE;
|
||||||
|
|
||||||
public class UiUtils {
|
public class UiUtils {
|
||||||
|
|
||||||
@@ -63,6 +65,12 @@ public class UiUtils {
|
|||||||
MIN_DATE_RESOLUTION, flags).toString();
|
MIN_DATE_RESOLUTION, flags).toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getDaysUntilExpiry() {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
long daysBeforeExpiry = (EXPIRY_DATE - now) / 1000 / 60 / 60 / 24;
|
||||||
|
return (int) daysBeforeExpiry;
|
||||||
|
}
|
||||||
|
|
||||||
public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) {
|
public static SpannableStringBuilder getTeaser(Context ctx, Spanned body) {
|
||||||
if (body.length() < TEASER_LENGTH)
|
if (body.length() < TEASER_LENGTH)
|
||||||
throw new IllegalArgumentException(
|
throw new IllegalArgumentException(
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package org.briarproject.briar.android.view;
|
package org.briarproject.briar.android.view;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Typeface;
|
import android.graphics.Typeface;
|
||||||
import android.support.annotation.DimenRes;
|
import android.support.annotation.DimenRes;
|
||||||
@@ -16,9 +15,7 @@ import android.widget.TextView;
|
|||||||
|
|
||||||
import org.briarproject.bramble.api.identity.Author;
|
import org.briarproject.bramble.api.identity.Author;
|
||||||
import org.briarproject.bramble.api.identity.Author.Status;
|
import org.briarproject.bramble.api.identity.Author.Status;
|
||||||
import org.briarproject.bramble.api.sync.GroupId;
|
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
import org.briarproject.briar.android.blog.BlogActivity;
|
|
||||||
import org.briarproject.briar.android.util.UiUtils;
|
import org.briarproject.briar.android.util.UiUtils;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
@@ -27,12 +24,10 @@ import de.hdodenhof.circleimageview.CircleImageView;
|
|||||||
import im.delight.android.identicons.IdenticonDrawable;
|
import im.delight.android.identicons.IdenticonDrawable;
|
||||||
|
|
||||||
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
|
||||||
import static android.graphics.Typeface.BOLD;
|
import static android.graphics.Typeface.BOLD;
|
||||||
import static android.util.TypedValue.COMPLEX_UNIT_PX;
|
import static android.util.TypedValue.COMPLEX_UNIT_PX;
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.NONE;
|
import static org.briarproject.bramble.api.identity.Author.Status.NONE;
|
||||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
||||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class AuthorView extends RelativeLayout {
|
public class AuthorView extends RelativeLayout {
|
||||||
@@ -110,24 +105,16 @@ public class AuthorView extends RelativeLayout {
|
|||||||
requestLayout();
|
requestLayout();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setBlogLink(final GroupId groupId) {
|
public void setAuthorClickable(OnClickListener listener) {
|
||||||
setClickable(true);
|
setClickable(true);
|
||||||
TypedValue outValue = new TypedValue();
|
TypedValue outValue = new TypedValue();
|
||||||
getContext().getTheme().resolveAttribute(
|
getContext().getTheme().resolveAttribute(
|
||||||
android.R.attr.selectableItemBackground, outValue, true);
|
android.R.attr.selectableItemBackground, outValue, true);
|
||||||
setBackgroundResource(outValue.resourceId);
|
setBackgroundResource(outValue.resourceId);
|
||||||
setOnClickListener(new OnClickListener() {
|
setOnClickListener(listener);
|
||||||
@Override
|
|
||||||
public void onClick(View v) {
|
|
||||||
Intent i = new Intent(getContext(), BlogActivity.class);
|
|
||||||
i.putExtra(GROUP_ID, groupId.getBytes());
|
|
||||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
|
||||||
getContext().startActivity(i);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void unsetBlogLink() {
|
public void setAuthorNotClickable() {
|
||||||
setClickable(false);
|
setClickable(false);
|
||||||
setBackgroundResource(android.R.color.transparent);
|
setBackgroundResource(android.R.color.transparent);
|
||||||
setOnClickListener(null);
|
setOnClickListener(null);
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.view;
|
|||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.support.annotation.Nullable;
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.support.design.widget.FloatingActionButton;
|
import android.support.design.widget.FloatingActionButton;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
@@ -13,6 +12,8 @@ import android.widget.TextView;
|
|||||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
import org.briarproject.briar.R;
|
import org.briarproject.briar.R;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
@NotNullByDefault
|
@NotNullByDefault
|
||||||
public class UnreadMessageButton extends FrameLayout {
|
public class UnreadMessageButton extends FrameLayout {
|
||||||
@@ -36,8 +37,7 @@ public class UnreadMessageButton extends FrameLayout {
|
|||||||
|
|
||||||
LayoutInflater inflater = (LayoutInflater) context
|
LayoutInflater inflater = (LayoutInflater) context
|
||||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||||
inflater
|
inflater.inflate(R.layout.unread_message_button, this, true);
|
||||||
.inflate(R.layout.unread_message_button, this, true);
|
|
||||||
|
|
||||||
fab = (FloatingActionButton) findViewById(R.id.fab);
|
fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||||
unread = (TextView) findViewById(R.id.unreadCountView);
|
unread = (TextView) findViewById(R.id.unreadCountView);
|
||||||
@@ -64,15 +64,11 @@ public class UnreadMessageButton extends FrameLayout {
|
|||||||
|
|
||||||
public void setUnreadCount(int count) {
|
public void setUnreadCount(int count) {
|
||||||
if (count == 0) {
|
if (count == 0) {
|
||||||
fab.setVisibility(GONE);
|
setVisibility(INVISIBLE);
|
||||||
// fab.hide();
|
|
||||||
unread.setVisibility(GONE);
|
|
||||||
} else {
|
} else {
|
||||||
// FIXME: Use animations when upgrading to support library 24.2.0
|
// FIXME: Use animations when upgrading to support library 24.2.0
|
||||||
// https://code.google.com/p/android/issues/detail?id=216469
|
// https://code.google.com/p/android/issues/detail?id=216469
|
||||||
fab.setVisibility(VISIBLE);
|
setVisibility(VISIBLE);
|
||||||
// if (!fab.isShown()) fab.show();
|
|
||||||
unread.setVisibility(VISIBLE);
|
|
||||||
unread.setText(String.valueOf(count));
|
unread.setText(String.valueOf(count));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,51 @@
|
|||||||
|
package org.briarproject.briar.android.widget;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.annotation.AttrRes;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.MotionEvent;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
import static android.view.MotionEvent.FLAG_WINDOW_IS_OBSCURED;
|
||||||
|
|
||||||
|
@NotNullByDefault
|
||||||
|
public class TapSafeFrameLayout extends FrameLayout {
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private OnTapFilteredListener listener;
|
||||||
|
|
||||||
|
public TapSafeFrameLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
setFilterTouchesWhenObscured(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
setFilterTouchesWhenObscured(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TapSafeFrameLayout(Context context, @Nullable AttributeSet attrs,
|
||||||
|
@AttrRes int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
setFilterTouchesWhenObscured(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnTapFilteredListener(OnTapFilteredListener listener) {
|
||||||
|
this.listener = listener;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onFilterTouchEventForSecurity(MotionEvent e) {
|
||||||
|
boolean filter = (e.getFlags() & FLAG_WINDOW_IS_OBSCURED) != 0;
|
||||||
|
if (filter && listener != null) listener.onTapFiltered();
|
||||||
|
return !filter;
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface OnTapFilteredListener {
|
||||||
|
void onTapFiltered();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,19 +18,14 @@ public interface AndroidNotificationManager {
|
|||||||
String PREF_NOTIFY_RINGTONE_NAME = "notifyRingtoneName";
|
String PREF_NOTIFY_RINGTONE_NAME = "notifyRingtoneName";
|
||||||
String PREF_NOTIFY_RINGTONE_URI = "notifyRingtoneUri";
|
String PREF_NOTIFY_RINGTONE_URI = "notifyRingtoneUri";
|
||||||
String PREF_NOTIFY_VIBRATION = "notifyVibration";
|
String PREF_NOTIFY_VIBRATION = "notifyVibration";
|
||||||
|
String PREF_NOTIFY_LOCK_SCREEN = "notifyLockScreen";
|
||||||
|
|
||||||
void clearContactNotification(ContactId c);
|
void clearContactNotification(ContactId c);
|
||||||
|
|
||||||
void clearAllContactNotifications();
|
|
||||||
|
|
||||||
void clearGroupMessageNotification(GroupId g);
|
void clearGroupMessageNotification(GroupId g);
|
||||||
|
|
||||||
void clearAllGroupMessageNotifications();
|
|
||||||
|
|
||||||
void clearForumPostNotification(GroupId g);
|
void clearForumPostNotification(GroupId g);
|
||||||
|
|
||||||
void clearAllForumPostNotifications();
|
|
||||||
|
|
||||||
void clearBlogPostNotification(GroupId g);
|
void clearBlogPostNotification(GroupId g);
|
||||||
|
|
||||||
void clearAllBlogPostNotifications();
|
void clearAllBlogPostNotifications();
|
||||||
@@ -43,18 +38,6 @@ public interface AndroidNotificationManager {
|
|||||||
|
|
||||||
void unblockNotification(GroupId g);
|
void unblockNotification(GroupId g);
|
||||||
|
|
||||||
void blockAllContactNotifications();
|
|
||||||
|
|
||||||
void unblockAllContactNotifications();
|
|
||||||
|
|
||||||
void blockAllGroupMessageNotifications();
|
|
||||||
|
|
||||||
void unblockAllGroupMessageNotifications();
|
|
||||||
|
|
||||||
void blockAllForumPostNotifications();
|
|
||||||
|
|
||||||
void unblockAllForumPostNotifications();
|
|
||||||
|
|
||||||
void blockAllBlogPostNotifications();
|
void blockAllBlogPostNotifications();
|
||||||
|
|
||||||
void unblockAllBlogPostNotifications();
|
void unblockAllBlogPostNotifications();
|
||||||
|
|||||||
@@ -2,14 +2,10 @@ package org.briarproject.briar.api.android;
|
|||||||
|
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
|
|
||||||
import java.util.Collection;
|
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
public interface ScreenFilterMonitor {
|
public interface ScreenFilterMonitor {
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
Set<String> getApps();
|
Set<String> getApps();
|
||||||
|
|
||||||
@UiThread
|
|
||||||
void storeAppsAsShown(Collection<String> s, boolean persistent);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,29 +1,21 @@
|
|||||||
package org.thoughtcrime.securesms.components.emoji;
|
package org.thoughtcrime.securesms.components.emoji;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.graphics.Paint.FontMetricsInt;
|
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.UiThread;
|
import android.support.annotation.UiThread;
|
||||||
import android.text.TextUtils;
|
import android.support.v7.widget.AppCompatTextView;
|
||||||
import android.util.AttributeSet;
|
import android.util.AttributeSet;
|
||||||
import android.view.ViewConfiguration;
|
import android.view.ViewConfiguration;
|
||||||
import android.widget.TextView;
|
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
||||||
|
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static android.text.TextUtils.TruncateAt.END;
|
|
||||||
import static android.view.View.MeasureSpec.AT_MOST;
|
|
||||||
import static android.view.View.MeasureSpec.EXACTLY;
|
|
||||||
import static android.widget.TextView.BufferType.SPANNABLE;
|
import static android.widget.TextView.BufferType.SPANNABLE;
|
||||||
|
|
||||||
@UiThread
|
@UiThread
|
||||||
public class EmojiTextView extends TextView {
|
public class EmojiTextView extends AppCompatTextView {
|
||||||
|
|
||||||
private CharSequence source;
|
|
||||||
private boolean needsEllipsizing;
|
|
||||||
|
|
||||||
public EmojiTextView(Context context) {
|
public EmojiTextView(Context context) {
|
||||||
this(context, null);
|
this(context, null);
|
||||||
@@ -42,13 +34,9 @@ public class EmojiTextView extends TextView {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setText(@Nullable CharSequence text, BufferType type) {
|
public void setText(@Nullable CharSequence text, BufferType type) {
|
||||||
source = EmojiProvider.getInstance(getContext()).emojify(text, this);
|
CharSequence source =
|
||||||
|
EmojiProvider.getInstance(getContext()).emojify(text, this);
|
||||||
setTextEllipsized(source);
|
super.setText(source, SPANNABLE);
|
||||||
}
|
|
||||||
|
|
||||||
private void setTextEllipsized(final @Nullable CharSequence source) {
|
|
||||||
super.setText(needsEllipsizing ? ellipsize(source) : source, SPANNABLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -57,26 +45,6 @@ public class EmojiTextView extends TextView {
|
|||||||
else super.invalidateDrawable(drawable);
|
else super.invalidateDrawable(drawable);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
|
||||||
final int size = MeasureSpec.getSize(widthMeasureSpec);
|
|
||||||
final int mode = MeasureSpec.getMode(widthMeasureSpec);
|
|
||||||
if (getEllipsize() == END &&
|
|
||||||
!TextUtils.isEmpty(source) &&
|
|
||||||
(mode == AT_MOST || mode == EXACTLY) &&
|
|
||||||
getPaint().breakText(source, 0, source.length() - 1, true, size,
|
|
||||||
null) != source.length()) {
|
|
||||||
needsEllipsizing = true;
|
|
||||||
FontMetricsInt font = getPaint().getFontMetricsInt();
|
|
||||||
int height = Math.abs(font.top - font.bottom);
|
|
||||||
super.onMeasure(MeasureSpec.makeMeasureSpec(size, EXACTLY),
|
|
||||||
MeasureSpec.makeMeasureSpec(height, EXACTLY));
|
|
||||||
} else {
|
|
||||||
needsEllipsizing = false;
|
|
||||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onLayout(boolean changed, int left, int top, int right,
|
protected void onLayout(boolean changed, int left, int top, int right,
|
||||||
int bottom) {
|
int bottom) {
|
||||||
@@ -89,20 +57,6 @@ public class EmojiTextView extends TextView {
|
|||||||
if (size > drawingCacheSize) {
|
if (size > drawingCacheSize) {
|
||||||
setLayerType(LAYER_TYPE_NONE, null);
|
setLayerType(LAYER_TYPE_NONE, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (changed) setTextEllipsized(source);
|
|
||||||
super.onLayout(changed, left, top, right, bottom);
|
super.onLayout(changed, left, top, right, bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
|
||||||
public CharSequence ellipsize(@Nullable CharSequence text) {
|
|
||||||
if (TextUtils.isEmpty(text) || getWidth() == 0 ||
|
|
||||||
getEllipsize() != END) {
|
|
||||||
return text;
|
|
||||||
} else {
|
|
||||||
return TextUtils.ellipsize(text, getPaint(),
|
|
||||||
getWidth() - getPaddingRight() - getPaddingLeft(), END);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
9
briar-android/src/main/res/drawable/ic_close.xml
Normal file
9
briar-android/src/main/res/drawable/ic_close.xml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="18dp"
|
||||||
|
android:height="18dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFFFF"
|
||||||
|
android:pathData="M19,6.41L17.59,5 12,10.59 6.41,5 5,6.41 10.59,12 5,17.59 6.41,19 12,13.41 17.59,19 19,17.59 13.41,12z"/>
|
||||||
|
</vector>
|
||||||
@@ -7,7 +7,7 @@
|
|||||||
|
|
||||||
<path
|
<path
|
||||||
android:fillColor="#ffa500"
|
android:fillColor="#ffa500"
|
||||||
android:pathData="M0,8.88178e-16 L30,8.88178e-16 L30,30 L0,30 L0,8.88178e-16 Z"/>
|
android:pathData="M0,0 L30,0 L30,30 L0,30 L0,0 Z"/>
|
||||||
<path
|
<path
|
||||||
android:fillColor="#ffffff"
|
android:fillColor="#ffffff"
|
||||||
android:pathData="M8.9322,18.0339 C10.6078,18.0339,11.9661,19.3922,11.9661,21.0678
|
android:pathData="M8.9322,18.0339 C10.6078,18.0339,11.9661,19.3922,11.9661,21.0678
|
||||||
|
|||||||
@@ -12,9 +12,9 @@
|
|||||||
android:fillColor="#87c214"
|
android:fillColor="#87c214"
|
||||||
android:pathData="M64.9004,0 C55.2004,0,47.1992,7.99922,47.1992,17.6992 L47.1992,40.1992
|
android:pathData="M64.9004,0 C55.2004,0,47.1992,7.99922,47.1992,17.6992 L47.1992,40.1992
|
||||||
L90.8008,40.1992 L90.8008,17.6992
|
L90.8008,40.1992 L90.8008,17.6992
|
||||||
C90.8008,7.99922,82.8992,-4.73695e-15,73.1992,0 L64.9004,0 Z M161.9,0
|
C90.8008,7.99922,82.8992,0,73.1992,0 L64.9004,0 Z M161.9,0
|
||||||
C152.2,0,144.199,7.99922,144.199,17.6992 L144.199,137.199 L187.801,137.199
|
C152.2,0,144.199,7.99922,144.199,17.6992 L144.199,137.199 L187.801,137.199
|
||||||
L187.801,17.6992 C187.801,7.99922,179.899,-4.73695e-15,170.199,0 L161.9,0 Z
|
L187.801,17.6992 C187.801,7.99922,179.899,0,170.199,0 L161.9,0 Z
|
||||||
M47.1992,97.8008 L47.1992,217.301 C47.1992,227.001,55.1004,235,64.9004,235
|
M47.1992,97.8008 L47.1992,217.301 C47.1992,227.001,55.1004,235,64.9004,235
|
||||||
L73.1992,235 C82.8992,235,90.9004,227.001,90.9004,217.301 L90.9004,97.8008
|
L73.1992,235 C82.8992,235,90.9004,227.001,90.9004,217.301 L90.9004,97.8008
|
||||||
L47.1992,97.8008 Z M144.199,194.801 L144.199,217.301
|
L47.1992,97.8008 Z M144.199,194.801 L144.199,217.301
|
||||||
@@ -25,12 +25,12 @@ C179.899,235,187.9,227.001,187.9,217.301 L187.9,194.801 L144.199,194.801 Z"/>
|
|||||||
android:pathData="M144.2,144.2 L187.9,144.2 L187.9,187.9 L144.2,187.9 L144.2,144.2 Z"/>
|
android:pathData="M144.2,144.2 L187.9,144.2 L187.9,187.9 L144.2,187.9 L144.2,144.2 Z"/>
|
||||||
<path
|
<path
|
||||||
android:fillColor="#95d220"
|
android:fillColor="#95d220"
|
||||||
android:pathData="M17.6992,47.1992 C7.99922,47.1992,2.36848e-15,55.1004,0,64.9004 L0,73.1992
|
android:pathData="M17.6992,47.1992 C7.99922,47.1992,0,55.1004,0,64.9004 L0,73.1992
|
||||||
C0,82.8992,7.89922,90.9004,17.6992,90.9004 L137.199,90.9004 L137.199,47.1992
|
C0,82.8992,7.89922,90.9004,17.6992,90.9004 L137.199,90.9004 L137.199,47.1992
|
||||||
L17.6992,47.1992 Z M194.801,47.1992 L194.801,90.9004 L217.301,90.9004
|
L17.6992,47.1992 Z M194.801,47.1992 L194.801,90.9004 L217.301,90.9004
|
||||||
C227.001,90.9004,235,82.9992,235,73.1992 L235,64.9004
|
C227.001,90.9004,235,82.9992,235,73.1992 L235,64.9004
|
||||||
C235,55.1004,227.001,47.1992,217.301,47.1992 L194.801,47.1992 Z M17.6992,144.199
|
C235,55.1004,227.001,47.1992,217.301,47.1992 L194.801,47.1992 Z M17.6992,144.199
|
||||||
C7.99922,144.199,2.36848e-15,152.1,0,161.9 L0,170.199
|
C7.99922,144.199,0,152.1,0,161.9 L0,170.199
|
||||||
C0,179.899,7.89922,187.9,17.6992,187.9 L40.1992,187.9 L40.1992,144.199
|
C0,179.899,7.89922,187.9,17.6992,187.9 L40.1992,187.9 L40.1992,144.199
|
||||||
L17.6992,144.199 Z M97.8008,144.199 L97.8008,187.9 L217.301,187.9
|
L17.6992,144.199 Z M97.8008,144.199 L97.8008,187.9 L217.301,187.9
|
||||||
C227.001,187.9,235,179.899,235,170.199 L235,161.9
|
C227.001,187.9,235,179.899,235,170.199 L235,161.9
|
||||||
|
|||||||
@@ -1,37 +1,30 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:orientation="vertical"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:gravity="center_horizontal"
|
android:gravity="center_horizontal"
|
||||||
android:padding="20dp" >
|
android:orientation="vertical"
|
||||||
|
android:layout_margin="@dimen/margin_large">
|
||||||
<TextView
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:gravity="center"
|
|
||||||
android:textSize="@dimen/text_size_medium"
|
|
||||||
android:text="@string/choose_forum_name" />
|
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/createForumNameEntry"
|
android:id="@+id/createForumNameEntry"
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:inputType="text|textCapSentences" />
|
android:inputType="text|textCapSentences"
|
||||||
|
android:hint="@string/choose_forum_hint" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:id="@+id/createForumFeedback"
|
android:id="@+id/createForumFeedback"
|
||||||
android:gravity="center"
|
android:gravity="center" />
|
||||||
android:paddingLeft="50dp"
|
|
||||||
android:paddingRight="50dp" />
|
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
style="@style/BriarButton"
|
style="@style/BriarButton"
|
||||||
android:id="@+id/createForumButton"
|
android:id="@+id/createForumButton"
|
||||||
|
android:enabled="false"
|
||||||
android:text="@string/create_forum_button" />
|
android:text="@string/create_forum_button" />
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
|
|||||||
@@ -5,5 +5,5 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:text="@string/expiry_warning"
|
android:text="@string/expiry_date_reached"
|
||||||
android:textSize="@dimen/text_size_large"/>
|
android:textSize="@dimen/text_size_large"/>
|
||||||
@@ -15,6 +15,38 @@
|
|||||||
|
|
||||||
<include layout="@layout/toolbar"/>
|
<include layout="@layout/toolbar"/>
|
||||||
|
|
||||||
|
<RelativeLayout
|
||||||
|
android:id="@+id/expiryWarning"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@color/briar_warning_background"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:padding="@dimen/margin_medium"
|
||||||
|
android:visibility="gone"
|
||||||
|
tools:visibility="visible">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@+id/expiryWarningText"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_toLeftOf="@+id/expiryWarningClose"
|
||||||
|
android:text="@string/expiry_warning"
|
||||||
|
android:textColor="@color/briar_text_primary_inverse"
|
||||||
|
android:textSize="@dimen/text_size_small"/>
|
||||||
|
|
||||||
|
<ImageView
|
||||||
|
android:id="@+id/expiryWarningClose"
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:layout_alignParentRight="true"
|
||||||
|
android:layout_centerInParent="true"
|
||||||
|
android:contentDescription="@string/close"
|
||||||
|
android:scaleType="center"
|
||||||
|
android:src="@drawable/ic_close"
|
||||||
|
android:tint="@color/briar_text_tertiary_inverse"/>
|
||||||
|
|
||||||
|
</RelativeLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<FrameLayout
|
||||||
android:id="@+id/fragmentContainer"
|
android:id="@+id/fragmentContainer"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
android:layout_weight="1"
|
android:layout_weight="1"
|
||||||
android:gravity="top"
|
android:gravity="top"
|
||||||
android:hint="@string/blogs_rss_feeds_import_hint"
|
android:hint="@string/blogs_rss_feeds_import_hint"
|
||||||
android:inputType="textMultiLine|textUri"/>
|
android:inputType="textUri"/>
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/importButton"
|
android:id="@+id/importButton"
|
||||||
|
|||||||
@@ -1,40 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
|
||||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:filterTouchesWhenObscured="false"
|
|
||||||
android:orientation="vertical">
|
|
||||||
|
|
||||||
<ScrollView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_weight="1"
|
|
||||||
android:fadeScrollbars="false"
|
|
||||||
android:paddingLeft="20dp"
|
|
||||||
android:paddingRight="20dp"
|
|
||||||
android:paddingStart="20dp"
|
|
||||||
android:paddingTop="20dp"
|
|
||||||
android:theme="@style/BriarTheme">
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/alert_dialog_text"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:paddingLeft="3dp"
|
|
||||||
android:paddingRight="6dp"
|
|
||||||
android:text="TextView"
|
|
||||||
android:textAppearance="@style/BriarTextBody"
|
|
||||||
android:theme="@+theme/BriarDialogTheme"/>
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<CheckBox
|
|
||||||
android:id="@+id/checkBox_screen_filter_reminder"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginLeft="15dp"
|
|
||||||
android:layout_marginStart="15dp"
|
|
||||||
android:layout_weight="0"
|
|
||||||
android:filterTouchesWhenObscured="false"
|
|
||||||
android:text="@string/checkbox_dont_show_again"
|
|
||||||
android:textAppearance="@style/BriarTextBody"/>
|
|
||||||
</LinearLayout>
|
|
||||||
@@ -3,22 +3,27 @@
|
|||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
|
android:gravity="center_horizontal"
|
||||||
android:orientation="vertical"
|
android:orientation="vertical"
|
||||||
android:padding="@dimen/margin_medium">
|
android:layout_margin="@dimen/margin_large">
|
||||||
|
|
||||||
<EditText
|
<EditText
|
||||||
android:id="@+id/name"
|
android:id="@+id/name"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1"
|
|
||||||
android:gravity="bottom"
|
|
||||||
android:maxLines="1"
|
android:maxLines="1"
|
||||||
android:inputType="text|textCapSentences"
|
android:inputType="text|textCapSentences"
|
||||||
android:hint="@string/groups_create_group_hint"/>
|
android:hint="@string/groups_create_group_hint"/>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/feedback"
|
||||||
|
android:gravity="center" />
|
||||||
|
|
||||||
<Button
|
<Button
|
||||||
android:id="@+id/button"
|
|
||||||
style="@style/BriarButton"
|
style="@style/BriarButton"
|
||||||
|
android:id="@+id/button"
|
||||||
android:enabled="false"
|
android:enabled="false"
|
||||||
android:text="@string/groups_create_group_button"/>
|
android:text="@string/groups_create_group_button"/>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<LinearLayout
|
<ScrollView
|
||||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
@@ -7,25 +7,21 @@
|
|||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<ScrollView
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="0dp"
|
android:layout_height="wrap_content"
|
||||||
android:layout_weight="1">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_margin="@dimen/margin_activity_horizontal"
|
android:layout_margin="@dimen/margin_activity_horizontal"
|
||||||
android:orientation="vertical">
|
android:gravity="center"
|
||||||
|
android:orientation="horizontal">
|
||||||
<RelativeLayout
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content">
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_toLeftOf="@+id/introductionIcon"
|
|
||||||
android:gravity="top|center_horizontal"
|
android:gravity="top|center_horizontal"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
@@ -47,7 +43,7 @@
|
|||||||
android:layout_marginTop="@dimen/margin_small"
|
android:layout_marginTop="@dimen/margin_small"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="@color/briar_text_primary"
|
android:textColor="@color/briar_text_primary"
|
||||||
android:textSize="@dimen/text_size_tiny"
|
android:textSize="@dimen/text_size_small"
|
||||||
tools:text="Contact 1"/>
|
tools:text="Contact 1"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
@@ -56,15 +52,12 @@
|
|||||||
android:id="@+id/introductionIcon"
|
android:id="@+id/introductionIcon"
|
||||||
android:layout_width="@dimen/listitem_picture_size"
|
android:layout_width="@dimen/listitem_picture_size"
|
||||||
android:layout_height="@dimen/listitem_picture_size"
|
android:layout_height="@dimen/listitem_picture_size"
|
||||||
android:layout_centerHorizontal="true"
|
|
||||||
android:src="@drawable/ic_contact_introduction"
|
android:src="@drawable/ic_contact_introduction"
|
||||||
tools:ignore="ContentDescription"/>
|
tools:ignore="ContentDescription"/>
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_toEndOf="@+id/introductionIcon"
|
|
||||||
android:layout_toRightOf="@+id/introductionIcon"
|
|
||||||
android:gravity="top|center_horizontal"
|
android:gravity="top|center_horizontal"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
@@ -86,12 +79,12 @@
|
|||||||
android:layout_marginTop="@dimen/margin_small"
|
android:layout_marginTop="@dimen/margin_small"
|
||||||
android:gravity="center"
|
android:gravity="center"
|
||||||
android:textColor="@color/briar_text_primary"
|
android:textColor="@color/briar_text_primary"
|
||||||
android:textSize="@dimen/text_size_tiny"
|
android:textSize="@dimen/text_size_small"
|
||||||
tools:text="Contact 2"/>
|
tools:text="Contact 2"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
</RelativeLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<ProgressBar
|
<ProgressBar
|
||||||
android:id="@+id/progressBar"
|
android:id="@+id/progressBar"
|
||||||
@@ -101,19 +94,6 @@
|
|||||||
android:layout_gravity="center"
|
android:layout_gravity="center"
|
||||||
tools:visibility="gone"/>
|
tools:visibility="gone"/>
|
||||||
|
|
||||||
<org.thoughtcrime.securesms.components.emoji.EmojiTextView
|
|
||||||
android:id="@+id/introductionText"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="@dimen/margin_activity_horizontal"
|
|
||||||
android:textColor="@color/briar_text_secondary"
|
|
||||||
android:textSize="@dimen/text_size_medium"
|
|
||||||
tools:text="@string/introduction_message_text"/>
|
|
||||||
|
|
||||||
</LinearLayout>
|
|
||||||
|
|
||||||
</ScrollView>
|
|
||||||
|
|
||||||
<org.briarproject.briar.android.view.LargeTextInputView
|
<org.briarproject.briar.android.view.LargeTextInputView
|
||||||
android:id="@+id/introductionMessageView"
|
android:id="@+id/introductionMessageView"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
@@ -123,3 +103,4 @@
|
|||||||
app:maxLines="5"/>
|
app:maxLines="5"/>
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
</ScrollView>
|
||||||
@@ -19,57 +19,15 @@
|
|||||||
android:textStyle="italic"
|
android:textStyle="italic"
|
||||||
tools:text="@string/groups_member_joined"/>
|
tools:text="@string/groups_member_joined"/>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignBottom="@+id/info"
|
|
||||||
android:layout_alignLeft="@+id/text"
|
|
||||||
android:layout_alignTop="@+id/info"
|
|
||||||
android:layout_below="@+id/text"
|
|
||||||
android:layout_marginRight="@dimen/margin_medium"
|
|
||||||
android:scaleType="center"
|
|
||||||
tools:ignore="ContentDescription"
|
|
||||||
tools:src="@drawable/ic_visibility"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/info"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignEnd="@+id/text"
|
|
||||||
android:layout_alignRight="@+id/text"
|
|
||||||
android:layout_below="@+id/text"
|
|
||||||
android:layout_marginBottom="@dimen/margin_medium"
|
|
||||||
android:layout_toRightOf="@+id/icon"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:minHeight="24dp"
|
|
||||||
android:textColor="@color/briar_text_secondary"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textSize="@dimen/text_size_tiny"
|
|
||||||
android:textStyle="italic"
|
|
||||||
tools:text="@string/groups_reveal_visible_revealed_by_contact"/>
|
|
||||||
|
|
||||||
<org.briarproject.briar.android.view.AuthorView
|
<org.briarproject.briar.android.view.AuthorView
|
||||||
android:id="@+id/author"
|
android:id="@+id/author"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_alignLeft="@+id/text"
|
android:layout_alignLeft="@+id/text"
|
||||||
android:layout_below="@+id/info"
|
android:layout_alignStart="@+id/text"
|
||||||
android:layout_toLeftOf="@+id/optionsButton"
|
android:layout_below="@+id/text"
|
||||||
app:persona="commenter"/>
|
app:persona="commenter"/>
|
||||||
|
|
||||||
<Button
|
|
||||||
android:id="@+id/optionsButton"
|
|
||||||
style="@style/BriarButtonFlat.Positive.Tiny"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_alignBottom="@+id/author"
|
|
||||||
android:layout_alignEnd="@+id/text"
|
|
||||||
android:layout_alignRight="@+id/text"
|
|
||||||
android:layout_below="@+id/info"
|
|
||||||
android:gravity="right|center_vertical"
|
|
||||||
android:text="@string/options"/>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
style="@style/Divider.ThreadItem"
|
style="@style/Divider.ThreadItem"
|
||||||
android:layout_below="@+id/author"
|
android:layout_below="@+id/author"
|
||||||
|
|||||||
@@ -13,11 +13,13 @@
|
|||||||
android:id="@+id/authorView"
|
android:id="@+id/authorView"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_alignParentStart="true"
|
||||||
android:layout_alignParentLeft="true"
|
android:layout_alignParentLeft="true"
|
||||||
android:layout_alignParentTop="true"
|
android:layout_alignParentTop="true"
|
||||||
android:layout_marginBottom="@dimen/margin_small"
|
android:layout_marginBottom="@dimen/margin_small"
|
||||||
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
|
android:layout_toStartOf="@+id/bulbView"
|
||||||
android:layout_toLeftOf="@+id/bulbView"
|
android:layout_toLeftOf="@+id/bulbView"
|
||||||
app:persona="list"/>
|
app:persona="list"/>
|
||||||
|
|
||||||
@@ -29,6 +31,7 @@
|
|||||||
android:layout_alignParentEnd="true"
|
android:layout_alignParentEnd="true"
|
||||||
android:layout_alignParentRight="true"
|
android:layout_alignParentRight="true"
|
||||||
android:layout_alignTop="@+id/authorView"
|
android:layout_alignTop="@+id/authorView"
|
||||||
|
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
||||||
tools:src="@drawable/contact_connected"/>
|
tools:src="@drawable/contact_connected"/>
|
||||||
|
|
||||||
@@ -39,40 +42,16 @@
|
|||||||
android:layout_below="@+id/authorView"
|
android:layout_below="@+id/authorView"
|
||||||
android:layout_marginBottom="@dimen/margin_small"
|
android:layout_marginBottom="@dimen/margin_small"
|
||||||
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
|
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
|
||||||
|
android:layout_marginStart="@dimen/listitem_group_member_indentation"
|
||||||
android:text="@string/groups_member_created_you"
|
android:text="@string/groups_member_created_you"
|
||||||
android:textColor="@color/briar_text_secondary"
|
android:textColor="@color/briar_text_secondary"
|
||||||
tools:visibility="visible"/>
|
tools:visibility="visible"/>
|
||||||
|
|
||||||
<ImageView
|
|
||||||
android:id="@+id/icon"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@+id/creatorView"
|
|
||||||
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
|
|
||||||
android:layout_marginRight="@dimen/margin_small"
|
|
||||||
android:contentDescription="@string/forum_invitation_already_sharing"
|
|
||||||
android:src="@drawable/ic_visibility"/>
|
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@+id/info"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_below="@+id/creatorView"
|
|
||||||
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
|
|
||||||
android:layout_marginRight="@dimen/listitem_horizontal_margin"
|
|
||||||
android:layout_toRightOf="@+id/icon"
|
|
||||||
android:gravity="center_vertical"
|
|
||||||
android:minHeight="24dp"
|
|
||||||
android:textColor="@color/briar_text_secondary"
|
|
||||||
android:textIsSelectable="true"
|
|
||||||
android:textSize="@dimen/text_size_tiny"
|
|
||||||
android:textStyle="italic"
|
|
||||||
tools:text="@string/groups_reveal_visible_revealed_by_us"/>
|
|
||||||
|
|
||||||
<View
|
<View
|
||||||
android:id="@+id/divider"
|
android:id="@+id/divider"
|
||||||
style="@style/Divider.ContactList"
|
style="@style/Divider.ContactList"
|
||||||
android:layout_below="@+id/info"
|
android:layout_below="@+id/creatorView"
|
||||||
|
android:layout_marginStart="@dimen/listitem_group_member_indentation"
|
||||||
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
|
android:layout_marginLeft="@dimen/listitem_group_member_indentation"
|
||||||
android:layout_marginTop="@dimen/margin_medium"/>
|
android:layout_marginTop="@dimen/margin_medium"/>
|
||||||
|
|
||||||
|
|||||||
@@ -10,16 +10,16 @@
|
|||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_group_reveal"
|
android:id="@+id/action_group_invite"
|
||||||
android:icon="@drawable/ic_visibility_white"
|
android:icon="@drawable/social_share_white"
|
||||||
android:title="@string/groups_reveal_contacts"
|
android:title="@string/groups_invite_members"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="ifRoom"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_group_invite"
|
android:id="@+id/action_group_reveal"
|
||||||
android:icon="@drawable/ic_add_white"
|
android:icon="@drawable/ic_visibility_white"
|
||||||
android:title="@string/groups_invite_members"
|
android:title="@string/groups_reveal_contacts"
|
||||||
app:showAsAction="ifRoom"/>
|
app:showAsAction="never"/>
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_group_leave"
|
android:id="@+id/action_group_leave"
|
||||||
|
|||||||
@@ -76,7 +76,7 @@
|
|||||||
<string name="text_too_long">Der eingegebene Text ist leider zu lang</string>
|
<string name="text_too_long">Der eingegebene Text ist leider zu lang</string>
|
||||||
<string name="show_onboarding">Hilfe anzeigen</string>
|
<string name="show_onboarding">Hilfe anzeigen</string>
|
||||||
<!--Contacts and Private Conversations-->
|
<!--Contacts and Private Conversations-->
|
||||||
<string name="no_contacts">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nVerwende das \"+\"-Icon am oberen Bildschirmrand an und folge den Anweisungen um Freunde zu deiner Kontaktliste hinzuzufügen.\n\nDenke daran dass Du neue Kontakte nur dann hinzufügen kannst, wenn Du ihnen physisch begegnest. Das hindert andere daran, sich als Deine Person auszugeben oder Deine Nachrichten zu lesen.</string>
|
<string name="no_contacts">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nTippe auf das \"+\"-Symbol am oberen Bildschirmrand an und folge den Anweisungen, um Freunde zu deiner Kontaktliste hinzuzufügen.\n\nDenke daran, dass du neue Kontakte nur dann hinzufügen kannst, wenn du ihnen persönlich begegnest. Das hindert andere daran, sich als deine Person auszugeben oder deine Nachrichten zu lesen.</string>
|
||||||
<string name="date_no_private_messages">Keine Nachrichten.</string>
|
<string name="date_no_private_messages">Keine Nachrichten.</string>
|
||||||
<string name="no_private_messages">Das ist die Gesprächsansicht.\n\nEs scheint hier einen Mangel an Gesprächsthemen zu geben.\n\nUm ein neues Gespräch zu beginnen, verwende einfach das Eingabefeld am unteren Bildschirmrand.</string>
|
<string name="no_private_messages">Das ist die Gesprächsansicht.\n\nEs scheint hier einen Mangel an Gesprächsthemen zu geben.\n\nUm ein neues Gespräch zu beginnen, verwende einfach das Eingabefeld am unteren Bildschirmrand.</string>
|
||||||
<string name="message_hint">Nachricht eingeben</string>
|
<string name="message_hint">Nachricht eingeben</string>
|
||||||
@@ -117,8 +117,6 @@
|
|||||||
<string name="introduction_onboarding_text">Du kannst Deine Kontakte untereinander bekannt machen. So können sie sich über Briar verbinden, ohne sich persönlich treffen zu müssen.</string>
|
<string name="introduction_onboarding_text">Du kannst Deine Kontakte untereinander bekannt machen. So können sie sich über Briar verbinden, ohne sich persönlich treffen zu müssen.</string>
|
||||||
<string name="introduction_activity_title">Kontakt auswählen</string>
|
<string name="introduction_activity_title">Kontakt auswählen</string>
|
||||||
<string name="introduction_message_title">Kontakte untereinander bekannt machen</string>
|
<string name="introduction_message_title">Kontakte untereinander bekannt machen</string>
|
||||||
<string name="introduction_message_text">Du kannst eine Nachricht erstellen die mit Deiner Kontaktempfehlung an %1$s und %2$s mitgesendet wird:</string>
|
|
||||||
<string name="introduction_message_hint">Gib eine Nachricht ein (optional)</string>
|
|
||||||
<string name="introduction_button">Kontaktempfehlung abgeben</string>
|
<string name="introduction_button">Kontaktempfehlung abgeben</string>
|
||||||
<string name="introduction_sent">Deine Kontaktempfehlung wurde verschickt</string>
|
<string name="introduction_sent">Deine Kontaktempfehlung wurde verschickt</string>
|
||||||
<string name="introduction_error">Es gab einen Fehler beim Versuch, die Kontaktempfehlung zu verschicken</string>
|
<string name="introduction_error">Es gab einen Fehler beim Versuch, die Kontaktempfehlung zu verschicken</string>
|
||||||
@@ -137,7 +135,7 @@
|
|||||||
<item quantity="other">%d neue Kontakte hinzugefügt.</item>
|
<item quantity="other">%d neue Kontakte hinzugefügt.</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<!--Private Groups-->
|
<!--Private Groups-->
|
||||||
<string name="groups_list_empty">Du bist in keiner Gruppe Mitglied.\n\nVerwende das + Symbol am oberen Rand um eine Gruppe zu erstellen oder frage bitte Deine Kontakte um Aufnahme in einer deren Gruppen.</string>
|
<string name="groups_list_empty">Du bist in keiner Gruppe Mitglied.\n\nTippe auf das \"+\"-Symbol am oberen Bildschirmrand, um eine Gruppe zu erstellen, oder frage deine Kontakte um Aufnahme in einer ihrer Gruppen.</string>
|
||||||
<string name="groups_created_by">Erstellt durch %s</string>
|
<string name="groups_created_by">Erstellt durch %s</string>
|
||||||
<plurals name="messages">
|
<plurals name="messages">
|
||||||
<item quantity="one">%d Nachrichten</item>
|
<item quantity="one">%d Nachrichten</item>
|
||||||
@@ -149,9 +147,7 @@
|
|||||||
<string name="groups_create_group_title">Private Gruppe erstellen</string>
|
<string name="groups_create_group_title">Private Gruppe erstellen</string>
|
||||||
<string name="groups_create_group_button">Gruppe erstellen</string>
|
<string name="groups_create_group_button">Gruppe erstellen</string>
|
||||||
<string name="groups_create_group_invitation_button">Einladung schicken</string>
|
<string name="groups_create_group_invitation_button">Einladung schicken</string>
|
||||||
<string name="groups_create_group_hint">Gebe Deiner privaten Gruppe einen Namen</string>
|
|
||||||
<string name="groups_invitation_sent">Gruppeneinladung versendet</string>
|
<string name="groups_invitation_sent">Gruppeneinladung versendet</string>
|
||||||
<string name="groups_compose_message">Nachricht erstellen</string>
|
|
||||||
<string name="groups_message_sent">Nachricht gesendet</string>
|
<string name="groups_message_sent">Nachricht gesendet</string>
|
||||||
<string name="groups_member_list">Mitglieder</string>
|
<string name="groups_member_list">Mitglieder</string>
|
||||||
<string name="groups_invite_members">Mitglieder einladen</string>
|
<string name="groups_invite_members">Mitglieder einladen</string>
|
||||||
@@ -174,18 +170,24 @@
|
|||||||
<string name="groups_invitations_invitation_received">%1$s hat dich eingeladen der Gruppe \"%2$s\" beizutreten.</string>
|
<string name="groups_invitations_invitation_received">%1$s hat dich eingeladen der Gruppe \"%2$s\" beizutreten.</string>
|
||||||
<string name="groups_invitations_joined">Gruppe beigetreten</string>
|
<string name="groups_invitations_joined">Gruppe beigetreten</string>
|
||||||
<string name="groups_invitations_declined">Gruppeneinladung abgelehnt</string>
|
<string name="groups_invitations_declined">Gruppeneinladung abgelehnt</string>
|
||||||
|
<plurals name="groups_invitations_open">
|
||||||
|
<item quantity="one">%d offene Gruppeneinladung</item>
|
||||||
|
<item quantity="other">%d offene Gruppeneinladungen</item>
|
||||||
|
</plurals>
|
||||||
<string name="groups_invitations_response_accepted_sent">Gruppeneinladung von %s angenommen.</string>
|
<string name="groups_invitations_response_accepted_sent">Gruppeneinladung von %s angenommen.</string>
|
||||||
<string name="groups_invitations_response_declined_sent">Gruppeneinladung von %s abgelehnt.</string>
|
<string name="groups_invitations_response_declined_sent">Gruppeneinladung von %s abgelehnt.</string>
|
||||||
<string name="groups_invitations_response_accepted_received">%s hat die Einladung zur Gruppe angenommen.</string>
|
<string name="groups_invitations_response_accepted_received">%s hat die Einladung zur Gruppe angenommen.</string>
|
||||||
<string name="groups_invitations_response_declined_received">%s hat die Einladung zur Gruppe abgelehnt.</string>
|
<string name="groups_invitations_response_declined_received">%s hat die Einladung zur Gruppe abgelehnt.</string>
|
||||||
|
<string name="sharing_status_groups">Nur der Ersteller kann neue Mitglieder zu einer Gruppe einladen. HIer alle aktuellen Mitglieder dieser Gruppe.</string>
|
||||||
<!--Private Groups Revealing Contacts-->
|
<!--Private Groups Revealing Contacts-->
|
||||||
<string name="groups_reveal_contacts">Kontakte teilen</string>
|
<string name="groups_reveal_contacts">Kontakte teilen</string>
|
||||||
<string name="groups_reveal_dialog_message">Kontakte können mit allen derzeitigen und zukünftigen Mitgliedern dieser Gruppe geteilt werden.\n\nDas beschleunigt die Verbindung zu der Gruppe und macht sie zusätzlich zuverlässiger, da Kommunikation mit den Mitgliedern auch dann erfolgen kann, wenn der Ersteller der Gruppe offline ist.</string>
|
<string name="groups_reveal_dialog_message">Kontakte können mit allen derzeitigen und zukünftigen Mitgliedern dieser Gruppe geteilt werden.\n\nDas beschleunigt die Verbindung zu der Gruppe und macht sie zusätzlich zuverlässiger, da Kommunikation mit den Mitgliedern auch dann erfolgen kann, wenn der Ersteller der Gruppe offline ist.</string>
|
||||||
<string name="groups_reveal_visible">Verbindung zum Kontakt ist für die Gruppe sichtbar</string>
|
<string name="groups_reveal_visible">Verbindung zum Kontakt ist für die Gruppe sichtbar</string>
|
||||||
|
<string name="groups_reveal_visible_revealed_by_us">Beziehung zum Kontakt ist für diese Gruppe sichtbar (selbst offengelegt)</string>
|
||||||
|
<string name="groups_reveal_visible_revealed_by_contact">Beziehung zum Kontakt ist für diese Gruppe sichtbar (offengelegt durch %s)</string>
|
||||||
|
<string name="groups_reveal_invisible">Beziehung zum Kontakt ist für diese Gruppe nicht sichtbar</string>
|
||||||
<!--Forums-->
|
<!--Forums-->
|
||||||
<string name="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Icon am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
|
<string name="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Symbol am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
|
||||||
<string name="create_forum_title">Neues Forum</string>
|
|
||||||
<string name="choose_forum_name">Wähle einen Namen für dein Forum:</string>
|
|
||||||
<string name="create_forum_button">Forum erstellen</string>
|
<string name="create_forum_button">Forum erstellen</string>
|
||||||
<string name="forum_created_toast">Forum wurde erstellt</string>
|
<string name="forum_created_toast">Forum wurde erstellt</string>
|
||||||
<string name="no_forum_posts">Dieses Forum ist leer.\n\nBenutze das Stift-Icon am oberen Bildschirmrand um den ersten Beitrag zu verfassen.\n\nFühlst du dich einsam hier? Dann teile das Forum mit weiteren Kontakten!</string>
|
<string name="no_forum_posts">Dieses Forum ist leer.\n\nBenutze das Stift-Icon am oberen Bildschirmrand um den ersten Beitrag zu verfassen.\n\nFühlst du dich einsam hier? Dann teile das Forum mit weiteren Kontakten!</string>
|
||||||
@@ -194,15 +196,10 @@
|
|||||||
<item quantity="one">%d Beitrag</item>
|
<item quantity="one">%d Beitrag</item>
|
||||||
<item quantity="other">%d Beiträge</item>
|
<item quantity="other">%d Beiträge</item>
|
||||||
</plurals>
|
</plurals>
|
||||||
<string name="forum_compose_post">Neuer Forenbeitrag</string>
|
|
||||||
<string name="forum_new_entry_posted">Forenbeitrag wurde veröffentlicht.</string>
|
<string name="forum_new_entry_posted">Forenbeitrag wurde veröffentlicht.</string>
|
||||||
<string name="forum_new_message_hint">Neuer Eintrag</string>
|
<string name="forum_new_message_hint">Neuer Eintrag</string>
|
||||||
<string name="forum_message_reply_hint">Neue Antwort</string>
|
<string name="forum_message_reply_hint">Neue Antwort</string>
|
||||||
<string name="btn_reply">Antworten</string>
|
<string name="btn_reply">Antworten</string>
|
||||||
<plurals name="message_replies">
|
|
||||||
<item quantity="one">%1$d Antwort</item>
|
|
||||||
<item quantity="other">%1$d Antworten</item>
|
|
||||||
</plurals>
|
|
||||||
<string name="forum_leave">Forum verlassen</string>
|
<string name="forum_leave">Forum verlassen</string>
|
||||||
<string name="dialog_title_leave_forum">Verlassen des Forums bestätigen</string>
|
<string name="dialog_title_leave_forum">Verlassen des Forums bestätigen</string>
|
||||||
<string name="dialog_message_leave_forum">Bist du sicher, dass du dieses Forum verlassen willst? Kontakte, die du mit diesem Forum geteilt hast, werden keine Updates von diesem Forum mehr bekommen.</string>
|
<string name="dialog_message_leave_forum">Bist du sicher, dass du dieses Forum verlassen willst? Kontakte, die du mit diesem Forum geteilt hast, werden keine Updates von diesem Forum mehr bekommen.</string>
|
||||||
@@ -214,7 +211,6 @@
|
|||||||
<string name="activity_share_toolbar_header">Kontakte auswählen</string>
|
<string name="activity_share_toolbar_header">Kontakte auswählen</string>
|
||||||
<string name="no_contacts_selector">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nBitte komm zurück, wenn du deinen ersten Kontakt hinzugefügt hast.</string>
|
<string name="no_contacts_selector">Du scheinst hier neu zu sein und noch keine Kontakte zu haben.\n\nBitte komm zurück, wenn du deinen ersten Kontakt hinzugefügt hast.</string>
|
||||||
<string name="forum_shared_snackbar">Forum mit gewählten Kontakten geteilt</string>
|
<string name="forum_shared_snackbar">Forum mit gewählten Kontakten geteilt</string>
|
||||||
<string name="forum_share_message">Du kannst eine optionale Einladungsnachricht verfassen, die an die ausgewählten Kontakte geschickt wird.</string>
|
|
||||||
<string name="forum_share_error">Es gab einen Fehler beim Versuch, dieses Forum zu teilen.</string>
|
<string name="forum_share_error">Es gab einen Fehler beim Versuch, dieses Forum zu teilen.</string>
|
||||||
<string name="forum_invitation_received">%1$s hat das Forum \"%2$s\" mit dir geteilt.</string>
|
<string name="forum_invitation_received">%1$s hat das Forum \"%2$s\" mit dir geteilt.</string>
|
||||||
<string name="forum_invitation_sent">Du hast das Forum \"%1$s\" mit %2$s geteilt.</string>
|
<string name="forum_invitation_sent">Du hast das Forum \"%1$s\" mit %2$s geteilt.</string>
|
||||||
@@ -229,6 +225,8 @@
|
|||||||
<string name="forum_invitation_response_accepted_received">%s hat die Forumeinladung akzeptiert.</string>
|
<string name="forum_invitation_response_accepted_received">%s hat die Forumeinladung akzeptiert.</string>
|
||||||
<string name="forum_invitation_response_declined_received">%s hat die Forumseinladung abgelehnt.</string>
|
<string name="forum_invitation_response_declined_received">%s hat die Forumseinladung abgelehnt.</string>
|
||||||
<string name="sharing_status">Sharing Status</string>
|
<string name="sharing_status">Sharing Status</string>
|
||||||
|
<string name="sharing_status_forum">Jedes Mitglied eines Forums kann dieses mit seinen Kontakten teilen. Du teilst dieses Forum mit den folgenden Kontakten. Möglicherweise gibt es Mitglieder die nicht sichtbar sind.</string>
|
||||||
|
<string name="shared_with">Geteilt mit %1$d (%2$d online)</string>
|
||||||
<plurals name="forums_shared">
|
<plurals name="forums_shared">
|
||||||
<item quantity="one">%d Forum von Kontaken geteilt</item>
|
<item quantity="one">%d Forum von Kontaken geteilt</item>
|
||||||
<item quantity="other">%d Foren von Kontakten geteilt</item>
|
<item quantity="other">%d Foren von Kontakten geteilt</item>
|
||||||
@@ -249,7 +247,6 @@
|
|||||||
<string name="blogs_remove_blog_dialog_message">Bist Du sicher, dass Du diesen Blog und alle dazugehörigen Beiträge löschen möchtest?\nBeachte, dass dies nicht den Blog auf Geräten anderer Leute löscht.</string>
|
<string name="blogs_remove_blog_dialog_message">Bist Du sicher, dass Du diesen Blog und alle dazugehörigen Beiträge löschen möchtest?\nBeachte, dass dies nicht den Blog auf Geräten anderer Leute löscht.</string>
|
||||||
<string name="blogs_remove_blog_ok">Blog entfernen</string>
|
<string name="blogs_remove_blog_ok">Blog entfernen</string>
|
||||||
<string name="blogs_blog_removed">Blog wurde entfernt</string>
|
<string name="blogs_blog_removed">Blog wurde entfernt</string>
|
||||||
<string name="blogs_reblog_comment_hint">Füge optionalen Kommentar hinzu</string>
|
|
||||||
<string name="blogs_reblog_button">Rebloggen</string>
|
<string name="blogs_reblog_button">Rebloggen</string>
|
||||||
<!--Blog Sharing-->
|
<!--Blog Sharing-->
|
||||||
<string name="blogs_sharing_share">Blog teilen</string>
|
<string name="blogs_sharing_share">Blog teilen</string>
|
||||||
@@ -265,6 +262,7 @@
|
|||||||
<string name="blogs_sharing_invitations_title">Blogeinladungen</string>
|
<string name="blogs_sharing_invitations_title">Blogeinladungen</string>
|
||||||
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
|
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
|
||||||
<string name="blogs_sharing_declined_toast">Blogeinladung abgelehnt</string>
|
<string name="blogs_sharing_declined_toast">Blogeinladung abgelehnt</string>
|
||||||
|
<string name="sharing_status_blog">Jeder Abonnent eines Blogs kann diesen mit seinen Kontakten teilen. Du teilst diesen Blog mit den folgenden Kontakten. Möglicherweise gibt es Abonnenten die nicht sichtbar sind.</string>
|
||||||
<!--RSS Feeds-->
|
<!--RSS Feeds-->
|
||||||
<string name="blogs_rss_feeds_import">RSS-Feed importieren</string>
|
<string name="blogs_rss_feeds_import">RSS-Feed importieren</string>
|
||||||
<string name="blogs_rss_feeds_import_button">Importieren</string>
|
<string name="blogs_rss_feeds_import_button">Importieren</string>
|
||||||
@@ -274,6 +272,9 @@
|
|||||||
<string name="blogs_rss_feeds_manage_imported">Importiert:</string>
|
<string name="blogs_rss_feeds_manage_imported">Importiert:</string>
|
||||||
<string name="blogs_rss_feeds_manage_author">Autor:</string>
|
<string name="blogs_rss_feeds_manage_author">Autor:</string>
|
||||||
<string name="blogs_rss_feeds_manage_updated">Letzte Aktualisierung:</string>
|
<string name="blogs_rss_feeds_manage_updated">Letzte Aktualisierung:</string>
|
||||||
|
<string name="blogs_rss_remove_feed">Feed entfernen</string>
|
||||||
|
<string name="blogs_rss_remove_feed_dialog_message">Soll der Feed mit allen Posts wirklich gelöscht werden?\nGeteilte Posts werden dabei nicht von anderen Geräten gelöscht.</string>
|
||||||
|
<string name="blogs_rss_remove_feed_ok">Feed entfernen</string>
|
||||||
<string name="blogs_rss_feeds_manage_delete_error">Der Feed konnte nicht gelöscht werden!</string>
|
<string name="blogs_rss_feeds_manage_delete_error">Der Feed konnte nicht gelöscht werden!</string>
|
||||||
<string name="blogs_rss_feeds_manage_empty_state">Du hast bisher noch keine RSS Feeds importiert. Tippe auf das \"+\"-Icon am oberen Bildschirmrand um einen neuen Feed hinzuzufügen.</string>
|
<string name="blogs_rss_feeds_manage_empty_state">Du hast bisher noch keine RSS Feeds importiert. Tippe auf das \"+\"-Icon am oberen Bildschirmrand um einen neuen Feed hinzuzufügen.</string>
|
||||||
<string name="blogs_rss_feeds_manage_error">Es gab ein Problem beim Laden deiner Feeds. Bitte versuche es später erneut.</string>
|
<string name="blogs_rss_feeds_manage_error">Es gab ein Problem beim Laden deiner Feeds. Bitte versuche es später erneut.</string>
|
||||||
@@ -282,6 +283,10 @@
|
|||||||
<string name="bluetooth_setting">Über Bluetooth verbinden</string>
|
<string name="bluetooth_setting">Über Bluetooth verbinden</string>
|
||||||
<string name="bluetooth_setting_enabled">Sobald Kontakte in der Nähe sind</string>
|
<string name="bluetooth_setting_enabled">Sobald Kontakte in der Nähe sind</string>
|
||||||
<string name="bluetooth_setting_disabled">Nur beim Hinzufügen von Kontakten</string>
|
<string name="bluetooth_setting_disabled">Nur beim Hinzufügen von Kontakten</string>
|
||||||
|
<string name="tor_network_setting">Über Tor verbinden</string>
|
||||||
|
<string name="tor_network_setting_never">Nie</string>
|
||||||
|
<string name="tor_network_setting_wifi">Nur WLAN</string>
|
||||||
|
<string name="tor_network_setting_always">WLAN oder mobile Datenverbindung</string>
|
||||||
<!--Settings Security and Panic-->
|
<!--Settings Security and Panic-->
|
||||||
<string name="security_settings_title">Sicherheit</string>
|
<string name="security_settings_title">Sicherheit</string>
|
||||||
<string name="change_password">Passwort ändern</string>
|
<string name="change_password">Passwort ändern</string>
|
||||||
@@ -306,10 +311,6 @@
|
|||||||
<string name="uninstall_setting_summary">Diese Aktion benötigt manuelle Bestätigung im Falle eines Panik-Ereignisses</string>
|
<string name="uninstall_setting_summary">Diese Aktion benötigt manuelle Bestätigung im Falle eines Panik-Ereignisses</string>
|
||||||
<!--Settings Notifications-->
|
<!--Settings Notifications-->
|
||||||
<string name="notification_settings_title">Benachrichtigungen</string>
|
<string name="notification_settings_title">Benachrichtigungen</string>
|
||||||
<string name="notify_private_messages_setting">Benachrichtigungen für private Nachrichten anzeigen</string>
|
|
||||||
<string name="notify_group_messages_setting">Benachrichtigungen für Gruppennachrichten anzeigen</string>
|
|
||||||
<string name="notify_forum_posts_setting">Benachrichtigungen für Forenbeiträge anzeigen</string>
|
|
||||||
<string name="notify_blog_posts_setting">Benachrichtigungen für Blog-Beiträge anzeigen</string>
|
|
||||||
<string name="notify_vibration_setting">Vibration</string>
|
<string name="notify_vibration_setting">Vibration</string>
|
||||||
<string name="notify_sound_setting">Tonsignal</string>
|
<string name="notify_sound_setting">Tonsignal</string>
|
||||||
<string name="notify_sound_setting_default">Standardklingelton</string>
|
<string name="notify_sound_setting_default">Standardklingelton</string>
|
||||||
@@ -341,4 +342,5 @@
|
|||||||
<string name="dev_report_saved">Der Bericht wurde gespeichert. Er wird verschickt, wenn Du Dich das nächste Mal bei Briar anmeldest.</string>
|
<string name="dev_report_saved">Der Bericht wurde gespeichert. Er wird verschickt, wenn Du Dich das nächste Mal bei Briar anmeldest.</string>
|
||||||
<!--Sign Out-->
|
<!--Sign Out-->
|
||||||
<string name="progress_title_logout">Von Briar abmelden ...</string>
|
<string name="progress_title_logout">Von Briar abmelden ...</string>
|
||||||
|
<!--Screen Filters & Tapjacking-->
|
||||||
</resources>
|
</resources>
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user