mirror of
https://code.briarproject.org/briar/briar.git
synced 2026-02-12 02:39:05 +01:00
Compare commits
16 Commits
release-1.
...
release-1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
183f0c5f31 | ||
|
|
34c5aaae0a | ||
|
|
5531355ebd | ||
|
|
b9e607744a | ||
|
|
9dae3d191a | ||
|
|
20422edf78 | ||
|
|
f8bc5f08bf | ||
|
|
9434495d70 | ||
|
|
bf9e91fcf5 | ||
|
|
d9d86206a6 | ||
|
|
b410b8efcc | ||
|
|
39aa2d96b3 | ||
|
|
21dae824a6 | ||
|
|
cfdbd29cb4 | ||
|
|
4df335ebd3 | ||
|
|
682bee1486 |
@@ -1,15 +1,15 @@
|
||||
image: registry.gitlab.com/fdroid/ci-images-base:latest
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .gradle/wrapper
|
||||
- .gradle/caches
|
||||
|
||||
before_script:
|
||||
- set -e
|
||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||
image: briar/ci-image-android:latest
|
||||
|
||||
test:
|
||||
before_script:
|
||||
- set -e
|
||||
- export GRADLE_USER_HOME=$PWD/.gradle
|
||||
|
||||
cache:
|
||||
paths:
|
||||
- .gradle/wrapper
|
||||
- .gradle/caches
|
||||
|
||||
script:
|
||||
- ./gradlew --no-daemon animalSnifferMain animalSnifferTest
|
||||
- ./gradlew --no-daemon test
|
||||
@@ -19,12 +19,13 @@ test:
|
||||
- rm -f $GRADLE_USER_HOME/caches/modules-2/modules-2.lock
|
||||
- rm -fr $GRADLE_USER_HOME/caches/*/plugin-resolution/
|
||||
|
||||
|
||||
test_reproducible:
|
||||
image: briar/reproducer:latest
|
||||
|
||||
script:
|
||||
- cd .. && mv briar /opt/briar-reproducer/
|
||||
- cd /opt/briar-reproducer
|
||||
- ./reproduce.py ${CI_COMMIT_REF_NAME}
|
||||
|
||||
only:
|
||||
- tags
|
||||
|
||||
|
||||
@@ -14,8 +14,8 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10004
|
||||
versionName "1.0.4"
|
||||
versionCode 10005
|
||||
versionName "1.0.5"
|
||||
consumerProguardFiles 'proguard-rules.txt'
|
||||
}
|
||||
|
||||
|
||||
@@ -8,12 +8,10 @@ import android.os.Build;
|
||||
import android.provider.Settings;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
import java.util.Scanner;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import static android.content.Context.MODE_PRIVATE;
|
||||
@@ -80,26 +78,6 @@ public class AndroidUtils {
|
||||
}
|
||||
}
|
||||
|
||||
public static File getSharedPrefsFile(Context ctx, String name) {
|
||||
File dataDir = new File(ctx.getApplicationInfo().dataDir);
|
||||
File prefsDir = new File(dataDir, "shared_prefs");
|
||||
return new File(prefsDir, name + ".xml");
|
||||
}
|
||||
|
||||
public static void logFileContents(File f) {
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
LOG.info("Contents of " + f.getAbsolutePath() + ":");
|
||||
try {
|
||||
Scanner s = new Scanner(f);
|
||||
while (s.hasNextLine()) LOG.info(s.nextLine());
|
||||
s.close();
|
||||
} catch (FileNotFoundException e) {
|
||||
LOG.info(f.getAbsolutePath() + " not found");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public static void deleteAppData(Context ctx, SharedPreferences... clear) {
|
||||
// Clear and commit shared preferences
|
||||
for (SharedPreferences prefs : clear) {
|
||||
|
||||
@@ -14,6 +14,8 @@ public interface DatabaseConfig {
|
||||
|
||||
File getDatabaseDirectory();
|
||||
|
||||
File getDatabaseKeyDirectory();
|
||||
|
||||
void setEncryptionKey(SecretKey key);
|
||||
|
||||
@Nullable
|
||||
|
||||
@@ -9,25 +9,31 @@ import java.io.File;
|
||||
@NotNullByDefault
|
||||
public class TestDatabaseConfig implements DatabaseConfig {
|
||||
|
||||
private final File dir;
|
||||
private final File dbDir, keyDir;
|
||||
private final long maxSize;
|
||||
private volatile SecretKey key = new SecretKey(new byte[SecretKey.LENGTH]);
|
||||
|
||||
public TestDatabaseConfig(File dir, long maxSize) {
|
||||
this.dir = dir;
|
||||
public TestDatabaseConfig(File testDir, long maxSize) {
|
||||
dbDir = new File(testDir, "db");
|
||||
keyDir = new File(testDir, "key");
|
||||
this.maxSize = maxSize;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean databaseExists() {
|
||||
if (!dir.isDirectory()) return false;
|
||||
File[] files = dir.listFiles();
|
||||
if (!dbDir.isDirectory()) return false;
|
||||
File[] files = dbDir.listFiles();
|
||||
return files != null && files.length > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDatabaseDirectory() {
|
||||
return dir;
|
||||
return dbDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDatabaseKeyDirectory() {
|
||||
return keyDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -238,15 +238,15 @@ android {
|
||||
defaultConfig {
|
||||
minSdkVersion 14
|
||||
targetSdkVersion 26
|
||||
versionCode 10004
|
||||
versionName "1.0.4"
|
||||
versionCode 10005
|
||||
versionName "1.0.5"
|
||||
applicationId "org.briarproject.briar.android"
|
||||
resValue "string", "app_package", "org.briarproject.briar.android"
|
||||
resValue "string", "app_name", "Briar"
|
||||
buildConfigField "String", "GitHash",
|
||||
"\"${getStdout(['git', 'rev-parse', '--short=7', 'HEAD'], 'No commit hash')}\""
|
||||
buildConfigField "Long", "BuildTimestamp",
|
||||
"${getStdout(['git', 'log', '-n', '1', '--date=unix', '--format=%cd'], 0)}000L"
|
||||
"${getStdout(['git', 'log', '-n', '1', '--format=%ct'], 0)}000L"
|
||||
}
|
||||
|
||||
buildTypes {
|
||||
@@ -256,11 +256,13 @@ android {
|
||||
resValue "string", "app_name", "Briar Debug"
|
||||
shrinkResources false
|
||||
minifyEnabled true
|
||||
crunchPngs false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
release {
|
||||
shrinkResources true
|
||||
minifyEnabled true
|
||||
crunchPngs false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
}
|
||||
}
|
||||
@@ -276,10 +278,6 @@ android {
|
||||
}
|
||||
}
|
||||
|
||||
aaptOptions {
|
||||
cruncherEnabled = false
|
||||
}
|
||||
|
||||
lintOptions {
|
||||
warning 'MissingTranslation'
|
||||
warning 'ImpliedQuantity'
|
||||
|
||||
@@ -17,31 +17,32 @@ class AndroidDatabaseConfig implements DatabaseConfig {
|
||||
private static final Logger LOG =
|
||||
Logger.getLogger(AndroidDatabaseConfig.class.getName());
|
||||
|
||||
private final File dir;
|
||||
private final File dbDir, keyDir;
|
||||
|
||||
@Nullable
|
||||
private volatile SecretKey key = null;
|
||||
@Nullable
|
||||
private volatile String nickname = null;
|
||||
|
||||
AndroidDatabaseConfig(File dir) {
|
||||
this.dir = dir;
|
||||
AndroidDatabaseConfig(File dbDir, File keyDir) {
|
||||
this.dbDir = dbDir;
|
||||
this.keyDir = keyDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean databaseExists() {
|
||||
// FIXME should not run on UiThread #620
|
||||
if (!dir.isDirectory()) {
|
||||
if (!dbDir.isDirectory()) {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info(dir.getAbsolutePath() + " is not a directory");
|
||||
LOG.info(dbDir.getAbsolutePath() + " is not a directory");
|
||||
return false;
|
||||
}
|
||||
File[] files = dir.listFiles();
|
||||
File[] files = dbDir.listFiles();
|
||||
if (LOG.isLoggable(INFO)) {
|
||||
if (files == null) {
|
||||
LOG.info("Could not list files in " + dir.getAbsolutePath());
|
||||
LOG.info("Could not list files in " + dbDir.getAbsolutePath());
|
||||
} else {
|
||||
LOG.info("Files in " + dir.getAbsolutePath() + ":");
|
||||
LOG.info("Files in " + dbDir.getAbsolutePath() + ":");
|
||||
for (File f : files) LOG.info(f.getName());
|
||||
}
|
||||
LOG.info("Database exists: " + (files != null && files.length > 0));
|
||||
@@ -51,10 +52,16 @@ class AndroidDatabaseConfig implements DatabaseConfig {
|
||||
|
||||
@Override
|
||||
public File getDatabaseDirectory() {
|
||||
File dir = this.dir;
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Database directory: " + dir.getAbsolutePath());
|
||||
return dir;
|
||||
LOG.info("Database directory: " + dbDir.getAbsolutePath());
|
||||
return dbDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
public File getDatabaseKeyDirectory() {
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Database key directory: " + keyDir.getAbsolutePath());
|
||||
return keyDir;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -87,11 +87,13 @@ public class AppModule {
|
||||
//FIXME: StrictMode
|
||||
StrictMode.ThreadPolicy tp = StrictMode.allowThreadDiskReads();
|
||||
StrictMode.allowThreadDiskWrites();
|
||||
File dir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
|
||||
File dbDir = app.getApplicationContext().getDir("db", MODE_PRIVATE);
|
||||
File keyDir = app.getApplicationContext().getDir("key", MODE_PRIVATE);
|
||||
StrictMode.setThreadPolicy(tp);
|
||||
@MethodsNotNullByDefault
|
||||
@ParametersNotNullByDefault
|
||||
DatabaseConfig databaseConfig = new AndroidDatabaseConfig(dir);
|
||||
DatabaseConfig databaseConfig =
|
||||
new AndroidDatabaseConfig(dbDir, keyDir);
|
||||
return databaseConfig;
|
||||
}
|
||||
|
||||
|
||||
@@ -12,7 +12,7 @@ public interface ConfigController {
|
||||
@Nullable
|
||||
String getEncryptedDatabaseKey();
|
||||
|
||||
void storeEncryptedDatabaseKey(String hex);
|
||||
boolean storeEncryptedDatabaseKey(String hex);
|
||||
|
||||
void deleteAccount(Context ctx);
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package org.briarproject.briar.android.controller;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.SharedPreferences;
|
||||
import android.support.v7.preference.PreferenceManager;
|
||||
@@ -9,12 +8,19 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
import org.briarproject.bramble.util.AndroidUtils;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import javax.inject.Inject;
|
||||
|
||||
import static java.util.logging.Level.INFO;
|
||||
import static java.util.logging.Level.WARNING;
|
||||
|
||||
@NotNullByDefault
|
||||
public class ConfigControllerImpl implements ConfigController {
|
||||
@@ -23,8 +29,11 @@ public class ConfigControllerImpl implements ConfigController {
|
||||
Logger.getLogger(ConfigControllerImpl.class.getName());
|
||||
|
||||
private static final String PREF_DB_KEY = "key";
|
||||
private static final String DB_KEY_FILENAME = "db.key";
|
||||
private static final String DB_KEY_BACKUP_FILENAME = "db.key.bak";
|
||||
|
||||
private final SharedPreferences briarPrefs;
|
||||
private final File dbKeyFile, dbKeyBackupFile;
|
||||
protected final DatabaseConfig databaseConfig;
|
||||
|
||||
@Inject
|
||||
@@ -32,22 +41,113 @@ public class ConfigControllerImpl implements ConfigController {
|
||||
DatabaseConfig databaseConfig) {
|
||||
this.briarPrefs = briarPrefs;
|
||||
this.databaseConfig = databaseConfig;
|
||||
File keyDir = databaseConfig.getDatabaseKeyDirectory();
|
||||
dbKeyFile = new File(keyDir, DB_KEY_FILENAME);
|
||||
dbKeyBackupFile = new File(keyDir, DB_KEY_BACKUP_FILENAME);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Nullable
|
||||
public String getEncryptedDatabaseKey() {
|
||||
String key = briarPrefs.getString(PREF_DB_KEY, null);
|
||||
if (LOG.isLoggable(INFO))
|
||||
LOG.info("Got database key from preferences: " + (key != null));
|
||||
String key = getDatabaseKeyFromPreferences();
|
||||
if (key == null) key = getDatabaseKeyFromFile();
|
||||
else migrateDatabaseKeyToFile(key);
|
||||
return key;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getDatabaseKeyFromPreferences() {
|
||||
String key = briarPrefs.getString(PREF_DB_KEY, null);
|
||||
if (key == null) LOG.info("No database key in preferences");
|
||||
else LOG.info("Found database key in preferences");
|
||||
return key;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String getDatabaseKeyFromFile() {
|
||||
String key = readDbKeyFromFile(dbKeyFile);
|
||||
if (key == null) {
|
||||
LOG.info("No database key in primary file");
|
||||
key = readDbKeyFromFile(dbKeyBackupFile);
|
||||
if (key == null) LOG.info("No database key in backup file");
|
||||
else LOG.warning("Found database key in backup file");
|
||||
} else {
|
||||
LOG.info("Found database key in primary file");
|
||||
}
|
||||
return key;
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private String readDbKeyFromFile(File f) {
|
||||
if (!f.exists()) {
|
||||
LOG.info("Key file does not exist");
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), "UTF-8"));
|
||||
String key = reader.readLine();
|
||||
reader.close();
|
||||
return key;
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private void migrateDatabaseKeyToFile(String key) {
|
||||
if (storeEncryptedDatabaseKey(key)) {
|
||||
if (briarPrefs.edit().remove(PREF_DB_KEY).commit())
|
||||
LOG.info("Database key migrated to file");
|
||||
else LOG.warning("Database key not removed from preferences");
|
||||
} else {
|
||||
LOG.warning("Database key not migrated to file");
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@SuppressLint("ApplySharedPref")
|
||||
public void storeEncryptedDatabaseKey(String hex) {
|
||||
LOG.info("Storing database key in preferences");
|
||||
briarPrefs.edit().putString(PREF_DB_KEY, hex).commit();
|
||||
public boolean storeEncryptedDatabaseKey(String hex) {
|
||||
LOG.info("Storing database key in file");
|
||||
// Create the directory if necessary
|
||||
if (databaseConfig.getDatabaseKeyDirectory().mkdirs())
|
||||
LOG.info("Created database key directory");
|
||||
// If only the backup file exists, rename it so we don't overwrite it
|
||||
if (dbKeyBackupFile.exists() && !dbKeyFile.exists()) {
|
||||
if (dbKeyBackupFile.renameTo(dbKeyFile))
|
||||
LOG.info("Renamed old backup");
|
||||
else LOG.warning("Failed to rename old backup");
|
||||
}
|
||||
try {
|
||||
// Write to the backup file
|
||||
writeDbKeyToFile(hex, dbKeyBackupFile);
|
||||
LOG.info("Stored database key in backup file");
|
||||
// Delete the old primary file, if it exists
|
||||
if (dbKeyFile.exists()) {
|
||||
if (dbKeyFile.delete()) LOG.info("Deleted primary file");
|
||||
else LOG.warning("Failed to delete primary file");
|
||||
}
|
||||
// The backup file becomes the new primary
|
||||
if (dbKeyBackupFile.renameTo(dbKeyFile)) {
|
||||
LOG.info("Renamed backup file to primary");
|
||||
} else {
|
||||
LOG.warning("Failed to rename backup file to primary");
|
||||
return false; // Don't overwrite our only copy
|
||||
}
|
||||
// Write a second copy to the backup file
|
||||
writeDbKeyToFile(hex, dbKeyBackupFile);
|
||||
LOG.info("Stored second copy of database key in backup file");
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
if (LOG.isLoggable(WARNING)) LOG.log(WARNING, e.toString(), e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeDbKeyToFile(String key, File f) throws IOException {
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(key.getBytes("UTF-8"));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -73,5 +173,4 @@ public class ConfigControllerImpl implements ConfigController {
|
||||
if (LOG.isLoggable(INFO)) LOG.info("Signed in: " + signedIn);
|
||||
return signedIn;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -72,8 +72,7 @@ public class PasswordControllerImpl extends ConfigControllerImpl
|
||||
} else {
|
||||
String hex =
|
||||
encryptDatabaseKey(new SecretKey(key), newPassword);
|
||||
storeEncryptedDatabaseKey(hex);
|
||||
resultHandler.onResult(true);
|
||||
resultHandler.onResult(storeEncryptedDatabaseKey(hex));
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -102,9 +102,11 @@
|
||||
<string name="show_onboarding">Hilfe anzeigen</string>
|
||||
<string name="fix">Behoben</string>
|
||||
<string name="help">Hilfe</string>
|
||||
<string name="sorry">Entschuldigung</string>
|
||||
<!--Contacts and Private Conversations-->
|
||||
<string name="no_contacts">Du hast noch keine Kontakte\n\nTippe auf das +-Symbol um einen Kontakt hinzuzufügen</string>
|
||||
<string name="date_no_private_messages">Keine Nachrichten.</string>
|
||||
<string name="no_private_messages">Keine Nachrichten zum Anzeigen</string>
|
||||
<string name="message_hint">Nachricht eingeben</string>
|
||||
<string name="delete_contact">Kontakt löschen</string>
|
||||
<string name="dialog_title_delete_contact">Löschen des Kontakts bestätigen</string>
|
||||
@@ -208,6 +210,7 @@
|
||||
<string name="choose_forum_hint">Wähle einen Namen für dein Forum</string>
|
||||
<string name="create_forum_button">Forum erstellen</string>
|
||||
<string name="forum_created_toast">Forum wurde erstellt</string>
|
||||
<string name="no_forum_posts">Keine Beiträge zum Anzeigen</string>
|
||||
<string name="no_posts">Keine Beiträge</string>
|
||||
<plurals name="posts">
|
||||
<item quantity="one">%d Beitrag</item>
|
||||
@@ -250,6 +253,7 @@
|
||||
</plurals>
|
||||
<string name="nobody">Niemand</string>
|
||||
<!--Blogs-->
|
||||
<string name="blogs_other_blog_empty_state">Keine Blogbeiträge zum Anzeigen</string>
|
||||
<string name="read_more">weiterlesen</string>
|
||||
<string name="blogs_write_blog_post">Blogbeitrag erstellen</string>
|
||||
<string name="blogs_write_blog_post_body_hint">Gib deinen Blogbeitrag ein</string>
|
||||
@@ -325,12 +329,16 @@
|
||||
<string name="notification_settings_title">Benachrichtigungen</string>
|
||||
<string name="notify_private_messages_setting_title">Private Nachrichten</string>
|
||||
<string name="notify_private_messages_setting_summary">Zeige Benachrichtigungen für private Nachrichten</string>
|
||||
<string name="notify_private_messages_setting_summary_26">Benachrichtigungen für private Nachrichten konfigurieren</string>
|
||||
<string name="notify_group_messages_setting_title">Gruppennachrichten</string>
|
||||
<string name="notify_group_messages_setting_summary">Benachrichtigungen für Gruppennachrichten anzeigen</string>
|
||||
<string name="notify_group_messages_setting_summary_26">Benachrichtigungen für Gruppennachrichten konfigurieren</string>
|
||||
<string name="notify_forum_posts_setting_title">Forenbeiträge</string>
|
||||
<string name="notify_forum_posts_setting_summary">Benachrichtigungen für Forenbeiträge anzeigen</string>
|
||||
<string name="notify_forum_posts_setting_summary_26">Benachrichtigungen für Forenbeiträge konfigurieren</string>
|
||||
<string name="notify_blog_posts_setting_title">Blogbeiträge</string>
|
||||
<string name="notify_blog_posts_setting_summary">Benachrichtigungen für Blogbeiträge anzeigen</string>
|
||||
<string name="notify_blog_posts_setting_summary_26">Benachrichtigungen für Blogbeiträge konfigurieren</string>
|
||||
<string name="notify_vibration_setting">Vibration</string>
|
||||
<string name="notify_lock_screen_setting_title">Sperrbildschirm</string>
|
||||
<string name="notify_lock_screen_setting_summary">Zeigt Benachrichtigungen auf dem Sperrbildschirm an</string>
|
||||
@@ -375,4 +383,5 @@
|
||||
<string name="permission_camera_denied_body">Du hast den Zugriff auf die Kamera verweigert, aber das Hinzufügen von Kontakten erfordert die Verwendung der Kamera.\n\nBitte gewähre den Zugriff.</string>
|
||||
<string name="permission_camera_denied_toast">Berechtigung für Kamera wurde nicht gewährt</string>
|
||||
<string name="qr_code">QR-Code</string>
|
||||
<string name="show_qr_code_fullscreen">QR-Code im Vollbildmodus anzeigen</string>
|
||||
</resources>
|
||||
|
||||
@@ -5,9 +5,13 @@
|
||||
<string name="setup_name_explanation">نام مستعارتان کنار هر مطلب شما قرار خواهد گرفت و بعد از ایجاد حساب کاربری امکان تغییر آن وجود ندارد.</string>
|
||||
<string name="setup_next">بعدی</string>
|
||||
<string name="setup_password_intro">یک رمز عبور انتخاب کنید</string>
|
||||
<string name="setup_password_explanation">حساب کاربری برایر شما به صورت رمزگذاری شده روی وسیله شما به جای حافظه ابری ذخیره شده است. اگر شما رمز عبور خود را فراموش کنید یا برایر را پاک کنید، راهی برای بازیابی حساب کاربری شما وجود نخواهد داشت.
|
||||
|
||||
یک رمز عبور طولانی انتخاب کنید که حدس آن سخت باشد، مثل چهار عبارت تصادفی یا ده لغت تصادفی با اعداد و نماد ها.</string>
|
||||
<string name="setup_doze_title">اتصال های پس زمینه</string>
|
||||
<string name="setup_doze_intro">برای دریافت پیام ها، برایر نیاز دارد تا در پس زمینه اتصال داشته باشد.</string>
|
||||
<string name="setup_doze_button">اجازه دادن به اتصالات</string>
|
||||
<string name="setup_doze_intro">برای دریافت پیام، برایر نیاز دارد تا در پس زمینه اتصال داشته باشد.</string>
|
||||
<string name="setup_doze_explanation">برای دریافت پیام، برایر نیاز دارد تا در پس زمینه اتصال داشته باشد. لطفا بهینه سازی باتری را غیر فعال کنید تا برایر بتواند به اتصال خود ادامه دهد.</string>
|
||||
<string name="setup_doze_button">دادن اجازه به اتصالات</string>
|
||||
<string name="choose_nickname">نام مستعار خود را انتخاب کنید</string>
|
||||
<string name="choose_password">رمز عبور خود را انتخاب کنید</string>
|
||||
<string name="confirm_password">رمز عبور خود را تایید کنید</string>
|
||||
@@ -20,19 +24,31 @@
|
||||
<string name="setup_huawei_text">لطفا روی دکمه زیر کلیک کنید و مطمئن شوید که از برایر در صفحه \"برنامه های محافظت شده\" محافظت می شود.</string>
|
||||
<string name="setup_huawei_button">حفاظت از برایر</string>
|
||||
<string name="setup_huawei_help">اگر برایر به فهرست برنامه های محافظت شده اضافه نشده، نمی تواند در پس زمینه مشغول به کار باشد.</string>
|
||||
<string name="warning_dozed">ناتوانی %s برای اجراء در پس زمینه</string>
|
||||
<!--Login-->
|
||||
<string name="enter_password">رمز عبور خود را وارد کنید:</string>
|
||||
<string name="try_again">رمز عبور اشتباه است، لطفا دوباره سعی کنید</string>
|
||||
<string name="sign_in_button">ورود</string>
|
||||
<string name="forgotten_password">رمز عبور را فراموش کرده ام</string>
|
||||
<string name="dialog_title_lost_password">رمز عبور گمشده</string>
|
||||
<string name="dialog_message_lost_password">حساب کاربری برایر شما به صورت رمزنگاری شده روی سیستم شما به جای حافظه ابری ذخیره شده است برای همین ما نمی توانیم رمز عبور شما را به صورت مجدد تنظیم کنیم. آیا مایل هستید تا حساب کاربری شما را پاک کنیم و دوباره از ابتدا شروع کنیم؟
|
||||
|
||||
اخطار: هویت های شما، مخاطبان شما و پیام های شما برای همیشه از بین خواهند رفت.</string>
|
||||
<string name="startup_failed_notification_title">برایر نمی تواند شروع به کار کند.</string>
|
||||
<string name="startup_failed_notification_text">برای اطلاعات بیشتر کلیک کنید</string>
|
||||
<string name="startup_failed_activity_title">خطا در شروع برایر</string>
|
||||
<string name="startup_failed_db_error">به دلایلی، دیتابیس برایر شما خراب شده و قابل اصلاح نیست. حساب کاربری شما، داده های شما و تمام مخاطبان شما از بین رفته اند. متاسفانه، شما یا باید برایر را دوباره نصب کنید یا یک حساب کاربری جدید با انتخاب \'رمز عبور ام را فراموش کرده ام\' انتخاب کنید.</string>
|
||||
<string name="startup_failed_data_too_old_error">حساب کاربری شما با یک نسخه قدیمی از این برنامه ایجاد شده است و به همین خاطربا این نسخه نمی تواند باز شود. شما باید نسخه قدیمی را دوباره نصب کنید یا یک حساب کاربری جدید با \"رمز عبور ام را فراموش کرده ام\" در پرامپت رمز عبور ایجاد کنید.</string>
|
||||
<string name="startup_failed_data_too_new_error">این نسخه از برنامه قدیمی می باشد. لطفا به آخرین نسخه از برنامه ارتقاء داده و دوباره سعی کنید.</string>
|
||||
<string name="startup_failed_service_error">برایر نمی تواند یک پلاگین ضروری را اجراء کند. نصب دوباره برایر معمولا این مشکل را حل میکند. هرچند، توجه داشته باشید که حساب کاربری و تمام داده های مرتبط با آن را از دست خواهید داد از آنجایی که برایر از هیچ سرور مرکزی برای ذخیره داده های شما استفاده نمی کند.</string>
|
||||
<plurals name="expiry_warning">
|
||||
<item quantity="one">این یک نسخه آزمایشی از برایر می باشد. حساب کاربری شما در %d روز آینده به پایان می رسد و امکان تمدید آن وجود نخواهد داشت.</item>
|
||||
<item quantity="other">این یک نسخه آزمایشی از برایر می باشد. حساب کاربری شما در %d روز آینده به پایان می رسد و امکان تمدید آن وجود نخواهد داشت.</item>
|
||||
</plurals>
|
||||
<string name="expiry_update">تاریخ اتمام آزمایش افزایش یافته است. حساب کاربری شما در %d روز آینده به پایان می رسد.</string>
|
||||
<string name="expiry_date_reached">این نرم افزار منقضی شده است.
|
||||
|
||||
بابت تست از شما سپاسگزاریم.</string>
|
||||
<string name="download_briar">برای اینکه به استفاده خود از برایر ادامه دهید، لطفا نسخه ۱.۰ را دانلود کنید.</string>
|
||||
<string name="create_new_account">لازم است تا یک حساب کاربری جدید ایجاد کنید، می توانید از نام مستعار یکسان برای حساب های کاربری استفاده کنید.</string>
|
||||
<string name="download_briar_button">دانلود برایر 1.0</string>
|
||||
@@ -52,6 +68,7 @@
|
||||
<string name="transport_bt">بلوتوث</string>
|
||||
<string name="transport_lan">وای فای</string>
|
||||
<!--Notifications-->
|
||||
<string name="ongoing_notification_title">وارد برایر شد</string>
|
||||
<string name="ongoing_notification_text">برای باز کردن برایر کلیک کنید.</string>
|
||||
<plurals name="private_message_notification_text">
|
||||
<item quantity="one">%dپیام خصوصی جدید</item>
|
||||
@@ -88,10 +105,14 @@
|
||||
<string name="no_data">داده ای موجود نمی باشد</string>
|
||||
<string name="ellipsis">…</string>
|
||||
<string name="text_too_long">متن وارد شده بیش از حد طولانی می باشد</string>
|
||||
<string name="show_onboarding">نمایش پنجره راهنما</string>
|
||||
<string name="fix">اصلاح</string>
|
||||
<string name="help">راهنما</string>
|
||||
<string name="sorry">ببخشید</string>
|
||||
<!--Contacts and Private Conversations-->
|
||||
<string name="no_contacts">مخاطبی برای نشان دادن وجود ندارد
|
||||
|
||||
روی علامت + برای افزودن مخاطب کلیک کنید</string>
|
||||
<string name="date_no_private_messages">هیچ پیامی موجود نیست</string>
|
||||
<string name="no_private_messages">هیچ پیامی برای نشان دادن وجود ندارد</string>
|
||||
<string name="message_hint">نوشتن پیام</string>
|
||||
@@ -106,9 +127,22 @@
|
||||
<string name="continue_button">ادامه</string>
|
||||
<string name="connection_failed">اتصال ناموفق بود</string>
|
||||
<string name="try_again_button">دوباره سعی کنید</string>
|
||||
<string name="waiting_for_contact_to_scan">انتظار برای اسکن و اتصال مخاطبu2026\</string>
|
||||
<string name="exchanging_contact_details">تبادیل جزییات مخاطبu2026\</string>
|
||||
<string name="contact_added_toast">مخاطب اضافه شد: %s</string>
|
||||
<string name="contact_already_exists">مخاطب %s از قبل وجود دارد</string>
|
||||
<string name="contact_exchange_failed">تبادل مخاطب با خطا مواجه شد</string>
|
||||
<string name="qr_code_invalid">کد QR نامعتبر می باشد</string>
|
||||
<string name="qr_code_unsupported">کد QR که شما سعی دارید اسکن کنید متعلق به یک نسخه قدیمی از %s میباشد که دیگر پشتیبانی نمی شود.
|
||||
|
||||
لطفا مطمئن شوید که هردوی شما از آخرین نسخه استفاده میکنید و دوباره امتحان کنید.</string>
|
||||
<string name="camera_error">خطای دوربین</string>
|
||||
<string name="connecting_to_device">اتصال به دستگاهu2026\</string>
|
||||
<string name="authenticating_with_device">تصدیق سازی با دستگاه u2026\</string>
|
||||
<string name="connection_aborted_local">اتصال قطع شد! این میتواند نشان دهنده این باشد که شخصی قصد دارد در اتصال شما اختلال ایجاد کند</string>
|
||||
<string name="connection_aborted_remote">اتصال توسط مخاطب شما بی نتیجه ماند! این ممکن است نشان دهنده این باشد که شخصی سعی دارد تا در اتصال شما اختلال ایجاد کند</string>
|
||||
<!--Introductions-->
|
||||
<string name="introduction_onboarding_title">معرفی مخاطبان</string>
|
||||
<string name="introduction_onboarding_text">شما می توانید مخاطبان خود را به یکدیگر معرفی کنید، در این صورت دیگر نیازی به دیدار حضوری برای اتصال روی برایر نمی باشد.</string>
|
||||
<string name="introduction_activity_title">انتخاب مخاطب</string>
|
||||
<string name="introduction_not_possible">شما همین الان یک معرفی در مرحله انجام با این مخاطبان دارید. لطفا اجازه دهید تا این معرفی به پایان برسد. اگر شما و یا مخاطبانتان به ندرت آنلاین هستید، ممکن است کمی زمان ببرد.</string>
|
||||
@@ -118,6 +152,9 @@
|
||||
<string name="introduction_sent">معرفی شما فرستاده شد.</string>
|
||||
<string name="introduction_error">خطایی در معرفی کردن رخ داده است.</string>
|
||||
<string name="introduction_response_error">خطا در هنگام پاسخ به معرفی</string>
|
||||
<string name="introduction_request_sent">شما میخواهید %1$s را به %2$s معرفی کنید.</string>
|
||||
<string name="introduction_request_received">%1$s مایل است شما را به %2$s کند. آیا میخواهید %2$s را به لیست مخاطبانتان اضافه کنید؟</string>
|
||||
<string name="introduction_request_exists_received">%1$s مایل است شما را به %2$s معرفی کند، اما %2$s از قبل جزء لیست مخاطبان شما می باشد. از آنجایی که %1$s ممکن است از این موضوع خبر نداشته باشد، شما هم چنان میتوانید پاسخ دهید:</string>
|
||||
<string name="introduction_request_answered_received">%1$s میخواهد شما را به %2$s معرفی کند.</string>
|
||||
<string name="introduction_response_accepted_sent">شما معرفی به %1$sرا پذیرفتید.</string>
|
||||
<string name="introduction_response_accepted_sent_info">قبل از اضافه شدن %1$s به مخاطبان شما، آن ها باید معرفی را بپذیرند. این شاید کمی زمان ببرد.</string>
|
||||
@@ -130,6 +167,9 @@
|
||||
<item quantity="other">%d مخاطب جدید افزوده شد.</item>
|
||||
</plurals>
|
||||
<!--Private Groups-->
|
||||
<string name="groups_list_empty">هیچ گروهی برای نمایش وجود ندارد
|
||||
|
||||
روی آیکون + برای ایجاد یک گروه کلیک کنید، یا از مخاطبان خود بخواهید تا گروهی را با شما به اشتراک بگذارند</string>
|
||||
<string name="groups_created_by">ایجاد شده توسط %s</string>
|
||||
<plurals name="messages">
|
||||
<item quantity="one">%d پیام</item>
|
||||
@@ -155,13 +195,24 @@
|
||||
<string name="groups_leave_dialog_message">مطمئن هستید که میخواهید این گروه را ترک کنید؟</string>
|
||||
<string name="groups_dissolve">انحلال گروه</string>
|
||||
<string name="groups_dissolve_dialog_title">تایید انحلال گروه</string>
|
||||
<string name="groups_dissolve_dialog_message">آیا از انحلال این گروه اطمینان دارید؟
|
||||
|
||||
سایر اعضاء قادر نخواهند بود تا مکالمات خود را ادامه دهند و ممکن است آخرین پیام ها را دریافت نکنند.</string>
|
||||
<string name="groups_dissolve_button">انحلال</string>
|
||||
<string name="groups_dissolved_dialog_title">گروه انحلال یافت</string>
|
||||
<string name="groups_dissolved_dialog_message">ایجاد کننده این گروه آن را منحل کرده است.
|
||||
|
||||
شما از این به بعد قادر نخواهید بود پیام های دیگری در گروه بنویسید و ممکن است تمام پست هایی که نوشته شده اند را دریافت نکنید.</string>
|
||||
<!--Private Group Invitations-->
|
||||
<string name="groups_invitations_title">دعوت نامه های گروه</string>
|
||||
<string name="groups_invitations_invitation_sent">شما %1$s را برای عضویت به گروه \"%2$s\" دعوت کردید.</string>
|
||||
<string name="groups_invitations_invitation_received">%1$s شما را دعوت کرده تا عضو گروه \"%2$s\" شوید.</string>
|
||||
<string name="groups_invitations_joined">عضو گروه شد</string>
|
||||
<string name="groups_invitations_declined">دعوت نامه گروه رد شد</string>
|
||||
<plurals name="groups_invitations_open">
|
||||
<item quantity="one">%d دعوتنامه سرگشاده</item>
|
||||
<item quantity="other">%d دعوت نامه سرگشاده</item>
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">شما دعوت نامه گروه از %s را پذیرفتید.</string>
|
||||
<string name="groups_invitations_response_declined_sent">شما دعوت نامه گروه از %s را رد کردید.</string>
|
||||
<string name="groups_invitations_response_accepted_received">%sدعوت نامه گروه را پذیرفت.</string>
|
||||
@@ -169,7 +220,17 @@
|
||||
<string name="sharing_status_groups">فقط سازنده گروه می تواند اعضای جدید را به گروه دعوت کند. در پایین تمام اعضای فعلی گروه آمده است.</string>
|
||||
<!--Private Groups Revealing Contacts-->
|
||||
<string name="groups_reveal_contacts">نشان دادن مخاطبان</string>
|
||||
<string name="groups_reveal_dialog_message">شما میتوانید مخاطبان را به تمام اعضای فعلی و آینده این گروه نشان دهید.
|
||||
|
||||
نشان دادن مخاطبان اتصال شما به گروه را سریع تر و قابل اطمینان تر میکند، چراکه میتوانید با مخاطبان نشان داده شده ارتباط برقرار کنید حتی در موقعی که گروه آفلاین می باشد.</string>
|
||||
<string name="groups_reveal_visible">ارتباط مخاطب برای گروه قابل دیدن می باشد</string>
|
||||
<string name="groups_reveal_visible_revealed_by_us">ارتباط مخاطب برای گروه قابل دیدن می باشد (آشکار شده توسط شما)</string>
|
||||
<string name="groups_reveal_visible_revealed_by_contact">ارتباط مخاطب برای گروه قابل یدن می باشد (آشکار شده توسط %s)</string>
|
||||
<string name="groups_reveal_invisible">ارتباط مخاطب برای گروه قابل دیدن نمی باشد</string>
|
||||
<!--Forums-->
|
||||
<string name="no_forums">هیچ فرومی برای نمایش وجود ندارد
|
||||
|
||||
روی آیکون + برای ایجاد فروم کلیک کنید یا از مخاطبانتان بخواهید تا با شما فروم به اشتراک بگذارند</string>
|
||||
<string name="create_forum_title">ایجاد فروم</string>
|
||||
<string name="choose_forum_hint">انتخاب یک نام برای فروم</string>
|
||||
<string name="create_forum_button">ایجاد فروم</string>
|
||||
@@ -180,28 +241,48 @@
|
||||
<item quantity="one">%d پست</item>
|
||||
<item quantity="other">%d پست</item>
|
||||
</plurals>
|
||||
<string name="forum_new_entry_posted">ثبت فروم پست شد</string>
|
||||
<string name="forum_new_message_hint">ثبت جدید</string>
|
||||
<string name="forum_message_reply_hint">پاسخ جدید</string>
|
||||
<string name="btn_reply">پاسخ</string>
|
||||
<string name="forum_leave">ترک فروم</string>
|
||||
<string name="dialog_title_leave_forum">تأیید ترک فروم</string>
|
||||
<string name="dialog_message_leave_forum">آیا اطمینان دارید که میخواهید این فروم را ترک کنید؟
|
||||
|
||||
هر مخاطبی که این فروم را با آنها به اشتراک گذاشته اید ممکن است آپدیت دیگری دریافت نکنند.</string>
|
||||
<string name="dialog_button_leave">ترک</string>
|
||||
<string name="forum_left_toast">ترک فروم</string>
|
||||
<!--Forum Sharing-->
|
||||
<string name="forum_share_button">اشتراک گذاری فروم</string>
|
||||
<string name="contacts_selected">مخاطبان انتخاب شدند</string>
|
||||
<string name="activity_share_toolbar_header">انتخاب مخاطبان</string>
|
||||
<string name="no_contacts_selector">هیچ مخاطبی برای نمایش دادن وجود ندارد
|
||||
|
||||
لطفا بعد از افزودن مخاطب برگردید.</string>
|
||||
<string name="forum_shared_snackbar">فروم با مخاطبان انتخاب شده به اشتراک گذاشته شد</string>
|
||||
<string name="forum_share_message">افزودن یک پیام (اختیاری)</string>
|
||||
<string name="forum_share_error">خطایی با اشتراک گذاری این فروم رخ داد</string>
|
||||
<string name="forum_invitation_received">%1$s فروم \"%2$s\" را با شما به اشتراک گذاشته است.</string>
|
||||
<string name="forum_invitation_sent">شما فروم \"%1$s\" را با %2$s به اشتراک گذاشتید.</string>
|
||||
<string name="forum_invitations_title">دعوت نامه های فروم</string>
|
||||
<string name="forum_invitation_exists">شما دعوت به این فروم را پذیرفته بودید.
|
||||
|
||||
پذیرفتن دعوت نامه های بیشتر باعث می شود تا اتصال شما به فروم سریع تر و قابل اطمینان تر شود.</string>
|
||||
<string name="forum_joined_toast">عضو فروم شد</string>
|
||||
<string name="forum_declined_toast">دعوت نامه رد شد</string>
|
||||
<string name="shared_by_format">به اشتراک گذاشته شده توسط %s</string>
|
||||
<string name="forum_invitation_already_sharing">در حال به اشتراک گذاری</string>
|
||||
<string name="forum_invitation_response_accepted_sent">شما دعوت نامه فروم از %s را پذیرفتید.</string>
|
||||
<string name="forum_invitation_response_declined_sent">شما دعوت نامه فروم از %s را رد کردید.</string>
|
||||
<string name="forum_invitation_response_accepted_received">%s دعوت نامه فروم را پذیرفت.</string>
|
||||
<string name="forum_invitation_response_declined_received">%s دعوت نامه فروم را رد کرد.</string>
|
||||
<string name="sharing_status">به اشتراک گذاری وضعیت</string>
|
||||
<string name="sharing_status_forum">هر عضو فروم می تواند آن را با دیگر مخاطبان خود به اشتراک بگذارد. شما این فروم را با مخاطبان زیر به اشتراک می گذارید. ممکن است اعضای دیگری هم باشند که شما نمی توانید آن ها را ببینید.</string>
|
||||
<string name="shared_with">به اشتراک گذاشته شده با %1$d (%2$d نفر آنلاین)</string>
|
||||
<plurals name="forums_shared">
|
||||
<item quantity="one"> %d فروم به اشتراک گذاشته شده توسط مخاطبان</item>
|
||||
<item quantity="other">%d فروم به اشتراک گذاشته شده توسط مخاطبان</item>
|
||||
</plurals>
|
||||
<string name="nobody">هیچکس</string>
|
||||
<!--Blogs-->
|
||||
<string name="blogs_other_blog_empty_state">هیچ پستی برای نشان دادن وجود ندارد</string>
|
||||
@@ -211,12 +292,25 @@
|
||||
<string name="blogs_publish_blog_post">انتشار</string>
|
||||
<string name="blogs_blog_post_created">پست بلاگ ایجاد شد</string>
|
||||
<string name="blogs_blog_post_received">پست جدید بلاگ دریافت شد</string>
|
||||
<string name="blogs_blog_post_scroll_to">حرکت به</string>
|
||||
<string name="blogs_feed_empty_state">هیچ پستی برای نشان دادن وجود ندارد
|
||||
|
||||
پست های مخاطبان و بلاگ هایی که مشترک آن ها هستید اینجا نشان داده خواهند شد
|
||||
|
||||
روی آیکون خودکار برای ایجاد یک پست کلیک کنید</string>
|
||||
<string name="blogs_remove_blog">حذف بلاگ</string>
|
||||
<string name="blogs_remove_blog_dialog_message">آیا اطمینان دارید که میخواهید این بلاگ را حذف کنید؟
|
||||
|
||||
پست ها از روی دستگاه شما حذف خواهند شد اما از روی دستگاه سایر افراد حذف نخواهد شد.
|
||||
|
||||
هر مخاطبی که شما این بلاگ را با آن ها به اشتراک گذاشته اید ممکن است از این به بعد آپدیتی دریافت نکند.</string>
|
||||
<string name="blogs_remove_blog_ok">حذف</string>
|
||||
<string name="blogs_blog_removed">بلاگ حذف شد</string>
|
||||
<string name="blogs_reblog_comment_hint">افزودن یک کامنت (اختیاری)</string>
|
||||
<string name="blogs_reblog_button">ریبلاگ</string>
|
||||
<!--Blog Sharing-->
|
||||
<string name="blogs_sharing_share">به اشتراک گذاری بلاگ</string>
|
||||
<string name="blogs_sharing_error">خطایی با اشتراک گذاری این بلاگ وجود داشت.</string>
|
||||
<string name="blogs_sharing_button">به اشتراک گذاری بلاگ</string>
|
||||
<string name="blogs_sharing_snackbar">بلاگ با مخاطبان انتخاب شده به اشتراک گذاشته شد</string>
|
||||
<string name="blogs_sharing_response_accepted_sent">شما دعوت نامه بلاگ از طرف %s را پذیرفتید.</string>
|
||||
@@ -226,8 +320,12 @@
|
||||
<string name="blogs_sharing_invitation_received">%1$s بلاگ \"%2$s\" را با شما به اشتراک گذاشت.</string>
|
||||
<string name="blogs_sharing_invitation_sent">شما بلاگ \"%1$s\" را با %2$s به اشتراک گذاشته اید.</string>
|
||||
<string name="blogs_sharing_invitations_title">دعوت نامه های بلاگ</string>
|
||||
<string name="blogs_sharing_joined_toast">به اشتراک بلاگ درآمد</string>
|
||||
<string name="blogs_sharing_declined_toast">دعوت نامه رد شد</string>
|
||||
<string name="sharing_status_blog">هرکسی که به اشتراک یک بلاگ در بیاید می تواند آن را با مخاطبان خود به اشتراک بگذارد. شما این بلاگ را با مخاطبان زیر به اشتراک میگذارید. ممکن است مشترکان دیگری هم باشند که شما نتوانید آن ها را ببینید.</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">وارد کردن خوراک RSS</string>
|
||||
<string name="blogs_rss_feeds_import_button">وارد کردن</string>
|
||||
<string name="blogs_rss_feeds_import_hint">آدرس خوراک RSS را وارد کنید</string>
|
||||
<string name="blogs_rss_feeds_import_error">متاسفیم! وارد کردن خوراک شما با خطا مواجه شده است.</string>
|
||||
<string name="blogs_rss_feeds_manage">مدیریت خوراک های RSS</string>
|
||||
@@ -235,8 +333,16 @@
|
||||
<string name="blogs_rss_feeds_manage_author">نویسنده:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">آخرین به روز رسانی:</string>
|
||||
<string name="blogs_rss_remove_feed">حذف فید</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">آیا مطمئن هستید که میخواهید این خوراک را حذف کنید؟
|
||||
|
||||
پست ها از دستگاه شما پاک خواهند شد اما روی دستگاه سایر افراد باقی خواهند ماند
|
||||
|
||||
هر مخاطبی که با آن این خوراک را به اشتراک گذاشته اید ممکن است دیگر آپدیت دریافت نکند.</string>
|
||||
<string name="blogs_rss_remove_feed_ok">حذف</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">خوراک نمی تواند پاک شود!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">هیچ خوراک RSS برای نمایش وجود ندارد
|
||||
|
||||
برای وارد کردن خوراک روی آیکون + کلیک کنید</string>
|
||||
<string name="blogs_rss_feeds_manage_error">مشکلی با بارگذاری فیدهای شما وجود داشت. لطفا بعدا امتحان کنید.</string>
|
||||
<!--Settings Network-->
|
||||
<string name="network_settings_title">شبکه ها</string>
|
||||
@@ -254,24 +360,36 @@
|
||||
<string name="choose_new_password">رمز عبور جدید خود را انتخاب کنید:</string>
|
||||
<string name="confirm_new_password">رمز عبور جدید خود را تأیید کنید:</string>
|
||||
<string name="password_changed">رمز عبور تغییر کرده است</string>
|
||||
<string name="panic_setting">برپایی دکمه هراس</string>
|
||||
<string name="panic_setting_title">دکمه هراس</string>
|
||||
<string name="panic_setting_hint">تنظیم نحوه واکنش برایر هنگام فعال سازی دکمه هراس</string>
|
||||
<string name="panic_app_setting_title">برنامه دکمه هراس</string>
|
||||
<string name="unknown_app">یک برنامه ناشناخته</string>
|
||||
<string name="panic_app_setting_summary">هیچ برنامه تنظیم نشده است</string>
|
||||
<string name="panic_app_setting_none">هیچکدام</string>
|
||||
<string name="dialog_title_connect_panic_app">تایید برنامه هراس</string>
|
||||
<string name="dialog_message_connect_panic_app">آیا مطمئن هستید که میخواهید به %1$s اجازه دهید تا باعث عملیات مخرب دکمه هراس بشود؟</string>
|
||||
<string name="lock_setting_title">خروج</string>
|
||||
<string name="lock_setting_summary">در صورت کلیک بر روی کلید هراس از برایر خارج شو</string>
|
||||
<string name="purge_setting_title">حذف حساب کاربری</string>
|
||||
<string name="purge_setting_summary">پاک کردن حساب کاربری برایر شما در صورتی که دکمه هراس فشار داده شود. اخطار: این باعث پاک شدن دائمی تمام هویت ها، مخاطبان و پیام های شما خواهد شد</string>
|
||||
<string name="uninstall_setting_title">پاک کردن برایر</string>
|
||||
<string name="uninstall_setting_summary">این نیازمند تایید دستی در موعد هراس میباشد</string>
|
||||
<!--Settings Notifications-->
|
||||
<string name="notification_settings_title">نوتیفیکیشن ها</string>
|
||||
<string name="notify_private_messages_setting_title">پیام های خصوصی</string>
|
||||
<string name="notify_private_messages_setting_summary">نمایش هشدار برای پیام های خصوصی</string>
|
||||
<string name="notify_private_messages_setting_summary_26">تنظیم هشدار برای پیام های شخصی</string>
|
||||
<string name="notify_group_messages_setting_title">پیام های گروه</string>
|
||||
<string name="notify_group_messages_setting_summary">نمایش هشدار برای پیام های گروه</string>
|
||||
<string name="notify_forum_posts_setting_title">پست های فروم</string>
|
||||
<string name="notify_forum_posts_setting_summary">نمایش هشدار برای پست های فروم</string>
|
||||
<string name="notify_group_messages_setting_summary_26">تنظیم هشدار برای پیام های گروه</string>
|
||||
<string name="notify_forum_posts_setting_title">پست های تالار گفتمان</string>
|
||||
<string name="notify_forum_posts_setting_summary">نمایش هشدار برای پست های تالار گفتمان</string>
|
||||
<string name="notify_forum_posts_setting_summary_26">تنظیم هشدار برای پست های تالار گفتمان</string>
|
||||
<string name="notify_blog_posts_setting_title">پست های بلاگ</string>
|
||||
<string name="notify_blog_posts_setting_summary">نمایش هشدار برای پست های بلاگ</string>
|
||||
<string name="notify_blog_posts_setting_summary_26">تنظیم هشدار برای پست های بلاگ</string>
|
||||
<string name="notify_vibration_setting">لرزش</string>
|
||||
<string name="notify_lock_screen_setting_title">قفل صفحه</string>
|
||||
<string name="notify_lock_screen_setting_summary">نمایش نوتیفیکشن روی صفحه قفل</string>
|
||||
<string name="notify_sound_setting">صدا</string>
|
||||
@@ -297,6 +415,8 @@
|
||||
<string name="describe_crash">توضیح دهید چه اتفاقی افتاد (اختیاری)</string>
|
||||
<string name="enter_feedback">بازخورد خود را وارد کنید</string>
|
||||
<string name="optional_contact_email">آدرس ایمیل شما (اختیاری)</string>
|
||||
<string name="include_debug_report_crash">قرار دادن داده های ناشناس مربوط به خرابی</string>
|
||||
<string name="include_debug_report_feedback">قرار دادن داده های ناشناس درباره این دستگاه</string>
|
||||
<string name="could_not_load_report_data">امکان بارگذاری داده های گزارش وجود ندارد.</string>
|
||||
<string name="send_report">ارسال گزارش</string>
|
||||
<string name="close">بستن</string>
|
||||
@@ -304,9 +424,20 @@
|
||||
<!--Sign Out-->
|
||||
<string name="progress_title_logout">خروج از برایر...</string>
|
||||
<!--Screen Filters & Tapjacking-->
|
||||
<string name="screen_filter_title">قرارگیری صفحه شناسایی شد</string>
|
||||
<string name="screen_filter_body">برنامه ای دیگری روی برایر قرار گرفته است. برای حفاظت از امنیت شما، برایر پاسخگو به لمس های صفحه نمایش تا زمانی که برنامه دیگری بالای آن قرار دارد نخواهد بود.
|
||||
|
||||
این برنامه ها ممکن است روی برایر قرار گرفته باشند:
|
||||
|
||||
%1$s</string>
|
||||
<string name="screen_filter_allow">به این برنامه ها اجازه بده تا روی برایر قرار بگیرند</string>
|
||||
<!--Permission Requests-->
|
||||
<string name="permission_camera_title">دسترسی به دوربین</string>
|
||||
<string name="permission_camera_request_body">برای اسکن کردن کد QR دسترسی به دوربین لازم است.</string>
|
||||
<string name="permission_camera_denied_body">شما دسترسی به دوربین را رد کرده اید، اما افزودن مخاطب نیاز به دوربین دارد.
|
||||
|
||||
لطفا اجازه دسترسی را بدهید.</string>
|
||||
<string name="permission_camera_denied_toast">اجازه دسترسی به دوربین پذیرفته نشد</string>
|
||||
<string name="qr_code">کد QR</string>
|
||||
<string name="show_qr_code_fullscreen">نمایش کد QR به صورت فول اسکرین</string>
|
||||
</resources>
|
||||
|
||||
@@ -42,25 +42,25 @@
|
||||
<plurals name="private_message_notification_text">
|
||||
<item quantity="one">הודעה פרטית חדשה.</item>
|
||||
<item quantity="two">%d הודעות פריטות חדשות.</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">%d הודעות פריטות חדשות.</item>
|
||||
<item quantity="other">%d הודעות פריטות חדשות.</item>
|
||||
</plurals>
|
||||
<plurals name="group_message_notification_text">
|
||||
<item quantity="one">הודעה קבוצתית חדשה.</item>
|
||||
<item quantity="two">%d הודעות קבוצתיות חדשות.</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">%d הודעות קבוצתיות חדשות.</item>
|
||||
<item quantity="other">%d הודעות קבוצתיות חדשות.</item>
|
||||
</plurals>
|
||||
<plurals name="forum_post_notification_text">
|
||||
<item quantity="one">פוסט חדש בפורומים.</item>
|
||||
<item quantity="two">%d פוסטים חדשים בפורומים.</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">%d פוסטים חדשים בפורומים.</item>
|
||||
<item quantity="other">%d פוסטים חדשים בפורומים.</item>
|
||||
</plurals>
|
||||
<plurals name="blog_post_notification_text">
|
||||
<item quantity="one">פוסט חדש בבלוגים.</item>
|
||||
<item quantity="two">%d פוסטים חדשים בבלוגים.</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">%d פוסטים חדשים בבלוגים.</item>
|
||||
<item quantity="other">%d פוסטים חדשים בבלוגים.</item>
|
||||
</plurals>
|
||||
<!--Misc-->
|
||||
@@ -130,7 +130,7 @@
|
||||
<plurals name="introduction_notification_text">
|
||||
<item quantity="one">נוסף איש קשר חדש.</item>
|
||||
<item quantity="two">נוספו %d אנשי קשר חדשים.</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">נוספו %d אנשי קשר חדשים.</item>
|
||||
<item quantity="other">נוספו %d אנשי קשר חדשים.</item>
|
||||
</plurals>
|
||||
<!--Private Groups-->
|
||||
@@ -138,7 +138,7 @@
|
||||
<plurals name="messages">
|
||||
<item quantity="one">הודעה %d</item>
|
||||
<item quantity="two">%d הודעות</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">%d הודעות</item>
|
||||
<item quantity="other">%d הודעות</item>
|
||||
</plurals>
|
||||
<string name="groups_group_is_empty">קבוצה זו הינה ריקה</string>
|
||||
@@ -178,7 +178,7 @@
|
||||
<plurals name="groups_invitations_open">
|
||||
<item quantity="one">הזמנה קבוצתית פתוחה %d</item>
|
||||
<item quantity="two">%dהזמנות קבוצתית פתוחות </item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">%dהזמנות קבוצתית פתוחות </item>
|
||||
<item quantity="other">%dהזמנות קבוצתית פתוחות </item>
|
||||
</plurals>
|
||||
<string name="groups_invitations_response_accepted_sent">הסכמתם להזמנה הקבוצתית מ- %s.</string>
|
||||
@@ -204,7 +204,7 @@
|
||||
<plurals name="posts">
|
||||
<item quantity="one">פוסט %d</item>
|
||||
<item quantity="two">%d פוסטים</item>
|
||||
<item quantity="many"></item>
|
||||
<item quantity="many">%d פוסטים</item>
|
||||
<item quantity="other">%d פוסטים</item>
|
||||
</plurals>
|
||||
<string name="forum_new_entry_posted">הפוסט נוצר</string>
|
||||
|
||||
@@ -141,6 +141,7 @@
|
||||
<string name="introduction_request_exists_received">%1$s 想要将您介绍给 %2$s,但是 %2$s 已经在您的联系人列表。%1$s 可能并不知情,但您仍可以做出回复:</string>
|
||||
<string name="introduction_request_answered_received">%1$s 想要将您介绍给 %2$s。</string>
|
||||
<string name="introduction_response_accepted_sent">您已接受与 %1$s 建立联系</string>
|
||||
<string name="introduction_response_accepted_sent_info">在 %1$s添加到您通讯录之前,他们需要接受您的名片。这可能要花点时间。</string>
|
||||
<string name="introduction_response_declined_sent">您已谢绝与 %1$s 建立联系</string>
|
||||
<string name="introduction_response_accepted_received">%1$s 接受与 %2$s 建立联系</string>
|
||||
<string name="introduction_response_declined_received">%1$s 谢绝与 %2$s 建立联系</string>
|
||||
@@ -149,6 +150,7 @@
|
||||
<item quantity="other">已添加 %d 位新联系人。</item>
|
||||
</plurals>
|
||||
<!--Private Groups-->
|
||||
<string name="groups_list_empty">没有群组\n\n点击 + 号创建群聊,或者让您的联系人分享群组给您</string>
|
||||
<string name="groups_created_by">由 %s 创建</string>
|
||||
<plurals name="messages">
|
||||
<item quantity="other">%d 条消息。</item>
|
||||
@@ -199,6 +201,7 @@
|
||||
<string name="groups_reveal_visible_revealed_by_contact">联系人关系对群聊可见 (由 %s 设定)</string>
|
||||
<string name="groups_reveal_invisible">联系人关系对群聊不可见</string>
|
||||
<!--Forums-->
|
||||
<string name="no_forums">没有论坛\n\n点击 + 号来创建一个,或者让您的联系人分享一个论坛给您</string>
|
||||
<string name="create_forum_title">创建论坛</string>
|
||||
<string name="choose_forum_hint">为论坛命名</string>
|
||||
<string name="create_forum_button">创建论坛</string>
|
||||
@@ -214,17 +217,23 @@
|
||||
<string name="btn_reply">回复</string>
|
||||
<string name="forum_leave">退出论坛</string>
|
||||
<string name="dialog_title_leave_forum">确认退出论坛</string>
|
||||
<string name="dialog_message_leave_forum">确定要退出论坛?\n\n那些您分享过论坛的联系人将不会再收到更新。</string>
|
||||
<string name="dialog_button_leave">退出</string>
|
||||
<string name="forum_left_toast">已退出论坛</string>
|
||||
<!--Forum Sharing-->
|
||||
<string name="forum_share_button">分享论坛</string>
|
||||
<string name="contacts_selected">已选择联系人</string>
|
||||
<string name="activity_share_toolbar_header">选择联系人</string>
|
||||
<string name="no_contacts_selector">没有联系人\n\n请添加联系人后再来</string>
|
||||
<string name="forum_shared_snackbar">论坛已分享给选中的联系人</string>
|
||||
<string name="forum_share_message">添加一句话 (选填)</string>
|
||||
<string name="forum_share_error">分享此论坛时发生错误。</string>
|
||||
<string name="forum_invitation_received">%1$s 已将论坛 “%2$s” 分享给你。</string>
|
||||
<string name="forum_invitation_sent">您已将论坛 “%1$s” 分享给 %2$s。</string>
|
||||
<string name="forum_invitations_title">论坛邀请</string>
|
||||
<string name="forum_invitation_exists">您已接受论坛邀请。\n\n接受更多的邀请,会使论坛访问速度更快更可靠。</string>
|
||||
<string name="forum_joined_toast">已加入论坛</string>
|
||||
<string name="forum_declined_toast">邀请已拒绝</string>
|
||||
<string name="shared_by_format">由 %s 分享</string>
|
||||
<string name="forum_invitation_already_sharing">已在分享</string>
|
||||
<string name="forum_invitation_response_accepted_sent">您接受了来自 %s的论坛邀请</string>
|
||||
@@ -242,12 +251,16 @@
|
||||
<string name="blogs_other_blog_empty_state">尚无帖子可供展示</string>
|
||||
<string name="read_more">阅读更多</string>
|
||||
<string name="blogs_write_blog_post">写博文</string>
|
||||
<string name="blogs_write_blog_post_body_hint">输入博文内容</string>
|
||||
<string name="blogs_publish_blog_post">发布</string>
|
||||
<string name="blogs_blog_post_created">博文已创建</string>
|
||||
<string name="blogs_blog_post_received">收到新博文</string>
|
||||
<string name="blogs_blog_post_scroll_to">滑动至</string>
|
||||
<string name="blogs_feed_empty_state">没有文章\n\n此处显示您好友的博文或您订阅的文章\n\n点击钢笔图标来写一篇文章</string>
|
||||
<string name="blogs_remove_blog">删除博客</string>
|
||||
<string name="blogs_remove_blog_dialog_message">确定要删除博文吗?\n\n文章将会从您的设备删除,但仍存在于别人的设备上。\n\n您分享过博文的用户将不再收到更新。</string>
|
||||
<string name="blogs_remove_blog_ok">删除</string>
|
||||
<string name="blogs_blog_removed">博客已删除</string>
|
||||
<string name="blogs_reblog_comment_hint">添加评论 (选填)</string>
|
||||
<string name="blogs_reblog_button">转载</string>
|
||||
<!--Blog Sharing-->
|
||||
@@ -262,6 +275,8 @@
|
||||
<string name="blogs_sharing_invitation_received">%1$s 向您分享博客 “%2$s”。</string>
|
||||
<string name="blogs_sharing_invitation_sent">您已将博客 “%1$s” 分享给 %2$s。</string>
|
||||
<string name="blogs_sharing_invitations_title">博客邀请</string>
|
||||
<string name="blogs_sharing_joined_toast">订阅博客</string>
|
||||
<string name="blogs_sharing_declined_toast">邀请已拒绝</string>
|
||||
<string name="sharing_status_blog">任何订阅博客的人都可以将它分享给他的联系人。您正在将该博客分享给下列联系人。可能有其他您所不可见的订阅者存在。</string>
|
||||
<!--RSS Feeds-->
|
||||
<string name="blogs_rss_feeds_import">导入 RSS 订阅</string>
|
||||
@@ -273,8 +288,10 @@
|
||||
<string name="blogs_rss_feeds_manage_author">作者:</string>
|
||||
<string name="blogs_rss_feeds_manage_updated">最后更新于:</string>
|
||||
<string name="blogs_rss_remove_feed">删除订阅</string>
|
||||
<string name="blogs_rss_remove_feed_dialog_message">确定要删除信息流?\n\n文章将从您的设备上移除,但仍存于别人的设备。\n\n您分享过信息流的人将不再收到更新。</string>
|
||||
<string name="blogs_rss_remove_feed_ok">删除</string>
|
||||
<string name="blogs_rss_feeds_manage_delete_error">该订阅无法被删除!</string>
|
||||
<string name="blogs_rss_feeds_manage_empty_state">没有订阅内容\n\n点击 + 号导入一个 RSS 信息流</string>
|
||||
<string name="blogs_rss_feeds_manage_error">加载订阅时出错。请稍候再试。</string>
|
||||
<!--Settings Network-->
|
||||
<string name="network_settings_title">网络</string>
|
||||
@@ -311,12 +328,16 @@
|
||||
<string name="notification_settings_title">消息提醒</string>
|
||||
<string name="notify_private_messages_setting_title">私信</string>
|
||||
<string name="notify_private_messages_setting_summary">显示私信通知</string>
|
||||
<string name="notify_private_messages_setting_summary_26">配置私聊消息通知</string>
|
||||
<string name="notify_group_messages_setting_title">群消息</string>
|
||||
<string name="notify_group_messages_setting_summary">显示群聊消息通知</string>
|
||||
<string name="notify_group_messages_setting_summary_26">配置群聊消息通知</string>
|
||||
<string name="notify_forum_posts_setting_title">论坛帖子</string>
|
||||
<string name="notify_forum_posts_setting_summary">显示论坛帖子通知</string>
|
||||
<string name="notify_forum_posts_setting_summary_26">配置论坛文章通知</string>
|
||||
<string name="notify_blog_posts_setting_title">博文</string>
|
||||
<string name="notify_blog_posts_setting_summary">显示博文通知</string>
|
||||
<string name="notify_blog_posts_setting_summary_26">配置博客文章通知</string>
|
||||
<string name="notify_vibration_setting">震动</string>
|
||||
<string name="notify_lock_screen_setting_title">锁定屏幕</string>
|
||||
<string name="notify_lock_screen_setting_summary">在锁定屏幕上显示通知</string>
|
||||
@@ -360,4 +381,6 @@
|
||||
<string name="permission_camera_request_body">Briar 需要获得相机权限以扫描二维码。</string>
|
||||
<string name="permission_camera_denied_body">您拒绝了相机权限,而添加联系人需要使用相机\n\n请考虑授予相机权限。</string>
|
||||
<string name="permission_camera_denied_toast">未授予相机权限</string>
|
||||
<string name="qr_code">二维码</string>
|
||||
<string name="show_qr_code_fullscreen">全屏显示二维码</string>
|
||||
</resources>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
package org.briarproject.briar.android;
|
||||
|
||||
import org.briarproject.bramble.api.nullsafety.NotNullByDefault;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
|
||||
@NotNullByDefault
|
||||
public class TestDatabaseKeyUtils {
|
||||
|
||||
public static void storeDatabaseKey(File f, String hex) throws IOException {
|
||||
f.getParentFile().mkdirs();
|
||||
FileOutputStream out = new FileOutputStream(f);
|
||||
out.write(hex.getBytes("UTF-8"));
|
||||
out.flush();
|
||||
out.close();
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public static String loadDatabaseKey(File f) throws IOException {
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(
|
||||
new FileInputStream(f), "UTF-8"));
|
||||
String hex = reader.readLine();
|
||||
reader.close();
|
||||
return hex;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,205 @@
|
||||
package org.briarproject.briar.android.controller;
|
||||
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.SharedPreferences.Editor;
|
||||
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertNull;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
|
||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
|
||||
|
||||
public class ConfigControllerImplTest extends BrambleMockTestCase {
|
||||
|
||||
private final SharedPreferences prefs =
|
||||
context.mock(SharedPreferences.class);
|
||||
private final DatabaseConfig databaseConfig =
|
||||
context.mock(DatabaseConfig.class);
|
||||
private final Editor editor = context.mock(Editor.class);
|
||||
|
||||
private final byte[] encryptedKey = getRandomBytes(123);
|
||||
private final String encryptedKeyHex = toHexString(encryptedKey);
|
||||
private final String oldEncryptedKeyHex = toHexString(getRandomBytes(123));
|
||||
private final File testDir = getTestDirectory();
|
||||
private final File keyDir = new File(testDir, "key");
|
||||
private final File keyFile = new File(keyDir, "db.key");
|
||||
private final File keyBackupFile = new File(keyDir, "db.key.bak");
|
||||
|
||||
@Test
|
||||
public void testDbKeyIsMigratedFromPreferencesToFile() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(prefs).getString("key", null);
|
||||
will(returnValue(encryptedKeyHex));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
oneOf(prefs).edit();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).remove("key");
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).commit();
|
||||
will(returnValue(true));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
||||
databaseConfig);
|
||||
|
||||
assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbKeyIsLoadedFromPrimaryFile() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(prefs).getString("key", null);
|
||||
will(returnValue(null));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
storeDatabaseKey(keyFile, encryptedKeyHex);
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||
|
||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
||||
databaseConfig);
|
||||
|
||||
assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbKeyIsLoadedFromBackupFile() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(prefs).getString("key", null);
|
||||
will(returnValue(null));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
storeDatabaseKey(keyBackupFile, encryptedKeyHex);
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||
|
||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
||||
databaseConfig);
|
||||
|
||||
assertEquals(encryptedKeyHex, c.getEncryptedDatabaseKey());
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDbKeyIsNullIfNotFound() {
|
||||
context.checking(new Expectations() {{
|
||||
oneOf(prefs).getString("key", null);
|
||||
will(returnValue(null));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
ConfigControllerImpl c = new ConfigControllerImpl(prefs,
|
||||
databaseConfig);
|
||||
|
||||
assertNull(c.getEncryptedDatabaseKey());
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringDbKeyOverwritesPrimary() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
storeDatabaseKey(keyFile, oldEncryptedKeyHex);
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyFile));
|
||||
|
||||
ConfigController c = new ConfigControllerImpl(prefs,
|
||||
databaseConfig);
|
||||
|
||||
assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testStoringDbKeyOverwritesBackup() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
storeDatabaseKey(keyBackupFile, oldEncryptedKeyHex);
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(oldEncryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||
|
||||
ConfigController c = new ConfigControllerImpl(prefs,
|
||||
databaseConfig);
|
||||
|
||||
assertTrue(c.storeEncryptedDatabaseKey(encryptedKeyHex));
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyFile));
|
||||
assertEquals(encryptedKeyHex, loadDatabaseKey(keyBackupFile));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
@@ -8,14 +8,23 @@ import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||
import org.jmock.Expectations;
|
||||
import org.junit.After;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
|
||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.storeDatabaseKey;
|
||||
|
||||
public class PasswordControllerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@@ -26,62 +35,90 @@ public class PasswordControllerImplTest extends BrambleMockTestCase {
|
||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
private final PasswordStrengthEstimator estimator =
|
||||
context.mock(PasswordStrengthEstimator.class);
|
||||
private final SharedPreferences.Editor editor =
|
||||
context.mock(SharedPreferences.Editor.class);
|
||||
|
||||
private final Executor cryptoExecutor = new ImmediateExecutor();
|
||||
|
||||
private final String oldPassword = "some.old.pass";
|
||||
private final String newPassword = "some.new.pass";
|
||||
private final String oldEncryptedHex = "010203";
|
||||
private final String newEncryptedHex = "020304";
|
||||
private final byte[] oldEncryptedBytes = new byte[] {1, 2, 3};
|
||||
private final byte[] newEncryptedBytes = new byte[] {2, 3, 4};
|
||||
private final byte[] keyBytes = getSecretKey().getBytes();
|
||||
private final byte[] oldEncryptedKey = getRandomBytes(123);
|
||||
private final byte[] newEncryptedKey = getRandomBytes(123);
|
||||
private final byte[] key = getSecretKey().getBytes();
|
||||
private final File testDir = getTestDirectory();
|
||||
private final File keyDir = new File(testDir, "key");
|
||||
private final File keyFile = new File(keyDir, "db.key");
|
||||
private final File keyBackupFile = new File(keyDir, "db.key.bak");
|
||||
|
||||
@Test
|
||||
public void testChangePasswordReturnsTrue() {
|
||||
public void testChangePasswordReturnsTrue() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Look up the encrypted DB key
|
||||
oneOf(briarPrefs).getString("key", null);
|
||||
will(returnValue(oldEncryptedHex));
|
||||
will(returnValue(null));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
// Decrypt and re-encrypt the key
|
||||
oneOf(crypto).decryptWithPassword(oldEncryptedBytes, oldPassword);
|
||||
will(returnValue(keyBytes));
|
||||
oneOf(crypto).encryptWithPassword(keyBytes, newPassword);
|
||||
will(returnValue(newEncryptedBytes));
|
||||
// Store the re-encrypted key
|
||||
oneOf(briarPrefs).edit();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).putString("key", newEncryptedHex);
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).commit();
|
||||
oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
|
||||
will(returnValue(key));
|
||||
oneOf(crypto).encryptWithPassword(key, newPassword);
|
||||
will(returnValue(newEncryptedKey));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
|
||||
storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
|
||||
|
||||
PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
|
||||
databaseConfig, cryptoExecutor, crypto, estimator);
|
||||
|
||||
AtomicBoolean capturedResult = new AtomicBoolean(false);
|
||||
p.changePassword(oldPassword, newPassword, capturedResult::set);
|
||||
assertTrue(capturedResult.get());
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(toHexString(newEncryptedKey), loadDatabaseKey(keyFile));
|
||||
assertEquals(toHexString(newEncryptedKey),
|
||||
loadDatabaseKey(keyBackupFile));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testChangePasswordReturnsFalseIfOldPasswordIsWrong() {
|
||||
public void testChangePasswordReturnsFalseIfOldPasswordIsWrong()
|
||||
throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Look up the encrypted DB key
|
||||
oneOf(briarPrefs).getString("key", null);
|
||||
will(returnValue(oldEncryptedHex));
|
||||
will(returnValue(null));
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
// Try to decrypt the key - the password is wrong
|
||||
oneOf(crypto).decryptWithPassword(oldEncryptedBytes, oldPassword);
|
||||
oneOf(crypto).decryptWithPassword(oldEncryptedKey, oldPassword);
|
||||
will(returnValue(null));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
storeDatabaseKey(keyFile, toHexString(oldEncryptedKey));
|
||||
storeDatabaseKey(keyBackupFile, toHexString(oldEncryptedKey));
|
||||
|
||||
PasswordControllerImpl p = new PasswordControllerImpl(briarPrefs,
|
||||
databaseConfig, cryptoExecutor, crypto, estimator);
|
||||
|
||||
AtomicBoolean capturedResult = new AtomicBoolean(true);
|
||||
p.changePassword(oldPassword, newPassword, capturedResult::set);
|
||||
assertFalse(capturedResult.get());
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(toHexString(oldEncryptedKey), loadDatabaseKey(keyFile));
|
||||
assertEquals(toHexString(oldEncryptedKey),
|
||||
loadDatabaseKey(keyBackupFile));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import org.briarproject.bramble.api.crypto.SecretKey;
|
||||
import org.briarproject.bramble.api.db.DatabaseConfig;
|
||||
import org.briarproject.bramble.test.BrambleMockTestCase;
|
||||
import org.briarproject.bramble.test.ImmediateExecutor;
|
||||
import org.briarproject.bramble.test.TestUtils;
|
||||
import org.jmock.Expectations;
|
||||
import org.jmock.lib.legacy.ClassImposteriser;
|
||||
import org.junit.After;
|
||||
@@ -19,10 +18,17 @@ import java.io.File;
|
||||
import java.util.concurrent.Executor;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import static junit.framework.Assert.assertEquals;
|
||||
import static junit.framework.Assert.assertFalse;
|
||||
import static junit.framework.Assert.assertTrue;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_AUTHOR_NAME_LENGTH;
|
||||
import static org.briarproject.bramble.test.TestUtils.deleteTestDirectory;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTestDirectory;
|
||||
import static org.briarproject.bramble.util.StringUtils.getRandomString;
|
||||
import static org.briarproject.bramble.util.StringUtils.toHexString;
|
||||
import static org.briarproject.briar.android.TestDatabaseKeyUtils.loadDatabaseKey;
|
||||
|
||||
public class SetupControllerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@@ -33,18 +39,18 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
|
||||
private final CryptoComponent crypto = context.mock(CryptoComponent.class);
|
||||
private final PasswordStrengthEstimator estimator =
|
||||
context.mock(PasswordStrengthEstimator.class);
|
||||
private final SharedPreferences.Editor editor =
|
||||
context.mock(SharedPreferences.Editor.class);
|
||||
private final SetupActivity setupActivity;
|
||||
|
||||
private final Executor cryptoExecutor = new ImmediateExecutor();
|
||||
|
||||
private final String authorName = getRandomString(MAX_AUTHOR_NAME_LENGTH);
|
||||
private final String password = "some.strong.pass";
|
||||
private final String encryptedHex = "010203";
|
||||
private final byte[] encryptedBytes = new byte[] {1, 2, 3};
|
||||
private final byte[] encryptedKey = getRandomBytes(123);
|
||||
private final SecretKey key = getSecretKey();
|
||||
private final File testDir = TestUtils.getTestDirectory();
|
||||
private final File testDir = getTestDirectory();
|
||||
private final File keyDir = new File(testDir, "key");
|
||||
private final File keyFile = new File(keyDir, "db.key");
|
||||
private final File keyBackupFile = new File(keyDir, "db.key.bak");
|
||||
|
||||
public SetupControllerImplTest() {
|
||||
context.setImposteriser(ClassImposteriser.INSTANCE);
|
||||
@@ -53,7 +59,7 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
|
||||
|
||||
@Test
|
||||
@SuppressWarnings("ResultOfMethodCallIgnored")
|
||||
public void testCreateAccount() {
|
||||
public void testCreateAccount() throws Exception {
|
||||
context.checking(new Expectations() {{
|
||||
// Allow the contents of the data directory to be logged
|
||||
allowing(setupActivity).getApplicationInfo();
|
||||
@@ -76,15 +82,15 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
|
||||
oneOf(databaseConfig).setEncryptionKey(key);
|
||||
// Encrypt the key with the password
|
||||
oneOf(crypto).encryptWithPassword(key.getBytes(), password);
|
||||
will(returnValue(encryptedBytes));
|
||||
will(returnValue(encryptedKey));
|
||||
// Store the encrypted key
|
||||
oneOf(briarPrefs).edit();
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).putString("key", encryptedHex);
|
||||
will(returnValue(editor));
|
||||
oneOf(editor).commit();
|
||||
allowing(databaseConfig).getDatabaseKeyDirectory();
|
||||
will(returnValue(keyDir));
|
||||
}});
|
||||
|
||||
assertFalse(keyFile.exists());
|
||||
assertFalse(keyBackupFile.exists());
|
||||
|
||||
SetupControllerImpl s = new SetupControllerImpl(briarPrefs,
|
||||
databaseConfig, cryptoExecutor, crypto, estimator);
|
||||
s.setSetupActivity(setupActivity);
|
||||
@@ -94,10 +100,15 @@ public class SetupControllerImplTest extends BrambleMockTestCase {
|
||||
s.setPassword(password);
|
||||
s.createAccount(result -> called.set(true));
|
||||
assertTrue(called.get());
|
||||
|
||||
assertTrue(keyFile.exists());
|
||||
assertTrue(keyBackupFile.exists());
|
||||
assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyFile));
|
||||
assertEquals(toHexString(encryptedKey), loadDatabaseKey(keyBackupFile));
|
||||
}
|
||||
|
||||
@After
|
||||
public void tearDown() {
|
||||
TestUtils.deleteTestDirectory(testDir);
|
||||
deleteTestDirectory(testDir);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -92,9 +92,27 @@ class IntroducerProtocolEngine
|
||||
throw new UnsupportedOperationException(); // Invalid in this role
|
||||
}
|
||||
|
||||
IntroducerSession onAbortAction(Transaction txn, IntroducerSession s)
|
||||
IntroducerSession onIntroduceeRemoved(Transaction txn,
|
||||
Introducee remainingIntroducee, IntroducerSession session)
|
||||
throws DbException {
|
||||
return abort(txn, s);
|
||||
// abort session
|
||||
IntroducerSession s = abort(txn, session);
|
||||
// reset information for introducee that was removed
|
||||
Introducee introduceeA, introduceeB;
|
||||
if (remainingIntroducee.author.equals(s.getIntroduceeA().author)) {
|
||||
introduceeA = s.getIntroduceeA();
|
||||
introduceeB =
|
||||
new Introducee(s.getSessionId(), s.getIntroduceeB().groupId,
|
||||
s.getIntroduceeB().author);
|
||||
} else if (remainingIntroducee.author
|
||||
.equals(s.getIntroduceeB().author)) {
|
||||
introduceeA =
|
||||
new Introducee(s.getSessionId(), s.getIntroduceeA().groupId,
|
||||
s.getIntroduceeA().author);
|
||||
introduceeB = s.getIntroduceeB();
|
||||
} else throw new DbException();
|
||||
return new IntroducerSession(s.getSessionId(), s.getState(),
|
||||
s.getRequestTimestamp(), introduceeA, introduceeB);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -79,7 +79,7 @@ class IntroducerSession extends Session<IntroducerState> {
|
||||
i.lastLocalMessageId, remoteMessageId);
|
||||
}
|
||||
|
||||
private Introducee(SessionId sessionId, GroupId groupId,
|
||||
Introducee(SessionId sessionId, GroupId groupId,
|
||||
Author author) {
|
||||
this(sessionId, groupId, author, -1, null, null);
|
||||
}
|
||||
|
||||
@@ -555,7 +555,8 @@ class IntroductionManagerImpl extends ConversationClientImpl
|
||||
IntroducerSession s, MessageId storageId, Introducee i,
|
||||
LocalAuthor localAuthor) throws DbException {
|
||||
if (db.containsContact(txn, i.author.getId(), localAuthor.getId())) {
|
||||
IntroducerSession session = introducerEngine.onAbortAction(txn, s);
|
||||
IntroducerSession session =
|
||||
introducerEngine.onIntroduceeRemoved(txn, i, s);
|
||||
storeSession(txn, storageId, session);
|
||||
} else {
|
||||
db.removeMessage(txn, storageId);
|
||||
|
||||
@@ -19,7 +19,7 @@ public interface TestData {
|
||||
"Ibrahim ibn Sinan",
|
||||
"Muhammad Al-Karaji",
|
||||
"Yang Hui",
|
||||
"René Descartes",
|
||||
"Ren\u00e9 Descartes",
|
||||
"Pierre de Fermat",
|
||||
"Blaise Pascal",
|
||||
"Jacob Bernoulli",
|
||||
@@ -33,13 +33,13 @@ public interface TestData {
|
||||
"George Boole",
|
||||
"John Venn",
|
||||
"Gottlob Frege",
|
||||
"Henri Poincaré",
|
||||
"Henri Poincar\u00e9",
|
||||
"David Hilbert",
|
||||
"Bertrand Russell",
|
||||
"John von Neumann",
|
||||
"Kurt Gödel",
|
||||
"Kurt G\u00f6del",
|
||||
"Alan Turing",
|
||||
"Benoît Mandelbrot",
|
||||
"Beno\u00eet Mandelbrot",
|
||||
"John Nash",
|
||||
getRandomString(MAX_AUTHOR_NAME_LENGTH),
|
||||
getRandomString(MAX_AUTHOR_NAME_LENGTH),
|
||||
|
||||
@@ -44,6 +44,7 @@ import java.util.concurrent.TimeoutException;
|
||||
import static org.briarproject.bramble.api.identity.AuthorConstants.MAX_PUBLIC_KEY_LENGTH;
|
||||
import static org.briarproject.bramble.test.TestPluginConfigModule.TRANSPORT_ID;
|
||||
import static org.briarproject.bramble.test.TestUtils.getRandomBytes;
|
||||
import static org.briarproject.bramble.test.TestUtils.getSecretKey;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTransportProperties;
|
||||
import static org.briarproject.bramble.test.TestUtils.getTransportPropertiesMap;
|
||||
import static org.briarproject.briar.api.introduction.IntroductionManager.CLIENT_ID;
|
||||
@@ -918,6 +919,51 @@ public class IntroductionIntegrationTest
|
||||
.getMessageMetadataAsDictionary(group0.getId()).size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testIntroductionAfterReAddingContacts() throws Exception {
|
||||
// make introduction
|
||||
long time = clock.currentTimeMillis();
|
||||
introductionManager0
|
||||
.makeIntroduction(contact1From0, contact2From0, null, time);
|
||||
|
||||
// 0 and 1 remove and re-add each other
|
||||
contactManager0.removeContact(contactId1From0);
|
||||
contactManager1.removeContact(contactId0From1);
|
||||
contactId1From0 = contactManager0
|
||||
.addContact(author1, author0.getId(), getSecretKey(),
|
||||
clock.currentTimeMillis(), true, true, true);
|
||||
contact1From0 = contactManager0.getContact(contactId1From0);
|
||||
contactId0From1 = contactManager1
|
||||
.addContact(author0, author1.getId(), getSecretKey(),
|
||||
clock.currentTimeMillis(), true, true, true);
|
||||
contact0From1 = contactManager1.getContact(contactId0From1);
|
||||
|
||||
// Sync initial client versioning updates and transport properties
|
||||
sync0To1(1, true);
|
||||
sync1To0(1, true);
|
||||
sync0To1(2, true);
|
||||
sync1To0(1, true);
|
||||
|
||||
// a new introduction should be possible
|
||||
assertTrue(introductionManager0
|
||||
.canIntroduce(contact1From0, contact2From0));
|
||||
|
||||
// listen to events, so we don't miss new request
|
||||
addListeners(true, true);
|
||||
|
||||
// make new introduction
|
||||
time = clock.currentTimeMillis();
|
||||
introductionManager0
|
||||
.makeIntroduction(contact1From0, contact2From0, null, time);
|
||||
|
||||
// introduction should sync and not be INVALID or PENDING
|
||||
sync0To1(1, true);
|
||||
|
||||
// assert that new request was received
|
||||
eventWaiter.await(TIMEOUT, 1);
|
||||
assertTrue(listener1.requestReceived);
|
||||
}
|
||||
|
||||
private void testModifiedResponse(StateVisitor visitor)
|
||||
throws Exception {
|
||||
addListeners(true, true);
|
||||
@@ -1099,6 +1145,7 @@ public class IntroductionIntegrationTest
|
||||
protected volatile boolean aborted = false;
|
||||
protected volatile Event latestEvent;
|
||||
|
||||
@SuppressWarnings("WeakerAccess")
|
||||
IntroductionResponse getResponse() {
|
||||
assertTrue(
|
||||
latestEvent instanceof IntroductionResponseReceivedEvent);
|
||||
|
||||
Reference in New Issue
Block a user