Compare commits

...

69 Commits

Author SHA1 Message Date
Moxie Marlinspike
4ea481f9dc Bumping version to 2.0.6
// FREEBIE
2014-04-04 09:27:05 -07:00
Moxie Marlinspike
e94fa0d752 Updated language translations.
// FREEBIE
2014-04-03 16:02:36 -07:00
Moxie Marlinspike
817070e76f Merge pull request #1288 from mcginty/send-icons-redux-redux
remove lock coloring in send icons
2014-04-03 15:11:33 -07:00
Jake McGinty
92b90cd798 remove lock coloring in send icons
// FREEBIE
2014-04-03 14:05:34 -07:00
Moxie Marlinspike
33ecc4d690 Add null check. // FREEBIE 2014-04-03 14:01:15 -07:00
Jake McGinty
7d5e66eb6e fix send icon merge botch, encryption icons logic
// FREEBIE
2014-04-03 12:12:59 -07:00
Moxie Marlinspike
d4ac0c077d Merge pull request #1248 from mcginty/wrong-group-secure
outgoing group MMS should not be encrypted and considered push
2014-04-03 11:22:00 -07:00
Jake McGinty
cbe87aa05c outgoing group MMS should never be encrypted
// FREEBIE
2014-04-03 11:21:13 -07:00
Moxie Marlinspike
9e2f82954f Merge pull request #1260 from mcginty/message-layout-lint
Conversation item tweaks
2014-04-03 10:07:23 -07:00
Jake McGinty
d4d684b670 simplified layouts 2014-04-03 09:59:32 -07:00
Moxie Marlinspike
c43ef8bce0 Merge pull request #1233 from mcginty/send-icons
new send icons, fix logic for disabling/enabling the button
2014-04-03 09:58:10 -07:00
Jake McGinty
54a882a11d new send icons, fix logic for disabling/enabling
// FREEBIE
2014-04-03 09:49:50 -07:00
Moxie Marlinspike
73e2f6ce59 Merge pull request #1272 from mcginty/flag-secure
hide screen security when unsupported
2014-04-02 22:14:08 -07:00
Moxie Marlinspike
54d2184c72 Merge pull request #1205 from mcginty/contact-select
refactor and improve contact selection
2014-04-02 22:11:44 -07:00
Jake McGinty
832763f695 UX for unencrypted fallback case 2014-04-02 22:10:51 -07:00
Moxie Marlinspike
40629a3bcf Fix decryption corner case when passphrase is disabled. 2014-04-02 22:10:51 -07:00
Moxie Marlinspike
5a3daf4846 Curve25519 keys to 1 mod 8 for ephemerals. 2014-04-02 22:10:51 -07:00
Moxie Marlinspike
fd1a18d2d0 Don't display duplicate push messages. 2014-04-02 22:10:50 -07:00
Moxie Marlinspike
ad5d6d5bb7 Add refresh path for PreKey queue. 2014-04-02 22:10:50 -07:00
Moxie Marlinspike
926d3c929f Handle simultaneous initiate protocol case.
1) Modify SessionRecord to store a list of "previous" sessions
   in addition to the current active session.  Previous sessions
   can be used for receiving messages, but not for sending
   messages.

2) When a possible "simultaneous initiate" is detected, push the
   current session onto the "previous session" stack instead of
   clearing it and starting over.

3) Additionally, mark the new session created on a received
   possible "simultaneous initiate" as stale for sending.  The
   next outgoing message would trigger a full prekey refresh.

4) Work to do: outgoing messages on the SMS transport should
   probably not use the existing session if it's marked stale
   for sending.  These messages need to fail and notify the user,
   similar to how we'll handle SMS fallback to push users before
   a prekey session is created.
2014-04-02 22:10:50 -07:00
Moxie Marlinspike
edc20883eb Merge pull request #1281 from thoughtbox/patch-6
Changed MMS Proxy setting for 24201 (Telenor)
2014-04-02 09:14:32 -07:00
thoughtbox
0d7363e36e Changed MMS Proxy setting for 24201 (Telenor)
Going forward, Telenor will be using FQDN.
2014-04-02 12:47:05 +02:00
Jake McGinty
ca6d8a8a0d refactor and improve contact selection
* unify single and multi contact selection activities
* follow android listview design recommendations more closely
* add contact photos to selection
* change indicator for push to be more obvious
* cache circle-cropped bitmaps
* dedupe numbers when contact has multiple of same phone number

// FREEBIE
2014-04-01 14:56:45 -07:00
Moxie Marlinspike
c414334059 Merge pull request #1228 from backspace/support-commas-in-contact-names
Remove commas in names in recipient list string
2014-03-31 08:39:15 -07:00
Moxie Marlinspike
24a38985cf Merge pull request #1249 from mcginty/convo-scroll
scroll to the new message in conversation
2014-03-31 08:14:26 -07:00
Brian Conway
60f2d4d6b6 Add APN for Aio wireless (USA).
Source: http://www.aiowireless.com/support/Apps-and-Services/Bring-Your-Own-Device-BYOD/customer/Bring-Your-Own-Android.html
2014-03-31 08:09:10 -07:00
Jake McGinty
06659fd98f hide screen security when unsupported
// FREEBIE
2014-03-30 02:28:38 -07:00
Jake McGinty
3c9c5213a7 Merge pull request #946 from DorianScholz/groupavatar
Use standard intent to select group avatar.
2014-03-28 12:07:30 -07:00
Dorian Scholz
a183f8d387 Fix #641 by using standard intent to select group avatar.
The ACTION_GET_CONTENT used with cropping is not supported on all devices.
To make this work more reliably I removed the cropping and MediaStore.EXTRA_OUTPUT.
The image is now read via getContentResolver().openInputStream() which should work on all device including KitKat/CM11.
2014-03-27 12:44:56 +01:00
Jake McGinty
216446c55b scroll to the new message in conversation
// FREEBIE
2014-03-26 15:12:01 -07:00
Buck Doyle
bc143059f6 Remove separators from names in recipient list
Fixes #1225 and #1174
2014-03-25 11:11:20 -07:00
Jake McGinty
33000582ed one more try at that one..
// FREEBIE
2014-03-25 03:31:44 -07:00
Jake McGinty
e651f352bb fix NPE in isPushDestination
// FREEBIE
2014-03-24 17:02:39 -07:00
Buck Doyle
cab4a06974 Remove commas in names in recipient list string
Fixes #1225
2014-03-23 15:02:21 -07:00
martinstingl
ccc1f5e9d6 Added the dependency "Android SDK Build-tools".
//FREEBIE
2014-03-20 18:06:58 -07:00
Moxie Marlinspike
b860aeff85 Minor ConversationList scrolling optimization. 2014-03-16 14:36:21 -07:00
Martin Ranta
34c885f08d Fixed a few localization names. 2014-03-16 10:06:26 -07:00
Moxie Marlinspike
71ab6f5c7d Merge pull request #1178 from backspace/extract-input-settings-string
Extract Input Settings preference header string.
2014-03-16 10:02:59 -07:00
Buck Doyle
61fbf382eb Extract Input Settings preference header string.
Fixes #1159

FREEBIE
2014-03-15 22:18:14 -04:00
Moxie Marlinspike
8b21f3f7d6 Bump version to 2.0.5 2014-03-14 10:18:13 -07:00
Moxie Marlinspike
941d0089f4 Add languages to selector 2014-03-14 09:40:58 -07:00
Moxie Marlinspike
8b8c6dd45f Updated translations
//FREEBIE
2014-03-14 09:40:43 -07:00
Jake McGinty
938545444e Merge pull request #984 from mcginty/sms-prefs
more precise sms controls
2014-03-13 21:03:02 -07:00
Jake McGinty
d827ab1b36 more precise sms controls
// FREEBIE
2014-03-13 20:59:20 -07:00
Jake McGinty
4701e59197 Merge pull request #1076 from phenx-de/fix-big-fontsize
Fix conversation list view for larger text sizes.
2014-03-13 13:05:56 -07:00
Moxie Marlinspike
2b2da84918 Merge pull request #1140 from psm14/bugs/group_mms_local_number
Also check cc for duplicates in group MMS
2014-03-12 18:58:08 -07:00
Pat McLaughlin
d229a4274c Also check cc for duplicates 2014-03-12 20:49:55 -04:00
3xo
64711771f0 Fix locale when using country codes. 2014-03-12 16:56:24 -07:00
Moxie Marlinspike
ad54d2a05f Modify string tag.
//FREEBIE
2014-03-12 09:57:32 -07:00
Wikinaut
068c40336c added Google Play Store text 2014-03-11 14:18:23 -07:00
Jake McGinty
11cfc4f1a1 upgrade gradle version
// FREEBIE
2014-03-11 01:05:24 -07:00
phenx-de
f51989b23e Fix conversation list view for larger text sizes. 2014-03-10 10:32:32 +01:00
Moxie Marlinspike
0b4fe84a41 Format-neutral compare numbers in MMS group with local device.
Closes #1018
2014-03-07 13:05:35 -08:00
Moxie Marlinspike
b855a1805f Mark message details on pending messages as such.
Closes #1034
2014-03-07 12:39:16 -08:00
3xo
339193af12 Give user the opportunity to chose no LED-notification. 2014-03-07 12:04:19 -08:00
Jake McGinty
574f33c92d Merge pull request #1069 from phenx-de/color-cleanup
Moved colors to the right place.
2014-03-07 12:00:49 -08:00
Manuel
b6a9eb5bf2 Closes #913: Message ID is -1 when called in aggregate 2014-03-07 11:59:53 -08:00
phenx-de
54b43b7536 Moved colors to the right place. 2014-03-07 11:03:22 +01:00
Moxie Marlinspike
37c9fb7cd1 Merge pull request #1053 from shenki/master
ConversationFragment: enable back button to dismiss message detail dialog
2014-03-06 20:46:43 -08:00
Joel Stanley
667d22bace ConversationFragment: enable back button to dismiss
The ConversationFragment has a AlertDialog for showing the message
details, which sets the cancelable property to be false. This stops the
user from being able to use the back button to dismiss the dialog.
2014-03-06 15:32:51 +10:30
Moxie Marlinspike
3d782449ed Merge pull request #1025 from DorianScholz/sharetextplain
Only receive text/plain.
2014-03-05 09:50:57 -08:00
Moxie Marlinspike
003095b08c Merge pull request #1020 from lxgr/fix-screensec
Use the "screen security" preference for the conversation list
2014-03-05 09:12:33 -08:00
Jake McGinty
d121d9531e Merge pull request #992 from veeti/overdraw
Fix overdraw issues in the conversation view
2014-03-05 00:10:06 -08:00
Veeti Paananen
42aeca26f1 Fix overdraw issues in the conversation view
Remove the redundant window background and row backgrounds to improve
drawing performance.
2014-03-05 08:03:55 +02:00
Dorian Scholz
ea8a1bae46 Only receive text/plain.
At the moment only text/plain is handled in RoutingActivity, so set filter in manifest accordingly.
This prevents #316. But at some point sharing text/calendar or better */* would be nice...

// FREEBIE
2014-03-04 23:57:36 +01:00
Jake McGinty
054fcdca8d Merge pull request #1007 from SamWhited/issue998
s/Verify Recipient/Verify Identity/
2014-03-04 13:49:49 -08:00
Lukas Ribisch
278220cf18 Use the "screen security" preference for the conversation list
It was previously only used for ConversationListActivity. This should be all
the locations (according to a grep for FLAG_SECURE).

FREEBIE
2014-03-04 21:04:04 +01:00
Chris Glass
35eeaa9bd5 Added more build information to BUILDING.md
Added more instructions about setting up a development environment.
2014-03-04 08:44:54 -08:00
Sam Whited
fa423e4432 s/Verify Recipient/Verify Identity/ 2014-03-04 09:55:45 -05:00
195 changed files with 6255 additions and 3073 deletions

View File

@@ -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"

View File

@@ -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

View File

@@ -12,6 +12,9 @@ Current BitHub Payment For Commit:
=================
[![Current Price](https://bithub.herokuapp.com/v1/status/payment/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

Binary file not shown.

View 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')
}

View File

@@ -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

View File

@@ -4,7 +4,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:0.8.+'
classpath 'com.android.tools.build:gradle:0.9.+'
}
}

View File

@@ -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;

View File

@@ -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);

Binary file not shown.

View File

@@ -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 {

View File

@@ -0,0 +1,7 @@
package org.whispersystems.textsecure.crypto;
public class DuplicateMessageException extends Exception {
public DuplicateMessageException(String s) {
super(s);
}
}

View File

@@ -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));

View File

@@ -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,

View File

@@ -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();
}

View File

@@ -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);
}
}

View File

@@ -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);
}
}

View File

@@ -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,

View File

@@ -0,0 +1,12 @@
package org.whispersystems.textsecure.push;
public class PreKeyStatus {
private int count;
public PreKeyStatus() {}
public int getCount() {
return count;
}
}

View File

@@ -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());

View File

@@ -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);
}
}

View File

@@ -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 {

View File

@@ -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

View File

@@ -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();
}
}

View File

@@ -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,

View 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>

View 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>

View 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>

View 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>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 884 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 557 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 677 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 550 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 858 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 873 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -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>

View File

@@ -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>

View File

@@ -9,7 +9,7 @@
<shape
android:shape="rectangle" >
<solid
android:color="#ffeeeeee" />
android:color="@color/conversation_item_received_background_light" />
</shape>
</rotate>
</item>

View File

@@ -9,7 +9,7 @@
<shape
android:shape="rectangle" >
<solid
android:color="#ff333333" />
android:color="@color/conversation_item_received_background_dark" />
</shape>
</rotate>
</item>

View 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>

View 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_dark" />
<corners android:bottomLeftRadius="@dimen/conversation_item_corner_radius" android:bottomRightRadius="@dimen/conversation_item_corner_radius" />
</shape>
</item>
</layer-list>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View 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>

View 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>

View File

@@ -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" />

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View 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>

View File

@@ -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>

View File

@@ -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"

View 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>

View File

@@ -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"

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

View File

@@ -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>

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