Compare commits
69 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4ea481f9dc | ||
|
|
e94fa0d752 | ||
|
|
817070e76f | ||
|
|
92b90cd798 | ||
|
|
33ecc4d690 | ||
|
|
7d5e66eb6e | ||
|
|
d4ac0c077d | ||
|
|
cbe87aa05c | ||
|
|
9e2f82954f | ||
|
|
d4d684b670 | ||
|
|
c43ef8bce0 | ||
|
|
54a882a11d | ||
|
|
73e2f6ce59 | ||
|
|
54d2184c72 | ||
|
|
832763f695 | ||
|
|
40629a3bcf | ||
|
|
5a3daf4846 | ||
|
|
fd1a18d2d0 | ||
|
|
ad5d6d5bb7 | ||
|
|
926d3c929f | ||
|
|
edc20883eb | ||
|
|
0d7363e36e | ||
|
|
ca6d8a8a0d | ||
|
|
c414334059 | ||
|
|
24a38985cf | ||
|
|
60f2d4d6b6 | ||
|
|
06659fd98f | ||
|
|
3c9c5213a7 | ||
|
|
a183f8d387 | ||
|
|
216446c55b | ||
|
|
bc143059f6 | ||
|
|
33000582ed | ||
|
|
e651f352bb | ||
|
|
cab4a06974 | ||
|
|
ccc1f5e9d6 | ||
|
|
b860aeff85 | ||
|
|
34c885f08d | ||
|
|
71ab6f5c7d | ||
|
|
61fbf382eb | ||
|
|
8b21f3f7d6 | ||
|
|
941d0089f4 | ||
|
|
8b8c6dd45f | ||
|
|
938545444e | ||
|
|
d827ab1b36 | ||
|
|
4701e59197 | ||
|
|
2b2da84918 | ||
|
|
d229a4274c | ||
|
|
64711771f0 | ||
|
|
ad54d2a05f | ||
|
|
068c40336c | ||
|
|
11cfc4f1a1 | ||
|
|
f51989b23e | ||
|
|
0b4fe84a41 | ||
|
|
b855a1805f | ||
|
|
339193af12 | ||
|
|
574f33c92d | ||
|
|
b6a9eb5bf2 | ||
|
|
54b43b7536 | ||
|
|
37c9fb7cd1 | ||
|
|
667d22bace | ||
|
|
3d782449ed | ||
|
|
003095b08c | ||
|
|
d121d9531e | ||
|
|
42aeca26f1 | ||
|
|
ea8a1bae46 | ||
|
|
054fcdca8d | ||
|
|
278220cf18 | ||
|
|
35eeaa9bd5 | ||
|
|
fa423e4432 |
@@ -2,8 +2,8 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
package="org.thoughtcrime.securesms"
|
||||
android:versionCode="67"
|
||||
android:versionName="2.0.4">
|
||||
android:versionCode="69"
|
||||
android:versionName="2.0.6">
|
||||
|
||||
<uses-sdk android:minSdkVersion="9" android:targetSdkVersion="19"/>
|
||||
|
||||
@@ -69,7 +69,7 @@
|
||||
<category android:name="android.intent.category.DEFAULT"/>
|
||||
<data android:mimeType="audio/*" />
|
||||
<data android:mimeType="image/*" />
|
||||
<data android:mimeType="text/*" />
|
||||
<data android:mimeType="text/plain" />
|
||||
<data android:mimeType="video/*" />
|
||||
</intent-filter>
|
||||
|
||||
@@ -90,7 +90,7 @@
|
||||
android:windowSoftInputMode="stateUnchanged"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".MmsPreferencesActivity"
|
||||
<activity android:name=".MmsPreferencesActivity"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".ConversationListActivity"
|
||||
@@ -135,15 +135,15 @@
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".SingleContactSelectionActivity"
|
||||
android:label="@string/AndroidManifest__select_contact"
|
||||
<activity android:name=".NewConversationActivity"
|
||||
android:label="@string/AndroidManifest__select_contacts"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".PushContactSelectionActivity"
|
||||
android:label="@string/AndroidManifest__select_contacts"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
android:label="@string/AndroidManifest__select_contacts"
|
||||
android:windowSoftInputMode="stateHidden"
|
||||
android:configChanges="touchscreen|keyboard|keyboardHidden|orientation|screenLayout|screenSize"/>
|
||||
|
||||
<activity android:name=".AutoInitiateActivity"
|
||||
android:theme="@style/TextSecure.Light.Dialog"
|
||||
@@ -205,6 +205,7 @@
|
||||
<service android:enabled="true" android:name=".service.SendReceiveService"/>
|
||||
<service android:enabled="true" android:name=".service.RegistrationService"/>
|
||||
<service android:enabled="true" android:name=".service.DirectoryRefreshService"/>
|
||||
<service android:enabled="true" android:name=".service.PreKeyService"/>
|
||||
<service android:enabled="true" android:name=".gcm.GcmIntentService"/>
|
||||
|
||||
<service android:name=".service.QuickResponseService"
|
||||
|
||||
53
BUILDING.md
@@ -1,11 +1,30 @@
|
||||
Building TextSecure
|
||||
===================
|
||||
=====================
|
||||
|
||||
1. Ensure the 'Android Support Repository' is installed from the Android SDK manager.
|
||||
Basics
|
||||
------
|
||||
|
||||
Execute Gradle:
|
||||
TextSecure uses [Gradle](http://gradle.org) to build the project and to maintain
|
||||
dependencies.
|
||||
|
||||
./gradlew build
|
||||
Building TextSecure
|
||||
-------------------
|
||||
|
||||
The following steps should help you (re)build TextSecure from the command line.
|
||||
|
||||
1. Checkout the source somewhere on your filesystem with
|
||||
|
||||
git clone https://github.com/WhisperSystems/TextSecure.git
|
||||
|
||||
2. Make sure you have the [Android SDK](https://developer.android.com/sdk/index.html) installed somewhere on your system.
|
||||
3. Ensure the "Android Support Repository" and "Android SDK Build-tools" are installed from the Android SDK manager.
|
||||
4. Create a local.properties file at the root of your source checkout and add an sdk.dir entry to it.
|
||||
|
||||
sdk.dir=\<path to your sdk installation\>
|
||||
|
||||
5. Execute Gradle:
|
||||
|
||||
./gradlew build
|
||||
|
||||
Re-building native components
|
||||
-----------------------------
|
||||
@@ -20,3 +39,29 @@ Execute ndk-build:
|
||||
ndk-build
|
||||
|
||||
Afterwards, execute Gradle as above to re-create the APK.
|
||||
|
||||
Setting up a development environment
|
||||
------------------------------------
|
||||
|
||||
[Android Studio](https://developer.android.com/sdk/installing/studio.html) is the recommended development environment.
|
||||
|
||||
1. Install Android Studio
|
||||
2. Make sure the "Android Support Repository" is installed in the Android Studio SDK.
|
||||
3. Make sure the latest "Android SDK build-tools" is installed in the Android Studio SDK.
|
||||
4. Create a new Android Studio project. from the Quickstart pannel (use File > Close Project to see it), choose "Checkout from Version Control" then "git".
|
||||
5. Paste the URL for the TextSecure project when prompted (https://github.com/WhisperSystems/TextSecure.git)
|
||||
6. Android studio should detect the presence of a project file and ask you wethere to open it. Click "yes".
|
||||
7. Default config options should be good enough.
|
||||
8. Project initialisation and build should proceed.
|
||||
|
||||
Contributing code
|
||||
-----------------
|
||||
|
||||
Code contributions should be sent via github as pull requests, from feature branches [as explained here](https://help.github.com/articles/using-pull-requests).
|
||||
|
||||
Mailing list
|
||||
------------
|
||||
|
||||
Development discussion happens on the whispersystems mailing list.
|
||||
[To join](https://lists.riseup.net/www/info/whispersystems)
|
||||
Send emails to whispersystems@lists.riseup.net
|
||||
|
||||
@@ -12,6 +12,9 @@ Current BitHub Payment For Commit:
|
||||
=================
|
||||
[](https://whispersystems.org/blog/bithub/)
|
||||
|
||||
Building and contributing code
|
||||
==============================
|
||||
Instructions on how to build TextSecure, as well as on how to setup an IDE to modify it can be found in the "BUILDING.md" file.
|
||||
|
||||
Bug tracker
|
||||
-----------
|
||||
|
||||
BIN
artwork/ic_send.psd
Normal file
@@ -3,7 +3,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.8.+'
|
||||
classpath 'com.android.tools.build:gradle:0.9.+'
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ dependencies {
|
||||
compile 'com.actionbarsherlock:actionbarsherlock:4.4.0@aar'
|
||||
compile 'com.android.support:support-v4:19.0.1'
|
||||
compile 'com.google.android.gcm:gcm-client:1.0.2'
|
||||
compile 'se.emilsjolander:stickylistheaders:2.2.0'
|
||||
|
||||
compile project(':library')
|
||||
}
|
||||
|
||||
4
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -1,6 +1,6 @@
|
||||
#Sat Dec 21 23:48:05 PST 2013
|
||||
#Mon Mar 10 23:44:05 PDT 2014
|
||||
distributionBase=GRADLE_USER_HOME
|
||||
distributionPath=wrapper/dists
|
||||
zipStoreBase=GRADLE_USER_HOME
|
||||
zipStorePath=wrapper/dists
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.10-bin.zip
|
||||
distributionUrl=http\://services.gradle.org/distributions/gradle-1.11-all.zip
|
||||
|
||||
@@ -4,7 +4,7 @@ buildscript {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:0.8.+'
|
||||
classpath 'com.android.tools.build:gradle:0.9.+'
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,11 +22,16 @@
|
||||
#include "curve25519-donna.h"
|
||||
|
||||
JNIEXPORT jbyteArray JNICALL Java_org_whispersystems_textsecure_crypto_ecc_Curve25519_generatePrivateKey
|
||||
(JNIEnv *env, jclass clazz, jbyteArray random)
|
||||
(JNIEnv *env, jclass clazz, jbyteArray random, jboolean ephemeral)
|
||||
{
|
||||
uint8_t* privateKey = (uint8_t*)(*env)->GetByteArrayElements(env, random, 0);
|
||||
|
||||
privateKey[0] &= 248;
|
||||
|
||||
if (ephemeral) {
|
||||
privateKey[0] |= 1;
|
||||
}
|
||||
|
||||
privateKey[31] &= 127;
|
||||
privateKey[31] |= 64;
|
||||
|
||||
|
||||
@@ -720,9 +720,6 @@ curve25519_donna(u8 *mypublic, const u8 *secret, const u8 *basepoint) {
|
||||
int i;
|
||||
|
||||
for (i = 0; i < 32; ++i) e[i] = secret[i];
|
||||
e[0] &= 248;
|
||||
e[31] &= 127;
|
||||
e[31] |= 64;
|
||||
|
||||
fexpand(bp, basepoint);
|
||||
cmult(x, z, e, bp);
|
||||
|
||||
@@ -54,6 +54,13 @@ message SessionStructure {
|
||||
|
||||
optional uint32 remoteRegistrationId = 10;
|
||||
optional uint32 localRegistrationId = 11;
|
||||
|
||||
optional bool needsRefresh = 12;
|
||||
}
|
||||
|
||||
message RecordStructure {
|
||||
optional SessionStructure currentSession = 1;
|
||||
repeated SessionStructure previousSessions = 2;
|
||||
}
|
||||
|
||||
message PreKeyRecordStructure {
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
package org.whispersystems.textsecure.crypto;
|
||||
|
||||
public class DuplicateMessageException extends Exception {
|
||||
public DuplicateMessageException(String s) {
|
||||
super(s);
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,6 @@ import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStreamReader;
|
||||
import java.util.Arrays;
|
||||
import java.util.Comparator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@@ -49,7 +48,7 @@ public class PreKeyUtil {
|
||||
|
||||
for (int i=0;i<BATCH_SIZE;i++) {
|
||||
int preKeyId = (preKeyIdOffset + i) % Medium.MAX_VALUE;
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair();
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair(true);
|
||||
PreKeyRecord record = new PreKeyRecord(context, masterSecret, preKeyId, keyPair);
|
||||
|
||||
record.save();
|
||||
@@ -70,7 +69,7 @@ public class PreKeyUtil {
|
||||
}
|
||||
}
|
||||
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair();
|
||||
ECKeyPair keyPair = Curve25519.generateKeyPair(true);
|
||||
PreKeyRecord record = new PreKeyRecord(context, masterSecret, Medium.MAX_VALUE, keyPair);
|
||||
|
||||
record.save();
|
||||
@@ -78,38 +77,38 @@ public class PreKeyUtil {
|
||||
return record;
|
||||
}
|
||||
|
||||
public static List<PreKeyRecord> getPreKeys(Context context, MasterSecret masterSecret) {
|
||||
List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
|
||||
File directory = getPreKeysDirectory(context);
|
||||
String[] keyRecordIds = directory.list();
|
||||
|
||||
Arrays.sort(keyRecordIds, new PreKeyRecordIdComparator());
|
||||
|
||||
for (String keyRecordId : keyRecordIds) {
|
||||
try {
|
||||
if (!keyRecordId.equals(PreKeyIndex.FILE_NAME) && Integer.parseInt(keyRecordId) != Medium.MAX_VALUE) {
|
||||
records.add(new PreKeyRecord(context, masterSecret, Integer.parseInt(keyRecordId)));
|
||||
}
|
||||
} catch (InvalidKeyIdException e) {
|
||||
Log.w("PreKeyUtil", e);
|
||||
new File(getPreKeysDirectory(context), keyRecordId).delete();
|
||||
} catch (NumberFormatException nfe) {
|
||||
Log.w("PreKeyUtil", nfe);
|
||||
new File(getPreKeysDirectory(context), keyRecordId).delete();
|
||||
}
|
||||
}
|
||||
|
||||
return records;
|
||||
}
|
||||
|
||||
public static void clearPreKeys(Context context) {
|
||||
File directory = getPreKeysDirectory(context);
|
||||
String[] keyRecords = directory.list();
|
||||
|
||||
for (String keyRecord : keyRecords) {
|
||||
new File(directory, keyRecord).delete();
|
||||
}
|
||||
}
|
||||
// public static List<PreKeyRecord> getPreKeys(Context context, MasterSecret masterSecret) {
|
||||
// List<PreKeyRecord> records = new LinkedList<PreKeyRecord>();
|
||||
// File directory = getPreKeysDirectory(context);
|
||||
// String[] keyRecordIds = directory.list();
|
||||
//
|
||||
// Arrays.sort(keyRecordIds, new PreKeyRecordIdComparator());
|
||||
//
|
||||
// for (String keyRecordId : keyRecordIds) {
|
||||
// try {
|
||||
// if (!keyRecordId.equals(PreKeyIndex.FILE_NAME) && Integer.parseInt(keyRecordId) != Medium.MAX_VALUE) {
|
||||
// records.add(new PreKeyRecord(context, masterSecret, Integer.parseInt(keyRecordId)));
|
||||
// }
|
||||
// } catch (InvalidKeyIdException e) {
|
||||
// Log.w("PreKeyUtil", e);
|
||||
// new File(getPreKeysDirectory(context), keyRecordId).delete();
|
||||
// } catch (NumberFormatException nfe) {
|
||||
// Log.w("PreKeyUtil", nfe);
|
||||
// new File(getPreKeysDirectory(context), keyRecordId).delete();
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return records;
|
||||
// }
|
||||
//
|
||||
// public static void clearPreKeys(Context context) {
|
||||
// File directory = getPreKeysDirectory(context);
|
||||
// String[] keyRecords = directory.list();
|
||||
//
|
||||
// for (String keyRecord : keyRecords) {
|
||||
// new File(directory, keyRecord).delete();
|
||||
// }
|
||||
// }
|
||||
|
||||
private static void setNextPreKeyId(Context context, int id) {
|
||||
try {
|
||||
@@ -126,7 +125,7 @@ public class PreKeyUtil {
|
||||
try {
|
||||
File nextFile = new File(getPreKeysDirectory(context), PreKeyIndex.FILE_NAME);
|
||||
|
||||
if (nextFile.exists()) {
|
||||
if (!nextFile.exists()) {
|
||||
return Util.getSecureRandom().nextInt(Medium.MAX_VALUE);
|
||||
} else {
|
||||
InputStreamReader reader = new InputStreamReader(new FileInputStream(nextFile));
|
||||
|
||||
@@ -29,7 +29,7 @@ public abstract class SessionCipher {
|
||||
protected static final Object SESSION_LOCK = new Object();
|
||||
|
||||
public abstract CiphertextMessage encrypt(byte[] paddedMessage);
|
||||
public abstract byte[] decrypt(byte[] decodedMessage) throws InvalidMessageException;
|
||||
public abstract byte[] decrypt(byte[] decodedMessage) throws InvalidMessageException, DuplicateMessageException;
|
||||
public abstract int getRemoteRegistrationId();
|
||||
|
||||
public static SessionCipher createFor(Context context,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package org.whispersystems.textsecure.crypto;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
||||
@@ -14,10 +15,12 @@ import org.whispersystems.textsecure.crypto.ratchet.MessageKeys;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.RootKey;
|
||||
import org.whispersystems.textsecure.storage.RecipientDevice;
|
||||
import org.whispersystems.textsecure.storage.SessionRecordV2;
|
||||
import org.whispersystems.textsecure.storage.SessionState;
|
||||
import org.whispersystems.textsecure.util.Conversions;
|
||||
|
||||
import java.security.InvalidAlgorithmParameterException;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.BadPaddingException;
|
||||
import javax.crypto.Cipher;
|
||||
@@ -45,27 +48,28 @@ public class SessionCipherV2 extends SessionCipher {
|
||||
public CiphertextMessage encrypt(byte[] paddedMessage) {
|
||||
synchronized (SESSION_LOCK) {
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
ChainKey chainKey = sessionRecord.getSenderChainKey();
|
||||
SessionState sessionState = sessionRecord.getSessionState();
|
||||
ChainKey chainKey = sessionState.getSenderChainKey();
|
||||
MessageKeys messageKeys = chainKey.getMessageKeys();
|
||||
ECPublicKey senderEphemeral = sessionRecord.getSenderEphemeral();
|
||||
int previousCounter = sessionRecord.getPreviousCounter();
|
||||
ECPublicKey senderEphemeral = sessionState.getSenderEphemeral();
|
||||
int previousCounter = sessionState.getPreviousCounter();
|
||||
|
||||
byte[] ciphertextBody = getCiphertext(messageKeys, paddedMessage);
|
||||
CiphertextMessage ciphertextMessage = new WhisperMessageV2(messageKeys.getMacKey(),
|
||||
senderEphemeral, chainKey.getIndex(),
|
||||
previousCounter, ciphertextBody);
|
||||
|
||||
if (sessionRecord.hasPendingPreKey()) {
|
||||
Pair<Integer, ECPublicKey> pendingPreKey = sessionRecord.getPendingPreKey();
|
||||
int localRegistrationId = sessionRecord.getLocalRegistrationId();
|
||||
if (sessionState.hasPendingPreKey()) {
|
||||
Pair<Integer, ECPublicKey> pendingPreKey = sessionState.getPendingPreKey();
|
||||
int localRegistrationId = sessionState.getLocalRegistrationId();
|
||||
|
||||
ciphertextMessage = new PreKeyWhisperMessage(localRegistrationId, pendingPreKey.first,
|
||||
pendingPreKey.second,
|
||||
sessionRecord.getLocalIdentityKey(),
|
||||
sessionState.getLocalIdentityKey(),
|
||||
(WhisperMessageV2) ciphertextMessage);
|
||||
}
|
||||
|
||||
sessionRecord.setSenderChainKey(chainKey.getNextChainKey());
|
||||
sessionState.setSenderChainKey(chainKey.getNextChainKey());
|
||||
sessionRecord.save();
|
||||
|
||||
return ciphertextMessage;
|
||||
@@ -73,52 +77,88 @@ public class SessionCipherV2 extends SessionCipher {
|
||||
}
|
||||
|
||||
@Override
|
||||
public byte[] decrypt(byte[] decodedMessage) throws InvalidMessageException {
|
||||
public byte[] decrypt(byte[] decodedMessage)
|
||||
throws InvalidMessageException, DuplicateMessageException
|
||||
{
|
||||
synchronized (SESSION_LOCK) {
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
WhisperMessageV2 ciphertextMessage = new WhisperMessageV2(decodedMessage);
|
||||
ECPublicKey theirEphemeral = ciphertextMessage.getSenderEphemeral();
|
||||
int counter = ciphertextMessage.getCounter();
|
||||
ChainKey chainKey = getOrCreateChainKey(sessionRecord, theirEphemeral);
|
||||
MessageKeys messageKeys = getOrCreateMessageKeys(sessionRecord, theirEphemeral,
|
||||
chainKey, counter);
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
SessionState sessionState = sessionRecord.getSessionState();
|
||||
List<SessionState> previousStates = sessionRecord.getPreviousSessions();
|
||||
|
||||
ciphertextMessage.verifyMac(messageKeys.getMacKey());
|
||||
try {
|
||||
byte[] plaintext = decrypt(sessionState, decodedMessage);
|
||||
sessionRecord.save();
|
||||
|
||||
byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody());
|
||||
return plaintext;
|
||||
} catch (InvalidMessageException e) {
|
||||
Log.w("SessionCipherV2", e);
|
||||
}
|
||||
|
||||
sessionRecord.clearPendingPreKey();
|
||||
sessionRecord.save();
|
||||
for (SessionState previousState : previousStates) {
|
||||
try {
|
||||
Log.w("SessionCipherV2", "Attempting decrypt on previous state...");
|
||||
byte[] plaintext = decrypt(previousState, decodedMessage);
|
||||
sessionRecord.save();
|
||||
|
||||
return plaintext;
|
||||
return plaintext;
|
||||
} catch (InvalidMessageException e) {
|
||||
Log.w("SessionCipherV2", e);
|
||||
}
|
||||
}
|
||||
|
||||
throw new InvalidMessageException("No valid sessions.");
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] decrypt(SessionState sessionState, byte[] decodedMessage)
|
||||
throws InvalidMessageException, DuplicateMessageException
|
||||
{
|
||||
if (!sessionState.hasSenderChain()) {
|
||||
throw new InvalidMessageException("Uninitialized session!");
|
||||
}
|
||||
|
||||
WhisperMessageV2 ciphertextMessage = new WhisperMessageV2(decodedMessage);
|
||||
ECPublicKey theirEphemeral = ciphertextMessage.getSenderEphemeral();
|
||||
int counter = ciphertextMessage.getCounter();
|
||||
ChainKey chainKey = getOrCreateChainKey(sessionState, theirEphemeral);
|
||||
MessageKeys messageKeys = getOrCreateMessageKeys(sessionState, theirEphemeral,
|
||||
chainKey, counter);
|
||||
|
||||
ciphertextMessage.verifyMac(messageKeys.getMacKey());
|
||||
|
||||
byte[] plaintext = getPlaintext(messageKeys, ciphertextMessage.getBody());
|
||||
|
||||
sessionState.clearPendingPreKey();
|
||||
|
||||
return plaintext;
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getRemoteRegistrationId() {
|
||||
synchronized (SESSION_LOCK) {
|
||||
SessionRecordV2 sessionRecord = getSessionRecord();
|
||||
return sessionRecord.getRemoteRegistrationId();
|
||||
return sessionRecord.getSessionState().getRemoteRegistrationId();
|
||||
}
|
||||
}
|
||||
|
||||
private ChainKey getOrCreateChainKey(SessionRecordV2 sessionRecord, ECPublicKey theirEphemeral)
|
||||
private ChainKey getOrCreateChainKey(SessionState sessionState, ECPublicKey theirEphemeral)
|
||||
throws InvalidMessageException
|
||||
{
|
||||
try {
|
||||
if (sessionRecord.hasReceiverChain(theirEphemeral)) {
|
||||
return sessionRecord.getReceiverChainKey(theirEphemeral);
|
||||
if (sessionState.hasReceiverChain(theirEphemeral)) {
|
||||
return sessionState.getReceiverChainKey(theirEphemeral);
|
||||
} else {
|
||||
RootKey rootKey = sessionRecord.getRootKey();
|
||||
ECKeyPair ourEphemeral = sessionRecord.getSenderEphemeralPair();
|
||||
RootKey rootKey = sessionState.getRootKey();
|
||||
ECKeyPair ourEphemeral = sessionState.getSenderEphemeralPair();
|
||||
Pair<RootKey, ChainKey> receiverChain = rootKey.createChain(theirEphemeral, ourEphemeral);
|
||||
ECKeyPair ourNewEphemeral = Curve.generateKeyPairForType(Curve.DJB_TYPE);
|
||||
ECKeyPair ourNewEphemeral = Curve.generateKeyPairForType(Curve.DJB_TYPE, true);
|
||||
Pair<RootKey, ChainKey> senderChain = receiverChain.first.createChain(theirEphemeral, ourNewEphemeral);
|
||||
|
||||
sessionRecord.setRootKey(senderChain.first);
|
||||
sessionRecord.addReceiverChain(theirEphemeral, receiverChain.second);
|
||||
sessionRecord.setPreviousCounter(sessionRecord.getSenderChainKey().getIndex()-1);
|
||||
sessionRecord.setSenderChain(ourNewEphemeral, senderChain.second);
|
||||
sessionState.setRootKey(senderChain.first);
|
||||
sessionState.addReceiverChain(theirEphemeral, receiverChain.second);
|
||||
sessionState.setPreviousCounter(sessionState.getSenderChainKey().getIndex()-1);
|
||||
sessionState.setSenderChain(ourNewEphemeral, senderChain.second);
|
||||
|
||||
return receiverChain.second;
|
||||
}
|
||||
@@ -127,30 +167,31 @@ public class SessionCipherV2 extends SessionCipher {
|
||||
}
|
||||
}
|
||||
|
||||
private MessageKeys getOrCreateMessageKeys(SessionRecordV2 sessionRecord,
|
||||
private MessageKeys getOrCreateMessageKeys(SessionState sessionState,
|
||||
ECPublicKey theirEphemeral,
|
||||
ChainKey chainKey, int counter)
|
||||
throws InvalidMessageException
|
||||
throws InvalidMessageException, DuplicateMessageException
|
||||
{
|
||||
if (chainKey.getIndex() > counter) {
|
||||
if (sessionRecord.hasMessageKeys(theirEphemeral, counter)) {
|
||||
return sessionRecord.removeMessageKeys(theirEphemeral, counter);
|
||||
if (sessionState.hasMessageKeys(theirEphemeral, counter)) {
|
||||
return sessionState.removeMessageKeys(theirEphemeral, counter);
|
||||
} else {
|
||||
throw new InvalidMessageException("Received message with old counter!");
|
||||
throw new DuplicateMessageException("Received message with old counter: " +
|
||||
chainKey.getIndex() + " , " + counter);
|
||||
}
|
||||
}
|
||||
|
||||
if (chainKey.getIndex() - counter > 500) {
|
||||
throw new InvalidMessageException("Over 500 messages into the future!");
|
||||
if (chainKey.getIndex() - counter > 2000) {
|
||||
throw new InvalidMessageException("Over 2000 messages into the future!");
|
||||
}
|
||||
|
||||
while (chainKey.getIndex() < counter) {
|
||||
MessageKeys messageKeys = chainKey.getMessageKeys();
|
||||
sessionRecord.setMessageKeys(theirEphemeral, messageKeys);
|
||||
sessionState.setMessageKeys(theirEphemeral, messageKeys);
|
||||
chainKey = chainKey.getNextChainKey();
|
||||
}
|
||||
|
||||
sessionRecord.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey());
|
||||
sessionState.setReceiverChainKey(theirEphemeral, chainKey.getNextChainKey());
|
||||
return chainKey.getMessageKeys();
|
||||
}
|
||||
|
||||
|
||||
@@ -25,9 +25,9 @@ public class Curve {
|
||||
private static final int NIST_TYPE2 = 0x03;
|
||||
public static final int DJB_TYPE = 0x05;
|
||||
|
||||
public static ECKeyPair generateKeyPairForType(int keyType) {
|
||||
public static ECKeyPair generateKeyPairForType(int keyType, boolean ephemeral) {
|
||||
if (keyType == DJB_TYPE) {
|
||||
return Curve25519.generateKeyPair();
|
||||
return Curve25519.generateKeyPair(ephemeral);
|
||||
} else if (keyType == NIST_TYPE || keyType == NIST_TYPE2) {
|
||||
return CurveP256.generateKeyPair();
|
||||
} else {
|
||||
@@ -35,11 +35,11 @@ public class Curve {
|
||||
}
|
||||
}
|
||||
|
||||
public static ECKeyPair generateKeyPairForSession(int messageVersion) {
|
||||
public static ECKeyPair generateKeyPairForSession(int messageVersion, boolean ephemeral) {
|
||||
if (messageVersion <= CiphertextMessage.LEGACY_VERSION) {
|
||||
return generateKeyPairForType(NIST_TYPE);
|
||||
return generateKeyPairForType(NIST_TYPE, ephemeral);
|
||||
} else {
|
||||
return generateKeyPairForType(DJB_TYPE);
|
||||
return generateKeyPairForType(DJB_TYPE, ephemeral);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -37,10 +37,10 @@ public class Curve25519 {
|
||||
|
||||
private static native byte[] calculateAgreement(byte[] ourPrivate, byte[] theirPublic);
|
||||
private static native byte[] generatePublicKey(byte[] privateKey);
|
||||
private static native byte[] generatePrivateKey(byte[] random);
|
||||
private static native byte[] generatePrivateKey(byte[] random, boolean ephemeral);
|
||||
|
||||
public static ECKeyPair generateKeyPair() {
|
||||
byte[] privateKey = generatePrivateKey();
|
||||
public static ECKeyPair generateKeyPair(boolean ephemeral) {
|
||||
byte[] privateKey = generatePrivateKey(ephemeral);
|
||||
byte[] publicKey = generatePublicKey(privateKey);
|
||||
|
||||
return new ECKeyPair(new DjbECPublicKey(publicKey), new DjbECPrivateKey(privateKey));
|
||||
@@ -65,11 +65,11 @@ public class Curve25519 {
|
||||
return new DjbECPublicKey(keyBytes);
|
||||
}
|
||||
|
||||
private static byte[] generatePrivateKey() {
|
||||
private static byte[] generatePrivateKey(boolean ephemeral) {
|
||||
byte[] privateKey = new byte[32];
|
||||
random.nextBytes(privateKey);
|
||||
|
||||
return generatePrivateKey(privateKey);
|
||||
return generatePrivateKey(privateKey, ephemeral);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.crypto.kdf.DerivedSecrets;
|
||||
import org.whispersystems.textsecure.crypto.kdf.HKDF;
|
||||
import org.whispersystems.textsecure.storage.SessionRecordV2;
|
||||
import org.whispersystems.textsecure.storage.SessionState;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
|
||||
public class RatchetingSession {
|
||||
|
||||
public static void initializeSession(SessionRecordV2 sessionRecord,
|
||||
public static void initializeSession(SessionState sessionState,
|
||||
ECKeyPair ourBaseKey,
|
||||
ECPublicKey theirBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
@@ -27,48 +27,48 @@ public class RatchetingSession {
|
||||
throws InvalidKeyException
|
||||
{
|
||||
if (isAlice(ourBaseKey.getPublicKey(), theirBaseKey, ourEphemeralKey.getPublicKey(), theirEphemeralKey)) {
|
||||
initializeSessionAsAlice(sessionRecord, ourBaseKey, theirBaseKey, theirEphemeralKey,
|
||||
initializeSessionAsAlice(sessionState, ourBaseKey, theirBaseKey, theirEphemeralKey,
|
||||
ourIdentityKey, theirIdentityKey);
|
||||
} else {
|
||||
initializeSessionAsBob(sessionRecord, ourBaseKey, theirBaseKey,
|
||||
initializeSessionAsBob(sessionState, ourBaseKey, theirBaseKey,
|
||||
ourEphemeralKey, ourIdentityKey, theirIdentityKey);
|
||||
}
|
||||
}
|
||||
|
||||
private static void initializeSessionAsAlice(SessionRecordV2 sessionRecord,
|
||||
private static void initializeSessionAsAlice(SessionState sessionState,
|
||||
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
|
||||
ECPublicKey theirEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey,
|
||||
IdentityKey theirIdentityKey)
|
||||
throws InvalidKeyException
|
||||
{
|
||||
sessionRecord.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionRecord.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
sessionState.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
|
||||
ECKeyPair sendingKey = Curve.generateKeyPairForType(ourIdentityKey.getPublicKey().getPublicKey().getType());
|
||||
ECKeyPair sendingKey = Curve.generateKeyPairForType(ourIdentityKey.getPublicKey().getPublicKey().getType(), true);
|
||||
Pair<RootKey, ChainKey> receivingChain = calculate3DHE(true, ourBaseKey, theirBaseKey, ourIdentityKey, theirIdentityKey);
|
||||
Pair<RootKey, ChainKey> sendingChain = receivingChain.first.createChain(theirEphemeralKey, sendingKey);
|
||||
|
||||
sessionRecord.addReceiverChain(theirEphemeralKey, receivingChain.second);
|
||||
sessionRecord.setSenderChain(sendingKey, sendingChain.second);
|
||||
sessionRecord.setRootKey(sendingChain.first);
|
||||
sessionState.addReceiverChain(theirEphemeralKey, receivingChain.second);
|
||||
sessionState.setSenderChain(sendingKey, sendingChain.second);
|
||||
sessionState.setRootKey(sendingChain.first);
|
||||
}
|
||||
|
||||
private static void initializeSessionAsBob(SessionRecordV2 sessionRecord,
|
||||
private static void initializeSessionAsBob(SessionState sessionState,
|
||||
ECKeyPair ourBaseKey, ECPublicKey theirBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey,
|
||||
IdentityKey theirIdentityKey)
|
||||
throws InvalidKeyException
|
||||
{
|
||||
sessionRecord.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionRecord.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
sessionState.setRemoteIdentityKey(theirIdentityKey);
|
||||
sessionState.setLocalIdentityKey(ourIdentityKey.getPublicKey());
|
||||
|
||||
Pair<RootKey, ChainKey> sendingChain = calculate3DHE(false, ourBaseKey, theirBaseKey,
|
||||
ourIdentityKey, theirIdentityKey);
|
||||
|
||||
sessionRecord.setSenderChain(ourEphemeralKey, sendingChain.second);
|
||||
sessionRecord.setRootKey(sendingChain.first);
|
||||
sessionState.setSenderChain(ourEphemeralKey, sendingChain.second);
|
||||
sessionState.setRootKey(sendingChain.first);
|
||||
}
|
||||
|
||||
private static Pair<RootKey, ChainKey> calculate3DHE(boolean isAlice,
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
package org.whispersystems.textsecure.push;
|
||||
|
||||
public class PreKeyStatus {
|
||||
|
||||
private int count;
|
||||
|
||||
public PreKeyStatus() {}
|
||||
|
||||
public int getCount() {
|
||||
return count;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public class PushServiceSocket {
|
||||
private static final String CREATE_ACCOUNT_VOICE_PATH = "/v1/accounts/voice/code/%s";
|
||||
private static final String VERIFY_ACCOUNT_PATH = "/v1/accounts/code/%s";
|
||||
private static final String REGISTER_GCM_PATH = "/v1/accounts/gcm/";
|
||||
private static final String PREKEY_METADATA_PATH = "/v1/keys/";
|
||||
private static final String PREKEY_PATH = "/v1/keys/%s";
|
||||
private static final String PREKEY_DEVICE_PATH = "/v1/keys/%s/%s";
|
||||
|
||||
@@ -123,6 +124,13 @@ public class PushServiceSocket {
|
||||
PreKeyList.toJson(new PreKeyList(lastResortEntity, entities)));
|
||||
}
|
||||
|
||||
public int getAvailablePreKeys() throws IOException {
|
||||
String responseText = makeRequest(PREKEY_METADATA_PATH, "GET", null);
|
||||
PreKeyStatus preKeyStatus = new Gson().fromJson(responseText, PreKeyStatus.class);
|
||||
|
||||
return preKeyStatus.getCount();
|
||||
}
|
||||
|
||||
public List<PreKeyEntity> getPreKeys(PushAddress destination) throws IOException {
|
||||
try {
|
||||
String deviceId = String.valueOf(destination.getDeviceId());
|
||||
|
||||
@@ -70,7 +70,7 @@ public class LocalKeyRecord extends Record {
|
||||
|
||||
this.localCurrentKeyPair = this.localNextKeyPair;
|
||||
this.localNextKeyPair = new KeyPair((this.localNextKeyPair.getId()+1) % Medium.MAX_VALUE,
|
||||
Curve.generateKeyPairForType(keyType),
|
||||
Curve.generateKeyPairForType(keyType, true),
|
||||
masterSecret);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,27 @@ public class Session {
|
||||
return hasV1Session(context, recipient) || hasV2Session(context, masterSecret, recipient);
|
||||
}
|
||||
|
||||
public static boolean hasEncryptCapableSession(Context context,
|
||||
MasterSecret masterSecret,
|
||||
CanonicalRecipient recipient)
|
||||
{
|
||||
RecipientDevice device = new RecipientDevice(recipient.getRecipientId(),
|
||||
RecipientDevice.DEFAULT_DEVICE_ID);
|
||||
|
||||
return hasEncryptCapableSession(context, masterSecret, recipient, device);
|
||||
}
|
||||
|
||||
public static boolean hasEncryptCapableSession(Context context,
|
||||
MasterSecret masterSecret,
|
||||
CanonicalRecipient recipient,
|
||||
RecipientDevice device)
|
||||
{
|
||||
return
|
||||
hasV1Session(context, recipient) ||
|
||||
(hasV2Session(context, masterSecret, recipient) &&
|
||||
!SessionRecordV2.needsRefresh(context, masterSecret, device));
|
||||
}
|
||||
|
||||
public static boolean hasRemoteIdentityKey(Context context,
|
||||
MasterSecret masterSecret,
|
||||
CanonicalRecipient recipient)
|
||||
@@ -49,7 +70,6 @@ public class Session {
|
||||
return SessionRecordV2.hasSession(context, masterSecret, recipient.getRecipientId(),
|
||||
RecipientDevice.DEFAULT_DEVICE_ID);
|
||||
}
|
||||
|
||||
private static boolean hasV1Session(Context context, CanonicalRecipient recipient) {
|
||||
return SessionRecordV1.hasSession(context, recipient) &&
|
||||
RemoteKeyRecord.hasRecord(context, recipient) &&
|
||||
@@ -70,7 +90,8 @@ public class Session {
|
||||
RecipientDevice.DEFAULT_DEVICE_ID))
|
||||
{
|
||||
return new SessionRecordV2(context, masterSecret, recipientId,
|
||||
RecipientDevice.DEFAULT_DEVICE_ID).getRemoteIdentityKey();
|
||||
RecipientDevice.DEFAULT_DEVICE_ID).getSessionState()
|
||||
.getRemoteIdentityKey();
|
||||
} else if (SessionRecordV1.hasSession(context, recipientId)) {
|
||||
return new SessionRecordV1(context, masterSecret, recipientId).getIdentityKey();
|
||||
} else {
|
||||
|
||||
@@ -18,26 +18,10 @@ package org.whispersystems.textsecure.storage;
|
||||
|
||||
import android.content.Context;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.crypto.InvalidMessageException;
|
||||
import org.whispersystems.textsecure.crypto.MasterCipher;
|
||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.ChainKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.MessageKeys;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.RootKey;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Chain;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingKeyExchange;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
@@ -45,11 +29,11 @@ import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.RandomAccessFile;
|
||||
import java.nio.channels.FileChannel;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
import static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure;
|
||||
import static org.whispersystems.textsecure.storage.StorageProtos.SessionStructure;
|
||||
|
||||
/**
|
||||
* A disk record representing a current session.
|
||||
@@ -60,11 +44,15 @@ import javax.crypto.spec.SecretKeySpec;
|
||||
public class SessionRecordV2 extends Record {
|
||||
|
||||
private static final Object FILE_LOCK = new Object();
|
||||
private static final int CURRENT_VERSION = 1;
|
||||
|
||||
private static final int SINGLE_STATE_VERSION = 1;
|
||||
private static final int ARCHIVE_STATES_VERSION = 2;
|
||||
private static final int CURRENT_VERSION = 2;
|
||||
|
||||
private final MasterSecret masterSecret;
|
||||
private StorageProtos.SessionStructure sessionStructure =
|
||||
StorageProtos.SessionStructure.newBuilder().build();
|
||||
|
||||
private SessionState sessionState = new SessionState(SessionStructure.newBuilder().build());
|
||||
private List<SessionState> previousStates = new LinkedList<SessionState>();
|
||||
|
||||
public SessionRecordV2(Context context, MasterSecret masterSecret, RecipientDevice peer) {
|
||||
this(context, masterSecret, peer.getRecipientId(), peer.getDeviceId());
|
||||
@@ -80,6 +68,15 @@ public class SessionRecordV2 extends Record {
|
||||
return recipientId + (deviceId == RecipientDevice.DEFAULT_DEVICE_ID ? "" : "." + deviceId);
|
||||
}
|
||||
|
||||
public SessionState getSessionState() {
|
||||
return sessionState;
|
||||
}
|
||||
|
||||
|
||||
public List<SessionState> getPreviousSessions() {
|
||||
return previousStates;
|
||||
}
|
||||
|
||||
public static List<Integer> getSessionSubDevices(Context context, CanonicalRecipient recipient) {
|
||||
List<Integer> results = new LinkedList<Integer>();
|
||||
File parent = getParentDirectory(context, SESSIONS_DIRECTORY_V2);
|
||||
@@ -129,404 +126,49 @@ public class SessionRecordV2 extends Record {
|
||||
long recipientId, int deviceId)
|
||||
{
|
||||
return hasRecord(context, SESSIONS_DIRECTORY_V2, getRecordName(recipientId, deviceId)) &&
|
||||
new SessionRecordV2(context, masterSecret, recipientId, deviceId).hasSenderChain();
|
||||
new SessionRecordV2(context, masterSecret, recipientId, deviceId).sessionState.hasSenderChain();
|
||||
}
|
||||
|
||||
public static boolean needsRefresh(Context context, MasterSecret masterSecret,
|
||||
RecipientDevice recipient)
|
||||
{
|
||||
return new SessionRecordV2(context, masterSecret,
|
||||
recipient.getRecipientId(),
|
||||
recipient.getDeviceId()).getSessionState()
|
||||
.getNeedsRefresh();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
this.sessionStructure = StorageProtos.SessionStructure.newBuilder().build();
|
||||
this.sessionState = new SessionState(SessionStructure.newBuilder().build());
|
||||
this.previousStates = new LinkedList<SessionState>();
|
||||
}
|
||||
|
||||
public void setSessionVersion(int version) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setSessionVersion(version)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getSessionVersion() {
|
||||
return this.sessionStructure.getSessionVersion();
|
||||
}
|
||||
|
||||
public void setRemoteIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setLocalIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public IdentityKey getRemoteIdentityKey() {
|
||||
try {
|
||||
if (!this.sessionStructure.hasRemoteIdentityPublic()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IdentityKey(this.sessionStructure.getRemoteIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityKey getLocalIdentityKey() {
|
||||
try {
|
||||
return new IdentityKey(this.sessionStructure.getLocalIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPreviousCounter() {
|
||||
return sessionStructure.getPreviousCounter();
|
||||
}
|
||||
|
||||
public void setPreviousCounter(int previousCounter) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPreviousCounter(previousCounter)
|
||||
.build();
|
||||
}
|
||||
|
||||
public RootKey getRootKey() {
|
||||
return new RootKey(this.sessionStructure.getRootKey().toByteArray());
|
||||
}
|
||||
|
||||
public void setRootKey(RootKey rootKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRootKey(ByteString.copyFrom(rootKey.getKeyBytes()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public ECPublicKey getSenderEphemeral() {
|
||||
try {
|
||||
return Curve.decodePoint(sessionStructure.getSenderChain().getSenderEphemeral().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ECKeyPair getSenderEphemeralPair() {
|
||||
ECPublicKey publicKey = getSenderEphemeral();
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getSenderChain()
|
||||
.getSenderEphemeralPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasReceiverChain(ECPublicKey senderEphemeral) {
|
||||
return getReceiverChain(senderEphemeral) != null;
|
||||
}
|
||||
|
||||
public boolean hasSenderChain() {
|
||||
return sessionStructure.hasSenderChain();
|
||||
}
|
||||
|
||||
private Pair<Chain,Integer> getReceiverChain(ECPublicKey senderEphemeral) {
|
||||
List<Chain> receiverChains = sessionStructure.getReceiverChainsList();
|
||||
int index = 0;
|
||||
|
||||
for (Chain receiverChain : receiverChains) {
|
||||
try {
|
||||
ECPublicKey chainSenderEphemeral = Curve.decodePoint(receiverChain.getSenderEphemeral().toByteArray(), 0);
|
||||
|
||||
if (chainSenderEphemeral.equals(senderEphemeral)) {
|
||||
return new Pair<Chain,Integer>(receiverChain,index);
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) {
|
||||
Pair<Chain,Integer> receiverChainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain receiverChain = receiverChainAndIndex.first;
|
||||
|
||||
if (receiverChain == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new ChainKey(receiverChain.getChainKey().getKey().toByteArray(),
|
||||
receiverChain.getChainKey().getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void addReceiverChain(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = Chain.newBuilder()
|
||||
.setChainKey(chainKeyStructure)
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeral.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().addReceiverChains(chain).build();
|
||||
|
||||
if (this.sessionStructure.getReceiverChainsList().size() > 5) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.removeReceiverChains(0)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSenderChain(ECKeyPair senderEphemeralPair, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain senderChain = Chain.newBuilder()
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeralPair.getPublicKey().serialize()))
|
||||
.setSenderEphemeralPrivate(ByteString.copyFrom(senderEphemeralPair.getPrivateKey().serialize()))
|
||||
.setChainKey(chainKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(senderChain).build();
|
||||
}
|
||||
|
||||
public ChainKey getSenderChainKey() {
|
||||
Chain.ChainKey chainKeyStructure = sessionStructure.getSenderChain().getChainKey();
|
||||
return new ChainKey(chainKeyStructure.getKey().toByteArray(), chainKeyStructure.getIndex());
|
||||
}
|
||||
|
||||
|
||||
public void setSenderChainKey(ChainKey nextChainKey) {
|
||||
Chain.ChainKey chainKey = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(nextChainKey.getKey()))
|
||||
.setIndex(nextChainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = sessionStructure.getSenderChain().toBuilder()
|
||||
.setChainKey(chainKey).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(chain).build();
|
||||
}
|
||||
|
||||
public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = chain.getMessageKeysList();
|
||||
|
||||
for (Chain.MessageKey messageKey : messageKeyList) {
|
||||
if (messageKey.getIndex() == counter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = new LinkedList<Chain.MessageKey>(chain.getMessageKeysList());
|
||||
Iterator<Chain.MessageKey> messageKeyIterator = messageKeyList.iterator();
|
||||
MessageKeys result = null;
|
||||
|
||||
while (messageKeyIterator.hasNext()) {
|
||||
Chain.MessageKey messageKey = messageKeyIterator.next();
|
||||
|
||||
if (messageKey.getIndex() == counter) {
|
||||
result = new MessageKeys(new SecretKeySpec(messageKey.getCipherKey().toByteArray(), "AES"),
|
||||
new SecretKeySpec(messageKey.getMacKey().toByteArray(), "HmacSHA256"),
|
||||
messageKey.getIndex());
|
||||
|
||||
messageKeyIterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Chain updatedChain = chain.toBuilder().clearMessageKeys()
|
||||
.addAllMessageKeys(messageKeyList)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
Chain.MessageKey messageKeyStructure = Chain.MessageKey.newBuilder()
|
||||
.setCipherKey(ByteString.copyFrom(messageKeys.getCipherKey().getEncoded()))
|
||||
.setMacKey(ByteString.copyFrom(messageKeys.getMacKey().getEncoded()))
|
||||
.setIndex(messageKeys.getCounter())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder()
|
||||
.addMessageKeys(messageKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder().setChainKey(chainKeyStructure).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setPendingKeyExchange(int sequence,
|
||||
ECKeyPair ourBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey)
|
||||
{
|
||||
PendingKeyExchange structure =
|
||||
PendingKeyExchange.newBuilder()
|
||||
.setSequence(sequence)
|
||||
.setLocalBaseKey(ByteString.copyFrom(ourBaseKey.getPublicKey().serialize()))
|
||||
.setLocalBaseKeyPrivate(ByteString.copyFrom(ourBaseKey.getPrivateKey().serialize()))
|
||||
.setLocalEphemeralKey(ByteString.copyFrom(ourEphemeralKey.getPublicKey().serialize()))
|
||||
.setLocalEphemeralKeyPrivate(ByteString.copyFrom(ourEphemeralKey.getPrivateKey().serialize()))
|
||||
.setLocalIdentityKey(ByteString.copyFrom(ourIdentityKey.getPublicKey().serialize()))
|
||||
.setLocalIdentityKeyPrivate(ByteString.copyFrom(ourIdentityKey.getPrivateKey().serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingKeyExchange(structure)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getPendingKeyExchangeSequence() {
|
||||
return sessionStructure.getPendingKeyExchange().getSequence();
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeEphemeralKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException {
|
||||
IdentityKey publicKey = new IdentityKey(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getPublicKey().getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new IdentityKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasPendingKeyExchange() {
|
||||
return sessionStructure.hasPendingKeyExchange();
|
||||
}
|
||||
|
||||
public void setPendingPreKey(int preKeyId, ECPublicKey baseKey) {
|
||||
PendingPreKey pending = PendingPreKey.newBuilder()
|
||||
.setPreKeyId(preKeyId)
|
||||
.setBaseKey(ByteString.copyFrom(baseKey.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingPreKey(pending)
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean hasPendingPreKey() {
|
||||
return this.sessionStructure.hasPendingPreKey();
|
||||
}
|
||||
|
||||
public Pair<Integer, ECPublicKey> getPendingPreKey() {
|
||||
try {
|
||||
return new Pair<Integer, ECPublicKey>(sessionStructure.getPendingPreKey().getPreKeyId(),
|
||||
Curve.decodePoint(sessionStructure.getPendingPreKey()
|
||||
.getBaseKey()
|
||||
.toByteArray(), 0));
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPendingPreKey() {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.clearPendingPreKey()
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setRemoteRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getRemoteRegistrationId() {
|
||||
return this.sessionStructure.getRemoteRegistrationId();
|
||||
}
|
||||
|
||||
public void setLocalRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getLocalRegistrationId() {
|
||||
return this.sessionStructure.getLocalRegistrationId();
|
||||
public void archiveCurrentState() {
|
||||
this.previousStates.add(sessionState);
|
||||
this.sessionState = new SessionState(SessionStructure.newBuilder().build());
|
||||
}
|
||||
|
||||
public void save() {
|
||||
synchronized (FILE_LOCK) {
|
||||
try {
|
||||
List<SessionStructure> previousStructures = new LinkedList<SessionStructure>();
|
||||
|
||||
for (SessionState previousState : previousStates) {
|
||||
previousStructures.add(previousState.getStructure());
|
||||
}
|
||||
|
||||
RecordStructure record = RecordStructure.newBuilder()
|
||||
.setCurrentSession(sessionState.getStructure())
|
||||
.addAllPreviousSessions(previousStructures)
|
||||
.build();
|
||||
|
||||
RandomAccessFile file = openRandomAccessFile();
|
||||
FileChannel out = file.getChannel();
|
||||
out.position(0);
|
||||
|
||||
MasterCipher cipher = new MasterCipher(masterSecret);
|
||||
writeInteger(CURRENT_VERSION, out);
|
||||
writeBlob(cipher.encryptBytes(sessionStructure.toByteArray()), out);
|
||||
writeBlob(cipher.encryptBytes(record.toByteArray()), out);
|
||||
|
||||
out.truncate(out.position());
|
||||
file.close();
|
||||
@@ -549,11 +191,26 @@ public class SessionRecordV2 extends Record {
|
||||
MasterCipher cipher = new MasterCipher(masterSecret);
|
||||
byte[] encryptedBlob = readBlob(in);
|
||||
|
||||
if (versionMarker == SINGLE_STATE_VERSION) {
|
||||
byte[] plaintextBytes = cipher.decryptBytes(encryptedBlob);
|
||||
SessionStructure sessionStructure = SessionStructure.parseFrom(plaintextBytes);
|
||||
this.sessionState = new SessionState(sessionStructure);
|
||||
} else if (versionMarker == ARCHIVE_STATES_VERSION) {
|
||||
byte[] plaintextBytes = cipher.decryptBytes(encryptedBlob);
|
||||
RecordStructure recordStructure = RecordStructure.parseFrom(plaintextBytes);
|
||||
|
||||
this.sessionStructure = StorageProtos.SessionStructure
|
||||
.parseFrom(cipher.decryptBytes(encryptedBlob));
|
||||
this.sessionState = new SessionState(recordStructure.getCurrentSession());
|
||||
this.previousStates = new LinkedList<SessionState>();
|
||||
|
||||
for (SessionStructure sessionStructure : recordStructure.getPreviousSessionsList()) {
|
||||
this.previousStates.add(new SessionState(sessionStructure));
|
||||
}
|
||||
} else {
|
||||
throw new AssertionError("Unknown version: " + versionMarker);
|
||||
}
|
||||
|
||||
in.close();
|
||||
|
||||
} catch (FileNotFoundException e) {
|
||||
Log.w("SessionRecordV2", "No session information found.");
|
||||
// XXX
|
||||
|
||||
@@ -0,0 +1,436 @@
|
||||
package org.whispersystems.textsecure.storage;
|
||||
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import com.google.protobuf.ByteString;
|
||||
|
||||
import org.whispersystems.textsecure.crypto.IdentityKey;
|
||||
import org.whispersystems.textsecure.crypto.IdentityKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.InvalidKeyException;
|
||||
import org.whispersystems.textsecure.crypto.ecc.Curve;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECKeyPair;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPrivateKey;
|
||||
import org.whispersystems.textsecure.crypto.ecc.ECPublicKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.ChainKey;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.MessageKeys;
|
||||
import org.whispersystems.textsecure.crypto.ratchet.RootKey;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Chain;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingKeyExchange;
|
||||
import org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey;
|
||||
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
import static org.whispersystems.textsecure.storage.StorageProtos.SessionStructure;
|
||||
|
||||
public class SessionState {
|
||||
|
||||
private SessionStructure sessionStructure;
|
||||
|
||||
public SessionState(SessionStructure sessionStructure) {
|
||||
this.sessionStructure = sessionStructure;
|
||||
}
|
||||
|
||||
public SessionStructure getStructure() {
|
||||
return sessionStructure;
|
||||
}
|
||||
|
||||
public void setNeedsRefresh(boolean needsRefresh) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setNeedsRefresh(needsRefresh)
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean getNeedsRefresh() {
|
||||
return this.sessionStructure.getNeedsRefresh();
|
||||
}
|
||||
|
||||
public void setSessionVersion(int version) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setSessionVersion(version)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getSessionVersion() {
|
||||
return this.sessionStructure.getSessionVersion();
|
||||
}
|
||||
|
||||
public void setRemoteIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setLocalIdentityKey(IdentityKey identityKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalIdentityPublic(ByteString.copyFrom(identityKey.serialize()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public IdentityKey getRemoteIdentityKey() {
|
||||
try {
|
||||
if (!this.sessionStructure.hasRemoteIdentityPublic()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return new IdentityKey(this.sessionStructure.getRemoteIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public IdentityKey getLocalIdentityKey() {
|
||||
try {
|
||||
return new IdentityKey(this.sessionStructure.getLocalIdentityPublic().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public int getPreviousCounter() {
|
||||
return sessionStructure.getPreviousCounter();
|
||||
}
|
||||
|
||||
public void setPreviousCounter(int previousCounter) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPreviousCounter(previousCounter)
|
||||
.build();
|
||||
}
|
||||
|
||||
public RootKey getRootKey() {
|
||||
return new RootKey(this.sessionStructure.getRootKey().toByteArray());
|
||||
}
|
||||
|
||||
public void setRootKey(RootKey rootKey) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRootKey(ByteString.copyFrom(rootKey.getKeyBytes()))
|
||||
.build();
|
||||
}
|
||||
|
||||
public ECPublicKey getSenderEphemeral() {
|
||||
try {
|
||||
return Curve.decodePoint(sessionStructure.getSenderChain().getSenderEphemeral().toByteArray(), 0);
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public ECKeyPair getSenderEphemeralPair() {
|
||||
ECPublicKey publicKey = getSenderEphemeral();
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getSenderChain()
|
||||
.getSenderEphemeralPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasReceiverChain(ECPublicKey senderEphemeral) {
|
||||
return getReceiverChain(senderEphemeral) != null;
|
||||
}
|
||||
|
||||
public boolean hasSenderChain() {
|
||||
return sessionStructure.hasSenderChain();
|
||||
}
|
||||
|
||||
private Pair<Chain,Integer> getReceiverChain(ECPublicKey senderEphemeral) {
|
||||
List<Chain> receiverChains = sessionStructure.getReceiverChainsList();
|
||||
int index = 0;
|
||||
|
||||
for (Chain receiverChain : receiverChains) {
|
||||
try {
|
||||
ECPublicKey chainSenderEphemeral = Curve.decodePoint(receiverChain.getSenderEphemeral().toByteArray(), 0);
|
||||
|
||||
if (chainSenderEphemeral.equals(senderEphemeral)) {
|
||||
return new Pair<Chain,Integer>(receiverChain,index);
|
||||
}
|
||||
} catch (InvalidKeyException e) {
|
||||
Log.w("SessionRecordV2", e);
|
||||
}
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public ChainKey getReceiverChainKey(ECPublicKey senderEphemeral) {
|
||||
Pair<Chain,Integer> receiverChainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain receiverChain = receiverChainAndIndex.first;
|
||||
|
||||
if (receiverChain == null) {
|
||||
return null;
|
||||
} else {
|
||||
return new ChainKey(receiverChain.getChainKey().getKey().toByteArray(),
|
||||
receiverChain.getChainKey().getIndex());
|
||||
}
|
||||
}
|
||||
|
||||
public void addReceiverChain(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = Chain.newBuilder()
|
||||
.setChainKey(chainKeyStructure)
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeral.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().addReceiverChains(chain).build();
|
||||
|
||||
if (this.sessionStructure.getReceiverChainsList().size() > 5) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.removeReceiverChains(0)
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
public void setSenderChain(ECKeyPair senderEphemeralPair, ChainKey chainKey) {
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain senderChain = Chain.newBuilder()
|
||||
.setSenderEphemeral(ByteString.copyFrom(senderEphemeralPair.getPublicKey().serialize()))
|
||||
.setSenderEphemeralPrivate(ByteString.copyFrom(senderEphemeralPair.getPrivateKey().serialize()))
|
||||
.setChainKey(chainKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(senderChain).build();
|
||||
}
|
||||
|
||||
public ChainKey getSenderChainKey() {
|
||||
Chain.ChainKey chainKeyStructure = sessionStructure.getSenderChain().getChainKey();
|
||||
return new ChainKey(chainKeyStructure.getKey().toByteArray(), chainKeyStructure.getIndex());
|
||||
}
|
||||
|
||||
|
||||
public void setSenderChainKey(ChainKey nextChainKey) {
|
||||
Chain.ChainKey chainKey = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(nextChainKey.getKey()))
|
||||
.setIndex(nextChainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain chain = sessionStructure.getSenderChain().toBuilder()
|
||||
.setChainKey(chainKey).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder().setSenderChain(chain).build();
|
||||
}
|
||||
|
||||
public boolean hasMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = chain.getMessageKeysList();
|
||||
|
||||
for (Chain.MessageKey messageKey : messageKeyList) {
|
||||
if (messageKey.getIndex() == counter) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public MessageKeys removeMessageKeys(ECPublicKey senderEphemeral, int counter) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
if (chain == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<Chain.MessageKey> messageKeyList = new LinkedList<Chain.MessageKey>(chain.getMessageKeysList());
|
||||
Iterator<Chain.MessageKey> messageKeyIterator = messageKeyList.iterator();
|
||||
MessageKeys result = null;
|
||||
|
||||
while (messageKeyIterator.hasNext()) {
|
||||
Chain.MessageKey messageKey = messageKeyIterator.next();
|
||||
|
||||
if (messageKey.getIndex() == counter) {
|
||||
result = new MessageKeys(new SecretKeySpec(messageKey.getCipherKey().toByteArray(), "AES"),
|
||||
new SecretKeySpec(messageKey.getMacKey().toByteArray(), "HmacSHA256"),
|
||||
messageKey.getIndex());
|
||||
|
||||
messageKeyIterator.remove();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
Chain updatedChain = chain.toBuilder().clearMessageKeys()
|
||||
.addAllMessageKeys(messageKeyList)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public void setMessageKeys(ECPublicKey senderEphemeral, MessageKeys messageKeys) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
Chain.MessageKey messageKeyStructure = Chain.MessageKey.newBuilder()
|
||||
.setCipherKey(ByteString.copyFrom(messageKeys.getCipherKey().getEncoded()))
|
||||
.setMacKey(ByteString.copyFrom(messageKeys.getMacKey().getEncoded()))
|
||||
.setIndex(messageKeys.getCounter())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder()
|
||||
.addMessageKeys(messageKeyStructure)
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setReceiverChainKey(ECPublicKey senderEphemeral, ChainKey chainKey) {
|
||||
Pair<Chain,Integer> chainAndIndex = getReceiverChain(senderEphemeral);
|
||||
Chain chain = chainAndIndex.first;
|
||||
|
||||
Chain.ChainKey chainKeyStructure = Chain.ChainKey.newBuilder()
|
||||
.setKey(ByteString.copyFrom(chainKey.getKey()))
|
||||
.setIndex(chainKey.getIndex())
|
||||
.build();
|
||||
|
||||
Chain updatedChain = chain.toBuilder().setChainKey(chainKeyStructure).build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setReceiverChains(chainAndIndex.second, updatedChain)
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setPendingKeyExchange(int sequence,
|
||||
ECKeyPair ourBaseKey,
|
||||
ECKeyPair ourEphemeralKey,
|
||||
IdentityKeyPair ourIdentityKey)
|
||||
{
|
||||
PendingKeyExchange structure =
|
||||
PendingKeyExchange.newBuilder()
|
||||
.setSequence(sequence)
|
||||
.setLocalBaseKey(ByteString.copyFrom(ourBaseKey.getPublicKey().serialize()))
|
||||
.setLocalBaseKeyPrivate(ByteString.copyFrom(ourBaseKey.getPrivateKey().serialize()))
|
||||
.setLocalEphemeralKey(ByteString.copyFrom(ourEphemeralKey.getPublicKey().serialize()))
|
||||
.setLocalEphemeralKeyPrivate(ByteString.copyFrom(ourEphemeralKey.getPrivateKey().serialize()))
|
||||
.setLocalIdentityKey(ByteString.copyFrom(ourIdentityKey.getPublicKey().serialize()))
|
||||
.setLocalIdentityKeyPrivate(ByteString.copyFrom(ourIdentityKey.getPrivateKey().serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingKeyExchange(structure)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getPendingKeyExchangeSequence() {
|
||||
return sessionStructure.getPendingKeyExchange().getSequence();
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeBaseKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalBaseKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public ECKeyPair getPendingKeyExchangeEphemeralKey() throws InvalidKeyException {
|
||||
ECPublicKey publicKey = Curve.decodePoint(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalEphemeralKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new ECKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public IdentityKeyPair getPendingKeyExchangeIdentityKey() throws InvalidKeyException {
|
||||
IdentityKey publicKey = new IdentityKey(sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKey().toByteArray(), 0);
|
||||
|
||||
ECPrivateKey privateKey = Curve.decodePrivatePoint(publicKey.getPublicKey().getType(),
|
||||
sessionStructure.getPendingKeyExchange()
|
||||
.getLocalIdentityKeyPrivate()
|
||||
.toByteArray());
|
||||
|
||||
return new IdentityKeyPair(publicKey, privateKey);
|
||||
}
|
||||
|
||||
public boolean hasPendingKeyExchange() {
|
||||
return sessionStructure.hasPendingKeyExchange();
|
||||
}
|
||||
|
||||
public void setPendingPreKey(int preKeyId, ECPublicKey baseKey) {
|
||||
PendingPreKey pending = PendingPreKey.newBuilder()
|
||||
.setPreKeyId(preKeyId)
|
||||
.setBaseKey(ByteString.copyFrom(baseKey.serialize()))
|
||||
.build();
|
||||
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setPendingPreKey(pending)
|
||||
.build();
|
||||
}
|
||||
|
||||
public boolean hasPendingPreKey() {
|
||||
return this.sessionStructure.hasPendingPreKey();
|
||||
}
|
||||
|
||||
public Pair<Integer, ECPublicKey> getPendingPreKey() {
|
||||
try {
|
||||
return new Pair<Integer, ECPublicKey>(sessionStructure.getPendingPreKey().getPreKeyId(),
|
||||
Curve.decodePoint(sessionStructure.getPendingPreKey()
|
||||
.getBaseKey()
|
||||
.toByteArray(), 0));
|
||||
} catch (InvalidKeyException e) {
|
||||
throw new AssertionError(e);
|
||||
}
|
||||
}
|
||||
|
||||
public void clearPendingPreKey() {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.clearPendingPreKey()
|
||||
.build();
|
||||
}
|
||||
|
||||
public void setRemoteRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setRemoteRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getRemoteRegistrationId() {
|
||||
return this.sessionStructure.getRemoteRegistrationId();
|
||||
}
|
||||
|
||||
public void setLocalRegistrationId(int registrationId) {
|
||||
this.sessionStructure = this.sessionStructure.toBuilder()
|
||||
.setLocalRegistrationId(registrationId)
|
||||
.build();
|
||||
}
|
||||
|
||||
public int getLocalRegistrationId() {
|
||||
return this.sessionStructure.getLocalRegistrationId();
|
||||
}
|
||||
|
||||
public byte[] serialize() {
|
||||
return sessionStructure.toByteArray();
|
||||
}
|
||||
}
|
||||
@@ -63,6 +63,10 @@ public final class StorageProtos {
|
||||
// optional uint32 localRegistrationId = 11;
|
||||
boolean hasLocalRegistrationId();
|
||||
int getLocalRegistrationId();
|
||||
|
||||
// optional bool needsRefresh = 12;
|
||||
boolean hasNeedsRefresh();
|
||||
boolean getNeedsRefresh();
|
||||
}
|
||||
public static final class SessionStructure extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
@@ -2992,6 +2996,16 @@ public final class StorageProtos {
|
||||
return localRegistrationId_;
|
||||
}
|
||||
|
||||
// optional bool needsRefresh = 12;
|
||||
public static final int NEEDSREFRESH_FIELD_NUMBER = 12;
|
||||
private boolean needsRefresh_;
|
||||
public boolean hasNeedsRefresh() {
|
||||
return ((bitField0_ & 0x00000400) == 0x00000400);
|
||||
}
|
||||
public boolean getNeedsRefresh() {
|
||||
return needsRefresh_;
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
sessionVersion_ = 0;
|
||||
localIdentityPublic_ = com.google.protobuf.ByteString.EMPTY;
|
||||
@@ -3004,6 +3018,7 @@ public final class StorageProtos {
|
||||
pendingPreKey_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey.getDefaultInstance();
|
||||
remoteRegistrationId_ = 0;
|
||||
localRegistrationId_ = 0;
|
||||
needsRefresh_ = false;
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
@@ -3050,6 +3065,9 @@ public final class StorageProtos {
|
||||
if (((bitField0_ & 0x00000200) == 0x00000200)) {
|
||||
output.writeUInt32(11, localRegistrationId_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000400) == 0x00000400)) {
|
||||
output.writeBool(12, needsRefresh_);
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
@@ -3103,6 +3121,10 @@ public final class StorageProtos {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeUInt32Size(11, localRegistrationId_);
|
||||
}
|
||||
if (((bitField0_ & 0x00000400) == 0x00000400)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeBoolSize(12, needsRefresh_);
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
@@ -3269,6 +3291,8 @@ public final class StorageProtos {
|
||||
bitField0_ = (bitField0_ & ~0x00000200);
|
||||
localRegistrationId_ = 0;
|
||||
bitField0_ = (bitField0_ & ~0x00000400);
|
||||
needsRefresh_ = false;
|
||||
bitField0_ = (bitField0_ & ~0x00000800);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -3368,6 +3392,10 @@ public final class StorageProtos {
|
||||
to_bitField0_ |= 0x00000200;
|
||||
}
|
||||
result.localRegistrationId_ = localRegistrationId_;
|
||||
if (((from_bitField0_ & 0x00000800) == 0x00000800)) {
|
||||
to_bitField0_ |= 0x00000400;
|
||||
}
|
||||
result.needsRefresh_ = needsRefresh_;
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
@@ -3440,6 +3468,9 @@ public final class StorageProtos {
|
||||
if (other.hasLocalRegistrationId()) {
|
||||
setLocalRegistrationId(other.getLocalRegistrationId());
|
||||
}
|
||||
if (other.hasNeedsRefresh()) {
|
||||
setNeedsRefresh(other.getNeedsRefresh());
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
@@ -3539,6 +3570,11 @@ public final class StorageProtos {
|
||||
localRegistrationId_ = input.readUInt32();
|
||||
break;
|
||||
}
|
||||
case 96: {
|
||||
bitField0_ |= 0x00000800;
|
||||
needsRefresh_ = input.readBool();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4157,6 +4193,27 @@ public final class StorageProtos {
|
||||
return this;
|
||||
}
|
||||
|
||||
// optional bool needsRefresh = 12;
|
||||
private boolean needsRefresh_ ;
|
||||
public boolean hasNeedsRefresh() {
|
||||
return ((bitField0_ & 0x00000800) == 0x00000800);
|
||||
}
|
||||
public boolean getNeedsRefresh() {
|
||||
return needsRefresh_;
|
||||
}
|
||||
public Builder setNeedsRefresh(boolean value) {
|
||||
bitField0_ |= 0x00000800;
|
||||
needsRefresh_ = value;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
public Builder clearNeedsRefresh() {
|
||||
bitField0_ = (bitField0_ & ~0x00000800);
|
||||
needsRefresh_ = false;
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:textsecure.SessionStructure)
|
||||
}
|
||||
|
||||
@@ -4168,6 +4225,703 @@ public final class StorageProtos {
|
||||
// @@protoc_insertion_point(class_scope:textsecure.SessionStructure)
|
||||
}
|
||||
|
||||
public interface RecordStructureOrBuilder
|
||||
extends com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
// optional .textsecure.SessionStructure currentSession = 1;
|
||||
boolean hasCurrentSession();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getCurrentSession();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder();
|
||||
|
||||
// repeated .textsecure.SessionStructure previousSessions = 2;
|
||||
java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure>
|
||||
getPreviousSessionsList();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getPreviousSessions(int index);
|
||||
int getPreviousSessionsCount();
|
||||
java.util.List<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsOrBuilderList();
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder(
|
||||
int index);
|
||||
}
|
||||
public static final class RecordStructure extends
|
||||
com.google.protobuf.GeneratedMessage
|
||||
implements RecordStructureOrBuilder {
|
||||
// Use RecordStructure.newBuilder() to construct.
|
||||
private RecordStructure(Builder builder) {
|
||||
super(builder);
|
||||
}
|
||||
private RecordStructure(boolean noInit) {}
|
||||
|
||||
private static final RecordStructure defaultInstance;
|
||||
public static RecordStructure getDefaultInstance() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public RecordStructure getDefaultInstanceForType() {
|
||||
return defaultInstance;
|
||||
}
|
||||
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable;
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
// optional .textsecure.SessionStructure currentSession = 1;
|
||||
public static final int CURRENTSESSION_FIELD_NUMBER = 1;
|
||||
private org.whispersystems.textsecure.storage.StorageProtos.SessionStructure currentSession_;
|
||||
public boolean hasCurrentSession() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getCurrentSession() {
|
||||
return currentSession_;
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() {
|
||||
return currentSession_;
|
||||
}
|
||||
|
||||
// repeated .textsecure.SessionStructure previousSessions = 2;
|
||||
public static final int PREVIOUSSESSIONS_FIELD_NUMBER = 2;
|
||||
private java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> previousSessions_;
|
||||
public java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> getPreviousSessionsList() {
|
||||
return previousSessions_;
|
||||
}
|
||||
public java.util.List<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsOrBuilderList() {
|
||||
return previousSessions_;
|
||||
}
|
||||
public int getPreviousSessionsCount() {
|
||||
return previousSessions_.size();
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getPreviousSessions(int index) {
|
||||
return previousSessions_.get(index);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder(
|
||||
int index) {
|
||||
return previousSessions_.get(index);
|
||||
}
|
||||
|
||||
private void initFields() {
|
||||
currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
previousSessions_ = java.util.Collections.emptyList();
|
||||
}
|
||||
private byte memoizedIsInitialized = -1;
|
||||
public final boolean isInitialized() {
|
||||
byte isInitialized = memoizedIsInitialized;
|
||||
if (isInitialized != -1) return isInitialized == 1;
|
||||
|
||||
memoizedIsInitialized = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
public void writeTo(com.google.protobuf.CodedOutputStream output)
|
||||
throws java.io.IOException {
|
||||
getSerializedSize();
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
output.writeMessage(1, currentSession_);
|
||||
}
|
||||
for (int i = 0; i < previousSessions_.size(); i++) {
|
||||
output.writeMessage(2, previousSessions_.get(i));
|
||||
}
|
||||
getUnknownFields().writeTo(output);
|
||||
}
|
||||
|
||||
private int memoizedSerializedSize = -1;
|
||||
public int getSerializedSize() {
|
||||
int size = memoizedSerializedSize;
|
||||
if (size != -1) return size;
|
||||
|
||||
size = 0;
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(1, currentSession_);
|
||||
}
|
||||
for (int i = 0; i < previousSessions_.size(); i++) {
|
||||
size += com.google.protobuf.CodedOutputStream
|
||||
.computeMessageSize(2, previousSessions_.get(i));
|
||||
}
|
||||
size += getUnknownFields().getSerializedSize();
|
||||
memoizedSerializedSize = size;
|
||||
return size;
|
||||
}
|
||||
|
||||
private static final long serialVersionUID = 0L;
|
||||
@java.lang.Override
|
||||
protected java.lang.Object writeReplace()
|
||||
throws java.io.ObjectStreamException {
|
||||
return super.writeReplace();
|
||||
}
|
||||
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
com.google.protobuf.ByteString data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
com.google.protobuf.ByteString data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(byte[] data)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
byte[] data,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
return newBuilder().mergeFrom(data, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseDelimitedFrom(java.io.InputStream input)
|
||||
throws java.io.IOException {
|
||||
Builder builder = newBuilder();
|
||||
if (builder.mergeDelimitedFrom(input)) {
|
||||
return builder.buildParsed();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseDelimitedFrom(
|
||||
java.io.InputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
Builder builder = newBuilder();
|
||||
if (builder.mergeDelimitedFrom(input, extensionRegistry)) {
|
||||
return builder.buildParsed();
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
com.google.protobuf.CodedInputStream input)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input).buildParsed();
|
||||
}
|
||||
public static org.whispersystems.textsecure.storage.StorageProtos.RecordStructure parseFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
return newBuilder().mergeFrom(input, extensionRegistry)
|
||||
.buildParsed();
|
||||
}
|
||||
|
||||
public static Builder newBuilder() { return Builder.create(); }
|
||||
public Builder newBuilderForType() { return newBuilder(); }
|
||||
public static Builder newBuilder(org.whispersystems.textsecure.storage.StorageProtos.RecordStructure prototype) {
|
||||
return newBuilder().mergeFrom(prototype);
|
||||
}
|
||||
public Builder toBuilder() { return newBuilder(this); }
|
||||
|
||||
@java.lang.Override
|
||||
protected Builder newBuilderForType(
|
||||
com.google.protobuf.GeneratedMessage.BuilderParent parent) {
|
||||
Builder builder = new Builder(parent);
|
||||
return builder;
|
||||
}
|
||||
public static final class Builder extends
|
||||
com.google.protobuf.GeneratedMessage.Builder<Builder>
|
||||
implements org.whispersystems.textsecure.storage.StorageProtos.RecordStructureOrBuilder {
|
||||
public static final com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptor() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_descriptor;
|
||||
}
|
||||
|
||||
protected com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internalGetFieldAccessorTable() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.internal_static_textsecure_RecordStructure_fieldAccessorTable;
|
||||
}
|
||||
|
||||
// Construct using org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.newBuilder()
|
||||
private Builder() {
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
|
||||
private Builder(BuilderParent parent) {
|
||||
super(parent);
|
||||
maybeForceBuilderInitialization();
|
||||
}
|
||||
private void maybeForceBuilderInitialization() {
|
||||
if (com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders) {
|
||||
getCurrentSessionFieldBuilder();
|
||||
getPreviousSessionsFieldBuilder();
|
||||
}
|
||||
}
|
||||
private static Builder create() {
|
||||
return new Builder();
|
||||
}
|
||||
|
||||
public Builder clear() {
|
||||
super.clear();
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
} else {
|
||||
currentSessionBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
previousSessions_ = java.util.Collections.emptyList();
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
} else {
|
||||
previousSessionsBuilder_.clear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
public Builder clone() {
|
||||
return create().mergeFrom(buildPartial());
|
||||
}
|
||||
|
||||
public com.google.protobuf.Descriptors.Descriptor
|
||||
getDescriptorForType() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.getDescriptor();
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.RecordStructure getDefaultInstanceForType() {
|
||||
return org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.getDefaultInstance();
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.RecordStructure build() {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(result);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private org.whispersystems.textsecure.storage.StorageProtos.RecordStructure buildParsed()
|
||||
throws com.google.protobuf.InvalidProtocolBufferException {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure result = buildPartial();
|
||||
if (!result.isInitialized()) {
|
||||
throw newUninitializedMessageException(
|
||||
result).asInvalidProtocolBufferException();
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.RecordStructure buildPartial() {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure result = new org.whispersystems.textsecure.storage.StorageProtos.RecordStructure(this);
|
||||
int from_bitField0_ = bitField0_;
|
||||
int to_bitField0_ = 0;
|
||||
if (((from_bitField0_ & 0x00000001) == 0x00000001)) {
|
||||
to_bitField0_ |= 0x00000001;
|
||||
}
|
||||
if (currentSessionBuilder_ == null) {
|
||||
result.currentSession_ = currentSession_;
|
||||
} else {
|
||||
result.currentSession_ = currentSessionBuilder_.build();
|
||||
}
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
previousSessions_ = java.util.Collections.unmodifiableList(previousSessions_);
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
}
|
||||
result.previousSessions_ = previousSessions_;
|
||||
} else {
|
||||
result.previousSessions_ = previousSessionsBuilder_.build();
|
||||
}
|
||||
result.bitField0_ = to_bitField0_;
|
||||
onBuilt();
|
||||
return result;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(com.google.protobuf.Message other) {
|
||||
if (other instanceof org.whispersystems.textsecure.storage.StorageProtos.RecordStructure) {
|
||||
return mergeFrom((org.whispersystems.textsecure.storage.StorageProtos.RecordStructure)other);
|
||||
} else {
|
||||
super.mergeFrom(other);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
public Builder mergeFrom(org.whispersystems.textsecure.storage.StorageProtos.RecordStructure other) {
|
||||
if (other == org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.getDefaultInstance()) return this;
|
||||
if (other.hasCurrentSession()) {
|
||||
mergeCurrentSession(other.getCurrentSession());
|
||||
}
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (!other.previousSessions_.isEmpty()) {
|
||||
if (previousSessions_.isEmpty()) {
|
||||
previousSessions_ = other.previousSessions_;
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
} else {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.addAll(other.previousSessions_);
|
||||
}
|
||||
onChanged();
|
||||
}
|
||||
} else {
|
||||
if (!other.previousSessions_.isEmpty()) {
|
||||
if (previousSessionsBuilder_.isEmpty()) {
|
||||
previousSessionsBuilder_.dispose();
|
||||
previousSessionsBuilder_ = null;
|
||||
previousSessions_ = other.previousSessions_;
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
previousSessionsBuilder_ =
|
||||
com.google.protobuf.GeneratedMessage.alwaysUseFieldBuilders ?
|
||||
getPreviousSessionsFieldBuilder() : null;
|
||||
} else {
|
||||
previousSessionsBuilder_.addAllMessages(other.previousSessions_);
|
||||
}
|
||||
}
|
||||
}
|
||||
this.mergeUnknownFields(other.getUnknownFields());
|
||||
return this;
|
||||
}
|
||||
|
||||
public final boolean isInitialized() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Builder mergeFrom(
|
||||
com.google.protobuf.CodedInputStream input,
|
||||
com.google.protobuf.ExtensionRegistryLite extensionRegistry)
|
||||
throws java.io.IOException {
|
||||
com.google.protobuf.UnknownFieldSet.Builder unknownFields =
|
||||
com.google.protobuf.UnknownFieldSet.newBuilder(
|
||||
this.getUnknownFields());
|
||||
while (true) {
|
||||
int tag = input.readTag();
|
||||
switch (tag) {
|
||||
case 0:
|
||||
this.setUnknownFields(unknownFields.build());
|
||||
onChanged();
|
||||
return this;
|
||||
default: {
|
||||
if (!parseUnknownField(input, unknownFields,
|
||||
extensionRegistry, tag)) {
|
||||
this.setUnknownFields(unknownFields.build());
|
||||
onChanged();
|
||||
return this;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 10: {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder subBuilder = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.newBuilder();
|
||||
if (hasCurrentSession()) {
|
||||
subBuilder.mergeFrom(getCurrentSession());
|
||||
}
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
setCurrentSession(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
case 18: {
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder subBuilder = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.newBuilder();
|
||||
input.readMessage(subBuilder, extensionRegistry);
|
||||
addPreviousSessions(subBuilder.buildPartial());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int bitField0_;
|
||||
|
||||
// optional .textsecure.SessionStructure currentSession = 1;
|
||||
private org.whispersystems.textsecure.storage.StorageProtos.SessionStructure currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder> currentSessionBuilder_;
|
||||
public boolean hasCurrentSession() {
|
||||
return ((bitField0_ & 0x00000001) == 0x00000001);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getCurrentSession() {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
return currentSession_;
|
||||
} else {
|
||||
return currentSessionBuilder_.getMessage();
|
||||
}
|
||||
}
|
||||
public Builder setCurrentSession(org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
currentSession_ = value;
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.setMessage(value);
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
return this;
|
||||
}
|
||||
public Builder setCurrentSession(
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSession_ = builderForValue.build();
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.setMessage(builderForValue.build());
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
return this;
|
||||
}
|
||||
public Builder mergeCurrentSession(org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
if (((bitField0_ & 0x00000001) == 0x00000001) &&
|
||||
currentSession_ != org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance()) {
|
||||
currentSession_ =
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.newBuilder(currentSession_).mergeFrom(value).buildPartial();
|
||||
} else {
|
||||
currentSession_ = value;
|
||||
}
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.mergeFrom(value);
|
||||
}
|
||||
bitField0_ |= 0x00000001;
|
||||
return this;
|
||||
}
|
||||
public Builder clearCurrentSession() {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSession_ = org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance();
|
||||
onChanged();
|
||||
} else {
|
||||
currentSessionBuilder_.clear();
|
||||
}
|
||||
bitField0_ = (bitField0_ & ~0x00000001);
|
||||
return this;
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder getCurrentSessionBuilder() {
|
||||
bitField0_ |= 0x00000001;
|
||||
onChanged();
|
||||
return getCurrentSessionFieldBuilder().getBuilder();
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getCurrentSessionOrBuilder() {
|
||||
if (currentSessionBuilder_ != null) {
|
||||
return currentSessionBuilder_.getMessageOrBuilder();
|
||||
} else {
|
||||
return currentSession_;
|
||||
}
|
||||
}
|
||||
private com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getCurrentSessionFieldBuilder() {
|
||||
if (currentSessionBuilder_ == null) {
|
||||
currentSessionBuilder_ = new com.google.protobuf.SingleFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>(
|
||||
currentSession_,
|
||||
getParentForChildren(),
|
||||
isClean());
|
||||
currentSession_ = null;
|
||||
}
|
||||
return currentSessionBuilder_;
|
||||
}
|
||||
|
||||
// repeated .textsecure.SessionStructure previousSessions = 2;
|
||||
private java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> previousSessions_ =
|
||||
java.util.Collections.emptyList();
|
||||
private void ensurePreviousSessionsIsMutable() {
|
||||
if (!((bitField0_ & 0x00000002) == 0x00000002)) {
|
||||
previousSessions_ = new java.util.ArrayList<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure>(previousSessions_);
|
||||
bitField0_ |= 0x00000002;
|
||||
}
|
||||
}
|
||||
|
||||
private com.google.protobuf.RepeatedFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder> previousSessionsBuilder_;
|
||||
|
||||
public java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> getPreviousSessionsList() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return java.util.Collections.unmodifiableList(previousSessions_);
|
||||
} else {
|
||||
return previousSessionsBuilder_.getMessageList();
|
||||
}
|
||||
}
|
||||
public int getPreviousSessionsCount() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return previousSessions_.size();
|
||||
} else {
|
||||
return previousSessionsBuilder_.getCount();
|
||||
}
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure getPreviousSessions(int index) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return previousSessions_.get(index);
|
||||
} else {
|
||||
return previousSessionsBuilder_.getMessage(index);
|
||||
}
|
||||
}
|
||||
public Builder setPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.set(index, value);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.setMessage(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder setPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.set(index, builderForValue.build());
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.setMessage(index, builderForValue.build());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(value);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure value) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
if (value == null) {
|
||||
throw new NullPointerException();
|
||||
}
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(index, value);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(index, value);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(builderForValue.build());
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(builderForValue.build());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addPreviousSessions(
|
||||
int index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder builderForValue) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.add(index, builderForValue.build());
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addMessage(index, builderForValue.build());
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder addAllPreviousSessions(
|
||||
java.lang.Iterable<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructure> values) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
super.addAll(values, previousSessions_);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.addAllMessages(values);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder clearPreviousSessions() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
previousSessions_ = java.util.Collections.emptyList();
|
||||
bitField0_ = (bitField0_ & ~0x00000002);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.clear();
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public Builder removePreviousSessions(int index) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
ensurePreviousSessionsIsMutable();
|
||||
previousSessions_.remove(index);
|
||||
onChanged();
|
||||
} else {
|
||||
previousSessionsBuilder_.remove(index);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder getPreviousSessionsBuilder(
|
||||
int index) {
|
||||
return getPreviousSessionsFieldBuilder().getBuilder(index);
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder getPreviousSessionsOrBuilder(
|
||||
int index) {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
return previousSessions_.get(index); } else {
|
||||
return previousSessionsBuilder_.getMessageOrBuilder(index);
|
||||
}
|
||||
}
|
||||
public java.util.List<? extends org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsOrBuilderList() {
|
||||
if (previousSessionsBuilder_ != null) {
|
||||
return previousSessionsBuilder_.getMessageOrBuilderList();
|
||||
} else {
|
||||
return java.util.Collections.unmodifiableList(previousSessions_);
|
||||
}
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder() {
|
||||
return getPreviousSessionsFieldBuilder().addBuilder(
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance());
|
||||
}
|
||||
public org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder addPreviousSessionsBuilder(
|
||||
int index) {
|
||||
return getPreviousSessionsFieldBuilder().addBuilder(
|
||||
index, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.getDefaultInstance());
|
||||
}
|
||||
public java.util.List<org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder>
|
||||
getPreviousSessionsBuilderList() {
|
||||
return getPreviousSessionsFieldBuilder().getBuilderList();
|
||||
}
|
||||
private com.google.protobuf.RepeatedFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>
|
||||
getPreviousSessionsFieldBuilder() {
|
||||
if (previousSessionsBuilder_ == null) {
|
||||
previousSessionsBuilder_ = new com.google.protobuf.RepeatedFieldBuilder<
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure, org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder, org.whispersystems.textsecure.storage.StorageProtos.SessionStructureOrBuilder>(
|
||||
previousSessions_,
|
||||
((bitField0_ & 0x00000002) == 0x00000002),
|
||||
getParentForChildren(),
|
||||
isClean());
|
||||
previousSessions_ = null;
|
||||
}
|
||||
return previousSessionsBuilder_;
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(builder_scope:textsecure.RecordStructure)
|
||||
}
|
||||
|
||||
static {
|
||||
defaultInstance = new RecordStructure(true);
|
||||
defaultInstance.initFields();
|
||||
}
|
||||
|
||||
// @@protoc_insertion_point(class_scope:textsecure.RecordStructure)
|
||||
}
|
||||
|
||||
public interface PreKeyRecordStructureOrBuilder
|
||||
extends com.google.protobuf.MessageOrBuilder {
|
||||
|
||||
@@ -4656,6 +5410,11 @@ public final class StorageProtos {
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_textsecure_SessionStructure_PendingPreKey_fieldAccessorTable;
|
||||
private static com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_textsecure_RecordStructure_descriptor;
|
||||
private static
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable
|
||||
internal_static_textsecure_RecordStructure_fieldAccessorTable;
|
||||
private static com.google.protobuf.Descriptors.Descriptor
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor;
|
||||
private static
|
||||
@@ -4671,7 +5430,7 @@ public final class StorageProtos {
|
||||
static {
|
||||
java.lang.String[] descriptorData = {
|
||||
"\n\032LocalStorageProtocol.proto\022\ntextsecure" +
|
||||
"\"\205\010\n\020SessionStructure\022\026\n\016sessionVersion\030" +
|
||||
"\"\233\010\n\020SessionStructure\022\026\n\016sessionVersion\030" +
|
||||
"\001 \001(\r\022\033\n\023localIdentityPublic\030\002 \001(\014\022\034\n\024re" +
|
||||
"moteIdentityPublic\030\003 \001(\014\022\017\n\007rootKey\030\004 \001(" +
|
||||
"\014\022\027\n\017previousCounter\030\005 \001(\r\0227\n\013senderChai" +
|
||||
@@ -4682,25 +5441,28 @@ public final class StorageProtos {
|
||||
"e.PendingKeyExchange\022A\n\rpendingPreKey\030\t ",
|
||||
"\001(\0132*.textsecure.SessionStructure.Pendin" +
|
||||
"gPreKey\022\034\n\024remoteRegistrationId\030\n \001(\r\022\033\n" +
|
||||
"\023localRegistrationId\030\013 \001(\r\032\253\002\n\005Chain\022\027\n\017" +
|
||||
"senderEphemeral\030\001 \001(\014\022\036\n\026senderEphemeral" +
|
||||
"Private\030\002 \001(\014\022=\n\010chainKey\030\003 \001(\0132+.textse" +
|
||||
"cure.SessionStructure.Chain.ChainKey\022B\n\013" +
|
||||
"messageKeys\030\004 \003(\0132-.textsecure.SessionSt" +
|
||||
"ructure.Chain.MessageKey\032&\n\010ChainKey\022\r\n\005" +
|
||||
"index\030\001 \001(\r\022\013\n\003key\030\002 \001(\014\032>\n\nMessageKey\022\r" +
|
||||
"\n\005index\030\001 \001(\r\022\021\n\tcipherKey\030\002 \001(\014\022\016\n\006macK",
|
||||
"ey\030\003 \001(\014\032\321\001\n\022PendingKeyExchange\022\020\n\010seque" +
|
||||
"nce\030\001 \001(\r\022\024\n\014localBaseKey\030\002 \001(\014\022\033\n\023local" +
|
||||
"BaseKeyPrivate\030\003 \001(\014\022\031\n\021localEphemeralKe" +
|
||||
"y\030\004 \001(\014\022 \n\030localEphemeralKeyPrivate\030\005 \001(" +
|
||||
"\014\022\030\n\020localIdentityKey\030\007 \001(\014\022\037\n\027localIden" +
|
||||
"tityKeyPrivate\030\010 \001(\014\0322\n\rPendingPreKey\022\020\n" +
|
||||
"\010preKeyId\030\001 \001(\r\022\017\n\007baseKey\030\002 \001(\014\"J\n\025PreK" +
|
||||
"eyRecordStructure\022\n\n\002id\030\001 \001(\r\022\021\n\tpublicK" +
|
||||
"ey\030\002 \001(\014\022\022\n\nprivateKey\030\003 \001(\014B6\n%org.whis" +
|
||||
"persystems.textsecure.storageB\rStoragePr",
|
||||
"otos"
|
||||
"\023localRegistrationId\030\013 \001(\r\022\024\n\014needsRefre" +
|
||||
"sh\030\014 \001(\010\032\253\002\n\005Chain\022\027\n\017senderEphemeral\030\001 " +
|
||||
"\001(\014\022\036\n\026senderEphemeralPrivate\030\002 \001(\014\022=\n\010c" +
|
||||
"hainKey\030\003 \001(\0132+.textsecure.SessionStruct" +
|
||||
"ure.Chain.ChainKey\022B\n\013messageKeys\030\004 \003(\0132" +
|
||||
"-.textsecure.SessionStructure.Chain.Mess" +
|
||||
"ageKey\032&\n\010ChainKey\022\r\n\005index\030\001 \001(\r\022\013\n\003key" +
|
||||
"\030\002 \001(\014\032>\n\nMessageKey\022\r\n\005index\030\001 \001(\r\022\021\n\tc",
|
||||
"ipherKey\030\002 \001(\014\022\016\n\006macKey\030\003 \001(\014\032\321\001\n\022Pendi" +
|
||||
"ngKeyExchange\022\020\n\010sequence\030\001 \001(\r\022\024\n\014local" +
|
||||
"BaseKey\030\002 \001(\014\022\033\n\023localBaseKeyPrivate\030\003 \001" +
|
||||
"(\014\022\031\n\021localEphemeralKey\030\004 \001(\014\022 \n\030localEp" +
|
||||
"hemeralKeyPrivate\030\005 \001(\014\022\030\n\020localIdentity" +
|
||||
"Key\030\007 \001(\014\022\037\n\027localIdentityKeyPrivate\030\010 \001" +
|
||||
"(\014\0322\n\rPendingPreKey\022\020\n\010preKeyId\030\001 \001(\r\022\017\n" +
|
||||
"\007baseKey\030\002 \001(\014\"\177\n\017RecordStructure\0224\n\016cur" +
|
||||
"rentSession\030\001 \001(\0132\034.textsecure.SessionSt" +
|
||||
"ructure\0226\n\020previousSessions\030\002 \003(\0132\034.text",
|
||||
"secure.SessionStructure\"J\n\025PreKeyRecordS" +
|
||||
"tructure\022\n\n\002id\030\001 \001(\r\022\021\n\tpublicKey\030\002 \001(\014\022" +
|
||||
"\022\n\nprivateKey\030\003 \001(\014B6\n%org.whispersystem" +
|
||||
"s.textsecure.storageB\rStorageProtos"
|
||||
};
|
||||
com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner assigner =
|
||||
new com.google.protobuf.Descriptors.FileDescriptor.InternalDescriptorAssigner() {
|
||||
@@ -4712,7 +5474,7 @@ public final class StorageProtos {
|
||||
internal_static_textsecure_SessionStructure_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_SessionStructure_descriptor,
|
||||
new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", },
|
||||
new java.lang.String[] { "SessionVersion", "LocalIdentityPublic", "RemoteIdentityPublic", "RootKey", "PreviousCounter", "SenderChain", "ReceiverChains", "PendingKeyExchange", "PendingPreKey", "RemoteRegistrationId", "LocalRegistrationId", "NeedsRefresh", },
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.class,
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.Builder.class);
|
||||
internal_static_textsecure_SessionStructure_Chain_descriptor =
|
||||
@@ -4755,8 +5517,16 @@ public final class StorageProtos {
|
||||
new java.lang.String[] { "PreKeyId", "BaseKey", },
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey.class,
|
||||
org.whispersystems.textsecure.storage.StorageProtos.SessionStructure.PendingPreKey.Builder.class);
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor =
|
||||
internal_static_textsecure_RecordStructure_descriptor =
|
||||
getDescriptor().getMessageTypes().get(1);
|
||||
internal_static_textsecure_RecordStructure_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_RecordStructure_descriptor,
|
||||
new java.lang.String[] { "CurrentSession", "PreviousSessions", },
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.class,
|
||||
org.whispersystems.textsecure.storage.StorageProtos.RecordStructure.Builder.class);
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor =
|
||||
getDescriptor().getMessageTypes().get(2);
|
||||
internal_static_textsecure_PreKeyRecordStructure_fieldAccessorTable = new
|
||||
com.google.protobuf.GeneratedMessage.FieldAccessorTable(
|
||||
internal_static_textsecure_PreKeyRecordStructure_descriptor,
|
||||
|
||||
17
res/anim/fade_scale_in.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="0.85"
|
||||
android:fromYScale="0.85"
|
||||
android:toXScale="1.0"
|
||||
android:toYScale="1.0"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="0.6"
|
||||
android:toAlpha="1.0" />
|
||||
</set>
|
||||
17
res/anim/fade_scale_out.xml
Normal file
@@ -0,0 +1,17 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<scale
|
||||
android:duration="150"
|
||||
android:fromXScale="1.0"
|
||||
android:fromYScale="1.0"
|
||||
android:toXScale="0.85"
|
||||
android:toYScale="0.85"
|
||||
android:pivotX="50%"
|
||||
android:pivotY="50%" />
|
||||
<alpha
|
||||
android:duration="150"
|
||||
android:fromAlpha="1.0"
|
||||
android:toAlpha="0.6" />
|
||||
</set>
|
||||
9
res/anim/slide_from_right.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="100%"
|
||||
android:toXDelta="0%" />
|
||||
</set>
|
||||
9
res/anim/slide_to_right.xml
Normal file
@@ -0,0 +1,9 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<set xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:interpolator="@android:anim/decelerate_interpolator">
|
||||
<translate
|
||||
android:duration="150"
|
||||
android:fromXDelta="0%"
|
||||
android:toXDelta="100%" />
|
||||
</set>
|
||||
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 884 B |
|
Before Width: | Height: | Size: 1.1 KiB |
|
Before Width: | Height: | Size: 875 B |
BIN
res/drawable-hdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-hdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
res/drawable-hdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
res/drawable-hdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 557 B |
|
Before Width: | Height: | Size: 677 B |
|
Before Width: | Height: | Size: 550 B |
BIN
res/drawable-mdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 1.0 KiB |
BIN
res/drawable-mdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 974 B |
BIN
res/drawable-mdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 858 B |
BIN
res/drawable-mdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 873 B |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.0 KiB |
BIN
res/drawable-xhdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 2.2 KiB |
BIN
res/drawable-xhdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 1.8 KiB |
BIN
res/drawable-xhdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-xhdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 1.6 KiB |
BIN
res/drawable-xxhdpi/ic_send_push.png
Normal file
|
After Width: | Height: | Size: 3.3 KiB |
BIN
res/drawable-xxhdpi/ic_send_sms_insecure.png
Normal file
|
After Width: | Height: | Size: 2.5 KiB |
BIN
res/drawable-xxhdpi/ic_send_sms_insecure_dark.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
BIN
res/drawable-xxhdpi/ic_send_sms_secure.png
Normal file
|
After Width: | Height: | Size: 2.3 KiB |
@@ -3,14 +3,14 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_received_shadow_light" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="@dimen/conversation_item_drop_shadow_dist">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#ffeeeeee" />
|
||||
<solid android:color="@color/conversation_item_received_background_light" />
|
||||
<!--stroke android:width="0.5dp" android:color="#03000000" /-->
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
|
||||
@@ -3,14 +3,14 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#ff222222" />
|
||||
<solid android:color="@color/conversation_item_received_shadow_dark" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="@dimen/conversation_item_drop_shadow_dist">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="#ff333333" />
|
||||
<solid android:color="@color/conversation_item_received_background_dark" />
|
||||
<!--stroke android:width="0.5dp" android:color="#03000000" /-->
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<solid
|
||||
android:color="#ffeeeeee" />
|
||||
android:color="@color/conversation_item_received_background_light" />
|
||||
</shape>
|
||||
</rotate>
|
||||
</item>
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
<shape
|
||||
android:shape="rectangle" >
|
||||
<solid
|
||||
android:color="#ff333333" />
|
||||
android:color="@color/conversation_item_received_background_dark" />
|
||||
</shape>
|
||||
</rotate>
|
||||
</item>
|
||||
|
||||
18
res/drawable/conversation_item_sent_indicator_text_shape.xml
Normal file
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="@dimen/conversation_item_drop_shadow_dist">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/conversation_item_received_background_light" />
|
||||
<corners android:bottomLeftRadius="@dimen/conversation_item_corner_radius" android:bottomRightRadius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
@@ -0,0 +1,18 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="@dimen/conversation_item_drop_shadow_dist">
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/conversation_item_received_background_dark" />
|
||||
<corners android:bottomLeftRadius="@dimen/conversation_item_corner_radius" android:bottomRightRadius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_light" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_dark" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_light" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_dark" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_light" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_dark" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#09000000" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_light" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#ff003c00" />
|
||||
<solid android:color="@color/conversation_item_sent_shadow_dark" />
|
||||
<corners android:radius="@dimen/conversation_item_corner_radius" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
</item>
|
||||
<item android:left="83dp" android:right="10dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#15000000" />
|
||||
<solid android:color="@color/conversation_list_divider_light" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@@ -7,7 +7,7 @@
|
||||
</item>
|
||||
<item android:left="83dp" android:right="10dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#22ffffff" />
|
||||
<solid android:color="@color/conversation_list_divider_dark" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
16
res/drawable/textlines.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#33000000" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="1dp">
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/white" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
16
res/drawable/textlines_dark.xml
Normal file
@@ -0,0 +1,16 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android" >
|
||||
<item>
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="#33ffffff" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
<item android:bottom="1dp">
|
||||
<shape
|
||||
android:shape="rectangle">
|
||||
<solid android:color="@color/black" />
|
||||
</shape>
|
||||
</item>
|
||||
|
||||
</layer-list>
|
||||
@@ -3,6 +3,7 @@
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:background="?conversation_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.RecipientsPanel
|
||||
@@ -17,7 +18,6 @@
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical"
|
||||
android:background="?conversation_background"
|
||||
android:gravity="bottom">
|
||||
|
||||
<fragment
|
||||
@@ -33,8 +33,7 @@
|
||||
android:id="@id/bottom_container"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="?conversation_background">
|
||||
android:orientation="vertical">
|
||||
|
||||
<ScrollView android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -112,7 +111,7 @@
|
||||
android:contentDescription="@string/conversation_activity__send"
|
||||
android:nextFocusLeft="@+id/embedded_text_editor"
|
||||
android:padding="12dp"
|
||||
android:src="?conversation_send_button"
|
||||
android:src="?conversation_send_button_sms_insecure"
|
||||
android:clickable="false"
|
||||
android:enabled="false" />
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@
|
||||
android:fadingEdge="none"
|
||||
android:divider="@android:color/transparent"
|
||||
android:dividerHeight="0dp"
|
||||
android:layout_marginBottom="1dip"/>
|
||||
android:layout_marginBottom="1dip"
|
||||
android:cacheColorHint="?conversation_background" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -5,7 +5,6 @@
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="?conversation_background"
|
||||
android:padding="8dp">
|
||||
|
||||
<TextView android:id="@+id/conversation_item_body"
|
||||
|
||||
@@ -4,7 +4,6 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="10dip"
|
||||
android:orientation="vertical"
|
||||
android:background="?conversation_background"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<TextView android:id="@+id/group_message_status"
|
||||
@@ -49,47 +48,37 @@
|
||||
android:background="?conversation_item_received_background"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
<TextView android:id="@+id/conversation_item_body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="5dip"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="10dp"
|
||||
android:orientation="horizontal">
|
||||
<ImageView android:id="@+id/key_exchange_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_menu_login"
|
||||
android:contentDescription="Key Exchange Indicator"
|
||||
android:visibility="gone" />
|
||||
<TextView android:id="@+id/conversation_item_body"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?conversation_received_text_primary_color"
|
||||
android:textSize="16sp"
|
||||
android:autoLink="all"
|
||||
android:linksClickable="true" />
|
||||
</LinearLayout>
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?conversation_received_text_primary_color"
|
||||
android:textSize="16sp"
|
||||
android:autoLink="all"
|
||||
android:linksClickable="true" />
|
||||
|
||||
<FrameLayout android:id="@+id/mms_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:visibility="gone">
|
||||
|
||||
<ImageView android:id="@+id/image_view"
|
||||
android:layout_width="230dip"
|
||||
android:layout_height="174dip"
|
||||
android:layout_gravity="center"
|
||||
android:scaleType="centerInside"
|
||||
android:adjustViewBounds="true"
|
||||
android:contentDescription="Message Media"
|
||||
android:contentDescription="@string/conversation_item__mms_image_description"
|
||||
android:visibility="gone" />
|
||||
<ImageButton android:id="@+id/play_slideshow_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center"
|
||||
android:src="@drawable/mms_play_btn"
|
||||
android:contentDescription="Play Button"
|
||||
android:contentDescription="@string/conversation_item__play_button_description"
|
||||
android:visibility="gone" />
|
||||
</FrameLayout>
|
||||
|
||||
@@ -131,7 +120,7 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingRight="3dip"
|
||||
android:src="?conversation_delivered_indicator"
|
||||
android:contentDescription="Delivered Indicator"
|
||||
android:contentDescription="@string/conversation_item_sent__delivered_description"
|
||||
android:visibility="gone" />
|
||||
|
||||
<ImageView android:id="@+id/sms_secure_indicator"
|
||||
@@ -141,7 +130,7 @@
|
||||
android:paddingRight="4dp"
|
||||
android:paddingTop="2dp"
|
||||
android:src="?menu_lock_icon_small"
|
||||
android:contentDescription="Secure Message Indicator"
|
||||
android:contentDescription="@string/conversation_item__secure_message_description"
|
||||
android:visibility="gone" />
|
||||
|
||||
<TextView android:id="@+id/conversation_item_date"
|
||||
@@ -169,7 +158,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_list_alert_sms_failed"
|
||||
android:contentDescription="Send Failed Indicator"
|
||||
android:contentDescription="@string/conversation_item_sent__send_failed_indicator_description"
|
||||
android:visibility="gone" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
@@ -4,8 +4,7 @@
|
||||
android:id="@+id/conversation_item"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:background="?conversation_background">
|
||||
android:orientation="horizontal">
|
||||
|
||||
<RelativeLayout android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
@@ -19,9 +18,10 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
android:gravity="right"
|
||||
android:gravity="left|center_vertical"
|
||||
android:layout_marginLeft="6dp"
|
||||
android:layout_alignParentLeft="true">
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_centerVertical="true">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/sms_failed_indicator"
|
||||
@@ -29,7 +29,16 @@
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_list_alert_sms_failed"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="Send Failed Indicator"/>
|
||||
android:contentDescription="@string/conversation_item_sent__send_failed_indicator_description"/>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/pending_approval_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_dialog_info_holo_light"
|
||||
android:visibility="gone"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:contentDescription="@string/conversation_item_sent__pending_approval_description"/>
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout android:id="@+id/conversation_item_parent"
|
||||
@@ -43,30 +52,16 @@
|
||||
android:layout_marginRight="12dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="5dip">
|
||||
|
||||
<TextView android:id="@+id/conversation_item_body"
|
||||
android:autoLink="all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:linksClickable="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:gravity="right"
|
||||
android:textColor="?conversation_sent_text_primary_color"
|
||||
android:textColorLink="?conversation_sent_text_primary_color"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/key_exchange_indicator"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/ic_menu_login"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="Key Exchange Indicator"/>
|
||||
</LinearLayout>
|
||||
<TextView android:id="@+id/conversation_item_body"
|
||||
android:autoLink="all"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingTop="5dip"
|
||||
android:linksClickable="true"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?conversation_sent_text_primary_color"
|
||||
android:textColorLink="?conversation_sent_text_primary_color"
|
||||
android:textSize="16sp" />
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@+id/mms_view"
|
||||
@@ -82,7 +77,7 @@
|
||||
android:scaleType="centerInside"
|
||||
android:adjustViewBounds="true"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="Media Message"/>
|
||||
android:contentDescription="@string/conversation_item__mms_image_description"/>
|
||||
|
||||
<ImageButton
|
||||
android:id="@+id/play_slideshow_button"
|
||||
@@ -91,7 +86,7 @@
|
||||
android:src="@drawable/mms_play_btn"
|
||||
android:layout_gravity="center"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="Play Button"/>
|
||||
android:contentDescription="@string/conversation_item__play_button_description"/>
|
||||
|
||||
</FrameLayout>
|
||||
|
||||
@@ -131,7 +126,7 @@
|
||||
android:paddingRight="3dip"
|
||||
android:src="?conversation_delivered_indicator"
|
||||
android:visibility="gone"
|
||||
android:contentDescription="Delivered Indicator"/>
|
||||
android:contentDescription="@string/conversation_item_sent__delivered_description"/>
|
||||
|
||||
<TextView android:id="@+id/group_message_status"
|
||||
android:layout_width="wrap_content"
|
||||
@@ -166,7 +161,7 @@
|
||||
android:visibility="gone"
|
||||
android:paddingLeft="4dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:contentDescription="Secure Message Indicator" />
|
||||
android:contentDescription="@string/conversation_item__secure_message_description" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
@@ -180,16 +175,22 @@
|
||||
android:layout_alignParentRight="true"
|
||||
/>
|
||||
|
||||
<ImageView xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:id="@id/contact_photo"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="40dp"
|
||||
android:cropToPadding="true"
|
||||
android:layout_marginRight="0dip"
|
||||
android:padding="0dip"
|
||||
android:scaleType="centerCrop"
|
||||
android:visibility="gone" />
|
||||
<TextView android:id="@+id/indicator_text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/conversation_item_parent"
|
||||
android:layout_alignParentRight="true"
|
||||
android:paddingRight="5dip"
|
||||
android:paddingLeft="5dip"
|
||||
android:paddingTop="3dp"
|
||||
android:paddingBottom="3dp"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_marginRight="15dp"
|
||||
android:layout_marginTop="-2dp"
|
||||
android:textSize="12sp"
|
||||
android:textColor="?conversation_sent_text_secondary_color"
|
||||
android:background="?conversation_item_sent_indicator_text_background"
|
||||
android:visibility="gone" />
|
||||
|
||||
</RelativeLayout>
|
||||
</org.thoughtcrime.securesms.ConversationItem>
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:drawSelectorOnTop="false"
|
||||
android:scrollbarStyle="insideOverlay"
|
||||
android:fadingEdgeLength="16dip"
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<org.thoughtcrime.securesms.ConversationListItem
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<FrameLayout android:id="@+id/contact_photo_frame"
|
||||
@@ -27,69 +27,67 @@
|
||||
android:layout_marginLeft="0dp" />
|
||||
</FrameLayout>
|
||||
|
||||
<TextView android:id="@+id/from"
|
||||
android:layout_width="wrap_content"
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?attr/conversation_list_item_contact_color"
|
||||
android:singleLine="true"
|
||||
android:layout_marginTop="12dip"
|
||||
android:layout_marginRight="5dip"
|
||||
android:layout_marginTop="4dip"
|
||||
android:layout_marginLeft="4dip"
|
||||
android:layout_marginBottom="10dip"
|
||||
android:layout_alignTop="@id/contact_photo_frame"
|
||||
android:layout_marginRight="8dip"
|
||||
android:layout_marginBottom="4dip"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_toRightOf="@id/contact_photo_frame"
|
||||
android:layout_alignParentRight="true"
|
||||
android:layout_alignWithParentIfMissing="true"
|
||||
android:ellipsize="marquee" />
|
||||
<!--android:layout_toLeftOf="@id/checkbox"-->
|
||||
android:orientation="vertical">
|
||||
|
||||
<TextView android:id="@+id/date"
|
||||
android:layout_marginTop="12dip"
|
||||
android:layout_marginLeft="5dip"
|
||||
android:paddingRight="10dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textSize="13dp"
|
||||
android:textColor="?attr/conversation_list_item_date_color"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:singleLine="true"
|
||||
android:layout_alignTop="@id/contact_photo_frame"
|
||||
android:layout_alignParentRight="true" />
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="1"
|
||||
android:gravity="bottom">
|
||||
|
||||
<ImageView android:id="@+id/error"
|
||||
android:layout_marginLeft="3dip"
|
||||
android:visibility="invisible"
|
||||
android:layout_toLeftOf="@id/date"
|
||||
android:layout_alignBottom="@id/date"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:src="@drawable/ic_list_alert_sms_failed"
|
||||
android:contentDescription="Error Alert" />
|
||||
<TextView android:id="@+id/from"
|
||||
android:layout_weight="1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textColor="?attr/conversation_list_item_contact_color"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee" />
|
||||
|
||||
<ImageView android:id="@+id/attachment"
|
||||
android:layout_marginLeft="3dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:visibility="gone"
|
||||
android:layout_toLeftOf="@id/error"
|
||||
android:layout_alignBottom="@id/date"
|
||||
android:src="@drawable/ic_attachment_universal_small"
|
||||
android:contentDescription="Attachment Indicator" />
|
||||
<ImageView android:id="@+id/attachment"
|
||||
android:layout_marginLeft="3dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="20sp"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/ic_attachment_universal_small"
|
||||
android:contentDescription="Attachment Indicator" />
|
||||
|
||||
<TextView android:id="@+id/subject"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/conversation_list_item_subject_color"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:singleLine="true"
|
||||
android:layout_marginBottom="12dip"
|
||||
android:layout_marginLeft="4dip"
|
||||
android:layout_alignBottom="@id/contact_photo_frame"
|
||||
android:layout_toRightOf="@id/contact_photo_frame"
|
||||
android:layout_toLeftOf="@id/date"
|
||||
android:ellipsize="end" />
|
||||
<ImageView android:id="@+id/error"
|
||||
android:layout_marginLeft="3dip"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_width="20sp"
|
||||
android:visibility="gone"
|
||||
android:src="@drawable/ic_list_alert_sms_failed"
|
||||
android:contentDescription="Error Alert" />
|
||||
|
||||
<TextView android:id="@+id/date"
|
||||
android:layout_marginLeft="3dip"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/conversation_list_item_date_color"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:singleLine="true" />
|
||||
</LinearLayout>
|
||||
|
||||
<TextView android:id="@+id/subject"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textColor="?attr/conversation_list_item_subject_color"
|
||||
android:fontFamily="sans-serif-light"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="end" />
|
||||
|
||||
</LinearLayout>
|
||||
</org.thoughtcrime.securesms.ConversationListItem>
|
||||
|
||||
@@ -5,12 +5,16 @@
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<org.thoughtcrime.securesms.components.SingleRecipientPanel android:id="@+id/recipients"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<fragment
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/contact_selection_list_fragment"
|
||||
android:name="org.thoughtcrime.securesms.SingleContactSelectionListFragment">
|
||||
android:name="org.thoughtcrime.securesms.PushContactSelectionListFragment">
|
||||
</fragment>
|
||||
|
||||
</LinearLayout>
|
||||
44
res/layout/outgoing_sms_preference.xml
Normal file
@@ -0,0 +1,44 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="15dp"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingBottom="15dp"
|
||||
android:paddingRight="10dp">
|
||||
|
||||
<CheckBox android:id="@+id/data_users"
|
||||
style="@style/TextSecureDialogPrimaryText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:singleLine="true"
|
||||
android:text="@string/preferences__sms_outgoing_push_users" />
|
||||
|
||||
<TextView
|
||||
style="@style/TextSecureDialogSecondaryText"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="-5dp"
|
||||
android:layout_marginLeft="32dp"
|
||||
android:textSize="12sp"
|
||||
android:text="@string/preferences__sms_outgoing_push_users_description" />
|
||||
|
||||
<CheckBox android:id="@+id/ask_before_fallback_data"
|
||||
style="@style/TextSecureDialogPrimaryText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:textSize="14sp"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:text="@string/preferences__sms_fallback_ask_fallback"
|
||||
android:layout_marginLeft="25dp" />
|
||||
|
||||
<CheckBox android:id="@+id/non_data_users"
|
||||
style="@style/TextSecureDialogPrimaryText"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:text="@string/preferences__sms_fallback_non_push_users" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -8,7 +8,7 @@
|
||||
<fragment
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:id="@+id/contact_selection_list_fragment"
|
||||
android:name="org.thoughtcrime.securesms.PushContactSelectionListFragment">
|
||||
</fragment>
|
||||
|
||||
@@ -1,13 +1,12 @@
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fastScrollEnabled="true" />
|
||||
<se.emilsjolander.stickylistheaders.StickyListHeadersListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
|
||||
25
res/layout/push_contact_selection_list_header.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="32sp"
|
||||
android:paddingLeft="10dp"
|
||||
android:paddingRight="25dp">
|
||||
|
||||
<TextView android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:textSize="15sp"
|
||||
android:textColor="?conversation_sent_text_secondary_color"
|
||||
android:textStyle="bold" />
|
||||
|
||||
<View android:layout_width="match_parent"
|
||||
android:layout_height="3dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginTop="2dp"
|
||||
android:background="?conversation_received_text_secondary_color" />
|
||||
</RelativeLayout>
|
||||
@@ -1,65 +1,48 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingRight="25dip">
|
||||
|
||||
<View android:id="@+id/push_support_label"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="3dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:background="#ff64a926"
|
||||
android:visibility="visible"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
<ImageView android:id="@+id/contact_photo_image"
|
||||
android:layout_width="@dimen/contact_selection_photo_size"
|
||||
android:layout_height="@dimen/contact_selection_photo_size"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold"
|
||||
android:visibility = "gone"
|
||||
/>
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginLeft="10dp"
|
||||
android:cropToPadding="true"
|
||||
android:scaleType="centerCrop"
|
||||
android:contentDescription="@string/SingleContactSelectionActivity_contact_photo" />
|
||||
|
||||
<TextView android:id="@+id/number"
|
||||
android:visibility = "visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_toRightOf="@id/contact_photo_image"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:fontFamily="sans-serif-light"
|
||||
/>
|
||||
android:fontFamily="sans-serif-light" />
|
||||
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/number"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginBottom="1dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:layout_toRightOf="@id/contact_photo_image"
|
||||
android:gravity="center_vertical|left"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
android:textAppearance="?android:attr/textAppearanceMedium" />
|
||||
|
||||
<CheckBox
|
||||
android:id="@+id/check_box"
|
||||
<CheckBox android:id="@+id/check_box"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentRight="true"
|
||||
|
||||
@@ -1,26 +0,0 @@
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<org.thoughtcrime.securesms.components.SingleRecipientPanel android:id="@+id/recipients"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingRight="15dp" />
|
||||
|
||||
<ListView android:id="@android:id/list"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="fill_parent"
|
||||
android:fastScrollEnabled="true" />
|
||||
|
||||
<TextView android:id="@android:id/empty"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center|center_vertical"
|
||||
android:layout_marginTop="15dp"
|
||||
android:text="@string/contact_selection_group_activity__finding_contacts"
|
||||
android:textSize="20sp" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -1,61 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="?android:attr/listPreferredItemHeight"
|
||||
android:paddingRight="25dip">
|
||||
|
||||
<View android:id="@+id/push_support_label"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_width="3dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:background="#ff64a926"
|
||||
android:visibility="visible"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:textStyle="bold"
|
||||
android:visibility = "gone"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/number"
|
||||
android:visibility = "visible"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dip"
|
||||
android:layout_marginTop="-8dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:fontFamily="sans-serif-light"
|
||||
/>
|
||||
|
||||
<TextView android:id="@+id/name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_above="@id/number"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentLeft="true"
|
||||
android:layout_marginBottom="1dip"
|
||||
android:layout_marginLeft="14dip"
|
||||
android:checkMark="?android:attr/listChoiceIndicatorMultiple"
|
||||
android:singleLine="true"
|
||||
android:ellipsize="marquee"
|
||||
android:gravity="center_vertical|left"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -13,7 +13,11 @@
|
||||
android:singleLine="true"
|
||||
android:hint="@string/recipients_panel__to"
|
||||
android:paddingRight="45dip"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:textColor="?conversation_editor_text_color"
|
||||
android:background="?conversation_editor_background"
|
||||
android:layout_width="fill_parent"/>
|
||||
|
||||
</RelativeLayout>
|
||||
@@ -5,8 +5,8 @@
|
||||
android:icon="?menu_lock_icon"
|
||||
android:showAsAction="ifRoom">
|
||||
<menu>
|
||||
<item android:title="@string/conversation_secure_verified__menu_verify_recipient"
|
||||
android:id="@+id/menu_verify_recipient"/>
|
||||
<item android:title="@string/conversation_secure_verified__menu_verify_identity"
|
||||
android:id="@+id/menu_verify_identity"/>
|
||||
</menu>
|
||||
</item>
|
||||
</menu>
|
||||
</menu>
|
||||
|
||||
@@ -15,6 +15,7 @@
|
||||
<string name="ApplicationPreferencesActivity_my">أنا</string>
|
||||
<string name="ApplicationPreferencesActivity_disable_storage_encryption">ايقاف تشفير التخزين؟</string>
|
||||
<string name="ApplicationPreferencesActivity_disable">تعطيل</string>
|
||||
<string name="ApplicationPreferencesActivity_unregistering">جاري إلغاء التسجيل...</string>
|
||||
<string name="ApplicationPreferencesActivity_error_connecting_to_server">خطأ في الاتصال مع مزود الخدمة...</string>
|
||||
<!--AttachmentTypeSelectorAdapter-->
|
||||
<string name="AttachmentTypeSelectorAdapter_picture">صورة</string>
|
||||
@@ -24,7 +25,6 @@
|
||||
<string name="ConversationItem_message_size_d_kb">حجم الرسالة: %d كيلوبايت</string>
|
||||
<string name="ConversationItem_expires_s">تاريخ الانتهاء: %s</string>
|
||||
<string name="ConversationItem_error_sending_message">خطأ بالارسال</string>
|
||||
<string name="ConversationItem_sending">ارسال...</string>
|
||||
<string name="ConversationItem_saving_attachment">حفظ الإضافة</string>
|
||||
<string name="ConversationItem_saving_attachment_to_sd_card">حفظ الإضافة على كرت الذاكرة</string>
|
||||
<string name="ConversationItem_save_to_sd_card">حفظ على كرت الذاكرة؟</string>
|
||||
@@ -35,9 +35,14 @@
|
||||
<string name="ConversationItem_view_secure_media_question">عرض الوسائط المشفرة؟</string>
|
||||
<string name="ConversationItem_received_and_processed_key_exchange_message">تم استقبال و معالجة رسالة تبادل المفاتيح</string>
|
||||
<string name="ConversationItem_error_received_stale_key_exchange_message">خطأ، تم استقبال رسالة تبادل مفاتيح غير صالحة</string>
|
||||
<string name="ConversationItem_group_action_left">%1$s تَرك المجموعة.</string>
|
||||
<string name="ConversationItem_group_action_joined">%1$s دخَل الفريق.</string>
|
||||
<string name="ConversationItem_group_action_modify">%1$s حَدث الفريق.</string>
|
||||
<!--ConversationActivity-->
|
||||
<string name="ConversationActivity_initiate_secure_session_question">هل أفتح قناة تشفير؟</string>
|
||||
<string name="ConversationActivity_initiate_secure_session_with_s_question">فتح قناة تشفير مع %s ؟</string>
|
||||
<string name="ConversationActivity_abort_secure_session_confirmation">تأكيد انهاء قناة التشفير</string>
|
||||
<string name="ConversationActivity_are_you_sure_that_you_want_to_abort_this_secure_session_question">هل انت متأكد من ان تريد انهاء قناة التشفير؟</string>
|
||||
<string name="ConversationActivity_delete_thread_confirmation">تأكيد حذف المحادثة</string>
|
||||
<string name="ConversationActivity_are_you_sure_that_you_want_to_permanently_delete_this_conversation_question">هل تريد حقاَ حذف هذه المحادثة نهائياً؟</string>
|
||||
<string name="ConversationActivity_add_attachment">إضافات</string>
|
||||
@@ -51,10 +56,15 @@
|
||||
<string name="ConversationActivity_forward_message_prefix">إعادة إرسال</string>
|
||||
<string name="ConversationActivity_group_conversation_recipients">مستقبلي رسائل المحادثة الجماعية</string>
|
||||
<string name="ConversationActivity_group_conversation">محادثة جماعية</string>
|
||||
<string name="ConversationActivity_unnamed_group">مجموعة لا اسم لها</string>
|
||||
<string name="ConversationActivity_d_recipients_in_group">أعضاء %d </string>
|
||||
<string name="ConversationActivity_d_recipients_in_group_singular">عضو 1</string>
|
||||
<string name="ConversationActivity_saving_draft">حفظ مسودة</string>
|
||||
<string name="ConversationActivity_invalid_recipient">جهة اتصال غير صحيحة!</string>
|
||||
<string name="ConversationActivity_calls_not_supported">خاصية الاتصال غير متوفرة</string>
|
||||
<string name="ConversationActivity_this_device_does_not_appear_to_support_dial_actions">هذا الجهاز لا يدعم خاصية الاتصال.</string>
|
||||
<string name="ConversationActivity_leave_group">اترك المجموعة؟</string>
|
||||
<string name="ConversationActivity_are_you_sure_you_want_to_leave_this_group">هل أنت متأكد من ترك المجموعة؟</string>
|
||||
<!--ConversationFragment-->
|
||||
<string name="ConversationFragment_message_details">تفاصيل الرسالة</string>
|
||||
<string name="ConversationFragment_confirm_message_delete">تأكيد حذف الرسالة</string>
|
||||
@@ -82,6 +92,13 @@
|
||||
<string name="ExportFragment_success">نجحت!</string>
|
||||
<string name="ExportFragment_exporting_keys_settings_and_messages">جاري تصدير مفاتيحك المشفرة و إعداداتك و رسائلك</string>
|
||||
<!--GroupCreateActivity-->
|
||||
<string name="GroupCreateActivity_actionbar_title">مجموعة جديدة</string>
|
||||
<string name="GroupCreateActivity_actionbar_update_title">حدث المجموعة</string>
|
||||
<string name="GroupCreateActivity_group_name_hint">اسم المجموعة</string>
|
||||
<string name="GroupCreateActivity_actionbar_mms_title">مجموعة MMS جديدة</string>
|
||||
<string name="GroupCreateActivity_avatar_content_description">الصورة الرمزية للمجموعة</string>
|
||||
<string name="GroupCreateActivity_menu_create_title">إنشاء مجموعة</string>
|
||||
<string name="GroupCreateActivity_creating_group">انشاء %1$s…</string>
|
||||
<!--ImportFragment-->
|
||||
<string name="ImportFragment_import_system_sms_database">استيراد الرسائل القصيرة بالنظام؟</string>
|
||||
<string name="ImportFragment_this_will_import_messages_from_the_system">هذه الخاصية ستستورد الرسائل القصيرة من النظام إلى داخل تكست سيكيور. إذا قمت بعمل هذا من قبل فسيتم تكرار رسائلك القصيرة.</string>
|
||||
@@ -126,12 +143,39 @@
|
||||
<string name="ReceiveKeyActivity_you_may_wish_to_verify_this_contact">ربما تود التحقق من جهة الاتصال هذه</string>
|
||||
<string name="ReceiveKeyActivity_the_signature_on_this_key_exchange_is_trusted_but">التوقيع لجهة الاتصال هذه معتمد لديك، لكنك قمت بتعطيل خاصية اكمال تبادل المفاتيح اوتوماتيكياً</string>
|
||||
<!--RegistrationActivity-->
|
||||
<string name="RegistrationActivity_select_your_country">اختر بلدك</string>
|
||||
<string name="RegistrationActivity_you_must_specify_your_phone_number">يجب عليك تحديد\nرقم الهاتف الخاص بك</string>
|
||||
<string name="RegistrationActivity_invalid_number">رقم غير صالح</string>
|
||||
<string name="RegistrationActivity_unsupported">غير مسنود </string>
|
||||
<string name="RegistrationActivity_continue">متابعة</string>
|
||||
<string name="RegistrationActivity_edit">تحرير</string>
|
||||
<!--RegistrationProblemsActivity-->
|
||||
<string name="RegistrationProblemsActivity_possible_problems">مشاكل ممكنة.</string>
|
||||
<!--RegistrationProgressActivity-->
|
||||
<string name="RegistrationProgressActivity_verifying_number">جاري التحقٌق من الرقم</string>
|
||||
<string name="RegistrationProgressActivity_edit_s">تعديل %s</string>
|
||||
<string name="RegistrationProgressActivity_registration_complete">اكتمال التسجيل!</string>
|
||||
<string name="RegistrationProgressActivity_possible_problems">مشاكل ممكنة.</string>
|
||||
<string name="RegistrationProgressActivity_you_must_enter_the_code_you_received_first">يجب عليك إدخال الرمز الذي تلقيته اولاً...</string>
|
||||
<string name="RegistrationProgressActivity_connecting">جاري الاتصال</string>
|
||||
<string name="RegistrationProgressActivity_connecting_for_verification">جاري الإتصال للتحقٌق...</string>
|
||||
<string name="RegistrationProgressActivity_network_error">خطأ في الشبكة!</string>
|
||||
<string name="RegistrationProgressActivity_verification_failed">فشل التحقٌق!</string>
|
||||
<string name="RegistrationProgressActivity_too_many_attempts">محاولات كثيرة</string>
|
||||
<string name="RegistrationProgressActivity_requesting_call">جاري طلب الاتصال</string>
|
||||
<string name="RegistrationProgressActivity_server_error">خطأ في الخادم</string>
|
||||
<string name="RegistrationProgressActivity_too_many_requests">طلبات كثيرة!</string>
|
||||
<string name="RegistrationProgressActivity_youve_already_requested_a_voice_call">لقد طلبت مؤخرا بآجراء مكالمة صوتية. يمكنك طلب مكالمة آخرى في 20 دقيقة. </string>
|
||||
<string name="RegistrationProgressActivity_verifying_voice_code">التحقق من رمز الصوت...</string>
|
||||
<string name="RegistrationProgressActivity_registration_conflict">تعارض في التسجيل</string>
|
||||
<!--RegistrationService-->
|
||||
<string name="RegistrationService_registration_complete">اكتمال التسجيل</string>
|
||||
<string name="RegistrationService_registration_error">تسجيل خاطئ</string>
|
||||
<!--SmsMessageRecord-->
|
||||
<string name="SmsMessageRecord_secure_session_ended">قناة التشفير انتهت.</string>
|
||||
<!--ThreadRecord-->
|
||||
<string name="ThreadRecord_left_the_group">ترك الفريق...</string>
|
||||
<string name="TheadRecord_secure_session_ended">قناة التشفير انتهت.</string>
|
||||
<!--VerifyIdentityActivity-->
|
||||
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">لا يوجد لديك مفتاح هوية!</string>
|
||||
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">لا يوجد لدى الطرف الاخر مفتاح هوية!</string>
|
||||
@@ -182,6 +226,9 @@
|
||||
<string name="ApplicationMigrationService_import_in_progress">جاري الاستيراد...</string>
|
||||
<string name="ApplicationMigrationService_importing_text_messages">جاري استيراد الرسائل النصية</string>
|
||||
<!--KeyCachingService-->
|
||||
<string name="KeyCachingService_textsecure_passphrase_cached">المس للفتح</string>
|
||||
<string name="KeyCachingService_passphrase_cached">انفتح تكست سيكيور </string>
|
||||
<string name="KeyCachingService_lock">أقفل بعبارة سرية</string>
|
||||
<!--MessageNotifier-->
|
||||
<string name="MessageNotifier_d_new_messages">(%d) رسائل جديدة</string>
|
||||
<string name="MessageNotifier_most_recent_from_s">آخر رسالة من: %s</string>
|
||||
@@ -218,6 +265,7 @@
|
||||
<!--contact_selection_recent_activity-->
|
||||
<string name="contact_selection_recent_activity__no_recent_calls">لا توجد مكالمات حديثة.</string>
|
||||
<!--conversation_activity-->
|
||||
<string name="conversation_activity__type_message"><small>ارسل رسالة</small></string>
|
||||
<string name="conversation_activity__send">ارسال</string>
|
||||
<string name="conversation_activity__remove">إزالة</string>
|
||||
<!--conversation_item_sent-->
|
||||
@@ -229,6 +277,7 @@
|
||||
<!--conversation_fragment_cab-->
|
||||
<string name="conversation_fragment_cab__batch_selection_mode">وضع الدفعات</string>
|
||||
<!--country_selection_fragment-->
|
||||
<string name="country_selection_fragment__loading_countries">جاري تحميل البلدان...</string>
|
||||
<string name="country_selection_fragment__search">بحث</string>
|
||||
<!--create_passphrase_activity-->
|
||||
<string name="create_passphrase_activity__passphrase">العبارة السرية:</string>
|
||||
@@ -236,8 +285,13 @@
|
||||
<string name="create_passphrase_activity__continue">متابعة</string>
|
||||
<string name="create_passphrase_activity__generating_secrets">جاري إنشاء الأسرار</string>
|
||||
<string name="create_passphrase_activity__skip">تجاهل</string>
|
||||
<string name="create_passphrase_activity__create">إنشاء</string>
|
||||
<!--log_submit_activity-->
|
||||
<string name="log_submit_activity__button_cancel">لا تقدم</string>
|
||||
<string name="log_submit_activity__button_ok">قدَم</string>
|
||||
<string name="log_submit_activity__log_submit_success_title">نجحت!</string>
|
||||
<string name="log_submit_activity__copied_to_clipboard">نسخ إلى الحافظة</string>
|
||||
<string name="log_submit_activity__thanks">شكرا لمساعدتك!</string>
|
||||
<!--database_migration_activity-->
|
||||
<string name="database_migration_activity__would_you_like_to_import_your_existing_text_messages">هل تود استيراد الرسائل النصية إلى قواعد بيانات تكست سيكيور المشفرة؟</string>
|
||||
<string name="database_migration_activity__the_default_system_database_will_not_be_modified">لن يتم تغيير قاعدة البيانات الأساسية بأي طريقة.</string>
|
||||
@@ -266,11 +320,31 @@
|
||||
<!--receive_key_activity-->
|
||||
<string name="receive_key_activity__complete">انتهاء</string>
|
||||
<!--registration_activity-->
|
||||
<string name="registration_activity__your_country">بلدك</string>
|
||||
<string name="registration_activity__phone_number">رقم الهاتف</string>
|
||||
<string name="registration_activity__register">سجل</string>
|
||||
<string name="registration_activity__skip">تجاهل</string>
|
||||
<!--registration_problems-->
|
||||
<string name="registration_problems__some_possible_problems_include">بعض المشاكل المحتملة\nوتشمل:</string>
|
||||
<string name="registration_problems__sms_interceptors">رسائل نصية قصيرة</string>
|
||||
<string name="registration_problems__incorrect_number"> رقم غير صحيح.</string>
|
||||
<string name="registration_problems__google_voice">.Google Voice</string>
|
||||
<string name="registration_problems__textsecure_will_not_work_with_google_voice_numbers">\nTextSecure لن يعمل مع أرقام Google Voice</string>
|
||||
<!--registration_progress_activity-->
|
||||
<string name="registration_progress_activity__voice_verification">التحقق الصوتي </string>
|
||||
<string name="registration_progress_activity__verify">تحقق</string>
|
||||
<string name="registration_progress_activity__call_me">اتصل بي</string>
|
||||
<string name="registration_progress_activity__edit_number">تعديل الرقم</string>
|
||||
<string name="registration_progress_activity__connectivity_error">خطأ في الاتصال</string>
|
||||
<string name="registration_progress_activity__some_possible_problems_include">بعض المشاكل \nالمحتملة وتشمل:</string>
|
||||
<string name="registration_progress_activity__no_network_connectivity">لا يوجد شبكة\nتوصيل.</string>
|
||||
<string name="registration_progress_activity__restrictive_firewall">جدار حماية مقيَد.</string>
|
||||
<string name="registration_progress_activity__textsecure_will_now_automatically_verify_your_number_with_a_confirmation_sms_message">\nسيتم الآن التحقق من رقم هاتفك تلقائيا بواسطة رسالة تأكيد SMS.</string>
|
||||
<string name="registration_progress_activity__connecting">جاري الاتصال...</string>
|
||||
<string name="registration_progress_activity__registering_with_server">جاري التسجيل مع الخادم...</string>
|
||||
<string name="registration_progress_activity__generating_keys">جارِ انشاء المفتاح...</string>
|
||||
<!--recipients_panel-->
|
||||
<string name="recipients_panel__to">إلى</string>
|
||||
<string name="recipients_panel__add_member">إضافة عضو</string>
|
||||
<!--review_identities-->
|
||||
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">لا يوجد لديك أي مفاتيح هوية مسجلة</string>
|
||||
<!--verify_identity_activity-->
|
||||
@@ -288,6 +362,7 @@
|
||||
<string name="AndroidManifest__create_passphrase">انشاء عبارة سرية</string>
|
||||
<string name="AndroidManifest__enter_passphrase">ادخل العبارة السرية</string>
|
||||
<string name="AndroidManifest__select_contacts">اختيار جهات الاتصال</string>
|
||||
<string name="AndroidManifest__select_contact">اختار جهة الاتصال</string>
|
||||
<string name="AndroidManifest__textsecure_detected">تم العثور على تكست سيكيور</string>
|
||||
<string name="AndroidManifest__public_identity_key">مفتاح الهوية العام</string>
|
||||
<string name="AndroidManifest__change_passphrase">غير العبارة السرية</string>
|
||||
@@ -301,10 +376,10 @@
|
||||
<string name="arrays__contact_keys">مفاتيح جهات الاتصال</string>
|
||||
<!--preferences.xml-->
|
||||
<string name="preferences__general">عام</string>
|
||||
<string name="preferences__pref_all_sms_title">استخدام لكل الرسائل القصيرة</string>
|
||||
<string name="preferences__pref_all_mms_title">استخدام لكل رسائل الوسائط المتعددة</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">استخدم تكست سيكيور لمشاهدة و حفظ كل الرسائل القصيرة الواردة</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">استخدم تكست سيكيور لمشاهدة وحفظ كل رسائل الوسائط المتعددة الواردة</string>
|
||||
<string name="preferences__input_settings">خيارات الادخال</string>
|
||||
<string name="preferences__enable_enter_key_title">تفعيل مفتاح الرجوع</string>
|
||||
<string name="preferences__pref_enter_sends_title">زر الرجوع Enter يرسل الرسالة</string>
|
||||
<string name="preferences__pressing_the_enter_key_will_send_text_messages">ضغط مفتاح الرجوع Enter سيرسل الرسالة</string>
|
||||
<string name="preferences__display_settings">اعدادات العرض</string>
|
||||
@@ -326,7 +401,10 @@
|
||||
<string name="preferences__manage_configured_identity_keys">إدارة مفاتيح الهوية التي تم ضبطها</string>
|
||||
<string name="preferences__notifications">التنبيهات</string>
|
||||
<string name="preferences__display_message_notifications_in_status_bar">عرض تنبيهات الرسائل بشريط التنبيهات</string>
|
||||
<string name="preferences__led_color">لون ضوء التنبيه LED</string>
|
||||
<string name="preferences__pref_led_blink_title">اسلوب وميض LED</string>
|
||||
<string name="preferences__sound">صوت</string>
|
||||
<string name="preferences__change_notification_sound">تغيير صوت التنبيه</string>
|
||||
<string name="preferences__inthread_notifications">تنبيهات داخل المحادثة</string>
|
||||
<string name="preferences__play_inthread_notifications">تشغيل صوت التنبيهات أثناء متابعة محادثة نشطة.</string>
|
||||
<string name="preferences__vibrate">هزاز</string>
|
||||
@@ -339,12 +417,14 @@
|
||||
<string name="preferences__orange">برتقالي</string>
|
||||
<string name="preferences__cyan">سماوي</string>
|
||||
<string name="preferences__magenta">وردي</string>
|
||||
<string name="preferences__white">أبيض</string>
|
||||
<string name="preferences__fast">سريع</string>
|
||||
<string name="preferences__normal">طبيعي</string>
|
||||
<string name="preferences__slow">بطىء</string>
|
||||
<string name="preferences__custom">مخصص</string>
|
||||
<string name="preferences__advanced">متقدم</string>
|
||||
<string name="preferences__passphrase">العبارة السرية</string>
|
||||
<string name="preferences__advanced_mms_access_point_names">تفضيلات MMS</string>
|
||||
<string name="preferences__mmsc_url_required">عنوان مركز رسائل الوسائط المتعددة (الزامي)</string>
|
||||
<string name="preferences__mms_proxy_host_optional">عنوان بروكسي رسائل الوسائط المتعددة (غير الزامي)</string>
|
||||
<string name="preferences__mms_proxy_port_optional">منفذ او بورت بروكسي الرسائل الوسائط المتعددة (غير الزامي)</string>
|
||||
@@ -370,6 +450,8 @@
|
||||
<string name="contact_selection_list__menu_unselect_all">إزالة اختيار الكل</string>
|
||||
<!--contact_selection-->
|
||||
<string name="contact_selection__menu_finished">تم الانتهاء</string>
|
||||
<!--refreshing push directory from menu-->
|
||||
<string name="push_directory__menu_refresh">انعش جهات الاتصال</string>
|
||||
<!--conversation_button_context-->
|
||||
<string name="conversation_button_context__menu_send_unencrypted">أرسل من دون تشفير</string>
|
||||
<!--conversation_callable-->
|
||||
@@ -389,9 +471,14 @@
|
||||
<string name="conversation_list__menu_search">بحث</string>
|
||||
<!--conversation_secure_verified-->
|
||||
<string name="conversation_secure_verified__menu_security">الأمن</string>
|
||||
<string name="conversation_secure_verified__menu_verify_recipient">تحقق من مستقبل الرسالة</string>
|
||||
<string name="conversation_secure_verified__menu_no_identity">لا هوية موجودة</string>
|
||||
<string name="conversation_secure_verified__menu_verify_identity">تحقق من الهوية</string>
|
||||
<string name="conversation_secure_verified__menu_abort_secure_session">انهاء قناة التشفير</string>
|
||||
<!--conversation-->
|
||||
<string name="conversation__menu_add_attachment">إضافات</string>
|
||||
<string name="conversation__menu_update_group">تحديث المجموعة</string>
|
||||
<string name="conversation__menu_leave_group">ترك المجموعة</string>
|
||||
<string name="conversation__menu_add_contact_info">إضافة معلومات الاتصال</string>
|
||||
<string name="conversation__menu_delete_thread">مسح المحاذفة</string>
|
||||
<!--conversation_group_options-->
|
||||
<string name="convesation_group_options__recipients_list">قائمة المشاركين</string>
|
||||
@@ -406,7 +493,9 @@
|
||||
<string name="text_secure_locked__menu_unlock">فتح القفل</string>
|
||||
<!--text_secure_normal-->
|
||||
<string name="text_secure_normal__menu_new_message">رسالة جديدة</string>
|
||||
<string name="text_secure_normal__menu_new_group">مجموعة جديدة</string>
|
||||
<string name="text_secure_normal__menu_settings">الاعدادات</string>
|
||||
<string name="text_secure_normal__menu_clear_passphrase">أقفل</string>
|
||||
<string name="text_secure_normal__mark_all_as_read">اعتبارها رسائل مقروءة</string>
|
||||
<!--verify_keys-->
|
||||
<string name="verify_keys__menu_verified">تم التحقق!</string>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<string name="ConversationItem_message_size_d_kb">Големина на съобщението: %d KB</string>
|
||||
<string name="ConversationItem_expires_s">Изтича: %s</string>
|
||||
<string name="ConversationItem_error_sending_message">Грешка при изпращане на съобщението</string>
|
||||
<string name="ConversationItem_sending">Праща се...</string>
|
||||
<string name="ConversationItem_saving_attachment">Записване на приложение</string>
|
||||
<string name="ConversationItem_saving_attachment_to_sd_card">Записване на приложение на SD карта</string>
|
||||
<string name="ConversationItem_save_to_sd_card">Запази на SD карта?</string>
|
||||
@@ -265,7 +264,6 @@
|
||||
<!--registration_problems-->
|
||||
<!--registration_progress_activity-->
|
||||
<!--recipients_panel-->
|
||||
<string name="recipients_panel__to">До</string>
|
||||
<!--review_identities-->
|
||||
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">Нямате никакви лични ключове в базата данни.</string>
|
||||
<!--verify_identity_activity-->
|
||||
@@ -296,8 +294,6 @@
|
||||
<string name="arrays__contact_keys">Контактни ключове</string>
|
||||
<!--preferences.xml-->
|
||||
<string name="preferences__general">Общо</string>
|
||||
<string name="preferences__pref_all_sms_title">Използвай за всички SMS-и</string>
|
||||
<string name="preferences__pref_all_mms_title">Използвай за всички MMS-и</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Използвайте TextSecure за разглеждане и съхраняване на всички входящи текстови съобщения</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Използвайте TextSecure за разглеждане и съхраняване на всички входящи мултимедийни съобщения</string>
|
||||
<string name="preferences__pref_enter_sends_title">Въведете изпращане</string>
|
||||
@@ -363,6 +359,7 @@
|
||||
<string name="contact_selection_list__menu_unselect_all">Не избирай нищо</string>
|
||||
<!--contact_selection-->
|
||||
<string name="contact_selection__menu_finished">Завършен</string>
|
||||
<!--refreshing push directory from menu-->
|
||||
<!--conversation_button_context-->
|
||||
<string name="conversation_button_context__menu_send_unencrypted">Прати без криптиране</string>
|
||||
<!--conversation_callable-->
|
||||
@@ -381,7 +378,7 @@
|
||||
<string name="conversation_list__menu_search">Търси</string>
|
||||
<!--conversation_secure_verified-->
|
||||
<string name="conversation_secure_verified__menu_security">Защита</string>
|
||||
<string name="conversation_secure_verified__menu_verify_recipient">Провери получателя</string>
|
||||
<string name="conversation_secure_verified__menu_verify_identity">Провери самоличността</string>
|
||||
<!--conversation-->
|
||||
<string name="conversation__menu_add_attachment">Добави файл</string>
|
||||
<string name="conversation__menu_delete_thread">Изтрий темата</string>
|
||||
|
||||
@@ -20,7 +20,6 @@
|
||||
<string name="ConversationItem_message_size_d_kb">འཕྲིན་ཐུང་ཆེ་ཆུང་།%d KB</string>
|
||||
<string name="ConversationItem_expires_s">དུས་ཚོད་རྫོགས།%s</string>
|
||||
<string name="ConversationItem_error_sending_message">གཏོང་བཞིན་པའི་འཕྲིན་ཐུང་དེར་ནོར་འཁྲུལ་ཞིག་འདུག </string>
|
||||
<string name="ConversationItem_sending">གཏོང་བཞིན་ཡོད། </string>
|
||||
<string name="ConversationItem_saving_attachment">ཟུར་སྣོན་ཉར་ཚགས་བྱེད་བཞིན་ཡོད།</string>
|
||||
<string name="ConversationItem_saving_attachment_to_sd_card">ཉེན་སྲུང་འཕྲུལ་ཆས་ཀྱི་བྱང་བུའི་(SD Card) ནང་ཟུར་སྣོན་ཉར་ཚགས་བྱེད་བཞིན་ཡོད།</string>
|
||||
<string name="ConversationItem_save_to_sd_card">ཉེན་སྲུང་འཕྲུལ་ཆས་ཀྱི་བྱང་བུའི་(SD Card) ནང་ཉར་ཚགས་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
|
||||
@@ -215,7 +214,6 @@
|
||||
<!--registration_problems-->
|
||||
<!--registration_progress_activity-->
|
||||
<!--recipients_panel-->
|
||||
<string name="recipients_panel__to">གཏོང་ཡུལ།</string>
|
||||
<!--review_identities-->
|
||||
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">གནས་སྐབས་རིང་ཁྱེད་ཀྱི་ཡིད་ཆེས་མཛོད་ཀྱི་ཁོངས་སུ་ཁྱེད་ལ་ངོས་འཛིན་ལྡེ་མིག་མི་འདུག </string>
|
||||
<!--verify_identity_activity-->
|
||||
@@ -242,10 +240,9 @@
|
||||
<string name="AndroidManifest__complete_key_exchange">ལྡེ་མིག་བརྗེ་ལེན་ཚར་བ་བྱོས། </string>
|
||||
<!--arrays.xml-->
|
||||
<!--preferences.xml-->
|
||||
<string name="preferences__pref_all_sms_title">འཕྲིན་ཐུང་SMSཚང་མའི་ཆེད་དུ་བེད་སྤྱོད་ཐོངས།</string>
|
||||
<string name="preferences__pref_all_mms_title"> སྣ་མང་འཕྲིན་ཐུང་ཚང་མའི་ཆེད་དུ་བེད་སྤྱོད་ཐོངས།</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">ནང་ཡོང་འཕྲིན་ཐུང་ཚང་མར་བལྟ་བ་དང་ཉར་བ་ལ་གཡག་འཕྲིན་བེད་སྤྱོད་ཐོངས།</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">ནང་ཡོང་སྣ་མང་འཕྲིན་ཐུང་རིགས་ལ་ཉར་ཚགས་དང་ཞིབ་ལྟའི་ཆེད་དུ་གཡག་འཕྲིན་བེད་སྤྱོད་ཐོངས།</string>
|
||||
<string name="preferences__input_settings">ནང་འཇུག་སྒྲིག་བཟོ། </string>
|
||||
<string name="preferences__pref_enter_sends_title">གཏོང་བ་ནང་འཇུག་བྱོས། </string>
|
||||
<string name="preferences__pressing_the_enter_key_will_send_text_messages">ནང་འཛུལ་ལྡེ་མིག་ལ་བསྣུན་ན་འཕྲིན་ཐུང་གཏོང་། </string>
|
||||
<string name="preferences__display_settings">སྒྲིག་བཟོ་སྟོན། </string>
|
||||
@@ -298,6 +295,7 @@
|
||||
<string name="contact_selection_list__menu_unselect_all">ཆ་ཚང་མ་འདེམས། </string>
|
||||
<!--contact_selection-->
|
||||
<string name="contact_selection__menu_finished">མཇུག་རྫོགས་སོ།</string>
|
||||
<!--refreshing push directory from menu-->
|
||||
<!--conversation_button_context-->
|
||||
<string name="conversation_button_context__menu_send_unencrypted">གསང་སྡོམ་མ་བྱས་བའི་འཕྲིན་ཐུང་བསྐུར།</string>
|
||||
<!--conversation_callable-->
|
||||
@@ -316,7 +314,7 @@
|
||||
<string name="conversation_list__menu_search">འཚོལ།</string>
|
||||
<!--conversation_secure_verified-->
|
||||
<string name="conversation_secure_verified__menu_security">ཉེན་སྲུང་།</string>
|
||||
<string name="conversation_secure_verified__menu_verify_recipient">འཕྲིན་ཐུང་འབྱོར་ཡུལ་དེ་ཁུངས་སྐྱེལ་བྱོས། </string>
|
||||
<string name="conversation_secure_verified__menu_verify_identity">ངོས་འཛིན་ཁུངས་སྐྱེལ་བྱོས། </string>
|
||||
<!--conversation-->
|
||||
<string name="conversation__menu_add_attachment">ཟུར་སྣོན་ཞོག </string>
|
||||
<string name="conversation__menu_delete_thread">འཕྲིན་ཐུང་སྐུད་རིམ་གསུབ།</string>
|
||||
|
||||
@@ -24,9 +24,7 @@
|
||||
<string name="ApplicationPreferencesActivity_you_are_not_registered_with_the_push_service">No estàs registrat al servei push...</string>
|
||||
<string name="ApplicationPreferencesActivity_updating_directory">Actualitzant directori</string>
|
||||
<string name="ApplicationPreferencesActivity_updating_push_directory">Actualitzant el Directori Push...</string>
|
||||
<string name="ApplicationPreferencesActivity_sms_enabled">SMS Habilitats</string>
|
||||
<string name="ApplicationPreferencesActivity_touch_to_change_your_default_sms_app">Toca per canviar la teva app d\'SMS predeterminada</string>
|
||||
<string name="ApplicationPreferencesActivity_sms_disabled">SMS Deshabilitats</string>
|
||||
<string name="ApplicationPreferencesActivity_touch_to_make_textsecure_your_default_sms_app">Toca per fer TextSecure la teva app d\'SMS predeterminada</string>
|
||||
<!--AttachmentTypeSelectorAdapter-->
|
||||
<string name="AttachmentTypeSelectorAdapter_picture">Fotografia</string>
|
||||
@@ -36,7 +34,6 @@
|
||||
<string name="ConversationItem_message_size_d_kb">Mida del missatge: %d KB</string>
|
||||
<string name="ConversationItem_expires_s">Expira: %s</string>
|
||||
<string name="ConversationItem_error_sending_message">Error enviant el missatge</string>
|
||||
<string name="ConversationItem_sending">Enviant...</string>
|
||||
<string name="ConversationItem_saving_attachment">Desant Adjunt</string>
|
||||
<string name="ConversationItem_saving_attachment_to_sd_card">Desant l\'adjunt a la targeta SD...</string>
|
||||
<string name="ConversationItem_save_to_sd_card">Desar a la targeta SD?</string>
|
||||
@@ -82,7 +79,7 @@
|
||||
<string name="ConversationActivity_are_you_sure_you_want_to_leave_this_group">Estàs segur que vols deixar aquest grup?</string>
|
||||
<!--ConversationFragment-->
|
||||
<string name="ConversationFragment_message_details">Detalls del missatge</string>
|
||||
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Transport: %1$s\nEnviat/Rebut:%2$s</string>
|
||||
<string name="ConversationFragment_transport_s_sent_received_s">Transport: %1$s\nEnviat/Rebut:%2$s</string>
|
||||
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Remitent: %1$s\nTransport: %2$s\nEnviat: %3$s\nRebut:%4$s</string>
|
||||
<string name="ConversationFragment_confirm_message_delete">Confirma l\'esborrat del missatge</string>
|
||||
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Estàs segur que vols esborrar permanentment aquest missatge?</string>
|
||||
@@ -308,9 +305,9 @@
|
||||
<!--contact_selection_group_activity-->
|
||||
<!--contact_selection_list_activity-->
|
||||
<string name="contact_selection_group_activity__no_contacts">Sense contactes.</string>
|
||||
<string name="contact_selection_group_activity__finding_contacts">Buscant contactes…</string>
|
||||
<!--single_contact_selection_activity-->
|
||||
<string name="single_contact_selection_group_activity__filter">Escriu un nom per filtrar...</string>
|
||||
<string name="SingleContactSelectionActivity_updating_directory">Actualitzant directori</string>
|
||||
<!--ContactSelectionListFragment-->
|
||||
<string name="ContactSelectionlistFragment_select_for">Selecciona per</string>
|
||||
<!--contact_selection_recent_activity-->
|
||||
@@ -420,7 +417,6 @@
|
||||
<string name="registration_progress_activity__sms_verification_failed">La verificació SMS ha fallat.</string>
|
||||
<string name="registration_progress_activity__generating_keys">Generant claus...</string>
|
||||
<!--recipients_panel-->
|
||||
<string name="recipients_panel__to">Per a</string>
|
||||
<string name="recipients_panel__add_member">Afegeix membre</string>
|
||||
<!--review_identities-->
|
||||
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">Actualment no tens cap clau d\'identitat a la teva base de dades de confiança.</string>
|
||||
@@ -454,8 +450,6 @@
|
||||
<string name="arrays__contact_keys">Claus de contactes</string>
|
||||
<!--preferences.xml-->
|
||||
<string name="preferences__general">General</string>
|
||||
<string name="preferences__pref_all_sms_title">Utilitza per tots els SMS</string>
|
||||
<string name="preferences__pref_all_mms_title">Utilitza per tots els MMS</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Utilitza TextSecure per veure i emmagatzemar tots els missatges de text entrants</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Utilitza TextSecure per veure i emmagatzemar tots els missatges multimèdia entrants</string>
|
||||
<string name="preferences__enable_enter_key_title">Habilita la tecla Enter</string>
|
||||
@@ -535,9 +529,7 @@
|
||||
<string name="preferences__make_textsecure_the_default_sms_mms_app">Fes TextSecure l\'aplicació d\'SMS/MMS predeterminada del teu sistema.</string>
|
||||
<string name="preferences__use_data_channel">Missatges Push</string>
|
||||
<string name="preferences__use_the_data_channel_for_communication_with_other_textsecure_users">\nAugmenta la privacitat i evita els càrrecs d\'SMS utilitzant el canal de dades per comunicar-se amb altres usuaris de TextSecure</string>
|
||||
<string name="preferences__allow_sms_fallback">SMS Alternatiu</string>
|
||||
<string name="preferences__allow_sms_fallback_disabled_reason">TextSecure és actualment la teva app d\'SMS predeterminada. Si us plau estableix una altra app d\'SMS abans de canviar aquesta preferència.</string>
|
||||
<string name="preferences__send_and_receive_sms_messages_when_push_is_not_available">Envia i rep missatges SMS quan push no estigui disponible</string>
|
||||
<string name="preferences__refresh_push_directory">Actualitza el Directori Push</string>
|
||||
<string name="preferences__submit_debug_log">Envia un registre de depuració</string>
|
||||
<!--****************************************-->
|
||||
@@ -548,6 +540,7 @@
|
||||
<string name="contact_selection_list__menu_unselect_all">Deselecciona tot</string>
|
||||
<!--contact_selection-->
|
||||
<string name="contact_selection__menu_finished">Finalitzat</string>
|
||||
<!--refreshing push directory from menu-->
|
||||
<!--conversation_button_context-->
|
||||
<string name="conversation_button_context__menu_send_unencrypted">Envia desencriptat</string>
|
||||
<!--conversation_callable-->
|
||||
@@ -570,7 +563,7 @@
|
||||
<!--conversation_secure_verified-->
|
||||
<string name="conversation_secure_verified__menu_security">Seguretat</string>
|
||||
<string name="conversation_secure_verified__menu_no_identity">No hi ha una identitat disponible</string>
|
||||
<string name="conversation_secure_verified__menu_verify_recipient">Verifica destinatari</string>
|
||||
<string name="conversation_secure_verified__menu_verify_identity">Verifica identitat</string>
|
||||
<string name="conversation_secure_verified__menu_abort_secure_session">Finalitza sessió segura</string>
|
||||
<!--conversation-->
|
||||
<string name="conversation__menu_add_attachment">Afegeix un adjunt</string>
|
||||
|
||||
@@ -27,7 +27,6 @@
|
||||
<string name="ConversationItem_message_size_d_kb">Délka zprávy: %d KB</string>
|
||||
<string name="ConversationItem_expires_s">Vyprší: %s</string>
|
||||
<string name="ConversationItem_error_sending_message">Chyba při odeslání zprávy</string>
|
||||
<string name="ConversationItem_sending">Odesílám...</string>
|
||||
<string name="ConversationItem_saving_attachment">Ukládám přílohu</string>
|
||||
<string name="ConversationItem_saving_attachment_to_sd_card">Ukládám přílohu na SD kartu..</string>
|
||||
<string name="ConversationItem_save_to_sd_card">Uložit na SD kartu?</string>
|
||||
@@ -294,7 +293,6 @@
|
||||
<!--contact_selection_group_activity-->
|
||||
<!--contact_selection_list_activity-->
|
||||
<string name="contact_selection_group_activity__no_contacts">Žádný kontakt</string>
|
||||
<string name="contact_selection_group_activity__finding_contacts">Hledat kontakty...</string>
|
||||
<!--single_contact_selection_activity-->
|
||||
<string name="single_contact_selection_group_activity__filter">Napište jméno pro vyfiltrování...</string>
|
||||
<!--ContactSelectionListFragment-->
|
||||
@@ -402,7 +400,6 @@
|
||||
<string name="registration_progress_activity__sms_verification_failed">Ověření pomocí SMS\n selhalo.\n </string>
|
||||
<string name="registration_progress_activity__generating_keys">Probíhá generování klíčů...</string>
|
||||
<!--recipients_panel-->
|
||||
<string name="recipients_panel__to">Komu</string>
|
||||
<string name="recipients_panel__add_member">Přidat člena</string>
|
||||
<!--review_identities-->
|
||||
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">Aktuálně nemáte žádné cizí identifikační klíče v databázi.</string>
|
||||
@@ -436,8 +433,6 @@
|
||||
<string name="arrays__contact_keys">Klíče kontaktů</string>
|
||||
<!--preferences.xml-->
|
||||
<string name="preferences__general">Obecné</string>
|
||||
<string name="preferences__pref_all_sms_title">Použít pro všechny SMS</string>
|
||||
<string name="preferences__pref_all_mms_title">Použít pro všechny MMS</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Používat TextSecure k prohlížení a uchování všech SMS</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Používat TextSecure k prohlížení a uchování všech MMS</string>
|
||||
<string name="preferences__enable_enter_key_title">Používat Enter</string>
|
||||
@@ -521,6 +516,7 @@
|
||||
<string name="contact_selection_list__menu_unselect_all">Odznačit vše</string>
|
||||
<!--contact_selection-->
|
||||
<string name="contact_selection__menu_finished">Dokončeno</string>
|
||||
<!--refreshing push directory from menu-->
|
||||
<!--conversation_button_context-->
|
||||
<string name="conversation_button_context__menu_send_unencrypted">Odeslat nezašifrovaně</string>
|
||||
<!--conversation_callable-->
|
||||
@@ -543,7 +539,7 @@
|
||||
<!--conversation_secure_verified-->
|
||||
<string name="conversation_secure_verified__menu_security">Zabezpečení</string>
|
||||
<string name="conversation_secure_verified__menu_no_identity">Není dostupná žádná identita</string>
|
||||
<string name="conversation_secure_verified__menu_verify_recipient">Ověřit příjemce</string>
|
||||
<string name="conversation_secure_verified__menu_verify_identity">Ověření identity</string>
|
||||
<string name="conversation_secure_verified__menu_abort_secure_session">Ukončit zabezpečenou relaci</string>
|
||||
<!--conversation-->
|
||||
<string name="conversation__menu_add_attachment">Přidat přílohu</string>
|
||||
|
||||
@@ -24,7 +24,6 @@
|
||||
<string name="ConversationItem_message_size_d_kb">Besked størrelse: %d KB</string>
|
||||
<string name="ConversationItem_expires_s">Udløber: %s</string>
|
||||
<string name="ConversationItem_error_sending_message">Kunne ikke sende besked</string>
|
||||
<string name="ConversationItem_sending">Sender...</string>
|
||||
<string name="ConversationItem_saving_attachment">Gemmer vedhæftning</string>
|
||||
<string name="ConversationItem_saving_attachment_to_sd_card">Gemmer vedhæftning til SD-kort...</string>
|
||||
<string name="ConversationItem_save_to_sd_card">Gem på SD-kort?</string>
|
||||
@@ -41,6 +40,7 @@
|
||||
<string name="ConversationActivity_delete_thread_confirmation">Bekræft! Slet tråd</string>
|
||||
<string name="ConversationActivity_are_you_sure_that_you_want_to_permanently_delete_this_conversation_question">Vil du slette denne samtale permanent?</string>
|
||||
<string name="ConversationActivity_add_attachment">Vedhæft fil.</string>
|
||||
<string name="ConversationActivity_select_contact_info">Vælg kontakt information</string>
|
||||
<string name="ConversationActivity_compose_message">Opret besked</string>
|
||||
<string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">Beklager, der opstod en fejl ved vedhæftning af fil.</string>
|
||||
<string name="ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions">Beklager, den valgte video overskrider grænsen på beskedstørrelsen</string>
|
||||
@@ -50,10 +50,14 @@
|
||||
<string name="ConversationActivity_forward_message_prefix">Videresend beskeden</string>
|
||||
<string name="ConversationActivity_group_conversation_recipients">Modtagere i gruppen</string>
|
||||
<string name="ConversationActivity_group_conversation">Gruppe beskeder</string>
|
||||
<string name="ConversationActivity_d_recipients_in_group">%d modtagere</string>
|
||||
<string name="ConversationActivity_d_recipients_in_group_singular">1 medlem</string>
|
||||
<string name="ConversationActivity_saving_draft">Gemmer kladde...</string>
|
||||
<string name="ConversationActivity_invalid_recipient">Ugyldig modtager!</string>
|
||||
<string name="ConversationActivity_calls_not_supported">Opkald er ikke understøttet</string>
|
||||
<string name="ConversationActivity_this_device_does_not_appear_to_support_dial_actions">Din enhed understøtter ikke initieret opkald.</string>
|
||||
<string name="ConversationActivity_leave_group">Forlad gruppe?</string>
|
||||
<string name="ConversationActivity_are_you_sure_you_want_to_leave_this_group">Er du sikker at du vil forlade denne gruppe?</string>
|
||||
<!--ConversationFragment-->
|
||||
<string name="ConversationFragment_message_details">Besked detaljer</string>
|
||||
<string name="ConversationFragment_confirm_message_delete">Bekræft sletning af besked</string>
|
||||
@@ -81,6 +85,14 @@
|
||||
<string name="ExportFragment_success">Fuldført!</string>
|
||||
<string name="ExportFragment_exporting_keys_settings_and_messages">Eksporterer krypterede nøgler, indstillinger og beskeder...</string>
|
||||
<!--GroupCreateActivity-->
|
||||
<string name="GroupCreateActivity_actionbar_title">Ny Gruppe</string>
|
||||
<string name="GroupCreateActivity_actionbar_update_title">Opdater Gruppe</string>
|
||||
<string name="GroupCreateActivity_group_name_hint">Gruppenavn</string>
|
||||
<string name="GroupCreateActivity_actionbar_mms_title">Ny MMS Gruppe</string>
|
||||
<string name="GroupCreateActivity_you_dont_own_this_group">Du er ikke ejer af denne gruppe, så du kan ikke ændre titlen eller billedet.</string>
|
||||
<string name="GroupCreateActivity_contacts_no_members">Der skal være mindst én person i gruppen!</string>
|
||||
<string name="GroupCreateActivity_contacts_invalid_number">En af medlemmerne i gruppen har et nummer der ikke kan læses. Ret nummeret eller slet kontakten, og prøv igen.</string>
|
||||
<string name="GroupCreateActivity_menu_create_title">Opret Gruppe</string>
|
||||
<!--ImportFragment-->
|
||||
<string name="ImportFragment_import_system_sms_database">Importer systemets SMS database?</string>
|
||||
<string name="ImportFragment_this_will_import_messages_from_the_system">Dette vil importere beskeder fra systemets standard SMS database til TextSecure.</string>
|
||||
@@ -131,17 +143,30 @@
|
||||
<string name="RegistrationActivity_continue">Fortsæt</string>
|
||||
<string name="RegistrationActivity_edit">Rediger</string>
|
||||
<!--RegistrationProblemsActivity-->
|
||||
<string name="RegistrationProblemsActivity_possible_problems">Mulige Problemer</string>
|
||||
<!--RegistrationProgressActivity-->
|
||||
<string name="RegistrationProgressActivity_verifying_number">Godkender nummer</string>
|
||||
<string name="RegistrationProgressActivity_edit_s">Rediger %s</string>
|
||||
<string name="RegistrationProgressActivity_registration_complete">Registrering fuldført!</string>
|
||||
<string name="RegistrationProgressActivity_possible_problems">Mulige problemer.</string>
|
||||
<string name="RegistrationProgressActivity_connecting">Forbinder</string>
|
||||
<string name="RegistrationProgressActivity_connecting_for_verification">Forbinder for godkendelse...</string>
|
||||
<string name="RegistrationProgressActivity_network_error">Netværksfejl!</string>
|
||||
<string name="RegistrationProgressActivity_unable_to_connect">Kunne ikke skabe forbindelse. Venligst tjek din netværks forbindelse og prøv igen.</string>
|
||||
<string name="RegistrationProgressActivity_verification_failed">Godkendelse fejlet!</string>
|
||||
<string name="RegistrationProgressActivity_the_verification_code_you_submitted_is_incorrect">Den godkendelses kode du indtastet er forkert. Prøv igen.</string>
|
||||
<string name="RegistrationProgressActivity_too_many_attempts">For mange forsøg</string>
|
||||
<string name="RegistrationProgressActivity_server_error">Server fejl</string>
|
||||
<string name="RegistrationProgressActivity_the_server_encountered_an_error">Serveren stødte på en fejl. Prøv igen.</string>
|
||||
<string name="RegistrationProgressActivity_registration_conflict">Registrerings-konflikt</string>
|
||||
<!--RegistrationService-->
|
||||
<string name="RegistrationService_registration_complete">Registrering Fuldført</string>
|
||||
<string name="RegistrationService_registration_error">Registreringsfejl</string>
|
||||
<!--SmsMessageRecord-->
|
||||
<string name="SmsMessageRecord_secure_session_ended">Sikker session afsluttet.</string>
|
||||
<!--ThreadRecord-->
|
||||
<string name="ThreadRecord_left_the_group">Forlod gruppen...</string>
|
||||
<string name="TheadRecord_secure_session_ended">Sikker session afsluttet.</string>
|
||||
<!--VerifyIdentityActivity-->
|
||||
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">Du har ingen ID-nøgle.</string>
|
||||
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">Modtageren har ingen ID-nøgle.</string>
|
||||
@@ -169,6 +194,7 @@
|
||||
<string name="ViewIdentityActivity_the_scanned_key_matches_exclamation">Den skannede nøgle stemmer overens!</string>
|
||||
<string name="ViewIdentityActivity_verified_exclamation">Bekræftet!</string>
|
||||
<string name="ViewIdentityActivity_identity_fingerprint">Identitet - fingeraftryk</string>
|
||||
<string name="ViewIdentityActivity_my_identity_fingerprint">Mit Identitets-fingeraftryk</string>
|
||||
<!--KeyExchangeInitiator-->
|
||||
<string name="KeyExchangeInitiator_initiate_despite_existing_request_question">initialiser til trods for eksisterende forespørgsel?</string>
|
||||
<string name="KeyExchangeInitiator_youve_already_sent_a_session_initiation_request_to_this_recipient_are_you_sure">Du har allerede sendt en forespørgsel om at initialisere en session til denne modtager. Er du sikker på at du vil sende en til? Dette vil gøre en tidligere forespørgsel ugyldig.</string>
|
||||
@@ -231,6 +257,7 @@
|
||||
<!--contact_selection_recent_activity-->
|
||||
<string name="contact_selection_recent_activity__no_recent_calls">Ingen nye samtaler.</string>
|
||||
<!--conversation_activity-->
|
||||
<string name="conversation_activity__type_message"><small>Send en besked</small></string>
|
||||
<string name="conversation_activity__send">Send</string>
|
||||
<string name="conversation_activity__remove">Slet</string>
|
||||
<!--conversation_item_sent-->
|
||||
@@ -250,8 +277,10 @@
|
||||
<string name="create_passphrase_activity__continue">Fortsæt</string>
|
||||
<string name="create_passphrase_activity__generating_secrets">GENERERER NØGLER</string>
|
||||
<string name="create_passphrase_activity__skip">Spring over</string>
|
||||
<string name="create_passphrase_activity__create">Opret</string>
|
||||
<!--log_submit_activity-->
|
||||
<string name="log_submit_activity__log_submit_success_title">Fuldført!</string>
|
||||
<string name="log_submit_activity__log_got_it">Finno</string>
|
||||
<!--database_migration_activity-->
|
||||
<string name="database_migration_activity__would_you_like_to_import_your_existing_text_messages">Ønsker du at importere dine eksisterende beskeder til TextSecures krypterede database?</string>
|
||||
<string name="database_migration_activity__the_default_system_database_will_not_be_modified">Den eksisterende systemdatabase vil ikke blive ændret.</string>
|
||||
@@ -289,13 +318,19 @@
|
||||
<string name="registration_activity__register">Register</string>
|
||||
<string name="registration_activity__skip">Spring over</string>
|
||||
<!--registration_problems-->
|
||||
<string name="registration_problems__incorrect_number">Forkert nummer.</string>
|
||||
<string name="registration_problems__google_voice">Google Voice.</string>
|
||||
<string name="registration_problems__textsecure_will_not_work_with_google_voice_numbers">\nTextSecure virker ikke med Google Voice numre.\n</string>
|
||||
<!--registration_progress_activity-->
|
||||
<string name="registration_progress_activity__verify">Godkend</string>
|
||||
<string name="registration_progress_activity__call_me">Ring til mig</string>
|
||||
<string name="registration_progress_activity__edit_number">Rediger nummer</string>
|
||||
<string name="registration_progress_activity__connectivity_error">Forbindelse fejl.</string>
|
||||
<string name="registration_progress_activity__connecting">Forbinder...</string>
|
||||
<string name="registration_progress_activity__waiting_for_sms_verification">Venter på SMS\ngodkendelse...\n</string>
|
||||
<string name="registration_progress_activity__sms_verification_failed">SMS godkendelse\nfejlede.\n</string>
|
||||
<!--recipients_panel-->
|
||||
<string name="recipients_panel__to">Til</string>
|
||||
<string name="recipients_panel__add_member">Tilføj medlem</string>
|
||||
<!--review_identities-->
|
||||
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">Du har ingen ID-nøgler i din sikret database.</string>
|
||||
<!--verify_identity_activity-->
|
||||
@@ -313,6 +348,7 @@
|
||||
<string name="AndroidManifest__create_passphrase">Opret kodeord</string>
|
||||
<string name="AndroidManifest__enter_passphrase">Indtast kodeord</string>
|
||||
<string name="AndroidManifest__select_contacts">Vælg kontakter</string>
|
||||
<string name="AndroidManifest__select_contact">Vælg kontakt</string>
|
||||
<string name="AndroidManifest__textsecure_detected">TextSecure Detekteret</string>
|
||||
<string name="AndroidManifest__public_identity_key">Offentlig Identitets Nøgle</string>
|
||||
<string name="AndroidManifest__change_passphrase">Skift kodeord</string>
|
||||
@@ -326,8 +362,6 @@
|
||||
<string name="arrays__contact_keys">Kontaktnøgler</string>
|
||||
<!--preferences.xml-->
|
||||
<string name="preferences__general">Generelt</string>
|
||||
<string name="preferences__pref_all_sms_title">Benyt til alle SMS</string>
|
||||
<string name="preferences__pref_all_mms_title">Benyt til alle MMS</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Benyt TextSecure til at læse og gemme alle indkommende SMS beskeder.</string>
|
||||
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Benyt TextSecure til at læse og gemme alle indkommende MMS beskeder.</string>
|
||||
<string name="preferences__pref_enter_sends_title">\"Enter\" sender</string>
|
||||
@@ -352,7 +386,10 @@
|
||||
<string name="preferences__manage_configured_identity_keys">Administrer konfigurerede ID-nøgler</string>
|
||||
<string name="preferences__notifications">Notifikation</string>
|
||||
<string name="preferences__display_message_notifications_in_status_bar">Vis notifikation om beskeder i status feltet</string>
|
||||
<string name="preferences__led_color">LED farve</string>
|
||||
<string name="preferences__pref_led_blink_title">LED blinkemønster</string>
|
||||
<string name="preferences__sound">Lyd</string>
|
||||
<string name="preferences__change_notification_sound">Ændre notifikations-lyd</string>
|
||||
<string name="preferences__vibrate">Vibrér</string>
|
||||
<string name="preferences__also_vibrate_when_notified">Vibrér også ved tilbagemelding</string>
|
||||
<string name="preferences__minutes">Minutter</string>
|
||||
@@ -363,6 +400,8 @@
|
||||
<string name="preferences__orange">Orange</string>
|
||||
<string name="preferences__cyan">Cyan</string>
|
||||
<string name="preferences__magenta">Magenta</string>
|
||||
<string name="preferences__white">Hvid</string>
|
||||
<string name="preferences__none">Ingen</string>
|
||||
<string name="preferences__fast">Hurtig</string>
|
||||
<string name="preferences__normal">Normal</string>
|
||||
<string name="preferences__slow">Langsom</string>
|
||||
@@ -394,6 +433,8 @@
|
||||
<string name="contact_selection_list__menu_unselect_all">Fjern markering</string>
|
||||
<!--contact_selection-->
|
||||
<string name="contact_selection__menu_finished">Afslut</string>
|
||||
<!--refreshing push directory from menu-->
|
||||
<string name="push_directory__menu_refresh">Opdater Kontakt Liste</string>
|
||||
<!--conversation_button_context-->
|
||||
<string name="conversation_button_context__menu_send_unencrypted">Send ukrypteret</string>
|
||||
<!--conversation_callable-->
|
||||
@@ -403,6 +444,7 @@
|
||||
<string name="conversation_context__menu_copy_text">Kopier tekst</string>
|
||||
<string name="conversation_context__menu_delete_message">Slet besked</string>
|
||||
<string name="conversation_context__menu_forward_message">Videresend besked</string>
|
||||
<string name="conversation_context__menu_resend_message">Send besked igen</string>
|
||||
<!--conversation_insecure-->
|
||||
<string name="conversation_insecure__menu_start_secure_session">Sikker session</string>
|
||||
<!--conversation_list_batch-->
|
||||
@@ -412,9 +454,12 @@
|
||||
<string name="conversation_list__menu_search">Søg</string>
|
||||
<!--conversation_secure_verified-->
|
||||
<string name="conversation_secure_verified__menu_security">Sikkerhed</string>
|
||||
<string name="conversation_secure_verified__menu_verify_recipient">Verificer modtager</string>
|
||||
<string name="conversation_secure_verified__menu_verify_identity">Verificer identitet</string>
|
||||
<!--conversation-->
|
||||
<string name="conversation__menu_add_attachment">Vedhæft fil</string>
|
||||
<string name="conversation__menu_update_group">Opdater Gruppe</string>
|
||||
<string name="conversation__menu_leave_group">Forlad Gruppe</string>
|
||||
<string name="conversation__menu_add_contact_info">Tilføj kontakt information</string>
|
||||
<string name="conversation__menu_delete_thread">Slet tråd</string>
|
||||
<!--conversation_group_options-->
|
||||
<string name="convesation_group_options__recipients_list">Modtager liste</string>
|
||||
@@ -429,6 +474,7 @@
|
||||
<string name="text_secure_locked__menu_unlock">Åben</string>
|
||||
<!--text_secure_normal-->
|
||||
<string name="text_secure_normal__menu_new_message">Ny Besked</string>
|
||||
<string name="text_secure_normal__menu_new_group">Ny Gruppe</string>
|
||||
<string name="text_secure_normal__menu_settings">Indstillinger</string>
|
||||
<string name="text_secure_normal__menu_clear_passphrase">Lås</string>
|
||||
<string name="text_secure_normal__mark_all_as_read">Marker alle som læst</string>
|
||||
|
||||