Compare commits

..

6 Commits

Author SHA1 Message Date
Torsten Grote
2d5f5d4419 WIP: FakeTestData proof-of-concept 2018-09-19 10:51:47 -03:00
Torsten Grote
8393513871 Remove code from TestDataCreator that breaks encapsulation 2018-09-19 10:50:09 -03:00
Torsten Grote
38270fbee8 Throw AssertionError when creating an account while a database key is in memory 2018-09-18 17:53:41 -03:00
Torsten Grote
65c0646812 Refactor tests so that all test data is created in the first test 2018-09-18 17:41:40 -03:00
Torsten Grote
0416583e8c Split up UI and Screenshot tests
Closes #1377
2018-09-18 17:41:40 -03:00
Torsten Grote
91f8c801b8 Create Screenshot of Conversation for Manual 2018-09-18 17:41:40 -03:00
218 changed files with 3676 additions and 3024 deletions

View File

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

View File

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

View File

@@ -0,0 +1,9 @@
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,6 +1,5 @@
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;
@@ -14,7 +13,7 @@ import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_K
*/
@Immutable
@NotNullByDefault
public class Author implements Nameable {
public class Author {
public enum Status {
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES

View File

@@ -3,13 +3,7 @@ package org.briarproject.bramble.api.keyagreement;
public interface KeyAgreementConstants {
/**
* 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.
* The current version of the BQP protocol. Version number 89 is reserved.
*/
byte PROTOCOL_VERSION = 4;

View File

@@ -1,20 +0,0 @@
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,7 +21,6 @@ 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;
@@ -44,11 +43,8 @@ class PayloadParserImpl implements PayloadParser {
// First byte: the protocol version
int protocolVersion = in.read();
if (protocolVersion == -1) throw new FormatException();
if (protocolVersion != PROTOCOL_VERSION) {
boolean tooOld = protocolVersion < PROTOCOL_VERSION ||
protocolVersion == BETA_PROTOCOL_VERSION;
throw new UnsupportedVersionException(tooOld);
}
if (protocolVersion != PROTOCOL_VERSION)
throw new UnsupportedVersionException();
// 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 85.229.131.78:444 50E433CCC5FEC11CC34CB4D92033561E065EA106
Bridge 178.62.62.193:8443 391B1F9B6A28A1C5FAE1872283985F975E5DB029
Bridge 45.76.29.92:8443 ECF1DD51A46FDEF2C50CED992EEEAE8DED18DA0C
Bridge 97.107.131.168:65341 DCDA8A5F1E2C50A6756A58462E5CF4B6B2DFDE26
Bridge 85.229.131.78:444 50E433CCC5FEC11CC34CB4D92033561E065EA106

View File

@@ -1,156 +0,0 @@
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,13 +10,9 @@ 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.briarproject.bramble.util.OsUtils;
import org.junit.After;
import org.junit.AfterClass;
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;
@@ -33,20 +29,13 @@ 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 {
@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 long TIMEOUT = SECONDS.toMillis(23);
private final static Logger LOG =
Logger.getLogger(BridgeTest.class.getName());
@@ -64,23 +53,17 @@ public class BridgeTest extends BrambleTestCase {
@Inject
Clock clock;
private final File torDir = getTestDirectory();
private final String bridge;
private List<String> bridges;
private LinuxTorPluginFactory factory;
private final static File torDir = getTestDirectory();
public BridgeTest(String bridge) {
this.bridge = bridge;
}
private volatile String currentBridge = null;
@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);
@@ -89,6 +72,7 @@ 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) {
@@ -102,7 +86,7 @@ public class BridgeTest extends BrambleTestCase {
@Override
public List<String> getBridges() {
return singletonList(bridge);
return singletonList(currentBridge);
}
};
factory = new LinuxTorPluginFactory(ioExecutor, networkManager,
@@ -110,18 +94,25 @@ public class BridgeTest extends BrambleTestCase {
resourceProvider, bridgeProvider, clock, torDir);
}
@After
public void tearDown() {
@AfterClass
public static 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,7 +4,6 @@ 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;
@@ -23,5 +22,4 @@ public interface BrambleJavaIntegrationTestComponent {
void inject(BridgeTest init);
CircumventionProvider getCircumventionProvider();
}

View File

@@ -22,8 +22,8 @@ android {
defaultConfig {
minSdkVersion 15
targetSdkVersion 26
versionCode 10102
versionName "1.1.2"
versionCode 10101
versionName "1.1.1"
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.3'
implementation 'com.android.support.constraint:constraint-layout:1.1.0'
implementation('ch.acra:acra:4.9.1') {
exclude module: 'support-v4'
@@ -140,6 +140,9 @@ 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"
}
@@ -153,9 +156,8 @@ 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-") && !exceptions.contains(dir.name)) {
if (dir.name.startsWith("values-") && !dir.name.endsWith("night") && !dir.name.endsWith("v21")) {
folders.add(dir.name.substring(7).replace("-r", "-"))
}
}

View File

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

View File

@@ -13,3 +13,4 @@
-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()
.appModule(new AppModule(this)).build();
.testAppModule(new TestAppModule(this)).build();
// We need to load the eager singletons directly after making the
// dependency graphs
BrambleCoreModule.initEagerSingletons(component);

View File

@@ -21,6 +21,8 @@ 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
@@ -30,7 +32,8 @@ public abstract class UiTest {
BriarTestComponentApplication app =
(BriarTestComponentApplication) getTargetContext()
.getApplicationContext();
inject((BriarUiTestComponent) app.getApplicationComponent());
alice = (BriarUiTestComponent) app.getApplicationComponent();
inject(alice);
}
protected abstract void inject(BriarUiTestComponent component);

View File

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

View File

@@ -0,0 +1,95 @@
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,23 +5,59 @@ 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;
@@ -30,9 +66,17 @@ 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)
@@ -48,11 +92,49 @@ 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
@@ -103,26 +185,132 @@ 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();
createTestData();
intended(hasComponent(NavDrawerActivity.class.getName()));
// 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 createTestData() {
try {
createTestDataExceptions();
} catch (DbException | FormatException e) {
throw new AssertionError(e);
}
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 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

@@ -0,0 +1,161 @@
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

@@ -0,0 +1,71 @@
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,10 +13,9 @@
<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" />
@@ -26,7 +25,6 @@
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,9 +41,13 @@ 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;
@@ -231,6 +235,19 @@ 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();
}
@@ -310,7 +327,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.private_message_notification_text, contactTotal,
contactTotal));
b.setNumber(contactTotal);
b.setNotificationCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, CONTACT_URI);
Set<ContactId> contacts = contactCounts.keySet();
@@ -412,7 +431,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.group_message_notification_text, groupTotal,
groupTotal));
b.setNumber(groupTotal);
b.setNotificationCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, GROUP_URI);
Set<GroupId> groups = groupCounts.keySet();
@@ -483,7 +504,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.forum_post_notification_text, forumTotal,
forumTotal));
b.setNumber(forumTotal);
b.setNotificationCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, FORUM_URI);
Set<GroupId> forums = forumCounts.keySet();
@@ -552,7 +575,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
R.plurals.blog_post_notification_text, blogTotal,
blogTotal));
b.setNumber(blogTotal);
b.setNotificationCategory(CATEGORY_SOCIAL);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_SOCIAL, showOnLockScreen);
if (mayAlertAgain) setAlertProperties(b);
setDeleteIntent(b, BLOG_URI);
// Touching the notification shows the combined blog feed
@@ -593,7 +618,9 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
b.setContentText(appContext.getResources().getQuantityString(
R.plurals.introduction_notification_text, introductionTotal,
introductionTotal));
b.setNotificationCategory(CATEGORY_MESSAGE);
boolean showOnLockScreen =
settings.getBoolean(PREF_NOTIFY_LOCK_SCREEN, false);
b.setLockscreenVisibility(CATEGORY_MESSAGE, showOnLockScreen);
setAlertProperties(b);
setDeleteIntent(b, INTRODUCTION_URI);
// Touching the notification shows the contact list

View File

@@ -1,7 +1,6 @@
package org.briarproject.briar.android;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.StrictMode;
@@ -11,24 +10,9 @@ 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;
@@ -39,25 +23,18 @@ 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
@Module(includes = PluginConfigModule.class)
public class AppModule {
static class EagerSingletons {
@@ -95,47 +72,6 @@ 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

@@ -0,0 +1,81 @@
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,8 +101,6 @@ 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;
BlogInvitationResponse r = b.getMessageHeader();
InvitationResponse r = b.getResponse();
if (r.getShareableId().equals(groupId) && r.wasAccepted()) {
LOG.info("Blog invitation accepted");
onBlogInvitationAccepted(b.getContactId());

View File

@@ -1,6 +1,7 @@
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;
@@ -35,10 +36,19 @@ 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;
@@ -47,7 +57,6 @@ 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;
@@ -113,7 +122,7 @@ public class ContactListFragment extends BaseFragment implements EventListener {
ContactId contactId = item.getContact().getId();
i.putExtra(CONTACT_ID, contactId.getInt());
if (SDK_INT >= 23) {
if (Build.VERSION.SDK_INT >= 23) {
ContactListItemViewHolder holder =
(ContactListItemViewHolder) list
.getRecyclerView()
@@ -247,16 +256,41 @@ 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, PrivateMessageHeader h) {
private void updateItem(ContactId c, BaseMessageHeader h) {
runOnUiThreadUnlessDestroyed(() -> {
adapter.incrementRevision();
int position = adapter.findItemPosition(c);
ContactListItem item = adapter.getItemAt(position);
if (item != null) {
item.addMessage(h);
ConversationItem i = ConversationItem.from(getContext(), h);
item.addMessage(i);
adapter.updateItemAt(position, item);
}
});

View File

@@ -3,7 +3,6 @@ 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;
@@ -23,10 +22,11 @@ public class ContactListItem extends ContactItem {
this.timestamp = count.getLatestMsgTime();
}
void addMessage(PrivateMessageHeader h) {
void addMessage(ConversationItem message) {
empty = false;
if (h.getTimestamp() > timestamp) timestamp = h.getTimestamp();
if (!h.isRead()) unread++;
if (message.getTime() > timestamp) timestamp = message.getTime();
if (!message.isRead())
unread++;
}
boolean isEmpty() {

View File

@@ -1,11 +1,8 @@
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;
@@ -53,7 +50,6 @@ 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;
@@ -66,15 +62,24 @@ 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.messaging.ConversationManager;
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.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;
@@ -82,10 +87,13 @@ 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;
@@ -112,8 +120,7 @@ import static uk.co.samuelwall.materialtaptargetprompt.MaterialTapTargetPrompt.S
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ConversationActivity extends BriarActivity
implements EventListener, ConversationListener, TextInputListener,
BodyCache {
implements EventListener, ConversationListener, TextInputListener {
public static final String CONTACT_ID = "briar.CONTACT_ID";
@@ -131,9 +138,7 @@ 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;
@@ -142,14 +147,24 @@ 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;
@@ -166,6 +181,8 @@ public class ConversationActivity extends BriarActivity
private volatile ContactId contactId;
@Nullable
private volatile String contactName;
@Nullable
private volatile AuthorId contactAuthorId;
@Nullable
private volatile GroupId messagingGroupId;
@@ -194,7 +211,6 @@ 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));
@@ -278,9 +294,9 @@ public class ConversationActivity extends BriarActivity
runOnDbThread(() -> {
try {
long start = now();
if (contactAuthorId == null) {
if (contactName == null || contactAuthorId == null) {
Contact contact = contactManager.getContact(contactId);
contactName.postValue(contact.getAuthor().getName());
contactName = contact.getAuthor().getName();
contactAuthorId = contact.getAuthor().getId();
}
logDuration(LOG, "Loading contact", start);
@@ -294,13 +310,12 @@ 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.getValue());
toolbarTitle.setText(contactName);
});
}
@@ -328,9 +343,23 @@ public class ConversationActivity extends BriarActivity
try {
long start = now();
Collection<PrivateMessageHeader> headers =
conversationManager.getMessageHeaders(contactId);
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);
logDuration(LOG, "Loading messages", start);
displayMessages(revision, headers);
displayMessages(revision, headers, introductions, invitations);
} catch (NoSuchContactException e) {
finishOnUiThread();
} catch (DbException e) {
@@ -340,12 +369,15 @@ public class ConversationActivity extends BriarActivity
}
private void displayMessages(int revision,
Collection<PrivateMessageHeader> headers) {
Collection<PrivateMessageHeader> headers,
Collection<IntroductionMessage> introductions,
Collection<InvitationMessage> invitations) {
runOnUiThreadUnlessDestroyed(() -> {
if (revision == adapter.getRevision()) {
adapter.incrementRevision();
textInputView.setSendButtonEnabled(true);
List<ConversationItem> items = createItems(headers);
List<ConversationItem> items = createItems(headers,
introductions, invitations);
adapter.addAll(items);
list.showData();
// Scroll to the bottom
@@ -364,9 +396,41 @@ public class ConversationActivity extends BriarActivity
*/
@SuppressWarnings("ConstantConditions")
private List<ConversationItem> createItems(
Collection<PrivateMessageHeader> headers) {
List<ConversationItem> items = new ArrayList<>(headers.size());
for (PrivateMessageHeader h : headers) items.add(h.accept(visitor));
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);
}
return items;
}
@@ -412,7 +476,9 @@ public class ConversationActivity extends BriarActivity
PrivateMessageReceivedEvent p = (PrivateMessageReceivedEvent) e;
if (p.getContactId().equals(contactId)) {
LOG.info("Message received, adding");
onNewPrivateMessage(p.getMessageHeader());
PrivateMessageHeader h = p.getMessageHeader();
addConversationItem(ConversationItem.from(h));
loadMessageBody(h.getId());
}
} else if (e instanceof MessagesSentEvent) {
MessagesSentEvent m = (MessagesSentEvent) e;
@@ -438,6 +504,38 @@ 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);
}
}
}
@@ -450,33 +548,84 @@ public class ConversationActivity extends BriarActivity
});
}
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 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 markMessages(Collection<MessageId> messageIds, boolean sent,
boolean seen) {
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) {
runOnUiThreadUnlessDestroyed(() -> {
adapter.incrementRevision();
Set<MessageId> messages = new HashSet<>(messageIds);
@@ -529,8 +678,7 @@ 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);
}
});
}
@@ -545,8 +693,10 @@ 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(h.accept(visitor));
addConversationItem(item);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
@@ -673,7 +823,7 @@ public class ConversationActivity extends BriarActivity
@UiThread
@Override
public void respondToRequest(ConversationRequestItem item, boolean accept) {
item.setAnswered();
item.setAnswered(true);
int position = adapter.findItemPosition(item);
if (position != INVALID_POSITION) {
adapter.notifyItemChanged(position, item);
@@ -759,11 +909,10 @@ public class ConversationActivity extends BriarActivity
groupInvitationManager.respondToInvitation(contactId, id, accept);
}
@Nullable
@Override
public String getBody(MessageId m) {
String body = bodyCache.get(m);
if (body == null) loadMessageBody(m);
return body;
private ListenableFutureTask<String> getContactNameTask() {
if (!contactNameTaskStarted.getAndSet(true))
runOnDbThread(contactNameTask);
return contactNameTask;
}
}

View File

@@ -1,20 +1,40 @@
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 {
@Nullable
protected String body;
protected @Nullable String body;
private final MessageId id;
private final GroupId groupId;
private final long time;
@@ -58,4 +78,216 @@ 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,7 +6,6 @@ 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;
@@ -18,17 +17,13 @@ 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;
@@ -44,4 +39,5 @@ 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,16 +17,11 @@ class ConversationNoticeOutItem extends ConversationOutItem {
@Nullable
private final String 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;
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;
}
@Nullable
@@ -39,4 +34,5 @@ class ConversationNoticeOutItem extends ConversationOutItem {
public int getLayout() {
return R.layout.list_item_conversation_notice_out;
}
}

View File

@@ -4,11 +4,9 @@ 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;
@@ -17,7 +15,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;
@@ -26,19 +24,17 @@ class ConversationRequestItem extends ConversationNoticeInItem {
private final boolean canBeOpened;
private boolean answered;
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;
}
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;
}
RequestType getRequestType() {
@@ -58,8 +54,8 @@ class ConversationRequestItem extends ConversationNoticeInItem {
return answered;
}
void setAnswered() {
this.answered = true;
void setAnswered(boolean answered) {
this.answered = answered;
}
public boolean canBeOpened() {
@@ -71,4 +67,5 @@ class ConversationRequestItem extends ConversationNoticeInItem {
public int getLayout() {
return R.layout.list_item_conversation_request;
}
}

View File

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

View File

@@ -2,7 +2,6 @@ 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;
@@ -83,8 +82,8 @@ public class IntroductionMessageFragment extends BaseFragment
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// change toolbar text
ActionBar actionBar = introductionActivity.getSupportActionBar();
@@ -183,7 +182,7 @@ public class IntroductionMessageFragment extends BaseFragment
}
@Override
public void onSendClick(@NonNull String text) {
public void onSendClick(String text) {
// disable button to prevent accidental double invitations
ui.message.setSendButtonEnabled(false);

View File

@@ -15,6 +15,7 @@ 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;
@@ -22,7 +23,6 @@ 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,14 +244,8 @@ public class KeyAgreementFragment extends BaseEventFragment
task.connectAndRunProtocol(remotePayload);
} catch (UnsupportedVersionException e) {
reset();
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));
}
String msg = getString(R.string.qr_code_unsupported,
getString(R.string.app_name));
showNextFragment(ContactExchangeErrorFragment.newInstance(msg));
} catch (CameraException e) {
logCameraExceptionAndFinish(e);

View File

@@ -7,7 +7,6 @@ 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;
@@ -16,7 +15,6 @@ 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;
@@ -37,11 +35,11 @@ public class StrengthMeter extends ProgressBar {
this(context, null);
}
public StrengthMeter(Context context, @Nullable AttributeSet attrs) {
public StrengthMeter(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.progressBarStyleHorizontal);
bar = new ShapeDrawable();
bar.getPaint().setColor(RED);
ClipDrawable clip = new ClipDrawable(bar, LEFT & START, HORIZONTAL);
ClipDrawable clip = new ClipDrawable(bar, LEFT, HORIZONTAL);
ShapeDrawable background = new ShapeDrawable();
Paint p = background.getPaint();
p.setStyle(FILL);
@@ -52,7 +50,6 @@ public class StrengthMeter extends ProgressBar {
Drawable[] layers = new Drawable[] { clip, background };
setProgressDrawable(new LayerDrawable(layers));
setIndeterminate(false);
if (isInEditMode()) setStrength(STRONG);
}
@Override

View File

@@ -1,84 +0,0 @@
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,12 +31,13 @@ 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;
private SwitchPreference lockPref, purgePref, uninstallPref;
private ListPreference panicAppPref;
@Override
@@ -48,6 +49,7 @@ 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())) {
@@ -95,11 +97,16 @@ 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;
@@ -138,15 +145,27 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences,
String key) {
// enable locking if purging gets enabled
if (key.equals(KEY_PURGE) &&
sharedPreferences.getBoolean(KEY_PURGE, false)) {
lockPref.setChecked(true);
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);
}
}
// disable purging if locking gets disabled
// enable purging and locking if uninstall gets enabled
if (key.equals(KEY_UNINSTALL) &&
sharedPreferences.getBoolean(KEY_UNINSTALL, false)) {
lockPref.setChecked(true);
purgePref.setChecked(true);
}
// disable purging and uninstalling if locking gets disabled
if (key.equals(KEY_LOCK) &&
!sharedPreferences.getBoolean(KEY_LOCK, true)) {
purgePref.setChecked(false);
uninstallPref.setChecked(false);
}
}
@@ -160,8 +179,10 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
panicAppPref.setIcon(
android.R.drawable.ic_menu_close_clear_cancel);
// disable destructive panic actions
// disable panic actions
lockPref.setEnabled(false);
purgePref.setEnabled(false);
uninstallPref.setEnabled(false);
} else {
// display connected panic app
try {
@@ -171,8 +192,10 @@ public class PanicPreferencesFragment extends PreferenceFragmentCompat
panicAppPref.setIcon(
pm.getApplicationIcon(triggerPackageName));
// enable destructive panic actions
// enable panic actions
lockPref.setEnabled(true);
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,6 +2,7 @@ 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;
@@ -10,6 +11,7 @@ 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;
@@ -20,8 +22,10 @@ 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 {
@@ -40,8 +44,8 @@ public class PanicResponderActivity extends BriarActivity {
TrustedIntents trustedIntents = TrustedIntents.get(this);
// Guardian Project Ripple
trustedIntents.addTrustedSigner(GuardianProjectRSA4096.class);
// F-Droid
trustedIntents.addTrustedSigner(FDroidSignaturePin.class);
// Amnesty International's Panic Button, made by iilab.org
trustedIntents.addTrustedSigner(IilabEngineeringRSA2048Pin.class);
Intent intent = trustedIntents.getIntentFromTrustedSender(this);
if (intent != null) {
@@ -56,16 +60,23 @@ public class PanicResponderActivity extends BriarActivity {
LOG.info("Panic Trigger came from connected app");
// Performing panic responses
if (sharedPref.getBoolean(KEY_PURGE, false)) {
if (sharedPref.getBoolean(KEY_UNINSTALL, 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,10 +99,11 @@ class GroupControllerImpl extends
} else if (e instanceof GroupInvitationResponseReceivedEvent) {
GroupInvitationResponseReceivedEvent g =
(GroupInvitationResponseReceivedEvent) e;
GroupInvitationResponse r = g.getMessageHeader();
GroupInvitationResponse r =
(GroupInvitationResponse) g.getResponse();
if (getGroupId().equals(r.getShareableId()) && r.wasAccepted()) {
listener.runOnUiThreadUnlessDestroyed(
() -> listener.onInvitationAccepted(g.getContactId()));
() -> listener.onInvitationAccepted(r.getContactId()));
}
} else if (e instanceof GroupDissolvedEvent) {
GroupDissolvedEvent g = (GroupDissolvedEvent) e;

View File

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

View File

@@ -89,6 +89,7 @@ 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;
@@ -126,6 +127,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
private SwitchPreference notifyForumPosts;
private SwitchPreference notifyBlogPosts;
private SwitchPreference notifyVibration;
private SwitchPreference notifyLockscreen;
private Preference notifySound;
@@ -177,6 +179,8 @@ 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);
@@ -203,6 +207,10 @@ 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 -> {
@@ -372,6 +380,8 @@ 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;
@@ -399,6 +409,7 @@ 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);
@@ -421,6 +432,7 @@ public class SettingsFragment extends PreferenceFragmentCompat
notifyForumPosts.setEnabled(enabled);
notifyBlogPosts.setEnabled(enabled);
notifyVibration.setEnabled(enabled);
notifyLockscreen.setEnabled(enabled);
notifySound.setEnabled(enabled);
}
@@ -541,6 +553,10 @@ 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,7 +1,6 @@
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;
@@ -27,7 +26,7 @@ public abstract class InvitationAdapter<I extends InvitationItem, VH extends Inv
}
@Override
public void onBindViewHolder(@NonNull VH ui, int position) {
public void onBindViewHolder(VH ui, int position) {
I item = getItemAt(position);
if (item == null) return;
ui.onBind(item, listener);

View File

@@ -1,6 +1,5 @@
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;
@@ -37,9 +36,8 @@ public class ThreadItemAdapter<I extends ThreadItem>
this.layoutManager = layoutManager;
}
@NonNull
@Override
public BaseThreadItemViewHolder<I> onCreateViewHolder(@NonNull
public BaseThreadItemViewHolder<I> onCreateViewHolder(
ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_thread, parent, false);
@@ -47,8 +45,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
}
@Override
public void onBindViewHolder(@NonNull BaseThreadItemViewHolder<I> ui,
int position) {
public void onBindViewHolder(BaseThreadItemViewHolder<I> ui, int position) {
I item = items.get(position);
ui.bind(item, listener);
}

View File

@@ -1,14 +1,15 @@
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 {
@@ -21,7 +22,6 @@ 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,8 +29,13 @@ public class BriarNotificationBuilder extends NotificationCompat.Builder {
return this;
}
public BriarNotificationBuilder setNotificationCategory(String category) {
if (SDK_INT >= 21) setCategory(category);
public BriarNotificationBuilder setLockscreenVisibility(String category,
boolean show) {
if (Build.VERSION.SDK_INT >= 21) {
setCategory(category);
if (show) setVisibility(VISIBILITY_PRIVATE);
else setVisibility(VISIBILITY_SECRET);
}
return this;
}

View File

@@ -61,6 +61,7 @@ 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;
@@ -156,7 +157,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,10 +5,11 @@ 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;
@@ -29,7 +30,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 ConstraintLayout {
public class AuthorView extends RelativeLayout {
public static final int NORMAL = 0;
public static final int REBLOGGER = 1;
@@ -132,24 +133,32 @@ public class AuthorView extends ConstraintLayout {
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);
@@ -157,6 +166,8 @@ public class AuthorView extends ConstraintLayout {
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);
@@ -164,6 +175,8 @@ public class AuthorView extends ConstraintLayout {
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;
}
}
@@ -181,4 +194,10 @@ public class AuthorView extends ConstraintLayout {
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,7 +19,8 @@ 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,6 +3,7 @@ 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;
@@ -10,11 +11,13 @@ 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 {
@@ -32,6 +35,7 @@ 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) {
@@ -68,4 +72,10 @@ 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,11 +61,9 @@ public class TextInputView extends KeyboardAwareLinearLayout {
public TextInputView(Context context, @Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
if (!isInEditMode()) {
BriarApplication app =
(BriarApplication) context.getApplicationContext();
app.getApplicationComponent().inject(this);
}
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();
if (!isInEditMode()) setUnreadCount(0);
setUnreadCount(0);
}
private void setDirection(int direction) {
@@ -64,11 +64,11 @@ public class UnreadMessageButton extends FrameLayout {
public void setUnreadCount(int count) {
if (count == 0) {
fab.hide();
unread.setVisibility(INVISIBLE);
setVisibility(INVISIBLE);
} else {
fab.show();
unread.setVisibility(VISIBLE);
// FIXME: Use animations when upgrading to support library 24.2.0
// https://code.google.com/p/android/issues/detail?id=216469
setVisibility(VISIBLE);
unread.setText(String.valueOf(count));
}
}

View File

@@ -5,7 +5,6 @@ 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;
@@ -43,8 +42,8 @@ public class LinkDialogFragment extends DialogFragment {
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
ViewGroup container, Bundle savedInstanceState) {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_link_dialog, container,
false);

View File

@@ -21,6 +21,7 @@ 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

@@ -0,0 +1,23 @@
/**
* 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

@@ -0,0 +1,105 @@
/*
* 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/thread_item_background"/>
<solid android:color="@color/window_background"/>
<stroke
android:width="@dimen/forum_nested_line_width"
android:width="2dp"
android:color="@color/thread_indicator"/>
</shape>

View File

@@ -1,9 +1,8 @@
<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,10 +1,5 @@
<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 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>

View File

@@ -1,9 +1,8 @@
<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,10 +1,5 @@
<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 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>

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_top_inner"
android:topRightRadius="@dimen/message_bubble_radius_top_outer"/>
android:topLeftRadius="@dimen/message_bubble_radius_small"
android:topRightRadius="@dimen/message_bubble_radius_big"/>
<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_top_inner"
android:topRightRadius="@dimen/message_bubble_radius_top_outer"/>
android:topLeftRadius="@dimen/message_bubble_radius_small"
android:topRightRadius="@dimen/message_bubble_radius_big"/>
<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_top_outer"
android:topRightRadius="@dimen/message_bubble_radius_top_inner"/>
android:topLeftRadius="@dimen/message_bubble_radius_big"
android:topRightRadius="@dimen/message_bubble_radius_small"/>
<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_top_outer"
android:topRightRadius="@dimen/message_bubble_radius_top_inner"/>
android:topLeftRadius="@dimen/message_bubble_radius_big"
android:topRightRadius="@dimen/message_bubble_radius_small"/>
<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_top_inner"
android:topRightRadius="@dimen/message_bubble_radius_top_outer"/>
android:topLeftRadius="@dimen/message_bubble_radius_small"
android:topRightRadius="@dimen/message_bubble_radius_big"/>
<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_top_outer"
android:topRightRadius="@dimen/message_bubble_radius_top_inner"/>
android:topLeftRadius="@dimen/message_bubble_radius_big"
android:topRightRadius="@dimen/message_bubble_radius_small"/>
<padding
android:bottom="@dimen/message_bubble_padding_bottom"
android:left="@dimen/message_bubble_padding_sides"

View File

@@ -2,7 +2,6 @@
<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,7 +1,6 @@
<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,8 +26,7 @@
app:layout_constraintBottom_toBottomOf="@id/explanationText"
app:layout_constraintEnd_toStartOf="@id/explanationText"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:ignore="ContentDescription"/>
app:layout_constraintTop_toTopOf="parent"/>
<ImageView
android:id="@+id/explanationImage"
@@ -35,8 +34,8 @@
android:layout_height="wrap_content"
android:layout_weight="1"
android:adjustViewBounds="true"
android:paddingEnd="@dimen/margin_large"
android:paddingStart="@dimen/margin_large"
android:paddingLeft="@dimen/margin_large"
android:paddingRight="@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">
<android.support.constraint.ConstraintLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
@@ -22,11 +22,9 @@
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
@@ -42,11 +40,10 @@
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
@@ -62,11 +59,10 @@
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
@@ -83,33 +79,30 @@
android:id="@+id/strength_meter"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:visibility="invisible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/new_password_confirm_wrapper"/>
android:layout_below="@id/new_password_confirm_wrapper"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
<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:visibility="invisible"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="@id/change_password"/>
android:layout_alignTop="@id/change_password"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</ScrollView>

View File

@@ -20,23 +20,20 @@
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal">
android:layout_height="match_parent">
<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"
android:maxLines="1"
android:ellipsize="end"
android:textColor="@color/action_bar_text"
tools:text="Contact Name of someone who chose a long name"/>
tools:text="Contact Name"/>
</LinearLayout>

View File

@@ -1,224 +1,186 @@
<?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">
<android.support.constraint.ConstraintLayout
<LinearLayout
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:visibility="invisible">
tools:context=".android.reporting.DevReportActivity">
<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"/>
<include layout="@layout/toolbar"/>
<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_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.TextInputEditText
android:id="@+id/user_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine|textCapSentences"
android:maxLines="5"
tools:hint="@string/describe_crash"/>
</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">
<android.support.design.widget.TextInputEditText
android:id="@+id/user_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="@string/optional_contact_email"
android:inputType="textEmailAddress"
android:maxLines="1"/>
</android.support.design.widget.TextInputLayout>
<CheckBox
android:id="@+id/include_debug_report"
android:layout_width="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"/>
<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"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/include_debug_report">
android:layout_height="match_parent"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large">
<LinearLayout
android:id="@+id/report_content"
<android.support.design.widget.TextInputLayout
android:id="@+id/user_comment_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:id="@+id/user_comment"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textMultiLine|textCapSentences"
tools:hint="@string/describe_crash"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/user_email_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android: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"/>
android:layout_below="@+id/user_comment_layout"
android:layout_marginTop="@dimen/margin_small">
</ScrollView>
<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"/>
<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.design.widget.TextInputLayout>
</android.support.constraint.ConstraintLayout>
<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"/>
<android.support.constraint.ConstraintLayout
<Button
android:id="@+id/chevron"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:layout_alignTop="@+id/include_debug_report"
android:text="@string/show"/>
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/include_debug_report"
android:layout_marginTop="@dimen/margin_small">
<LinearLayout
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"/>
</ScrollView>
<ProgressBar
android:id="@+id/progress_wheel"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_below="@+id/include_debug_report"
android:layout_centerHorizontal="true"
android:indeterminate="true"
android:visibility="gone"/>
</RelativeLayout>
</LinearLayout>
<RelativeLayout
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"
tools:visibility="visible">
android:visibility="invisible">
<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"
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"/>
android:textSize="@dimen/text_size_large"/>
<TextView
android:id="@+id/fault"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/crashed"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/not_your_fault"
android:textColor="?android:attr/textColorSecondary"
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"/>
android:textSize="@dimen/text_size_large"/>
<TextView
android:id="@+id/pleaseSend"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/fault"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/please_send_report"
android:textColor="?android:attr/textColorSecondary"
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"/>
android:textSize="@dimen/text_size_large"/>
<TextView
android:id="@+id/encrypted"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/pleaseSend"
android:layout_marginBottom="@dimen/margin_large"
android:layout_marginTop="@dimen/margin_large"
android:gravity="center"
android:text="@string/report_is_encrypted"
android:textColor="?android:attr/textColorSecondary"
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"/>
android:textSize="@dimen/text_size_large"/>
<Button
android:id="@+id/declineButton"
style="@style/BriarButtonFlat.Negative"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"/>
android:layout_alignParentLeft="true"
android:layout_below="@+id/encrypted"
android:text="@string/close"/>
<Button
android:id="@+id/acceptButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"/>
android:layout_alignParentRight="true"
android:layout_below="@+id/encrypted"
android:text="@string/send_report"/>
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</FrameLayout>

View File

@@ -2,75 +2,57 @@
<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 -->
<android.support.constraint.ConstraintLayout
android:id="@+id/constraintLayout"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_height="match_parent"
android:orientation="vertical">
<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"/>
<include layout="@layout/toolbar"/>
<android.support.constraint.ConstraintLayout
<RelativeLayout
android:id="@+id/expiryWarning"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/briar_warning_background"
android:orientation="horizontal"
android:padding="@dimen/margin_medium"
android:visibility="gone"
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="0dp"
android:layout_width="wrap_content"
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"
app:layout_constraintEnd_toStartOf="@+id/expiryWarningClose"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
android:textSize="@dimen/text_size_small"/>
<ImageView
android:id="@+id/expiryWarningClose"
android:layout_width="24dp"
android:layout_height="0dp"
android:layout_height="24dp"
android:layout_alignParentRight="true"
android:layout_centerInParent="true"
android:contentDescription="@string/close"
android:scaleType="center"
android:src="@drawable/ic_close"
android:tint="@color/briar_text_tertiary_inverse"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
android:tint="@color/briar_text_tertiary_inverse"/>
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
<FrameLayout
android:id="@+id/fragmentContainer"
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"/>
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
<!-- The second child is the menu -->
<include

View File

@@ -36,7 +36,6 @@
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,6 +1,7 @@
<?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,20 +5,24 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/margin_activity_vertical">
android:orientation="vertical"
android:paddingBottom="@dimen/margin_activity_vertical"
android:paddingEnd="@dimen/margin_activity_horizontal"
android:paddingLeft="@dimen/margin_activity_horizontal"
android:paddingRight="@dimen/margin_activity_horizontal"
android:paddingStart="@dimen/margin_activity_horizontal"
android:paddingTop="@dimen/margin_activity_vertical">
<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
@@ -36,36 +40,31 @@
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/password_layout"/>
android:text="@string/sign_in_button"/>
<ProgressBar
android:id="@+id/progress_wheel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"/>
android:layout_alignTop="@id/btn_sign_in"
android:layout_centerHorizontal="true"
android:visibility="invisible"/>
<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"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/btn_sign_in"/>
android:textColor="?android:attr/textColorLink"/>
</android.support.constraint.ConstraintLayout>
</RelativeLayout>
</ScrollView>

View File

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

View File

@@ -1,6 +1,7 @@
<?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,179 +1,178 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
<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"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fillViewport="true"
android:padding="@dimen/margin_medium"
tools:ignore="HardcodedText">
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
<ScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="0dp"
android:padding="8dp"
tools:ignore="HardcodedText">
<TextView
android:id="@+id/textViewContacts"
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
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"/>
android:layout_height="wrap_content">
<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/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"/>
<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"/>
<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/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/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"/>
<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/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"/>
<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/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"/>
<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"/>
<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/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"/>
<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"/>
<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/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"/>
<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"/>
<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/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"/>
<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"/>
<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/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"/>
<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"/>
<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/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"/>
<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"/>
<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"/>
<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>
<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"/>
</ScrollView>
<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>

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|end"
android:layout_gravity="top|right"
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|end"
android:layout_gravity="bottom|right"
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:fillHeight="true"
app:hint="@string/blogs_write_blog_post_body_hint"/>
app:hint="@string/blogs_write_blog_post_body_hint"
app:fillHeight="true"/>
<ProgressBar
android:id="@+id/progressBar"

View File

@@ -1,12 +1,7 @@
<?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
@@ -14,64 +9,54 @@
style="@style/BriarAvatar"
android:layout_width="@dimen/blogs_avatar_normal_size"
android:layout_height="@dimen/blogs_avatar_normal_size"
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"
android:layout_alignTop="@+id/authorName"
android:layout_marginRight="@dimen/margin_medium"
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"
app:layout_constraintBottom_toBottomOf="@+id/avatar"
app:layout_constraintEnd_toEndOf="@+id/avatar"/>
tools:ignore="ContentDescription"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/authorName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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_toEndOf="@+id/avatar"
android:layout_toRightOf="@+id/avatar"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_small"
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"/>
tools:text="Author Name"/>
<org.briarproject.briar.android.view.TrustIndicatorView
android:id="@+id/trustIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"
android:layout_alignBottom="@+id/authorName"
android:layout_alignTop="@+id/authorName"
android:layout_marginLeft="@dimen/margin_small"
android:layout_toRightOf="@id/authorName"
android:scaleType="center"
tools:src="@drawable/trust_indicator_verified"/>
<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"
app:layout_constraintStart_toStartOf="@+id/authorName"
app:layout_constraintTop_toBottomOf="@+id/authorName"
tools:text="yesterday"
tools:visibility="visible"/>
tools:text="yesterday"/>
</merge>

View File

@@ -1,5 +1,6 @@
<?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"
@@ -61,6 +62,8 @@
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"/>
@@ -76,6 +79,8 @@
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,7 +9,6 @@
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|end|right"
android:layout_gravity="bottom|right"
android:scaleType="fitCenter"
tools:ignore="ContentDescription"
tools:src="@drawable/contact_online"/>

View File

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

View File

@@ -5,7 +5,7 @@
android:layout_height="match_parent">
<FrameLayout
android:layout_width="match_parent"
android:layout_width="wrap_content"
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="match_parent"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
<ProgressBar

View File

@@ -11,8 +11,6 @@
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"
@@ -27,8 +25,6 @@
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="@string/waiting_for_contact_to_scan"/>
tools:text="Connection failed"/>
</LinearLayout>
<org.briarproject.briar.android.view.QrCodeView
@@ -47,9 +47,6 @@
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="@android:color/white"
tools:visibility="visible"/>
android:background="@android:color/white"/>
</LinearLayout>
</FrameLayout>

View File

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

View File

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

View File

@@ -7,125 +7,113 @@
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.constraint.ConstraintLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical">
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/avatarContact1"
style="@style/BriarAvatar"
android:layout_width="42dp"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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:layout_margin="@dimen/margin_activity_horizontal"
android:gravity="center"
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"/>
android:orientation="horizontal">
<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"/>
<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="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"/>
<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"/>
<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"/>
<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"/>
<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"/>
</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>
<ProgressBar
android:id="@+id/progressBar"
style="?android:attr/progressBarStyleLarge"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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"
android:layout_gravity="center"
tools:visibility="gone"/>
<TextView
android:id="@+id/introductionNotPossibleView"
android:layout_width="0dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:layout_margin="@dimen/margin_activity_horizontal"
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="0dp"
android:layout_width="match_parent"
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"/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
</ScrollView>

View File

@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
<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"
@@ -9,32 +9,27 @@
<View
android:id="@+id/inputDivider"
style="@style/Divider.Horizontal"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
style="@style/Divider.Horizontal"/>
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/authorView"
android:layout_width="0dp"
android:layout_width="wrap_content"
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="0dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/authorView"
android:paddingBottom="@dimen/listitem_vertical_margin"
android:paddingEnd="@dimen/listitem_vertical_margin"
android:paddingStart="@dimen/listitem_vertical_margin"
android:paddingLeft="@dimen/listitem_vertical_margin"
android:paddingRight="@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."/>
</android.support.constraint.ConstraintLayout>
</RelativeLayout>

View File

@@ -9,70 +9,63 @@
android:layout_height="wrap_content"
android:foreground="?attr/selectableItemBackground">
<android.support.constraint.ConstraintLayout
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
android:orientation="vertical">
<org.briarproject.briar.android.view.AuthorView
android:id="@+id/rebloggerView"
android:layout_width="0dp"
<RelativeLayout
android:layout_width="match_parent"
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_toTopOf="parent"
app:persona="reblogger"/>
android:padding="@dimen/listitem_vertical_margin">
<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/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"/>
<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"/>
<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"/>
<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."/>
<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>
<!-- TODO replace with RecyclerView -->
<LinearLayout
android:id="@+id/commentContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/listitem_vertical_margin"
android:orientation="vertical"
app:layout_constraintTop_toBottomOf="@+id/bodyView">
android:orientation="vertical">
<include
layout="@layout/list_item_blog_comment"
@@ -81,6 +74,6 @@
</LinearLayout>
</android.support.constraint.ConstraintLayout>
</LinearLayout>
</android.support.v7.widget.CardView>

View File

@@ -1,104 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:id="@+id/linearLayout4"
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?attr/selectableItemBackground"
android:clickable="true"
android:focusable="true">
android:focusable="true"
android:orientation="vertical">
<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"
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
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"/>
android:paddingBottom="@dimen/listitem_horizontal_margin"
android:paddingTop="@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"/>
<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">
<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"/>
<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"/>
<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"/>
<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"/>
</android.support.constraint.ConstraintLayout>
</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>

View File

@@ -23,9 +23,7 @@
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"
@@ -38,9 +36,7 @@
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,19 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
<LinearLayout
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:elevation="@dimen/message_bubble_elevation"
android:orientation="vertical">
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/text"
@@ -21,9 +19,6 @@
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
@@ -31,9 +26,8 @@
style="@style/TextMessage.Timestamp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/text"
android:layout_gravity="right|end"
android:layout_marginTop="@dimen/message_bubble_timestamp_margin"
tools:text="Dec 24, 13:37"/>
</android.support.constraint.ConstraintLayout>
</LinearLayout>

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