mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
64 Commits
beta-2017-
...
953-check-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
d311557f09 | ||
|
|
3449677b24 | ||
|
|
1ad3a6646e | ||
|
|
2d10f6b2bd | ||
|
|
5b05424d83 | ||
|
|
0826022d82 | ||
|
|
a901bfb9cb | ||
|
|
03cdce122a | ||
|
|
f2e0e16969 | ||
|
|
0c441e2ff3 | ||
|
|
21302304a5 | ||
|
|
6839d8b844 | ||
|
|
aee65a716c | ||
|
|
6a07d8f2c9 | ||
|
|
3c1ea81cd0 | ||
|
|
025f417bc7 | ||
|
|
c9dcd906c9 | ||
|
|
7024e04d15 | ||
|
|
0b8ac947db | ||
|
|
948410a064 | ||
|
|
2841339cac | ||
|
|
e8e82bd805 | ||
|
|
6876f40a0e | ||
|
|
5f4e1ecdfd | ||
|
|
044719432a | ||
|
|
d1a929da85 | ||
|
|
2a8978a60d | ||
|
|
c0afad7a26 | ||
|
|
37281c6c23 | ||
|
|
6de539a62d | ||
|
|
34704ec04d | ||
|
|
9fd6d46583 | ||
|
|
76a5e25656 | ||
|
|
3575b74837 | ||
|
|
f1c7996960 | ||
|
|
920f3581fa | ||
|
|
45e7af31fe | ||
|
|
67d5d8cdf1 | ||
|
|
9d8cadb7a9 | ||
|
|
6425c49d04 | ||
|
|
68d98b50f2 | ||
|
|
84986d393f | ||
|
|
115d488bc3 | ||
|
|
2eeb2213e3 | ||
|
|
1b48d661e8 | ||
|
|
49ba66dee9 | ||
|
|
46920f3bce | ||
|
|
4b955809f7 | ||
|
|
57d4d6546a | ||
|
|
9bfb58a764 | ||
|
|
0256ec0b8c | ||
|
|
b0b4a85d15 | ||
|
|
d40a058ef5 | ||
|
|
58b9efb24c | ||
|
|
17de785c12 | ||
|
|
c7ff1ba974 | ||
|
|
d17669f131 | ||
|
|
9755cd9ab4 | ||
|
|
6d2b18facc | ||
|
|
f8cf7034db | ||
|
|
a1e65c9fa7 | ||
|
|
499d2fe677 | ||
|
|
85c17b4cb0 | ||
|
|
0827b067ec |
20
.gitlab-ci.yml
Normal file
20
.gitlab-ci.yml
Normal file
@@ -0,0 +1,20 @@
|
||||
image: registry.gitlab.com/fdroid/ci-images:client-latest
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .gradle/wrapper
|
||||
- .gradle/caches
|
||||
|
||||
before_script:
|
||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||
# - export ANDROID_COMPILE_SDK=`sed -n 's,.*compileSdkVersion\s*\([0-9][0-9]*\).*,\1,p' app/build.gradle`
|
||||
# - echo y | android --silent update sdk --no-ui --filter android-${ANDROID_COMPILE_SDK}
|
||||
|
||||
test:
|
||||
script:
|
||||
- ./gradlew test
|
||||
|
||||
after_script:
|
||||
# this file changes every time but should not be cached
|
||||
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||
@@ -12,8 +12,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 22
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
versionCode 14
|
||||
versionName "0.14"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.briarproject.bramble.api;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
@@ -53,6 +54,12 @@ public class Bytes implements Comparable<Bytes> {
|
||||
return aBytes.length - bBytes.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return getClass().getSimpleName() +
|
||||
"(" + StringUtils.toHexString(getBytes()) + ")";
|
||||
}
|
||||
|
||||
public static class BytesComparator implements Comparator<Bytes> {
|
||||
|
||||
@Override
|
||||
|
||||
@@ -13,7 +13,9 @@ import javax.annotation.concurrent.Immutable;
|
||||
@NotNullByDefault
|
||||
public class Author {
|
||||
|
||||
public enum Status {ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES}
|
||||
public enum Status {
|
||||
NONE, ANONYMOUS, UNKNOWN, UNVERIFIED, VERIFIED, OURSELVES
|
||||
}
|
||||
|
||||
private final AuthorId id;
|
||||
private final String name;
|
||||
|
||||
@@ -8,6 +8,7 @@ public interface TorConstants {
|
||||
int CONTROL_PORT = 59051;
|
||||
|
||||
int CONNECT_TO_PROXY_TIMEOUT = 5000; // Milliseconds
|
||||
int EXTRA_SOCKET_TIMEOUT = 30000; // Milliseconds
|
||||
|
||||
String PREF_TOR_NETWORK = "network";
|
||||
String PREF_TOR_PORT = "port";
|
||||
|
||||
@@ -19,7 +19,7 @@ public class PrivacyUtils {
|
||||
|
||||
@Nullable
|
||||
public static String scrubMacAddress(@Nullable String address) {
|
||||
if (address == null) return null;
|
||||
if (address == null || address.length() == 0) return null;
|
||||
// this is a fake address we need to know about
|
||||
if (address.equals("02:00:00:00:00:00")) return address;
|
||||
// keep first and last octet of MAC address
|
||||
|
||||
@@ -668,7 +668,9 @@ class DatabaseComponentImpl<T> implements DatabaseComponent {
|
||||
acked.add(m);
|
||||
}
|
||||
}
|
||||
transaction.attach(new MessagesAckedEvent(c, acked));
|
||||
if (acked.size() > 0) {
|
||||
transaction.attach(new MessagesAckedEvent(c, acked));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -68,8 +68,8 @@ import static org.briarproject.bramble.db.ExponentialBackoff.calculateExpiry;
|
||||
@NotNullByDefault
|
||||
abstract class JdbcDatabase implements Database<Connection> {
|
||||
|
||||
private static final int SCHEMA_VERSION = 29;
|
||||
private static final int MIN_SCHEMA_VERSION = 29;
|
||||
private static final int SCHEMA_VERSION = 30;
|
||||
private static final int MIN_SCHEMA_VERSION = 30;
|
||||
|
||||
private static final String CREATE_SETTINGS =
|
||||
"CREATE TABLE settings"
|
||||
|
||||
@@ -8,6 +8,7 @@ import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.CONNECT_TO_PROXY_TIMEOUT;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.EXTRA_SOCKET_TIMEOUT;
|
||||
import static org.briarproject.bramble.api.plugin.TorConstants.SOCKS_PORT;
|
||||
|
||||
@Module
|
||||
@@ -17,6 +18,7 @@ public class SocksModule {
|
||||
SocketFactory provideTorSocketFactory() {
|
||||
InetSocketAddress proxy = new InetSocketAddress("127.0.0.1",
|
||||
SOCKS_PORT);
|
||||
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT);
|
||||
return new SocksSocketFactory(proxy, CONNECT_TO_PROXY_TIMEOUT,
|
||||
EXTRA_SOCKET_TIMEOUT);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,11 +29,13 @@ class SocksSocket extends Socket {
|
||||
private static final byte[] UNSPECIFIED_ADDRESS = new byte[4];
|
||||
|
||||
private final SocketAddress proxy;
|
||||
private final int connectToProxyTimeout;
|
||||
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||
|
||||
SocksSocket(SocketAddress proxy, int connectToProxyTimeout) {
|
||||
SocksSocket(SocketAddress proxy, int connectToProxyTimeout,
|
||||
int extraSocketTimeout) {
|
||||
this.proxy = proxy;
|
||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||
this.extraSocketTimeout = extraSocketTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -47,7 +49,7 @@ class SocksSocket extends Socket {
|
||||
InetAddress address = inet.getAddress();
|
||||
if (address != null
|
||||
&& !Arrays.equals(address.getAddress(), UNSPECIFIED_ADDRESS)) {
|
||||
throw new IllegalArgumentException();
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
String host = inet.getHostName();
|
||||
if (host.length() > 255) throw new IllegalArgumentException();
|
||||
@@ -62,16 +64,16 @@ class SocksSocket extends Socket {
|
||||
sendMethodRequest(out);
|
||||
receiveMethodResponse(in);
|
||||
|
||||
// Use the supplied timeout temporarily
|
||||
// Use the supplied timeout temporarily, plus any configured extra
|
||||
int oldTimeout = getSoTimeout();
|
||||
setSoTimeout(timeout);
|
||||
setSoTimeout(timeout + extraSocketTimeout);
|
||||
|
||||
// Connect to the endpoint via the proxy
|
||||
sendConnectRequest(out, host, port);
|
||||
receiveConnectResponse(in);
|
||||
|
||||
// Restore the old timeout
|
||||
setSoTimeout(oldTimeout);
|
||||
// Restore the old timeout, plus any configured extra
|
||||
setSoTimeout(oldTimeout + extraSocketTimeout);
|
||||
}
|
||||
|
||||
private void sendMethodRequest(OutputStream out) throws IOException {
|
||||
|
||||
@@ -11,16 +11,18 @@ import javax.net.SocketFactory;
|
||||
class SocksSocketFactory extends SocketFactory {
|
||||
|
||||
private final SocketAddress proxy;
|
||||
private final int connectToProxyTimeout;
|
||||
private final int connectToProxyTimeout, extraSocketTimeout;
|
||||
|
||||
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout) {
|
||||
SocksSocketFactory(SocketAddress proxy, int connectToProxyTimeout,
|
||||
int extraSocketTimeout) {
|
||||
this.proxy = proxy;
|
||||
this.connectToProxyTimeout = connectToProxyTimeout;
|
||||
this.extraSocketTimeout = extraSocketTimeout;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Socket createSocket() {
|
||||
return new SocksSocket(proxy, connectToProxyTimeout);
|
||||
return new SocksSocket(proxy, connectToProxyTimeout, extraSocketTimeout);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
package org.briarproject.bramble.test;
|
||||
|
||||
import javax.net.SocketFactory;
|
||||
|
||||
import dagger.Module;
|
||||
import dagger.Provides;
|
||||
|
||||
@Module
|
||||
public class TestSocksModule {
|
||||
|
||||
@Provides
|
||||
SocketFactory provideSocketFactory() {
|
||||
return SocketFactory.getDefault();
|
||||
}
|
||||
|
||||
}
|
||||
@@ -82,6 +82,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 22
|
||||
versionCode 14
|
||||
versionName "0.14"
|
||||
resValue "string", "app_package", "org.briarproject.briar"
|
||||
buildConfigField "String", "GitHash", "\"${getGitHash()}\""
|
||||
}
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest
|
||||
package="org.briarproject.briar"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:versionCode="13"
|
||||
android:versionName="0.13">
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-feature android:name="android.hardware.bluetooth"/>
|
||||
<uses-feature android:name="android.hardware.camera" />
|
||||
|
||||
@@ -32,6 +32,7 @@ import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||
import org.briarproject.briar.api.blog.BlogManager;
|
||||
import org.briarproject.briar.api.blog.BlogPostFactory;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.feed.FeedManager;
|
||||
import org.briarproject.briar.api.forum.ForumManager;
|
||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||
@@ -78,6 +79,8 @@ public interface AndroidComponent
|
||||
@DatabaseExecutor
|
||||
Executor databaseExecutor();
|
||||
|
||||
MessageTracker messageTracker();
|
||||
|
||||
LifecycleManager lifecycleManager();
|
||||
|
||||
IdentityManager identityManager();
|
||||
|
||||
@@ -59,7 +59,6 @@ import static android.app.Notification.DEFAULT_SOUND;
|
||||
import static android.app.Notification.DEFAULT_VIBRATE;
|
||||
import static android.content.Context.NOTIFICATION_SERVICE;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_MESSAGE;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SOCIAL;
|
||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
@@ -310,7 +309,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
ContactId c = contactCounts.keySet().iterator().next();
|
||||
i.putExtra(CONTACT_ID, c.getInt());
|
||||
i.setData(Uri.parse(CONTACT_URI + "/" + c.getInt()));
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(ConversationActivity.class);
|
||||
t.addNextIntent(i);
|
||||
@@ -319,7 +318,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
// Touching the notification shows the contact list
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_CONTACTS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.setData(Uri.parse(CONTACT_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
@@ -415,7 +414,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
i.putExtra(GROUP_ID, g.getBytes());
|
||||
String idHex = StringUtils.toHexString(g.getBytes());
|
||||
i.setData(Uri.parse(GROUP_URI + "/" + idHex));
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(GroupActivity.class);
|
||||
t.addNextIntent(i);
|
||||
@@ -424,7 +423,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
// Touching the notification shows the group list
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_GROUPS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.setData(Uri.parse(GROUP_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
@@ -507,7 +506,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
i.putExtra(GROUP_ID, g.getBytes());
|
||||
String idHex = StringUtils.toHexString(g.getBytes());
|
||||
i.setData(Uri.parse(FORUM_URI + "/" + idHex));
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(ForumActivity.class);
|
||||
t.addNextIntent(i);
|
||||
@@ -516,7 +515,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
// Touching the notification shows the forum list
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_FORUMS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.setData(Uri.parse(FORUM_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
@@ -595,7 +594,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
// Touching the notification shows the combined blog feed
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_BLOGS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.setData(Uri.parse(BLOG_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
@@ -651,7 +650,7 @@ class AndroidNotificationManagerImpl implements AndroidNotificationManager,
|
||||
// Touching the notification shows the contact list
|
||||
Intent i = new Intent(appContext, NavDrawerActivity.class);
|
||||
i.putExtra(INTENT_CONTACTS, true);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i.setData(Uri.parse(CONTACT_URI));
|
||||
TaskStackBuilder t = TaskStackBuilder.create(appContext);
|
||||
t.addParentStack(NavDrawerActivity.class);
|
||||
|
||||
@@ -28,7 +28,6 @@ import javax.inject.Inject;
|
||||
import static android.app.PendingIntent.FLAG_UPDATE_CURRENT;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
||||
import static android.support.v4.app.NotificationCompat.CATEGORY_SERVICE;
|
||||
import static android.support.v4.app.NotificationCompat.PRIORITY_MIN;
|
||||
import static android.support.v4.app.NotificationCompat.VISIBILITY_SECRET;
|
||||
@@ -83,8 +82,7 @@ public class BriarService extends Service {
|
||||
b.setWhen(0); // Don't show the time
|
||||
b.setOngoing(true);
|
||||
Intent i = new Intent(this, NavDrawerActivity.class);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP |
|
||||
FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i.setFlags(FLAG_ACTIVITY_NEW_TASK | FLAG_ACTIVITY_CLEAR_TOP);
|
||||
b.setContentIntent(PendingIntent.getActivity(this, 0, i, 0));
|
||||
if (Build.VERSION.SDK_INT >= 21) {
|
||||
b.setCategory(CATEGORY_SERVICE);
|
||||
|
||||
@@ -6,11 +6,10 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.IntentFilter;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.ApplicationInfo;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.content.pm.PackageManager.NameNotFoundException;
|
||||
import android.content.pm.Signature;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
|
||||
@@ -19,11 +18,16 @@ import org.briarproject.bramble.api.lifecycle.ServiceException;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.bramble.api.system.AndroidExecutor;
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.api.android.ScreenFilterMonitor;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStream;
|
||||
import java.security.cert.CertificateException;
|
||||
import java.security.cert.CertificateFactory;
|
||||
import java.security.cert.X509Certificate;
|
||||
import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
@@ -31,32 +35,58 @@ import java.util.concurrent.Callable;
|
||||
import java.util.concurrent.ExecutionException;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.Manifest.permission.SYSTEM_ALERT_WINDOW;
|
||||
import static android.content.Intent.ACTION_PACKAGE_ADDED;
|
||||
import static android.content.Intent.EXTRA_REPLACING;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_SYSTEM;
|
||||
import static android.content.pm.ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||
import static android.content.pm.PackageManager.GET_PERMISSIONS;
|
||||
import static android.content.pm.PackageManager.GET_SIGNATURES;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
implements Service,
|
||||
ScreenFilterMonitor {
|
||||
implements Service, ScreenFilterMonitor {
|
||||
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(ScreenFilterMonitorImpl.class.getName());
|
||||
private static final String PREF_SCREEN_FILTER_APPS =
|
||||
"shownScreenFilterApps";
|
||||
|
||||
/*
|
||||
* Ignore Play Services if it uses this package name and public key - it's
|
||||
* effectively a system app, but not flagged as such on older systems
|
||||
*/
|
||||
private static final String PLAY_SERVICES_PACKAGE =
|
||||
"com.google.android.gms";
|
||||
private static final String PLAY_SERVICES_PUBLIC_KEY =
|
||||
"30820120300D06092A864886F70D01010105000382010D0030820108" +
|
||||
"0282010100AB562E00D83BA208AE0A966F124E29DA11F2AB56D08F58" +
|
||||
"E2CCA91303E9B754D372F640A71B1DCB130967624E4656A7776A9219" +
|
||||
"3DB2E5BFB724A91E77188B0E6A47A43B33D9609B77183145CCDF7B2E" +
|
||||
"586674C9E1565B1F4C6A5955BFF251A63DABF9C55C27222252E875E4" +
|
||||
"F8154A645F897168C0B1BFC612EABF785769BB34AA7984DC7E2EA276" +
|
||||
"4CAE8307D8C17154D7EE5F64A51A44A602C249054157DC02CD5F5C0E" +
|
||||
"55FBEF8519FBE327F0B1511692C5A06F19D18385F5C4DBC2D6B93F68" +
|
||||
"CC2979C70E18AB93866B3BD5DB8999552A0E3B4C99DF58FB918BEDC1" +
|
||||
"82BA35E003C1B4B10DD244A8EE24FFFD333872AB5221985EDAB0FC0D" +
|
||||
"0B145B6AA192858E79020103";
|
||||
|
||||
private final Context appContext;
|
||||
private final AndroidExecutor androidExecutor;
|
||||
private final LinkedList<String> appNames = new LinkedList<>();
|
||||
private final PackageManager pm;
|
||||
private final SharedPreferences prefs;
|
||||
private final AtomicBoolean used = new AtomicBoolean(false);
|
||||
|
||||
// The following must only be accessed on the UI thread
|
||||
private final Set<String> apps = new HashSet<>();
|
||||
private final Set<String> shownApps;
|
||||
// Used solely for the UiThread
|
||||
private boolean serviceStarted = false;
|
||||
|
||||
@Inject
|
||||
@@ -75,7 +105,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
@Override
|
||||
public Void call() {
|
||||
IntentFilter intentFilter = new IntentFilter();
|
||||
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
|
||||
intentFilter.addAction(ACTION_PACKAGE_ADDED);
|
||||
intentFilter.addDataScheme("package");
|
||||
appContext.registerReceiver(ScreenFilterMonitorImpl.this,
|
||||
intentFilter);
|
||||
@@ -109,9 +139,8 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
}
|
||||
|
||||
private Set<String> getShownScreenFilterApps() {
|
||||
// res must not be modified
|
||||
Set<String> s =
|
||||
prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
|
||||
// Result must not be modified
|
||||
Set<String> s = prefs.getStringSet(PREF_SCREEN_FILTER_APPS, null);
|
||||
HashSet<String> result = new HashSet<>();
|
||||
if (s != null) {
|
||||
result.addAll(s);
|
||||
@@ -121,7 +150,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (!intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) {
|
||||
if (!intent.getBooleanExtra(EXTRA_REPLACING, false)) {
|
||||
final String packageName =
|
||||
intent.getData().getEncodedSchemeSpecificPart();
|
||||
androidExecutor.runOnUiThread(new Runnable() {
|
||||
@@ -155,7 +184,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
@Override
|
||||
@UiThread
|
||||
public void storeAppsAsShown(Collection<String> s, boolean persistent) {
|
||||
HashSet<String> buf = new HashSet(s);
|
||||
HashSet<String> buf = new HashSet<>(s);
|
||||
shownApps.addAll(buf);
|
||||
if (persistent && !s.isEmpty()) {
|
||||
buf.addAll(getShownScreenFilterApps());
|
||||
@@ -168,7 +197,7 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
private Set<String> getInstalledScreenFilterApps() {
|
||||
HashSet<String> screenFilterApps = new HashSet<>();
|
||||
List<PackageInfo> packageInfos =
|
||||
pm.getInstalledPackages(PackageManager.GET_PERMISSIONS);
|
||||
pm.getInstalledPackages(GET_PERMISSIONS);
|
||||
for (PackageInfo packageInfo : packageInfos) {
|
||||
if (isOverlayApp(packageInfo)) {
|
||||
String name = pkgToString(packageInfo);
|
||||
@@ -180,25 +209,22 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
return screenFilterApps;
|
||||
}
|
||||
|
||||
// Checks if pkg uses the SYSTEM_ALERT_WINDOW permission and if so
|
||||
// Checks if a package uses the SYSTEM_ALERT_WINDOW permission and if so
|
||||
// returns the app name.
|
||||
@Nullable
|
||||
private String isOverlayApp(String pkg) {
|
||||
try {
|
||||
PackageInfo pkgInfo =
|
||||
pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
|
||||
PackageInfo pkgInfo = pm.getPackageInfo(pkg, GET_PERMISSIONS);
|
||||
if (isOverlayApp(pkgInfo)) {
|
||||
return pkgToString(pkgInfo);
|
||||
}
|
||||
} catch (PackageManager.NameNotFoundException ignored) {
|
||||
if (LOG.isLoggable(Level.WARNING)) {
|
||||
LOG.warning("Package name not found: " + pkg);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// Fetch the application name for a given package.
|
||||
// Fetches the application name for a given package.
|
||||
@Nullable
|
||||
private String pkgToString(PackageInfo pkgInfo) {
|
||||
CharSequence seq = pm.getApplicationLabel(pkgInfo.applicationInfo);
|
||||
@@ -208,25 +234,49 @@ public class ScreenFilterMonitorImpl extends BroadcastReceiver
|
||||
return null;
|
||||
}
|
||||
|
||||
// Checks if an installed pkg is a user app using the permission.
|
||||
// Checks if an installed package is a user app using the permission.
|
||||
private boolean isOverlayApp(PackageInfo packageInfo) {
|
||||
int mask = ApplicationInfo.FLAG_SYSTEM |
|
||||
ApplicationInfo.FLAG_UPDATED_SYSTEM_APP;
|
||||
int mask = FLAG_SYSTEM | FLAG_UPDATED_SYSTEM_APP;
|
||||
// Ignore system apps
|
||||
if ((packageInfo.applicationInfo.flags & mask) != 0) {
|
||||
return false;
|
||||
}
|
||||
//Get Permissions
|
||||
String[] requestedPermissions =
|
||||
packageInfo.requestedPermissions;
|
||||
// Ignore Play Services, it's effectively a system app
|
||||
if (isPlayServices(packageInfo.packageName)) {
|
||||
return false;
|
||||
}
|
||||
// Get permissions
|
||||
String[] requestedPermissions = packageInfo.requestedPermissions;
|
||||
if (requestedPermissions != null) {
|
||||
for (String requestedPermission : requestedPermissions) {
|
||||
if (requestedPermission
|
||||
.equals(SYSTEM_ALERT_WINDOW)) {
|
||||
if (requestedPermission.equals(SYSTEM_ALERT_WINDOW)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isPlayServices(String pkg) {
|
||||
if (!PLAY_SERVICES_PACKAGE.equals(pkg)) return false;
|
||||
try {
|
||||
PackageInfo sigs = pm.getPackageInfo(pkg, GET_SIGNATURES);
|
||||
// The genuine Play Services app should have a single signature
|
||||
Signature[] signatures = sigs.signatures;
|
||||
if (signatures == null || signatures.length != 1) return false;
|
||||
// Extract the public key from the signature
|
||||
CertificateFactory certFactory =
|
||||
CertificateFactory.getInstance("X509");
|
||||
byte[] signatureBytes = signatures[0].toByteArray();
|
||||
InputStream in = new ByteArrayInputStream(signatureBytes);
|
||||
X509Certificate cert =
|
||||
(X509Certificate) certFactory.generateCertificate(in);
|
||||
byte[] publicKeyBytes = cert.getPublicKey().getEncoded();
|
||||
String publicKey = StringUtils.toHexString(publicKeyBytes);
|
||||
return PLAY_SERVICES_PUBLIC_KEY.equals(publicKey);
|
||||
} catch (NameNotFoundException | CertificateException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.activity;
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Intent;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.app.ActionBar;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.transition.Slide;
|
||||
@@ -21,6 +20,7 @@ import org.briarproject.briar.android.panic.ExitActivity;
|
||||
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TASK;
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.blog;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.view.View;
|
||||
|
||||
@@ -15,6 +14,7 @@ import org.briarproject.briar.android.activity.BriarActivity;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment.BaseFragmentListener;
|
||||
import org.briarproject.briar.android.sharing.BlogSharingStatusActivity;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
|
||||
@@ -169,7 +169,7 @@ class BlogControllerImpl extends BaseControllerImpl
|
||||
LocalAuthor a = identityManager.getLocalAuthor();
|
||||
Blog b = blogManager.getBlog(groupId);
|
||||
boolean ours = a.getId().equals(b.getAuthor().getId());
|
||||
boolean removable = blogManager.canBeRemoved(groupId);
|
||||
boolean removable = blogManager.canBeRemoved(b);
|
||||
BlogItem blog = new BlogItem(b, ours, removable);
|
||||
long duration = System.currentTimeMillis() - now;
|
||||
if (LOG.isLoggable(INFO))
|
||||
|
||||
@@ -43,7 +43,6 @@ import javax.inject.Inject;
|
||||
|
||||
import static android.app.Activity.RESULT_OK;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_BLOG;
|
||||
@@ -149,14 +148,14 @@ public class BlogFragment extends BaseFragment
|
||||
return true;
|
||||
case R.id.action_blog_share:
|
||||
Intent i2 = new Intent(getActivity(), ShareBlogActivity.class);
|
||||
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i2.putExtra(GROUP_ID, groupId.getBytes());
|
||||
startActivityForResult(i2, REQUEST_SHARE_BLOG);
|
||||
return true;
|
||||
case R.id.action_blog_sharing_status:
|
||||
Intent i3 = new Intent(getActivity(),
|
||||
BlogSharingStatusActivity.class);
|
||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i3.putExtra(GROUP_ID, groupId.getBytes());
|
||||
startActivity(i3);
|
||||
return true;
|
||||
|
||||
@@ -48,6 +48,10 @@ public class BlogPostItem implements Comparable<BlogPostItem> {
|
||||
return body;
|
||||
}
|
||||
|
||||
public boolean isRssFeed() {
|
||||
return header.isRssFeed();
|
||||
}
|
||||
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
@@ -108,7 +108,8 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
author.setAuthor(a);
|
||||
author.setAuthorStatus(post.getAuthorStatus());
|
||||
author.setDate(post.getTimestamp());
|
||||
author.setPersona(AuthorView.NORMAL);
|
||||
author.setPersona(
|
||||
item.isRssFeed() ? AuthorView.RSS_FEED : AuthorView.NORMAL);
|
||||
// TODO make author clickable more often #624
|
||||
if (item.getHeader().getType() == POST) {
|
||||
author.setBlogLink(post.getGroupId());
|
||||
@@ -168,7 +169,9 @@ class BlogPostViewHolder extends RecyclerView.ViewHolder {
|
||||
reblogger.setVisibility(VISIBLE);
|
||||
reblogger.setPersona(AuthorView.REBLOGGER);
|
||||
|
||||
author.setPersona(AuthorView.COMMENTER);
|
||||
author.setPersona(item.getHeader().getRootPost().isRssFeed() ?
|
||||
AuthorView.RSS_FEED_REBLOGGED :
|
||||
AuthorView.COMMENTER);
|
||||
|
||||
// comments
|
||||
for (BlogCommentHeader c : item.getComments()) {
|
||||
|
||||
@@ -179,7 +179,6 @@ public class FeedFragment extends BaseFragment implements
|
||||
case R.id.action_rss_feeds_import:
|
||||
Intent i2 =
|
||||
new Intent(getActivity(), RssFeedImportActivity.class);
|
||||
i2.putExtra(GROUP_ID, personalBlog.getId().getBytes());
|
||||
startActivity(i2);
|
||||
return true;
|
||||
case R.id.action_rss_feeds_manage:
|
||||
|
||||
@@ -6,7 +6,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.View.OnClickListener;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.ImageButton;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.briar.R;
|
||||
@@ -39,12 +39,7 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
if (item == null) return;
|
||||
|
||||
// Feed Title
|
||||
if (item.getTitle() != null) {
|
||||
ui.title.setText(item.getTitle());
|
||||
ui.title.setVisibility(VISIBLE);
|
||||
} else {
|
||||
ui.title.setVisibility(GONE);
|
||||
}
|
||||
ui.title.setText(item.getTitle());
|
||||
|
||||
// Delete Button
|
||||
ui.delete.setOnClickListener(new OnClickListener() {
|
||||
@@ -75,6 +70,14 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
} else {
|
||||
ui.description.setVisibility(GONE);
|
||||
}
|
||||
|
||||
// Open feed's blog when clicked
|
||||
ui.layout.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
listener.onFeedClick(item);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -99,8 +102,9 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
}
|
||||
|
||||
static class FeedViewHolder extends RecyclerView.ViewHolder {
|
||||
private final View layout;
|
||||
private final TextView title;
|
||||
private final ImageView delete;
|
||||
private final ImageButton delete;
|
||||
private final TextView imported;
|
||||
private final TextView updated;
|
||||
private final TextView author;
|
||||
@@ -110,8 +114,9 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
private FeedViewHolder(View v) {
|
||||
super(v);
|
||||
|
||||
layout = v;
|
||||
title = (TextView) v.findViewById(R.id.titleView);
|
||||
delete = (ImageView) v.findViewById(R.id.deleteButton);
|
||||
delete = (ImageButton) v.findViewById(R.id.deleteButton);
|
||||
imported = (TextView) v.findViewById(R.id.importedView);
|
||||
updated = (TextView) v.findViewById(R.id.updatedView);
|
||||
author = (TextView) v.findViewById(R.id.authorView);
|
||||
@@ -121,6 +126,7 @@ class RssFeedAdapter extends BriarAdapter<Feed, RssFeedAdapter.FeedViewHolder> {
|
||||
}
|
||||
|
||||
interface RssFeedListener {
|
||||
void onFeedClick(Feed feed);
|
||||
void onDeleteClick(Feed feed);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.blog;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.Editable;
|
||||
@@ -15,7 +14,6 @@ import android.widget.ProgressBar;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.lifecycle.IoExecutor;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
@@ -44,9 +42,6 @@ public class RssFeedImportActivity extends BriarActivity {
|
||||
@IoExecutor
|
||||
Executor ioExecutor;
|
||||
|
||||
// Fields that are accessed from background threads must be volatile
|
||||
private volatile GroupId groupId = null;
|
||||
|
||||
@Inject
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
volatile FeedManager feedManager;
|
||||
@@ -55,12 +50,6 @@ public class RssFeedImportActivity extends BriarActivity {
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// GroupId from Intent
|
||||
Intent i = getIntent();
|
||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
||||
if (b == null) throw new IllegalStateException("No Group in intent.");
|
||||
groupId = new GroupId(b);
|
||||
|
||||
setContentView(R.layout.activity_rss_feed_import);
|
||||
|
||||
urlInput = (EditText) findViewById(R.id.urlInput);
|
||||
@@ -128,7 +117,7 @@ public class RssFeedImportActivity extends BriarActivity {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
feedManager.addFeed(url, groupId);
|
||||
feedManager.addFeed(url);
|
||||
feedImported();
|
||||
} catch (DbException | IOException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
|
||||
@@ -1,15 +1,16 @@
|
||||
package org.briarproject.briar.android.blog;
|
||||
|
||||
import android.content.DialogInterface;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.design.widget.Snackbar;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.activity.BriarActivity;
|
||||
@@ -23,6 +24,7 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.support.design.widget.Snackbar.LENGTH_LONG;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
@@ -34,7 +36,6 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
|
||||
private BriarRecyclerView list;
|
||||
private RssFeedAdapter adapter;
|
||||
private GroupId groupId;
|
||||
|
||||
@Inject
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
@@ -44,12 +45,6 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
public void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
// GroupId from Intent
|
||||
Intent i = getIntent();
|
||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
||||
if (b == null) throw new IllegalStateException("No Group in intent.");
|
||||
groupId = new GroupId(b);
|
||||
|
||||
setContentView(R.layout.activity_rss_feed_manage);
|
||||
|
||||
adapter = new RssFeedAdapter(this, this);
|
||||
@@ -87,7 +82,6 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
return true;
|
||||
case R.id.action_rss_feeds_import:
|
||||
Intent i = new Intent(this, RssFeedImportActivity.class);
|
||||
i.putExtra(GROUP_ID, groupId.getBytes());
|
||||
startActivity(i);
|
||||
return true;
|
||||
default:
|
||||
@@ -100,21 +94,32 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFeedClick(Feed feed) {
|
||||
Intent i = new Intent(this, BlogActivity.class);
|
||||
i.putExtra(GROUP_ID, feed.getBlogId().getBytes());
|
||||
i.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
startActivity(i);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDeleteClick(final Feed feed) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
feedManager.removeFeed(feed.getUrl());
|
||||
onFeedDeleted(feed);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
onDeleteError();
|
||||
}
|
||||
}
|
||||
});
|
||||
DialogInterface.OnClickListener okListener =
|
||||
new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
deleteFeed(feed);
|
||||
}
|
||||
};
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this,
|
||||
R.style.BriarDialogTheme);
|
||||
builder.setTitle(getString(R.string.blogs_rss_remove_feed));
|
||||
builder.setMessage(
|
||||
getString(R.string.blogs_rss_remove_feed_dialog_message));
|
||||
builder.setPositiveButton(R.string.cancel, null);
|
||||
builder.setNegativeButton(R.string.blogs_rss_remove_feed_ok,
|
||||
okListener);
|
||||
builder.show();
|
||||
}
|
||||
|
||||
private void loadFeeds() {
|
||||
@@ -149,6 +154,22 @@ public class RssFeedManageActivity extends BriarActivity
|
||||
});
|
||||
}
|
||||
|
||||
private void deleteFeed(final Feed feed) {
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
feedManager.removeFeed(feed);
|
||||
onFeedDeleted(feed);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
onDeleteError();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onLoadError() {
|
||||
runOnUiThreadUnlessDestroyed(new Runnable() {
|
||||
@Override
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.briar.android.contact;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
import android.view.View;
|
||||
@@ -13,6 +12,8 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.contact.BaseContactListAdapter.OnContactClickListener;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import im.delight.android.identicons.IdenticonDrawable;
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -946,6 +946,7 @@ public class ConversationActivity extends BriarActivity
|
||||
@Override
|
||||
public void respondToRequest(final ConversationRequestItem item,
|
||||
final boolean accept) {
|
||||
item.setAnswered(true);
|
||||
int position = adapter.findItemPosition(item);
|
||||
if (position != INVALID_POSITION) {
|
||||
adapter.notifyItemChanged(position, item);
|
||||
|
||||
@@ -21,7 +21,8 @@ class ConversationRequestItem extends ConversationNoticeInItem {
|
||||
private final GroupId requestedGroupId;
|
||||
private final RequestType requestType;
|
||||
private final SessionId sessionId;
|
||||
private final boolean answered, canBeOpened;
|
||||
private final boolean canBeOpened;
|
||||
private boolean answered;
|
||||
|
||||
ConversationRequestItem(MessageId id, GroupId groupId,
|
||||
RequestType requestType, SessionId sessionId, String text,
|
||||
@@ -53,6 +54,10 @@ class ConversationRequestItem extends ConversationNoticeInItem {
|
||||
return answered;
|
||||
}
|
||||
|
||||
void setAnswered(boolean answered) {
|
||||
this.answered = answered;
|
||||
}
|
||||
|
||||
public boolean canBeOpened() {
|
||||
return canBeOpened;
|
||||
}
|
||||
|
||||
@@ -51,6 +51,8 @@ class ConversationRequestViewHolder extends ConversationNoticeInViewHolder {
|
||||
acceptButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
acceptButton.setEnabled(false);
|
||||
declineButton.setEnabled(false);
|
||||
listener.respondToRequest(item, true);
|
||||
}
|
||||
});
|
||||
@@ -58,6 +60,8 @@ class ConversationRequestViewHolder extends ConversationNoticeInViewHolder {
|
||||
declineButton.setOnClickListener(new OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
acceptButton.setEnabled(false);
|
||||
declineButton.setEnabled(false);
|
||||
listener.respondToRequest(item, false);
|
||||
}
|
||||
});
|
||||
|
||||
@@ -17,7 +17,7 @@ public class DbControllerImpl implements DbController {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(DbControllerImpl.class.getName());
|
||||
|
||||
private final Executor dbExecutor;
|
||||
protected final Executor dbExecutor;
|
||||
private final LifecycleManager lifecycleManager;
|
||||
|
||||
@Inject
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.briarproject.briar.android.controller;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.event.Event;
|
||||
import org.briarproject.bramble.api.event.EventBus;
|
||||
@@ -15,6 +13,7 @@ import java.util.Collection;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
@NotNullByDefault
|
||||
|
||||
@@ -34,7 +34,6 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_SINGLE_TOP;
|
||||
import static android.widget.Toast.LENGTH_SHORT;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_SHARE_FORUM;
|
||||
import static org.briarproject.briar.api.forum.ForumConstants.MAX_FORUM_POST_BODY_LENGTH;
|
||||
@@ -117,18 +116,15 @@ public class ForumActivity extends
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
// Handle presses on the action bar items
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_forum_compose_post:
|
||||
showTextInput(null);
|
||||
return true;
|
||||
case R.id.action_forum_share:
|
||||
Intent i2 = new Intent(this, ShareForumActivity.class);
|
||||
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i2.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i2.putExtra(GROUP_ID, groupId.getBytes());
|
||||
startActivityForResult(i2, REQUEST_SHARE_FORUM);
|
||||
return true;
|
||||
case R.id.action_forum_sharing_status:
|
||||
Intent i3 = new Intent(this, ForumSharingStatusActivity.class);
|
||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP | FLAG_ACTIVITY_SINGLE_TOP);
|
||||
i3.setFlags(FLAG_ACTIVITY_CLEAR_TOP);
|
||||
i3.putExtra(GROUP_ID, groupId.getBytes());
|
||||
startActivity(i3);
|
||||
return true;
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.briar.android.forum.ForumController.ForumListener;
|
||||
import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||
import org.briarproject.briar.api.forum.Forum;
|
||||
import org.briarproject.briar.api.forum.ForumInvitationResponse;
|
||||
@@ -55,10 +56,10 @@ class ForumControllerImpl extends
|
||||
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
||||
@CryptoExecutor Executor cryptoExecutor,
|
||||
ForumManager forumManager, ForumSharingManager forumSharingManager,
|
||||
EventBus eventBus, Clock clock,
|
||||
EventBus eventBus, Clock clock, MessageTracker messageTracker,
|
||||
AndroidNotificationManager notificationManager) {
|
||||
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
|
||||
eventBus, clock, notificationManager);
|
||||
eventBus, clock, notificationManager, messageTracker);
|
||||
this.forumManager = forumManager;
|
||||
this.forumSharingManager = forumSharingManager;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.fragment;
|
||||
import android.app.Dialog;
|
||||
import android.content.DialogInterface;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.v4.app.DialogFragment;
|
||||
import android.support.v7.app.AlertDialog;
|
||||
import android.text.TextUtils;
|
||||
@@ -19,6 +18,8 @@ import org.briarproject.briar.android.activity.BaseActivity;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class SFDialogFragment extends DialogFragment {
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.fragment;
|
||||
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
@@ -9,6 +8,8 @@ import android.view.ViewGroup;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class SignOutFragment extends BaseFragment {
|
||||
|
||||
private static final String TAG = SignOutFragment.class.getName();
|
||||
|
||||
@@ -37,6 +37,7 @@ import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static org.briarproject.briar.android.activity.RequestCodes.REQUEST_GROUP_INVITE;
|
||||
import static org.briarproject.briar.api.privategroup.PrivateGroupConstants.MAX_GROUP_POST_BODY_LENGTH;
|
||||
|
||||
@@ -50,8 +51,8 @@ public class GroupActivity extends
|
||||
GroupController controller;
|
||||
|
||||
private boolean isCreator, isDissolved = false;
|
||||
private MenuItem writeMenuItem, revealMenuItem, inviteMenuItem,
|
||||
leaveMenuItem, dissolveMenuItem;
|
||||
private MenuItem revealMenuItem, inviteMenuItem, leaveMenuItem,
|
||||
dissolveMenuItem;
|
||||
|
||||
@Override
|
||||
public void injectActivity(ActivityComponent component) {
|
||||
@@ -139,7 +140,6 @@ public class GroupActivity extends
|
||||
MenuInflater inflater = getMenuInflater();
|
||||
inflater.inflate(R.menu.group_actions, menu);
|
||||
|
||||
writeMenuItem = menu.findItem(R.id.action_group_compose_message);
|
||||
revealMenuItem = menu.findItem(R.id.action_group_reveal);
|
||||
inviteMenuItem = menu.findItem(R.id.action_group_invite);
|
||||
leaveMenuItem = menu.findItem(R.id.action_group_leave);
|
||||
@@ -152,9 +152,6 @@ public class GroupActivity extends
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(final MenuItem item) {
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_group_compose_message:
|
||||
showTextInput(null);
|
||||
return true;
|
||||
case R.id.action_group_member_list:
|
||||
Intent i1 = new Intent(this, GroupMemberListActivity.class);
|
||||
i1.putExtra(GROUP_ID, groupId.getBytes());
|
||||
@@ -205,7 +202,6 @@ public class GroupActivity extends
|
||||
|
||||
private void setGroupEnabled(boolean enabled) {
|
||||
isDissolved = !enabled;
|
||||
if (writeMenuItem != null) writeMenuItem.setVisible(enabled);
|
||||
textInput.setSendButtonEnabled(enabled);
|
||||
list.getRecyclerView().setAlpha(enabled ? 1f : 0.5f);
|
||||
|
||||
@@ -213,6 +209,8 @@ public class GroupActivity extends
|
||||
textInput.setVisibility(GONE);
|
||||
if (textInput.isKeyboardOpen()) textInput.hideSoftKeyboard();
|
||||
if (textInput.isEmojiDrawerOpen()) textInput.hideEmojiDrawer();
|
||||
} else {
|
||||
textInput.setVisibility(VISIBLE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -229,7 +227,6 @@ public class GroupActivity extends
|
||||
leaveMenuItem.setVisible(true);
|
||||
dissolveMenuItem.setVisible(false);
|
||||
}
|
||||
writeMenuItem.setVisible(!isDissolved);
|
||||
}
|
||||
|
||||
private void showLeaveGroupDialog() {
|
||||
|
||||
@@ -17,6 +17,7 @@ import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.briar.android.privategroup.conversation.GroupController.GroupListener;
|
||||
import org.briarproject.briar.android.threaded.ThreadListControllerImpl;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.MessageTracker.GroupCount;
|
||||
import org.briarproject.briar.api.privategroup.GroupMember;
|
||||
import org.briarproject.briar.api.privategroup.GroupMessage;
|
||||
@@ -60,9 +61,10 @@ class GroupControllerImpl extends
|
||||
@CryptoExecutor Executor cryptoExecutor,
|
||||
PrivateGroupManager privateGroupManager,
|
||||
GroupMessageFactory groupMessageFactory, EventBus eventBus,
|
||||
Clock clock, AndroidNotificationManager notificationManager) {
|
||||
MessageTracker messageTracker, Clock clock,
|
||||
AndroidNotificationManager notificationManager) {
|
||||
super(dbExecutor, lifecycleManager, identityManager, cryptoExecutor,
|
||||
eventBus, clock, notificationManager);
|
||||
eventBus, clock, notificationManager, messageTracker);
|
||||
this.privateGroupManager = privateGroupManager;
|
||||
this.groupMessageFactory = groupMessageFactory;
|
||||
}
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.privategroup.conversation;
|
||||
|
||||
import android.support.annotation.LayoutRes;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
@@ -12,6 +11,7 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.threaded.ThreadItem;
|
||||
import org.briarproject.briar.api.privategroup.GroupMessageHeader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@UiThread
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.privategroup.creation;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
@@ -14,6 +13,8 @@ import org.briarproject.briar.android.controller.handler.UiResultExceptionHandle
|
||||
import org.briarproject.briar.android.privategroup.conversation.GroupActivity;
|
||||
import org.briarproject.briar.android.sharing.BaseMessageFragment.MessageFragmentListener;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
public class CreateGroupActivity extends BaseGroupInviteActivity implements
|
||||
|
||||
@@ -10,6 +10,7 @@ import android.view.ViewGroup;
|
||||
import android.widget.Button;
|
||||
import android.widget.EditText;
|
||||
|
||||
import org.briarproject.bramble.util.StringUtils;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.android.fragment.BaseFragment;
|
||||
@@ -84,9 +85,9 @@ public class CreateGroupFragment extends BaseFragment {
|
||||
|
||||
private void validateName() {
|
||||
String name = this.name.getText().toString();
|
||||
if (name.length() < 1 || name.length() > MAX_GROUP_NAME_LENGTH)
|
||||
if (name.length() < 1 || StringUtils.utf8IsTooLong(name, MAX_GROUP_NAME_LENGTH))
|
||||
button.setEnabled(false);
|
||||
else if(!button.isEnabled())
|
||||
else if (!button.isEnabled())
|
||||
button.setEnabled(true);
|
||||
}
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
@@ -40,7 +41,7 @@ public class GroupMemberListActivity extends BriarActivity {
|
||||
public void onCreate(@Nullable final Bundle state) {
|
||||
super.onCreate(state);
|
||||
|
||||
setContentView(R.layout.list);
|
||||
setContentView(R.layout.activity_sharing_status);
|
||||
|
||||
Intent i = getIntent();
|
||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
||||
@@ -52,6 +53,9 @@ public class GroupMemberListActivity extends BriarActivity {
|
||||
list.setLayoutManager(linearLayoutManager);
|
||||
adapter = new MemberListAdapter(this);
|
||||
list.setAdapter(adapter);
|
||||
|
||||
TextView info = (TextView) findViewById(R.id.info);
|
||||
info.setText(R.string.sharing_status_groups);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package org.briarproject.briar.android.privategroup.memberlist;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
|
||||
import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.identity.Author;
|
||||
import org.briarproject.bramble.api.identity.Author.Status;
|
||||
@@ -9,6 +7,7 @@ import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.api.privategroup.GroupMember;
|
||||
import org.briarproject.briar.api.privategroup.Visibility;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.NotThreadSafe;
|
||||
|
||||
@NotThreadSafe
|
||||
|
||||
@@ -146,7 +146,7 @@ public class BriarReportPrimer implements ReportPrimer {
|
||||
NetworkInfo wifi = cm.getNetworkInfo(TYPE_WIFI);
|
||||
boolean wifiAvailable = wifi != null && wifi.isAvailable();
|
||||
// Is wifi enabled?
|
||||
o = ctx.getSystemService(WIFI_SERVICE);
|
||||
o = ctx.getApplicationContext().getSystemService(WIFI_SERVICE);
|
||||
WifiManager wm = (WifiManager) o;
|
||||
boolean wifiEnabled = wm != null &&
|
||||
wm.getWifiState() == WIFI_STATE_ENABLED;
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.api.blog.BlogSharingManager;
|
||||
|
||||
@@ -25,6 +26,11 @@ public class BlogSharingStatusActivity extends SharingStatusActivity {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
int getInfoText() {
|
||||
return R.string.sharing_status_blog;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DatabaseExecutor
|
||||
protected Collection<Contact> getSharedWith() throws DbException {
|
||||
|
||||
@@ -5,6 +5,7 @@ import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.MethodsNotNullByDefault;
|
||||
import org.briarproject.bramble.api.nullsafety.ParametersNotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.activity.ActivityComponent;
|
||||
import org.briarproject.briar.api.forum.ForumSharingManager;
|
||||
|
||||
@@ -25,6 +26,11 @@ public class ForumSharingStatusActivity extends SharingStatusActivity {
|
||||
component.inject(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
int getInfoText() {
|
||||
return R.string.sharing_status_forum;
|
||||
}
|
||||
|
||||
@Override
|
||||
@DatabaseExecutor
|
||||
protected Collection<Contact> getSharedWith() throws DbException {
|
||||
|
||||
@@ -2,9 +2,10 @@ package org.briarproject.briar.android.sharing;
|
||||
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.view.MenuItem;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.briarproject.bramble.api.contact.Contact;
|
||||
import org.briarproject.bramble.api.db.DatabaseExecutor;
|
||||
@@ -23,6 +24,7 @@ import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.WARNING;
|
||||
@@ -45,7 +47,7 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
public void onCreate(@Nullable Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
setContentView(R.layout.list);
|
||||
setContentView(R.layout.activity_sharing_status);
|
||||
|
||||
Intent i = getIntent();
|
||||
byte[] b = i.getByteArrayExtra(GROUP_ID);
|
||||
@@ -57,6 +59,9 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
list.setLayoutManager(new LinearLayoutManager(this));
|
||||
list.setAdapter(adapter);
|
||||
list.setEmptyText(getString(R.string.nobody));
|
||||
|
||||
TextView info = (TextView) findViewById(R.id.info);
|
||||
info.setText(getInfoText());
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -84,6 +89,9 @@ abstract class SharingStatusActivity extends BriarActivity {
|
||||
}
|
||||
}
|
||||
|
||||
@StringRes
|
||||
abstract int getInfoText();
|
||||
|
||||
@DatabaseExecutor
|
||||
abstract protected Collection<Contact> getSharedWith() throws DbException;
|
||||
|
||||
|
||||
@@ -30,8 +30,8 @@ public class SplashScreenActivity extends BaseActivity {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(SplashScreenActivity.class.getName());
|
||||
|
||||
// This build expires on 1 May 2017
|
||||
private static final long EXPIRY_DATE = 1493593200 * 1000L;
|
||||
// This build expires on 1 July 2017
|
||||
private static final long EXPIRY_DATE = 1498863600 * 1000L;
|
||||
|
||||
@Inject
|
||||
protected ConfigController configController;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
package org.briarproject.briar.android.threaded;
|
||||
|
||||
import android.support.annotation.Nullable;
|
||||
import android.os.Handler;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.v7.widget.LinearLayoutManager;
|
||||
import android.support.v7.widget.RecyclerView;
|
||||
@@ -14,6 +14,8 @@ import org.briarproject.briar.android.util.VersionedAdapter;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||
|
||||
@UiThread
|
||||
@@ -26,6 +28,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
protected final NestedTreeList<I> items = new NestedTreeList<>();
|
||||
private final ThreadItemListener<I> listener;
|
||||
private final LinearLayoutManager layoutManager;
|
||||
private final Handler handler = new Handler();
|
||||
|
||||
private volatile int revision = 0;
|
||||
|
||||
@@ -64,6 +67,17 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
revision++;
|
||||
}
|
||||
|
||||
void setItemWithIdVisible(MessageId messageId) {
|
||||
int pos = 0;
|
||||
for (I item : items) {
|
||||
if (item.getId().equals(messageId)) {
|
||||
layoutManager.scrollToPosition(pos);
|
||||
break;
|
||||
}
|
||||
pos++;
|
||||
}
|
||||
}
|
||||
|
||||
public void setItems(Collection<I> items) {
|
||||
this.items.clear();
|
||||
this.items.addAll(items);
|
||||
@@ -144,7 +158,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
/**
|
||||
* Returns the position of the first unread item below the current viewport
|
||||
*/
|
||||
public int getVisibleUnreadPosBottom() {
|
||||
int getVisibleUnreadPosBottom() {
|
||||
final int positionBottom = layoutManager.findLastVisibleItemPosition();
|
||||
if (positionBottom == NO_POSITION) return NO_POSITION;
|
||||
for (int i = positionBottom + 1; i < items.size(); i++) {
|
||||
@@ -156,7 +170,7 @@ public class ThreadItemAdapter<I extends ThreadItem>
|
||||
/**
|
||||
* Returns the position of the first unread item above the current viewport
|
||||
*/
|
||||
public int getVisibleUnreadPosTop() {
|
||||
int getVisibleUnreadPosTop() {
|
||||
final int positionTop = layoutManager.findFirstVisibleItemPosition();
|
||||
int position = NO_POSITION;
|
||||
for (int i = 0; i < items.size(); i++) {
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package org.briarproject.briar.android.threaded;
|
||||
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public interface ThreadItemList<I extends ThreadItem> extends List<I> {
|
||||
|
||||
@Nullable
|
||||
MessageId getFirstVisibleItemId();
|
||||
|
||||
void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId);
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package org.briarproject.briar.android.threaded;
|
||||
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
public class ThreadItemListImpl<I extends ThreadItem> extends ArrayList<I>
|
||||
implements ThreadItemList<I> {
|
||||
|
||||
private MessageId bottomVisibleItemId;
|
||||
|
||||
@Override
|
||||
public MessageId getFirstVisibleItemId() {
|
||||
return bottomVisibleItemId;
|
||||
}
|
||||
|
||||
public void setFirstVisibleId(@Nullable MessageId bottomVisibleItemId) {
|
||||
this.bottomVisibleItemId = bottomVisibleItemId;
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.android.threaded;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.support.annotation.CallSuper;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.StringRes;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.design.widget.Snackbar;
|
||||
@@ -26,6 +25,7 @@ import org.briarproject.briar.android.controller.SharingController;
|
||||
import org.briarproject.briar.android.controller.SharingController.SharingListener;
|
||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter.ThreadItemListener;
|
||||
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListDataSource;
|
||||
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
||||
import org.briarproject.briar.android.view.BriarRecyclerView;
|
||||
import org.briarproject.briar.android.view.TextInputView;
|
||||
@@ -38,13 +38,12 @@ import org.thoughtcrime.securesms.components.KeyboardAwareLinearLayout;
|
||||
import java.util.Collection;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static android.support.design.widget.Snackbar.make;
|
||||
import static android.support.v7.widget.RecyclerView.NO_POSITION;
|
||||
import static android.support.v7.widget.RecyclerView.SCROLL_STATE_IDLE;
|
||||
import static android.view.View.GONE;
|
||||
import static android.view.View.VISIBLE;
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCount;
|
||||
|
||||
@@ -53,9 +52,8 @@ import static org.briarproject.briar.android.threaded.ThreadItemAdapter.UnreadCo
|
||||
public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadItemAdapter<I>, I extends ThreadItem, H extends PostHeader>
|
||||
extends BriarActivity
|
||||
implements ThreadListListener<H>, TextInputListener, SharingListener,
|
||||
ThreadItemListener<I> {
|
||||
ThreadItemListener<I>, ThreadListDataSource {
|
||||
|
||||
protected static final String KEY_INPUT_VISIBILITY = "inputVisibility";
|
||||
protected static final String KEY_REPLY_ID = "replyId";
|
||||
|
||||
private static final Logger LOG =
|
||||
@@ -71,6 +69,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
private MessageId replyId;
|
||||
|
||||
protected abstract ThreadListController<G, I, H> getController();
|
||||
|
||||
@Inject
|
||||
protected SharingController sharingController;
|
||||
|
||||
@@ -89,7 +88,6 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
getController().setGroupId(groupId);
|
||||
|
||||
textInput = (TextInputView) findViewById(R.id.text_input_container);
|
||||
textInput.setVisibility(GONE);
|
||||
textInput.setListener(this);
|
||||
list = (BriarRecyclerView) findViewById(R.id.list);
|
||||
layoutManager = new LinearLayoutManager(this);
|
||||
@@ -108,6 +106,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
updateUnreadCount();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onScrollStateChanged(RecyclerView recyclerView,
|
||||
int newState) {
|
||||
@@ -148,6 +147,18 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
loadSharingContacts();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public MessageId getFirstVisibleMessageId() {
|
||||
if (layoutManager != null && adapter != null) {
|
||||
int position =
|
||||
layoutManager.findFirstVisibleItemPosition();
|
||||
I i = adapter.getItemAt(position);
|
||||
return i == null ? null : i.getId();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected abstract A createAdapter(LinearLayoutManager layoutManager);
|
||||
|
||||
protected void loadNamedGroup() {
|
||||
@@ -171,18 +182,17 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
protected void loadItems() {
|
||||
final int revision = adapter.getRevision();
|
||||
getController().loadItems(
|
||||
new UiResultExceptionHandler<Collection<I>, DbException>(this) {
|
||||
new UiResultExceptionHandler<ThreadItemList<I>, DbException>(
|
||||
this) {
|
||||
@Override
|
||||
public void onResultUi(Collection<I> items) {
|
||||
public void onResultUi(ThreadItemList<I> items) {
|
||||
if (revision == adapter.getRevision()) {
|
||||
adapter.incrementRevision();
|
||||
if (items.isEmpty()) {
|
||||
list.showData();
|
||||
} else {
|
||||
adapter.setItems(items);
|
||||
list.showData();
|
||||
if (replyId != null)
|
||||
adapter.setHighlightedItem(replyId);
|
||||
initList(items);
|
||||
updateTextInput(replyId);
|
||||
}
|
||||
} else {
|
||||
LOG.info("Concurrent update, reloading");
|
||||
@@ -197,6 +207,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
});
|
||||
}
|
||||
|
||||
private void initList(final ThreadItemList<I> items) {
|
||||
adapter.setItems(items);
|
||||
MessageId messageId = items.getFirstVisibleItemId();
|
||||
if (messageId != null)
|
||||
adapter.setItemWithIdVisible(messageId);
|
||||
updateUnreadCount();
|
||||
list.showData();
|
||||
}
|
||||
|
||||
protected void loadSharingContacts() {
|
||||
getController().loadSharingContacts(
|
||||
new UiResultExceptionHandler<Collection<ContactId>, DbException>(
|
||||
@@ -231,18 +250,9 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
list.stopPeriodicUpdate();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Bundle savedInstanceState) {
|
||||
super.onRestoreInstanceState(savedInstanceState);
|
||||
boolean visible = savedInstanceState.getBoolean(KEY_INPUT_VISIBILITY);
|
||||
textInput.setVisibility(visible ? VISIBLE : GONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
boolean visible = textInput.getVisibility() == VISIBLE;
|
||||
outState.putBoolean(KEY_INPUT_VISIBILITY, visible);
|
||||
ThreadItem replyItem = adapter.getHighlightedItem();
|
||||
if (replyItem != null) {
|
||||
outState.putByteArray(KEY_REPLY_ID, replyItem.getId().getBytes());
|
||||
@@ -262,9 +272,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (textInput.getVisibility() == VISIBLE) {
|
||||
textInput.setVisibility(GONE);
|
||||
adapter.setHighlightedItem(null);
|
||||
if (adapter.getHighlightedItem() != null) {
|
||||
updateTextInput(null);
|
||||
} else {
|
||||
super.onBackPressed();
|
||||
}
|
||||
@@ -280,7 +289,7 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
|
||||
@Override
|
||||
public void onReplyClick(final I item) {
|
||||
showTextInput(item);
|
||||
updateTextInput(item.getId());
|
||||
if (textInput.isKeyboardOpen()) {
|
||||
scrollToItemAtTop(item);
|
||||
} else {
|
||||
@@ -330,20 +339,15 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
snackbar.show();
|
||||
}
|
||||
|
||||
protected void showTextInput(@Nullable I replyItem) {
|
||||
// An animation here would be an overkill because of the keyboard
|
||||
// popping up.
|
||||
// only clear the text when the input container was not visible
|
||||
if (textInput.getVisibility() != VISIBLE) {
|
||||
textInput.setVisibility(VISIBLE);
|
||||
textInput.setText("");
|
||||
private void updateTextInput(@Nullable MessageId replyItemId) {
|
||||
if (replyItemId != null) {
|
||||
textInput.setHint(R.string.forum_message_reply_hint);
|
||||
textInput.requestFocus();
|
||||
textInput.showSoftKeyboard();
|
||||
} else {
|
||||
textInput.setHint(R.string.forum_new_message_hint);
|
||||
}
|
||||
textInput.requestFocus();
|
||||
textInput.showSoftKeyboard();
|
||||
textInput.setHint(replyItem == null ? R.string.forum_new_message_hint :
|
||||
R.string.forum_message_reply_hint);
|
||||
adapter.setHighlightedItem(
|
||||
replyItem == null ? null : replyItem.getId());
|
||||
adapter.setHighlightedItem(replyItemId);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -369,9 +373,8 @@ public abstract class ThreadListActivity<G extends NamedGroup, A extends ThreadI
|
||||
};
|
||||
getController().createAndStoreMessage(text, replyItem, handler);
|
||||
textInput.hideSoftKeyboard();
|
||||
textInput.setVisibility(GONE);
|
||||
textInput.setText("");
|
||||
adapter.setHighlightedItem(null);
|
||||
updateTextInput(null);
|
||||
}
|
||||
|
||||
protected abstract int getMaxBodyLength();
|
||||
|
||||
@@ -6,6 +6,7 @@ import org.briarproject.bramble.api.contact.ContactId;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
import org.briarproject.briar.android.DestroyableContext;
|
||||
import org.briarproject.briar.android.controller.ActivityLifecycleController;
|
||||
import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||
@@ -30,7 +31,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
||||
|
||||
void loadItem(H header, ResultExceptionHandler<I, DbException> handler);
|
||||
|
||||
void loadItems(ResultExceptionHandler<Collection<I>, DbException> handler);
|
||||
void loadItems(ResultExceptionHandler<ThreadItemList<I>, DbException> handler);
|
||||
|
||||
void markItemRead(I item);
|
||||
|
||||
@@ -41,7 +42,7 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
||||
|
||||
void deleteNamedGroup(ExceptionHandler<DbException> handler);
|
||||
|
||||
interface ThreadListListener<H> extends DestroyableContext {
|
||||
interface ThreadListListener<H> extends ThreadListDataSource {
|
||||
@UiThread
|
||||
void onHeaderReceived(H header);
|
||||
|
||||
@@ -52,4 +53,10 @@ public interface ThreadListController<G extends NamedGroup, I extends ThreadItem
|
||||
void onInvitationAccepted(ContactId c);
|
||||
}
|
||||
|
||||
interface ThreadListDataSource extends DestroyableContext {
|
||||
|
||||
@UiThread @Nullable
|
||||
MessageId getFirstVisibleMessageId();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -22,14 +22,13 @@ import org.briarproject.briar.android.controller.handler.ExceptionHandler;
|
||||
import org.briarproject.briar.android.controller.handler.ResultExceptionHandler;
|
||||
import org.briarproject.briar.android.threaded.ThreadListController.ThreadListListener;
|
||||
import org.briarproject.briar.api.android.AndroidNotificationManager;
|
||||
import org.briarproject.briar.api.client.MessageTracker;
|
||||
import org.briarproject.briar.api.client.NamedGroup;
|
||||
import org.briarproject.briar.api.client.PostHeader;
|
||||
import org.briarproject.briar.api.client.ThreadedMessage;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.Executor;
|
||||
@@ -55,18 +54,21 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
protected final AndroidNotificationManager notificationManager;
|
||||
protected final Executor cryptoExecutor;
|
||||
protected final Clock clock;
|
||||
private final MessageTracker messageTracker;
|
||||
protected volatile L listener;
|
||||
|
||||
protected ThreadListControllerImpl(@DatabaseExecutor Executor dbExecutor,
|
||||
LifecycleManager lifecycleManager, IdentityManager identityManager,
|
||||
@CryptoExecutor Executor cryptoExecutor, EventBus eventBus,
|
||||
Clock clock, AndroidNotificationManager notificationManager) {
|
||||
Clock clock, AndroidNotificationManager notificationManager,
|
||||
MessageTracker messageTracker) {
|
||||
super(dbExecutor, lifecycleManager);
|
||||
this.identityManager = identityManager;
|
||||
this.cryptoExecutor = cryptoExecutor;
|
||||
this.notificationManager = notificationManager;
|
||||
this.clock = clock;
|
||||
this.eventBus = eventBus;
|
||||
this.messageTracker = messageTracker;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -97,6 +99,20 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
|
||||
@Override
|
||||
public void onActivityDestroy() {
|
||||
final MessageId first = listener.getFirstVisibleMessageId();
|
||||
if (first != null) {
|
||||
dbExecutor.execute(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
messageTracker.storeMessageId(groupId, first);
|
||||
} catch (DbException e) {
|
||||
if (LOG.isLoggable(WARNING))
|
||||
LOG.log(WARNING, e.toString(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@CallSuper
|
||||
@@ -144,7 +160,7 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
|
||||
@Override
|
||||
public void loadItems(
|
||||
final ResultExceptionHandler<Collection<I>, DbException> handler) {
|
||||
final ResultExceptionHandler<ThreadItemList<I>, DbException> handler) {
|
||||
checkGroupId();
|
||||
runOnDbThread(new Runnable() {
|
||||
@Override
|
||||
@@ -293,11 +309,16 @@ public abstract class ThreadListControllerImpl<G extends NamedGroup, I extends T
|
||||
@DatabaseExecutor
|
||||
protected abstract void deleteNamedGroup(G groupItem) throws DbException;
|
||||
|
||||
private List<I> buildItems(Collection<H> headers) {
|
||||
List<I> items = new ArrayList<>();
|
||||
private ThreadItemList<I> buildItems(Collection<H> headers)
|
||||
throws DbException {
|
||||
ThreadItemList<I> items = new ThreadItemListImpl<>();
|
||||
for (H h : headers) {
|
||||
items.add(buildItem(h, bodyCache.get(h.getId())));
|
||||
}
|
||||
MessageId msgId = messageTracker.loadStoredMessageId(groupId);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Loaded last top visible message id " + msgId);
|
||||
items.setFirstVisibleId(msgId);
|
||||
return items;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package org.briarproject.briar.android.util;
|
||||
|
||||
import android.content.Context;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.design.widget.TextInputLayout;
|
||||
import android.support.v4.app.FragmentManager;
|
||||
import android.support.v4.content.ContextCompat;
|
||||
@@ -23,6 +22,8 @@ import org.briarproject.briar.R;
|
||||
import org.briarproject.briar.android.view.ArticleMovementMethod;
|
||||
import org.briarproject.briar.android.widget.LinkDialogFragment;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.text.format.DateUtils.DAY_IN_MILLIS;
|
||||
import static android.text.format.DateUtils.FORMAT_ABBREV_MONTH;
|
||||
import static android.text.format.DateUtils.FORMAT_ABBREV_RELATIVE;
|
||||
|
||||
@@ -30,6 +30,7 @@ import static android.content.Context.LAYOUT_INFLATER_SERVICE;
|
||||
import static android.content.Intent.FLAG_ACTIVITY_CLEAR_TOP;
|
||||
import static android.graphics.Typeface.BOLD;
|
||||
import static android.util.TypedValue.COMPLEX_UNIT_PX;
|
||||
import static org.briarproject.bramble.api.identity.Author.Status.NONE;
|
||||
import static org.briarproject.bramble.api.identity.Author.Status.OURSELVES;
|
||||
import static org.briarproject.briar.android.activity.BriarActivity.GROUP_ID;
|
||||
|
||||
@@ -40,6 +41,8 @@ public class AuthorView extends RelativeLayout {
|
||||
public static final int REBLOGGER = 1;
|
||||
public static final int COMMENTER = 2;
|
||||
public static final int LIST = 3;
|
||||
public static final int RSS_FEED = 4;
|
||||
public static final int RSS_FEED_REBLOGGED = 5;
|
||||
|
||||
private final CircleImageView avatar;
|
||||
private final ImageView avatarIcon;
|
||||
@@ -83,7 +86,13 @@ public class AuthorView extends RelativeLayout {
|
||||
}
|
||||
|
||||
public void setAuthorStatus(Status status) {
|
||||
trustIndicator.setTrustLevel(status);
|
||||
if (status != NONE) {
|
||||
trustIndicator.setTrustLevel(status);
|
||||
trustIndicator.setVisibility(VISIBLE);
|
||||
} else {
|
||||
trustIndicator.setVisibility(GONE);
|
||||
}
|
||||
|
||||
if (status == OURSELVES) {
|
||||
authorName.setTypeface(authorNameTypeface, BOLD);
|
||||
} else {
|
||||
@@ -124,10 +133,17 @@ public class AuthorView extends RelativeLayout {
|
||||
setOnClickListener(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Styles this view for a different persona.
|
||||
*
|
||||
* Attention: RSS_FEED and RSS_FEED_REBLOGGED change the avatar
|
||||
* and override the one set by
|
||||
* {@link AuthorView#setAuthor(Author)}.
|
||||
*/
|
||||
public void setPersona(int persona) {
|
||||
switch (persona) {
|
||||
case NORMAL:
|
||||
avatarIcon.setVisibility(VISIBLE);
|
||||
avatarIcon.setVisibility(INVISIBLE);
|
||||
date.setVisibility(VISIBLE);
|
||||
setAvatarSize(R.dimen.blogs_avatar_normal_size);
|
||||
setTextSize(authorName, R.dimen.text_size_small);
|
||||
@@ -158,6 +174,24 @@ public class AuthorView extends RelativeLayout {
|
||||
setCenterVertical(authorName, true);
|
||||
setCenterVertical(trustIndicator, true);
|
||||
break;
|
||||
case RSS_FEED:
|
||||
avatarIcon.setVisibility(INVISIBLE);
|
||||
date.setVisibility(VISIBLE);
|
||||
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);
|
||||
date.setVisibility(VISIBLE);
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ package org.briarproject.briar.android.view;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.support.annotation.Nullable;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.support.design.widget.FloatingActionButton;
|
||||
import android.util.AttributeSet;
|
||||
@@ -13,6 +12,8 @@ import android.widget.TextView;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.briar.R;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@UiThread
|
||||
@NotNullByDefault
|
||||
public class UnreadMessageButton extends FrameLayout {
|
||||
@@ -36,8 +37,7 @@ public class UnreadMessageButton extends FrameLayout {
|
||||
|
||||
LayoutInflater inflater = (LayoutInflater) context
|
||||
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
|
||||
inflater
|
||||
.inflate(R.layout.unread_message_button, this, true);
|
||||
inflater.inflate(R.layout.unread_message_button, this, true);
|
||||
|
||||
fab = (FloatingActionButton) findViewById(R.id.fab);
|
||||
unread = (TextView) findViewById(R.id.unreadCountView);
|
||||
@@ -64,15 +64,11 @@ public class UnreadMessageButton extends FrameLayout {
|
||||
|
||||
public void setUnreadCount(int count) {
|
||||
if (count == 0) {
|
||||
fab.setVisibility(GONE);
|
||||
// fab.hide();
|
||||
unread.setVisibility(GONE);
|
||||
setVisibility(INVISIBLE);
|
||||
} else {
|
||||
// FIXME: Use animations when upgrading to support library 24.2.0
|
||||
// https://code.google.com/p/android/issues/detail?id=216469
|
||||
fab.setVisibility(VISIBLE);
|
||||
// if (!fab.isShown()) fab.show();
|
||||
unread.setVisibility(VISIBLE);
|
||||
setVisibility(VISIBLE);
|
||||
unread.setText(String.valueOf(count));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,29 +1,21 @@
|
||||
package org.thoughtcrime.securesms.components.emoji;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Paint.FontMetricsInt;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.UiThread;
|
||||
import android.text.TextUtils;
|
||||
import android.support.v7.widget.AppCompatTextView;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.ViewConfiguration;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.components.emoji.EmojiProvider.EmojiDrawable;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static android.text.TextUtils.TruncateAt.END;
|
||||
import static android.view.View.MeasureSpec.AT_MOST;
|
||||
import static android.view.View.MeasureSpec.EXACTLY;
|
||||
import static android.widget.TextView.BufferType.SPANNABLE;
|
||||
|
||||
@UiThread
|
||||
public class EmojiTextView extends TextView {
|
||||
|
||||
private CharSequence source;
|
||||
private boolean needsEllipsizing;
|
||||
public class EmojiTextView extends AppCompatTextView {
|
||||
|
||||
public EmojiTextView(Context context) {
|
||||
this(context, null);
|
||||
@@ -42,13 +34,9 @@ public class EmojiTextView extends TextView {
|
||||
|
||||
@Override
|
||||
public void setText(@Nullable CharSequence text, BufferType type) {
|
||||
source = EmojiProvider.getInstance(getContext()).emojify(text, this);
|
||||
|
||||
setTextEllipsized(source);
|
||||
}
|
||||
|
||||
private void setTextEllipsized(final @Nullable CharSequence source) {
|
||||
super.setText(needsEllipsizing ? ellipsize(source) : source, SPANNABLE);
|
||||
CharSequence source =
|
||||
EmojiProvider.getInstance(getContext()).emojify(text, this);
|
||||
super.setText(source, SPANNABLE);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -57,26 +45,6 @@ public class EmojiTextView extends TextView {
|
||||
else super.invalidateDrawable(drawable);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
|
||||
final int size = MeasureSpec.getSize(widthMeasureSpec);
|
||||
final int mode = MeasureSpec.getMode(widthMeasureSpec);
|
||||
if (getEllipsize() == END &&
|
||||
!TextUtils.isEmpty(source) &&
|
||||
(mode == AT_MOST || mode == EXACTLY) &&
|
||||
getPaint().breakText(source, 0, source.length() - 1, true, size,
|
||||
null) != source.length()) {
|
||||
needsEllipsizing = true;
|
||||
FontMetricsInt font = getPaint().getFontMetricsInt();
|
||||
int height = Math.abs(font.top - font.bottom);
|
||||
super.onMeasure(MeasureSpec.makeMeasureSpec(size, EXACTLY),
|
||||
MeasureSpec.makeMeasureSpec(height, EXACTLY));
|
||||
} else {
|
||||
needsEllipsizing = false;
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onLayout(boolean changed, int left, int top, int right,
|
||||
int bottom) {
|
||||
@@ -89,20 +57,6 @@ public class EmojiTextView extends TextView {
|
||||
if (size > drawingCacheSize) {
|
||||
setLayerType(LAYER_TYPE_NONE, null);
|
||||
}
|
||||
|
||||
if (changed) setTextEllipsized(source);
|
||||
super.onLayout(changed, left, top, right, bottom);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public CharSequence ellipsize(@Nullable CharSequence text) {
|
||||
if (TextUtils.isEmpty(text) || getWidth() == 0 ||
|
||||
getEllipsize() != END) {
|
||||
return text;
|
||||
} else {
|
||||
return TextUtils.ellipsize(text, getPaint(),
|
||||
getWidth() - getPaddingRight() - getPaddingLeft(), END);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="48.0"
|
||||
android:viewportWidth="48.0">
|
||||
<path
|
||||
android:fillColor="#06b9ff"
|
||||
android:pathData="M9.1,19.3l14.9,11.8l14.9,-11.8l-1.9,-2.4l-13,10.4l-13,-10.4z"/>
|
||||
</vector>
|
||||
@@ -1,9 +0,0 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportHeight="48.0"
|
||||
android:viewportWidth="48.0">
|
||||
<path
|
||||
android:fillColor="#06b9ff"
|
||||
android:pathData="M38.9,28.7l-14.9,-11.8l-14.9,11.8l1.9,2.4l13,-10.4l13,10.4z"/>
|
||||
</vector>
|
||||
10
briar-android/src/main/res/drawable/ic_info.xml
Normal file
10
briar-android/src/main/res/drawable/ic_info.xml
Normal file
@@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:alpha="0.56"
|
||||
android:viewportHeight="24.0"
|
||||
android:viewportWidth="24.0">
|
||||
<path
|
||||
android:fillColor="#000000"
|
||||
android:pathData="M11,17h2v-6h-2v6zM12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM12,20c-4.41,0 -8,-3.59 -8,-8s3.59,-8 8,-8 8,3.59 8,8 -3.59,8 -8,8zM11,9h2L13,7h-2v2z"/>
|
||||
</vector>
|
||||
25
briar-android/src/main/res/drawable/ic_rss_feed.xml
Normal file
25
briar-android/src/main/res/drawable/ic_rss_feed.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="30dp"
|
||||
android:height="30dp"
|
||||
android:viewportHeight="30"
|
||||
android:viewportWidth="30">
|
||||
|
||||
<path
|
||||
android:fillColor="#ffa500"
|
||||
android:pathData="M0,0 L30,0 L30,30 L0,30 L0,0 Z"/>
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M8.9322,18.0339 C10.6078,18.0339,11.9661,19.3922,11.9661,21.0678
|
||||
C11.9661,22.7434,10.6078,24.1017,8.9322,24.1017
|
||||
C7.25663,24.1017,5.8983,22.7434,5.8983,21.0678
|
||||
C5.8983,19.3922,7.25663,18.0339,8.9322,18.0339 Z"/>
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M5.8983,15 A9.1016949,9.1016949,0,0,1,15,24.1017 L18.0339,24.1017
|
||||
A12.135593,12.135593,0,0,0,5.8983,11.9661 Z"/>
|
||||
<path
|
||||
android:fillColor="#ffffff"
|
||||
android:pathData="M5.8983,8.9322 A15.169492,15.169492,0,0,1,21.0678,24.1017 L24.1017,24.1017
|
||||
A18.20339,18.20339,0,0,0,5.8983,5.8983 Z"/>
|
||||
</vector>
|
||||
@@ -5,5 +5,5 @@
|
||||
|
||||
<stroke
|
||||
android:width="2dp"
|
||||
android:color="@color/forum_discussion_nested_line"/>
|
||||
android:color="@color/thread_indicator"/>
|
||||
</shape>
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item android:drawable="@drawable/chevron48dp_down" android:state_selected="true"/>
|
||||
<item android:drawable="@drawable/chevron48dp_up"/>
|
||||
</selector>
|
||||
@@ -12,9 +12,9 @@
|
||||
android:fillColor="#87c214"
|
||||
android:pathData="M64.9004,0 C55.2004,0,47.1992,7.99922,47.1992,17.6992 L47.1992,40.1992
|
||||
L90.8008,40.1992 L90.8008,17.6992
|
||||
C90.8008,7.99922,82.8992,-4.73695e-15,73.1992,0 L64.9004,0 Z M161.9,0
|
||||
C90.8008,7.99922,82.8992,0,73.1992,0 L64.9004,0 Z M161.9,0
|
||||
C152.2,0,144.199,7.99922,144.199,17.6992 L144.199,137.199 L187.801,137.199
|
||||
L187.801,17.6992 C187.801,7.99922,179.899,-4.73695e-15,170.199,0 L161.9,0 Z
|
||||
L187.801,17.6992 C187.801,7.99922,179.899,0,170.199,0 L161.9,0 Z
|
||||
M47.1992,97.8008 L47.1992,217.301 C47.1992,227.001,55.1004,235,64.9004,235
|
||||
L73.1992,235 C82.8992,235,90.9004,227.001,90.9004,217.301 L90.9004,97.8008
|
||||
L47.1992,97.8008 Z M144.199,194.801 L144.199,217.301
|
||||
@@ -25,12 +25,12 @@ C179.899,235,187.9,227.001,187.9,217.301 L187.9,194.801 L144.199,194.801 Z"/>
|
||||
android:pathData="M144.2,144.2 L187.9,144.2 L187.9,187.9 L144.2,187.9 L144.2,144.2 Z"/>
|
||||
<path
|
||||
android:fillColor="#95d220"
|
||||
android:pathData="M17.6992,47.1992 C7.99922,47.1992,2.36848e-15,55.1004,0,64.9004 L0,73.1992
|
||||
android:pathData="M17.6992,47.1992 C7.99922,47.1992,0,55.1004,0,64.9004 L0,73.1992
|
||||
C0,82.8992,7.89922,90.9004,17.6992,90.9004 L137.199,90.9004 L137.199,47.1992
|
||||
L17.6992,47.1992 Z M194.801,47.1992 L194.801,90.9004 L217.301,90.9004
|
||||
C227.001,90.9004,235,82.9992,235,73.1992 L235,64.9004
|
||||
C235,55.1004,227.001,47.1992,217.301,47.1992 L194.801,47.1992 Z M17.6992,144.199
|
||||
C7.99922,144.199,2.36848e-15,152.1,0,161.9 L0,170.199
|
||||
C7.99922,144.199,0,152.1,0,161.9 L0,170.199
|
||||
C0,179.899,7.89922,187.9,17.6992,187.9 L40.1992,187.9 L40.1992,144.199
|
||||
L17.6992,144.199 Z M97.8008,144.199 L97.8008,187.9 L217.301,187.9
|
||||
C227.001,187.9,235,179.899,235,170.199 L235,161.9
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
<?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"
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/info"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:drawableLeft="@drawable/ic_info"
|
||||
android:drawablePadding="@dimen/margin_medium"
|
||||
android:padding="@dimen/margin_medium"
|
||||
android:textColor="?android:textColorSecondary"
|
||||
android:textSize="@dimen/text_size_tiny"
|
||||
tools:text="@string/sharing_status_forum"/>
|
||||
|
||||
<View style="@style/Divider.Horizontal"/>
|
||||
|
||||
<org.briarproject.briar.android.view.BriarRecyclerView
|
||||
android:id="@+id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:scrollToEnd="false"
|
||||
tools:listitem="@layout/list_item_contact"/>
|
||||
|
||||
</LinearLayout>
|
||||
@@ -19,7 +19,7 @@
|
||||
android:textSize="@dimen/text_size_medium"
|
||||
tools:text="This is a name of a RSS Feed"/>
|
||||
|
||||
<ImageView
|
||||
<ImageButton
|
||||
android:id="@+id/deleteButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_forum_compose_post"
|
||||
android:icon="@drawable/forum_item_create_white"
|
||||
android:title="@string/forum_compose_post"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_forum_share"
|
||||
android:icon="@drawable/social_share_white"
|
||||
|
||||
@@ -3,12 +3,6 @@
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<item
|
||||
android:id="@+id/action_group_compose_message"
|
||||
android:icon="@drawable/forum_item_create_white"
|
||||
android:title="@string/groups_compose_message"
|
||||
app:showAsAction="always"/>
|
||||
|
||||
<item
|
||||
android:id="@+id/action_group_member_list"
|
||||
android:icon="@drawable/ic_group_white"
|
||||
|
||||
@@ -151,7 +151,6 @@
|
||||
<string name="groups_create_group_invitation_button">Einladung schicken</string>
|
||||
<string name="groups_create_group_hint">Gebe Deiner privaten Gruppe einen Namen</string>
|
||||
<string name="groups_invitation_sent">Gruppeneinladung versendet</string>
|
||||
<string name="groups_compose_message">Nachricht erstellen</string>
|
||||
<string name="groups_message_sent">Nachricht gesendet</string>
|
||||
<string name="groups_member_list">Mitglieder</string>
|
||||
<string name="groups_invite_members">Mitglieder einladen</string>
|
||||
@@ -174,14 +173,22 @@
|
||||
<string name="groups_invitations_invitation_received">%1$s hat dich eingeladen der Gruppe \"%2$s\" beizutreten.</string>
|
||||
<string name="groups_invitations_joined">Gruppe beigetreten</string>
|
||||
<string name="groups_invitations_declined">Gruppeneinladung abgelehnt</string>
|
||||
<plurals name="groups_invitations_open">
|
||||
<item quantity="one">%d offene Gruppeneinladung</item>
|
||||
<item quantity="other">%d offene Gruppeneinladungen</item>
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">Gruppeneinladung von %s angenommen.</string>
|
||||
<string name="groups_invitations_response_declined_sent">Gruppeneinladung von %s abgelehnt.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s hat die Einladung zur Gruppe angenommen.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s hat die Einladung zur Gruppe abgelehnt.</string>
|
||||
<string name="sharing_status_groups">Nur der Ersteller kann neue Mitglieder zu einer Gruppe einladen. HIer alle aktuellen Mitglieder dieser Gruppe.</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<string name="groups_reveal_contacts">Kontakte teilen</string>
|
||||
<string name="groups_reveal_dialog_message">Kontakte können mit allen derzeitigen und zukünftigen Mitgliedern dieser Gruppe geteilt werden.\n\nDas beschleunigt die Verbindung zu der Gruppe und macht sie zusätzlich zuverlässiger, da Kommunikation mit den Mitgliedern auch dann erfolgen kann, wenn der Ersteller der Gruppe offline ist.</string>
|
||||
<string name="groups_reveal_visible">Verbindung zum Kontakt ist für die Gruppe sichtbar</string>
|
||||
<string name="groups_reveal_visible_revealed_by_us">Beziehung zum Kontakt ist für diese Gruppe sichtbar (selbst offengelegt)</string>
|
||||
<string name="groups_reveal_visible_revealed_by_contact">Beziehung zum Kontakt ist für diese Gruppe sichtbar (offengelegt durch %s)</string>
|
||||
<string name="groups_reveal_invisible">Beziehung zum Kontakt ist für diese Gruppe nicht sichtbar</string>
|
||||
<!--Forums-->
|
||||
<string name="no_forums">Du hast noch keine Foren.\n\nWarum erstellst du nicht einfach selbst ein neues Forum, indem du auf das \"+\"-Icon am oberen Bildschirmrand tippst?\n\nDu kannst auch deine Kontakte auffordern, Foren mit dir zu teilen.</string>
|
||||
<string name="create_forum_title">Neues Forum</string>
|
||||
@@ -194,15 +201,10 @@
|
||||
<item quantity="one">%d Beitrag</item>
|
||||
<item quantity="other">%d Beiträge</item>
|
||||
</plurals>
|
||||
<string name="forum_compose_post">Neuer Forenbeitrag</string>
|
||||
<string name="forum_new_entry_posted">Forenbeitrag wurde veröffentlicht.</string>
|
||||
<string name="forum_new_message_hint">Neuer Eintrag</string>
|
||||
<string name="forum_message_reply_hint">Neue Antwort</string>
|
||||
<string name="btn_reply">Antworten</string>
|
||||
<plurals name="message_replies">
|
||||
<item quantity="one">%1$d Antwort</item>
|
||||
<item quantity="other">%1$d Antworten</item>
|
||||
</plurals>
|
||||
<string name="forum_leave">Forum verlassen</string>
|
||||
<string name="dialog_title_leave_forum">Verlassen des Forums bestätigen</string>
|
||||
<string name="dialog_message_leave_forum">Bist du sicher, dass du dieses Forum verlassen willst? Kontakte, die du mit diesem Forum geteilt hast, werden keine Updates von diesem Forum mehr bekommen.</string>
|
||||
@@ -229,6 +231,8 @@
|
||||
<string name="forum_invitation_response_accepted_received">%s hat die Forumeinladung akzeptiert.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s hat die Forumseinladung abgelehnt.</string>
|
||||
<string name="sharing_status">Sharing Status</string>
|
||||
<string name="sharing_status_forum">Jedes Mitglied eines Forums kann dieses mit seinen Kontakten teilen. Du teilst dieses Forum mit den folgenden Kontakten. Möglicherweise gibt es Mitglieder die nicht sichtbar sind.</string>
|
||||
<string name="shared_with">Geteilt mit %1$d (%2$d online)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one">%d Forum von Kontaken geteilt</item>
|
||||
<item quantity="other">%d Foren von Kontakten geteilt</item>
|
||||
@@ -265,6 +269,7 @@
|
||||
<string name="blogs_sharing_invitations_title">Blogeinladungen</string>
|
||||
<string name="blogs_sharing_joined_toast">Blog abonniert</string>
|
||||
<string name="blogs_sharing_declined_toast">Blogeinladung abgelehnt</string>
|
||||
<string name="sharing_status_blog">Jeder Abonnent eines Blogs kann diesen mit seinen Kontakten teilen. Du teilst diesen Blog mit den folgenden Kontakten. Möglicherweise gibt es Abonnenten die nicht sichtbar sind.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">RSS-Feed importieren</string>
|
||||
<string name="blogs_rss_feeds_import_button">Importieren</string>
|
||||
@@ -274,6 +279,9 @@
|
||||
<string name="blogs_rss_feeds_manage_imported">Importiert:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">Autor:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">Letzte Aktualisierung:</string>
|
||||
<string name="blogs_rss_remove_feed">Feed entfernen</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">Soll der Feed mit allen Posts wirklich gelöscht werden?\nGeteilte Posts werden dabei nicht von anderen Geräten gelöscht.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Feed entfernen</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">Der Feed konnte nicht gelöscht werden!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Du hast bisher noch keine RSS Feeds importiert. Tippe auf das \"+\"-Icon am oberen Bildschirmrand um einen neuen Feed hinzuzufügen.</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Es gab ein Problem beim Laden deiner Feeds. Bitte versuche es später erneut.</string>
|
||||
@@ -282,6 +290,10 @@
|
||||
<string name="bluetooth_setting">Über Bluetooth verbinden</string>
|
||||
<string name="bluetooth_setting_enabled">Sobald Kontakte in der Nähe sind</string>
|
||||
<string name="bluetooth_setting_disabled">Nur beim Hinzufügen von Kontakten</string>
|
||||
<string name="tor_network_setting">Über Tor verbinden</string>
|
||||
<string name="tor_network_setting_never">Nie</string>
|
||||
<string name="tor_network_setting_wifi">Nur WLAN</string>
|
||||
<string name="tor_network_setting_always">WLAN oder mobile Datenverbindung</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">Sicherheit</string>
|
||||
<string name="change_password">Passwort ändern</string>
|
||||
@@ -341,4 +353,9 @@
|
||||
<string name="dev_report_saved">Der Bericht wurde gespeichert. Er wird verschickt, wenn Du Dich das nächste Mal bei Briar anmeldest.</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">Von Briar abmelden ...</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">Bildschirmfilter erkannt</string>
|
||||
<string name="screen_filter_body">Diese Apps können andere Apps überlagern:\n\n%1$s \n\nBriar reagiert im Fall einer Überlagerung nicht auf Benutzereingaben.
|
||||
Falls dadurch Probleme in der Verwendung von Briar entstehen, versuche diese Apps zu deaktivieren.\n</string>
|
||||
<string name="checkbox_dont_show_again">Nicht noch einmal für diese Apps warnen</string>
|
||||
</resources>
|
||||
|
||||
@@ -151,7 +151,6 @@
|
||||
<string name="groups_create_group_invitation_button">Enviar invitación</string>
|
||||
<string name="groups_create_group_hint">Dar nombre al grupo privado</string>
|
||||
<string name="groups_invitation_sent">Se ha mandado la invitación de grupo</string>
|
||||
<string name="groups_compose_message">Escribir mensaje</string>
|
||||
<string name="groups_message_sent">Mensaje enviado</string>
|
||||
<string name="groups_member_list">Integrantes</string>
|
||||
<string name="groups_invite_members">Invitar miembros</string>
|
||||
@@ -182,6 +181,7 @@
|
||||
<string name="groups_invitations_response_declined_sent">Declinaste la invitación de grupo de %s.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s ha aceptado la invitación al grupo.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s ha declinado la invitación al grupo.</string>
|
||||
<string name="sharing_status_groups">Solo el creador puede invitar nuevos miembros al grupo. Abajo se listan todos los participantes actuales.</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<string name="groups_reveal_contacts">Revelar contactos</string>
|
||||
<string name="groups_reveal_dialog_message">Puedes elegir si revelar los contactos a los actuales y futuros integrantes de este grupo.\n\nRevelar los contactos mejora la velocidad y la fiabilidad de tu conexión, porque puedes comunicarte con los contactos revelados incluso cuando el creador del grupo no se encuentra conectado.</string>
|
||||
@@ -201,15 +201,10 @@
|
||||
<item quantity="one">%d publicación</item>
|
||||
<item quantity="other">%d publicaciones</item>
|
||||
</plurals>
|
||||
<string name="forum_compose_post">Nueva publicación en el foro</string>
|
||||
<string name="forum_new_entry_posted">Entrada en el foro publicada</string>
|
||||
<string name="forum_new_message_hint">Nueva entrada</string>
|
||||
<string name="forum_message_reply_hint">Nueva respuesta</string>
|
||||
<string name="btn_reply">Responder</string>
|
||||
<plurals name="message_replies">
|
||||
<item quantity="one">%1$d respuesta</item>
|
||||
<item quantity="other">%1$d respuestas</item>
|
||||
</plurals>
|
||||
<string name="forum_leave">Abandonar foro</string>
|
||||
<string name="dialog_title_leave_forum">Confirmación abandono del foro</string>
|
||||
<string name="dialog_message_leave_forum">¿Seguro que quieres abandonar el foro? Puede que los contactos que invitaste al foro dejen de recibir actualizaciones del mismo.</string>
|
||||
@@ -236,6 +231,7 @@
|
||||
<string name="forum_invitation_response_accepted_received">%s aceptó la invitación al foro.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s rechazó la invitación al foro.</string>
|
||||
<string name="sharing_status">Estado de la compartición</string>
|
||||
<string name="sharing_status_forum">Cualquier miembro de un foro puede compartirlo con sus contactos. Estás compartiendo este foro con los contactos listados a continuación. Puede haber otros miembros que no puedes ver.</string>
|
||||
<string name="shared_with">Compartido con %1$d (%2$d en línea)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one">%d foro compartido por contactos</item>
|
||||
@@ -273,6 +269,7 @@
|
||||
<string name="blogs_sharing_invitations_title">Invitaciones a blogs</string>
|
||||
<string name="blogs_sharing_joined_toast">Suscrito al blog</string>
|
||||
<string name="blogs_sharing_declined_toast">Rechazada invitación al blog</string>
|
||||
<string name="sharing_status_blog">Cualquiera que se suscriba a un blog puede compartirlo con sus contactos. Estás compartiendo este blog con contactos listados a continuación. Puede haber otros suscriptores que no puedes ver.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">Importar canal RSS</string>
|
||||
<string name="blogs_rss_feeds_import_button">Importar</string>
|
||||
@@ -282,6 +279,9 @@
|
||||
<string name="blogs_rss_feeds_manage_imported">Importado:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">Autor:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">Última actualización:</string>
|
||||
<string name="blogs_rss_remove_feed">Eliminar canal RSS</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">¿Seguro que quieres borrar este canal RSS y todas sus publicaciones?\nNo se eliminará ningún artículo que hayas compartido de los dispositivos de otras personas.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Eliminar canal RSS</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">¡El canal no pudo ser eliminado!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">No has importado ningún canal RSS.\n\n¿Por qué no pulsas el signo más de la esquina superior derecha para añadir el primero?</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Hubo un problema cargando tus canales RSS. Por favor, prueba más tarde.</string>
|
||||
@@ -290,6 +290,10 @@
|
||||
<string name="bluetooth_setting">Connectar mediante Bluetooth</string>
|
||||
<string name="bluetooth_setting_enabled">Cuando haya contactos cerca</string>
|
||||
<string name="bluetooth_setting_disabled">Solo al añadir contactos</string>
|
||||
<string name="tor_network_setting">Conectar a través de Tor</string>
|
||||
<string name="tor_network_setting_never">Nunca</string>
|
||||
<string name="tor_network_setting_wifi">Solo con wifi</string>
|
||||
<string name="tor_network_setting_always">Con wifi o datos móviles</string>
|
||||
<!--Settings Security and Panic-->
|
||||
<string name="security_settings_title">Seguridad</string>
|
||||
<string name="change_password">Cambiar contraseña</string>
|
||||
@@ -349,4 +353,9 @@
|
||||
<string name="dev_report_saved">Informe guardado. Se enviará la próxima vez que inicies Briar.</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">Saliendo de Briar…</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">Filtro de pantalla detectado</string>
|
||||
<string name="screen_filter_body">Las siguientes aplicaciones pueden mostrarse por encima de otras:\n\n%1$s \n\nBriar no reaccionará a los toques mientras otra aplicación se muestre encima.
|
||||
Si experiencia algún problema, pruebe a desactivar esas aplicaciones cuando use Briar.\n</string>
|
||||
<string name="checkbox_dont_show_again">No mostrar más para estas aplicaciones</string>
|
||||
</resources>
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
<string name="dialog_message_lost_password">Votre compte Briar est enregistré chiffré sur votre appareil et non sur le \"cloud\", par conséquent, votre mot de passe ne peut être réinitialisé. Voulez-vous supprimer votre compte et démarrer à nouveau ?\n\nAttention : vos identités, contacts et messages seront définitivement perdus.</string>
|
||||
<string name="startup_failed_notification_title">Briar n\'a pas pu démarrer</string>
|
||||
<string name="startup_failed_notification_text">Vous devrez peut-être réinstaller Briar.</string>
|
||||
<string name="startup_failed_activity_title">Echec de démarrage de Briar.</string>
|
||||
<string name="startup_failed_activity_title">Échec de démarrage de Briar</string>
|
||||
<string name="startup_failed_db_error">Pour une raison indéterminée, votre base de donnée Briar est corrompue et irrécupérable. Vos comptes, données et contacts sont perdus. Vous devez malheureusement réinstaller Briar et configurer un nouveau compte.</string>
|
||||
<string name="startup_failed_service_error">Briar n\'a pas pu démarrer un module nécessaire. Réinstaller Briar résout généralement ce problème. Veuillez noter que vous perdrez votre compte et toutes les données associées puisque Briar n\'utilise pas de serveurs centralisés pour enregistrer les données.</string>
|
||||
<string name="expiry_warning">Ce logiciel est arrivé à expiration.\nVeuillez installer une version plus récente.</string>
|
||||
@@ -151,7 +151,6 @@
|
||||
<string name="groups_create_group_invitation_button">Envoyer invitation</string>
|
||||
<string name="groups_create_group_hint">Ajouter un nom au groupe privé</string>
|
||||
<string name="groups_invitation_sent">Invitation envoyée au groupe</string>
|
||||
<string name="groups_compose_message">Composer message</string>
|
||||
<string name="groups_message_sent">Message envoyé</string>
|
||||
<string name="groups_member_list">Liste de participants</string>
|
||||
<string name="groups_invite_members">Inviter des participants</string>
|
||||
@@ -182,6 +181,7 @@
|
||||
<string name="groups_invitations_response_declined_sent">Vous avez refusé l\'invitation du groupe de %s.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s a accepté l\'invitation du groupe.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s a décliné l\'invitation du groupe.</string>
|
||||
<string name="sharing_status_groups">Seul l\'initiateur du groupe peut inviter de nouveaux membres. Voici la liste des membres actuels.</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<string name="groups_reveal_contacts">Dévoiler les contacts</string>
|
||||
<string name="groups_reveal_dialog_message">Vous pouvez choisir de dévoiler les contacts à tous les membres actuels et futurs de ce groupe.\n\nDévoiler les contacts vous assure une connexion au groupe plus rapide et plus fiable car vous pouvez communiquer avec les contacts dévoilés même si l\'initiateur du groupe est hors ligne.</string>
|
||||
@@ -201,15 +201,10 @@
|
||||
<item quantity="one">%d message</item>
|
||||
<item quantity="other">%d messages</item>
|
||||
</plurals>
|
||||
<string name="forum_compose_post">Nouveau post de forum</string>
|
||||
<string name="forum_new_entry_posted">Entrée de forum postée</string>
|
||||
<string name="forum_new_message_hint">Nouvelle entrée</string>
|
||||
<string name="forum_message_reply_hint">Nouvelle réponse</string>
|
||||
<string name="btn_reply">Répondre</string>
|
||||
<plurals name="message_replies">
|
||||
<item quantity="one">%1$d réponse</item>
|
||||
<item quantity="other">%1$d réponses</item>
|
||||
</plurals>
|
||||
<string name="forum_leave">Quitter le forum</string>
|
||||
<string name="dialog_title_leave_forum">Confirmer la sortie du forum</string>
|
||||
<string name="dialog_message_leave_forum">Êtes-vous sûr de vouloir quitter ce forum ? Les contacts avec qui vous l\'avez partagé pourraient ne plus recevoir ses mises à jour.</string>
|
||||
@@ -236,6 +231,7 @@
|
||||
<string name="forum_invitation_response_accepted_received">%s a accepté l\'invitation au forum.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s a décliné l\'invitation au forum.</string>
|
||||
<string name="sharing_status">Etat de partage</string>
|
||||
<string name="sharing_status_forum">Tous les participants d\'un forum peuvent le partager avec leurs contacts. Vous partagez ce forum avec les contacts suivants. Il y a peut-être d\'autres participants que vous ne pouvez voir.</string>
|
||||
<string name="shared_with">Partagé avec %1$d (%2$d en ligne)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one">%d forum partagé par des contacts</item>
|
||||
@@ -273,6 +269,7 @@
|
||||
<string name="blogs_sharing_invitations_title">Invitations au blog</string>
|
||||
<string name="blogs_sharing_joined_toast">Abonné au Blog</string>
|
||||
<string name="blogs_sharing_declined_toast">Invitation au blog refusée</string>
|
||||
<string name="sharing_status_blog">Quiconque est abonné à un blog peut le partager avec ses contacts. Vous partagez ce blog avec les contacts suivants. Il y a peut-être d\'autres abonnés que vous ne pouvez voir.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">Import de flux RSS</string>
|
||||
<string name="blogs_rss_feeds_import_button">Importer</string>
|
||||
@@ -282,6 +279,9 @@
|
||||
<string name="blogs_rss_feeds_manage_imported">Importé :</string>
|
||||
<string name="blogs_rss_feeds_manage_author">Auteur :</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">Dernière mise à jour :</string>
|
||||
<string name="blogs_rss_remove_feed">Supprimer le flux</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">Êtes-vous sûr de vouloir supprimer ce flux et tous ses messages ?\nLes postes que vous avez partagés ne seront pas supprimés des appareils des autres personnes.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Supprimer le flux</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">Le flux n\'a pas pu être supprimé !</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">Vous n\'avez importé aucun flux RSS.\n\nPourquoi ne pas cliquer le plus dans le coin en haut à droite pour ajouter votre premier ?</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Problème lors du chargement de vos flux. Réessayer ultérieurement.</string>
|
||||
@@ -353,4 +353,9 @@
|
||||
<string name="dev_report_saved">Rapport enregistré. Il sera envoyé lors de votre prochaine connexion à Briar.</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">Déconnexion de Briar ...</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">Filtre d\'écran détecté</string>
|
||||
<string name="screen_filter_body">Les applications suivantes ont l\'autorisation de s\'afficher par dessus d\'autres :\n\n%1$s \n\nBriar ne répondra pas aux touches lorsqu\'une autre application s\'affiche par dessus.
|
||||
En cas de problèmes, essayer d\'arrêter ces applications durant l\'utilisation de Briar.\n</string>
|
||||
<string name="checkbox_dont_show_again">Ne plus m\'avertir à propos de ces applications</string>
|
||||
</resources>
|
||||
|
||||
@@ -124,7 +124,6 @@
|
||||
<string name="groups_create_group_invitation_button">Invia invito</string>
|
||||
<string name="groups_create_group_hint">Inserisci un nome per il tuo gruppo privato</string>
|
||||
<string name="groups_invitation_sent">Invito a partecipare al gruppo spedito</string>
|
||||
<string name="groups_compose_message">Scrivi un messaggio</string>
|
||||
<string name="groups_message_sent">Messaggio inviato</string>
|
||||
<string name="groups_member_list">Lista membri</string>
|
||||
<string name="groups_invite_members">Invita Membri</string>
|
||||
@@ -152,10 +151,6 @@
|
||||
<item quantity="other">%d post</item>
|
||||
</plurals>
|
||||
<string name="forum_message_reply_hint">Nuova Risposta</string>
|
||||
<plurals name="message_replies">
|
||||
<item quantity="one">risposta di %1$d</item>
|
||||
<item quantity="other">risposte di %1$d</item>
|
||||
</plurals>
|
||||
<string name="forum_leave">Lascia Forum</string>
|
||||
<string name="dialog_title_leave_forum">Conferma l\'abbandono del forum</string>
|
||||
<string name="dialog_button_leave">Lascia</string>
|
||||
@@ -261,4 +256,5 @@
|
||||
<string name="send_report">Invia report</string>
|
||||
<string name="close">Chiudi</string>
|
||||
<!--Sign Out-->
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
</resources>
|
||||
|
||||
@@ -151,7 +151,6 @@
|
||||
<string name="groups_create_group_invitation_button">Enviar Convite</string>
|
||||
<string name="groups_create_group_hint">Adicionar um nome para seu grupo privado</string>
|
||||
<string name="groups_invitation_sent">Convite do Grupo enviado </string>
|
||||
<string name="groups_compose_message">Escrever Mensagem</string>
|
||||
<string name="groups_message_sent">Mensagem enviada</string>
|
||||
<string name="groups_member_list">Lista de membros</string>
|
||||
<string name="groups_invite_members">Convidar membros</string>
|
||||
@@ -201,15 +200,10 @@
|
||||
<item quantity="one">%d Post</item>
|
||||
<item quantity="other">%d Posts</item>
|
||||
</plurals>
|
||||
<string name="forum_compose_post">Nova postagem em fórum</string>
|
||||
<string name="forum_new_entry_posted">postada com sucesso</string>
|
||||
<string name="forum_new_message_hint">Novo tópico</string>
|
||||
<string name="forum_message_reply_hint">Nova resposta</string>
|
||||
<string name="btn_reply">Responder</string>
|
||||
<plurals name="message_replies">
|
||||
<item quantity="one">%1$d resposta</item>
|
||||
<item quantity="other">%1$d respostas</item>
|
||||
</plurals>
|
||||
<string name="forum_leave">Sair do fórum</string>
|
||||
<string name="dialog_title_leave_forum">Confirmar saída do fórum</string>
|
||||
<string name="dialog_message_leave_forum">Você tem certeza que deseja sair deste fórum? Seus contatos com quem você o compartilhou podem deixar de receber notificações dele.</string>
|
||||
@@ -349,4 +343,5 @@
|
||||
<string name="dev_report_saved">Relatório salvo. Ele será enviado na próxima vez em que você entrar no Briar.</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">Saindo do Briar…</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
</resources>
|
||||
|
||||
@@ -151,7 +151,6 @@
|
||||
<string name="groups_create_group_invitation_button">Dërgoje Ftesën</string>
|
||||
<string name="groups_create_group_hint">Shtoni një emër për grupin tuaj privat</string>
|
||||
<string name="groups_invitation_sent">Ftesa e grupit u dërgua</string>
|
||||
<string name="groups_compose_message">Hartoni Mesazh</string>
|
||||
<string name="groups_message_sent">Mesazhi u dërgua</string>
|
||||
<string name="groups_member_list">Listë Anëtarësh</string>
|
||||
<string name="groups_invite_members">Ftoni Anëtarë</string>
|
||||
@@ -182,6 +181,7 @@
|
||||
<string name="groups_invitations_response_declined_sent">Hodhët poshtë ftesën e grupit nga %s.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s e pranoi ftesën e grupit.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s e hodhi poshtë ftesën e grupit.</string>
|
||||
<string name="sharing_status_groups">Vetëm krijuesi mund të ftojë anëtarë të rinj në grup. Më poshtë gjenden tërë anëtarët e tanishëm të grupit.</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<string name="groups_reveal_contacts">Shfaqua Kontaktet</string>
|
||||
<string name="groups_reveal_dialog_message">Mund të zgjidhni t’ua shfaqni ose jo kontaktet krejt anëtarëve të tanishëm dhe të ardhshëm të këtij grupi.\n\nShfaqja e kontakteve e bën lidhjen tuaj me grupin më të shpejtë dhe më të besueshme, ngaqë mund të komunikoni me kontaktet e shfaqura edhe kur krijuesi i grupit s’është në linjë.</string>
|
||||
@@ -201,15 +201,10 @@
|
||||
<item quantity="one">%d postim</item>
|
||||
<item quantity="other">%d postime</item>
|
||||
</plurals>
|
||||
<string name="forum_compose_post">Postim i Ri Forumi</string>
|
||||
<string name="forum_new_entry_posted">U postua zë forumi</string>
|
||||
<string name="forum_new_message_hint">Zë i Ri</string>
|
||||
<string name="forum_message_reply_hint">Përgjigje e Re</string>
|
||||
<string name="btn_reply">Përgjigju</string>
|
||||
<plurals name="message_replies">
|
||||
<item quantity="one">%1$d përgjigje</item>
|
||||
<item quantity="other">%1$d përgjigje</item>
|
||||
</plurals>
|
||||
<string name="forum_leave">Braktiseni Forumin</string>
|
||||
<string name="dialog_title_leave_forum">Ripohoni Braktisjen e Forumit</string>
|
||||
<string name="dialog_message_leave_forum">Jeni i sigurt se doni ta braktisni këtë forum? Kontaktet me të cilët e keni ndarë këtë forum mundet të mbeten jashtë marrjes së përditësimeve nga ky forum.</string>
|
||||
@@ -236,6 +231,7 @@
|
||||
<string name="forum_invitation_response_accepted_received">%s pranoi ftesën e forumit.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s e hodhi poshtë ftesën e forumit.</string>
|
||||
<string name="sharing_status">Gjendje Ndarjeje Me të Tjerë</string>
|
||||
<string name="sharing_status_forum">Cilido anëtar i grupit mund ta ndajë me të tjerët. Këtë forum po e ndani me kontaktet vijuese. Mund të ketë edhe anëtarë të tjerët, të cilët s’mund t’i shihni.</string>
|
||||
<string name="shared_with">E ndarë me %1$d (%2$d në linjë)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one">%d forum ndarë nga kontakte</item>
|
||||
@@ -273,6 +269,7 @@
|
||||
<string name="blogs_sharing_invitations_title">Ftesa Blogu</string>
|
||||
<string name="blogs_sharing_joined_toast">U pajtuat te Blogu</string>
|
||||
<string name="blogs_sharing_declined_toast">Ftesa e Blogut u Hodh Poshtë</string>
|
||||
<string name="sharing_status_blog">Cilido që pajtohet te një blog mund ta ndajë atë me kontaktet e veta. Këtë blog po e ndani me kontaktet vijuese. Mund të ketë edhe pajtimtarë të tjerë, të cilët s’mund t’i shihni.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">Importoni Prurje RSS</string>
|
||||
<string name="blogs_rss_feeds_import_button">Importo</string>
|
||||
@@ -282,6 +279,9 @@
|
||||
<string name="blogs_rss_feeds_manage_imported">Të importuara:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">Autor:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">Përditësuar Së Fundi:</string>
|
||||
<string name="blogs_rss_remove_feed">Hiqe Prurjen</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">Jeni i sigurt se doni të hiqet kjo prurje dhe krejt postimet e saj?\nÇfarëdo postimi që keni ndarë me të tjerët, nuk do të hiqet nga pajisjet e personave të tjerë.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Hiqe Prurjen</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">S’u fshi dot prurja!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">S’keni importuar ende ndonjë prurje RSS.\n\nPse nuk klikoni mbi shenjën plus në cepin e sipërm djathtas që të shtoni të parën tuaj?</string>
|
||||
<string name="blogs_rss_feeds_manage_error">Pati një problem me ngarkimin e prurjeve tuaja. Ju lutemi, riprovoni më vonë.</string>
|
||||
@@ -353,4 +353,9 @@
|
||||
<string name="dev_report_saved">Njoftimi u ruajt. Do të dërgohet herën tjetër që do të hyni në Briar.</string>
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">Po dilet nga Briar-i…</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">U pikas filtër ekrani</string>
|
||||
<string name="screen_filter_body">Aplikacionet vijuese kanë leje të vizatojnë përmbi aplikacione të tjera:\n\n%1$s \n\nBriar-i nuk do të reagojë ndaj prekjesh, kur mbi të po vizaton një tjetër aplikacion.
|
||||
Nëse hasni probleme, provoni t’i mbyllni këto aplikacione, kur përdorni Briar-in.\n</string>
|
||||
<string name="checkbox_dont_show_again">Mos më sinjalizo më për këto aplikacione</string>
|
||||
</resources>
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
<enum name="reblogger" value="1"/>
|
||||
<enum name="commenter" value="2"/>
|
||||
<enum name="list" value="3"/>
|
||||
<enum name="rss_feed" value="4"/>
|
||||
<enum name="rss_feed_reblogged" value="5"/>
|
||||
</attr>
|
||||
</declare-styleable>
|
||||
|
||||
|
||||
@@ -33,11 +33,11 @@
|
||||
|
||||
<!-- this is needed as preference_category_material layout uses this color as the text color -->
|
||||
<color name="preference_fallback_accent_color">@color/briar_accent</color>
|
||||
|
||||
<color name="thread_indicator">#9e9e9e</color>
|
||||
<color name="divider">#c1c1c1</color>
|
||||
<color name="default_separator_inverted">#ffffff</color>
|
||||
<color name="menu_background">#FFFFFF</color>
|
||||
|
||||
<color name="spinner_border">#61000000</color> <!-- 38% Black -->
|
||||
<color name="forum_discussion_nested_line">#cfd2d4</color>
|
||||
<color name="forum_cell_highlight">#ffffff</color>
|
||||
</resources>
|
||||
@@ -162,7 +162,6 @@
|
||||
<string name="groups_create_group_invitation_button">Send Invitation</string>
|
||||
<string name="groups_create_group_hint">Add a name for your private group</string>
|
||||
<string name="groups_invitation_sent">Group invitation has been sent</string>
|
||||
<string name="groups_compose_message">Compose Message</string>
|
||||
<string name="groups_message_sent">Message sent</string>
|
||||
<string name="groups_member_list">Member List</string>
|
||||
<string name="groups_invite_members">Invite Members</string>
|
||||
@@ -194,6 +193,7 @@
|
||||
<string name="groups_invitations_response_declined_sent">You declined the group invitation from %s.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%s accepted the group invitation.</string>
|
||||
<string name="groups_invitations_response_declined_received">%s declined the group invitation.</string>
|
||||
<string name="sharing_status_groups">Only the creator can invite new members to the group. Below are all current members of the group.</string>
|
||||
|
||||
<!-- Private Groups Revealing Contacts -->
|
||||
<string name="groups_reveal_contacts">Reveal Contacts</string>
|
||||
@@ -215,15 +215,10 @@
|
||||
<item quantity="one">%d post</item>
|
||||
<item quantity="other">%d posts</item>
|
||||
</plurals>
|
||||
<string name="forum_compose_post">New Forum Post</string>
|
||||
<string name="forum_new_entry_posted">Forum entry posted</string>
|
||||
<string name="forum_new_message_hint">New Entry</string>
|
||||
<string name="forum_message_reply_hint">New Reply</string>
|
||||
<string name="btn_reply">Reply</string>
|
||||
<plurals name="message_replies">
|
||||
<item quantity="one">%1$d reply</item>
|
||||
<item quantity="other">%1$d replies</item>
|
||||
</plurals>
|
||||
<string name="forum_leave">Leave Forum</string>
|
||||
<string name="dialog_title_leave_forum">Confirm Leaving Forum</string>
|
||||
<string name="dialog_message_leave_forum">Are you sure that you want to leave this forum? Contacts you have shared this forum with might get cut off from receiving updates for this forum.</string>
|
||||
@@ -252,6 +247,7 @@
|
||||
<string name="forum_invitation_response_declined_received">%s declined the forum invitation.</string>
|
||||
|
||||
<string name="sharing_status">Sharing Status</string>
|
||||
<string name="sharing_status_forum">Any member of a forum can share it with their contacts. You are sharing this forum with the following contacts. There may also be other members who you can\'t see.</string>
|
||||
<string name="shared_with">Shared with %1$d (%2$d online)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one">%d forum shared by contacts</item>
|
||||
@@ -291,6 +287,7 @@
|
||||
<string name="blogs_sharing_invitations_title">Blog Invitations</string>
|
||||
<string name="blogs_sharing_joined_toast">Subscribed to Blog</string>
|
||||
<string name="blogs_sharing_declined_toast">Blog Invitation Declined</string>
|
||||
<string name="sharing_status_blog">Anyone who subscribes to a blog can share it with their contacts. You are sharing this blog with the following contacts. There may also be other subscribers who you can\'t see.</string>
|
||||
|
||||
<!-- RSS Feeds -->
|
||||
<string name="blogs_rss_feeds_import">Import RSS Feed</string>
|
||||
@@ -301,6 +298,9 @@
|
||||
<string name="blogs_rss_feeds_manage_imported">Imported:</string>
|
||||
<string name="blogs_rss_feeds_manage_author">Author:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">Last Updated:</string>
|
||||
<string name="blogs_rss_remove_feed">Remove Feed</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">Are you sure you want to remove this feed and all its posts?\nAny posts you have shared will not be removed from other people\'s devices.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">Remove Feed</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">The feed could not be deleted!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">You haven\'t imported any RSS feeds.\n\nWhy don\'t you click the plus in the top right screen corner to add your first?</string>
|
||||
<string name="blogs_rss_feeds_manage_error">There was a problem loading your feeds. Please try again later.</string>
|
||||
|
||||
@@ -102,7 +102,7 @@
|
||||
|
||||
<style name="DiscussionLevelIndicator">
|
||||
<item name="android:layout_marginLeft">4dp</item>
|
||||
<item name="android:background">@color/divider</item>
|
||||
<item name="android:background">@color/thread_indicator</item>
|
||||
</style>
|
||||
|
||||
<style name="BriarTabLayout" parent="Widget.Design.TabLayout">
|
||||
|
||||
@@ -5,14 +5,8 @@
|
||||
<item name="colorPrimary">@color/briar_primary</item>
|
||||
<item name="colorPrimaryDark">@color/briar_primary_dark</item>
|
||||
<item name="colorAccent">@color/briar_accent</item>
|
||||
<item name="android:windowBackground">@color/window_background</item>
|
||||
<item name="android:textColorPrimary">@color/briar_text_primary</item>
|
||||
<item name="android:textColorPrimaryInverse">@color/briar_text_primary_inverse</item>
|
||||
<item name="android:textColorSecondary">@color/briar_text_secondary</item>
|
||||
<item name="android:textColorSecondaryInverse">@color/briar_text_secondary_inverse</item>
|
||||
<item name="android:textColorTertiary">@color/briar_text_tertiary</item>
|
||||
<item name="android:textColorTertiaryInverse">@color/briar_text_tertiary_inverse</item>
|
||||
<item name="android:textColorLink">@color/briar_text_link</item>
|
||||
<item name="android:windowBackground">@color/window_background</item>
|
||||
<item name="android:windowAnimationStyle">@style/ActivityAnimation</item>
|
||||
<item name="android:filterTouchesWhenObscured">true</item>
|
||||
|
||||
@@ -40,12 +34,6 @@
|
||||
<item name="colorAccent">@color/briar_accent</item>
|
||||
<item name="buttonBarPositiveButtonStyle">@style/BriarButtonFlat.Positive</item>
|
||||
<item name="buttonBarNegativeButtonStyle">@style/BriarButtonFlat.Negative</item>
|
||||
<item name="android:textColorPrimary">@color/briar_text_primary</item>
|
||||
<item name="android:textColorPrimaryInverse">@color/briar_text_primary_inverse</item>
|
||||
<item name="android:textColorSecondary">@color/briar_text_secondary</item>
|
||||
<item name="android:textColorSecondaryInverse">@color/briar_text_secondary_inverse</item>
|
||||
<item name="android:textColorTertiary">@color/briar_text_tertiary</item>
|
||||
<item name="android:textColorTertiaryInverse">@color/briar_text_tertiary_inverse</item>
|
||||
<item name="android:textColorLink">@color/briar_text_link</item>
|
||||
<item name="android:windowAnimationStyle">@style/DialogAnimation</item>
|
||||
<item name="android:filterTouchesWhenObscured">true</item>
|
||||
|
||||
@@ -13,6 +13,8 @@ import org.briarproject.briar.BuildConfig;
|
||||
import org.briarproject.briar.android.TestBriarApplication;
|
||||
import org.briarproject.briar.android.controller.handler.UiResultExceptionHandler;
|
||||
import org.briarproject.briar.android.threaded.ThreadItemAdapter;
|
||||
import org.briarproject.briar.android.threaded.ThreadItemList;
|
||||
import org.briarproject.briar.android.threaded.ThreadItemListImpl;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.junit.runner.RunWith;
|
||||
@@ -23,10 +25,7 @@ import org.robolectric.Robolectric;
|
||||
import org.robolectric.RobolectricGradleTestRunner;
|
||||
import org.robolectric.annotation.Config;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
@@ -81,7 +80,7 @@ public class ForumActivityTest {
|
||||
|
||||
private TestForumActivity forumActivity;
|
||||
@Captor
|
||||
private ArgumentCaptor<UiResultExceptionHandler<Collection<ForumItem>, DbException>>
|
||||
private ArgumentCaptor<UiResultExceptionHandler<ThreadItemList<ForumItem>, DbException>>
|
||||
rc;
|
||||
|
||||
@Before
|
||||
@@ -93,7 +92,7 @@ public class ForumActivityTest {
|
||||
.withIntent(intent).create().resume().get();
|
||||
}
|
||||
|
||||
private List<ForumItem> getDummyData() {
|
||||
private ThreadItemList<ForumItem> getDummyData() {
|
||||
ForumItem[] forumItems = new ForumItem[6];
|
||||
for (int i = 0; i < forumItems.length; i++) {
|
||||
AuthorId authorId = new AuthorId(TestUtils.getRandomId());
|
||||
@@ -103,13 +102,15 @@ public class ForumActivityTest {
|
||||
AUTHORS[i], System.currentTimeMillis(), author, UNKNOWN);
|
||||
forumItems[i].setLevel(LEVELS[i]);
|
||||
}
|
||||
return new ArrayList<>(Arrays.asList(forumItems));
|
||||
ThreadItemList<ForumItem> list = new ThreadItemListImpl<>();
|
||||
list.addAll(Arrays.asList(forumItems));
|
||||
return list;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testNestedEntries() {
|
||||
ForumController mc = forumActivity.getController();
|
||||
List<ForumItem> dummyData = getDummyData();
|
||||
ThreadItemList<ForumItem> dummyData = getDummyData();
|
||||
verify(mc, times(1)).loadItems(rc.capture());
|
||||
rc.getValue().onResult(dummyData);
|
||||
ThreadItemAdapter<ForumItem> adapter = forumActivity.getAdapter();
|
||||
|
||||
@@ -13,16 +13,22 @@ import javax.annotation.concurrent.Immutable;
|
||||
public class Blog extends BaseGroup implements Shareable {
|
||||
|
||||
private final Author author;
|
||||
private final boolean rssFeed;
|
||||
|
||||
public Blog(Group group, Author author) {
|
||||
public Blog(Group group, Author author, boolean rssFeed) {
|
||||
super(group);
|
||||
this.author = author;
|
||||
this.rssFeed = rssFeed;
|
||||
}
|
||||
|
||||
public Author getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public boolean isRssFeed() {
|
||||
return rssFeed;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean equals(Object o) {
|
||||
return o instanceof Blog && super.equals(o);
|
||||
|
||||
@@ -26,7 +26,7 @@ public class BlogCommentHeader extends BlogPostHeader {
|
||||
Status authorStatus, boolean read) {
|
||||
|
||||
super(type, groupId, id, parent.getId(), timestamp,
|
||||
timeReceived, author, authorStatus, read);
|
||||
timeReceived, author, authorStatus, false, read);
|
||||
|
||||
if (type != COMMENT && type != WRAPPED_COMMENT)
|
||||
throw new IllegalArgumentException("Incompatible Message Type");
|
||||
@@ -43,4 +43,11 @@ public class BlogCommentHeader extends BlogPostHeader {
|
||||
public BlogPostHeader getParent() {
|
||||
return parent;
|
||||
}
|
||||
|
||||
public BlogPostHeader getRootPost() {
|
||||
if (parent instanceof BlogCommentHeader)
|
||||
return ((BlogCommentHeader) parent).getRootPost();
|
||||
return parent;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -28,6 +28,7 @@ public interface BlogConstants {
|
||||
String KEY_AUTHOR_NAME = "name";
|
||||
String KEY_PUBLIC_KEY = "publicKey";
|
||||
String KEY_AUTHOR = "author";
|
||||
String KEY_RSS_FEED = "rssFeed";
|
||||
String KEY_READ = "read";
|
||||
String KEY_COMMENT = "comment";
|
||||
String KEY_ORIGINAL_MSG_ID = "originalMessageId";
|
||||
|
||||
@@ -13,6 +13,11 @@ public interface BlogFactory {
|
||||
*/
|
||||
Blog createBlog(Author author);
|
||||
|
||||
/**
|
||||
* Creates a RSS feed blog for a given author.
|
||||
*/
|
||||
Blog createFeedBlog(Author author);
|
||||
|
||||
/**
|
||||
* Parses a blog with the given Group
|
||||
*/
|
||||
|
||||
@@ -34,13 +34,18 @@ public interface BlogManager {
|
||||
/**
|
||||
* Returns true if a blog can be removed.
|
||||
*/
|
||||
boolean canBeRemoved(GroupId g) throws DbException;
|
||||
boolean canBeRemoved(Blog b) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes and deletes a blog.
|
||||
*/
|
||||
void removeBlog(Blog b) throws DbException;
|
||||
|
||||
/**
|
||||
* Removes and deletes a blog with the given {@link Transaction}.
|
||||
*/
|
||||
void removeBlog(Transaction txn, Blog b) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores a local blog post.
|
||||
*/
|
||||
@@ -55,7 +60,7 @@ public interface BlogManager {
|
||||
* Adds a comment to an existing blog post or reblogs it.
|
||||
*/
|
||||
void addLocalComment(LocalAuthor author, GroupId groupId,
|
||||
@Nullable String comment, BlogPostHeader wHeader)
|
||||
@Nullable String comment, BlogPostHeader parentHeader)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
|
||||
@@ -25,7 +25,8 @@ public interface BlogPostFactory {
|
||||
throws FormatException, GeneralSecurityException;
|
||||
|
||||
Message createBlogComment(GroupId groupId, LocalAuthor author,
|
||||
@Nullable String comment, MessageId originalId, MessageId wrappedId)
|
||||
@Nullable String comment, MessageId parentOriginalId,
|
||||
MessageId parentCurrentId)
|
||||
throws FormatException, GeneralSecurityException;
|
||||
|
||||
/**
|
||||
@@ -44,11 +45,11 @@ public interface BlogPostFactory {
|
||||
* Wraps a blog comment
|
||||
*/
|
||||
Message wrapComment(GroupId groupId, byte[] descriptor, long timestamp,
|
||||
BdfList body, MessageId currentId) throws FormatException;
|
||||
BdfList body, MessageId parentCurrentId) throws FormatException;
|
||||
|
||||
/**
|
||||
* Re-wraps a previously wrapped comment
|
||||
*/
|
||||
Message rewrapWrappedComment(GroupId groupId, BdfList body,
|
||||
MessageId currentId) throws FormatException;
|
||||
MessageId parentCurrentId) throws FormatException;
|
||||
}
|
||||
|
||||
@@ -17,21 +17,23 @@ public class BlogPostHeader extends PostHeader {
|
||||
private final MessageType type;
|
||||
private final GroupId groupId;
|
||||
private final long timeReceived;
|
||||
private final boolean rssFeed;
|
||||
|
||||
public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
|
||||
@Nullable MessageId parentId, long timestamp, long timeReceived,
|
||||
Author author, Status authorStatus, boolean read) {
|
||||
Author author, Status authorStatus, boolean rssFeed, boolean read) {
|
||||
super(id, parentId, timestamp, author, authorStatus, read);
|
||||
this.type = type;
|
||||
this.groupId = groupId;
|
||||
this.timeReceived = timeReceived;
|
||||
this.rssFeed = rssFeed;
|
||||
}
|
||||
|
||||
public BlogPostHeader(MessageType type, GroupId groupId, MessageId id,
|
||||
long timestamp, long timeReceived, Author author,
|
||||
Status authorStatus, boolean read) {
|
||||
Status authorStatus, boolean rssFeed, boolean read) {
|
||||
this(type, groupId, id, null, timestamp, timeReceived, author,
|
||||
authorStatus, read);
|
||||
authorStatus, rssFeed, read);
|
||||
}
|
||||
|
||||
public MessageType getType() {
|
||||
@@ -45,4 +47,9 @@ public class BlogPostHeader extends PostHeader {
|
||||
public long getTimeReceived() {
|
||||
return timeReceived;
|
||||
}
|
||||
|
||||
public boolean isRssFeed() {
|
||||
return rssFeed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -13,18 +13,18 @@ public abstract class BaseMessageHeader {
|
||||
private final MessageId id;
|
||||
private final GroupId groupId;
|
||||
private final long timestamp;
|
||||
private final boolean local, read, sent, seen;
|
||||
private final boolean local, sent, seen, read;
|
||||
|
||||
public BaseMessageHeader(MessageId id, GroupId groupId, long timestamp,
|
||||
boolean local, boolean read, boolean sent, boolean seen) {
|
||||
boolean local, boolean sent, boolean seen, boolean read) {
|
||||
|
||||
this.id = id;
|
||||
this.groupId = groupId;
|
||||
this.timestamp = timestamp;
|
||||
this.local = local;
|
||||
this.read = read;
|
||||
this.sent = sent;
|
||||
this.seen = seen;
|
||||
this.read = read;
|
||||
}
|
||||
|
||||
public MessageId getId() {
|
||||
@@ -43,10 +43,6 @@ public abstract class BaseMessageHeader {
|
||||
return local;
|
||||
}
|
||||
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
public boolean isSent() {
|
||||
return sent;
|
||||
}
|
||||
@@ -55,4 +51,8 @@ public abstract class BaseMessageHeader {
|
||||
return seen;
|
||||
}
|
||||
|
||||
public boolean isRead() {
|
||||
return read;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -7,6 +7,8 @@ import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.bramble.api.sync.Message;
|
||||
import org.briarproject.bramble.api.sync.MessageId;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
@NotNullByDefault
|
||||
public interface MessageTracker {
|
||||
|
||||
@@ -38,6 +40,19 @@ public interface MessageTracker {
|
||||
void trackMessage(Transaction txn, GroupId g, long timestamp, boolean read)
|
||||
throws DbException;
|
||||
|
||||
/**
|
||||
* Loads the stored message id for the respective group id or returns null
|
||||
* if none is available.
|
||||
*/
|
||||
@Nullable
|
||||
MessageId loadStoredMessageId(GroupId g) throws DbException;
|
||||
|
||||
/**
|
||||
* Stores the message id for the respective group id. Exactly one message id
|
||||
* can be stored for any group id at any time, older values are overwritten.
|
||||
*/
|
||||
void storeMessageId(GroupId g, MessageId m) throws DbException;
|
||||
|
||||
/**
|
||||
* Marks a message as read or unread and updates the group count.
|
||||
*/
|
||||
|
||||
@@ -1,40 +1,31 @@
|
||||
package org.briarproject.briar.api.feed;
|
||||
|
||||
import org.briarproject.bramble.api.FormatException;
|
||||
import org.briarproject.bramble.api.data.BdfDictionary;
|
||||
import org.briarproject.bramble.api.data.BdfEntry;
|
||||
import org.briarproject.bramble.api.identity.LocalAuthor;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
import org.briarproject.briar.api.blog.Blog;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_BLOG_GROUP_ID;
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_ADDED;
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_AUTHOR;
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_DESC;
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_LAST_ENTRY;
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_TITLE;
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_UPDATED;
|
||||
import static org.briarproject.briar.api.feed.FeedConstants.KEY_FEED_URL;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
public class Feed {
|
||||
|
||||
private final String url;
|
||||
private final GroupId blogId;
|
||||
private final Blog blog;
|
||||
private final LocalAuthor localAuthor;
|
||||
@Nullable
|
||||
private final String title, description, author;
|
||||
private final String description, author;
|
||||
private final long added, updated, lastEntryTime;
|
||||
|
||||
public Feed(String url, GroupId blogId, @Nullable String title,
|
||||
@Nullable String description, @Nullable String author,
|
||||
long added, long updated, long lastEntryTime) {
|
||||
public Feed(String url, Blog blog, LocalAuthor localAuthor,
|
||||
@Nullable String description, @Nullable String author, long added,
|
||||
long updated, long lastEntryTime) {
|
||||
|
||||
this.url = url;
|
||||
this.blogId = blogId;
|
||||
this.title = title;
|
||||
this.blog = blog;
|
||||
this.localAuthor = localAuthor;
|
||||
this.description = description;
|
||||
this.author = author;
|
||||
this.added = added;
|
||||
@@ -42,13 +33,13 @@ public class Feed {
|
||||
this.lastEntryTime = lastEntryTime;
|
||||
}
|
||||
|
||||
public Feed(String url, GroupId blogId, @Nullable String title,
|
||||
public Feed(String url, Blog blog, LocalAuthor localAuthor,
|
||||
@Nullable String description, @Nullable String author, long added) {
|
||||
this(url, blogId, title, description, author, added, 0L, 0L);
|
||||
this(url, blog, localAuthor, description, author, added, 0L, 0L);
|
||||
}
|
||||
|
||||
public Feed(String url, GroupId blogId, long added) {
|
||||
this(url, blogId, null, null, null, added, 0L, 0L);
|
||||
public Feed(String url, Blog blog, LocalAuthor localAuthor, long added) {
|
||||
this(url, blog, localAuthor, null, null, added, 0L, 0L);
|
||||
}
|
||||
|
||||
public String getUrl() {
|
||||
@@ -56,39 +47,19 @@ public class Feed {
|
||||
}
|
||||
|
||||
public GroupId getBlogId() {
|
||||
return blogId;
|
||||
return blog.getId();
|
||||
}
|
||||
|
||||
public BdfDictionary toBdfDictionary() {
|
||||
BdfDictionary d = BdfDictionary.of(
|
||||
new BdfEntry(KEY_FEED_URL, url),
|
||||
new BdfEntry(KEY_BLOG_GROUP_ID, blogId.getBytes()),
|
||||
new BdfEntry(KEY_FEED_ADDED, added),
|
||||
new BdfEntry(KEY_FEED_UPDATED, updated),
|
||||
new BdfEntry(KEY_FEED_LAST_ENTRY, lastEntryTime)
|
||||
);
|
||||
if (title != null) d.put(KEY_FEED_TITLE, title);
|
||||
if (description != null) d.put(KEY_FEED_DESC, description);
|
||||
if (author != null) d.put(KEY_FEED_AUTHOR, author);
|
||||
return d;
|
||||
public Blog getBlog() {
|
||||
return blog;
|
||||
}
|
||||
|
||||
public static Feed from(BdfDictionary d) throws FormatException {
|
||||
String url = d.getString(KEY_FEED_URL);
|
||||
GroupId blogId = new GroupId(d.getRaw(KEY_BLOG_GROUP_ID));
|
||||
String title = d.getOptionalString(KEY_FEED_TITLE);
|
||||
String desc = d.getOptionalString(KEY_FEED_DESC);
|
||||
String author = d.getOptionalString(KEY_FEED_AUTHOR);
|
||||
long added = d.getLong(KEY_FEED_ADDED, 0L);
|
||||
long updated = d.getLong(KEY_FEED_UPDATED, 0L);
|
||||
long lastEntryTime = d.getLong(KEY_FEED_LAST_ENTRY, 0L);
|
||||
return new Feed(url, blogId, title, desc, author, added, updated,
|
||||
lastEntryTime);
|
||||
public LocalAuthor getLocalAuthor() {
|
||||
return localAuthor;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public String getTitle() {
|
||||
return title;
|
||||
return blog.getName();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
@@ -118,20 +89,9 @@ public class Feed {
|
||||
if (this == o) return true;
|
||||
if (o instanceof Feed) {
|
||||
Feed f = (Feed) o;
|
||||
return url.equals(f.url) && blogId.equals(f.getBlogId()) &&
|
||||
equalsWithNull(title, f.getTitle()) &&
|
||||
equalsWithNull(description, f.getDescription()) &&
|
||||
equalsWithNull(author, f.getAuthor()) &&
|
||||
added == f.getAdded() &&
|
||||
updated == f.getUpdated() &&
|
||||
lastEntryTime == f.getLastEntryTime();
|
||||
return blog.equals(f.blog);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean equalsWithNull(@Nullable Object a, @Nullable Object b) {
|
||||
if (a == b) return true;
|
||||
if (a == null || b == null) return false;
|
||||
return a.equals(b);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,8 +18,9 @@ public interface FeedConstants {
|
||||
// group metadata keys
|
||||
String KEY_FEEDS = "feeds";
|
||||
String KEY_FEED_URL = "feedURL";
|
||||
String KEY_BLOG_GROUP_ID = "blogGroupId";
|
||||
String KEY_FEED_TITLE = "feedTitle";
|
||||
String KEY_BLOG_TITLE = "blogTitle";
|
||||
String KEY_PUBLIC_KEY = "publicKey";
|
||||
String KEY_PRIVATE_KEY = "privateKey";
|
||||
String KEY_FEED_DESC = "feedDesc";
|
||||
String KEY_FEED_AUTHOR = "feedAuthor";
|
||||
String KEY_FEED_ADDED = "feedAdded";
|
||||
|
||||
@@ -3,7 +3,6 @@ package org.briarproject.briar.api.feed;
|
||||
import org.briarproject.bramble.api.db.DbException;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.api.sync.ClientId;
|
||||
import org.briarproject.bramble.api.sync.GroupId;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
@@ -17,14 +16,14 @@ public interface FeedManager {
|
||||
ClientId CLIENT_ID = new ClientId("org.briarproject.briar.feed");
|
||||
|
||||
/**
|
||||
* Adds an RSS feed.
|
||||
* Adds an RSS feed as a new dedicated blog.
|
||||
*/
|
||||
void addFeed(String url, GroupId g) throws DbException, IOException;
|
||||
void addFeed(String url) throws DbException, IOException;
|
||||
|
||||
/**
|
||||
* Removes an RSS feed.
|
||||
*/
|
||||
void removeFeed(String url) throws DbException;
|
||||
void removeFeed(Feed feed) throws DbException;
|
||||
|
||||
/**
|
||||
* Returns a list of all added RSS feeds
|
||||
|
||||
@@ -22,7 +22,7 @@ public class IntroductionMessage extends BaseMessageHeader {
|
||||
GroupId groupId, int role, long time, boolean local, boolean sent,
|
||||
boolean seen, boolean read) {
|
||||
|
||||
super(messageId, groupId, time, local, read, sent, seen);
|
||||
super(messageId, groupId, time, local, sent, seen, read);
|
||||
this.sessionId = sessionId;
|
||||
this.messageId = messageId;
|
||||
this.role = role;
|
||||
|
||||
@@ -14,7 +14,7 @@ public class PrivateMessageHeader extends BaseMessageHeader {
|
||||
public PrivateMessageHeader(MessageId id, GroupId groupId, long timestamp,
|
||||
boolean local, boolean read, boolean sent, boolean seen) {
|
||||
|
||||
super(id, groupId, timestamp, local, read, sent, seen);
|
||||
super(id, groupId, timestamp, local, sent, seen, read);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ public class InvitationMessage extends BaseMessageHeader {
|
||||
boolean local, boolean sent, boolean seen, boolean read,
|
||||
SessionId sessionId, ContactId contactId) {
|
||||
|
||||
super(id, groupId, time, local, read, sent, seen);
|
||||
super(id, groupId, time, local, sent, seen, read);
|
||||
this.sessionId = sessionId;
|
||||
this.contactId = contactId;
|
||||
}
|
||||
|
||||
@@ -2,6 +2,7 @@ package org.briarproject.briar;
|
||||
|
||||
import org.briarproject.briar.blog.BlogModule;
|
||||
import org.briarproject.briar.client.BriarClientModule;
|
||||
import org.briarproject.briar.feed.DnsModule;
|
||||
import org.briarproject.briar.feed.FeedModule;
|
||||
import org.briarproject.briar.forum.ForumModule;
|
||||
import org.briarproject.briar.introduction.IntroductionModule;
|
||||
@@ -16,6 +17,7 @@ import dagger.Module;
|
||||
BlogModule.class,
|
||||
BriarClientModule.class,
|
||||
FeedModule.class,
|
||||
DnsModule.class,
|
||||
ForumModule.class,
|
||||
GroupInvitationModule.class,
|
||||
IntroductionModule.class,
|
||||
|
||||
@@ -14,6 +14,9 @@ import org.briarproject.briar.api.blog.BlogFactory;
|
||||
import javax.annotation.concurrent.Immutable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
|
||||
@Immutable
|
||||
@NotNullByDefault
|
||||
class BlogFactoryImpl implements BlogFactory {
|
||||
@@ -33,28 +36,45 @@ class BlogFactoryImpl implements BlogFactory {
|
||||
|
||||
@Override
|
||||
public Blog createBlog(Author a) {
|
||||
return createBlog(a, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blog createFeedBlog(Author a) {
|
||||
return createBlog(a, true);
|
||||
}
|
||||
|
||||
private Blog createBlog(Author a, boolean rssFeed) {
|
||||
try {
|
||||
BdfList blog = BdfList.of(
|
||||
a.getName(),
|
||||
a.getPublicKey()
|
||||
a.getPublicKey(),
|
||||
rssFeed
|
||||
);
|
||||
byte[] descriptor = clientHelper.toByteArray(blog);
|
||||
Group g = groupFactory
|
||||
.createGroup(BlogManagerImpl.CLIENT_ID, descriptor);
|
||||
return new Blog(g, a);
|
||||
return new Blog(g, a, rssFeed);
|
||||
} catch (FormatException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Blog parseBlog(Group g) throws FormatException {
|
||||
byte[] descriptor = g.getDescriptor();
|
||||
// Author Name, Public Key
|
||||
public Blog parseBlog(Group group) throws FormatException {
|
||||
byte[] descriptor = group.getDescriptor();
|
||||
// Author name, public key, RSS feed
|
||||
BdfList blog = clientHelper.toList(descriptor);
|
||||
Author a =
|
||||
authorFactory.createAuthor(blog.getString(0), blog.getRaw(1));
|
||||
return new Blog(g, a);
|
||||
String name = blog.getString(0);
|
||||
if (name.length() > MAX_AUTHOR_NAME_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
byte[] publicKey = blog.getRaw(1);
|
||||
if (publicKey.length > MAX_PUBLIC_KEY_LENGTH)
|
||||
throw new IllegalArgumentException();
|
||||
|
||||
Author author = authorFactory.createAuthor(name, publicKey);
|
||||
boolean rssFeed = blog.getBoolean(2);
|
||||
return new Blog(group, author, rssFeed);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user