Compare commits

..

1 Commits

Author SHA1 Message Date
akwizgran
7f840d25d9 Add a simple Tor relay probe. 2018-10-24 10:38:49 +01:00
45 changed files with 203 additions and 2046 deletions

View File

@@ -0,0 +1,153 @@
package org.briarproject.bramble.plugin.tor;
import org.spongycastle.crypto.tls.Certificate;
import org.spongycastle.crypto.tls.CipherSuite;
import org.spongycastle.crypto.tls.DefaultTlsClient;
import org.spongycastle.crypto.tls.ServerOnlyTlsAuthentication;
import org.spongycastle.crypto.tls.TlsAuthentication;
import org.spongycastle.crypto.tls.TlsClientProtocol;
import java.io.DataInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.Socket;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Logger;
import static java.util.logging.Level.INFO;
public class TorProbe {
private static final Logger LOG =
Logger.getLogger(TorProbe.class.getName());
private static final int READ_TIMEOUT = 10 * 1000;
// https://trac.torproject.org/projects/tor/wiki/org/projects/Tor/TLSHistory
private static final int SSL3_RSA_FIPS_WITH_3DES_EDE_CBC_SHA = 0xfeff;
// https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt#n347
private static final int[] TOR_CIPHER_SUITES = new int[] {
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_DHE_DSS_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_RSA_WITH_AES_256_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_DHE_DSS_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_RC4_128_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_RSA_WITH_RC4_128_MD5,
CipherSuite.TLS_RSA_WITH_RC4_128_SHA,
CipherSuite.TLS_RSA_WITH_AES_128_CBC_SHA,
CipherSuite.TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA,
SSL3_RSA_FIPS_WITH_3DES_EDE_CBC_SHA,
CipherSuite.TLS_RSA_WITH_3DES_EDE_CBC_SHA
};
// https://gitweb.torproject.org/torspec.git/tree/tor-spec.txt#n412
private static final byte[] VERSIONS_CELL = new byte[] {
0x00, 0x00, // Circuit ID: 0
0x07, // Command: Versions
0x00, 0x06, // Payload length: 6 bytes
0x00, 0x03, 0x00, 0x04, 0x00, 0x05 // Supported versions: 3, 4, 5
};
public List<Integer> probe(String address, int port) throws IOException {
if (LOG.isLoggable(INFO))
LOG.info("Connecting to " + address + ":" + port);
Socket socket = new Socket(address, port);
LOG.info("Connected");
TlsClientProtocol client = new TlsClientProtocol(
socket.getInputStream(), socket.getOutputStream(),
new SecureRandom());
client.connect(new TorTlsClient());
LOG.info("TLS handshake succeeded");
socket.setSoTimeout(READ_TIMEOUT);
try {
// Send a versions cell
OutputStream out = client.getOutputStream();
out.write(VERSIONS_CELL);
out.flush();
LOG.info("Sent versions cell");
// Expect a versions cell in response
List<Integer> versions = new ArrayList<>();
DataInputStream in = new DataInputStream(client.getInputStream());
int circuitId = in.readUnsignedShort();
if (circuitId != 0)
throw new IOException("Unexpected circuit ID: " + circuitId);
int command = in.readUnsignedByte();
if (command != 7)
throw new IOException("Unexpected command: " + command);
int payloadLength = in.readUnsignedShort();
if (payloadLength == 0 || payloadLength % 2 != 0) {
throw new IOException("Invalid payload length: "
+ payloadLength);
}
for (int i = 0; i < payloadLength / 2; i++) {
int version = in.readUnsignedShort();
versions.add(version);
}
if (LOG.isLoggable(INFO))
LOG.info("Supported versions: " + versions);
return versions;
} finally {
client.close();
}
}
public static void main(String[] args) throws IOException {
if (args.length != 2) {
System.err.println("Usage: TorProbe <address> <port>");
System.exit(1);
}
String address = args[0];
int port = Integer.parseInt(args[1]);
new TorProbe().probe(address, port);
}
private static class TorTlsClient extends DefaultTlsClient {
@Override
public TlsAuthentication getAuthentication() {
return new ServerOnlyTlsAuthentication() {
@Override
public void notifyServerCertificate(Certificate cert)
throws IOException {
LOG.info("Received server certificate");
org.spongycastle.asn1.x509.Certificate[] chain =
cert.getCertificateList();
if (chain.length != 1)
throw new IOException("Certificate is not self-signed");
for (org.spongycastle.asn1.x509.Certificate c : chain) {
if (LOG.isLoggable(INFO)) {
LOG.info("Subject: " + c.getSubject());
LOG.info("Issuer: " + c.getIssuer());
}
}
}
};
}
@Override
public int[] getCipherSuites() {
return TOR_CIPHER_SUITES;
}
}
}

View File

@@ -153,15 +153,4 @@ public class StringUtils {
return new String(c);
}
public static String getRandomBase32String(int length) {
char[] c = new char[length];
for (int i = 0; i < length; i++) {
int character = random.nextInt(32);
if (character < 26) c[i] = (char) ('a' + character);
else c[i] = (char) ('2' + (character - 26));
}
return new String(c);
}
}

View File

@@ -1,59 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
style="width:24px;height:24px"
viewBox="0 0 24 24"
version="1.1"
id="svg4"
sodipodi:docname="ic_link_down.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
<dc:title></dc:title>
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1020"
id="namedview6"
showgrid="false"
inkscape:zoom="13.906433"
inkscape:cx="5.1490538"
inkscape:cy="22.945407"
inkscape:window-x="1440"
inkscape:window-y="24"
inkscape:window-maximized="0"
inkscape:current-layer="svg4" />
<path
fill="#000000"
d="M16,6H13V7.9H16C18.26,7.9 20.1,9.73 20.1,12A4.1,4.1 0 0,1 16,16.1H13V18H16A6,6 0 0,0 22,12C22,8.68 19.31,6 16,6M3.9,12C3.9,9.73 5.74,7.9 8,7.9H11V6H8A6,6 0 0,0 2,12A6,6 0 0,0 8,18H11V16.1H8C5.74,16.1 3.9,14.26 3.9,12M8,13H16V11H8V13Z"
id="path2" />
<path
id="path884"
d="m 21.659779,17.473157 h -2.295918 v 3.061224 H 17.51182 l 3.000001,3 2.999999,-3 h -1.852041 z"
inkscape:connector-curvature="0"
style="fill:#000000;stroke-width:0.38265303" />
</svg>

Before

Width:  |  Height:  |  Size: 2.0 KiB

View File

@@ -1,58 +0,0 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns:cc="http://creativecommons.org/ns#"
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns="http://www.w3.org/2000/svg"
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
style="width:24px;height:24px"
viewBox="0 0 24 24"
version="1.1"
id="svg4"
sodipodi:docname="ic_link_up.svg"
inkscape:version="0.92.3 (2405546, 2018-03-11)">
<metadata
id="metadata10">
<rdf:RDF>
<cc:Work
rdf:about="">
<dc:format>image/svg+xml</dc:format>
<dc:type
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
</cc:Work>
</rdf:RDF>
</metadata>
<defs
id="defs8" />
<sodipodi:namedview
pagecolor="#ffffff"
bordercolor="#666666"
borderopacity="1"
objecttolerance="10"
gridtolerance="10"
guidetolerance="10"
inkscape:pageopacity="0"
inkscape:pageshadow="2"
inkscape:window-width="1920"
inkscape:window-height="1020"
id="namedview6"
showgrid="false"
inkscape:zoom="13.906433"
inkscape:cx="5.1490538"
inkscape:cy="22.945407"
inkscape:window-x="1440"
inkscape:window-y="24"
inkscape:window-maximized="0"
inkscape:current-layer="svg4" />
<path
fill="#000000"
d="M16,6H13V7.9H16C18.26,7.9 20.1,9.73 20.1,12A4.1,4.1 0 0,1 16,16.1H13V18H16A6,6 0 0,0 22,12C22,8.68 19.31,6 16,6M3.9,12C3.9,9.73 5.74,7.9 8,7.9H11V6H8A6,6 0 0,0 2,12A6,6 0 0,0 8,18H11V16.1H8C5.74,16.1 3.9,14.26 3.9,12M8,13H16V11H8V13Z"
id="path2" />
<path
id="path884"
d="M 21.659779,23.534381 H 19.363861 V 20.473157 H 17.51182 l 3.000001,-3 2.999999,3 h -1.852041 z"
inkscape:connector-curvature="0"
style="fill:#000000;stroke-width:0.38265303" />
</svg>

Before

Width:  |  Height:  |  Size: 1.9 KiB

View File

@@ -2,70 +2,6 @@ apply plugin: 'com.android.application'
apply plugin: 'witness'
apply from: 'witness.gradle'
// prototype
repositories {
maven { url 'https://jitpack.io' }
}
dependencies {
implementation project(path: ':briar-core', configuration: 'default')
implementation project(path: ':bramble-core', configuration: 'default')
implementation project(':bramble-android')
def supportVersion = '28.0.0'
implementation "com.android.support:support-v4:$supportVersion"
implementation("com.android.support:appcompat-v7:$supportVersion") {
exclude module: 'support-v4'
}
implementation("com.android.support:preference-v14:$supportVersion") {
exclude module: 'support-v4'
}
implementation("com.android.support:design:$supportVersion") {
exclude module: 'support-v4'
exclude module: 'recyclerview-v7'
}
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('ch.acra:acra:4.9.1') {
exclude module: 'support-v4'
exclude module: 'support-annotations'
}
implementation 'info.guardianproject.panic:panic:0.5'
implementation 'info.guardianproject.trustedintents:trustedintents:0.2'
implementation 'de.hdodenhof:circleimageview:2.2.0'
implementation 'com.google.zxing:core:3.3.0'
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.8.0'
implementation 'com.vanniktech:emoji-google:0.5.1'
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
compileOnly 'javax.annotation:jsr250-api:1.0'
testImplementation project(path: ':bramble-api', configuration: 'testOutput')
testImplementation project(path: ':bramble-core', configuration: 'testOutput')
testImplementation 'org.robolectric:robolectric:3.8'
testImplementation 'org.robolectric:shadows-support-v4:3.3.2'
testImplementation 'org.mockito:mockito-core:2.13.0'
testImplementation 'junit:junit:4.12'
testImplementation "org.jmock:jmock:2.8.2"
testImplementation "org.jmock:jmock-junit4:2.8.2"
testImplementation "org.jmock:jmock-legacy:2.8.2"
testImplementation "org.hamcrest:hamcrest-library:1.3"
testImplementation "org.hamcrest:hamcrest-core:1.3"
def espressoVersion = '3.0.2'
androidTestImplementation "com.android.support.test.espresso:espresso-core:$espressoVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-contrib:$espressoVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-intents:$espressoVersion"
androidTestImplementation "tools.fastlane:screengrab:1.1.0"
androidTestImplementation "com.android.support.test.uiautomator:uiautomator-v18:2.1.3"
androidTestAnnotationProcessor "com.google.dagger:dagger-compiler:2.0.2"
androidTestCompileOnly 'javax.annotation:jsr250-api:1.0'
androidTestImplementation 'junit:junit:4.12'
}
def getStdout = { command, defaultValue ->
def stdout = new ByteArrayOutputStream()
try {
@@ -99,7 +35,7 @@ android {
buildTypes {
debug {
applicationIdSuffix ".debug.qr" // prototype
applicationIdSuffix ".debug"
shrinkResources false
minifyEnabled true
crunchPngs false
@@ -181,9 +117,6 @@ dependencies {
implementation 'uk.co.samuelwall:material-tap-target-prompt:2.12.4'
implementation 'com.vanniktech:emoji-google:0.5.1'
// prototype
implementation "com.github.kobakei:MaterialFabSpeedDial:1.2.0"
annotationProcessor 'com.google.dagger:dagger-compiler:2.0.2'
compileOnly 'javax.annotation:jsr250-api:1.0'

View File

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

View File

@@ -1,8 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest
package="org.briarproject.briar"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools">
xmlns:android="http://schemas.android.com/apk/res/android">
<uses-feature android:name="android.hardware.bluetooth" android:required="false"/>
<uses-feature android:name="android.hardware.camera" android:required="false"/>
@@ -28,8 +27,7 @@
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/BriarTheme"
tools:ignore="GoogleAppIndexingWarning">
android:theme="@style/BriarTheme">
<receiver
android:name="org.briarproject.briar.android.login.SignInReminderReceiver"
@@ -413,29 +411,5 @@
android:launchMode="singleTask"
android:theme="@style/BriarTheme.NoActionBar"/>
<!-- Prototype -->
<activity
android:name=".android.contact.ContactLinkExchangeActivity"
android:theme="@style/BriarTheme"
android:label="@string/add_contact_title"
android:windowSoftInputMode="stateHidden|adjustResize">
<intent-filter>
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="briar"/>
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND"/>
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="text/plain"/>
</intent-filter>
</activity>
<activity
android:name=".android.contact.PendingRequestsActivity"
android:label="@string/pending_contact_requests"
android:theme="@style/BriarTheme"/>
</application>
</manifest>

View File

@@ -15,14 +15,9 @@ import org.briarproject.briar.android.blog.ReblogFragment;
import org.briarproject.briar.android.blog.RssFeedImportActivity;
import org.briarproject.briar.android.blog.RssFeedManageActivity;
import org.briarproject.briar.android.blog.WriteBlogPostActivity;
import org.briarproject.briar.android.contact.ContactLinkExchangeActivity;
import org.briarproject.briar.android.contact.ContactLinkExchangeFragment;
import org.briarproject.briar.android.contact.ContactListFragment;
import org.briarproject.briar.android.contact.ContactModule;
import org.briarproject.briar.android.contact.ContactQrCodeInputFragment;
import org.briarproject.briar.android.contact.ContactQrCodeOutputFragment;
import org.briarproject.briar.android.contact.ConversationActivity;
import org.briarproject.briar.android.contact.PendingRequestsActivity;
import org.briarproject.briar.android.forum.CreateForumActivity;
import org.briarproject.briar.android.forum.ForumActivity;
import org.briarproject.briar.android.forum.ForumListFragment;
@@ -172,10 +167,6 @@ public interface ActivityComponent {
void inject(UnlockActivity activity);
void inject(ContactLinkExchangeActivity activity);
void inject(PendingRequestsActivity activity);
// Fragments
void inject(AuthorNameFragment fragment);
@@ -220,11 +211,4 @@ public interface ActivityComponent {
void inject(ScreenFilterDialogFragment fragment);
void inject(ContactExchangeErrorFragment fragment);
void inject(ContactLinkExchangeFragment fragment);
void inject(ContactQrCodeOutputFragment fragment);
void inject(ContactQrCodeInputFragment fragment);
}

View File

@@ -1,184 +0,0 @@
package org.briarproject.briar.android.contact;
import android.app.AlarmManager;
import android.app.PendingIntent;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.view.MenuItem;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.lifecycle.LifecycleManager;
import org.briarproject.bramble.api.system.Clock;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
import org.briarproject.briar.api.messaging.MessagingManager;
import java.util.logging.Logger;
import java.util.regex.Pattern;
import javax.annotation.Nullable;
import javax.inject.Inject;
import static android.app.AlarmManager.ELAPSED_REALTIME;
import static android.content.Intent.ACTION_SEND;
import static android.content.Intent.ACTION_VIEW;
import static android.content.Intent.EXTRA_TEXT;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.SystemClock.elapsedRealtime;
import static java.lang.String.CASE_INSENSITIVE_ORDER;
import static java.util.Objects.requireNonNull;
import static java.util.concurrent.TimeUnit.MINUTES;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.api.lifecycle.LifecycleManager.LifecycleState.RUNNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.bramble.util.StringUtils.getRandomBase32String;
public class ContactLinkExchangeActivity extends BriarActivity implements
BaseFragmentListener {
private static final Logger LOG =
Logger.getLogger(ContactLinkExchangeActivity.class.getName());
static final int LINK_LENGTH = 128;
static final Pattern LINK_REGEX =
Pattern.compile("(briar://)?([a-z2-7]{" + LINK_LENGTH + "})");
static final String OUR_LINK = "briar://" + getRandomBase32String(LINK_LENGTH);;
@Inject
LifecycleManager lifecycleManager;
@Inject
MessagingManager messagingManager;
@Inject
Clock clock;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setContentView(R.layout.activity_fragment_container);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
}
Intent i = getIntent();
if (i != null) {
String action = i.getAction();
if (ACTION_SEND.equals(action) || ACTION_VIEW.equals(action)) {
String text = i.getStringExtra(EXTRA_TEXT);
if (text != null) {
showInitialFragment(
ContactLinkExchangeFragment.newInstance(text));
return;
}
String uri = i.getDataString();
if (uri != null) {
showInitialFragment(
ContactLinkExchangeFragment.newInstance(uri));
return;
}
} else if ("addContact".equals(action)) {
removeFakeRequest(i.getStringExtra("name"),
i.getLongExtra("timestamp", 0));
setIntent(null);
finish();
}
}
if (state == null) {
showInitialFragment(new ContactLinkExchangeFragment());
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
boolean isBriarLink(@Nullable CharSequence s) {
return s != null && LINK_REGEX.matcher(s).matches();
}
void scanCode() {
showNextFragment(new ContactQrCodeInputFragment());
}
void linkScanned(@Nullable String link) {
// FIXME: Contact name is lost
showNextFragment(ContactLinkExchangeFragment.newInstance(link));
}
void showCode() {
showNextFragment(new ContactQrCodeOutputFragment());
}
void addFakeRequest(String name, String link) {
long timestamp = clock.currentTimeMillis();
try {
messagingManager.addNewPendingContact(name, timestamp);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
AlarmManager alarmManager =
(AlarmManager) requireNonNull(getSystemService(ALARM_SERVICE));
double random = getPseudoRandom(link, OUR_LINK.replace("briar://", ""));
long m = MINUTES.toMillis(1);
long fromNow = (long) (-m * Math.log(random));
LOG.info("Delay " + fromNow + " ms based on seed " + random);
long triggerAt = elapsedRealtime() + fromNow;
Intent i = new Intent(this, ContactLinkExchangeActivity.class);
i.setAction("addContact");
i.setFlags(FLAG_ACTIVITY_NEW_TASK);
i.putExtra("name", name);
i.putExtra("timestamp", timestamp);
PendingIntent pendingIntent =
PendingIntent.getActivity(this, (int) timestamp / 1000, i, 0);
alarmManager.set(ELAPSED_REALTIME, triggerAt, pendingIntent);
}
/**
* Returns a pseudo-random value greater than or equal to 0 and less than 1,
* approximately uniformly distributed, based on the given strings. The
* same value is returned if the strings are swapped.
*/
private double getPseudoRandom(String a, String b) {
String first, second;
if (CASE_INSENSITIVE_ORDER.compare(a, b) < 0) {
first = a;
second = b;
} else {
first = b;
second = a;
}
int hash = (first + second).hashCode() & Integer.MAX_VALUE;
return hash / (1.0 + Integer.MAX_VALUE);
}
private void removeFakeRequest(String name, long timestamp) {
if (lifecycleManager.getLifecycleState() != RUNNING) {
LOG.info("Lifecycle not started, not adding contact " + name);
return;
}
LOG.info("Adding Contact " + name);
try {
messagingManager.removePendingContact(name, timestamp);
} catch (DbException e) {
logException(LOG, WARNING, e);
}
}
}

View File

@@ -1,225 +0,0 @@
package org.briarproject.briar.android.contact;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Intent;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.design.widget.TextInputEditText;
import android.support.design.widget.TextInputLayout;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import android.widget.Toast;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment;
import java.util.regex.Matcher;
import javax.annotation.Nullable;
import static android.content.Context.CLIPBOARD_SERVICE;
import static android.content.Intent.ACTION_SEND;
import static android.content.Intent.EXTRA_TEXT;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.v4.graphics.drawable.DrawableCompat.setTint;
import static android.support.v4.graphics.drawable.DrawableCompat.wrap;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.Objects.requireNonNull;
import static org.briarproject.briar.android.contact.ContactLinkExchangeActivity.LINK_REGEX;
import static org.briarproject.briar.android.contact.ContactLinkExchangeActivity.OUR_LINK;
import static org.briarproject.briar.android.util.UiUtils.resolveColorAttribute;
public class ContactLinkExchangeFragment extends BaseFragment
implements TextWatcher {
static final String TAG = ContactLinkExchangeFragment.class.getName();
static BaseFragment newInstance(@Nullable String link) {
BaseFragment f = new ContactLinkExchangeFragment();
Bundle bundle = new Bundle();
bundle.putString("link", link);
f.setArguments(bundle);
return f;
}
private ClipboardManager clipboard;
private TextInputLayout linkInputLayout;
private TextInputEditText linkInput, contactNameInput;
private Button addButton;
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (getActivity() == null || getContext() == null) return null;
getActivity().setTitle(R.string.add_contact_title);
View v = inflater.inflate(R.layout.fragment_contact_link_exchange,
container, false);
clipboard = (ClipboardManager) requireNonNull(
getContext().getSystemService(CLIPBOARD_SERVICE));
int color =
resolveColorAttribute(getContext(), R.attr.colorControlNormal);
addButton = v.findViewById(R.id.addButton);
addButton.setOnClickListener(view -> onAddButtonClicked());
contactNameInput = v.findViewById(R.id.contactNameInput);
contactNameInput.addTextChangedListener(this);
if (SDK_INT < 23) {
Drawable drawable =
wrap(contactNameInput.getCompoundDrawables()[0]);
setTint(drawable, color);
contactNameInput.setCompoundDrawables(drawable, null, null, null);
}
linkInputLayout = v.findViewById(R.id.linkInputLayout);
linkInput = v.findViewById(R.id.linkInput);
if (SDK_INT < 23) {
Drawable drawable = wrap(linkInput.getCompoundDrawables()[0]);
setTint(drawable, color);
linkInput.setCompoundDrawables(drawable, null, null, null);
}
linkInput.addTextChangedListener(this);
if (getArguments() != null)
linkInput.setText(getArguments().getString("link"));
Button pasteButton = v.findViewById(R.id.pasteButton);
pasteButton.setOnClickListener(view -> {
ClipData clip = clipboard.getPrimaryClip();
if (clip != null)
linkInput.setText(clip.getItemAt(0).getText());
});
Button scanCodeButton = v.findViewById(R.id.scanCodeButton);
scanCodeButton.setOnClickListener(view -> {
ContactLinkExchangeActivity activity = getCastActivity();
if (activity != null) activity.scanCode();
});
TextView linkView = v.findViewById(R.id.linkView);
linkView.setText(OUR_LINK);
ClipData clip = ClipData.newPlainText(
getString(R.string.link_clip_label), OUR_LINK);
Button copyButton = v.findViewById(R.id.copyButton);
copyButton.setOnClickListener(view -> {
clipboard.setPrimaryClip(clip);
Toast.makeText(getContext(), R.string.link_copied_toast,
LENGTH_SHORT).show();
});
Button shareButton = v.findViewById(R.id.shareButton);
shareButton.setOnClickListener(view -> {
Intent i = new Intent(ACTION_SEND);
i.putExtra(EXTRA_TEXT, OUR_LINK);
i.setType("text/plain");
startActivity(i);
});
Button showCodeButton = v.findViewById(R.id.showCodeButton);
showCodeButton.setOnClickListener(
view -> {
ContactLinkExchangeActivity activity = getCastActivity();
if (activity != null) activity.showCode();
});
return v;
}
private ContactLinkExchangeActivity getCastActivity() {
return (ContactLinkExchangeActivity) getActivity();
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,
int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before,
int count) {
updateAddButtonState();
}
@Override
public void afterTextChanged(Editable s) {
}
private void updateAddButtonState() {
boolean validContactName = contactNameInput.getText() != null &&
contactNameInput.getText().length() > 0;
boolean briarLink = isBriarLink(linkInput.getText());
if (briarLink) {
linkInputLayout.setErrorEnabled(false);
} else {
linkInputLayout.setError("Invalid link");
}
addButton.setEnabled(validContactName && briarLink);
}
private boolean isBriarLink(@Nullable CharSequence s) {
ContactLinkExchangeActivity activity = getCastActivity();
return activity != null && activity.isBriarLink(s);
}
@Nullable
private String getLink() {
Matcher matcher = LINK_REGEX.matcher(linkInput.getText());
if (matcher.matches()) // needs to be called before groups become available
return matcher.group(2);
else
return null;
}
private void onAddButtonClicked() {
ContactLinkExchangeActivity activity = getCastActivity();
if (activity == null) return;
String linkText = getLink();
if (linkText == null) throw new AssertionError();
if (OUR_LINK.equals("briar://" + linkText)) {
new AlertDialog.Builder(activity, R.style.BriarDialogTheme_Neutral)
.setMessage(
"Add the link you get from your contact, not your own link.")
.setNeutralButton(R.string.ok,
(dialog, which) -> {
linkInput.setText(null);
dialog.cancel();
})
.show();
return;
}
activity.addFakeRequest(contactNameInput.getText().toString(),
linkText);
Intent intent = new Intent(activity, PendingRequestsActivity.class);
startActivity(intent);
finish();
}
}

View File

@@ -2,22 +2,20 @@ package org.briarproject.briar.android.contact;
import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.FloatingActionButton;
import android.support.design.widget.Snackbar;
import android.support.v4.app.ActivityCompat;
import android.support.v4.app.ActivityOptionsCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.util.Pair;
import android.support.v7.widget.LinearLayoutManager;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.contact.event.ContactRemovedEvent;
import org.briarproject.bramble.api.contact.event.ContactStatusChangedEvent;
import org.briarproject.bramble.api.db.DbException;
@@ -39,24 +37,17 @@ import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.android.AndroidNotificationManager;
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
import org.briarproject.briar.api.messaging.ConversationManager;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
import org.briarproject.briar.api.messaging.event.PrivateMessageReceivedEvent;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import javax.inject.Inject;
import io.github.kobakei.materialfabspeeddial.FabSpeedDial;
import io.github.kobakei.materialfabspeeddial.FabSpeedDial.OnMenuItemClickListener;
import static android.os.Build.VERSION.SDK_INT;
import static android.support.design.widget.Snackbar.LENGTH_INDEFINITE;
import static android.support.v4.app.ActivityOptionsCompat.makeSceneTransitionAnimation;
import static android.support.v4.view.ViewCompat.getTransitionName;
import static java.util.logging.Level.WARNING;
@@ -67,8 +58,7 @@ import static org.briarproject.briar.android.contact.ConversationActivity.CONTAC
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class ContactListFragment extends BaseFragment implements EventListener,
OnMenuItemClickListener {
public class ContactListFragment extends BaseFragment implements EventListener {
public static final String TAG = ContactListFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
@@ -80,13 +70,8 @@ public class ContactListFragment extends BaseFragment implements EventListener,
@Inject
AndroidNotificationManager notificationManager;
// TODO remove
@Inject
MessagingManager messagingManager;
private ContactListAdapter adapter;
private BriarRecyclerView list;
private Snackbar snackbar;
// Fields that are accessed from background threads must be volatile
@Inject
@@ -119,12 +104,7 @@ public class ContactListFragment extends BaseFragment implements EventListener,
getActivity().setTitle(R.string.contact_list_button);
View contentView =
inflater.inflate(R.layout.fragment_contact_list, container,
false);
FabSpeedDial speedDialView = contentView.findViewById(R.id.speedDial);
speedDialView.addOnMenuItemClickListener(this);
View contentView = inflater.inflate(R.layout.list, container, false);
OnContactClickListener<ContactListItem> onContactClickListener =
(view, item) -> {
@@ -163,32 +143,26 @@ public class ContactListFragment extends BaseFragment implements EventListener,
list.setEmptyText(getString(R.string.no_contacts));
list.setEmptyAction(getString(R.string.no_contacts_action));
snackbar = Snackbar.make(contentView,
R.string.pending_contact_requests_snackbar, LENGTH_INDEFINITE);
snackbar.getView().setBackgroundResource(R.color.briar_primary);
snackbar.setAction(R.string.show, v -> startActivity(
new Intent(getContext(), PendingRequestsActivity.class)));
snackbar.setActionTextColor(ContextCompat
.getColor(getContext(), R.color.briar_button_text_positive));
return contentView;
}
@Override
public void onMenuItemClick(FloatingActionButton fab, @Nullable TextView v,
int itemId) {
switch (itemId) {
case R.id.action_add_contact_nearby:
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.contact_list_actions, menu);
super.onCreateOptionsMenu(menu, inflater);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle presses on the action bar items
switch (item.getItemId()) {
case R.id.action_add_contact:
Intent intent =
new Intent(getContext(), ContactExchangeActivity.class);
startActivity(intent);
return;
case R.id.action_add_contact_remotely:
startActivity(new Intent(getContext(),
ContactLinkExchangeActivity.class));
return;
return true;
default:
return;
return super.onOptionsItemSelected(item);
}
}
@@ -200,26 +174,6 @@ public class ContactListFragment extends BaseFragment implements EventListener,
notificationManager.clearAllIntroductionNotifications();
loadContacts();
list.startPeriodicUpdate();
// TODO remove
checkForPendingContacts();
}
// TODO remove
private void checkForPendingContacts() {
listener.runOnDbThread(() -> {
try {
Collection<PendingContact> contacts =
messagingManager.getPendingContacts();
if (contacts.isEmpty()) {
runOnUiThreadUnlessDestroyed(() -> snackbar.dismiss());
} else {
runOnUiThreadUnlessDestroyed(() -> snackbar.show());
}
} catch (DbException e) {
e.printStackTrace();
}
});
}
@Override
@@ -294,11 +248,6 @@ public class ContactListFragment extends BaseFragment implements EventListener,
PrivateMessageHeader h = p.getMessageHeader();
updateItem(p.getContactId(), h);
}
// TODO remove
else if (e instanceof ContactAddedEvent) {
checkForPendingContacts();
}
}
private void updateItem(ContactId c, PrivateMessageHeader h) {

View File

@@ -1,185 +0,0 @@
package org.briarproject.briar.android.contact;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.ActivityCompat;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import com.google.zxing.Result;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.keyagreement.CameraException;
import org.briarproject.briar.android.keyagreement.CameraView;
import org.briarproject.briar.android.keyagreement.QrCodeDecoder;
import org.briarproject.briar.android.util.UiUtils;
import java.util.logging.Logger;
import javax.annotation.Nullable;
import static android.Manifest.permission.CAMERA;
import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
import static android.content.pm.PackageManager.PERMISSION_GRANTED;
import static android.widget.Toast.LENGTH_LONG;
import static android.widget.Toast.LENGTH_SHORT;
import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_PERMISSION_CAMERA;
public class ContactQrCodeInputFragment extends BaseFragment
implements QrCodeDecoder.ResultCallback {
static final String TAG = ContactQrCodeInputFragment.class.getName();
private static final Logger LOG = Logger.getLogger(TAG);
private CameraView cameraView;
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
if (getActivity() == null) throw new AssertionError();
super.onActivityCreated(savedInstanceState);
getActivity().setRequestedOrientation(SCREEN_ORIENTATION_NOSENSOR);
cameraView.setPreviewConsumer(new QrCodeDecoder(this));
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (getActivity() == null) return null;
getActivity().setTitle(R.string.scan_qr_code_title);
View v = inflater.inflate(R.layout.fragment_contact_qr_code_input,
container, false);
cameraView = v.findViewById(R.id.camera_view);
return v;
}
@Override
public void onStart() {
super.onStart();
if (checkPermissions()) {
startCamera();
}
}
@Override
public void onStop() {
super.onStop();
try {
cameraView.stop();
} catch (CameraException e) {
logException(LOG, WARNING, e);
}
}
private void startCamera() {
try {
cameraView.start();
} catch (CameraException e) {
logException(LOG, WARNING, e);
Toast.makeText(getContext(), R.string.camera_error_toast,
LENGTH_SHORT).show();
}
}
private boolean checkPermissions() {
if (getContext() == null) return false;
if (ActivityCompat.checkSelfPermission(getContext(), CAMERA) !=
PERMISSION_GRANTED) {
// Should we show an explanation?
if (shouldShowRequestPermissionRationale(CAMERA)) {
DialogInterface.OnClickListener continueListener =
(dialog, which) -> requestPermission();
AlertDialog.Builder
builder = new AlertDialog.Builder(getContext(),
R.style.BriarDialogTheme);
builder.setTitle(R.string.permission_camera_title);
builder.setMessage(R.string.permission_camera_request_body);
builder.setNeutralButton(R.string.continue_button,
continueListener);
builder.show();
} else {
requestPermission();
}
return false;
} else {
return true;
}
}
@Override
public void onRequestPermissionsResult(int requestCode,
@NonNull String[] permissions, @NonNull int[] grantResults) {
if (getContext() == null) return;
if (requestCode == REQUEST_PERMISSION_CAMERA) {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0 &&
grantResults[0] == PERMISSION_GRANTED) {
startCamera();
} else {
if (!shouldShowRequestPermissionRationale(CAMERA)) {
// The user has permanently denied the request
AlertDialog.Builder
builder = new AlertDialog.Builder(getContext(),
R.style.BriarDialogTheme);
builder.setTitle(R.string.permission_camera_title);
builder.setMessage(R.string.permission_camera_denied_body);
builder.setPositiveButton(R.string.ok,
UiUtils.getGoToSettingsListener(getContext()));
builder.setNegativeButton(R.string.cancel,
(dialog, which) -> cancel());
builder.show();
} else {
Toast.makeText(getContext(),
R.string.permission_camera_denied_toast,
LENGTH_LONG).show();
cancel();
}
}
}
}
private void requestPermission() {
requestPermissions(new String[] {CAMERA}, REQUEST_PERMISSION_CAMERA);
}
@Nullable
private ContactLinkExchangeActivity getCastActivity() {
return (ContactLinkExchangeActivity) getActivity();
}
private void cancel() {
ContactLinkExchangeActivity activity = getCastActivity();
if (activity != null) activity.linkScanned(null);
}
@Override
public void handleResult(@NonNull Result result) {
LOG.info("Scanned link: " + result.getText());
ContactLinkExchangeActivity activity = getCastActivity();
if (activity != null) activity.linkScanned(result.getText());
}
}

View File

@@ -1,54 +0,0 @@
package org.briarproject.briar.android.contact;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.fragment.BaseFragment;
import org.briarproject.briar.android.view.QrCodeView;
import javax.annotation.Nullable;
import static org.briarproject.briar.android.contact.ContactLinkExchangeActivity.OUR_LINK;
import static org.briarproject.briar.android.keyagreement.QrCodeUtils.createQrCode;
public class ContactQrCodeOutputFragment extends BaseFragment {
static final String TAG = ContactQrCodeOutputFragment.class.getName();
@Override
public String getUniqueTag() {
return TAG;
}
@Override
public void injectFragment(ActivityComponent component) {
component.inject(this);
}
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater,
@Nullable ViewGroup container,
@Nullable Bundle savedInstanceState) {
if (getActivity() == null) return null;
getActivity().setTitle(R.string.show_qr_code_title);
View v = inflater.inflate(R.layout.fragment_contact_qr_code_output,
container, false);
DisplayMetrics dm = getResources().getDisplayMetrics();
Bitmap qrCode = createQrCode(dm, OUR_LINK);
QrCodeView qrCodeView = v.findViewById(R.id.qrCodeView);
qrCodeView.setQrCode(qrCode);
return v;
}
}

View File

@@ -1,122 +0,0 @@
package org.briarproject.briar.android.contact;
import android.os.Bundle;
import android.support.v7.app.ActionBar;
import android.support.v7.widget.LinearLayoutManager;
import android.view.MenuItem;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.event.ContactAddedEvent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.event.Event;
import org.briarproject.bramble.api.event.EventBus;
import org.briarproject.bramble.api.event.EventListener;
import org.briarproject.briar.R;
import org.briarproject.briar.android.activity.ActivityComponent;
import org.briarproject.briar.android.activity.BriarActivity;
import org.briarproject.briar.android.view.BriarRecyclerView;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
import java.util.Collection;
import javax.annotation.Nullable;
import javax.inject.Inject;
public class PendingRequestsActivity extends BriarActivity
implements EventListener {
@Inject
MessagingManager messagingManager;
@Inject
ContactManager contactManager;
@Inject
EventBus eventBus;
private PendingRequestsAdapter adapter;
private BriarRecyclerView list;
@Override
public void injectActivity(ActivityComponent component) {
component.inject(this);
}
@Override
public void onCreate(@Nullable Bundle state) {
super.onCreate(state);
setContentView(R.layout.list);
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setDisplayHomeAsUpEnabled(true);
}
adapter = new PendingRequestsAdapter(this, PendingContact.class);
list = findViewById(R.id.list);
list.setLayoutManager(new LinearLayoutManager(this));
list.setAdapter(adapter);
}
@Override
public void onStart() {
super.onStart();
eventBus.addListener(this);
runOnDbThread(() -> {
try {
Collection<PendingContact> contacts =
messagingManager.getPendingContacts();
addPendingContacts(contacts);
} catch (DbException e) {
e.printStackTrace();
}
});
}
@Override
protected void onStop() {
super.onStop();
adapter.clear();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
@Override
public void eventOccurred(Event e) {
if (e instanceof ContactAddedEvent) {
runOnDbThread(() -> {
try {
Contact contact = contactManager
.getContact(((ContactAddedEvent) e).getContactId());
runOnUiThreadUnlessDestroyed(() -> {
adapter.remove(contact);
if (adapter.isEmpty()) finish();
});
} catch (DbException e1) {
e1.printStackTrace();
}
});
}
}
private void addPendingContacts(Collection<PendingContact> contacts) {
runOnUiThreadUnlessDestroyed(() -> {
if (contacts.isEmpty()) {
list.showData();
} else {
adapter.addAll(contacts);
}
});
}
}

View File

@@ -1,67 +0,0 @@
package org.briarproject.briar.android.contact;
import android.content.Context;
import android.support.annotation.NonNull;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.util.BriarAdapter;
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
@NotNullByDefault
public class PendingRequestsAdapter extends
BriarAdapter<PendingContact, PendingRequestsViewHolder> {
public PendingRequestsAdapter(Context ctx, Class<PendingContact> c) {
super(ctx, c);
}
@NonNull
@Override
public PendingRequestsViewHolder onCreateViewHolder(
ViewGroup viewGroup, int i) {
View v = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.list_item_pending_contact, viewGroup, false);
return new PendingRequestsViewHolder(v);
}
@Override
public void onBindViewHolder(
PendingRequestsViewHolder pendingRequestsViewHolder, int i) {
pendingRequestsViewHolder.bind(items.get(i));
}
@Override
public int compare(PendingContact item1, PendingContact item2) {
return (int) (item1.getTimestamp() - item2.getTimestamp());
}
@Override
public boolean areContentsTheSame(PendingContact item1,
PendingContact item2) {
return item1.getName().equals(item2.getName()) &&
item1.getTimestamp() == item2.getTimestamp();
}
@Override
public boolean areItemsTheSame(PendingContact item1,
PendingContact item2) {
return item1.getName().equals(item2.getName()) &&
item1.getTimestamp() == item2.getTimestamp();
}
// TODO remove
public void remove(Contact contact) {
for (int i = 0; i < items.size(); i++) {
if (items.get(i).getName().equals(contact.getAuthor().getName())) {
items.removeItemAt(i);
return;
}
}
}
}

View File

@@ -1,36 +0,0 @@
package org.briarproject.briar.android.contact;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.view.View;
import android.widget.TextView;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.briar.R;
import org.briarproject.briar.android.view.TextAvatarView;
import org.briarproject.briar.api.messaging.MessagingManager.PendingContact;
import static org.briarproject.bramble.util.StringUtils.toUtf8;
import static org.briarproject.briar.android.util.UiUtils.formatDate;
@NotNullByDefault
public class PendingRequestsViewHolder extends ViewHolder {
private final TextAvatarView avatar;
private final TextView name;
private final TextView time;
public PendingRequestsViewHolder(View v) {
super(v);
avatar = v.findViewById(R.id.avatar);
name = v.findViewById(R.id.name);
time = v.findViewById(R.id.time);
}
public void bind(PendingContact item) {
avatar.setText(item.getName());
avatar.setBackgroundBytes(toUtf8(item.getName() + item.getTimestamp()));
name.setText(item.getName());
time.setText(formatDate(time.getContext(), item.getTimestamp()));
}
}

View File

@@ -2,7 +2,7 @@ package org.briarproject.briar.android.keyagreement;
import java.io.IOException;
public class CameraException extends IOException {
class CameraException extends IOException {
CameraException(String message) {
super(message);

View File

@@ -7,7 +7,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
@SuppressWarnings("deprecation")
@NotNullByDefault
public interface PreviewConsumer {
interface PreviewConsumer {
@UiThread
void start(Camera camera, int cameraIndex);

View File

@@ -29,7 +29,7 @@ import static java.util.logging.Level.WARNING;
@SuppressWarnings("deprecation")
@MethodsNotNullByDefault
@ParametersNotNullByDefault
public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
private static final Logger LOG =
Logger.getLogger(QrCodeDecoder.class.getName());
@@ -40,7 +40,7 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
private Camera camera = null;
private int cameraIndex = 0;
public QrCodeDecoder(ResultCallback callback) {
QrCodeDecoder(ResultCallback callback) {
this.callback = callback;
}
@@ -73,7 +73,8 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
if (data.length == size.width * size.height * 3 / 2) {
CameraInfo info = new CameraInfo();
Camera.getCameraInfo(cameraIndex, info);
new DecoderTask(data, size.width, size.height).execute();
new DecoderTask(data, size.width, size.height,
info.orientation).execute();
} else {
// Camera parameters have changed - ask for a new preview
LOG.info("Preview size does not match camera parameters");
@@ -90,18 +91,19 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
private class DecoderTask extends AsyncTask<Void, Void, Void> {
private final byte[] data;
private final int width;
private final int height;
private final int width, height, orientation;
private DecoderTask(byte[] data, int width, int height) {
private DecoderTask(byte[] data, int width, int height,
int orientation) {
this.data = data;
this.width = width;
this.height = height;
this.orientation = orientation;
}
@Override
protected Void doInBackground(Void... params) {
BinaryBitmap bitmap = binarize(data, width, height);
BinaryBitmap bitmap = binarize(data, width, height, orientation);
Result result;
try {
result = reader.decode(bitmap,
@@ -125,14 +127,21 @@ public class QrCodeDecoder implements PreviewConsumer, PreviewCallback {
}
}
private static BinaryBitmap binarize(byte[] data, int width, int height) {
private static BinaryBitmap binarize(byte[] data, int width, int height,
int orientation) {
// Crop to a square at the top (portrait) or left (landscape) of the
// screen - this will be faster to decode and should include
// everything visible in the viewfinder
int crop = Math.min(width, height);
int left = orientation >= 180 ? width - crop : 0;
int top = orientation >= 180 ? height - crop : 0;
LuminanceSource src = new PlanarYUVLuminanceSource(data, width,
height, 0, 0, width, height, false);
height, left, top, crop, crop, false);
return new BinaryBitmap(new HybridBinarizer(src));
}
@NotNullByDefault
public interface ResultCallback {
interface ResultCallback {
void handleResult(Result result);
}

View File

@@ -21,13 +21,13 @@ import static java.util.logging.Level.WARNING;
import static org.briarproject.bramble.util.LogUtils.logException;
@NotNullByDefault
public class QrCodeUtils {
class QrCodeUtils {
private static final Logger LOG =
Logger.getLogger(QrCodeUtils.class.getName());
@Nullable
public static Bitmap createQrCode(DisplayMetrics dm, String input) {
static Bitmap createQrCode(DisplayMetrics dm, String input) {
int smallestDimen = Math.min(dm.widthPixels, dm.heightPixels);
try {
// Generate QR code

View File

@@ -39,9 +39,6 @@ public class QrCodeView extends FrameLayout {
listener.setFullscreen(fullscreen);
}
);
// TODO remove
fullscreenButton.setVisibility(INVISIBLE);
}
@UiThread

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M12,11c-1.1,0 -2,0.9 -2,2s0.9,2 2,2 2,-0.9 2,-2 -0.9,-2 -2,-2zM18,13c0,-3.31 -2.69,-6 -6,-6s-6,2.69 -6,6c0,2.22 1.21,4.15 3,5.19l1,-1.74c-1.19,-0.7 -2,-1.97 -2,-3.45 0,-2.21 1.79,-4 4,-4s4,1.79 4,4c0,1.48 -0.81,2.75 -2,3.45l1,1.74c1.79,-1.04 3,-2.97 3,-5.19zM12,3C6.48,3 2,7.48 2,13c0,3.7 2.01,6.92 4.99,8.65l1,-1.73C5.61,18.53 4,15.96 4,13c0,-4.42 3.58,-8 8,-8s8,3.58 8,8c0,2.96 -1.61,5.53 -4,6.92l1,1.73c2.99,-1.73 5,-4.95 5,-8.65 0,-5.52 -4.48,-10 -10,-10z"/>
</vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#2A93C6"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M16,1L4,1c-1.1,0 -2,0.9 -2,2v14h2L4,3h12L16,1zM19,5L8,5c-1.1,0 -2,0.9 -2,2v14c0,1.1 0.9,2 2,2h11c1.1,0 2,-0.9 2,-2L21,7c0,-1.1 -0.9,-2 -2,-2zM19,21L8,21L8,7h11v14z"/>
</vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#2A93C6"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FF000000"
android:pathData="M19,2h-4.18C14.4,0.84 13.3,0 12,0c-1.3,0 -2.4,0.84 -2.82,2L5,2c-1.1,0 -2,0.9 -2,2v16c0,1.1 0.9,2 2,2h14c1.1,0 2,-0.9 2,-2L21,4c0,-1.1 -0.9,-2 -2,-2zM12,2c0.55,0 1,0.45 1,1s-0.45,1 -1,1 -1,-0.45 -1,-1 0.45,-1 1,-1zM19,20L5,20L5,4h2v3h10L17,4h2v16z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3.9,12c0,-1.71 1.39,-3.1 3.1,-3.1h4L11,7L7,7c-2.76,0 -5,2.24 -5,5s2.24,5 5,5h4v-1.9L7,15.1c-1.71,0 -3.1,-1.39 -3.1,-3.1zM8,13h8v-2L8,11v2zM17,7h-4v1.9h4c1.71,0 3.1,1.39 3.1,3.1s-1.39,3.1 -3.1,3.1h-4L13,17h4c2.76,0 5,-2.24 5,-5s-2.24,-5 -5,-5z"/>
</vector>

View File

@@ -1,12 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M16,6H13V7.9H16C18.26,7.9 20.1,9.73 20.1,12A4.1,4.1 0,0 1,16 16.1H13V18H16A6,6 0,0 0,22 12C22,8.68 19.31,6 16,6M3.9,12C3.9,9.73 5.74,7.9 8,7.9H11V6H8A6,6 0,0 0,2 12A6,6 0,0 0,8 18H11V16.1H8C5.74,16.1 3.9,14.26 3.9,12M8,13H16V11H8V13Z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="m21.6598,17.4732h-2.2959v3.0612H17.5118l3,3 3,-3h-1.852z"/>
</vector>

View File

@@ -1,12 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M16,6H13V7.9H16C18.26,7.9 20.1,9.73 20.1,12A4.1,4.1 0,0 1,16 16.1H13V18H16A6,6 0,0 0,22 12C22,8.68 19.31,6 16,6M3.9,12C3.9,9.73 5.74,7.9 8,7.9H11V6H8A6,6 0,0 0,2 12A6,6 0,0 0,8 18H11V16.1H8C5.74,16.1 3.9,14.26 3.9,12M8,13H16V11H8V13Z"/>
<path
android:fillColor="#FFFFFF"
android:pathData="M21.6598,23.5344H19.3639V20.4732H17.5118l3,-3 3,3h-1.852z"/>
</vector>

View File

@@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M12,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM12,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View File

@@ -1,5 +0,0 @@
<vector android:height="24dp" android:tint="#FFFFFF"
android:viewportHeight="24.0" android:viewportWidth="24.0"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="#FF000000" android:pathData="M15,12c2.21,0 4,-1.79 4,-4s-1.79,-4 -4,-4 -4,1.79 -4,4 1.79,4 4,4zM6,10L6,7L4,7v3L1,10v2h3v3h2v-3h3v-2L6,10zM15,14c-2.67,0 -8,1.34 -8,4v2h16v-2c0,-2.66 -5.33,-4 -8,-4z"/>
</vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#2A93C6"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M3,11H5V13H3V11M11,5H13V9H11V5M9,11H13V15H11V13H9V11M15,11H17V13H19V11H21V13H19V15H21V19H19V21H17V19H13V21H11V17H15V15H17V13H15V11M19,19V15H17V19H19M15,3H21V9H15V3M17,5V7H19V5H17M3,3H9V9H3V3M5,5V7H7V5H5M3,15H9V21H3V15M5,17V19H7V17H5Z"/>
</vector>

View File

@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportHeight="24"
android:viewportWidth="24">
<path
android:fillColor="#FFFFFF"
android:pathData="M4,4H10V10H4V4M20,4V10H14V4H20M14,15H16V13H14V11H16V13H18V11H20V13H18V15H20V18H18V20H16V18H13V20H11V16H14V15M16,15V18H18V15H16M4,20V14H10V20H4M6,6V8H8V6H6M16,6V8H18V6H16M6,16V18H8V16H6M4,11H6V13H4V11M9,11H13V15H11V13H9V11M11,6H13V10H11V6M2,2V6H0V2A2,2 0 0,1 2,0H6V2H2M22,0A2,2 0 0,1 24,2V6H22V2H18V0H22M2,18V22H6V24H2A2,2 0 0,1 0,22V18H2M22,22V18H24V22A2,2 0 0,1 22,24H18V22H22Z"/>
</vector>

View File

@@ -1,10 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:tint="#2A93C6"
android:viewportHeight="24.0"
android:viewportWidth="24.0">
<path
android:fillColor="#FFFFFFFF"
android:pathData="M18,16.08c-0.76,0 -1.44,0.3 -1.96,0.77L8.91,12.7c0.05,-0.23 0.09,-0.46 0.09,-0.7s-0.04,-0.47 -0.09,-0.7l7.05,-4.11c0.54,0.5 1.25,0.81 2.04,0.81 1.66,0 3,-1.34 3,-3s-1.34,-3 -3,-3 -3,1.34 -3,3c0,0.24 0.04,0.47 0.09,0.7L8.04,9.81C7.5,9.31 6.79,9 6,9c-1.66,0 -3,1.34 -3,3s1.34,3 3,3c0.79,0 1.5,-0.31 2.04,-0.81l7.12,4.16c-0.05,0.21 -0.08,0.43 -0.08,0.65 0,1.61 1.31,2.92 2.92,2.92 1.61,0 2.92,-1.31 2.92,-2.92s-1.31,-2.92 -2.92,-2.92z"/>
</vector>

View File

@@ -1,37 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<EditText
android:id="@+id/contactNameInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:hint="@string/contact_name_hint"
android:importantForAutofill="no"
android:inputType="text|textCapWords"
app:layout_constraintBottom_toTopOf="@id/linkInput"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
<Button
android:id="@+id/addButton"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:layout_marginTop="8dp"
android:enabled="false"
android:text="@string/add_contact_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/contactNameInput"
tools:enabled="true"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -1,189 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.constraint.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="@dimen/margin_large">
<android.support.design.widget.TextInputLayout
android:id="@+id/contactNameLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:errorEnabled="true"
app:hintEnabled="false"
app:layout_constraintBottom_toTopOf="@+id/addButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pasteButton">
<android.support.design.widget.TextInputEditText
android:id="@+id/contactNameInput"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawableLeft="@drawable/ic_person"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_person"
android:drawableTint="?attr/colorControlNormal"
android:hint="@string/contact_name_hint"
android:importantForAutofill="no"
android:inputType="text|textCapWords"/>
</android.support.design.widget.TextInputLayout>
<android.support.design.widget.TextInputLayout
android:id="@+id/linkInputLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
app:errorEnabled="true"
app:hintEnabled="false"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/copyButton">
<android.support.design.widget.TextInputEditText
android:id="@+id/linkInput"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:drawableLeft="@drawable/ic_link"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_link"
android:drawableTint="?attr/colorControlNormal"
android:hint="@string/contact_link_hint"
android:importantForAutofill="no"
android:inputType="textUri"/>
</android.support.design.widget.TextInputLayout>
<Button
android:id="@+id/pasteButton"
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:drawableLeft="@drawable/ic_content_paste"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_content_paste"
android:text="@string/paste_button"
app:layout_constraintBottom_toTopOf="@+id/contactNameLayout"
app:layout_constraintEnd_toStartOf="@id/scanCodeButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linkInputLayout"/>
<Button
android:id="@+id/scanCodeButton"
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_qr_code"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_qr_code"
android:text="@string/scan_qr_code_button"
android:visibility="visible"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@id/pasteButton"
app:layout_constraintTop_toTopOf="@+id/pasteButton"
tools:visibility="visible"/>
<TextView
android:id="@+id/linkIntro"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:gravity="left|start"
android:text="@string/send_link_instructions"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@+id/linkView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
<TextView
android:id="@+id/linkView"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:background="@color/briar_white"
android:padding="8dp"
android:textColor="@color/briar_primary"
android:textIsSelectable="true"
android:textSize="18sp"
android:typeface="monospace"
app:layout_constraintBottom_toTopOf="@+id/copyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linkIntro"
tools:text="briar://scnsdflamslkfjgluoblmksdfbwevlewajfdlkjewwhqliafskfjhskdjhvoieiv"
/>
<Button
android:id="@+id/copyButton"
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:drawableLeft="@drawable/ic_content_copy"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_content_copy"
android:text="@string/copy_button"
app:layout_constraintBottom_toTopOf="@+id/linkInputLayout"
app:layout_constraintEnd_toStartOf="@id/shareButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linkView"/>
<Button
android:id="@+id/shareButton"
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/social_share_blue"
android:drawablePadding="8dp"
android:drawableStart="@drawable/social_share_blue"
android:text="@string/share_button"
app:layout_constraintBottom_toBottomOf="@id/copyButton"
app:layout_constraintEnd_toStartOf="@id/showCodeButton"
app:layout_constraintStart_toEndOf="@id/copyButton"
app:layout_constraintTop_toTopOf="@id/copyButton"/>
<Button
android:id="@+id/showCodeButton"
style="@style/BriarButtonFlat.Positive.Tiny"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_qr_code"
android:drawablePadding="8dp"
android:drawableStart="@drawable/ic_qr_code"
android:drawableTint="@color/briar_button_text_positive"
android:text="@string/show_qr_code_button"
android:visibility="visible"
app:layout_constraintBottom_toBottomOf="@id/copyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/shareButton"
app:layout_constraintTop_toTopOf="@id/copyButton"
tools:visibility="visible"/>
<Button
android:id="@+id/addButton"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="16dp"
android:enabled="false"
android:text="@string/add_contact_button"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/contactNameLayout"
tools:enabled="true"/>
</android.support.constraint.ConstraintLayout>
</ScrollView>

View File

@@ -1,95 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<EditText
android:id="@+id/contactNameInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:drawableLeft="@drawable/ic_person"
android:drawablePadding="8dp"
android:drawableTint="?attr/colorControlNormal"
android:hint="@string/contact_name_hint"
android:importantForAutofill="no"
android:inputType="text|textCapWords"
app:layout_constraintBottom_toTopOf="@id/linkInput"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
android:drawableStart="@drawable/ic_person"/>
<EditText
android:id="@+id/linkInput"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="@dimen/margin_large"
android:layout_marginRight="8dp"
android:layout_marginStart="@dimen/margin_large"
android:layout_marginTop="16dp"
android:drawableLeft="@drawable/ic_link"
android:drawablePadding="8dp"
android:drawableTint="?attr/colorControlNormal"
android:hint="@string/contact_link_hint"
android:importantForAutofill="no"
android:inputType="textUri"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/contactNameInput"/>
<Button
android:id="@+id/pasteButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:drawableLeft="@drawable/ic_content_paste"
android:drawablePadding="8dp"
android:text="@string/paste_button"
app:layout_constraintEnd_toStartOf="@+id/scanCodeButton"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linkInput"/>
<Button
android:id="@+id/scanCodeButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="8dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="8dp"
android:drawableLeft="@drawable/ic_qr_code"
android:drawablePadding="8dp"
android:text="Scan Code"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/pasteButton"
app:layout_constraintTop_toBottomOf="@+id/linkInput"/>
<Button
android:id="@+id/addButton"
style="@style/BriarButton"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:layout_marginTop="8dp"
android:enabled="false"
android:text="@string/add_contact_button"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.75"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/pasteButton"
tools:enabled="true"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -1,87 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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">
<TextView
android:id="@+id/linkIntro"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:text="@string/send_link_instructions"
android:textIsSelectable="true"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@id/linkView"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"/>
<TextView
android:id="@+id/linkView"
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:background="@color/briar_white"
android:padding="8dp"
android:textColor="@color/briar_primary"
android:textIsSelectable="true"
android:textSize="18sp"
android:typeface="monospace"
app:layout_constraintBottom_toTopOf="@+id/copyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/linkIntro"
tools:text="briar://scnsdflamslkfjgluoblmksdfbwevlewajfdlkjewwhqliafskfjhskdjhvoieiv"/>
<android.support.v7.widget.AppCompatButton
android:id="@+id/copyButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/margin_large"
android:drawableLeft="@drawable/ic_content_copy"
android:drawablePadding="8dp"
android:text="@string/copy_button"
app:layout_constraintEnd_toStartOf="@+id/shareButton"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="spread"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/linkView"/>
<Button
android:id="@+id/shareButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/social_share_blue"
android:drawablePadding="8dp"
android:text="@string/share_button"
app:layout_constraintBottom_toBottomOf="@id/copyButton"
app:layout_constraintEnd_toStartOf="@+id/showCodeButton"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/copyButton"
app:layout_constraintTop_toTopOf="@id/copyButton"/>
<Button
android:id="@+id/showCodeButton"
style="@style/BriarButtonFlat.Positive"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@drawable/ic_qr_code"
android:drawablePadding="8dp"
android:drawableTint="@color/briar_button_text_positive"
android:text="QR Code"
app:layout_constraintBottom_toBottomOf="@+id/copyButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/shareButton"
app:layout_constraintTop_toTopOf="@+id/copyButton"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
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="match_parent">
<org.briarproject.briar.android.view.BriarRecyclerView
android:id="@+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:scrollToEnd="false"/>
<io.github.kobakei.materialfabspeeddial.FabSpeedDial
android:id="@+id/speedDial"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:fab_fabDrawable="@drawable/ic_action_add"
app:fab_fabRippleColor="@android:color/transparent"
app:fab_menu="@menu/contact_list_actions"
app:fab_miniFabTextBackground="@color/briar_accent"
app:fab_miniFabTextColor="@android:color/white"/>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -1,53 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<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="match_parent">
<org.briarproject.briar.android.keyagreement.CameraView
android:id="@+id/camera_view"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintBottom_toTopOf="@+id/textView3"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="@+id/textView3"
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="16dp"
android:visibility="gone"
android:text="or"
android:textSize="18sp"
app:layout_constraintBottom_toTopOf="@+id/enterLinkButton"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/camera_view"/>
<Button
android:id="@+id/enterLinkButton"
style="@style/BriarButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="8dp"
android:layout_marginStart="8dp"
android:layout_marginTop="16dp"
android:drawableLeft="@drawable/ic_link"
android:drawablePadding="8dp"
android:visibility="gone"
android:text="Enter Link"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView3"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -1,13 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<org.briarproject.briar.android.view.QrCodeView
android:id="@+id/qrCodeView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/white"/>
</FrameLayout>

View File

@@ -1,81 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
android:id="@+id/linearLayout4"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<org.briarproject.briar.android.view.TextAvatarView
android:id="@+id/avatar"
android:layout_width="@dimen/listitem_picture_frame_size"
android:layout_height="@dimen/listitem_picture_frame_size"
android:layout_marginBottom="8dp"
android:layout_marginLeft="@dimen/listitem_horizontal_margin"
android:layout_marginStart="@dimen/listitem_horizontal_margin"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<com.vanniktech.emoji.EmojiTextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/margin_large"
android:layout_marginStart="@dimen/margin_large"
android:layout_marginTop="@dimen/margin_large"
android:textColor="?android:attr/textColorPrimary"
android:textSize="@dimen/text_size_medium"
app:layout_constrainedWidth="true"
app:layout_constraintBottom_toTopOf="@+id/status"
app:layout_constraintEnd_toStartOf="@+id/time"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintHorizontal_chainStyle="spread_inside"
app:layout_constraintStart_toEndOf="@+id/avatar"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_chainStyle="packed"
tools:text="This is a name of a contact"/>
<TextView
android:id="@+id/status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layout_marginEnd="16dp"
android:layout_marginRight="16dp"
android:layout_marginTop="8dp"
android:text="@string/add_contact_remote_connecting"
app:layout_constraintBottom_toTopOf="@+id/divider"
app:layout_constraintEnd_toStartOf="@+id/time"
app:layout_constraintHorizontal_bias="0.0"
app:layout_constraintStart_toStartOf="@+id/name"
app:layout_constraintTop_toBottomOf="@+id/name"/>
<TextView
android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_margin="@dimen/margin_large"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:textColor="?android:attr/textColorSecondary"
android:textSize="@dimen/text_size_small"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/name"
app:layout_constraintTop_toTopOf="parent"
tools:text="Dec 24"/>
<View
android:id="@+id/divider"
style="@style/Divider.ContactList"
android:layout_width="0dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/avatar"/>
</android.support.constraint.ConstraintLayout>

View File

@@ -4,17 +4,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_add_contact_nearby"
android:icon="@drawable/ic_add_nearby"
android:orderInCategory="3"
android:title="@string/add_contact_nearby_title"
app:showAsAction="never"/>
<item
android:id="@+id/action_add_contact_remotely"
android:icon="@drawable/ic_link"
android:orderInCategory="2"
android:title="@string/add_contact_remotely_title"
app:showAsAction="never"/>
android:id="@+id/action_add_contact"
android:icon="@drawable/ic_add_white"
android:title="@string/add_contact_title"
app:showAsAction="ifRoom"/>
</menu>

View File

@@ -151,26 +151,6 @@
<string name="connection_error_explanation">Please check that you\'re both connected to the same Wi-Fi network.</string>
<string name="connection_error_feedback">If this problem persists, please <a href="feedback">send feedback</a> to help us improve the app.</string>
<string name="add_contact_nearby_title">Add Contact Nearby</string>
<string name="add_contact_remotely_title">Add Contact with Link</string>
<string name="contact_name_hint">Give contact a nickname</string>
<string name="contact_link_hint">Your contact\'s link</string>
<string name="paste_button">Paste</string>
<string name="scan_qr_code_button">Scan Code</string>
<string name="add_contact_button">Add Contact</string>
<string name="copy_button">Copy</string>
<string name="share_button">Share</string>
<string name="show_qr_code_button">QR Code</string>
<string name="send_link_instructions">Give this link to your contact:</string>
<string name="link_clip_label">Briar link</string>
<string name="link_copied_toast">Link copied</string>
<string name="pending_contact_requests_snackbar">"There are pending contact requests"</string>
<string name="pending_contact_requests">Pending contact requests</string>
<string name="add_contact_remote_connecting">Connecting…</string>
<string name="scan_qr_code_title">Scan QR Code</string>
<string name="show_qr_code_title">Your QR Code</string>
<string name="camera_error_toast">Camera Error</string>
<!-- Introductions -->
<string name="introduction_onboarding_title">Introduce your contacts</string>
<string name="introduction_onboarding_text">You can introduce your contacts to each other, so they don\'t need to meet in person to connect on Briar.</string>

View File

@@ -81,7 +81,6 @@ dependencyVerification {
'com.android.tools:repository:26.2.1:repository-26.2.1.jar:fa74dae09103faef703df38550ad8fa244c5b6d1bf90d6198be932292b3d9cc1',
'com.android.tools:sdk-common:26.2.1:sdk-common-26.2.1.jar:759d4b292ca69a35cf961fca377b54158fc6c88108978006999442e80a011cf4',
'com.android.tools:sdklib:26.2.1:sdklib-26.2.1.jar:248df7ad5eac4aeb6f96c394c76760de4b7b89ac056e54d0c21a739368b91b45',
'com.github.kobakei:MaterialFabSpeedDial:1.2.0:MaterialFabSpeedDial-1.2.0.aar:fda1784d6f213e22079d5c1e9ea53e3e6eb4f908297ccfe6dea588c57aafc800',
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.0:accessibility-test-framework-2.0.jar:cdf16ef8f5b8023d003ce3cc1b0d51bda737762e2dab2fedf43d1c4292353f7f',
'com.google.android.apps.common.testing.accessibility.framework:accessibility-test-framework:2.1:accessibility-test-framework-2.1.jar:7b0aa6ed7553597ce0610684a9f7eca8021eee218f2e2f427c04a7fbf5f920bd',
'com.google.code.findbugs:jsr305:1.3.9:jsr305-1.3.9.jar:905721a0eea90a81534abb7ee6ef4ea2e5e645fa1def0a5cd88402df1b46c9ed',
@@ -192,7 +191,6 @@ dependencyVerification {
'org.robolectric:shadows-framework:3.8:shadows-framework-3.8.jar:83548db7249edf1af87e1a1f4d8f4eec3e85d6220161da601e6f6398476911b2',
'org.robolectric:shadows-support-v4:3.3.2:shadows-support-v4-3.3.2.jar:6f689264738266e70fe08db7c04b7b5a75155994f4e3f7f311960d90486bf005',
'org.robolectric:utils:3.8:utils-3.8.jar:e945d04d40e37554e02d4be1bc3abf9bede45375c843aa36d10ccb6b63edbf34',
'tools.fastlane:screengrab:1.1.0:screengrab-1.1.0.aar:03ce3868ee8a0082d14e7a1de0999f91531c0cc794392688beb08ee9bc4495fd',
'tools.fastlane:screengrab:1.2.0:screengrab-1.2.0.aar:af4ee23bb06f94404d3ab18e2ea69db8265539fc8da29f9ee45b7e472684ba83',
'uk.co.samuelwall:material-tap-target-prompt:2.12.4:material-tap-target-prompt-2.12.4.aar:6c0990ab3aa22de9f7d09dcb0a944e671128c31634ac8429012faa5c508202fb',
'xmlpull:xmlpull:1.1.3.1:xmlpull-1.1.3.1.jar:34e08ee62116071cbb69c0ed70d15a7a5b208d62798c59f2120bb8929324cb63',

View File

@@ -8,31 +8,9 @@ import org.briarproject.bramble.api.sync.GroupId;
import org.briarproject.bramble.api.sync.MessageId;
import org.briarproject.briar.api.messaging.ConversationManager.ConversationClient;
import java.util.Collection;
@NotNullByDefault
public interface MessagingManager extends ConversationClient {
// TODO remove (only for prototype)
void addNewPendingContact(String name, long timestamp) throws DbException;
void removePendingContact(String name, long timestamp) throws DbException;
Collection<PendingContact> getPendingContacts() throws DbException;
class PendingContact {
private final String name;
private final long timestamp;
public PendingContact(String name, long timestamp) {
this.name = name;
this.timestamp = timestamp;
}
public String getName() {
return name;
}
public long getTimestamp() {
return timestamp;
}
}
/**
* The unique ID of the messaging client.
*/

View File

@@ -5,19 +5,13 @@ import org.briarproject.bramble.api.client.ClientHelper;
import org.briarproject.bramble.api.client.ContactGroupFactory;
import org.briarproject.bramble.api.contact.Contact;
import org.briarproject.bramble.api.contact.ContactId;
import org.briarproject.bramble.api.contact.ContactManager;
import org.briarproject.bramble.api.contact.ContactManager.ContactHook;
import org.briarproject.bramble.api.data.BdfDictionary;
import org.briarproject.bramble.api.data.BdfEntry;
import org.briarproject.bramble.api.data.BdfList;
import org.briarproject.bramble.api.data.MetadataParser;
import org.briarproject.bramble.api.db.DatabaseComponent;
import org.briarproject.bramble.api.db.DbException;
import org.briarproject.bramble.api.db.Transaction;
import org.briarproject.bramble.api.identity.Author;
import org.briarproject.bramble.api.identity.AuthorFactory;
import org.briarproject.bramble.api.identity.AuthorId;
import org.briarproject.bramble.api.identity.IdentityManager;
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
import org.briarproject.bramble.api.sync.Client;
import org.briarproject.bramble.api.sync.Group;
@@ -29,7 +23,6 @@ import org.briarproject.bramble.api.sync.MessageStatus;
import org.briarproject.bramble.api.versioning.ClientVersioningManager;
import org.briarproject.bramble.api.versioning.ClientVersioningManager.ClientVersioningHook;
import org.briarproject.briar.api.client.MessageTracker;
import org.briarproject.briar.api.introduction.event.IntroductionSucceededEvent;
import org.briarproject.briar.api.messaging.MessagingManager;
import org.briarproject.briar.api.messaging.PrivateMessage;
import org.briarproject.briar.api.messaging.PrivateMessageHeader;
@@ -38,13 +31,11 @@ import org.briarproject.briar.client.ConversationClientImpl;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import javax.annotation.concurrent.Immutable;
import javax.inject.Inject;
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
import static org.briarproject.briar.client.MessageTrackerConstants.MSG_KEY_READ;
@Immutable
@@ -55,114 +46,14 @@ class MessagingManagerImpl extends ConversationClientImpl
private final ClientVersioningManager clientVersioningManager;
private final ContactGroupFactory contactGroupFactory;
// TODO remove
private final ContactManager contactManager;
private final IdentityManager identityManager;
private final AuthorFactory authorFactory;
@Inject
MessagingManagerImpl(DatabaseComponent db, ClientHelper clientHelper,
ClientVersioningManager clientVersioningManager,
MetadataParser metadataParser, MessageTracker messageTracker,
ContactGroupFactory contactGroupFactory,
ContactManager contactManager, IdentityManager identityManager,
AuthorFactory authorFactory) {
ContactGroupFactory contactGroupFactory) {
super(db, clientHelper, metadataParser, messageTracker);
this.clientVersioningManager = clientVersioningManager;
this.contactGroupFactory = contactGroupFactory;
// TODO remove
this.contactManager = contactManager;
this.identityManager = identityManager;
this.authorFactory = authorFactory;
}
private static final String PENDING_CONTACTS = "PENDING_CONTACTS";
@Override
public void addNewPendingContact(String name, long timestamp)
throws DbException {
Transaction txn = db.startTransaction(false);
try {
BdfList list = getPendingContacts(txn);
BdfDictionary contact = new BdfDictionary();
contact.put("name", name);
contact.put("timestamp", timestamp);
list.add(contact);
Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
MAJOR_VERSION);
BdfDictionary meta =
BdfDictionary.of(new BdfEntry(PENDING_CONTACTS, list));
clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta);
db.commitTransaction(txn);
} catch (FormatException e) {
throw new RuntimeException(e);
} finally {
db.endTransaction(txn);
}
}
@Override
public void removePendingContact(String name, long timestamp) throws DbException {
Transaction txn = db.startTransaction(false);
try {
BdfList list = getPendingContacts(txn);
BdfDictionary contactDict = new BdfDictionary();
contactDict.put("name", name);
contactDict.put("timestamp", timestamp);
list.remove(contactDict);
Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
MAJOR_VERSION);
BdfDictionary meta =
BdfDictionary.of(new BdfEntry(PENDING_CONTACTS, list));
clientHelper.mergeGroupMetadata(txn, localGroup.getId(), meta);
AuthorId local = identityManager.getLocalAuthor(txn).getId();
Author remote = authorFactory
.createAuthor(name, new byte[MAX_PUBLIC_KEY_LENGTH]);
contactManager.addContact(txn, remote, local, false, true);
Contact contact =
contactManager.getContact(txn, remote.getId(), local);
IntroductionSucceededEvent event =
new IntroductionSucceededEvent(contact);
txn.attach(event);
db.commitTransaction(txn);
} catch (FormatException e) {
throw new RuntimeException(e);
} finally {
db.endTransaction(txn);
}
}
@Override
public Collection<PendingContact> getPendingContacts() throws DbException {
Transaction txn = db.startTransaction(true);
try {
BdfList list = getPendingContacts(txn);
List<PendingContact> contacts = new ArrayList<>(list.size());
for (Object o : list) {
BdfDictionary d = (BdfDictionary) o;
contacts.add(new PendingContact(d.getString("name"),
d.getLong("timestamp")));
}
db.commitTransaction(txn);
return contacts;
} catch (FormatException e) {
throw new RuntimeException(e);
} finally {
db.endTransaction(txn);
}
}
private BdfList getPendingContacts(Transaction txn)
throws DbException, FormatException {
Group localGroup = contactGroupFactory.createLocalGroup(CLIENT_ID,
MAJOR_VERSION);
BdfDictionary d = clientHelper
.getGroupMetadataAsDictionary(txn, localGroup.getId());
return d.getList(PENDING_CONTACTS, new BdfList());
}
@Override