Compare commits

..

76 Commits

Author SHA1 Message Date
akwizgran
39478a7914 Bump version numbers for 1.1.2 release. 2018-10-01 14:58:45 +01:00
akwizgran
112e71a9cb Bump version numbers for 1.0.2 release. 2018-10-01 14:56:08 +01:00
akwizgran
5650bef310 Update translations. 2018-10-01 14:56:08 +01:00
akwizgran
2a87171c49 Merge branch 'manual-screenshots' into 'master'
Create Screenshot of Conversation for Manual

Closes #1377

See merge request briar/briar!910
2018-10-01 13:41:05 +00:00
akwizgran
15cb5409e7 Merge branch '1352-panic-ripple-fdroid' into 'master'
Fix Panic Button bugs

Closes #1109 and #1352

See merge request briar/briar!937
2018-09-28 09:57:18 +00:00
akwizgran
fd07dc006d Update comment. 2018-09-28 09:55:47 +01:00
akwizgran
cc87c4e37d Merge branch '1391-start-end-api15' into 'master'
RTL language layout fixes

Closes #1391

See merge request briar/briar!938
2018-09-28 08:45:17 +00:00
akwizgran
4a10e876f6 Merge branch '1389-notice-shadows' into 'master'
Fix notice shadows

Closes #1389

See merge request briar/briar!939
2018-09-28 08:43:06 +00:00
Torsten Grote
fad0057c4a Fix notice shadows 2018-09-27 20:32:13 -03:00
Torsten Grote
5aabfcea9a Unmirror checkboxes in RTL layouts 2018-09-27 19:09:08 -03:00
Torsten Grote
f7d928c774 Fix start/end margins for API 15
Fixes #1391
2018-09-27 19:07:13 -03:00
Torsten Grote
bd983d9796 Remove non-functional uninstall panic action
Fixes #1109
2018-09-27 18:37:57 -03:00
Torsten Grote
de8d1b7d96 Allow sign out by trusted non-paired panic apps 2018-09-27 18:25:31 -03:00
Torsten Grote
9155f62d0b Remove Amnesty International's key and add F-Droid Ripple key instead
Fixes #1352
2018-09-27 18:17:14 -03:00
akwizgran
86684e228a Merge branch 'slow-bridges' into 'master'
Update bridge list, test for slow bridges

See merge request briar/briar!936
2018-09-27 16:10:22 +00:00
akwizgran
9615eff649 Add new bridges to replace slow ones. 2018-09-27 16:11:52 +01:00
akwizgran
9381d46f51 Remove two bridges that are slow to bootstrap. 2018-09-27 16:08:14 +01:00
akwizgran
e4a3a1ad40 Delete Tor state after testing each bridge. 2018-09-27 16:07:46 +01:00
akwizgran
905dc2a662 Merge branch 'qr-code-version-errors' into 'master'
Show different error message if QR code is too new

See merge request briar/briar!934
2018-09-24 16:42:43 +00:00
Torsten Grote
c2b7f85b8e Remove code from TestDataCreator that breaks encapsulation 2018-09-24 12:37:11 -03:00
Torsten Grote
ae81eb3737 Throw AssertionError when creating an account while a database key is in memory 2018-09-24 12:37:11 -03:00
Torsten Grote
60d949c342 Refactor tests so that all test data is created in the first test 2018-09-24 12:37:11 -03:00
Torsten Grote
1c90e64894 Split up UI and Screenshot tests
Closes #1377
2018-09-24 12:37:06 -03:00
Torsten Grote
f0e2d5281f Create Screenshot of Conversation for Manual 2018-09-24 12:34:26 -03:00
akwizgran
c7522dae1f Show different error message if QR code is too new. 2018-09-24 13:21:45 +01:00
Torsten Grote
097d14b9a1 Merge branch 'private-message-visitor' into 'master'
Use visitor pattern to create conversation items

See merge request briar/briar!933
2018-09-20 19:04:14 +00:00
akwizgran
0491c3cace Use a visitor to create ConversationItems. 2018-09-20 16:53:48 +01:00
akwizgran
cbae13feca Merge branch 'gradle-4.10' into 'master'
Upgrade the gradle wrapper to version 4.10.2

See merge request briar/briar!932
2018-09-20 15:34:33 +00:00
Torsten Grote
b7c8859c82 Upgrade the gradle wrapper to version 4.10.2 2018-09-20 11:52:19 -03:00
akwizgran
2e120f752c Add parameterised return type. 2018-09-20 15:19:22 +01:00
akwizgran
031eac54c5 Add private message visitor. 2018-09-20 14:43:19 +01:00
akwizgran
2c2596afdd Merge branch 'conversation-manager' into 'master'
Use ConversationManager for private message retrieval

See merge request briar/briar!912
2018-09-20 13:01:53 +00:00
akwizgran
d1be14effe Merge branch '1386-app-lock-after-signout' into 'master'
Fix app lock after sign-out bug

Closes #1386

See merge request briar/briar!930
2018-09-20 11:05:37 +00:00
akwizgran
b56e7ab07d Merge branch 'roboelectric-android-http-client' into 'master'
Remove AndroidHttpClient class after Roboelectric update

See merge request briar/briar!929
2018-09-20 11:04:52 +00:00
akwizgran
089e9589ed Merge branch '1378-rtl-support' into 'master'
Add support for right-to-left languages

Closes #1378, #1076, #1078, #964, #1080, and #1079

See merge request briar/briar!918
2018-09-20 10:59:44 +00:00
Torsten Grote
660ba16a14 Fix app lock after sign-out bug
It some cases, it was observered how the app was locked after the user
had signed out.
This commit ensures that set alarms are canceled and that no new ones
will be set after the LockManager service has been stopped.

Fixes #1386
2018-09-19 16:06:13 -03:00
Torsten Grote
b101c4b636 Remove AndroidHttpClient class after Roboelectric update 2018-09-19 15:15:38 -03:00
Torsten Grote
fdfddd2667 Fix small RTL UI glitches in blog post layouts 2018-09-19 14:56:45 -03:00
akwizgran
296546544f Remove auto-generated layout IDs. 2018-09-19 17:42:25 +01:00
akwizgran
ad579a6ba3 Restore max of 50 contacts. 2018-09-19 17:42:09 +01:00
Torsten Grote
90e82357ba Move back to previous way of creating at least one test contact 2018-09-19 11:38:00 -03:00
Torsten Grote
b3b40753d8 RTL support: Address review issues 2018-09-19 11:33:03 -03:00
Torsten Grote
e60df3cece Don't crash when creating test data with 0 contacts
If no contacts exist, at least one will be created
2018-09-19 11:33:03 -03:00
Torsten Grote
da3cb95151 Move TrustIndicator behind date in AuthorView
to prevent it from being pushed off-screen

Fixes #1076
2018-09-19 11:33:03 -03:00
Torsten Grote
c27885072f Ellipzise long contact names in ConversationActivity
Fixes #1078
2018-09-19 11:33:03 -03:00
Torsten Grote
6557d564c9 Add RTL support to remaining layouts 2018-09-19 11:33:03 -03:00
Torsten Grote
53edcaf3e9 Add RTL support to remaining list items
Also fixes several small UI glitches with long texts
2018-09-19 11:33:03 -03:00
Torsten Grote
5122c961b4 Simplify dev reporter and add RTL support 2018-09-19 11:33:03 -03:00
Torsten Grote
f83b9244d4 Clean up threaded discussion screen
Adds RTL support
Fixing cropping bug

Closes #964
2018-09-19 11:33:03 -03:00
Torsten Grote
81292967e0 Add RTL support to conversation message bubbles 2018-09-19 11:33:03 -03:00
Torsten Grote
b72f6b4fc3 Migrate Introduction Screen to ConstraintLayout
Adds RTL support
Fixes issue with long contact names

Closes #1080
2018-09-19 11:33:03 -03:00
Torsten Grote
488be49c93 Convert contact lists to ConstraintLayout
Adds support for RTL languages
Fixes issues with long contact names

Closes #1079
2018-09-19 11:33:03 -03:00
Torsten Grote
90db45817a Add RTL language support to all activities and fragments 2018-09-19 11:33:03 -03:00
akwizgran
81863b9db6 Merge branch '1248-rename-lock-app' into 'master'
Rename "Screen lock" to "Lock app"

Closes #1248 and #1245

See merge request briar/briar!924
2018-09-19 14:28:34 +00:00
akwizgran
da069adb57 Merge branch '1186-remove-lock-screen-notification-setting' into 'master'
Remove lockscreen notification setting

Closes #1186

See merge request briar/briar!925
2018-09-19 14:27:31 +00:00
Torsten Grote
46425b09fa Rename "Screen lock" to "App lock" 2018-09-19 10:22:43 -03:00
akwizgran
41e1a436c9 Merge branch 'centre-decrypting-db-message' into 'master'
Align "decrypting database" message to centre

See merge request briar/briar!926
2018-09-19 11:46:55 +00:00
Torsten Grote
989394d18b Merge branch 'fingerprint-permission' into 'master'
Add obsolete fingerprint permission to satisfy Android Studio

See merge request briar/briar!927
2018-09-19 11:32:58 +00:00
akwizgran
b6b3f9c292 Align "decrypting database" message to centre. 2018-09-19 12:23:13 +01:00
akwizgran
a52547f73b Add obsolete fingerprint permission. 2018-09-19 12:19:33 +01:00
akwizgran
24f823a3ce Remove lock screen notification setting. 2018-09-19 10:50:46 +01:00
Torsten Grote
7d479063a9 ConversationManager: Address review issues 2018-09-18 10:10:21 -03:00
Torsten Grote
2309e73216 Fix bug where available invitations were marked answered
Now an invitiation was answered when it is no longer available
2018-09-18 10:10:21 -03:00
akwizgran
4b325f797b Combine LiveData observers, avoid redundant loads. 2018-09-18 10:10:21 -03:00
Torsten Grote
9be83c3cc7 Refactor ConversationItem creation 2018-09-18 10:10:21 -03:00
Torsten Grote
86f650503b Re-introduce InvitationResponse
This was done, so private responses don't need to include a Nameable already.
Retreiving a nameable is tricky and requires a data migration,
so we just don't do it now.
2018-09-18 10:10:21 -03:00
Torsten Grote
d430b4fd2d Move introduction role into IntroductionResponse 2018-09-18 10:10:21 -03:00
akwizgran
fcf7cf72ea Refactor doesExist() method. 2018-09-18 10:10:21 -03:00
Torsten Grote
b78dfea95f Remove ListenableFutureTask and replace it with LiveData 2018-09-18 10:10:21 -03:00
Torsten Grote
183fe08565 Rename object to nameable 2018-09-18 10:10:21 -03:00
Torsten Grote
7e32697696 Use ConversationManager to retrieve messages
This removes the public method for retrieving messages
from individual conversation clients
and just leaves methods that require a transaction
to be used by the ConversationManager only.
2018-09-18 10:10:21 -03:00
Torsten Grote
29758b174a Unify all events related to private messages 2018-09-18 10:10:21 -03:00
Torsten Grote
61e18f104e Unify all private message responses in one PrivateResponse class
This also adds `Shareable`s to invitation response
which is a precondition for #561
2018-09-18 10:10:21 -03:00
Torsten Grote
ffeca8817f Prepare private message retrieval through ConversationManager 2018-09-18 10:10:21 -03:00
Torsten Grote
59fae2fa3c Unify all private message requests in one PrivateRequest class 2018-09-18 10:10:21 -03:00
Torsten Grote
2d9345c018 Remove unnecessary information from private message classes 2018-09-18 10:10:21 -03:00
218 changed files with 3004 additions and 3656 deletions

View File

@@ -9,8 +9,8 @@ android {
defaultConfig {
minSdkVersion 14
targetSdkVersion 26
versionCode 10101
versionName "1.1.1"
versionCode 10102
versionName "1.1.2"
consumerProguardFiles 'proguard-rules.txt'
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"

View File

@@ -0,0 +1,10 @@
package org.briarproject.bramble.api;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@NotNullByDefault
public interface Nameable {
String getName();
}

View File

@@ -1,9 +0,0 @@
package org.briarproject.bramble.api;
import java.io.IOException;
/**
* An exception that indicates an unrecoverable version mismatch.
*/
public class UnsupportedVersionException extends IOException {
}

View File

@@ -1,5 +1,6 @@
package org.briarproject.bramble.api.identity;
import org.briarproject.bramble.api.Nameable;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.util.StringUtils;
@@ -13,7 +14,7 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
*/
@Immutable
@NotNullByDefault
public class Author {
public class Author implements Nameable {
public enum Status {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES

View File

@@ -3,7 +3,13 @@ package org.briarproject.bramble.api.keyagreement;
public interface KeyAgreementConstants {
/**
* The current version of the BQP protocol. Version number 89 is reserved.
* The version of the BQP protocol used in beta releases. This version
* number is reserved.
*/
byte BETA_PROTOCOL_VERSION = 89;
/**
* The current version of the BQP protocol.
*/
byte PROTOCOL_VERSION = 4;

View File

@@ -0,0 +1,20 @@
package org.briarproject.bramble.api.keyagreement;
import java.io.IOException;
/**
* Thrown when a QR code that has been scanned uses a protocol version that is
* not supported.
*/
public class UnsupportedVersionException extends IOException {
private final boolean tooOld;
public UnsupportedVersionException(boolean tooOld) {
this.tooOld = tooOld;
}
public boolean isTooOld() {
return tooOld;
}
}

View File

@@ -1,13 +1,13 @@
package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.keyagreement.TransportDescriptor;
import org.briarproject.bramble.api.keyagreement.UnsupportedVersionException;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BluetoothConstants;
import org.briarproject.bramble.api.plugin.LanTcpConstants;
@@ -21,6 +21,7 @@ import java.util.List;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.TRANSPORT_ID_BLUETOOTH;
@@ -43,8 +44,11 @@ class PayloadParserImpl implements PayloadParser {
// First byte: the protocol version
int protocolVersion = in.read();
if (protocolVersion == -1) throw new FormatException();
if (protocolVersion != PROTOCOL_VERSION)
throw new UnsupportedVersionException();
if (protocolVersion != PROTOCOL_VERSION) {
boolean tooOld = protocolVersion < PROTOCOL_VERSION ||
protocolVersion == BETA_PROTOCOL_VERSION;
throw new UnsupportedVersionException(tooOld);
}
// The rest of the payload is a BDF list with one or more elements
BdfReader r = bdfReaderFactory.createReader(in);
BdfList payload = r.readList();

View File

@@ -2,7 +2,7 @@ Bridge 131.252.210.150:8081 0E858AC201BF0F3FA3C462F64844CBFFC7297A42
Bridge 67.205.189.122:8443 12D64D5D44E20169585E7378580C0D33A872AD98
Bridge 45.32.148.146:8443 0CE016FB2462D8BF179AE71F7D702D09DEAC3F1D
Bridge 148.251.90.59:7510 019F727CA6DCA6CA5C90B55E477B7D87981E75BC
Bridge 195.91.239.8:9001 BA83F62551545655BBEBBFF353A45438D73FD45A
Bridge 45.55.1.74:8443 6F18FEFBB0CAECD5ABA755312FCCB34FC11A7AB8
Bridge 97.107.131.168:65341 DCDA8A5F1E2C50A6756A58462E5CF4B6B2DFDE26
Bridge 85.229.131.78:444 50E433CCC5FEC11CC34CB4D92033561E065EA106
Bridge 85.229.131.78:444 50E433CCC5FEC11CC34CB4D92033561E065EA106
Bridge 178.62.62.193:8443 391B1F9B6A28A1C5FAE1872283985F975E5DB029
Bridge 45.76.29.92:8443 ECF1DD51A46FDEF2C50CED992EEEAE8DED18DA0C

View File

@@ -0,0 +1,156 @@
package org.briarproject.bramble.keyagreement;
import org.briarproject.bramble.api.Bytes;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.BdfReader;
import org.briarproject.bramble.api.data.BdfReaderFactory;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.UnsupportedVersionException;
import org.briarproject.bramble.test.BrambleMockTestCase;
import org.jmock.Expectations;
import org.junit.Test;
import java.io.ByteArrayInputStream;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.BETA_PROTOCOL_VERSION;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.COMMIT_LENGTH;
import static org.briarproject.bramble.api.keyagreement.KeyAgreementConstants.PROTOCOL_VERSION;
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
public class PayloadParserImplTest extends BrambleMockTestCase {
private final BdfReaderFactory bdfReaderFactory =
context.mock(BdfReaderFactory.class);
private final BdfReader bdfReader = context.mock(BdfReader.class);
private final PayloadParserImpl payloadParser =
new PayloadParserImpl(bdfReaderFactory);
@Test(expected = FormatException.class)
public void testThrowsFormatExceptionIfPayloadIsEmpty() throws Exception {
payloadParser.parse(new byte[0]);
}
@Test
public void testThrowsUnsupportedVersionExceptionForOldVersion()
throws Exception {
try {
payloadParser.parse(new byte[] {PROTOCOL_VERSION - 1});
fail();
} catch (UnsupportedVersionException e) {
assertTrue(e.isTooOld());
}
}
@Test
public void testThrowsUnsupportedVersionExceptionForBetaVersion()
throws Exception {
try {
payloadParser.parse(new byte[] {BETA_PROTOCOL_VERSION});
fail();
} catch (UnsupportedVersionException e) {
assertTrue(e.isTooOld());
}
}
@Test
public void testThrowsUnsupportedVersionExceptionForNewVersion()
throws Exception {
try {
payloadParser.parse(new byte[] {PROTOCOL_VERSION + 1});
fail();
} catch (UnsupportedVersionException e) {
assertFalse(e.isTooOld());
}
}
@Test(expected = FormatException.class)
public void testThrowsFormatExceptionForEmptyList() throws Exception {
context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class)));
will(returnValue(bdfReader));
oneOf(bdfReader).readList();
will(returnValue(new BdfList()));
}});
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
}
@Test(expected = FormatException.class)
public void testThrowsFormatExceptionForDataAfterList()
throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class)));
will(returnValue(bdfReader));
oneOf(bdfReader).readList();
will(returnValue(BdfList.of(new Bytes(commitment))));
oneOf(bdfReader).eof();
will(returnValue(false));
}});
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
}
@Test(expected = FormatException.class)
public void testThrowsFormatExceptionForShortCommitment()
throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH - 1);
context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class)));
will(returnValue(bdfReader));
oneOf(bdfReader).readList();
will(returnValue(BdfList.of(new Bytes(commitment))));
oneOf(bdfReader).eof();
will(returnValue(true));
}});
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
}
@Test(expected = FormatException.class)
public void testThrowsFormatExceptionForLongCommitment()
throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH + 1);
context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class)));
will(returnValue(bdfReader));
oneOf(bdfReader).readList();
will(returnValue(BdfList.of(new Bytes(commitment))));
oneOf(bdfReader).eof();
will(returnValue(true));
}});
payloadParser.parse(new byte[] {PROTOCOL_VERSION});
}
@Test
public void testAcceptsPayloadWithNoDescriptors() throws Exception {
byte[] commitment = getRandomBytes(COMMIT_LENGTH);
context.checking(new Expectations() {{
oneOf(bdfReaderFactory).createReader(
with(any(ByteArrayInputStream.class)));
will(returnValue(bdfReader));
oneOf(bdfReader).readList();
will(returnValue(BdfList.of(new Bytes(commitment))));
oneOf(bdfReader).eof();
will(returnValue(true));
}});
Payload p = payloadParser.parse(new byte[] {PROTOCOL_VERSION});
assertArrayEquals(commitment, p.getCommitment());
assertTrue(p.getTransportDescriptors().isEmpty());
}
}

View File

@@ -10,9 +10,13 @@ import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.test.BrambleJavaIntegrationTestComponent;
import org.briarproject.bramble.test.BrambleTestCase;
import org.briarproject.bramble.test.DaggerBrambleJavaIntegrationTestComponent;
import org.junit.AfterClass;
import org.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameters;
import java.io.File;
import java.util.List;
@@ -29,13 +33,20 @@ import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
import static org.briarproject.bramble.test.TestUtils.isOptionalTestEnabled;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.junit.Assume.assumeTrue;
@RunWith(Parameterized.class)
public class BridgeTest extends BrambleTestCase {
private final static long TIMEOUT = SECONDS.toMillis(23);
@Parameters
public static Iterable<String> data() {
BrambleJavaIntegrationTestComponent component =
DaggerBrambleJavaIntegrationTestComponent.builder().build();
return component.getCircumventionProvider().getBridges();
}
private final static long TIMEOUT = SECONDS.toMillis(30);
private final static Logger LOG =
Logger.getLogger(BridgeTest.class.getName());
@@ -53,17 +64,23 @@ public class BridgeTest extends BrambleTestCase {
@Inject
Clock clock;
private List<String> bridges;
private LinuxTorPluginFactory factory;
private final static File torDir = getTestDirectory();
private final File torDir = getTestDirectory();
private final String bridge;
private volatile String currentBridge = null;
private LinuxTorPluginFactory factory;
public BridgeTest(String bridge) {
this.bridge = bridge;
}
@Before
public void setUp() {
// Skip this test unless it's explicitly enabled in the environment
assumeTrue(isOptionalTestEnabled(BridgeTest.class));
// TODO: Remove this assumption when the plugin supports other platforms
assumeTrue(OsUtils.isLinux());
BrambleJavaIntegrationTestComponent component =
DaggerBrambleJavaIntegrationTestComponent.builder().build();
component.inject(this);
@@ -72,7 +89,6 @@ public class BridgeTest extends BrambleTestCase {
LocationUtils locationUtils = () -> "US";
SocketFactory torSocketFactory = SocketFactory.getDefault();
bridges = circumventionProvider.getBridges();
CircumventionProvider bridgeProvider = new CircumventionProvider() {
@Override
public boolean isTorProbablyBlocked(String countryCode) {
@@ -86,7 +102,7 @@ public class BridgeTest extends BrambleTestCase {
@Override
public List<String> getBridges() {
return singletonList(currentBridge);
return singletonList(bridge);
}
};
factory = new LinuxTorPluginFactory(ioExecutor, networkManager,
@@ -94,25 +110,18 @@ public class BridgeTest extends BrambleTestCase {
resourceProvider, bridgeProvider, clock, torDir);
}
@AfterClass
public static void tearDown() {
@After
public void tearDown() {
deleteTestDirectory(torDir);
}
@Test
public void testBridges() throws Exception {
assertTrue(bridges.size() > 0);
for (String bridge : bridges) testBridge(bridge);
}
private void testBridge(String bridge) throws Exception {
DuplexPlugin duplexPlugin =
factory.createPlugin(new TorPluginCallBack());
assertNotNull(duplexPlugin);
LinuxTorPlugin plugin = (LinuxTorPlugin) duplexPlugin;
currentBridge = bridge;
LOG.warning("Testing " + bridge);
try {
plugin.start();

View File

@@ -4,6 +4,7 @@ import org.briarproject.bramble.BrambleJavaModule;
import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.plugin.PluginModule;
import org.briarproject.bramble.plugin.tor.BridgeTest;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.bramble.system.SystemModule;
import javax.inject.Singleton;
@@ -22,4 +23,5 @@ public interface BrambleJavaIntegrationTestComponent {
void inject(BridgeTest init);
CircumventionProvider getCircumventionProvider();
}

View File

@@ -22,8 +22,8 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 10101
versionName "1.1.1"
versionCode 10102
versionName "1.1.2"
applicationId "org.briarproject.briar.android"
buildConfigField "String", "GitHash",
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
@@ -104,7 +104,7 @@ dependencies {
}
implementation "com.android.support:cardview-v7:$supportVersion"
implementation "com.android.support:support-annotations:$supportVersion"
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation 'com.android.support.constraint:constraint-layout:1.1.3'
implementation('ch.acra:acra:4.9.1') {
exclude module: 'support-v4'
@@ -140,9 +140,6 @@ dependencies {
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.0.2"
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
androidTestImplementation 'junit:junit:4.12'
androidTestScreenshotImplementation project(path: ':bramble-api', configuration: 'testOutput')
androidTestScreenshotImplementation project(path: ':bramble-core', configuration: 'testOutput')
androidTestScreenshotImplementation "tools.fastlane:screengrab:1.2.0"
androidTestScreenshotImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.3"
}
@@ -156,8 +153,9 @@ task verifyTranslations {
lc.children().each { value -> translations.add(value.text()) }
def folders = ["default", "en-US"]
def exceptions = ["values-night", "values-v21", "values-ldrtl"]
project.file("src/main/res").eachDir { dir ->
if (dir.name.startsWith("values-") && !dir.name.endsWith("night") && !dir.name.endsWith("v21")) {
if (dir.name.startsWith("values-") && !exceptions.contains(dir.name)) {
folders.add(dir.name.substring(7).replace("-r", "-"))
}
}

View File

@@ -28,5 +28,3 @@
# Emoji
-keep class com.vanniktech.emoji.**
-keepclasseswithmembers public class android.support.v7.widget.RecyclerView { *; }

View File

@@ -13,4 +13,3 @@
-dontwarn junit.**
-dontwarn org.briarproject.briar.android.**
-dontwarn org.briarproject.bramble.**

View File

@@ -8,7 +8,7 @@ public class BriarTestComponentApplication extends BriarApplicationImpl {
@Override
protected AndroidComponent createApplicationComponent() {
AndroidComponent component = DaggerBriarUiTestComponent.builder()
.testAppModule(new TestAppModule(this)).build();
.appModule(new AppModule(this)).build();
// We need to load the eager singletons directly after making the
// dependency graphs
BrambleCoreModule.initEagerSingletons(component);

View File

@@ -21,8 +21,6 @@ public abstract class UiTest {
getTargetContext().getString(R.string.screenshot_alice);
protected static final String PASSWORD = "123456";
protected final BriarUiTestComponent alice;
@Inject
protected AccountManager accountManager;
@Inject
@@ -32,8 +30,7 @@ public abstract class UiTest {
BriarTestComponentApplication app =
(BriarTestComponentApplication) getTargetContext()
.getApplicationContext();
alice = (BriarUiTestComponent) app.getApplicationComponent();
inject(alice);
inject((BriarUiTestComponent) app.getApplicationComponent());
}
protected abstract void inject(BriarUiTestComponent component);

View File

@@ -13,13 +13,13 @@ import dagger.Component;
@Singleton
@Component(modules = {
TestAppModule.class,
AppModule.class,
BriarCoreModule.class,
BrambleAndroidModule.class,
BriarAccountModule.class,
BrambleCoreModule.class
})
public interface BriarUiTestComponent extends AndroidComponent, FakeDataTestComponent {
public interface BriarUiTestComponent extends AndroidComponent {
void inject(SetupDataTest test);

View File

@@ -1,95 +0,0 @@
package org.briarproject.briar.android;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.client.ClientModule;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.crypto.CryptoModule;
import org.briarproject.bramble.data.DataModule;
import org.briarproject.bramble.db.DatabaseModule;
import org.briarproject.bramble.event.EventModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.record.RecordModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.test.TestCryptoExecutorModule;
import org.briarproject.bramble.test.TestDatabaseModule;
import org.briarproject.bramble.test.TestSecureRandomModule;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.client.BriarClientModule;
import org.briarproject.briar.messaging.MessagingModule;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = {
TestCryptoExecutorModule.class,
TestDatabaseModule.class,
TestPluginConfigModule.class,
TestSecureRandomModule.class,
BriarClientModule.class,
ClientModule.class,
ContactModule.class,
CryptoModule.class,
DataModule.class,
DatabaseModule.class,
EventModule.class,
IdentityModule.class,
LifecycleModule.class,
MessagingModule.class,
RecordModule.class,
SyncModule.class,
SystemModule.class,
TransportModule.class,
VersioningModule.class
})
interface FakeDataTestComponent {
void inject(ContactModule.EagerSingletons init);
void inject(IdentityModule.EagerSingletons init);
void inject(LifecycleModule.EagerSingletons init);
void inject(MessagingModule.EagerSingletons init);
void inject(SyncModule.EagerSingletons init);
void inject(SystemModule.EagerSingletons init);
void inject(TransportModule.EagerSingletons init);
void inject(VersioningModule.EagerSingletons init);
LifecycleManager getLifecycleManager();
IdentityManager getIdentityManager();
ContactManager getContactManager();
MessagingManager getMessagingManager();
KeyManager getKeyManager();
PrivateMessageFactory getPrivateMessageFactory();
EventBus getEventBus();
StreamWriterFactory getStreamWriterFactory();
StreamReaderFactory getStreamReaderFactory();
SyncSessionFactory getSyncSessionFactory();
}

View File

@@ -5,59 +5,23 @@ import android.support.test.runner.AndroidJUnit4;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
import android.support.test.uiautomator.UiSelector;
import android.util.Log;
import org.briarproject.bramble.api.FormatException;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.crypto.SecretKey;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.identity.LocalAuthor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.SyncSession;
import org.briarproject.bramble.api.sync.SyncSessionFactory;
import org.briarproject.bramble.api.transport.KeyManager;
import org.briarproject.bramble.api.transport.StreamContext;
import org.briarproject.bramble.api.transport.StreamReaderFactory;
import org.briarproject.bramble.api.transport.StreamWriter;
import org.briarproject.bramble.api.transport.StreamWriterFactory;
import org.briarproject.bramble.contact.ContactModule;
import org.briarproject.bramble.identity.IdentityModule;
import org.briarproject.bramble.lifecycle.LifecycleModule;
import org.briarproject.bramble.sync.SyncModule;
import org.briarproject.bramble.system.SystemModule;
import org.briarproject.bramble.test.TestDatabaseModule;
import org.briarproject.bramble.test.TestUtils;
import org.briarproject.bramble.transport.TransportModule;
import org.briarproject.bramble.versioning.VersioningModule;
import org.briarproject.briar.R;
import org.briarproject.briar.android.login.OpenDatabaseActivity;
import org.briarproject.briar.android.login.SetupActivity;
import org.briarproject.briar.android.navdrawer.NavDrawerActivity;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.messaging.MessagingModule;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.InputStream;
import static android.content.Context.MODE_PRIVATE;
import static android.support.test.InstrumentationRegistry.getInstrumentation;
import static android.support.test.InstrumentationRegistry.getTargetContext;
import static android.support.test.espresso.Espresso.onView;
import static android.support.test.espresso.action.ViewActions.click;
import static android.support.test.espresso.action.ViewActions.typeText;
import static android.support.test.espresso.assertion.ViewAssertions.matches;
import static android.support.test.espresso.contrib.RecyclerViewActions.actionOnItemAtPosition;
import static android.support.test.espresso.intent.Intents.intended;
import static android.support.test.espresso.intent.matcher.IntentMatchers.hasComponent;
import static android.support.test.espresso.matcher.ViewMatchers.isDisplayed;
@@ -66,17 +30,9 @@ import static android.support.test.espresso.matcher.ViewMatchers.withId;
import static android.support.test.espresso.matcher.ViewMatchers.withText;
import static android.support.test.runner.lifecycle.Stage.PAUSED;
import static org.briarproject.bramble.api.plugin.LanTcpConstants.ID;
import static org.briarproject.bramble.api.transport.TransportConstants.TAG_LENGTH;
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
import static org.briarproject.briar.android.TestPluginConfigModule.MAX_LATENCY;
import static org.briarproject.briar.android.TestPluginConfigModule.TRANSPORT_ID;
import static org.briarproject.briar.android.ViewActions.waitForActivity;
import static org.briarproject.briar.android.ViewActions.waitUntilMatches;
import static org.briarproject.briar.android.util.UiUtils.needsDozeWhitelisting;
import static org.hamcrest.Matchers.allOf;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@RunWith(AndroidJUnit4.class)
@@ -92,49 +48,11 @@ public class SetupDataTest extends ScreenshotTest {
}
};
private FakeDataTestComponent bob;
private final File testDir =
getTargetContext().getDir("test", MODE_PRIVATE);
private final File bobDir = new File(testDir, "bob");
private final SecretKey master = getSecretKey();
@Override
protected void inject(BriarUiTestComponent component) {
component.inject(this);
}
@Before
public void setUp() {
Log.e("TEST", testDir.getAbsolutePath());
Log.e("TEST", "exists: " + testDir.exists());
assertTrue(testDir.mkdirs());
bob = DaggerFakeDataTestComponent.builder()
.testDatabaseModule(new TestDatabaseModule(bobDir)).build();
injectEagerSingletons(bob);
Log.e("TEST", "built bob");
}
@After
public void tearDown() throws Exception {
// Stop the lifecycle manager
LifecycleManager lifecycleManager = bob.getLifecycleManager();
lifecycleManager.stopServices();
lifecycleManager.waitForShutdown();
TestUtils.deleteTestDirectory(testDir);
}
private static void injectEagerSingletons(FakeDataTestComponent component) {
component.inject(new ContactModule.EagerSingletons());
component.inject(new IdentityModule.EagerSingletons());
component.inject(new LifecycleModule.EagerSingletons());
component.inject(new MessagingModule.EagerSingletons());
component.inject(new SyncModule.EagerSingletons());
component.inject(new SystemModule.EagerSingletons());
component.inject(new TransportModule.EagerSingletons());
component.inject(new VersioningModule.EagerSingletons());
}
@Test
public void createAccount() throws Exception {
// Enter username
@@ -185,132 +103,26 @@ public class SetupDataTest extends ScreenshotTest {
assertTrue(accountManager.hasDatabaseKey());
// WIP below
// wait for OpenDatabaseActivity to go away
onView(isRoot())
.perform(waitUntilMatches(
allOf(withId(R.id.progressBar), not(isDisplayed()))));
lifecycleManager.waitForStartup();
intended(hasComponent(NavDrawerActivity.class.getName()));
createTestData();
// close expiry warning
onView(withId(R.id.expiryWarning))
.perform(waitUntilMatches(isDisplayed()));
onView(withId(R.id.expiryWarningClose))
.check(matches(isDisplayed()));
onView(withId(R.id.expiryWarningClose))
.perform(click());
LocalAuthor aliceAuthor = alice.identityManager().getLocalAuthor();
LocalAuthor bobAuthor = bob.getIdentityManager().createLocalAuthor(
getTargetContext().getString(R.string.screenshot_bob));
bob.getIdentityManager().registerLocalAuthor(bobAuthor);
// Start the lifecycle manager
bob.getLifecycleManager().startServices(getSecretKey());
bob.getLifecycleManager().waitForStartup();
long timestamp = clock.currentTimeMillis();
// Bob adds Alice as a contact
ContactId aliceContactId = bob.getContactManager()
.addContact(aliceAuthor, bobAuthor.getId(), master,
timestamp, true, true, true);
// Alice adds Bob as a contact
ContactId bobContactId = alice.contactManager()
.addContact(bobAuthor, aliceAuthor.getId(), master,
timestamp, false, true, true);
// TODO figure out how many messages
read(alice, bobContactId, write(bob, aliceContactId));
read(bob, aliceContactId, write(alice, bobContactId));
read(alice, bobContactId, write(bob, aliceContactId));
read(bob, aliceContactId, write(alice, bobContactId));
read(alice, bobContactId, write(bob, aliceContactId));
read(bob, aliceContactId, write(alice, bobContactId));
read(alice, bobContactId, write(bob, aliceContactId));
read(bob, aliceContactId, write(alice, bobContactId));
read(alice, bobContactId, write(bob, aliceContactId));
read(bob, aliceContactId, write(alice, bobContactId));
read(alice, bobContactId, write(bob, aliceContactId));
read(bob, aliceContactId, write(alice, bobContactId));
read(alice, bobContactId, write(bob, aliceContactId));
sendMessage(bob, aliceContactId);
read(alice, bobContactId, write(bob, aliceContactId));
onView(isRoot())
.perform(waitUntilMatches(withText(bobAuthor.getName())));
onView(withId(R.id.recyclerView))
.perform(actionOnItemAtPosition(0, click()));
onView(isRoot())
.perform(waitUntilMatches(withText(R.string.screenshot_message_2)));
assertEquals(1,
alice.conversationManager().getGroupCount(bobContactId).getMsgCount());
Thread.sleep(5000);
}
private void sendMessage(FakeDataTestComponent device, ContactId contactId)
throws Exception {
// Send Bob a message
MessagingManager messagingManager = device.getMessagingManager();
GroupId groupId = messagingManager.getConversationId(contactId);
PrivateMessageFactory privateMessageFactory =
device.getPrivateMessageFactory();
PrivateMessage message = privateMessageFactory.createPrivateMessage(
groupId, getMinutesAgo(3),
getTargetContext().getString(R.string.screenshot_message_2));
messagingManager.addLocalMessage(message);
private void createTestData() {
try {
createTestDataExceptions();
} catch (DbException | FormatException e) {
throw new AssertionError(e);
}
}
private void read(FakeDataTestComponent device,
ContactId contactId, byte[] stream) throws Exception {
// Read and recognise the tag
ByteArrayInputStream in = new ByteArrayInputStream(stream);
byte[] tag = new byte[TAG_LENGTH];
int read = in.read(tag);
assertEquals(tag.length, read);
KeyManager keyManager = device.getKeyManager();
StreamContext ctx = keyManager.getStreamContext(TRANSPORT_ID, tag);
assertNotNull(ctx);
// Create a stream reader
StreamReaderFactory streamReaderFactory =
device.getStreamReaderFactory();
InputStream streamReader = streamReaderFactory.createStreamReader(
in, ctx);
// Create an incoming sync session
SyncSessionFactory syncSessionFactory = device.getSyncSessionFactory();
SyncSession session = syncSessionFactory.createIncomingSession(
contactId, streamReader);
// Read whatever needs to be read
session.run();
streamReader.close();
}
private byte[] write(FakeDataTestComponent device,
ContactId contactId) throws Exception {
ByteArrayOutputStream out = new ByteArrayOutputStream();
// Get a stream context
KeyManager keyManager = device.getKeyManager();
StreamContext ctx = keyManager.getStreamContext(contactId,
TRANSPORT_ID);
assertNotNull(ctx);
// Create a stream writer
StreamWriterFactory streamWriterFactory =
device.getStreamWriterFactory();
StreamWriter streamWriter =
streamWriterFactory.createStreamWriter(out, ctx);
// Create an outgoing sync session
SyncSessionFactory syncSessionFactory = device.getSyncSessionFactory();
SyncSession session = syncSessionFactory.createSimplexOutgoingSession(
contactId, MAX_LATENCY, streamWriter);
// Write whatever needs to be written
session.run();
streamWriter.sendEndOfStream();
// Return the contents of the stream
return out.toByteArray();
}
@Deprecated
private void createTestDataExceptions()
throws DbException, FormatException {
String bobName =

View File

@@ -1,161 +0,0 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.SharedPreferences;
import android.os.StrictMode;
import com.vanniktech.emoji.RecentEmoji;
import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.android.account.LockManagerImpl;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.android.DozeWatchdog;
import org.briarproject.briar.api.android.LockManager;
import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.File;
import java.security.GeneralSecurityException;
import javax.inject.Inject;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import static android.content.Context.MODE_PRIVATE;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
@Module(includes = TestPluginConfigModule.class)
public class TestAppModule {
static class EagerSingletons {
@Inject
AndroidNotificationManager androidNotificationManager;
@Inject
NetworkUsageLogger networkUsageLogger;
@Inject
DozeWatchdog dozeWatchdog;
@Inject
RecentEmoji recentEmoji;
}
private final Application application;
public TestAppModule(Application application) {
this.application = application;
}
@Provides
@Singleton
Application providesApplication() {
return application;
}
@Provides
@Singleton
DatabaseConfig provideDatabaseConfig(Application app) {
//FIXME: StrictMode
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskReads();
StrictMode.allowThreadDiskWrites();
File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
StrictMode.setThreadPolicy(tp);
return new AndroidDatabaseConfig(dbDir, keyDir);
}
@Provides
@Singleton
DevConfig provideDevConfig(Application app, CryptoComponent crypto) {
@NotNullByDefault
DevConfig devConfig = new DevConfig() {
@Override
public PublicKey getDevPublicKey() {
try {
return crypto.getMessageKeyParser().parsePublicKey(
StringUtils.fromHexString(DEV_PUBLIC_KEY_HEX));
} catch (GeneralSecurityException e) {
throw new RuntimeException(e);
}
}
@Override
public String getDevOnionAddress() {
return DEV_ONION_ADDRESS;
}
@Override
public File getReportDir() {
return AndroidUtils.getReportDir(app.getApplicationContext());
}
};
return devConfig;
}
@Provides
SharedPreferences provideSharedPreferences(Application app) {
// FIXME unify this with getDefaultSharedPreferences()
return app.getSharedPreferences("db", MODE_PRIVATE);
}
@Provides
@Singleton
AndroidNotificationManager provideAndroidNotificationManager(
LifecycleManager lifecycleManager, EventBus eventBus,
AndroidNotificationManagerImpl notificationManager) {
lifecycleManager.registerService(notificationManager);
eventBus.addListener(notificationManager);
return notificationManager;
}
@Provides
@Singleton
ScreenFilterMonitor provideScreenFilterMonitor(
LifecycleManager lifecycleManager,
ScreenFilterMonitorImpl screenFilterMonitor) {
lifecycleManager.registerService(screenFilterMonitor);
return screenFilterMonitor;
}
@Provides
NetworkUsageLogger provideNetworkUsageLogger(
LifecycleManager lifecycleManager) {
NetworkUsageLogger networkUsageLogger = new NetworkUsageLogger();
lifecycleManager.registerService(networkUsageLogger);
return networkUsageLogger;
}
@Provides
@Singleton
DozeWatchdog provideDozeWatchdog(LifecycleManager lifecycleManager) {
DozeWatchdogImpl dozeWatchdog = new DozeWatchdogImpl(application);
lifecycleManager.registerService(dozeWatchdog);
return dozeWatchdog;
}
@Provides
@Singleton
LockManager provideLockManager(LifecycleManager lifecycleManager,
EventBus eventBus, LockManagerImpl lockManager) {
lifecycleManager.registerService(lockManager);
eventBus.addListener(lockManager);
return lockManager;
}
@Provides
@Singleton
RecentEmoji provideRecentEmoji(LifecycleManager lifecycleManager,
RecentEmojiImpl recentEmoji) {
lifecycleManager.registerClient(recentEmoji);
return recentEmoji;
}
}

View File

@@ -1,71 +0,0 @@
package org.briarproject.briar.android;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.TransportId;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPlugin;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginCallback;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import java.util.Collection;
import javax.annotation.Nullable;
import dagger.Module;
import dagger.Provides;
import static java.util.Collections.emptyList;
import static java.util.Collections.singletonList;
import static org.briarproject.bramble.test.TestUtils.getTransportId;
@Module
public class TestPluginConfigModule {
public static final TransportId TRANSPORT_ID = getTransportId();
public static final int MAX_LATENCY = 2 * 60 * 1000; // 2 minutes
@NotNullByDefault
private final SimplexPluginFactory simplex = new SimplexPluginFactory() {
@Override
public TransportId getId() {
return TRANSPORT_ID;
}
@Override
public int getMaxLatency() {
return MAX_LATENCY;
}
@Override
@Nullable
public SimplexPlugin createPlugin(SimplexPluginCallback callback) {
return null;
}
};
@Provides
PluginConfig providePluginConfig() {
@NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() {
@Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return emptyList();
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return singletonList(simplex);
}
@Override
public boolean shouldPoll() {
return false;
}
};
return pluginConfig;
}
}

View File

@@ -13,9 +13,10 @@
<uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.USE_FINGERPRINT"/>
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission-sdk-23 android:name="android.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS"/>
<uses-permission-sdk-23 android:name="android.permission.USE_BIOMETRIC" />
@@ -25,6 +26,7 @@
android:icon="@mipmap/ic_launcher_round"
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/BriarTheme">
<receiver

View File

@@ -41,13 +41,9 @@ import org.briarproject.briar.android.util.BriarNotificationBuilder;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.forum.event.ForumPostReceivedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionSucceededEvent;
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
import org.briarproject.briar.api.privategroup.event.GroupMessageAddedEvent;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
import java.util.Set;
import java.util.concurrent.Callable;
@@ -235,19 +231,6 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
} else if (e instanceof BlogPostAddedEvent) {
BlogPostAddedEvent b = (BlogPostAddedEvent) e;
showBlogPostNotification(b.getGroupId());
} else if (e instanceof IntroductionRequestReceivedEvent) {
ContactId c = ((IntroductionRequestReceivedEvent) e).getContactId();
showContactNotification(c);
} else if (e instanceof IntroductionResponseReceivedEvent) {
ContactId c =
((IntroductionResponseReceivedEvent) e).getContactId();
showContactNotification(c);
} else if (e instanceof InvitationRequestReceivedEvent) {
ContactId c = ((InvitationRequestReceivedEvent) e).getContactId();
showContactNotification(c);
} else if (e instanceof InvitationResponseReceivedEvent) {
ContactId c = ((InvitationResponseReceivedEvent) e).getContactId();
showContactNotification(c);
} else if (e instanceof IntroductionSucceededEvent) {
showIntroductionNotification();
}
@@ -327,9 +310,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.private_message_notification_text, contactTotal,
contactTotal));
b.setNumber(contactTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
b.setNotificationCategory(CATEGORY_MESSAGE);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, CONTACT_URI);
Set<ContactId> contacts = contactCounts.keySet();
@@ -431,9 +412,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.group_message_notification_text, groupTotal,
groupTotal));
b.setNumber(groupTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
b.setNotificationCategory(CATEGORY_SOCIAL);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, GROUP_URI);
Set<GroupId> groups = groupCounts.keySet();
@@ -504,9 +483,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.forum_post_notification_text, forumTotal,
forumTotal));
b.setNumber(forumTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
b.setNotificationCategory(CATEGORY_SOCIAL);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, FORUM_URI);
Set<GroupId> forums = forumCounts.keySet();
@@ -575,9 +552,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.blog_post_notification_text, blogTotal,
blogTotal));
b.setNumber(blogTotal);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
b.setNotificationCategory(CATEGORY_SOCIAL);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, BLOG_URI);
// Touching the notification shows the combined blog feed
@@ -618,9 +593,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.introduction_notification_text, introductionTotal,
introductionTotal));
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
b.setNotificationCategory(CATEGORY_MESSAGE);
setAlertProperties(b);
setDeleteIntent(b, INTRODUCTION_URI);
// Touching the notification shows the contact list

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.StrictMode;
@@ -10,9 +11,24 @@ import org.briarproject.bramble.api.crypto.CryptoComponent;
import org.briarproject.bramble.api.crypto.PublicKey;
import org.briarproject.bramble.api.db.DatabaseConfig;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.reporting.DevConfig;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import org.briarproject.bramble.util.AndroidUtils;
import org.briarproject.bramble.util.StringUtils;
import org.briarproject.briar.android.account.LockManagerImpl;
@@ -23,18 +39,25 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
import java.io.File;
import java.security.GeneralSecurityException;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
import static android.content.Context.MODE_PRIVATE;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_ONION_ADDRESS;
import static org.briarproject.bramble.api.reporting.ReportingConstants.DEV_PUBLIC_KEY_HEX;
@Module(includes = PluginConfigModule.class)
@Module
public class AppModule {
static class EagerSingletons {
@@ -72,6 +95,47 @@ public class AppModule {
return new AndroidDatabaseConfig(dbDir, keyDir);
}
@Provides
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, SecureRandom random,
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
Application app, NetworkManager networkManager,
LocationUtils locationUtils, EventBus eventBus,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, Clock clock) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
scheduler, appContext, networkManager, locationUtils, eventBus,
torSocketFactory, backoffFactory, resourceProvider,
circumventionProvider, clock);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
eventBus, backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex = asList(bluetooth, tor, lan);
@NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() {
@Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex;
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return emptyList();
}
@Override
public boolean shouldPoll() {
return true;
}
};
return pluginConfig;
}
@Provides
@Singleton
DevConfig provideDevConfig(Application app, CryptoComponent crypto) {

View File

@@ -1,81 +0,0 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.Context;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.lifecycle.IoExecutor;
import org.briarproject.bramble.api.network.NetworkManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.plugin.BackoffFactory;
import org.briarproject.bramble.api.plugin.PluginConfig;
import org.briarproject.bramble.api.plugin.duplex.DuplexPluginFactory;
import org.briarproject.bramble.api.plugin.simplex.SimplexPluginFactory;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.bramble.api.system.LocationUtils;
import org.briarproject.bramble.api.system.ResourceProvider;
import org.briarproject.bramble.api.system.Scheduler;
import org.briarproject.bramble.plugin.bluetooth.AndroidBluetoothPluginFactory;
import org.briarproject.bramble.plugin.tcp.AndroidLanTcpPluginFactory;
import org.briarproject.bramble.plugin.tor.AndroidTorPluginFactory;
import org.briarproject.bramble.plugin.tor.CircumventionProvider;
import java.security.SecureRandom;
import java.util.Collection;
import java.util.concurrent.Executor;
import java.util.concurrent.ScheduledExecutorService;
import javax.net.SocketFactory;
import dagger.Module;
import dagger.Provides;
import static java.util.Arrays.asList;
import static java.util.Collections.emptyList;
@Module
public class PluginConfigModule {
@Provides
PluginConfig providePluginConfig(@IoExecutor Executor ioExecutor,
@Scheduler ScheduledExecutorService scheduler,
AndroidExecutor androidExecutor, SecureRandom random,
SocketFactory torSocketFactory, BackoffFactory backoffFactory,
Application app, NetworkManager networkManager,
LocationUtils locationUtils, EventBus eventBus,
ResourceProvider resourceProvider,
CircumventionProvider circumventionProvider, Clock clock) {
Context appContext = app.getApplicationContext();
DuplexPluginFactory bluetooth =
new AndroidBluetoothPluginFactory(ioExecutor, androidExecutor,
appContext, random, eventBus, backoffFactory);
DuplexPluginFactory tor = new AndroidTorPluginFactory(ioExecutor,
scheduler, appContext, networkManager, locationUtils, eventBus,
torSocketFactory, backoffFactory, resourceProvider,
circumventionProvider, clock);
DuplexPluginFactory lan = new AndroidLanTcpPluginFactory(ioExecutor,
eventBus, backoffFactory, appContext);
Collection<DuplexPluginFactory> duplex = asList(bluetooth, tor, lan);
@NotNullByDefault
PluginConfig pluginConfig = new PluginConfig() {
@Override
public Collection<DuplexPluginFactory> getDuplexFactories() {
return duplex;
}
@Override
public Collection<SimplexPluginFactory> getSimplexFactories() {
return emptyList();
}
@Override
public boolean shouldPoll() {
return true;
}
};
return pluginConfig;
}
}

View File

@@ -101,6 +101,8 @@ public class LockManagerImpl implements LockManager, Service, EventListener {
@Override
public void stopService() {
timeoutMinutes = timeoutNever;
if (alarmSet) alarmManager.cancel(lockIntent);
}
@UiThread

View File

@@ -21,11 +21,11 @@ import org.briarproject.briar.android.controller.ActivityLifecycleController;
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.blog.Blog;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.blog.BlogManager;
import org.briarproject.briar.api.blog.BlogSharingManager;
import org.briarproject.briar.api.blog.event.BlogInvitationResponseReceivedEvent;
import org.briarproject.briar.api.blog.event.BlogPostAddedEvent;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.event.ContactLeftShareableEvent;
import java.util.ArrayList;
@@ -107,7 +107,7 @@ class BlogControllerImpl extends BaseControllerImpl
} else if (e instanceof BlogInvitationResponseReceivedEvent) {
BlogInvitationResponseReceivedEvent b =
(BlogInvitationResponseReceivedEvent) e;
InvitationResponse r = b.getResponse();
BlogInvitationResponse r = b.getMessageHeader();
if (r.getShareableId().equals(groupId) && r.wasAccepted()) {
LOG.info("Blog invitation accepted");
onBlogInvitationAccepted(b.getContactId());

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android.contact;
import android.content.Intent;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
@@ -36,19 +35,10 @@ import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.keyagreement.ContactExchangeActivity;
import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.BaseMessageHeader;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
import org.briarproject.briar.api.messaging.ConversationManager;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
import org.briarproject.briar.api.sharing.InvitationRequest;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
import java.util.ArrayList;
import java.util.List;
@@ -57,6 +47,7 @@ import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static android.support.v4.view.ViewCompat.getTransitionName;
import static java.util.logging.Level.WARNING;
@@ -122,7 +113,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
ContactId contactId = item.getContact().getId();
i.putExtra(CONTACT_ID, contactId.getInt());
if (Build.VERSION.SDK_INT >= 23) {
if (SDK_INT >= 23) {
ContactListItemViewHolder holder =
(ContactListItemViewHolder) list
.getRecyclerView()
@@ -256,41 +247,16 @@ public class ContactListFragment extends BaseFragment implements EventListener {
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
PrivateMessageHeader h = p.getMessageHeader();
updateItem(p.getContactId(), h);
} else if (e instanceof IntroductionRequestReceivedEvent) {
LOG.info("Introduction request received, updating item");
IntroductionRequestReceivedEvent m =
(IntroductionRequestReceivedEvent) e;
IntroductionRequest ir = m.getIntroductionRequest();
updateItem(m.getContactId(), ir);
} else if (e instanceof IntroductionResponseReceivedEvent) {
LOG.info("Introduction response received, updating item");
IntroductionResponseReceivedEvent m =
(IntroductionResponseReceivedEvent) e;
IntroductionResponse ir = m.getIntroductionResponse();
updateItem(m.getContactId(), ir);
} else if (e instanceof InvitationRequestReceivedEvent) {
LOG.info("Invitation Request received, update item");
InvitationRequestReceivedEvent m =
(InvitationRequestReceivedEvent) e;
InvitationRequest ir = m.getRequest();
updateItem(m.getContactId(), ir);
} else if (e instanceof InvitationResponseReceivedEvent) {
LOG.info("Invitation response received, updating item");
InvitationResponseReceivedEvent m =
(InvitationResponseReceivedEvent) e;
InvitationResponse ir = m.getResponse();
updateItem(m.getContactId(), ir);
}
}
private void updateItem(ContactId c, BaseMessageHeader h) {
private void updateItem(ContactId c, PrivateMessageHeader h) {
runOnUiThreadUnlessDestroyed(() -> {
adapter.incrementRevision();
int position = adapter.findItemPosition(c);
ContactListItem item = adapter.getItemAt(position);
if (item != null) {
ConversationItem i = ConversationItem.from(getContext(), h);
item.addMessage(i);
item.addMessage(h);
adapter.updateItemAt(position, item);
}
});

View File

@@ -3,6 +3,7 @@ package org.briarproject.briar.android.contact;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import javax.annotation.concurrent.NotThreadSafe;
@@ -22,11 +23,10 @@ public class ContactListItem extends ContactItem {
this.timestamp = count.getLatestMsgTime();
}
void addMessage(ConversationItem message) {
void addMessage(PrivateMessageHeader h) {
empty = false;
if (message.getTime() > timestamp) timestamp = message.getTime();
if (!message.isRead())
unread++;
if (h.getTimestamp() > timestamp) timestamp = h.getTimestamp();
if (!h.isRead()) unread++;
}
boolean isEmpty() {

View File

@@ -1,8 +1,11 @@
package org.briarproject.briar.android.contact;
import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.Observer;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.annotation.UiThread;
import android.support.design.widget.Snackbar;
import android.support.v4.content.ContextCompat;
@@ -50,6 +53,7 @@ import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.blog.BlogActivity;
import org.briarproject.briar.android.contact.ConversationAdapter.ConversationListener;
import org.briarproject.briar.android.contact.ConversationVisitor.BodyCache;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.introduction.IntroductionActivity;
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
@@ -62,24 +66,15 @@ import org.briarproject.briar.api.client.ProtocolStateException;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.forum.ForumSharingManager;
import org.briarproject.briar.api.introduction.IntroductionManager;
import org.briarproject.briar.api.introduction.IntroductionMessage;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.introduction.event.IntroductionRequestReceivedEvent;
import org.briarproject.briar.api.introduction.event.IntroductionResponseReceivedEvent;
import org.briarproject.briar.api.messaging.ConversationManager;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageFactory;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.PrivateRequest;
import org.briarproject.briar.api.messaging.PrivateResponse;
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationManager;
import org.briarproject.briar.api.sharing.InvitationMessage;
import org.briarproject.briar.api.sharing.InvitationRequest;
import org.briarproject.briar.api.sharing.InvitationResponse;
import org.briarproject.briar.api.sharing.event.InvitationRequestReceivedEvent;
import org.briarproject.briar.api.sharing.event.InvitationResponseReceivedEvent;
import org.thoughtcrime.securesms.components.util.FutureTaskListener;
import org.thoughtcrime.securesms.components.util.ListenableFutureTask;
import java.util.ArrayList;
import java.util.Collection;
@@ -87,13 +82,10 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Callable;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import de.hdodenhof.circleimageview.CircleImageView;
@@ -120,7 +112,8 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ConversationActivity extends BriarActivity
implements EventListener, ConversationListener, TextInputListener {
implements EventListener, ConversationListener, TextInputListener,
BodyCache {
public static final String CONTACT_ID = "briar.CONTACT_ID";
@@ -138,7 +131,9 @@ public class ConversationActivity extends BriarActivity
Executor cryptoExecutor;
private final Map<MessageId, String> bodyCache = new ConcurrentHashMap<>();
private final MutableLiveData<String> contactName = new MutableLiveData<>();
private ConversationVisitor visitor;
private ConversationAdapter adapter;
private Toolbar toolbar;
private CircleImageView toolbarAvatar;
@@ -147,24 +142,14 @@ public class ConversationActivity extends BriarActivity
private BriarRecyclerView list;
private TextInputView textInputView;
private final ListenableFutureTask<String> contactNameTask =
new ListenableFutureTask<>(new Callable<String>() {
@Override
public String call() throws Exception {
Contact c = contactManager.getContact(contactId);
contactName = c.getAuthor().getName();
return c.getAuthor().getName();
}
});
private final AtomicBoolean contactNameTaskStarted =
new AtomicBoolean(false);
// Fields that are accessed from background threads must be volatile
@Inject
volatile ContactManager contactManager;
@Inject
volatile MessagingManager messagingManager;
@Inject
volatile ConversationManager conversationManager;
@Inject
volatile EventBus eventBus;
@Inject
volatile SettingsManager settingsManager;
@@ -181,8 +166,6 @@ public class ConversationActivity extends BriarActivity
private volatile ContactId contactId;
@Nullable
private volatile String contactName;
@Nullable
private volatile AuthorId contactAuthorId;
@Nullable
private volatile GroupId messagingGroupId;
@@ -211,6 +194,7 @@ public class ConversationActivity extends BriarActivity
setTransitionName(toolbarAvatar, getAvatarTransitionName(contactId));
setTransitionName(toolbarStatus, getBulbTransitionName(contactId));
visitor = new ConversationVisitor(this, this, contactName);
adapter = new ConversationAdapter(this, this);
list = findViewById(R.id.conversationView);
list.setLayoutManager(new LinearLayoutManager(this));
@@ -294,9 +278,9 @@ public class ConversationActivity extends BriarActivity
runOnDbThread(() -> {
try {
long start = now();
if (contactName == null || contactAuthorId == null) {
if (contactAuthorId == null) {
Contact contact = contactManager.getContact(contactId);
contactName = contact.getAuthor().getName();
contactName.postValue(contact.getAuthor().getName());
contactAuthorId = contact.getAuthor().getId();
}
logDuration(LOG, "Loading contact", start);
@@ -310,12 +294,13 @@ public class ConversationActivity extends BriarActivity
});
}
// contactAuthorId and contactName are expected to be set
private void displayContactDetails() {
runOnUiThreadUnlessDestroyed(() -> {
//noinspection ConstantConditions
toolbarAvatar.setImageDrawable(
new IdenticonDrawable(contactAuthorId.getBytes()));
toolbarTitle.setText(contactName);
toolbarTitle.setText(contactName.getValue());
});
}
@@ -343,23 +328,9 @@ public class ConversationActivity extends BriarActivity
try {
long start = now();
Collection<PrivateMessageHeader> headers =
messagingManager.getMessageHeaders(contactId);
Collection<IntroductionMessage> introductions =
introductionManager.getIntroductionMessages(contactId);
Collection<InvitationMessage> forumInvitations =
forumSharingManager.getInvitationMessages(contactId);
Collection<InvitationMessage> blogInvitations =
blogSharingManager.getInvitationMessages(contactId);
Collection<InvitationMessage> groupInvitations =
groupInvitationManager.getInvitationMessages(contactId);
List<InvitationMessage> invitations = new ArrayList<>(
forumInvitations.size() + blogInvitations.size() +
groupInvitations.size());
invitations.addAll(forumInvitations);
invitations.addAll(blogInvitations);
invitations.addAll(groupInvitations);
conversationManager.getMessageHeaders(contactId);
logDuration(LOG, "Loading messages", start);
displayMessages(revision, headers, introductions, invitations);
displayMessages(revision, headers);
} catch (NoSuchContactException e) {
finishOnUiThread();
} catch (DbException e) {
@@ -369,15 +340,12 @@ public class ConversationActivity extends BriarActivity
}
private void displayMessages(int revision,
Collection<PrivateMessageHeader> headers,
Collection<IntroductionMessage> introductions,
Collection<InvitationMessage> invitations) {
Collection<PrivateMessageHeader> headers) {
runOnUiThreadUnlessDestroyed(() -> {
if (revision == adapter.getRevision()) {
adapter.incrementRevision();
textInputView.setSendButtonEnabled(true);
List<ConversationItem> items = createItems(headers,
introductions, invitations);
List<ConversationItem> items = createItems(headers);
adapter.addAll(items);
list.showData();
// Scroll to the bottom
@@ -396,41 +364,9 @@ public class ConversationActivity extends BriarActivity
*/
@SuppressWarnings("ConstantConditions")
private List<ConversationItem> createItems(
Collection<PrivateMessageHeader> headers,
Collection<IntroductionMessage> introductions,
Collection<InvitationMessage> invitations) {
int size =
headers.size() + introductions.size() + invitations.size();
List<ConversationItem> items = new ArrayList<>(size);
for (PrivateMessageHeader h : headers) {
ConversationItem item = ConversationItem.from(h);
String body = bodyCache.get(h.getId());
if (body == null) loadMessageBody(h.getId());
else item.setBody(body);
items.add(item);
}
for (IntroductionMessage m : introductions) {
ConversationItem item;
if (m instanceof IntroductionRequest) {
IntroductionRequest i = (IntroductionRequest) m;
item = ConversationItem.from(this, contactName, i);
} else {
IntroductionResponse i = (IntroductionResponse) m;
item = ConversationItem.from(this, contactName, i);
}
items.add(item);
}
for (InvitationMessage i : invitations) {
ConversationItem item;
if (i instanceof InvitationRequest) {
InvitationRequest r = (InvitationRequest) i;
item = ConversationItem.from(this, contactName, r);
} else {
InvitationResponse r = (InvitationResponse) i;
item = ConversationItem.from(this, contactName, r);
}
items.add(item);
}
Collection<PrivateMessageHeader> headers) {
List<ConversationItem> items = new ArrayList<>(headers.size());
for (PrivateMessageHeader h : headers) items.add(h.accept(visitor));
return items;
}
@@ -476,9 +412,7 @@ public class ConversationActivity extends BriarActivity
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
if (p.getContactId().equals(contactId)) {
LOG.info("Message received, adding");
PrivateMessageHeader h = p.getMessageHeader();
addConversationItem(ConversationItem.from(h));
loadMessageBody(h.getId());
onNewPrivateMessage(p.getMessageHeader());
}
} else if (e instanceof MessagesSentEvent) {
MessagesSentEvent m = (MessagesSentEvent) e;
@@ -504,38 +438,6 @@ public class ConversationActivity extends BriarActivity
LOG.info("Contact disconnected");
displayContactOnlineStatus();
}
} else if (e instanceof IntroductionRequestReceivedEvent) {
IntroductionRequestReceivedEvent event =
(IntroductionRequestReceivedEvent) e;
if (event.getContactId().equals(contactId)) {
LOG.info("Introduction request received, adding...");
IntroductionRequest ir = event.getIntroductionRequest();
handleIntroductionRequest(ir);
}
} else if (e instanceof IntroductionResponseReceivedEvent) {
IntroductionResponseReceivedEvent event =
(IntroductionResponseReceivedEvent) e;
if (event.getContactId().equals(contactId)) {
LOG.info("Introduction response received, adding...");
IntroductionResponse ir = event.getIntroductionResponse();
handleIntroductionResponse(ir);
}
} else if (e instanceof InvitationRequestReceivedEvent) {
InvitationRequestReceivedEvent event =
(InvitationRequestReceivedEvent) e;
if (event.getContactId().equals(contactId)) {
LOG.info("Invitation received, adding...");
InvitationRequest ir = event.getRequest();
handleInvitationRequest(ir);
}
} else if (e instanceof InvitationResponseReceivedEvent) {
InvitationResponseReceivedEvent event =
(InvitationResponseReceivedEvent) e;
if (event.getContactId().equals(contactId)) {
LOG.info("Invitation response received, adding...");
InvitationResponse ir = event.getResponse();
handleInvitationResponse(ir);
}
}
}
@@ -548,84 +450,33 @@ public class ConversationActivity extends BriarActivity
});
}
private void handleIntroductionRequest(IntroductionRequest m) {
getContactNameTask().addListener(new FutureTaskListener<String>() {
@Override
public void onSuccess(String contactName) {
runOnUiThreadUnlessDestroyed(() -> {
ConversationItem item = ConversationItem
.from(ConversationActivity.this, contactName, m);
addConversationItem(item);
});
}
@Override
public void onFailure(Throwable exception) {
runOnUiThreadUnlessDestroyed(
() -> handleDbException((DbException) exception));
private void onNewPrivateMessage(PrivateMessageHeader h) {
runOnUiThreadUnlessDestroyed(() -> {
if (h instanceof PrivateRequest || h instanceof PrivateResponse) {
String cName = contactName.getValue();
if (cName == null) {
// Wait for the contact name to be loaded
contactName.observe(this, new Observer<String>() {
@Override
public void onChanged(@Nullable String cName) {
if (cName != null) {
addConversationItem(h.accept(visitor));
contactName.removeObserver(this);
}
}
});
} else {
addConversationItem(h.accept(visitor));
}
} else {
addConversationItem(h.accept(visitor));
loadMessageBody(h.getId());
}
});
}
private void handleIntroductionResponse(IntroductionResponse m) {
getContactNameTask().addListener(new FutureTaskListener<String>() {
@Override
public void onSuccess(String contactName) {
runOnUiThreadUnlessDestroyed(() -> {
ConversationItem item = ConversationItem
.from(ConversationActivity.this, contactName, m);
addConversationItem(item);
});
}
@Override
public void onFailure(Throwable exception) {
runOnUiThreadUnlessDestroyed(
() -> handleDbException((DbException) exception));
}
});
}
private void handleInvitationRequest(InvitationRequest m) {
getContactNameTask().addListener(new FutureTaskListener<String>() {
@Override
public void onSuccess(String contactName) {
runOnUiThreadUnlessDestroyed(() -> {
ConversationItem item = ConversationItem
.from(ConversationActivity.this, contactName, m);
addConversationItem(item);
});
}
@Override
public void onFailure(Throwable exception) {
runOnUiThreadUnlessDestroyed(
() -> handleDbException((DbException) exception));
}
});
}
private void handleInvitationResponse(InvitationResponse m) {
getContactNameTask().addListener(new FutureTaskListener<String>() {
@Override
public void onSuccess(String contactName) {
runOnUiThreadUnlessDestroyed(() -> {
ConversationItem item = ConversationItem
.from(ConversationActivity.this, contactName, m);
addConversationItem(item);
});
}
@Override
public void onFailure(Throwable exception) {
runOnUiThreadUnlessDestroyed(
() -> handleDbException((DbException) exception));
}
});
}
private void markMessages(Collection<MessageId> messageIds,
boolean sent, boolean seen) {
private void markMessages(Collection<MessageId> messageIds, boolean sent,
boolean seen) {
runOnUiThreadUnlessDestroyed(() -> {
adapter.incrementRevision();
Set<MessageId> messages = new HashSet<>(messageIds);
@@ -678,7 +529,8 @@ public class ConversationActivity extends BriarActivity
//noinspection ConstantConditions init in loadGroupId()
storeMessage(privateMessageFactory.createPrivateMessage(
messagingGroupId, timestamp, body), body);
} catch (FormatException e) {throw new RuntimeException(e);
} catch (FormatException e) {
throw new RuntimeException(e);
}
});
}
@@ -693,10 +545,8 @@ public class ConversationActivity extends BriarActivity
PrivateMessageHeader h = new PrivateMessageHeader(
message.getId(), message.getGroupId(),
message.getTimestamp(), true, false, false, false);
ConversationItem item = ConversationItem.from(h);
item.setBody(body);
bodyCache.put(message.getId(), body);
addConversationItem(item);
addConversationItem(h.accept(visitor));
} catch (DbException e) {
logException(LOG, WARNING, e);
}
@@ -823,7 +673,7 @@ public class ConversationActivity extends BriarActivity
@UiThread
@Override
public void respondToRequest(ConversationRequestItem item, boolean accept) {
item.setAnswered(true);
item.setAnswered();
int position = adapter.findItemPosition(item);
if (position != INVALID_POSITION) {
adapter.notifyItemChanged(position, item);
@@ -909,10 +759,11 @@ public class ConversationActivity extends BriarActivity
groupInvitationManager.respondToInvitation(contactId, id, accept);
}
private ListenableFutureTask<String> getContactNameTask() {
if (!contactNameTaskStarted.getAndSet(true))
runOnDbThread(contactNameTask);
return contactNameTask;
@Nullable
@Override
public String getBody(MessageId m) {
String body = bodyCache.get(m);
if (body == null) loadMessageBody(m);
return body;
}
}

View File

@@ -1,40 +1,20 @@
package org.briarproject.briar.android.contact;
import android.content.Context;
import android.support.annotation.LayoutRes;
import android.support.annotation.StringRes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.android.contact.ConversationRequestItem.RequestType;
import org.briarproject.briar.api.blog.BlogInvitationRequest;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.client.BaseMessageHeader;
import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
import org.briarproject.briar.api.sharing.InvitationRequest;
import org.briarproject.briar.api.sharing.InvitationResponse;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.BLOG;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.FORUM;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.GROUP;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
@NotThreadSafe
@NotNullByDefault
abstract class ConversationItem {
protected @Nullable String body;
@Nullable
protected String body;
private final MessageId id;
private final GroupId groupId;
private final long time;
@@ -78,216 +58,4 @@ abstract class ConversationItem {
@LayoutRes
abstract public int getLayout();
static ConversationItem from(PrivateMessageHeader h) {
if (h.isLocal()) {
return new ConversationMessageOutItem(h);
} else {
return new ConversationMessageInItem(h);
}
}
static ConversationItem from(Context ctx, String contactName,
IntroductionRequest ir) {
if (ir.isLocal()) {
String text = ctx.getString(R.string.introduction_request_sent,
contactName, ir.getName());
return new ConversationNoticeOutItem(ir.getMessageId(),
ir.getGroupId(), text, ir.getMessage(), ir.getTimestamp(),
ir.isSent(), ir.isSeen());
} else {
String text;
if (ir.wasAnswered()) {
text = ctx.getString(
R.string.introduction_request_answered_received,
contactName, ir.getName());
} else if (ir.contactExists()){
text = ctx.getString(
R.string.introduction_request_exists_received,
contactName, ir.getName());
} else {
text = ctx.getString(R.string.introduction_request_received,
contactName, ir.getName());
}
return new ConversationRequestItem(ir.getMessageId(),
ir.getGroupId(), INTRODUCTION, ir.getSessionId(), text,
ir.getMessage(), ir.getTimestamp(), ir.isRead(), null,
ir.wasAnswered(), false);
}
}
static ConversationItem from(Context ctx, String contactName,
IntroductionResponse ir) {
if (ir.isLocal()) {
String text;
if (ir.wasAccepted()) {
text = ctx.getString(
R.string.introduction_response_accepted_sent,
ir.getName());
text += "\n\n" + ctx.getString(
R.string.introduction_response_accepted_sent_info,
ir.getName());
} else {
text = ctx.getString(
R.string.introduction_response_declined_sent,
ir.getName());
}
return new ConversationNoticeOutItem(ir.getMessageId(),
ir.getGroupId(), text, null, ir.getTimestamp(), ir.isSent(),
ir.isSeen());
} else {
String text;
if (ir.wasAccepted()) {
text = ctx.getString(
R.string.introduction_response_accepted_received,
contactName, ir.getName());
} else {
if (ir.isIntroducer()) {
text = ctx.getString(
R.string.introduction_response_declined_received,
contactName, ir.getName());
} else {
text = ctx.getString(
R.string.introduction_response_declined_received_by_introducee,
contactName, ir.getName());
}
}
return new ConversationNoticeInItem(ir.getMessageId(),
ir.getGroupId(), text, null, ir.getTimestamp(),
ir.isRead());
}
}
static ConversationItem from(Context ctx, String contactName,
InvitationRequest ir) {
if (ir.isLocal()) {
String text;
if (ir instanceof ForumInvitationRequest) {
text = ctx.getString(R.string.forum_invitation_sent,
((ForumInvitationRequest) ir).getForumName(),
contactName);
} else if (ir instanceof BlogInvitationRequest) {
text = ctx.getString(R.string.blogs_sharing_invitation_sent,
((BlogInvitationRequest) ir).getBlogAuthorName(),
contactName);
} else if (ir instanceof GroupInvitationRequest) {
text = ctx.getString(
R.string.groups_invitations_invitation_sent,
contactName, ir.getShareable().getName());
} else {
throw new IllegalArgumentException("Unknown InvitationRequest");
}
return new ConversationNoticeOutItem(ir.getId(), ir.getGroupId(),
text, ir.getMessage(), ir.getTimestamp(), ir.isSent(),
ir.isSeen());
} else {
String text;
RequestType type;
if (ir instanceof ForumInvitationRequest) {
text = ctx.getString(R.string.forum_invitation_received,
contactName,
((ForumInvitationRequest) ir).getForumName());
type = FORUM;
} else if (ir instanceof BlogInvitationRequest) {
text = ctx.getString(R.string.blogs_sharing_invitation_received,
contactName,
((BlogInvitationRequest) ir).getBlogAuthorName());
type = BLOG;
} else if (ir instanceof GroupInvitationRequest) {
text = ctx.getString(
R.string.groups_invitations_invitation_received,
contactName, ir.getShareable().getName());
type = GROUP;
} else {
throw new IllegalArgumentException("Unknown InvitationRequest");
}
return new ConversationRequestItem(ir.getId(),
ir.getGroupId(), type, ir.getSessionId(), text,
ir.getMessage(), ir.getTimestamp(), ir.isRead(),
ir.getShareable().getId(), !ir.isAvailable(),
ir.canBeOpened());
}
}
static ConversationItem from(Context ctx, String contactName,
InvitationResponse ir) {
@StringRes int res;
if (ir.isLocal()) {
if (ir.wasAccepted()) {
if (ir instanceof ForumInvitationResponse) {
res = R.string.forum_invitation_response_accepted_sent;
} else if (ir instanceof BlogInvitationResponse) {
res = R.string.blogs_sharing_response_accepted_sent;
} else if (ir instanceof GroupInvitationResponse) {
res = R.string.groups_invitations_response_accepted_sent;
} else {
throw new IllegalArgumentException(
"Unknown InvitationResponse");
}
} else {
if (ir instanceof ForumInvitationResponse) {
res = R.string.forum_invitation_response_declined_sent;
} else if (ir instanceof BlogInvitationResponse) {
res = R.string.blogs_sharing_response_declined_sent;
} else if (ir instanceof GroupInvitationResponse) {
res = R.string.groups_invitations_response_declined_sent;
} else {
throw new IllegalArgumentException(
"Unknown InvitationResponse");
}
}
String text = ctx.getString(res, contactName);
return new ConversationNoticeOutItem(ir.getId(), ir.getGroupId(),
text, null, ir.getTimestamp(), ir.isSent(), ir.isSeen());
} else {
if (ir.wasAccepted()) {
if (ir instanceof ForumInvitationResponse) {
res = R.string.forum_invitation_response_accepted_received;
} else if (ir instanceof BlogInvitationResponse) {
res = R.string.blogs_sharing_response_accepted_received;
} else if (ir instanceof GroupInvitationResponse) {
res = R.string.groups_invitations_response_accepted_received;
} else {
throw new IllegalArgumentException(
"Unknown InvitationResponse");
}
} else {
if (ir instanceof ForumInvitationResponse) {
res = R.string.forum_invitation_response_declined_received;
} else if (ir instanceof BlogInvitationResponse) {
res = R.string.blogs_sharing_response_declined_received;
} else if (ir instanceof GroupInvitationResponse) {
res = R.string.groups_invitations_response_declined_received;
} else {
throw new IllegalArgumentException(
"Unknown InvitationResponse");
}
}
String text = ctx.getString(res, contactName);
return new ConversationNoticeInItem(ir.getId(), ir.getGroupId(),
text, null, ir.getTimestamp(), ir.isRead());
}
}
/**
* This method should not be used to display the resulting ConversationItem
* in the UI, but only to update list information based on the
* BaseMessageHeader.
**/
static ConversationItem from(Context ctx, BaseMessageHeader h) {
if (h instanceof PrivateMessageHeader) {
return from((PrivateMessageHeader) h);
} else if(h instanceof IntroductionRequest) {
return from(ctx, "", (IntroductionRequest) h);
} else if(h instanceof IntroductionResponse) {
return from(ctx, "", (IntroductionResponse) h);
} else if(h instanceof InvitationRequest) {
return from(ctx, "", (InvitationRequest) h);
} else if(h instanceof InvitationResponse) {
return from(ctx, "", (InvitationResponse) h);
} else {
throw new IllegalArgumentException("Unknown message header");
}
}
}

View File

@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.api.messaging.PrivateResponse;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@@ -17,13 +18,17 @@ class ConversationNoticeInItem extends ConversationItem {
@Nullable
private final String msgText;
ConversationNoticeInItem(MessageId id, GroupId groupId,
String text, @Nullable String msgText, long time,
boolean read) {
ConversationNoticeInItem(MessageId id, GroupId groupId, String text,
@Nullable String msgText, long time, boolean read) {
super(id, groupId, text, time, read);
this.msgText = msgText;
}
ConversationNoticeInItem(String text, PrivateResponse r) {
super(r.getId(), r.getGroupId(), text, r.getTimestamp(), r.isRead());
this.msgText = null;
}
@Nullable
String getMsgText() {
return msgText;
@@ -39,5 +44,4 @@ class ConversationNoticeInItem extends ConversationItem {
public int getLayout() {
return R.layout.list_item_conversation_notice_in;
}
}

View File

@@ -3,9 +3,9 @@ package org.briarproject.briar.android.contact;
import android.support.annotation.LayoutRes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.api.messaging.PrivateRequest;
import org.briarproject.briar.api.messaging.PrivateResponse;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@@ -17,11 +17,16 @@ class ConversationNoticeOutItem extends ConversationOutItem {
@Nullable
private final String msgText;
ConversationNoticeOutItem(MessageId id, GroupId groupId,
String text, @Nullable String msgText, long time,
boolean sent, boolean seen) {
super(id, groupId, text, time, sent, seen);
this.msgText = msgText;
ConversationNoticeOutItem(String text, PrivateRequest r) {
super(r.getId(), r.getGroupId(), text, r.getTimestamp(), r.isSent(),
r.isSeen());
this.msgText = r.getMessage();
}
ConversationNoticeOutItem(String text, PrivateResponse r) {
super(r.getId(), r.getGroupId(), text, r.getTimestamp(), r.isSent(),
r.isSeen());
this.msgText = null;
}
@Nullable
@@ -34,5 +39,4 @@ class ConversationNoticeOutItem extends ConversationOutItem {
public int getLayout() {
return R.layout.list_item_conversation_notice_out;
}
}

View File

@@ -4,9 +4,11 @@ import android.support.annotation.LayoutRes;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.api.client.SessionId;
import org.briarproject.briar.api.messaging.PrivateRequest;
import org.briarproject.briar.api.sharing.InvitationRequest;
import org.briarproject.briar.api.sharing.Shareable;
import javax.annotation.Nullable;
import javax.annotation.concurrent.NotThreadSafe;
@@ -15,7 +17,7 @@ import javax.annotation.concurrent.NotThreadSafe;
@NotNullByDefault
class ConversationRequestItem extends ConversationNoticeInItem {
enum RequestType { INTRODUCTION, FORUM, BLOG, GROUP }
enum RequestType {INTRODUCTION, FORUM, BLOG, GROUP}
@Nullable
private final GroupId requestedGroupId;
@@ -24,17 +26,19 @@ class ConversationRequestItem extends ConversationNoticeInItem {
private final boolean canBeOpened;
private boolean answered;
ConversationRequestItem(MessageId id, GroupId groupId,
RequestType requestType, SessionId sessionId, String text,
@Nullable String msgText, long time, boolean read,
@Nullable GroupId requestedGroupId, boolean answered,
boolean canBeOpened) {
super(id, groupId, text, msgText, time, read);
this.requestType = requestType;
this.sessionId = sessionId;
this.requestedGroupId = requestedGroupId;
this.answered = answered;
this.canBeOpened = canBeOpened;
ConversationRequestItem(String text, RequestType type, PrivateRequest r) {
super(r.getId(), r.getGroupId(), text, r.getMessage(),
r.getTimestamp(), r.isRead());
this.requestType = type;
this.sessionId = r.getSessionId();
this.answered = r.wasAnswered();
if (r instanceof InvitationRequest) {
this.requestedGroupId = ((Shareable) r.getNameable()).getId();
this.canBeOpened = ((InvitationRequest) r).canBeOpened();
} else {
this.requestedGroupId = null;
this.canBeOpened = false;
}
}
RequestType getRequestType() {
@@ -54,8 +58,8 @@ class ConversationRequestItem extends ConversationNoticeInItem {
return answered;
}
void setAnswered(boolean answered) {
this.answered = answered;
void setAnswered() {
this.answered = true;
}
public boolean canBeOpened() {
@@ -67,5 +71,4 @@ class ConversationRequestItem extends ConversationNoticeInItem {
public int getLayout() {
return R.layout.list_item_conversation_request;
}
}

View File

@@ -0,0 +1,246 @@
package org.briarproject.briar.android.contact;
import android.arch.lifecycle.LiveData;
import android.content.Context;
import android.support.annotation.UiThread;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.R;
import org.briarproject.briar.api.blog.BlogInvitationRequest;
import org.briarproject.briar.api.blog.BlogInvitationResponse;
import org.briarproject.briar.api.forum.ForumInvitationRequest;
import org.briarproject.briar.api.forum.ForumInvitationResponse;
import org.briarproject.briar.api.introduction.IntroductionRequest;
import org.briarproject.briar.api.introduction.IntroductionResponse;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.PrivateMessageVisitor;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationRequest;
import org.briarproject.briar.api.privategroup.invitation.GroupInvitationResponse;
import javax.annotation.Nullable;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.BLOG;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.FORUM;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.GROUP;
import static org.briarproject.briar.android.contact.ConversationRequestItem.RequestType.INTRODUCTION;
@UiThread
@NotNullByDefault
class ConversationVisitor implements PrivateMessageVisitor<ConversationItem> {
private final Context ctx;
private final BodyCache bodyCache;
private final LiveData<String> contactName;
ConversationVisitor(Context ctx, BodyCache bodyCache,
LiveData<String> contactName) {
this.ctx = ctx;
this.bodyCache = bodyCache;
this.contactName = contactName;
}
@Override
public ConversationItem visitPrivateMessageHeader(PrivateMessageHeader h) {
ConversationItem item;
if (h.isLocal()) item = new ConversationMessageOutItem(h);
else item = new ConversationMessageInItem(h);
String body = bodyCache.getBody(h.getId());
if (body != null) item.setBody(body);
return item;
}
@Override
public ConversationItem visitBlogInvitationRequest(
BlogInvitationRequest r) {
if (r.isLocal()) {
String text = ctx.getString(R.string.blogs_sharing_invitation_sent,
r.getName(), contactName.getValue());
return new ConversationNoticeOutItem(text, r);
} else {
String text = ctx.getString(
R.string.blogs_sharing_invitation_received,
contactName.getValue(), r.getName());
return new ConversationRequestItem(text, BLOG, r);
}
}
@Override
public ConversationItem visitBlogInvitationResponse(
BlogInvitationResponse r) {
if (r.isLocal()) {
String text;
if (r.wasAccepted()) {
text = ctx.getString(
R.string.blogs_sharing_response_accepted_sent,
contactName.getValue());
} else {
text = ctx.getString(
R.string.blogs_sharing_response_declined_sent,
contactName.getValue());
}
return new ConversationNoticeOutItem(text, r);
} else {
String text;
if (r.wasAccepted()) {
text = ctx.getString(
R.string.blogs_sharing_response_accepted_received,
contactName.getValue());
} else {
text = ctx.getString(
R.string.blogs_sharing_response_declined_received,
contactName.getValue());
}
return new ConversationNoticeInItem(text, r);
}
}
@Override
public ConversationItem visitForumInvitationRequest(
ForumInvitationRequest r) {
if (r.isLocal()) {
String text = ctx.getString(R.string.forum_invitation_sent,
r.getName(), contactName.getValue());
return new ConversationNoticeOutItem(text, r);
} else {
String text = ctx.getString(
R.string.forum_invitation_received,
contactName.getValue(), r.getName());
return new ConversationRequestItem(text, FORUM, r);
}
}
@Override
public ConversationItem visitForumInvitationResponse(
ForumInvitationResponse r) {
if (r.isLocal()) {
String text;
if (r.wasAccepted()) {
text = ctx.getString(
R.string.forum_invitation_response_accepted_sent,
contactName.getValue());
} else {
text = ctx.getString(
R.string.forum_invitation_response_declined_sent,
contactName.getValue());
}
return new ConversationNoticeOutItem(text, r);
} else {
String text;
if (r.wasAccepted()) {
text = ctx.getString(
R.string.forum_invitation_response_accepted_received,
contactName.getValue());
} else {
text = ctx.getString(
R.string.forum_invitation_response_declined_received,
contactName.getValue());
}
return new ConversationNoticeInItem(text, r);
}
}
@Override
public ConversationItem visitGroupInvitationRequest(
GroupInvitationRequest r) {
if (r.isLocal()) {
String text = ctx.getString(
R.string.groups_invitations_invitation_sent,
contactName.getValue(), r.getName());
return new ConversationNoticeOutItem(text, r);
} else {
String text = ctx.getString(
R.string.groups_invitations_invitation_received,
contactName.getValue(), r.getName());
return new ConversationRequestItem(text, GROUP, r);
}
}
@Override
public ConversationItem visitGroupInvitationResponse(
GroupInvitationResponse r) {
if (r.isLocal()) {
String text;
if (r.wasAccepted()) {
text = ctx.getString(
R.string.groups_invitations_response_accepted_sent,
contactName.getValue());
} else {
text = ctx.getString(
R.string.groups_invitations_response_declined_sent,
contactName.getValue());
}
return new ConversationNoticeOutItem(text, r);
} else {
String text;
if (r.wasAccepted()) {
text = ctx.getString(
R.string.groups_invitations_response_accepted_received,
contactName.getValue());
} else {
text = ctx.getString(
R.string.groups_invitations_response_declined_received,
contactName.getValue());
}
return new ConversationNoticeInItem(text, r);
}
}
@Override
public ConversationItem visitIntroductionRequest(IntroductionRequest r) {
if (r.isLocal()) {
String text = ctx.getString(R.string.introduction_request_sent,
contactName.getValue(), r.getName());
return new ConversationNoticeOutItem(text, r);
} else {
String text = ctx.getString(R.string.introduction_request_received,
contactName.getValue(), r.getName());
return new ConversationRequestItem(text, INTRODUCTION, r);
}
}
@Override
public ConversationItem visitIntroductionResponse(IntroductionResponse r) {
if (r.isLocal()) {
String text;
if (r.wasAccepted()) {
String introducee = r.getIntroducedAuthor().getName();
text = ctx.getString(
R.string.introduction_response_accepted_sent,
introducee)
+ "\n\n" + ctx.getString(
R.string.introduction_response_accepted_sent_info,
introducee);
} else {
text = ctx.getString(
R.string.introduction_response_declined_sent,
r.getIntroducedAuthor().getName());
}
return new ConversationNoticeOutItem(text, r);
} else {
String text;
if (r.wasAccepted()) {
text = ctx.getString(
R.string.introduction_response_accepted_received,
contactName.getValue(),
r.getIntroducedAuthor().getName());
} else if (r.isIntroducer()) {
text = ctx.getString(
R.string.introduction_response_declined_received,
contactName.getValue(),
r.getIntroducedAuthor().getName());
} else {
text = ctx.getString(
R.string.introduction_response_declined_received_by_introducee,
contactName.getValue(),
r.getIntroducedAuthor().getName());
}
return new ConversationNoticeInItem(text, r);
}
}
interface BodyCache {
@Nullable
String getBody(MessageId m);
}
}

View File

@@ -84,11 +84,10 @@ class ForumControllerImpl extends
} else if (e instanceof ForumInvitationResponseReceivedEvent) {
ForumInvitationResponseReceivedEvent f =
(ForumInvitationResponseReceivedEvent) e;
ForumInvitationResponse r =
(ForumInvitationResponse) f.getResponse();
ForumInvitationResponse r = f.getMessageHeader();
if (r.getShareableId().equals(getGroupId()) && r.wasAccepted()) {
LOG.info("Forum invitation was accepted");
onForumInvitationAccepted(r.getContactId());
onForumInvitationAccepted(f.getContactId());
}
} else if (e instanceof ContactLeftShareableEvent) {
ContactLeftShareableEvent c = (ContactLeftShareableEvent) e;

View File

@@ -2,6 +2,7 @@ package org.briarproject.briar.android.introduction;
import android.content.Context;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.app.ActionBar;
import android.view.LayoutInflater;
@@ -82,8 +83,8 @@ public class IntroductionMessageFragment extends BaseFragment
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
// change toolbar text
ActionBar actionBar = introductionActivity.getSupportActionBar();
@@ -182,7 +183,7 @@ public class IntroductionMessageFragment extends BaseFragment
}
@Override
public void onSendClick(String text) {
public void onSendClick(@NonNull String text) {
// disable button to prevent accidental double invitations
ui.message.setSendButtonEnabled(false);

View File

@@ -15,7 +15,6 @@ import android.widget.Toast;
import com.google.zxing.Result;
import org.briarproject.bramble.api.UnsupportedVersionException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.keyagreement.KeyAgreementResult;
@@ -23,6 +22,7 @@ import org.briarproject.bramble.api.keyagreement.KeyAgreementTask;
import org.briarproject.bramble.api.keyagreement.Payload;
import org.briarproject.bramble.api.keyagreement.PayloadEncoder;
import org.briarproject.bramble.api.keyagreement.PayloadParser;
import org.briarproject.bramble.api.keyagreement.UnsupportedVersionException;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementAbortedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFailedEvent;
import org.briarproject.bramble.api.keyagreement.event.KeyAgreementFinishedEvent;
@@ -244,8 +244,14 @@ public class KeyAgreementFragment extends BaseEventFragment
task.connectAndRunProtocol(remotePayload);
} catch (UnsupportedVersionException e) {
reset();
String msg = getString(R.string.qr_code_unsupported,
getString(R.string.app_name));
String msg;
if (e.isTooOld()) {
msg = getString(R.string.qr_code_too_old,
getString(R.string.app_name));
} else {
msg = getString(R.string.qr_code_too_new,
getString(R.string.app_name));
}
showNextFragment(ContactExchangeErrorFragment.newInstance(msg));
} catch (CameraException e) {
logCameraExceptionAndFinish(e);

View File

@@ -7,6 +7,7 @@ import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.widget.ProgressBar;
@@ -15,6 +16,7 @@ import static android.graphics.Paint.Style.FILL;
import static android.graphics.Paint.Style.STROKE;
import static android.graphics.drawable.ClipDrawable.HORIZONTAL;
import static android.view.Gravity.LEFT;
import static android.view.Gravity.START;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_STRONG;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.QUITE_WEAK;
import static org.briarproject.bramble.api.crypto.PasswordStrengthEstimator.STRONG;
@@ -35,11 +37,11 @@ public class StrengthMeter extends ProgressBar {
this(context, null);
}
public StrengthMeter(Context context, AttributeSet attrs) {
public StrengthMeter(Context context, @Nullable AttributeSet attrs) {
super(context, attrs, android.R.attr.progressBarStyleHorizontal);
bar = new ShapeDrawable();
bar.getPaint().setColor(RED);
ClipDrawable clip = new ClipDrawable(bar, LEFT, HORIZONTAL);
ClipDrawable clip = new ClipDrawable(bar, LEFT & START, HORIZONTAL);
ShapeDrawable background = new ShapeDrawable();
Paint p = background.getPaint();
p.setStyle(FILL);
@@ -50,6 +52,7 @@ public class StrengthMeter extends ProgressBar {
Drawable[] layers = new Drawable[] { clip, background };
setProgressDrawable(new LayerDrawable(layers));
setIndeterminate(false);
if (isInEditMode()) setStrength(STRONG);
}
@Override

View File

@@ -0,0 +1,84 @@
package org.briarproject.briar.android.panic;
import info.guardianproject.trustedintents.ApkSignaturePin;
// needs to be public, because TrustedIntents will instantiate
public final class FDroidSignaturePin extends ApkSignaturePin {
public FDroidSignaturePin() {
this.fingerprints = new String[] {
"927f7e38b6acbecd84e02dace33efa9a7a2f0979750f28f585688ee38b3a4e28"};
this.certificates = new byte[][] {
{48, -126, 3, 95, 48, -126, 2, 71, -96, 3, 2, 1, 2, 2, 4, 28,
-30, 107, -102, 48, 13, 6, 9, 42, -122, 72, -122, -9,
13, 1, 1, 11, 5, 0, 48, 96, 49, 11, 48, 9, 6, 3, 85, 4,
6, 19, 2, 85, 75, 49, 12, 48, 10, 6, 3, 85, 4, 8, 19, 3,
79, 82, 71, 49, 12, 48, 10, 6, 3, 85, 4, 7, 19, 3, 79,
82, 71, 49, 19, 48, 17, 6, 3, 85, 4, 10, 19, 10, 102,
100, 114, 111, 105, 100, 46, 111, 114, 103, 49, 15, 48,
13, 6, 3, 85, 4, 11, 19, 6, 70, 68, 114, 111, 105, 100,
49, 15, 48, 13, 6, 3, 85, 4, 3, 19, 6, 70, 68, 114, 111,
105, 100, 48, 30, 23, 13, 49, 55, 49, 50, 48, 55, 49,
55, 51, 48, 52, 50, 90, 23, 13, 52, 53, 48, 52, 50, 52,
49, 55, 51, 48, 52, 50, 90, 48, 96, 49, 11, 48, 9, 6, 3,
85, 4, 6, 19, 2, 85, 75, 49, 12, 48, 10, 6, 3, 85, 4, 8,
19, 3, 79, 82, 71, 49, 12, 48, 10, 6, 3, 85, 4, 7, 19,
3, 79, 82, 71, 49, 19, 48, 17, 6, 3, 85, 4, 10, 19, 10,
102, 100, 114, 111, 105, 100, 46, 111, 114, 103, 49, 15,
48, 13, 6, 3, 85, 4, 11, 19, 6, 70, 68, 114, 111, 105,
100, 49, 15, 48, 13, 6, 3, 85, 4, 3, 19, 6, 70, 68, 114,
111, 105, 100, 48, -126, 1, 34, 48, 13, 6, 9, 42, -122,
72, -122, -9, 13, 1, 1, 1, 5, 0, 3, -126, 1, 15, 0, 48,
-126, 1, 10, 2, -126, 1, 1, 0, -107, -115, -106, 1, -26,
72, -105, -99, 62, 3, -55, 34, 99, -112, -68, -20, -115,
31, 34, 118, -50, 12, -32, -59, 74, -58, -37, -87, 21,
105, 36, -82, 13, -51, 66, 4, 55, -111, 13, -46, -7,
-69, -15, 36, 118, -7, 101, -86, 123, -83, -103, 110,
116, -54, 112, 46, 12, 96, -76, -48, -70, -33, -81, 52,
59, 73, 107, -126, -72, -25, 32, 93, 29, -20, 5, -41,
-27, 123, -9, 104, -31, -59, -1, -83, -93, 99, 85, -116,
-62, -55, 18, -63, 6, -51, -110, 33, 9, 7, -49, 102,
-20, -122, -124, -68, 93, -102, 31, 48, 86, 96, -99,
105, -52, 95, 12, 57, 99, 12, -24, 70, 40, -99, -20,
-21, -85, -70, -105, 95, 117, -31, 126, -126, -39, 46,
-62, 59, -23, -74, 108, -12, -56, -40, -96, 79, -37,
-82, 1, 99, -104, 48, -60, 92, 14, 109, 127, -22, 31,
115, -27, 108, 9, 92, 118, -45, 103, 117, 57, -50, -82,
114, -113, 68, -82, 87, 96, 111, 72, 65, -63, 12, 31,
-34, -31, -55, -101, 101, 101, 59, 73, -119, -122, 82,
28, 47, -108, -85, 59, 46, 89, -93, -1, 9, -11, -51, 63,
-44, 109, -76, -103, -26, -49, -80, 6, 52, -27, 73,
-104, 40, 2, -101, -124, 60, -52, -105, -70, -24, -62,
88, 38, 53, -99, -92, 31, 119, 26, 79, 60, -124, 25,
-115, -89, -115, -109, 0, 6, 122, -78, 116, 82, 3, 39,
-67, 45, -43, 17, -39, 2, 3, 1, 0, 1, -93, 33, 48, 31,
48, 29, 6, 3, 85, 29, 14, 4, 22, 4, 20, 63, 109, -42,
-109, 25, 22, 7, -37, -22, -41, -38, 58, -56, 2, -68,
-38, -22, 65, -28, -60, 48, 13, 6, 9, 42, -122, 72,
-122, -9, 13, 1, 1, 11, 5, 0, 3, -126, 1, 1, 0, 94, 17,
31, 36, 85, -11, 85, 44, 19, -80, -20, -92, -118, 93,
40, 45, 96, 31, -3, -37, -110, -96, 102, 81, 61, -74,
-125, -117, -112, 58, -47, 17, 78, -18, 111, -116, 26,
-91, 73, 100, 84, -99, 21, 87, 73, -106, 108, -51, -125,
-21, 119, -88, -78, 2, 82, -109, -64, -9, -86, -112,
-115, 66, -86, 46, 71, 107, -65, 96, -102, 47, 35, -45,
-126, 33, 34, 121, -25, -85, -121, -56, -42, 22, -1,
-95, -86, 81, 100, -70, 113, 104, -73, 22, -19, 79, -19,
52, 62, 42, 76, -112, 94, -34, 42, -57, -75, -90, -58,
118, 127, -106, -39, 108, -56, -79, 103, -33, 22, 3, 47,
103, -76, -81, 53, -22, -44, -26, -102, 63, -99, 39, 38,
-108, 75, 33, 10, 25, -110, -125, -115, 114, -69, 73,
-112, 36, 74, 77, -82, -44, 29, -123, -8, -117, 71,
-105, 15, -109, 51, 22, 4, 80, 1, 43, 118, 121, -113,
-70, 83, -56, 82, -110, 4, -63, 16, -57, 126, -70, 81,
73, 61, 2, -61, 24, -14, -10, 4, -21, 90, 24, 66, 41,
-57, -60, -113, -18, -54, -1, 103, -75, 32, -64, 67,
103, 109, -79, -12, -113, -27, 114, 89, 116, 115, -13,
-123, -70, 61, -41, -46, -118, 29, -105, -97, -75, 39,
-51, 60, 88, 125, 55, -46, -95, 52, 57, 52, -115, 80,
44, 109, 119, -116, -62, -77, -74, -88, 41, 57, -65,
-71, -115, -67, 23, 66, -21, 56, 51, -91, 109}};
}
}

View File

@@ -31,13 +31,12 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
public static final String KEY_LOCK = "pref_key_lock";
public static final String KEY_PANIC_APP = "pref_key_panic_app";
public static final String KEY_PURGE = "pref_key_purge";
public static final String KEY_UNINSTALL = "pref_key_uninstall";
private static final Logger LOG =
Logger.getLogger(PanicPreferencesFragment.class.getName());
private PackageManager pm;
private SwitchPreference lockPref, purgePref, uninstallPref;
private SwitchPreference lockPref, purgePref;
private ListPreference panicAppPref;
@Override
@@ -49,7 +48,6 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
lockPref = (SwitchPreference) findPreference(KEY_LOCK);
panicAppPref = (ListPreference) findPreference(KEY_PANIC_APP);
purgePref = (SwitchPreference) findPreference(KEY_PURGE);
uninstallPref = (SwitchPreference) findPreference(KEY_UNINSTALL);
// check for connect/disconnect intents from panic trigger apps
if (PanicResponder.checkForDisconnectIntent(getActivity())) {
@@ -97,16 +95,11 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
showPanicApp(packageName);
if (packageName.equals(Panic.PACKAGE_NAME_NONE)) {
lockPref.setEnabled(false);
purgePref.setChecked(false);
purgePref.setEnabled(false);
uninstallPref.setChecked(false);
uninstallPref.setEnabled(false);
getActivity().setResult(Activity.RESULT_CANCELED);
} else {
lockPref.setEnabled(true);
purgePref.setEnabled(true);
uninstallPref.setEnabled(true);
}
return true;
@@ -145,27 +138,15 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
if (key.equals(KEY_PURGE)) {
// enable locking if purging gets enabled
if (sharedPreferences.getBoolean(KEY_PURGE, false)) {
lockPref.setChecked(true);
}
// disable uninstall if purging gets disabled
else {
uninstallPref.setChecked(false);
}
}
// enable purging and locking if uninstall gets enabled
if (key.equals(KEY_UNINSTALL) &&
sharedPreferences.getBoolean(KEY_UNINSTALL, false)) {
// enable locking if purging gets enabled
if (key.equals(KEY_PURGE) &&
sharedPreferences.getBoolean(KEY_PURGE, false)) {
lockPref.setChecked(true);
purgePref.setChecked(true);
}
// disable purging and uninstalling if locking gets disabled
// disable purging if locking gets disabled
if (key.equals(KEY_LOCK) &&
!sharedPreferences.getBoolean(KEY_LOCK, true)) {
purgePref.setChecked(false);
uninstallPref.setChecked(false);
}
}
@@ -179,10 +160,8 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
panicAppPref.setIcon(
android.R.drawable.ic_menu_close_clear_cancel);
// disable panic actions
lockPref.setEnabled(false);
// disable destructive panic actions
purgePref.setEnabled(false);
uninstallPref.setEnabled(false);
} else {
// display connected panic app
try {
@@ -192,10 +171,8 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
panicAppPref.setIcon(
pm.getApplicationIcon(triggerPackageName));
// enable panic actions
lockPref.setEnabled(true);
// enable destructive panic actions
purgePref.setEnabled(true);
uninstallPref.setEnabled(true);
} catch (PackageManager.NameNotFoundException e) {
// revert back to no app, just to be safe
PanicResponder.setTriggerPackageName(getActivity(),

View File

@@ -2,7 +2,6 @@ package org.briarproject.briar.android.panic;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.preference.PreferenceManager;
@@ -11,7 +10,6 @@ import org.briarproject.bramble.api.account.AccountManager;
import org.briarproject.bramble.api.system.AndroidExecutor;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.iilab.IilabEngineeringRSA2048Pin;
import java.util.logging.Logger;
@@ -22,10 +20,8 @@ import info.guardianproject.panic.Panic;
import info.guardianproject.panic.PanicResponder;
import info.guardianproject.trustedintents.TrustedIntents;
import static android.content.Intent.ACTION_DELETE;
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_LOCK;
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_PURGE;
import static org.briarproject.briar.android.panic.PanicPreferencesFragment.KEY_UNINSTALL;
public class PanicResponderActivity extends BriarActivity {
@@ -44,8 +40,8 @@ public class PanicResponderActivity extends BriarActivity {
TrustedIntents trustedIntents = TrustedIntents.get(this);
// Guardian Project Ripple
trustedIntents.addTrustedSigner(GuardianProjectRSA4096.class);
// Amnesty International's Panic Button, made by iilab.org
trustedIntents.addTrustedSigner(IilabEngineeringRSA2048Pin.class);
// F-Droid
trustedIntents.addTrustedSigner(FDroidSignaturePin.class);
Intent intent = trustedIntents.getIntentFromTrustedSender(this);
if (intent != null) {
@@ -60,23 +56,16 @@ public class PanicResponderActivity extends BriarActivity {
LOG.info("Panic Trigger came from connected app");
// Performing panic responses
if (sharedPref.getBoolean(KEY_UNINSTALL, false)) {
if (sharedPref.getBoolean(KEY_PURGE, false)) {
LOG.info("Purging all data...");
deleteAllData();
LOG.info("Uninstalling...");
Intent uninstall = new Intent(ACTION_DELETE);
uninstall.setData(
Uri.parse("package:" + getPackageName()));
startActivity(uninstall);
} else if (sharedPref.getBoolean(KEY_PURGE, false)) {
LOG.info("Purging all data...");
deleteAllData();
} else if (sharedPref.getBoolean(KEY_LOCK, true)) {
LOG.info("Signing out...");
signOut(true);
}
}
// non-destructive actions are allowed by non-connected trusted apps
if (sharedPref.getBoolean(KEY_LOCK, true)) {
LOG.info("Signing out...");
signOut(true);
}
}
}

View File

@@ -99,11 +99,10 @@ class GroupControllerImpl extends
} else if (e instanceof GroupInvitationResponseReceivedEvent) {
GroupInvitationResponseReceivedEvent g =
(GroupInvitationResponseReceivedEvent) e;
GroupInvitationResponse r =
(GroupInvitationResponse) g.getResponse();
GroupInvitationResponse r = g.getMessageHeader();
if (getGroupId().equals(r.getShareableId()) && r.wasAccepted()) {
listener.runOnUiThreadUnlessDestroyed(
() -> listener.onInvitationAccepted(r.getContactId()));
() -> listener.onInvitationAccepted(g.getContactId()));
}
} else if (e instanceof GroupDissolvedEvent) {
GroupDissolvedEvent g = (GroupDissolvedEvent) e;

View File

@@ -292,8 +292,7 @@ public class DevReportActivity extends BaseCrashReportDialog
cb.setChecked(required || !excluded);
cb.setEnabled(!required);
cb.setOnCheckedChangeListener(DevReportActivity.this);
TextView title = v.findViewById(R.id.title);
title.setText(field.toString());
cb.setText(field.toString());
TextView content = v.findViewById(R.id.content);
content.setText(value);
report.addView(v);

View File

@@ -89,7 +89,6 @@ import static org.briarproject.briar.api.android.AndroidNotificationManager.GROU
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_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_RINGTONE_NAME;
import static org.briarproject.briar.api.android.AndroidNotificationManager.PREF_NOTIFY_RINGTONE_URI;
@@ -127,7 +126,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
private SwitchPreference notifyForumPosts;
private SwitchPreference notifyBlogPosts;
private SwitchPreference notifyVibration;
private SwitchPreference notifyLockscreen;
private Preference notifySound;
@@ -179,8 +177,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
"pref_key_notify_blog_posts");
notifyVibration = (SwitchPreference) findPreference(
"pref_key_notify_vibration");
notifyLockscreen = (SwitchPreference) findPreference(
"pref_key_notify_lock_screen");
notifySound = findPreference("pref_key_notify_sound");
language.setOnPreferenceChangeListener(this);
@@ -207,10 +203,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
torMobile.setOnPreferenceChangeListener(this);
screenLock.setOnPreferenceChangeListener(this);
screenLockTimeout.setOnPreferenceChangeListener(this);
if (SDK_INT >= 21) {
notifyLockscreen.setVisible(true);
notifyLockscreen.setOnPreferenceChangeListener(this);
}
findPreference("pref_key_send_feedback").setOnPreferenceClickListener(
preference -> {
@@ -380,8 +372,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyForumPosts.setOnPreferenceChangeListener(this);
notifyBlogPosts.setOnPreferenceChangeListener(this);
notifyVibration.setOnPreferenceChangeListener(this);
notifyLockscreen.setChecked(settings.getBoolean(
PREF_NOTIFY_LOCK_SCREEN, false));
notifySound.setOnPreferenceClickListener(
pref -> onNotificationSoundClicked());
String text;
@@ -409,7 +399,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
setupNotificationPreference(notifyBlogPosts, BLOG_CHANNEL_ID,
R.string.notify_blog_posts_setting_summary_26);
notifyVibration.setVisible(false);
notifyLockscreen.setVisible(false);
notifySound.setVisible(false);
}
setSettingsEnabled(true);
@@ -432,7 +421,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyForumPosts.setEnabled(enabled);
notifyBlogPosts.setEnabled(enabled);
notifyVibration.setEnabled(enabled);
notifyLockscreen.setEnabled(enabled);
notifySound.setEnabled(enabled);
}
@@ -553,10 +541,6 @@ public class SettingsFragment extends PreferenceFragmentCompat
Settings s = new Settings();
s.putBoolean(PREF_NOTIFY_VIBRATION, (Boolean) newValue);
storeSettings(s);
} else if (preference == notifyLockscreen) {
Settings s = new Settings();
s.putBoolean(PREF_NOTIFY_LOCK_SCREEN, (Boolean) newValue);
storeSettings(s);
}
return true;
}

View File

@@ -1,6 +1,7 @@
package org.briarproject.briar.android.sharing;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
@@ -26,7 +27,7 @@ public abstract class InvitationAdapter<I extends InvitationItem, VH extends Inv
}
@Override
public void onBindViewHolder(VH ui, int position) {
public void onBindViewHolder(@NonNull VH ui, int position) {
I item = getItemAt(position);
if (item == null) return;
ui.onBind(item, listener);

View File

@@ -1,5 +1,6 @@
package org.briarproject.briar.android.threaded;
import android.support.annotation.NonNull;
import android.support.annotation.UiThread;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
@@ -36,8 +37,9 @@ public class ThreadItemAdapter<I extends ThreadItem>
this.layoutManager = layoutManager;
}
@NonNull
@Override
public BaseThreadItemViewHolder<I> onCreateViewHolder(
public BaseThreadItemViewHolder<I> onCreateViewHolder(@NonNull
ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_thread, parent, false);
@@ -45,7 +47,8 @@ public class ThreadItemAdapter<I extends ThreadItem>
}
@Override
public void onBindViewHolder(BaseThreadItemViewHolder<I> ui, int position) {
public void onBindViewHolder(@NonNull BaseThreadItemViewHolder<I> ui,
int position) {
I item = items.get(position);
ui.bind(item, listener);
}

View File

@@ -1,15 +1,14 @@
package org.briarproject.briar.android.util;
import android.content.Context;
import android.os.Build;
import android.support.annotation.ColorRes;
import android.support.v4.app.NotificationCompat;
import android.support.v4.content.ContextCompat;
import org.briarproject.briar.R;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.app.NotificationCompat.VISIBILITY_PRIVATE;
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
public class BriarNotificationBuilder extends NotificationCompat.Builder {
@@ -22,6 +21,7 @@ public class BriarNotificationBuilder extends NotificationCompat.Builder {
setLights(ContextCompat.getColor(context, R.color.briar_green_light),
750, 500);
if (SDK_INT >= 21) setVisibility(VISIBILITY_PRIVATE);
}
public BriarNotificationBuilder setColorRes(@ColorRes int res) {
@@ -29,13 +29,8 @@ public class BriarNotificationBuilder extends NotificationCompat.Builder {
return this;
}
public BriarNotificationBuilder setLockscreenVisibility(String category,
boolean show) {
if (Build.VERSION.SDK_INT >= 21) {
setCategory(category);
if (show) setVisibility(VISIBILITY_PRIVATE);
else setVisibility(VISIBILITY_SECRET);
}
public BriarNotificationBuilder setNotificationCategory(String category) {
if (SDK_INT >= 21) setCategory(category);
return this;
}

View File

@@ -61,7 +61,6 @@ import static android.text.format.DateUtils.FORMAT_SHOW_DATE;
import static android.text.format.DateUtils.MINUTE_IN_MILLIS;
import static android.text.format.DateUtils.WEEK_IN_MILLIS;
import static android.view.KeyEvent.ACTION_DOWN;
import static android.view.KeyEvent.ACTION_UP;
import static android.view.KeyEvent.KEYCODE_ENTER;
import static android.view.inputmethod.EditorInfo.IME_NULL;
import static org.briarproject.briar.BuildConfig.APPLICATION_ID;
@@ -157,7 +156,7 @@ public class UiUtils {
/**
* Executes the runnable when clicking the link in the textView's text.
*
* <p>
* Attention: This assumes that there's only <b>one</b> link in the text.
*/
public static void onSingleLinkClick(TextView textView, Runnable runnable) {

View File

@@ -5,11 +5,10 @@ import android.content.res.TypedArray;
import android.graphics.Typeface;
import android.support.annotation.DimenRes;
import android.support.annotation.UiThread;
import android.support.constraint.ConstraintLayout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
@@ -30,7 +29,7 @@ import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
import static org.briarproject.briar.android.util.UiUtils.resolveAttribute;
@UiThread
public class AuthorView extends RelativeLayout {
public class AuthorView extends ConstraintLayout {
public static final int NORMAL = 0;
public static final int REBLOGGER = 1;
@@ -133,32 +132,24 @@ public class AuthorView extends RelativeLayout {
date.setVisibility(VISIBLE);
setAvatarSize(R.dimen.blogs_avatar_normal_size);
setTextSize(authorName, R.dimen.text_size_small);
setCenterVertical(authorName, false);
setCenterVertical(trustIndicator, false);
break;
case REBLOGGER:
avatarIcon.setVisibility(VISIBLE);
date.setVisibility(VISIBLE);
setAvatarSize(R.dimen.blogs_avatar_normal_size);
setTextSize(authorName, R.dimen.text_size_small);
setCenterVertical(authorName, false);
setCenterVertical(trustIndicator, false);
break;
case COMMENTER:
avatarIcon.setVisibility(INVISIBLE);
date.setVisibility(VISIBLE);
setAvatarSize(R.dimen.blogs_avatar_comment_size);
setTextSize(authorName, R.dimen.text_size_tiny);
setCenterVertical(authorName, false);
setCenterVertical(trustIndicator, false);
break;
case LIST:
avatarIcon.setVisibility(INVISIBLE);
date.setVisibility(GONE);
setAvatarSize(R.dimen.listitem_picture_size_small);
setTextSize(authorName, R.dimen.text_size_medium);
setCenterVertical(authorName, true);
setCenterVertical(trustIndicator, true);
break;
case RSS_FEED:
avatarIcon.setVisibility(INVISIBLE);
@@ -166,8 +157,6 @@ public class AuthorView extends RelativeLayout {
avatar.setImageResource(R.drawable.ic_rss_feed);
setAvatarSize(R.dimen.blogs_avatar_normal_size);
setTextSize(authorName, R.dimen.text_size_small);
setCenterVertical(authorName, false);
setCenterVertical(trustIndicator, false);
break;
case RSS_FEED_REBLOGGED:
avatarIcon.setVisibility(INVISIBLE);
@@ -175,8 +164,6 @@ public class AuthorView extends RelativeLayout {
avatar.setImageResource(R.drawable.ic_rss_feed);
setAvatarSize(R.dimen.blogs_avatar_comment_size);
setTextSize(authorName, R.dimen.text_size_tiny);
setCenterVertical(authorName, false);
setCenterVertical(trustIndicator, false);
break;
}
}
@@ -194,10 +181,4 @@ public class AuthorView extends RelativeLayout {
v.setTextSize(COMPLEX_UNIT_PX, textSize);
}
private void setCenterVertical(View v, boolean center) {
LayoutParams params = (LayoutParams) v.getLayoutParams();
params.addRule(CENTER_VERTICAL, center ? RelativeLayout.TRUE : 0);
v.setLayoutParams(params);
}
}

View File

@@ -19,8 +19,7 @@ public class QrCodeView extends FrameLayout {
private boolean fullscreen = false;
private FullscreenListener listener;
public QrCodeView(@NonNull Context context,
@Nullable AttributeSet attrs) {
public QrCodeView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
LayoutInflater inflater = (LayoutInflater) context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

View File

@@ -3,7 +3,6 @@ package org.briarproject.briar.android.view;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.support.annotation.UiThread;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
@@ -11,13 +10,11 @@ import android.view.LayoutInflater;
import android.widget.FrameLayout;
import android.widget.TextView;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.briar.R;
import javax.annotation.Nullable;
import de.hdodenhof.circleimageview.CircleImageView;
import im.delight.android.identicons.IdenticonDrawable;
@UiThread
public class TextAvatarView extends FrameLayout {
@@ -35,7 +32,6 @@ public class TextAvatarView extends FrameLayout {
character = findViewById(R.id.textAvatarView);
background = findViewById(R.id.avatarBackground);
badge = findViewById(R.id.unreadCountView);
badge.setVisibility(INVISIBLE);
}
public TextAvatarView(Context context) {
@@ -72,10 +68,4 @@ public class TextAvatarView extends FrameLayout {
}
}
public void setAuthorAvatar(Author author) {
Drawable drawable = new IdenticonDrawable(author.getId().getBytes());
background.setImageDrawable(drawable);
character.setVisibility(GONE);
}
}

View File

@@ -61,9 +61,11 @@ public class TextInputView extends KeyboardAwareLinearLayout {
public TextInputView(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
BriarApplication app =
(BriarApplication) context.getApplicationContext();
app.getApplicationComponent().inject(this);
if (!isInEditMode()) {
BriarApplication app =
(BriarApplication) context.getApplicationContext();
app.getApplicationComponent().inject(this);
}
setOrientation(VERTICAL);
setLayoutTransition(new LayoutTransition());
inflateLayout(context);

View File

@@ -49,7 +49,7 @@ public class UnreadMessageButton extends FrameLayout {
setDirection(direction);
attributes.recycle();
setUnreadCount(0);
if (!isInEditMode()) setUnreadCount(0);
}
private void setDirection(int direction) {
@@ -64,11 +64,11 @@ public class UnreadMessageButton extends FrameLayout {
public void setUnreadCount(int count) {
if (count == 0) {
setVisibility(INVISIBLE);
fab.hide();
unread.setVisibility(INVISIBLE);
} else {
// FIXME: Use animations when upgrading to support library 24.2.0
// https://code.google.com/p/android/issues/detail?id=216469
setVisibility(VISIBLE);
fab.show();
unread.setVisibility(VISIBLE);
unread.setText(String.valueOf(count));
}
}

View File

@@ -5,6 +5,7 @@ import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
@@ -42,8 +43,8 @@ public class LinkDialogFragment extends DialogFragment {
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_link_dialog, container,
false);

View File

@@ -21,7 +21,6 @@ public interface AndroidNotificationManager {
String PREF_NOTIFY_RINGTONE_NAME = "notifyRingtoneName";
String PREF_NOTIFY_RINGTONE_URI = "notifyRingtoneUri";
String PREF_NOTIFY_VIBRATION = "notifyVibration";
String PREF_NOTIFY_LOCK_SCREEN = "notifyLockScreen";
// Notification IDs
int ONGOING_NOTIFICATION_ID = 1;

View File

@@ -1,23 +0,0 @@
/**
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.components.util;
public interface FutureTaskListener<V> {
void onSuccess(V result);
void onFailure(Throwable error);
}

View File

@@ -1,105 +0,0 @@
/*
* Copyright (C) 2014 Open Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.components.util;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
import javax.annotation.Nullable;
public class ListenableFutureTask<V> extends FutureTask<V> {
private final List<FutureTaskListener<V>> listeners = new LinkedList<>();
@Nullable
private final Object identifier;
public ListenableFutureTask(Callable<V> callable) {
this(callable, null);
}
private ListenableFutureTask(Callable<V> callable,
@Nullable Object identifier) {
super(callable);
this.identifier = identifier;
}
public ListenableFutureTask(V result) {
this(result, null);
}
private ListenableFutureTask(V result, @Nullable Object identifier) {
super(() -> result);
this.identifier = identifier;
this.run();
}
public synchronized void addListener(FutureTaskListener<V> listener) {
if (this.isDone()) {
callback(listener);
} else {
this.listeners.add(listener);
}
}
public synchronized void removeListener(FutureTaskListener<V> listener) {
this.listeners.remove(listener);
}
@Override
protected synchronized void done() {
callback();
}
private void callback() {
for (FutureTaskListener<V> listener : listeners) {
callback(listener);
}
}
private void callback(FutureTaskListener<V> listener) {
if (listener != null) {
try {
listener.onSuccess(get());
} catch (InterruptedException e) {
throw new AssertionError(e);
} catch (ExecutionException e) {
listener.onFailure(e);
}
}
}
@Override
public boolean equals(Object other) {
if (other != null && other instanceof ListenableFutureTask &&
this.identifier != null) {
return identifier.equals(other);
} else {
return super.equals(other);
}
}
@Override
public int hashCode() {
if (identifier != null) return identifier.hashCode();
else return super.hashCode();
}
}

View File

@@ -3,9 +3,9 @@
xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="oval">
<solid android:color="@color/window_background"/>
<solid android:color="@color/thread_item_background"/>
<stroke
android:width="2dp"
android:width="@dimen/forum_nested_line_width"
android:color="@color/thread_indicator"/>
</shape>

View File

@@ -1,8 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zm4.24,-1.41L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>

View File

@@ -1,5 +1,10 @@
<vector android:height="16dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFFFF" android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zm4.24,-1.41L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M18,7l-1.41,-1.41 -6.34,6.34 1.41,1.41L18,7zm4.24,-1.41L11.66,16.17 7.48,12l-1.41,1.41L11.66,19l12,-12 -1.42,-1.41zM0.41,13.41L6,19l1.41,-1.41L1.83,12 0.41,13.41z"/>
</vector>

View File

@@ -1,8 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>

View File

@@ -1,5 +1,10 @@
<vector android:height="16dp"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="16dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FFFFFFFF" android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
<vector
xmlns:android="http://schemas.android.com/apk/res/android"
android:width="16dp"
android:height="16dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z"/>
</vector>

View File

@@ -5,8 +5,8 @@
<corners
android:bottomLeftRadius="@dimen/message_bubble_radius_big"
android:bottomRightRadius="@dimen/message_bubble_radius_big"
android:topLeftRadius="@dimen/message_bubble_radius_small"
android:topRightRadius="@dimen/message_bubble_radius_big"/>
android:topLeftRadius="@dimen/message_bubble_radius_top_inner"
android:topRightRadius="@dimen/message_bubble_radius_top_outer"/>
<padding
android:bottom="@dimen/message_bubble_padding_bottom"
android:left="@dimen/message_bubble_padding_sides"

View File

@@ -5,8 +5,8 @@
<corners
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"
android:topLeftRadius="@dimen/message_bubble_radius_small"
android:topRightRadius="@dimen/message_bubble_radius_big"/>
android:topLeftRadius="@dimen/message_bubble_radius_top_inner"
android:topRightRadius="@dimen/message_bubble_radius_top_outer"/>
<padding
android:bottom="@dimen/message_bubble_padding_top"
android:left="@dimen/message_bubble_padding_sides"

View File

@@ -5,8 +5,8 @@
<corners
android:bottomLeftRadius="@dimen/message_bubble_radius_big"
android:bottomRightRadius="@dimen/message_bubble_radius_big"
android:topLeftRadius="@dimen/message_bubble_radius_big"
android:topRightRadius="@dimen/message_bubble_radius_small"/>
android:topLeftRadius="@dimen/message_bubble_radius_top_outer"
android:topRightRadius="@dimen/message_bubble_radius_top_inner"/>
<padding
android:bottom="@dimen/message_bubble_padding_bottom"
android:left="@dimen/message_bubble_padding_sides"

View File

@@ -5,8 +5,8 @@
<corners
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"
android:topLeftRadius="@dimen/message_bubble_radius_big"
android:topRightRadius="@dimen/message_bubble_radius_small"/>
android:topLeftRadius="@dimen/message_bubble_radius_top_outer"
android:topRightRadius="@dimen/message_bubble_radius_top_inner"/>
<padding
android:bottom="@dimen/message_bubble_padding_top"
android:left="@dimen/message_bubble_padding_sides"

View File

@@ -5,8 +5,8 @@
<corners
android:bottomLeftRadius="@dimen/message_bubble_radius_big"
android:bottomRightRadius="@dimen/message_bubble_radius_big"
android:topLeftRadius="@dimen/message_bubble_radius_small"
android:topRightRadius="@dimen/message_bubble_radius_big"/>
android:topLeftRadius="@dimen/message_bubble_radius_top_inner"
android:topRightRadius="@dimen/message_bubble_radius_top_outer"/>
<padding
android:bottom="@dimen/message_bubble_padding_bottom"
android:left="@dimen/message_bubble_padding_sides"

View File

@@ -5,8 +5,8 @@
<corners
android:bottomLeftRadius="@dimen/message_bubble_radius_big"
android:bottomRightRadius="@dimen/message_bubble_radius_big"
android:topLeftRadius="@dimen/message_bubble_radius_big"
android:topRightRadius="@dimen/message_bubble_radius_small"/>
android:topLeftRadius="@dimen/message_bubble_radius_top_outer"
android:topRightRadius="@dimen/message_bubble_radius_top_inner"/>
<padding
android:bottom="@dimen/message_bubble_padding_bottom"
android:left="@dimen/message_bubble_padding_sides"

View File

@@ -2,6 +2,7 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="400dp"
android:height="100dp"
android:autoMirrored="true"
android:viewportHeight="49.5"
android:viewportWidth="194.8">
<path

View File

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

View File

@@ -26,7 +26,8 @@
app:layout_constraintBottom_toBottomOf="@id/explanationText"
app:layout_constraintEnd_toStartOf="@id/explanationText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"/>
<ImageView
android:id="@+id/explanationImage"
@@ -34,8 +35,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:adjustViewBounds="true"
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@dimen/margin_large"
android:paddingEnd="@dimen/margin_large"
android:paddingStart="@dimen/margin_large"
android:paddingTop="@dimen/margin_large"
android:scaleType="fitCenter"
android:src="@drawable/qr_code_explanation"

View File

@@ -7,7 +7,7 @@
android:layout_height="match_parent"
tools:context=".android.login.ChangePasswordActivity">
<RelativeLayout
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -22,9 +22,11 @@
android:id="@+id/current_password_entry_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
app:errorEnabled="true"
app:hintEnabled="false"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:passwordToggleEnabled="true">
<EditText
@@ -40,10 +42,11 @@
android:id="@+id/new_password_entry_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/current_password_entry_wrapper"
android:layout_centerHorizontal="true"
app:errorEnabled="true"
app:hintEnabled="false"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/current_password_entry_wrapper"
app:passwordToggleEnabled="true">
<EditText
@@ -59,10 +62,11 @@
android:id="@+id/new_password_confirm_wrapper"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/new_password_entry_wrapper"
android:layout_centerHorizontal="true"
app:errorEnabled="true"
app:hintEnabled="false"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/new_password_entry_wrapper"
app:passwordToggleEnabled="true">
<EditText
@@ -79,30 +83,33 @@
android:id="@+id/strength_meter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/new_password_confirm_wrapper"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
android:visibility="invisible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/new_password_confirm_wrapper"/>
<Button
android:id="@+id/change_password"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/strength_meter"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/margin_medium"
android:enabled="false"
android:text="@string/change_password"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/strength_meter"
tools:enabled="true"/>
<ProgressBar
android:id="@+id/progress_wheel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/change_password"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
android:visibility="invisible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/change_password"/>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
</ScrollView>

View File

@@ -20,20 +20,23 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="horizontal">
<include layout="@layout/contact_avatar_status"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/contactName"
style="@style/TextAppearance.AppCompat.Widget.ActionBar.Title.Inverse"
android:textColor="@color/action_bar_text"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginStart="@dimen/margin_medium"
android:gravity="center"
tools:text="Contact Name"/>
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/action_bar_text"
tools:text="Contact Name of someone who chose a long name"/>
</LinearLayout>

View File

@@ -1,186 +1,224 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
<android.support.constraint.ConstraintLayout
android:id="@+id/report_form"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="visible"
tools:context=".android.reporting.DevReportActivity">
tools:context=".android.reporting.DevReportActivity"
tools:visibility="invisible">
<include layout="@layout/toolbar"/>
<include
android:id="@+id/appBar"
layout="@layout/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
<android.support.design.widget.TextInputLayout
android:id="@+id/user_comment_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large">
android:layout_marginStart="@dimen/margin_large"
android:layout_marginTop="@dimen/margin_large"
app:hintEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar">
<android.support.design.widget.TextInputLayout
android:id="@+id/user_comment_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/user_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine|textCapSentences"
tools:hint="@string/describe_crash"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/user_email_layout"
<android.support.design.widget.TextInputEditText
android:id="@+id/user_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/user_comment_layout"
android:layout_marginTop="@dimen/margin_small">
android:inputType="textMultiLine|textCapSentences"
android:maxLines="5"
tools:hint="@string/describe_crash"/>
<android.support.design.widget.TextInputEditText
android:id="@+id/user_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/optional_contact_email"
android:inputType="textEmailAddress"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/user_email_layout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
app:hintEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/user_comment_layout">
<CheckBox
android:id="@+id/include_debug_report"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/user_email_layout"
android:layout_marginTop="@dimen/margin_small"
android:layout_toLeftOf="@+id/chevron"
android:checked="false"
android:text="@string/include_debug_report_crash"/>
<Button
android:id="@+id/chevron"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/include_debug_report"
android:text="@string/show"/>
<ScrollView
<android.support.design.widget.TextInputEditText
android:id="@+id/user_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/include_debug_report"
android:layout_marginTop="@dimen/margin_small">
android:hint="@string/optional_contact_email"
android:inputType="textEmailAddress"
android:maxLines="1"/>
<LinearLayout
android:id="@+id/report_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/listitem_height_one_line_avatar"
android:paddingEnd="@dimen/margin_large"
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@dimen/margin_large"
android:paddingStart="@dimen/margin_large"
android:paddingTop="@dimen/margin_small"
android:visibility="gone"/>
</android.support.design.widget.TextInputLayout>
</ScrollView>
<CheckBox
android:id="@+id/include_debug_report"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
android:checked="false"
android:text="@string/include_debug_report_crash"
app:layout_constraintBottom_toBottomOf="@+id/chevron"
app:layout_constraintEnd_toStartOf="@+id/chevron"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/chevron"/>
<ProgressBar
android:id="@+id/progress_wheel"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
<Button
android:id="@+id/chevron"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/show"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/user_email_layout"/>
<ScrollView
android:id="@+id/report_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/include_debug_report">
<LinearLayout
android:id="@+id/report_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/include_debug_report"
android:layout_centerHorizontal="true"
android:indeterminate="true"
android:visibility="gone"/>
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingBottom="@dimen/listitem_height_one_line_avatar"
android:paddingEnd="@dimen/margin_large"
android:paddingStart="@dimen/margin_large"
android:paddingTop="@dimen/margin_small"
android:visibility="gone"
tools:visibility="visible"/>
</RelativeLayout>
</LinearLayout>
</ScrollView>
<RelativeLayout
<ProgressBar
android:id="@+id/progress_wheel"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:indeterminate="true"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/include_debug_report"
tools:visibility="visible"/>
</android.support.constraint.ConstraintLayout>
<android.support.constraint.ConstraintLayout
android:id="@+id/request_report"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clickable="true"
android:gravity="center"
android:padding="@dimen/margin_large"
android:visibility="invisible">
android:visibility="invisible"
tools:visibility="visible">
<TextView
android:id="@+id/crashed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/briar_crashed"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_large"/>
android:textSize="@dimen/text_size_large"
app:layout_constraintBottom_toTopOf="@+id/fault"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:layout_editor_absoluteY="8dp"/>
<TextView
android:id="@+id/fault"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/crashed"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/not_your_fault"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_large"/>
android:textSize="@dimen/text_size_large"
app:layout_constraintBottom_toTopOf="@+id/pleaseSend"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/crashed"/>
<TextView
android:id="@+id/pleaseSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/fault"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/please_send_report"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_large"/>
android:textSize="@dimen/text_size_large"
app:layout_constraintBottom_toTopOf="@+id/encrypted"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/fault"/>
<TextView
android:id="@+id/encrypted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/pleaseSend"
android:layout_marginBottom="@dimen/margin_large"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/report_is_encrypted"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_large"/>
android:textSize="@dimen/text_size_large"
app:layout_constraintBottom_toTopOf="@+id/acceptButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pleaseSend"/>
<Button
android:id="@+id/declineButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/encrypted"
android:text="@string/close"/>
android:text="@string/close"
app:layout_constraintBottom_toBottomOf="@+id/acceptButton"
app:layout_constraintEnd_toStartOf="@+id/acceptButton"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/acceptButton"/>
<Button
android:id="@+id/acceptButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_below="@+id/encrypted"
android:text="@string/send_report"/>
android:layout_marginTop="@dimen/margin_large"
android:text="@string/send_report"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_weight="1"
app:layout_constraintStart_toEndOf="@+id/declineButton"
app:layout_constraintTop_toBottomOf="@+id/encrypted"/>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
</FrameLayout>

View File

@@ -2,57 +2,75 @@
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".android.navdrawer.NavDrawerActivity">
<!-- The first child(root) is the content view -->
<LinearLayout
<android.support.constraint.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
android:layout_height="match_parent">
<include layout="@layout/toolbar"/>
<include
android:id="@+id/appBar"
layout="@layout/toolbar"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<RelativeLayout
<android.support.constraint.ConstraintLayout
android:id="@+id/expiryWarning"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:background="@color/briar_warning_background"
android:orientation="horizontal"
android:padding="@dimen/margin_medium"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/appBar"
tools:visibility="visible">
<TextView
android:id="@+id/expiryWarningText"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_toLeftOf="@+id/expiryWarningClose"
android:text="@plurals/expiry_warning"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/text_size_small"/>
android:textSize="@dimen/text_size_small"
app:layout_constraintEnd_toStartOf="@+id/expiryWarningClose"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
android:id="@+id/expiryWarningClose"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:layout_height="0dp"
android:contentDescription="@string/close"
android:scaleType="center"
android:src="@drawable/ic_close"
android:tint="@color/briar_text_tertiary_inverse"/>
android:tint="@color/briar_text_tertiary_inverse"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
<FrameLayout
android:id="@+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/expiryWarning"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
<!-- The second child is the menu -->
<include

View File

@@ -36,6 +36,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="8dp"
android:gravity="center"
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@dimen/margin_large"
android:text="@string/startup_open_database"

View File

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

View File

@@ -5,24 +5,20 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingLeft="@dimen/margin_activity_horizontal"
android:paddingRight="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
android:padding="@dimen/margin_activity_vertical">
<android.support.design.widget.TextInputLayout
android:id="@+id/password_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
app:errorEnabled="true"
app:hintEnabled="false"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:passwordToggleEnabled="true">
<EditText
@@ -40,31 +36,36 @@
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/password_layout"
android:layout_marginTop="@dimen/margin_medium"
android:onClick="onSignInClick"
android:text="@string/sign_in_button"/>
android:text="@string/sign_in_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/password_layout"/>
<ProgressBar
android:id="@+id/progress_wheel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/btn_sign_in"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/btn_sign_in"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/btn_sign_in"/>
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/btn_sign_in"
android:layout_centerHorizontal="true"
android:layout_marginTop="@dimen/margin_large"
android:clickable="true"
android:focusable="true"
android:onClick="onForgottenPasswordClick"
android:text="@string/forgotten_password"
android:textColor="?android:attr/textColorLink"/>
android:textColor="?android:attr/textColorLink"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_sign_in"/>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>
</ScrollView>

View File

@@ -6,7 +6,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_small"
android:padding="@dimen/margin_medium"
tools:context=".android.blog.RssFeedImportActivity">
<android.support.v7.widget.CardView
@@ -35,7 +35,8 @@
android:id="@+id/importButton"
style="@style/BriarButton"
android:enabled="false"
android:text="@string/blogs_rss_feeds_import_button"/>
android:text="@string/blogs_rss_feeds_import_button"
tools:enabled="true"/>
<ProgressBar
android:id="@+id/progressBar"
@@ -43,6 +44,7 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:visibility="gone"/>
android:visibility="gone"
tools:visibility="gone"/>
</LinearLayout>

View File

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

View File

@@ -1,178 +1,179 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:fillViewport="true"
android:padding="@dimen/margin_medium"
tools:ignore="HardcodedText">
<ScrollView
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:padding="8dp"
tools:ignore="HardcodedText">
android:layout_height="wrap_content">
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
<TextView
android:id="@+id/textViewContacts"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of contacts"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/textViewContacts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of contacts"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<SeekBar
android:id="@+id/seekBarContacts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="49"
android:progress="20"
app:layout_constraintEnd_toStartOf="@+id/textViewContactsSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewContacts"/>
<SeekBar
android:id="@+id/seekBarContacts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="49"
android:progress="20"
app:layout_constraintEnd_toStartOf="@+id/textViewContactsSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewContacts"/>
<TextView
android:id="@+id/textViewContactsSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarContacts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewContacts"/>
<TextView
android:id="@+id/textViewContactsSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarContacts"
app:layout_constraintEnd_toEndOf="parent"/>
<TextView
android:id="@+id/textViewMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of messages per contact"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarContacts"/>
<SeekBar
android:id="@+id/seekBarMessages"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="50"
android:paddingTop="5dp"
android:progress="20"
app:layout_constraintEnd_toStartOf="@+id/textViewMessagesSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewMessages"/>
<TextView
android:id="@+id/textViewMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of messages per contact"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarContacts"/>
<TextView
android:id="@+id/textViewMessagesSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarMessages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewMessages"/>
<SeekBar
android:id="@+id/seekBarMessages"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="50"
android:paddingTop="5dp"
android:progress="15"
app:layout_constraintEnd_toStartOf="@+id/textViewMessagesSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewMessages"/>
<TextView
android:id="@+id/textViewBlogPosts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of blog posts"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarMessages"/>
<TextView
android:id="@+id/textViewMessagesSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarMessages"
app:layout_constraintEnd_toEndOf="parent"/>
<SeekBar
android:id="@+id/seekBarBlogPosts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="50"
android:paddingTop="5dp"
android:progress="20"
app:layout_constraintEnd_toStartOf="@+id/TextViewBlogPostsSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewBlogPosts"/>
<TextView
android:id="@+id/textViewBlogPosts"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of blog posts"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarMessages"/>
<TextView
android:id="@+id/TextViewBlogPostsSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarBlogPosts"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewBlogPosts"/>
<SeekBar
android:id="@+id/seekBarBlogPosts"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="50"
android:paddingTop="5dp"
android:progress="30"
app:layout_constraintEnd_toStartOf="@+id/TextViewBlogPostsSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewBlogPosts"/>
<TextView
android:id="@+id/textViewForums"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of forums"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarBlogPosts"/>
<TextView
android:id="@+id/TextViewBlogPostsSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarBlogPosts"
app:layout_constraintEnd_toEndOf="parent"/>
<SeekBar
android:id="@+id/seekBarForums"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="50"
android:paddingTop="5dp"
android:progress="5"
app:layout_constraintEnd_toStartOf="@+id/TextViewForumsSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewForums"/>
<TextView
android:id="@+id/textViewForums"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of forums"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarBlogPosts"/>
<TextView
android:id="@+id/TextViewForumsSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="5"
app:layout_constraintBottom_toBottomOf="@+id/seekBarForums"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewForums"/>
<SeekBar
android:id="@+id/seekBarForums"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="10"
android:paddingTop="5dp"
android:progress="3"
app:layout_constraintEnd_toStartOf="@+id/TextViewForumsSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewForums"/>
<TextView
android:id="@+id/textViewForumMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of forum messages"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarForums"/>
<TextView
android:id="@+id/TextViewForumsSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarForums"
app:layout_constraintEnd_toEndOf="parent"/>
<SeekBar
android:id="@+id/seekBarForumMessages"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="50"
android:paddingTop="5dp"
android:progress="20"
app:layout_constraintEnd_toStartOf="@+id/TextViewForumMessagesSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewForumMessages"/>
<TextView
android:id="@+id/textViewForumMessages"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_medium"
android:text="Number of forum messages"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarForums"/>
<TextView
android:id="@+id/TextViewForumMessagesSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarForumMessages"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewForumMessages"/>
<SeekBar
android:id="@+id/seekBarForumMessages"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:max="50"
android:paddingTop="5dp"
android:progress="30"
app:layout_constraintEnd_toStartOf="@+id/TextViewForumMessagesSb"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textViewForumMessages"/>
<Button
android:id="@+id/buttonCreateTestData"
style="@style/BriarButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Create test data"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarForumMessages"
app:layout_constraintVertical_bias="1.0"/>
</android.support.constraint.ConstraintLayout>
<TextView
android:id="@+id/TextViewForumMessagesSb"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ems="2"
android:text="20"
app:layout_constraintBottom_toBottomOf="@+id/seekBarForumMessages"
app:layout_constraintEnd_toEndOf="parent"/>
<Button
android:id="@+id/buttonCreateTestData"
style="@style/BriarButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="10dp"
android:text="Create test data"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/seekBarForumMessages"/>
</android.support.constraint.ConstraintLayout>
</ScrollView>
</android.support.constraint.ConstraintLayout>
</ScrollView>

View File

@@ -26,14 +26,14 @@
android:id="@+id/upButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
android:layout_gravity="top|right|end"
app:direction="up"/>
<org.briarproject.briar.android.view.UnreadMessageButton
android:id="@+id/downButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|right"
android:layout_gravity="bottom|right|end"
app:direction="down"/>
</FrameLayout>

View File

@@ -13,8 +13,8 @@
android:layout_height="match_parent"
android:gravity="bottom"
app:buttonText="@string/blogs_publish_blog_post"
app:hint="@string/blogs_write_blog_post_body_hint"
app:fillHeight="true"/>
app:fillHeight="true"
app:hint="@string/blogs_write_blog_post_body_hint"/>
<ProgressBar
android:id="@+id/progressBar"

View File

@@ -1,7 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<merge
android:id="@+id/merge"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:layout_height="wrap_content"
tools:layout_width="match_parent"
tools:parentTag="android.support.constraint.ConstraintLayout"
tools:showIn="@layout/list_item_blog_post">
<de.hdodenhof.circleimageview.CircleImageView
@@ -9,54 +14,64 @@
style="@style/BriarAvatar"
android:layout_width="@dimen/blogs_avatar_normal_size"
android:layout_height="@dimen/blogs_avatar_normal_size"
android:layout_alignTop="@+id/authorName"
android:layout_marginRight="@dimen/margin_medium"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/authorName"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round"/>
<ImageView
android:id="@+id/avatarIcon"
android:layout_width="@dimen/blogs_avatar_icon_size"
android:layout_height="@dimen/blogs_avatar_icon_size"
android:layout_alignBottom="@+id/avatar"
android:layout_alignRight="@+id/avatar"
android:background="@drawable/bubble_white"
android:contentDescription="@string/blogs_reblog_button"
android:padding="2dp"
android:scaleType="fitCenter"
android:src="@drawable/ic_repeat"
android:visibility="invisible"
tools:ignore="ContentDescription"/>
app:layout_constraintBottom_toBottomOf="@+id/avatar"
app:layout_constraintEnd_toEndOf="@+id/avatar"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/authorName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:layout_marginEnd="@dimen/margin_medium"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:layout_marginStart="@dimen/margin_medium"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
tools:text="Author Name"/>
app:layout_constrainedWidth="true"
app:layout_constraintEnd_toStartOf="@+id/trustIndicator"
app:layout_constraintStart_toEndOf="@+id/avatar"
app:layout_constraintTop_toTopOf="parent"
tools:text="Author View that can have a long name"/>
<org.briarproject.briar.android.view.TrustIndicatorView
android:id="@+id/trustIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBottom="@+id/authorName"
android:layout_alignTop="@+id/authorName"
android:layout_marginLeft="@dimen/margin_small"
android:layout_toRightOf="@id/authorName"
android:scaleType="center"
app:layout_constraintBottom_toBottomOf="@+id/authorName"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/authorName"
app:layout_constraintTop_toTopOf="@+id/authorName"
app:layout_goneMarginEnd="0dp"
app:layout_goneMarginStart="0dp"
tools:src="@drawable/trust_indicator_verified"/>
<TextView
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/authorName"
android:layout_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:gravity="bottom"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_tiny"
tools:text="yesterday"/>
app:layout_constraintStart_toStartOf="@+id/authorName"
app:layout_constraintTop_toBottomOf="@+id/authorName"
tools:text="yesterday"
tools:visibility="visible"/>
</merge>

View File

@@ -1,6 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:id="@+id/relativeLayout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -62,8 +61,6 @@
android:textSize="@dimen/text_size_large"
app:layout_constraintBottom_toTopOf="@+id/emptyAction"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/emptyImage"
tools:text="@string/no_contacts"/>
@@ -79,8 +76,6 @@
android:textSize="@dimen/text_size_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/emptyText"
tools:text="@string/no_contacts_action"/>

View File

@@ -9,6 +9,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="@dimen/margin_activity_horizontal"
android:layout_marginStart="@dimen/margin_activity_horizontal"
android:layout_marginTop="@dimen/margin_activity_vertical"
android:checked="false"
android:text="@string/don_t_ask_again"/>

View File

@@ -19,7 +19,7 @@
android:id="@+id/contactStatus"
android:layout_width="15dp"
android:layout_height="15dp"
android:layout_gravity="bottom|right"
android:layout_gravity="bottom|end|right"
android:scaleType="fitCenter"
tools:ignore="ContentDescription"
tools:src="@drawable/contact_online"/>

View File

@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_large">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/margin_large">
<ScrollView
android:layout_width="wrap_content"
@@ -13,7 +15,8 @@
<TextView
android:id="@+id/screen_filter_message"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
android:layout_height="wrap_content"
tools:text="@string/screen_filter_body"/>
</ScrollView>
@@ -21,8 +24,8 @@
android:id="@+id/screen_filter_checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="0"
android:layout_marginTop="@dimen/margin_large"
android:layout_weight="0"
android:text="@string/screen_filter_allow"/>
</LinearLayout>

View File

@@ -5,7 +5,7 @@
android:layout_height="match_parent">
<FrameLayout
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:descendantFocusability="beforeDescendants"
android:focusable="true"
@@ -17,7 +17,7 @@
android:id="@+id/postLayout"
style="@style/BriarCard"
layout="@layout/list_item_blog_post"
android:layout_width="wrap_content"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<ProgressBar

View File

@@ -11,6 +11,8 @@
android:layout_width="128dp"
android:layout_height="128dp"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:src="@drawable/alerts_and_states_error"
@@ -25,6 +27,8 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:text="@string/sorry"

View File

@@ -39,7 +39,7 @@
android:layout_height="wrap_content"
android:gravity="center"
android:paddingTop="@dimen/margin_large"
tools:text="Connection failed"/>
tools:text="@string/waiting_for_contact_to_scan"/>
</LinearLayout>
<org.briarproject.briar.android.view.QrCodeView
@@ -47,6 +47,9 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/white"/>
android:background="@android:color/white"
tools:visibility="visible"/>
</LinearLayout>
</FrameLayout>

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -47,27 +48,28 @@
</LinearLayout>
<LinearLayout
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_height="@dimen/button_size">
<Button
android:id="@+id/cancelButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="@string/cancel"/>
android:text="@string/cancel"
app:layout_constraintEnd_toStartOf="@+id/openButton"
app:layout_constraintStart_toStartOf="parent"/>
<Button
android:id="@+id/openButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.5"
android:text="@string/link_warning_open_link"/>
android:text="@string/link_warning_open_link"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/cancelButton"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
</LinearLayout>

View File

@@ -10,19 +10,13 @@
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingLeft="@dimen/margin_activity_horizontal"
android:paddingRight="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
android:padding="@dimen/margin_activity_vertical">
<org.briarproject.briar.android.login.DozeView
android:id="@+id/dozeView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
@@ -30,8 +24,8 @@
android:id="@+id/huaweiView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/dozeView"/>
<Button
@@ -42,8 +36,8 @@
android:enabled="false"
android:text="@string/create_account_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/huaweiView"
app:layout_constraintVertical_bias="1.0"
tools:enabled="true"/>
@@ -55,8 +49,8 @@
android:layout_height="wrap_content"
android:visibility="invisible"
app:layout_constraintBottom_toBottomOf="@+id/next"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/next"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -7,113 +7,125 @@
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="wrap_content">
<LinearLayout
android:layout_width="match_parent"
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact1"
style="@style/BriarAvatar"
android:layout_width="42dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_activity_horizontal"
android:layout_margin="@dimen/margin_large"
app:layout_constraintEnd_toStartOf="@+id/introductionIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/nameContact1"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
android:layout_marginTop="@dimen/margin_medium"
android:gravity="center"
android:orientation="horizontal">
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
app:layout_constraintEnd_toStartOf="@+id/introductionIcon"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/avatarContact1"
tools:text="Contact 1"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_horizontal"
android:orientation="vertical">
<android.support.v7.widget.AppCompatImageView
android:id="@+id/introductionIcon"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:src="@drawable/ic_contact_introduction"
app:layout_constraintBottom_toBottomOf="@+id/barrier"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/avatarContact2"
app:tint="?attr/colorControlNormal"
tools:ignore="ContentDescription"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact1"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@mipmap/ic_launcher_round"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact2"
style="@style/BriarAvatar"
android:layout_width="42dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/introductionIcon"
app:layout_constraintTop_toTopOf="parent"
tools:src="@mipmap/ic_launcher_round"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/nameContact1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
tools:text="Contact 1"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/nameContact2"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
android:layout_marginTop="@dimen/margin_medium"
android:gravity="center"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/introductionIcon"
app:layout_constraintTop_toBottomOf="@+id/avatarContact2"
tools:text="Contact 2 can have quite a long name"/>
</LinearLayout>
<android.support.v7.widget.AppCompatImageView
android:id="@+id/introductionIcon"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:src="@drawable/ic_contact_introduction"
app:tint="?attr/colorControlNormal"
tools:ignore="ContentDescription"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="top|center_horizontal"
android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact2"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
tools:src="@mipmap/ic_launcher_round"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/nameContact2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_small"
android:gravity="center"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
tools:text="Contact 2"/>
</LinearLayout>
</LinearLayout>
<android.support.constraint.Barrier
android:id="@+id/barrier"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:barrierDirection="bottom"
app:constraint_referenced_ids="nameContact1,nameContact2"/>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_margin="@dimen/margin_large"
app:layout_constraintBottom_toTopOf="@+id/introductionNotPossibleView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/barrier"
tools:visibility="gone"/>
<TextView
android:id="@+id/introductionNotPossibleView"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_activity_horizontal"
android:layout_margin="@dimen/margin_large"
android:text="@string/introduction_not_possible"
android:textSize="@dimen/text_size_large"
android:visibility="gone"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/progressBar"
tools:visibility="visible"/>
<org.briarproject.briar.android.view.LargeTextInputView
android:id="@+id/introductionMessageView"
android:layout_width="match_parent"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:visibility="gone"
app:buttonText="@string/introduction_button"
app:hint="@string/introduction_message_hint"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/introductionNotPossibleView"
app:maxLines="5"
tools:visibility="visible"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
</ScrollView>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
@@ -9,27 +9,32 @@
<View
android:id="@+id/inputDivider"
style="@style/Divider.Horizontal"/>
style="@style/Divider.Horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/authorView"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:padding="@dimen/listitem_vertical_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:persona="commenter"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/bodyView"
android:layout_width="wrap_content"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_below="@+id/authorView"
android:paddingBottom="@dimen/listitem_vertical_margin"
android:paddingLeft="@dimen/listitem_vertical_margin"
android:paddingRight="@dimen/listitem_vertical_margin"
android:paddingEnd="@dimen/listitem_vertical_margin"
android:paddingStart="@dimen/listitem_vertical_margin"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/authorView"
tools:text="This is a comment that appears below a blog post. Usually, it is expected to be rather short. Not much longer than this one."/>
</RelativeLayout>
</android.support.constraint.ConstraintLayout>

View File

@@ -9,63 +9,70 @@
android:layout_height="wrap_content"
android:foreground="?attr/selectableItemBackground">
<LinearLayout
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/rebloggerView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:padding="@dimen/listitem_vertical_margin">
android:layout_marginBottom="@dimen/listitem_horizontal_margin"
android:layout_marginEnd="@dimen/listitem_vertical_margin"
android:layout_marginLeft="@dimen/listitem_vertical_margin"
android:layout_marginRight="@dimen/listitem_vertical_margin"
android:layout_marginStart="@dimen/listitem_vertical_margin"
android:layout_marginTop="@dimen/listitem_vertical_margin"
app:layout_constraintEnd_toStartOf="@+id/commentView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:persona="reblogger"/>
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/rebloggerView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginBottom="@dimen/listitem_horizontal_margin"
android:layout_toLeftOf="@+id/commentView"
app:persona="reblogger"/>
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/authorView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_horizontal_margin"
android:layout_marginEnd="@dimen/listitem_vertical_margin"
android:layout_marginLeft="@dimen/listitem_vertical_margin"
android:layout_marginRight="@dimen/listitem_vertical_margin"
android:layout_marginStart="@dimen/listitem_vertical_margin"
android:layout_marginTop="@dimen/listitem_vertical_margin"
app:layout_constraintEnd_toStartOf="@+id/commentView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/rebloggerView"/>
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/authorView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_below="@+id/rebloggerView"
android:layout_marginBottom="@dimen/listitem_vertical_margin"
android:layout_toLeftOf="@+id/commentView"/>
<android.support.v7.widget.AppCompatImageButton
android:id="@+id/commentView"
android:layout_width="@dimen/button_size"
android:layout_height="@dimen/button_size"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/blogs_reblog_comment_hint"
android:src="@drawable/ic_repeat"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:tint="?attr/colorControlNormal"/>
<android.support.v7.widget.AppCompatImageButton
android:id="@+id/commentView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignParentTop="true"
android:background="?attr/selectableItemBackground"
android:contentDescription="@string/blogs_reblog_comment_hint"
android:padding="@dimen/margin_small"
android:src="@drawable/ic_repeat"
app:tint="?attr/colorControlNormal"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/bodyView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/authorView"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a body text that shows the content of a blog post.\n\nThis one is not short, but it is also not too long."/>
</RelativeLayout>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/bodyView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/listitem_vertical_margin"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/authorView"
tools:text="This is a body text that shows the content of a blog post.\n\nThis one is not short, but it is also not too long."/>
<!-- TODO replace with RecyclerView -->
<LinearLayout
android:id="@+id/commentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
android:layout_marginTop="@dimen/listitem_vertical_margin"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/bodyView">
<include
layout="@layout/list_item_blog_comment"
@@ -74,6 +81,6 @@
</LinearLayout>
</LinearLayout>
</android.support.constraint.ConstraintLayout>
</android.support.v7.widget.CardView>

View File

@@ -1,95 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<android.support.constraint.ConstraintLayout
android:id="@+id/linearLayout4"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true"
android:orientation="vertical">
android:focusable="true">
<RelativeLayout
android:layout_width="match_parent"
<FrameLayout
android:id="@+id/avatarFrameView"
android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_margin="@dimen/listitem_horizontal_margin"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_gravity="bottom|left|start"
tools:src="@mipmap/ic_launcher_round"/>
</FrameLayout>
<TextView
android:id="@+id/unreadCountView"
android:layout_width="wrap_content"
android:layout_height="@dimen/unread_bubble_size"
android:background="@drawable/bubble"
android:gravity="center"
android:minWidth="@dimen/unread_bubble_size"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/unread_bubble_text_size"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="@+id/avatarFrameView"
app:layout_constraintTop_toTopOf="@+id/avatarFrameView"
tools:text="123"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/nameView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:paddingTop="@dimen/listitem_horizontal_margin">
android:layout_marginEnd="@dimen/margin_medium"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:layout_marginStart="@dimen/margin_medium"
android:layout_marginTop="@dimen/listitem_vertical_margin"
android:paddingEnd="@dimen/margin_medium"
android:paddingStart="@dimen/margin_medium"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"
app:layout_constraintBottom_toTopOf="@+id/dateView"
app:layout_constraintEnd_toStartOf="@+id/bulbView"
app:layout_constraintStart_toEndOf="@+id/avatarFrameView"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="This is a name of a contact"/>
<FrameLayout
android:id="@+id/avatarFrameView"
android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin">
<TextView
android:id="@+id/dateView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/listitem_vertical_margin"
android:layout_marginEnd="@dimen/margin_medium"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:layout_marginStart="@dimen/margin_medium"
android:paddingEnd="@dimen/margin_medium"
android:paddingStart="@dimen/margin_medium"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintEnd_toStartOf="@+id/bulbView"
app:layout_constraintStart_toEndOf="@+id/avatarFrameView"
app:layout_constraintTop_toBottomOf="@+id/nameView"
tools:text="Dec 24"/>
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarView"
style="@style/BriarAvatar"
android:layout_width="@dimen/listitem_picture_size"
android:layout_height="@dimen/listitem_picture_size"
android:layout_gravity="bottom|left"
tools:src="@mipmap/ic_launcher_round"/>
<ImageView
android:id="@+id/bulbView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/listitem_horizontal_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"
tools:src="@drawable/contact_connected"/>
<TextView
android:id="@+id/unreadCountView"
android:layout_width="wrap_content"
android:layout_height="@dimen/unread_bubble_size"
android:layout_gravity="right|top"
android:background="@drawable/bubble"
android:gravity="center"
android:minWidth="@dimen/unread_bubble_size"
android:textColor="@color/briar_text_primary_inverse"
android:textSize="@dimen/unread_bubble_text_size"
android:textStyle="bold"
tools:text="123"/>
<View
android:id="@+id/divider"
style="@style/Divider.ContactList"
android:layout_width="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="@+id/nameView"/>
</FrameLayout>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:layout_toEndOf="@+id/avatarFrameView"
android:layout_toLeftOf="@+id/bulbView"
android:layout_toRightOf="@+id/avatarFrameView"
android:orientation="vertical">
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/nameView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:maxLines="2"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"
tools:text="This is a name of a contact"/>
<TextView
android:id="@+id/dateView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small"
tools:text="Dec 24"/>
</LinearLayout>
<ImageView
android:id="@+id/bulbView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
tools:src="@drawable/contact_connected"/>
</RelativeLayout>
<View style="@style/Divider.ContactList"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>

View File

@@ -23,7 +23,9 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/margin_medium"
android:layout_marginLeft="@dimen/margin_medium"
android:layout_marginRight="@dimen/margin_medium"
android:layout_marginStart="@dimen/margin_medium"
android:layout_weight="1"
android:maxLines="2"
@@ -36,7 +38,9 @@
android:layout_width="@dimen/listitem_horizontal_margin"
android:layout_height="@dimen/listitem_horizontal_margin"
android:layout_gravity="center_vertical"
android:layout_marginEnd="@dimen/listitem_horizontal_margin"
android:layout_marginRight="@dimen/listitem_horizontal_margin"
tools:ignore="ContentDescription"
tools:src="@drawable/contact_connected"/>
</LinearLayout>

View File

@@ -1,17 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
<android.support.constraint.ConstraintLayout
android:id="@+id/layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="@dimen/message_bubble_margin"
android:layout_marginEnd="@dimen/message_bubble_margin_non_tail"
android:layout_marginLeft="@dimen/message_bubble_margin_tail"
android:layout_marginRight="@dimen/message_bubble_margin_non_tail"
android:layout_marginStart="@dimen/message_bubble_margin_tail"
android:layout_marginTop="@dimen/message_bubble_margin"
android:background="@drawable/msg_in"
android:elevation="@dimen/message_bubble_elevation"
android:orientation="vertical">
android:elevation="@dimen/message_bubble_elevation">
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/text"
@@ -19,6 +21,9 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
app:layout_constraintBottom_toTopOf="@+id/time"
app:layout_constraintEnd_toEndOf="@+id/time"
app:layout_constraintTop_toTopOf="parent"
tools:text="Short message"/>
<TextView
@@ -26,8 +31,9 @@
style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="right|end"
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text"
tools:text="Dec 24, 13:37"/>
</LinearLayout>
</android.support.constraint.ConstraintLayout>

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