Compare commits

..

62 Commits
v0.8 ... v0.9

Author SHA1 Message Date
Moxie Marlinspike
3fac2ae97e Bumped version to 0.9 2013-02-12 14:07:10 -08:00
Moxie Marlinspike
3d1ade7554 Update language translations. 2013-02-12 11:53:43 -08:00
Moxie Marlinspike
c2dcf7ae74 Refactor MasterSecret initialization, access, and timeout paths.
1) Consolidate all of the KeyCachingService interaction into a single
   mixin. Activities extend delegates which call through to the mixin.

2) Switch Activity increment/decrement triggers from onStop to onPause
   in order to account for some screen locks that don't stop activities.
2013-02-10 17:30:51 -08:00
Moxie Marlinspike
90280a62ae Poor man's styling. 2013-02-09 17:43:31 -08:00
Moxie Marlinspike
2277dbd572 Localize ok/cancel 2013-02-09 17:38:33 -08:00
Moxie Marlinspike
471ef16a5b Support for robust delivery.
1) If a message fails to be delivered, post a notification in the
   status bar if that thread is not active and visible.

2) If a message fails to be delivered because there is no service,
   keep retrying every time service becomes available again.
2013-02-09 15:17:55 -08:00
Moxie Marlinspike
71f43075a9 Distinguish unread threads with background color. 2013-02-09 10:03:38 -08:00
Moxie Marlinspike
0a8c62e0e3 Include incoming message body in notifications.
1) Refactor the master secret reset logic to properly interact with
   services.

2) Add support for "BigText" and "Inbox" style notifications.

3) Decrypt message bodies when unlocked, display 'encrypted' when
   locked.
2013-02-08 16:27:43 -08:00
Moxie Marlinspike
10865bc75f Update README to reflect Play Store location. 2013-02-07 12:05:36 -08:00
Moxie Marlinspike
c426ed728a Bumped version to 0.8.6 2013-02-06 16:57:42 -08:00
Moxie Marlinspike
d1d0f8afe5 Fix attachment icon for GB devices 2013-02-06 16:45:14 -08:00
Moxie Marlinspike
49ea135e37 Don't auto-set thread to read on send. 2013-02-06 16:40:09 -08:00
Moxie Marlinspike
8dad82323d Fix for missing format string params in Tibetan translation. 2013-02-06 16:08:50 -08:00
Moxie Marlinspike
295abf8da3 Updated translations 2013-02-06 15:46:29 -08:00
Moxie Marlinspike
a29120d911 Fix for IndexOutOfBoundException (race condition on Recipients resolution) 2013-02-04 22:09:17 -08:00
Moxie Marlinspike
b044a68168 Fix for mysterious 'anonymous sender' NPE. 2013-02-04 21:51:25 -08:00
Moxie Marlinspike
01a5c889b0 Export stray strings for localization. 2013-02-04 11:12:03 -08:00
Moxie Marlinspike
dcf73cd009 Fix for 'group message' regression. 2013-02-04 11:03:19 -08:00
Moxie Marlinspike
bf92de394b Add support for resuming compose drafts. 2013-02-04 00:13:07 -08:00
Moxie Marlinspike
209711ae40 Fix notification behavior.
1) Don't add a notification item to the notification bar if the thread the
   message is for is active and visible.

2) Only sound the notification ringtone at 1/4th volume if the thread the
   message is for is active and visible.

3) Auto-clear the notification in the notification bar when a thread becomes
   visible from a screen-off situation.

4) Make notification updates asynchronous.
2013-02-03 18:41:34 -08:00
Moxie Marlinspike
288e2b5572 Suppport receiving 'share' actions from other apps. 2013-02-02 20:37:40 -08:00
Moxie Marlinspike
6bdb0e2d66 Bump version to 0.8.5 2013-01-14 13:13:59 -08:00
Moxie Marlinspike
ebdeea1401 Updated language files from transifex 2013-01-14 12:20:32 -08:00
Moxie Marlinspike
232e7bba2d A conversation can't be trimmed to 0. 2013-01-14 12:10:57 -08:00
Moxie Marlinspike
fe43ef65ab Support for auto-deleting old messages beyond a certain conversation thread length. 2013-01-09 21:06:56 -08:00
Moxie Marlinspike
a185750bb7 Preserve ConversationList scroll state in between onPause/onResume. 2013-01-09 20:39:05 -08:00
Moxie Marlinspike
5cb02445e8 Add support for "delivery notifications." Currently SMS-only. 2013-01-06 21:38:36 -08:00
Moxie Marlinspike
118560cf0d Fix for DB creation and migration with new date schema changes. 2013-01-06 18:47:20 -08:00
Moxie Marlinspike
2204584d8f Improve locking and performance on asynchronous contact loading. 2013-01-06 15:46:26 -08:00
Moxie Marlinspike
25f75cb3d2 Change wording around local APN configuration preference. 2013-01-06 14:41:07 -08:00
Moxie Marlinspike
83f90ddd4e Display both sent and received time in message details.
1) We record time sent in SMS database (date_sent).

2) We record time received in MMS database (date_received).

3) We union this information correctly in MmsSmsDatabase.
2013-01-06 13:13:14 -08:00
Moxie Marlinspike
ead97953e8 Add proxy port option to local APN settings. 2013-01-05 15:48:29 -08:00
Moxie Marlinspike
5f16da8fca Bumping version to 0.8.4 2013-01-02 13:47:42 -08:00
Moxie Marlinspike
61b6d11911 Remove permission to fix bug on 2.3.X devices. 2013-01-02 13:42:56 -08:00
Moxie Marlinspike
68c29f283d Fall back to user-configured MMS params on SecurityException 2013-01-02 10:06:34 -08:00
Moxie Marlinspike
8e720e0ede Bumping version to 0.8.3 2013-01-01 15:42:02 -08:00
Moxie Marlinspike
abd7fc67da Fix NoClassDefFound errors on 2.2 devices. 2013-01-01 15:38:09 -08:00
Moxie Marlinspike
e8c6acdec9 Bumped version to 0.8.2 2013-01-01 13:04:17 -08:00
Moxie Marlinspike
b805c4db4e Fix for NPE on empty MMS PDU 2013-01-01 12:54:12 -08:00
Moxie Marlinspike
62cad05acb Updated language translations 2013-01-01 12:42:14 -08:00
Moxie Marlinspike
61d4192798 Refactor ContactPhotoFactory to handle changing local contact URIs. 2012-12-30 16:42:33 -08:00
Moxie Marlinspike
9b45e6068b Better asynchronous loading for Recipient information.
1) Switch back from AsyncTasks to an Executor and Futures.

2) Make the Executor operate LIFO.

3) Make the Executor thread a BACKGROUND_PRIORITY thread.
2012-12-27 12:00:14 -08:00
Moxie Marlinspike
9939830551 Refactor recipient access.
1) Refactor recipient class to support asynchronous loading operations.

2) Refactor recipient factory to simplify recipient access.

3) Consoliate everything into one recipient provider that is capable of
doing async lookups and intelligent caching.
2012-12-24 08:40:37 -08:00
Moxie Marlinspike
f685cb550b Collapse NewRecipientProvider into RecipientProvider 2012-12-12 03:56:38 -08:00
Moxie Marlinspike
54fad30f9f Add ability to specify APN information in TextSecure settings.
1) Add configuration options for APN information in TextSecure settings.

2) Fall back to TextSecure settings if system settings are unavailable
while sending/receiving MMS.

3) Catch sqlite exception when devices randomly don't have the same
APN db or table structure.
2012-12-08 16:49:57 -08:00
Moxie Marlinspike
10885fa8db Merge pull request #101 from charlesmunger/master
Merge SMS broadcast receiver permission.
2012-12-06 15:42:51 -08:00
Charles Munger
28326d1c10 Now requires permission for SMS broadcast reciever.
This should prevent other applications from sending spoofed SMS messages
via a broadcast intent.
2012-12-06 14:59:35 -08:00
Moxie Marlinspike
84a29df1ee Switch passphrase creation to async task. 2012-12-01 16:02:56 -08:00
Moxie Marlinspike
0e5ff9bdac Fix for occasional NPE 2012-12-01 16:02:55 -08:00
Moxie Marlinspike
575341704e Proper recipient bracket parsing 2012-12-01 16:02:55 -08:00
Moxie Marlinspike
fa5e993191 Weird NPE 2012-12-01 16:02:55 -08:00
Moxie Marlinspike
c83d57aa59 Merge pull request #94 from thoughtbox/Change-default-behaviour-when-receiving-"flash"-messages
Changes default behaviour for CLASS 0 messages
2012-11-24 19:25:53 -08:00
Tor Houghton
4ced1a0f18 Changes default behaviour for CLASS 0 messages
"Flash" messages are no longer handled by TS.

Sorry for the multiple pulls -- previous request had a cut+paste typo.
2012-11-24 14:29:31 +01:00
Moxie Marlinspike
1496688ba0 Bump version to 0.8.1 2012-11-20 21:26:02 -08:00
Moxie Marlinspike
01f5ec5ab8 Align date for GB devices. Compromise nice checkbox alignment. 2012-11-20 20:56:21 -08:00
Moxie Marlinspike
158cf53d2c Update translations 2012-11-20 20:54:07 -08:00
Moxie Marlinspike
9bf434cb3b The <= GB QuickContactBadge is ugly. Let's do something nicer for them.
Unfortunately GB is 50% of the TS userbase, best not to neglect them.
2012-11-20 20:14:30 -08:00
Moxie Marlinspike
80a6c65790 We need READ_CALL_LOG if targetSDK is > 15. What a drag. 2012-11-20 19:09:46 -08:00
Moxie Marlinspike
c66e221598 Signal that we don't want screenshots of unlocked activities. 2012-11-20 18:49:56 -08:00
Moxie Marlinspike
47c83d2747 When retrieving MMS, continue on with best-effort in absence of APNs. 2012-11-18 14:52:33 -08:00
Moxie Marlinspike
5050b43234 Merge pull request #86 from thoughtbox/Fix-for-'forward'-string-reference
Fix reference to "forward" prefix
2012-11-05 22:17:53 -08:00
Tor Houghton
e8ffce53fe Fix reference to "forward" prefix
Code was erroneously referencing the string ID, rather than the
contents of the string (sorry!).
2012-11-05 18:45:04 +01:00
110 changed files with 7299 additions and 1494 deletions

View File

@@ -1,8 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="org.thoughtcrime.securesms"
android:versionCode="30"
android:versionName="0.8">
android:versionCode="37"
android:versionName="0.9">
<uses-sdk android:minSdkVersion="8" android:targetSdkVersion="16"/>
@@ -29,6 +29,7 @@
<uses-permission android:name="android.permission.WAKE_LOCK" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_CALL_LOG" />
<application android:icon="@drawable/icon"
android:label="@string/app_name"
@@ -52,6 +53,14 @@
<data android:scheme="smsto" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT"/>
<data android:mimeType="audio/*" />
<data android:mimeType="image/*" />
<data android:mimeType="text/*" />
</intent-filter>
</activity>
<activity android:name=".ConversationActivity"
@@ -154,6 +163,15 @@
<data android:mimeType="application/vnd.wap.mms-message" />
</intent-filter>
</receiver>
<receiver android:name=".service.SystemStateListener"
android:enabled="true"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.SERVICE_STATE"></action>
<action android:name="android.net.conn.CONNECTIVITY_CHANGE"></action>
</intent-filter>
</receiver>
<provider android:name=".providers.PartProvider"
android:authorities="org.thoughtcrime.provider.securesms" />

View File

@@ -39,6 +39,13 @@ Interested in helping to translate TextSecure? Contribute here:
https://www.transifex.com/projects/p/textsecure-official/
Downloads
------------
TextSecure can be downloaded from the Play Store here:
https://play.google.com/store/apps/details?id=org.thoughtcrime.securesms
Cryptography Notice
------------

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 927 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 156 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 748 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 993 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 692 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 151 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 562 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.2 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 160 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
android:drawable="@android:color/transparent" />
<item android:state_pressed="true" android:state_selected="false"
android:drawable="@android:color/transparent" />
<item android:state_selected="false" android:state_activated="false"
android:drawable="@color/read_bgcolor" />
<item android:state_activated="true"
android:drawable="@drawable/list_selector_background_selected" />
</selector>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2007 The Android Open Source Project
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true"
android:drawable="@android:color/transparent" />
<item android:state_pressed="true" android:state_selected="false"
android:drawable="@android:color/transparent" />
<item android:state_selected="false" android:state_activated="false"
android:drawable="@color/unread_bgcolor" />
<item android:state_activated="true"
android:drawable="@drawable/list_selector_background_selected" />
</selector>

View File

@@ -96,6 +96,13 @@
android:orientation="horizontal"
android:gravity="left">
<ImageView android:id="@+id/delivered_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="3dip"
android:src="@drawable/ic_sms_mms_delivered"
android:visibility="gone" />
<TextView android:id="@+id/group_message_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -105,8 +112,7 @@
android:textColor="#ffcccccc"
android:visibility="gone"
android:layout_marginRight="8dip"
android:paddingTop="1dip"/>
android:paddingTop="1dip"/>
<TextView android:id="@+id/conversation_item_date"
android:autoLink="all"

View File

@@ -118,6 +118,13 @@
android:orientation="horizontal"
android:gravity="right">
<ImageView android:id="@+id/delivered_indicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingRight="3dip"
android:src="@drawable/ic_sms_mms_delivered"
android:visibility="gone" />
<TextView android:id="@+id/group_message_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"

View File

@@ -1,15 +1,28 @@
<?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_height="?android:attr/listPreferredItemHeight"
android:layout_height="wrap_content"
android:paddingRight="10dip"
>
<QuickContactBadge android:id="@+id/contact_photo"
style="?android:attr/quickContactBadgeStyleWindowLarge"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true" />
<FrameLayout android:id="@+id/contact_photo_frame"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_alignParentLeft="true"
android:visibility="visible">
<QuickContactBadge android:id="@+id/contact_photo_badge"
style="?android:attr/quickContactBadgeStyleWindowLarge"
android:visibility="gone" />
<ImageView android:id="@+id/contact_photo_image"
android:layout_width="60dp"
android:layout_height="60dp"
android:cropToPadding="true"
android:scaleType="centerCrop"
android:visibility="gone" />
</FrameLayout>
<CheckBox android:id="@+id/checkbox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
@@ -25,8 +38,8 @@
android:layout_marginTop="6dip"
android:layout_marginRight="5dip"
android:layout_marginLeft="5dip"
android:layout_alignTop="@id/contact_photo"
android:layout_toRightOf="@id/contact_photo"
android:layout_alignTop="@id/contact_photo_frame"
android:layout_toRightOf="@id/contact_photo_frame"
android:layout_toLeftOf="@id/checkbox"
android:layout_alignWithParentIfMissing="true"
android:ellipsize="marquee"
@@ -41,8 +54,8 @@
android:textAppearance="?android:attr/textAppearanceSmall"
android:textColor="?android:attr/textColorSecondary"
android:singleLine="true"
android:layout_alignParentRight="true"
android:layout_alignParentBottom="true" />
android:layout_alignBottom="@id/contact_photo_frame"
android:layout_alignParentRight="true"/>
<ImageView android:id="@+id/error"
android:layout_marginLeft="3dip"
@@ -69,8 +82,8 @@
android:singleLine="true"
android:layout_marginBottom="10dip"
android:layout_marginLeft="5dip"
android:layout_alignBottom="@id/contact_photo"
android:layout_toRightOf="@id/contact_photo"
android:layout_alignBottom="@id/contact_photo_frame"
android:layout_toRightOf="@id/contact_photo_frame"
android:layout_toLeftOf="@id/date"
android:ellipsize="end" />

View File

@@ -27,6 +27,7 @@
<string name="ApplicationMigrationManager_copy">انسخ</string>
<string name="ApplicationMigrationManager_dont_copy">لا تنسخ</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">حاليا: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">غير موجود!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">لا يوجد مفتاح هوية صالح لجهة الاتصال المحددة</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">لا يوجد لديك مفتاح هوية</string>
@@ -35,7 +36,15 @@
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">يجب عليك إدخال العبارة السرية قبل استيراد المفاتيح السرية</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">يجب عليك إدخال العبارة السرية قبل ضبط المفاتيح السرية</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">لم تقم بتحديد عبارة سرية بعد!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">حد طول المحادثة</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">عدد الرسائل بالمحادثة</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">امسح كل الرسائل القديمة الآن؟</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">هل انت متأكد من مسح كل الرسائل بالمحادثات وابقاء أحدث %s رسالة فقط؟</string>
<string name="ApplicationPreferencesActivity_delete">حذف</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">صورة</string>
<string name="AttachmentTypeSelectorAdapter_video">فيديو</string>
<string name="AttachmentTypeSelectorAdapter_audio">صوت</string>
<!--ConversationItem-->
<string name="ConversationItem_message_size_d_kb">حجم الرسالة: %d كيلوبايت</string>
<string name="ConversationItem_expires_s">تاريخ الانتهاء: %s</string>
@@ -68,9 +77,14 @@
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">عذراً، حجم هذا التسجيل يزيد عن الحد المسموح بالرسالة.</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">الطرف الآخر ليس رقم هاتف أو عنوان بريد الكتروني صحيح!</string>
<string name="ConversationActivity_message_is_empty_exclamation">الرسالة فارغة</string>
<string name="ConversationActivity_forward_message_prefix">إعادة إرسال</string>
<string name="ConversationActivity_group_conversation_recipients">مستقبلي رسائل المحادثة الجماعية</string>
<string name="ConversationActivity_group_conversation">محادثة جماعية</string>
<string name="ConversationActivity_d_recipients_in_group">%d مشاركين في المجموعة</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">تفاصيل الرسالة</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">المرسل: %1$s\nالاتصال: %2$s\nارسال/استقبال: %3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">المرسل: %1$s\nالارسال: %2$s\nتم ارسال: %3$s\nتم استقبال:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">تأكيد حذف الرسالة</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">هل أنت متأكد من حذف هذه الرسالة نهائياً؟</string>
<!--ConversationListAdapter-->
@@ -177,6 +191,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">انتظر، يتم فك تشفير رسالة الوسائط المتعددة...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">خطأ بالرسالة المشفرة...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">الرسالة مشفرة دون وجود قناة اتصال...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">يتم النقل...</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">يتم الان نقل قواعد بيانات رسائلك القصيرة...</string>
@@ -185,8 +200,8 @@
<string name="KeyCachingService_passphrase_cached">تم حفظ العبارة السرية مؤقتاً</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) رسائل جديدة</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) رسائل جديدة، آخر رسالة من: %2$s</string>
<string name="MessageNotifier_most_recent_from_s">آخر رسالة من: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">وصلتك رسالة من شخص لديه القدرة على بدء قناة تشفير. هل تريد بدء قناة تشفير حتى يكون الاتصال سري و آمن؟</string>
<string name="auto_initiate_activity__initiate_exchange">البدء بتبادل المفاتيح</string>
@@ -205,6 +220,12 @@
<string name="conversation_activity__type_message">اكتب الرسالة</string>
<string name="conversation_activity__send">ارسال</string>
<string name="conversation_activity__remove">إزالة</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">تحميل</string>
<string name="conversation_item_sent__downloading">تحميل</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">تحميل</string>
<string name="conversation_item_received__downloading">تحميل</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">وضع الدفعات</string>
<!--create_passphrase_activity-->
@@ -305,6 +326,23 @@
<string name="preferences__normal">طبيعي</string>
<string name="preferences__slow">بطىء</string>
<string name="preferences__custom">مخصص</string>
<string name="preferences__advanced_mms_access_point_names">متقدم: نقطة اتصال رسائل الوسائط المتعددة</string>
<string name="preferences__enable_fallback_mmsc">شغل مركز رسائل الوسائط المتعددة الاحتياطي</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">استخدم مركز رسائل الوسائط المتعددة هنا اذا لم تتوفر بيناتات اتصال النظام APN</string>
<string name="preferences__mmsc_url_required">عنوان مركز رسائل الوسائط المتعددة (الزامي)</string>
<string name="preferences__mms_proxy_host_optional">عنوان بروكسي رسائل الوسائط المتعددة (غير الزامي)</string>
<string name="preferences__mms_proxy_port_optional">منفذ او بورت بروكسي الرسائل الوسائط المتعددة (غير الزامي)</string>
<string name="preferences__delivery_reports">تقرير التوصيل</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">اطلب تقرير توصيل لكل رسالة قصيرة ترسلها</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">اطلب تقرير توصيل لكل رسالة وسائط متعددة ترسلها</string>
<string name="preferences__mms_delivery_reports">تقرير توصيل رسائل الوسائط المتعددة</string>
<string name="preferences__sms_delivery_reports">تقرير توصيل الرسائل القصيرة</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">امسح كل الرسائل القديمة تلقائياً عند تخطي المحادثة الطول المحدد</string>
<string name="preferences__delete_old_messages">امسح الرسائل القديمة</string>
<string name="preferences__storage">التخزين</string>
<string name="preferences__conversation_length_limit">حد طول المحادثة</string>
<string name="preferences__trim_all_threads_now">نظف كل المحادثات الآن</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">افحص كل المحادثات وافرض حد الطول لكل محادثة</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
@@ -340,6 +378,7 @@
<string name="conversation__menu_delete_thread">مسح المحاذفة</string>
<string name="conversation__menu_compare">قارن</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">قائمة المشاركين</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">قارن</string>
<string name="key_scanning__menu_get_scanned_to_compare">قم بالمسح الضوئي للمقارنة</string>
@@ -356,7 +395,6 @@
<!--verify_keys-->
<string name="verify_keys__menu_verified">تم التحقق!</string>
<!--Misc. piggybacking-->
<string name="PlayStoreListing">تكست سيكيور برنامج يقدم الحماية والامن للرسائل القصيرة. الرسائل القصيرة المرسلة لمستخدمي تكست سيكيور الآخرين ترسل مشفرة بالكامل على الشبكة، وكل الرسائل القصيرة تحفظ مشفرة بقاعدة بيانات محلية بجهازك. اذا ضاع جهازك أو تمت سرقته فإن رسائلك بأمان ولن يمكن لأحد التنصت على الرسائل القصيرة المتبادلة بينك وبين مستخدمي تكست سيكيور الآخرين.</string>
<!--EOF-->
<string name="conversation_item_sent__download">تحميل</string>
<string name="conversation_item_received__download">تحميل</string>
</resources>

View File

@@ -1,83 +1,94 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">འཕྲིན་ཐུང་བདེ་འཇགས།</string>
<string name="yes">གཏན་འཁེལ།</string>
<string name="no">རྩིས་མེད་གཏོང་།</string>
<string name="delete">སུབ།</string>
<string name="yes">ཡིན། </string>
<string name="no">མེད། </string>
<string name="delete">སུབ། </string>
<!--ApplicationExportManager-->
<string name="ApplicationExportManager_import_database_and_settings_title">གཞི་གྲངས་དང་སྒྲིག་བཟོ་རྣམས་ནང་འཇུག་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="ApplicationExportManager_import_database_and_settings_message">SD Card ནས་བདེ་འཇགས་འཕྲིན་ཐུང་གི་གཞི་གྲངས། ལྡེ་མིག་དང་སྒྲིག་བཟོ་རྣམས་ནང་འཇུག་བྱེད་རྒྱུ་ཡིན་ནམ།\nཉེན་བརྡ།: འདི་ཡིས་ད་ཡོད་འཕྲིན་ཐུང་དང་ལྡེ་མིག སྒྲིག་བཟོ་སོགས་གསུབ་གི་རེད།</string>
<string name="ApplicationExportManager_importing_database_and_keys">གཞི་གྲངས་དང་ལྡེ་མིག་རྣམས་ནང་འཇུག་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationExportManager_importing_your_sms_database_keys_and_settings">ཁྱོད་ཀྱི་འཕྲིན་ཐུང་གི་གཞི་གྲངས། ལྡེ་མིག་དང་སྒྲིག་བཟོ་རྣམས་ནང་འཇུག་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationExportManager_export_database_question">གཞི་གྲངས་ཕྱིར་འདོན་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="ApplicationExportManager_import_database_and_settings_title">རེའུ་མིག་དང་སྒྲིག་བཟོ་རྣམས་ནང་འཇུག་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="ApplicationExportManager_import_database_and_settings_message">SD བྱང་བུ་ནས་བདེ་འཇགས་འཕྲིན་ཐུང་གི་རེའུ་མིག་དང་ལྡེ་མིག སྒྲིག་བཟོ་རྣམས་ནང་འཇུག་བྱེད་རྒྱུ་ཡིན་ནམ།\nཉེན་བརྡ།: འདི་ཡིས་ད་ཡོད་འཕྲིན་ཐུང་དང་ལྡེ་མིག སྒྲིག་བཟོ་སོགས་གསུབ་གི་རེད།</string>
<string name="ApplicationExportManager_importing_database_and_keys">རེའུ་མིག་དང་ལྡེ་མིག་རྣམས་ནང་འཇུག་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationExportManager_importing_your_sms_database_keys_and_settings">ཁྱོད་ཀྱི་འཕྲིན་ཐུང་གི་རེའུ་མིག་དང་ལྡེ་མིག སྒྲིག་བཟོ་རྣམས་ནང་འཇུག་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationExportManager_export_database_question">རེའུ་མིག་ཕྱི་ལ་དབོར་རྒྱུ་ཡིན་ནམ།</string>
<string name="ApplicationExportManager_export_textsecure_database_keys_and_settings_prompt">SD Card ནས་བདེ་འཇགས་འཕྲིན་ཐུང་གི་གཞི་གྲངས། ལྡེ་མིག་དང་སྒྲིག་བཟོ་རྣམས་ཕྱིར་འདོན་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="ApplicationExportManager_exporting_database_and_keys">གཞི་གྲངས་དང་ལྡེ་མིག་ཕྱིར་འདོན་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationExportManager_exporting_your_sms_database_keys_and_settings">ཁྱོད་ཀྱི་འཕྲིན་ཐུང་གི་གཞི་གྲངས། ལྡེ་མིག་དང་སྒྲིག་བཟོ་རྣམས་ཕྱིར་འདོན་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationExportManager_no_sd_card_found_exclamation">SD Card མི་འདུག</string>
<string name="ApplicationExportManager_error_exporting_to_sd_exclamation">SD Card ཕྱིར་འདན་བྱེད་སྐབས་ནོར་འཁྲུལ་བྱུང་</string>
<string name="ApplicationExportManager_import_successful_exclamation">ནང་འཇུག་གྲུབ་འབྲས་ལེགས་པོ་བྱུང་།</string>
<string name="ApplicationExportManager_export_successful_exclamation">ཕྱིར་འདོན་གྲུབ་འབྲས་ལེགས་པོ་བྱུང་།</string>
<string name="ApplicationExportManager_import">ནང་འཇུག་བྱོས།</string>
<string name="ApplicationExportManager_export">ཕྱི་འདན་བྱེད།</string>
<string name="ApplicationExportManager_exporting_your_sms_database_keys_and_settings">ཁྱོད་ཀྱི་འཕྲིན་ཐུང་གི་རེའུ་མིག་དང་ལྡེ་མིག་སྒྲིག་བཟོ་རྣམས་ཕྱིར་འདོན་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationExportManager_no_sd_card_found_exclamation">SD བྱང་བུ་མི་འདུག</string>
<string name="ApplicationExportManager_error_exporting_to_sd_exclamation">SD ལ་ཕྱིར་འདྲེན་བྱེད་པར་ནོར་འཁྲུལ། </string>
<string name="ApplicationExportManager_import_successful_exclamation">ནང་འཇུག་ལེགས་འགྲུབ་བྱུང་། </string>
<string name="ApplicationExportManager_export_successful_exclamation">ཕྱིར་འདྲེན་ལེགས་འགྲུབ་བྱུང་། </string>
<string name="ApplicationExportManager_import">ནང་འདྲེན་བྱོས། </string>
<string name="ApplicationExportManager_export">ཕྱི་འདྲེན་བྱོས། </string>
<!--ApplicationMigrationManager-->
<string name="ApplicationMigrationManager_migrating_database">གཞི་གྲངས་གནས་སྤོ་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationMigrationManager_migrating_text_message_database">འཕྲིན་ཐུང་གི་གཞི་གྲངས་གནས་སྤོ་བྱེད་བཞིན་ཡོད།</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_question">མ་ལག་འཕྲིན་ཐུང་ག་གི་གྲངས་དེ་འདྲ་བཟོ་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_explanation">ཡི་གེའི་ཉེན་སྲུང་གི་པར་གཞི་དེས། གཏན་འཇགས་མ་ལག་གཞི་གྲངས་མཛོད་ལས་ལོགས་སུ་ཡིན་པའི་གསང་སྡོམ་གྱི་གཞི་གྲངས་མཛོད་ཞིག་བེད་སྤྱོད་གཏོང་གི་ཡོད། ཁྱེད་རང་སོ་སོའི་ད་ཡོད་འཕྲིན་ཐུང་རྣམས་བདེ་འཇགས་འཕྲིན་ཐུང་གི་གསང་སྡོམ་གྱི་གཞི་གྲངས་མཛོད་དེའི་ཁོངས་སུ་བཤུ་ན་འདོད་དམ། ཁྱེད་རང་གི་གཏན་འཇགས་མ་ལག་གི་གཞི་གྲངས་མཛོད་དེ་ནུས་མེད་ཆགས་གི་མ་རེད།</string>
<string name="ApplicationMigrationManager_copy">འདྲ་བཟོ་བྱེད།</string>
<string name="ApplicationMigrationManager_dont_copy">འདྲ་བཟོ་མ་བྱེད།</string>
<string name="ApplicationMigrationManager_migrating_database">རེའུ་མིག་གནས་སྤོ་བྱེད་བཞིན་ཡོད། </string>
<string name="ApplicationMigrationManager_migrating_text_message_database">འཕྲིན་ཐུང་གི་རེའུ་མིག་གནས་སྤོ་བྱེད་བཞིན་ཡོད། </string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_question">འཕྲིན་ཐུང་རེའུ་མིག་གི་མ་ལག་དེ་འདྲ་བཤུས་བྱེད་རྒྱུ་ཡིན་ནམ། </string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_explanation">ཡི་གེའི་ཉེན་སྲུང་གི་པར་གཞི་དེས། གཏན་འཇགས་མ་ལག་གཞི་གྲངས་མཛོད་ལས་ལོགས་སུ་ཡིན་པའི་གསང་སྡོམ་གྱི་གཞི་གྲངས་མཛོད་ཞིག་བེད་སྤྱོད་གཏོང་གི་ཡོད། ཁྱེད་རང་གིས་ད་ཡོད་འཕྲིན་ཐུང་རྣམས་བདེ་འཇགས་འཕྲིན་ཐུང་གི་གསང་སྡོམ་གྱི་གཞི་གྲངས་མཛོད་དེའི་ཁོངས་སུ་བཤུ་ན་འདོད་དམ། ཁྱེད་རང་གི་གཏན་འཇགས་མ་ལག་གི་གཞི་གྲངས་མཛོད་དེ་ནུས་མེད་ཆགས་གི་མ་རེད།</string>
<string name="ApplicationMigrationManager_copy">འདྲ་བཤུས་བྱོས། </string>
<string name="ApplicationMigrationManager_dont_copy">འདྲ་བཤུས་མ་བྱེད། </string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">ད་ལྟ།:%s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">རྙེད་མ་སོང་། </string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">འགྲེལ་བརྗོད་བྱས་པའི་འབྲེལ་གཏུག་ནས་ཁུངས་ཐུབ་པའི་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་རྙེད།</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">ཁྱེད་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="ApplicationPreferenceActivity_you_have_not_yet_defined_a_contact_for_yourself">ཁྱེད་ཀྱིས་འབྲེལ་ལམ་བྱ་ཡུལ་གསལ་བཤད་བྱས་མི་འདུག སྒྲིག་བཀོད་འདེམ་ཐོ་ལས་བདམས་པར་ཞུ༏</string>
<string name="ApplicationPreferenceActivity_exported_to_contacts_database">འབྲེལ་མིང་ཆ་ཚང་གཞི་གྲངས་མཛོད་ནང་ཕྱི་འདན་བྱས།</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">དམིགས་སུ་བཀར་བའི་འབྲེལ་གཏུགས་ལས་ཁུངས་ངོས་འཛིན་ཅན་གྱི་ལྡེ་མིག་མི་རྙེད།</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">ཁྱེད་ལ་ངོས་འཛིན་ལྡེ་མིག་མི་འདུག </string>
<string name="ApplicationPreferenceActivity_you_have_not_yet_defined_a_contact_for_yourself">ཁྱེད་ཀྱིས་འབྲེལ་ལམ་བྱ་ཡུལ་གསལ་བཤད་བྱས་མི་འདུག སྒྲིག་བཀོད་འདེམ་ཐོ་ལས་འདེམས་པར་ཞུ༏</string>
<string name="ApplicationPreferenceActivity_exported_to_contacts_database">འབྲེལ་མིང་ཆ་ཚང་གཞི་གྲངས་མཛོད་ནང་ཕྱི་འདྲེན་བྱས། </string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">ཁྱོད་ཀྱིས་ལྡེ་མིག་ནང་འཇུག་མ་བྱས་སྔོན་ལ་གསང་གྲངས་འཇོག་དགོས།</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">ཁྱོད་ཀྱིས་ལྡེ་མིག་དོ་དམ་མ་བྱས་སྔོན་ལ་གསང་གྲངས་འཇོག་དགོས།</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">ཁྱོད་ཀྱིས་གསང་ཡིག་བཟོས་མི་འདུག</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">གླེང་མོལ་གྱི་རིང་ཐུང་གི་ཚད། </string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">གླེང་མོལ་རེའི་འཕྲིན་ཐུང་། </string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">ད་ལྟ་རང་འཕྲིན་ཐུང་རྙིང་པ་ཚང་མ་གསུབ་གི་ཡིན་ནམ། </string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">ཁྱེད་ཀྱིས་མྱུར་དུ་ཉེ་འཆར་གྱི་འཕྲིན་ཐུང་དེའི་སླད་དུ་གླེང་མོལ་གྱི་སྐུད་པ་ཚང་མ་གཙང་བཟོ་བྱེད་ཀྱི་ཡིན་ནམ། </string>
<string name="ApplicationPreferencesActivity_delete">གསུབ། </string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">འདྲ་པར།</string>
<string name="AttachmentTypeSelectorAdapter_video">བརྙན་པར།</string>
<string name="AttachmentTypeSelectorAdapter_audio">སྒྲ་ཟློས། </string>
<!--ConversationItem-->
<string name="ConversationItem_message_size_d_kb">འཕྲིན་ཐུང་ཆ་ཚད།: %d KB</string>
<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>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_warning">བརྒྱུད་ལམ་འདི་གཞི་གྲངས་མཛོད་ཀྱི་གསང་སྡོམ་ཁོངས་སུ་ཉར་ཡོད་། ཁྱེད་ནས་ SD card ནང་དུ་ཉར་ཚགས་བྱས་བའི་པར་གཞི་དེ་གསང་སྡོམ་བྱས་མི་འདུག ཁྱེད་ནས་མུ་མཐུད་ན་འདོད་དམ།</string>
<string name="ConversationItem_error_while_saving_attachment_to_sd_card">SD Card ནང་ཟུར་སྣོན་ཉར་ཚགས་བྱེད་སྐབས་ནོར་འཁྲུལ་བྱུང་།</string>
<string name="ConversationItem_success_exclamation">གྲུབ་འབྲས་ལེགས།</string>
<string name="ConversationItem_unable_to_write_to_sd_card_exclamation">SD Card ནང་འདྲ་བཤུས་བྱེད་མི་ཐུབ།</string>
<string name="ConversationItem_saving_attachment_to_sd_card">SD བྱང་བུའི་ནང་ཟུར་སྣོན་ཉར་ཚགས་བྱེད་བཞིན་ཡོད།</string>
<string name="ConversationItem_save_to_sd_card">SD བྱང་བུའི་ནང་ཉར་ཚགས་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_warning">བརྒྱུད་ལམ་འདི་གཞི་གྲངས་མཛོད་ཀྱི་གསང་སྡོམ་ཁོངས་སུ་ཉར་ཡོད་། ཁྱེད་ནས་ SD བྱང་བུའི་ནང་དུ་ཉར་ཚགས་བྱས་བའི་པར་གཞི་དེ་གསང་སྡོམ་བྱས་མི་འདུག ཁྱེད་ནས་མུ་མཐུད་ན་འདོད་དམ།</string>
<string name="ConversationItem_error_while_saving_attachment_to_sd_card">SD བྱང་བུའི་ནང་ཟུར་སྣོན་ཉར་ཚགས་བྱེད་སྐབས་ནོར་འཁྲུལ་བྱུང་།</string>
<string name="ConversationItem_success_exclamation">ལེགས་འགྲུབ། </string>
<string name="ConversationItem_unable_to_write_to_sd_card_exclamation">SD བྱང་བུའི་ནང་འབྲི་མི་ཐུབ། </string>
<string name="ConversationItem_view_secure_media_question">ཉེན་སྲུང་ཡོད་པའི་སྨྱན་གཟུགས་ལ་བལྟ།</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning">བརྒྱུད་ལམ་འདི་གཞི་གྲངས་མཛོད་ཀྱི་གསང་སྡོམ་ཁོངས་སུ་ཉར་ཡོད། Unfortunately, to view it with an external content viewer currently requires the data to be temporarily decrypted and written to disk. Are you sure that you would like to do this?</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning">བརྒྱུད་ལམ་འདི་གཞི་གྲངས་མཛོད་ཀྱི་གསང་སྡོམ་ཁོངས་སུ་ཉར་ཡོད། སྟབས་མི་ལེགས་པ་ཞིག་ལ། ཕྱི་རོལ་གྱི་པར་གཞི་ཞིག་དང་མཉམ་དུ་དེ་ལ་བལྟ་བ་ལ། གནས་སྐབས་རིང་གསང་སྡོམ་བྱས་མེད་པའི་གཞི་གྲངས་ཤིག་དགོས་ཀྱི་ཡོད། ཁྱེད་རང་གིས་དེ་ལྟར་བྱེད་ན་འདོད་དམ། </string>
<string name="ConversationItem_key_exchange_message">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན།</string>
<string name="ConversationItem_received_and_processed_key_exchange_message">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འབྱོར་བ་དང་ལས་སྣོན་བྱས།</string>
<string name="ConversationItem_error_received_stale_key_exchange_message">ནོར་འཁྲུལ། ལྡེ་མིག་འབྱོར་བ་དེ་རྙིང་པ་རེད་འདུག</string>
<string name="ConversationItem_received_key_exchange_message_click_to_process">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འབྱོར་བྱུང་། ལས་སྣོན་བྱ་རྒྱུའི་སྒང་གནོན།</string>
<string name="ConversationItem_received_key_exchange_message_click_to_process">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འབྱོར་བྱུང་། ལས་སྣོན་བྱ་རྒྱུའི་སྒང་བསྣུན། </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_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>
<string name="ConversationActivity_compose_message">འཕྲིན་ཐུང་ྲིས།</string>
<string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">དགོངས་དག ཁྱོད་ཀྱི་ཟུར་སྣོན་ལ་སྒྲིག་བཟོ་བྱེད་པར་ནོར་འཁྲུལ་ཞིག་འདུག</string>
<string name="ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions">དགོངས་དག ཁྱོད་ཀྱིས་བདམས་པའི་བརྙན་རིས་དེ་གཏན་ཁེལ་བྱས་ཡོད་པའི་ཚད་ལས་བརྒལ་འདུག</string>
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">ཁྱོད་ཀྱིས་བདམས་པའི་སྒྲ་ཟློས་དེ་གཏན་ཁེལ་བྱས་ཡོད་པའི་ཚད་ལས་བརྒལ་འདུག</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">གཏོང་ས་དེ་གློག་འཕྲིན་ཁ་བྱང་ངམ་འཕྲིན་ཐུང་ཁ་བྱང་ཚད་ལྡན་མི་འདུག </string>
<string name="ConversationActivity_message_is_empty_exclamation">འཕྲིན་ཐུང་མི་འདུག</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>
<string name="ConversationActivity_compose_message">འཕྲིན་ཐུང་ྲིས། </string>
<string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">དགོངས་དག ཁྱོད་ཀྱི་ཟུར་སྣོན་ལ་སྒྲིག་བཟོ་བྱེད་པར་ནོར་འཁྲུལ་ཞིག་འདུག </string>
<string name="ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions">དགོངས་དག ཁྱོད་ཀྱིས་བདམས་པའི་བརྙན་རིས་དེ་གཏན་ཁེལ་བྱས་ཡོད་པའི་འཕྲིན་ཐུང་གི་ཚད་ལས་བརྒལ་འདུག</string>
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">ཁྱོད་ཀྱིས་བདམས་པའི་སྒྲ་ཟློས་དེ་གཏན་ཁེལ་བྱས་ཡོད་པའི་འཕྲིན་ཐུང་གི་ཚད་ལས་བརྒལ་འདུག</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">གཏོང་ས་དེ་གློག་འཕྲིན་ཁ་བྱང་ངམ་འཕྲིན་ཐུང་ཁ་བྱང་ཚད་ལྡན་མི་འདུག </string>
<string name="ConversationActivity_message_is_empty_exclamation">འཕྲིན་ཐུང་སྟོང་པ་རེད་འདུག </string>
<string name="ConversationActivity_forward_message_prefix">FWD</string>
<string name="ConversationActivity_group_conversation_recipients">མཉམ་འདུས་གླེང་མོལ་གྱི་ཁོངས་མི་དག </string>
<string name="ConversationActivity_group_conversation">མཉམ་འདུས་གླེང་མོལ། </string>
<string name="ConversationActivity_d_recipients_in_group">མཉམ་འདུས་ཀྱི་ཁོངས་སུ་ལེན་མཁན་གྱི་བརྒྱ་ཆ %d ཙམ། </string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">འཕྲིན་ཐུང་ཞིབ་ཆ།</string>
<string name="ConversationFragment_message_details">འཕྲིན་ཐུང་གི་ཞིབ་ཆ།</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">གཏོང་མཁན།:%1$s དབོར་བ།:%2$s བཏང་།/འབྱོར།:%3$s</string>
<string name="ConversationFragment_confirm_message_delete">འཕྲིན་ཐུང་སུབ་ན་སྒྲིག་ཀྱི་རེད་དམ།</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">གཏོང་མཁན: %1$s⏎ སྐྱེལ་འདྲེན: %2$s⏎ བཏང་ཚར: %3$s⏎ འབྱོར་ཚར:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">འཕྲིན་ཐུང་གསུབ་པ་ངེས་བརྟན་བཟོས། </string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">ཁྱོད་ཀྱིས་དངོས་གནས་འཕྲིན་ཐུང་འདི་གཏན་དུ་གསུབ་རྒྱུ་ཡིན་ནམ།</string>
<!--ConversationListAdapter-->
<string name="ConversationListAdapter_encrypted_message_enter_passphrase">གསང་སྡོམ་བྱེད་པའི་འཕྲིན་ཐུང་རེད། གསང་ཡིག་འཇོག</string>
<string name="ConversationListAdapter_encrypted_message_enter_passphrase">གསང་སྡོམ་བྱ་པའི་འཕྲིན་ཐུང་རེད། གསང་ཡིག་ནང་འཇུག་བྱོས། </string>
<string name="ConversationListAdapter_key_exchange_message">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན།</string>
<!--ConversationListFragment-->
<string name="ConversationListFragment_delete_threads_question">འཕྲིན་ཐུང་སྐུད་རིམ་གསུབ་རྒྱུ་ཡིན་ནམ།</string>
@@ -85,33 +96,33 @@
<!--ConversationListItem-->
<string name="ConversationListItem_key_exchange_message">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན།</string>
<!--KeyScanningActivity-->
<string name="KeyScanningActivity_no_scanned_key_found_exclamation">ཞིབ་བརྟག་བྱེད་པའི་ལྡེ་མིག་རྙེད་མི་ཐུབ།</string>
<string name="KeyScanningActivity_no_scanned_key_found_exclamation">ཞིབ་བརྟག་བྱ་པའི་ལྡེ་མིག་རྙེད་མི་ཐུབ།</string>
<!--PassphraseChangeActivity-->
<string name="PassphraseChangeActivity_passphrases_dont_match_exclamation">གསང་ིག་མཚུངས་ཀྱི་མི་འདུག</string>
<string name="PassphraseChangeActivity_incorrect_old_passphrase_exclamation">གསང་ིག་རྙིང་པ་མ་དག་པ་དེ་འདིར་བྲིས།</string>
<string name="PassphraseChangeActivity_passphrases_dont_match_exclamation">གསང་ིག་མཚུངས་ཀྱི་མི་འདུག</string>
<string name="PassphraseChangeActivity_incorrect_old_passphrase_exclamation">གསང་ིག་རྙིང་པ་དེ་ནོར་འདུག </string>
<!--PassphraseCreateActivity-->
<string name="PassphraseCreateActivity_passphrases_dont_match_exclamation">གསང་ིག་མཚུངས་ཀྱི་མི་འདུག</string>
<string name="PassphraseCreateActivity_generating_keypair">ལྡེ་མིག་གི་ཆ་བསྐྲུན་གྱི་ཡོད།</string>
<string name="PassphraseCreateActivity_generating_a_local_encryption_keypair">རང་སའི་གསང་སྡོམ་གྱི་ལྡེ་མིག་ཆ་ཞིག་སྐྱེད་སྒྲུབ།</string>
<string name="PassphraseCreateActivity_passphrases_dont_match_exclamation">གསང་ིག་མཚུངས་ཀྱི་མི་འདུག</string>
<string name="PassphraseCreateActivity_generating_keypair">ལྡེ་མིག་གི་ཆ་ཞིག་བསྐྲུན་གྱི་ཡོད།</string>
<string name="PassphraseCreateActivity_generating_a_local_encryption_keypair">རང་སའི་གསང་སྡོམ་གྱི་ལྡེ་མིག་ཆ་ཞིག་སྐྲུན་བཞིན་ཡོད། </string>
<!--PassphrasePromptActivity-->
<string name="PassphrasePromptActivity_invalid_passphrase_exclamation">གསང་ཡིག་ཁུངས་ལྡན་མིན།</string>
<string name="PassphrasePromptActivity_invalid_passphrase_exclamation">གསང་ཡིག་ཁུངས་ལྡན་རེད་མི་འདུག </string>
<!--ReceiveKeyActivity-->
<string name="ReceiveKeyActivity_error_you_have_received_a_corrupted_public_key">ནོར་འཁྲུལ།: ཁྱེད་ལ་མི་གཞན་ནས་ངན་ལྷད་ཅན་གྱི་ལྡེ་མིག་ཞིག་འབྱོར་འདུག ལྡེ་མིག་འདིས་ལས་སྣོན་བྱེད་མི་ཐུབ། བདེ་འཇགས་ཀྱི་གཏམ་གླེང་བསྐྱར་དུ་འགོ་བརྩམ་རོགས།</string>
<string name="ReceiveKeyActivity_error_you_have_received_a_public_key_from_an_unsupported_version_of_the_protocol">ནོར་འཁྲུལ།: ཁྱེད་ལ་པར་གཞི་གྲོས་མཐུན་གྱིས་རྒྱབ་སྐྱོར་མ་བྱས་བའི་སྤྱི་སྤྱོད་ལྡེ་མིག་ཞིག་འབྱོར་གི་རེད། ལྡེ་མིག་དེས་ལས་ཀ་བྱེད་ཐུབ་གི་མ་རེད། སྐུ་མཁྱེན། ཉེན་སྲུང་གི་སྡེ་ཚན་ཞིག་བསྐྱར་དུ་འགོ་ཚུགས། </string>
<string name="ReceiveKeyActivity_error_you_have_received_a_corrupted_public_key">ནོར་འཁྲུལ།: ཁྱེད་ལ་མི་གཞན་ནས་ངན་ལྷད་ཅན་གྱི་ལྡེ་མིག་ཞིག་འབྱོར་འདུག ལྡེ་མིག་འདིས་ལས་སྣོན་བྱེད་མི་ཐུབ། བདེ་འཇགས་ཀྱི་གཏམ་གླེང་བསྐྱར་དུ་འགོ་འཛུགས་རོགས། </string>
<string name="ReceiveKeyActivity_error_you_have_received_a_public_key_from_an_unsupported_version_of_the_protocol">ནོར་འཁྲུལ།: ཁྱེད་ལ་པར་གཞི་གྲོས་མཐུན་གྱིས་རྒྱབ་སྐྱོར་མ་བྱས་བའི་སྤྱི་སྤྱོད་ལྡེ་མིག་ཞིག་འབྱོར་འདུག ལྡེ་མིག་དེས་ལས་ཀ་བྱེད་ཐུབ་གི་མ་རེད། སྐུ་མཁྱེན། ཉེན་སྲུང་གི་སྡེ་ཚན་ཞིག་བསྐྱར་དུ་འགོ་ཚུགས། </string>
<string name="ReceiveKeyActivity_this_key_exchange_message_does_not_include_an_identity_signature">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འདིའི་ནང་ངོས་འཛིན་མིང་རྟགས་མེད།</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_but_you_do_not_yet_trust_it">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འདིའི་ནང་ངོས་འཛིན་མིང་རྟགས་ཡོད། ཡིན་ནའང་ད་ལྟ་ཁྱད་ཀྱི་དེར་བློ་འགེལ་མེད།</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_which_you_trust_for_s">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འདིའི་ནང་ངོས་འཛིན་མིང་རྟགས་ཡོད་པ་དེ་རྒྱུ་མཚན་གང་གི་དོན་དུ་བློ་འགེལ་ཡོད། : %s</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_sent_to_start_your_current_encrypted_session_with_s">%s དང་ལྷན་དུ་འགོ་འཛུགས་པའི་བདེ་འཇགས་ཀྱི་གཏམ་གླེང་བྱེད་བཞིན་པ་དེའི་ལྡེ་མིག་འདི་རེད།</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_received_to_start_your_current_encrypted_session_with_s">%s དང་ལྷན་དུ་འགོ་འཛུགས་པའི་བདེ་འཇགས་ཀྱི་གཏམ་གླེང་བྱེད་བཞིན་པ་དེའི་ལྡེ་མིག་འབྱོར་བ་འདི་རེད།</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_warning_you_already_have_an_encrypted_session">ཁྱེད་ལ་(%s.⏎ ⏎ )ནས་ཕན་ཚུན་བརྗེ་ལེན་གྱི་གསང་བའི་འཕྲིན་ཐུང་ཞིག་འབྱོར་འདུག \nཉེན་བརྡ།: འབྲེལ་བ་འདི་དང་མཉམ་དུ་ཁྱོད་ལ་གསང་རྒྱ་ཅན་གྱི་སྡེ་ཚན་ཞིག་འདུག གལ་སྲིད་ཁྱོད་ནས་ཕན་ཚུན་བརྗེ་ལེན་གྱི་གསང་བའི་འཕྲིན་ཐུང་འདི་ངོས་ལེན་བྱས་ཚེ། དེས་ཁྱེད་ཀྱི་ད་ཡོད་སྡེ་ཚན་དེ་གཏོར་བཤིག་གཏོང་བ་མ་ཟད་ཁྱེད་ནས་ཡང་བསྐྱར་ཁུངས་སྐྱེལ་བྱེད་དགོས། ཁྱེད་ནས་གསང་བའི་བརྗེ་ལེན་འདི་ལེགས་འགྲུབ་བྱེད་འདོད་དམ། </string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_previously_initiated">%s ནས་ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འབྱོར་འདུག ཁྱེད་ཀྱི་འབྲེལ་མིང་འདིའི་ལྷན་དུ་གཏམ་གླེང་འགོ་འཛུགས་འདུག ལྡེ་མིག་འདི་ལེན་མ་ཐག་ཏུ་ལྡེ་མིག་བརྗེ་ལེན་གྱི་རིམ་པ་ཆ་ཚང་བ་ཡིན། ཁྱེད་རང་ནས་ལྡེ་མིག་འདིའི་བརྗེ་བསྒྱུར་ལེགས་འགྲུབ་བྱེད་འདོད་འདུག་གམ།</string>
<string name="ReceiveKeyActivity_you_have_initiated_a_key_exchange_message_with_s_but_have_not_yet_received_a_reply">ཁྱོད་ཀྱི་ %d ལྡེ་མིག་བརྗེ་ལེན་བྱ་རྒྱུའི་བརྡ་ལན་བཏང་ཟིན་འདུག ཡིན་ནའང་དེར་ད་ལྟ་ཡ་ལན་མི་འདུག</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_no_existing_session">%s ནས་ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འབྱོར་འདུག You have no existing session with this contact, ཁྱེད་རང་ནས་ལྡེ་མིག་འདིའི་བརྗེ་བསྒྱུར་ལེགས་འགྲུབ་བྱེད་འདོད་འདུག་གམ།</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_but_you_do_not_yet_trust_it">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འདིའི་ནང་ངོས་འཛིན་མིང་རྟགས་ཡོད། ཡིན་ནའང་ཁྱད་ཀྱི་དེ་ལ་ད་དུང་ཡང་ཡིད་ཆེས་བྱེད་ཀྱི་མི་འདུག </string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_which_you_trust_for_s">ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འདིའི་ནང་ངོས་འཛིན་མིང་རྟགས་ཡོད་པ་དེར་ཁྱེད་ཀྱིས་ཡིད་ཆེས། : %s</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_sent_to_start_your_current_encrypted_session_with_s">%s དང་ལྷན་དུ་། འདི་ནི་ཁྱེད་ཀྱིས་ད་ཡོད་གསང་སྡོམ་སྡེ་ཚན་དེ་འགོ་འཛུགས་པར་བཏང་བའི་ལྡེ་མིག་དེ་རེད། </string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_received_to_start_your_current_encrypted_session_with_s">%s དང་ལྷན་དུ། འདི་ནི་ད་ཡོད་གསང་སྡོམ་སྡེ་ཚན་དེ་འགོ་འཛུགས་པར་ཁྱེད་ལ་འབྱོར་བའི་ལྡེ་མིག་དེ་རེད། </string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_warning_you_already_have_an_encrypted_session">(%s.⏎ ⏎ )ནས་ཁྱེད་ལ་ཕན་ཚུན་བརྗེ་ལེན་གྱི་གསང་བའི་འཕྲིན་ཐུང་ཞིག་འབྱོར་འདུག \nཉེན་བརྡ།: འབྲེལ་བ་འདི་དང་མཉམ་དུ་ཁྱོད་ལ་གསང་རྒྱ་ཅན་གྱི་སྡེ་ཚན་ཞིག་འདུག གལ་སྲིད་ཁྱོད་ནས་ཕན་ཚུན་བརྗེ་ལེན་གྱི་གསང་བའི་འཕྲིན་ཐུང་འདི་ངོས་ལེན་བྱས་ཚེ། དེས་ཁྱེད་ཀྱི་ད་ཡོད་སྡེ་ཚན་དེ་གཏོར་བཤིག་གཏོང་བ་མ་ཟད་ཁྱེད་ནས་ཡང་བསྐྱར་ཁུངས་སྐྱེལ་བྱེད་དགོས། ཁྱེད་ནས་གསང་བའི་བརྗེ་ལེན་འདི་ལེགས་འགྲུབ་བྱེད་འདོད་དམ། </string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_previously_initiated">%s ནས་ཁྱེད་ལ་ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འབྱོར་འདུག ཁྱེད་ཀྱི་འབྲེལ་མིང་འདིའི་ལྷན་དུ་གཏམ་གླེང་འགོ་འཛུགས་འདུག་པ་དང་ལྡེ་མིག་འདི་ངོས་ལེན་བརྒྱུད་ཁྱེད་ཀྱིས་ལྡེ་མིག་བརྗེ་ལེན་གྱི་རིམ་པ་འདི་ལེགས་འགྲུབ་བྱེད་ཀྱི་ཡོད། ཁྱེད་རང་ནས་ལྡེ་མིག་འདིའི་བརྗེ་བསྒྱུར་ལེགས་འགྲུབ་བྱེད་འདོད་འདུག་གམ།</string>
<string name="ReceiveKeyActivity_you_have_initiated_a_key_exchange_message_with_s_but_have_not_yet_received_a_reply">%s དང་མཉམ་དུ། ཁྱོད་ཀྱི་ལྡེ་མིག་བརྗེ་ལེན་བྱ་རྒྱུའི་བརྡ་ལན་འགོ་བཙུགས་འདུག ཡིན་ནའང་། དེ་ལ་་དེར་ཡ་ལན་འབྱོར་མི་འདུག </string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_no_existing_session">%s ནས། ཁྱེད་ལ་ལྡེ་མིག་བརྗེ་ལེན་བྱེད་རྒྱུའི་བརྡ་ལན་འབྱོར་འདུག ཁྱེད་ལ་འབྲེལ་བ་འདིའི་མཉམ་དུ་སོར་གནས་སྡེ་ཚན་མི་འདུག ཁྱེད་རང་ནས་ལྡེ་མིག་འདིའི་བརྗེ་བསྒྱུར་ཆ་ཚང་བར་བྱེད་འདོད་མ། </string>
<!--ReviewIdentitiesActivity-->
<string name="ReviewIdentitiesActivity_unable_to_view_corrupted_identity_key_exclamation">ངན་ལྷད་ཅན་གྱི་ངོས་འཛིན་ལྡེ་མིག་དེར་བལྟ་མི་ཐུབ།</string>
<string name="ReviewIdentitiesActivity_delete_identity">ངོས་འཛིན་ལྡེ་མིག་གསུབ་རྒྱུ་ཡིན་ནམ།</string>
<string name="ReviewIdentitiesActivity_delete_identity_are_you_sure_you_want_to_delete_this_identity_key">ཁྱེད་ཀྱིས་དངོས་གནས་ངོས་འཛིན་ལྡེ་མིག་དེ་གཏན་དུ་གསུབ་རྒྱུ་ཡིན་ནམ།</string>
<string name="ReviewIdentitiesActivity_invalid_identity">ངོ་སྤྲོད་ཁུངས་ལྡན་མིན</string>
<string name="ReviewIdentitiesActivity_unable_to_view_corrupted_identity_key_exclamation">ངན་ལྷད་ཅན་གྱི་ངོས་འཛིན་ལྡེ་མིག་དེར་བལྟ་ཐུབ་གི་མི་འདུག </string>
<string name="ReviewIdentitiesActivity_delete_identity">ངོ་རྟགས་དེ་གསུབ་རྒྱུ་ཡིན་ནམ།</string>
<string name="ReviewIdentitiesActivity_delete_identity_are_you_sure_you_want_to_delete_this_identity_key">ཁྱེད་ཀྱིས་དངོས་གནས་ངོ་རྟགས་ལྡེ་མིག་དེ་གཏན་དུ་གསུབ་རྒྱུ་ཡིན་ནམ།</string>
<string name="ReviewIdentitiesActivity_invalid_identity">ངོ་རྟགས་ཁུངས་ལྡན། </string>
<!--SaveIdentityActivity-->
<string name="SaveIdentityActivity_you_must_specify_a_name_for_this_identity_exclamation">ངོ་རྟགས་འདི་ལ་མིང་དམིགས་བསལ་བ་ཞིག་ཐོགས། </string>
<string name="SaveIdentityActivity_identity_name_exists_exclamation">ངོ་རྟགས་གི་མིང་འདུག </string>
@@ -120,43 +131,43 @@
<!--VerifyIdentityActivity-->
<string name="VerifyIdentityActivity_mark_identity_verified_question">ངོ་རྟགས་བདེན་དབང་གི་རྟགས་བཀོད་དམ། </string>
<string name="VerifyIdentityActivity_are_you_sure_you_have_validated_the_recipients_identity_fingerprint_and_would_like_to_mark_it_as_verified">ཁྱེད་ལ་ཁུངས་ཐུབ་ཀྱི་ངོ་རྟགས་དེའི་མཛུབ་ཐེལ་ཡོད་པ་མ་ཟད་དེ་ལ་ཁུངས་ཐུབ་ཀྱི་རྟགས་རྒྱག་ཐུབ་བམ། </string>
<string name="VerifyIdentityActivity_mark_verified">ཁུངས་ཐུབ་ཀྱི་རྟགས་བཀོད། </string>
<string name="VerifyIdentityActivity_mark_verified">ཁུངས་ཐུབ་ཡིན་པའི་རྟགས་བཀོད། </string>
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">ཁྱེད་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">སྦྱོར་ཡུལ་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key_exclamation">སྦྱོར་ཡུལ་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="VerifyIdentityActivity_scan_their_key_to_compare">ཞན་གྱི་ལྡེ་མིག་འགྲན་ཆེད་ཞིབ་བརྟག་བྱེད།</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">འབྱོར་ཡུལ་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key_exclamation">འབྱོར་ཡུལ་ལ་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="VerifyIdentityActivity_scan_their_key_to_compare">ཤིབ་བསྡུར་ཆེད་ཁོང་ཚོའི་ལྡེ་མིག་ལ་བརྟག་ཞིབ་བྱོས། </string>
<string name="VerifyIdentityActivity_get_my_key_scanned">ངའི་ལྡེ་མིག་ཞིབ་བརྟག་བྱོས།</string>
<string name="VerifyIdentityActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully">ཉེན་བརྡ། ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མི་མཚུངས མཛུབ་རིས་ཡི་གེ་དེ་གཟབ་ནན་ངང་ཞིབ་བཤེར་བྱོས།</string>
<string name="VerifyIdentityActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully">ཉེན་བརྡ། ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མཚུངས་གི་མི་འདུག མཛུབ་རིས་ཡི་གེ་དེ་གཟབ་ནན་ངང་ཞིབ་བཤེར་བྱོས།</string>
<string name="VerifyIdentityActivity_not_verified_exclamation">ར་སྤྲོད་བྱས་མི་འདུག</string>
<string name="VerifyIdentityActivity_their_key_is_correct_it_is_also_necessary_to_verify_your_key_with_them_as_well">གཞན་གྱི་ལྡེ་མིག་ཏག་ཏག་རེད། ཁྱེད་ཀྱི་ལྡེ་མིག་གཞན་གྱིས་ར་སྤྲོད་བྱེད་དགོས་ངེས</string>
<string name="VerifyIdentityActivity_their_key_is_correct_it_is_also_necessary_to_verify_your_key_with_them_as_well">གཞན་གྱི་ལྡེ་མིག་འགྲིག་འདུག ཁྱེད་ཀྱི་ལྡེ་མིག་གཞན་དང་མཉམ་དུ་ར་སྤྲོད་བྱེད་དགོས་ངེས་རེད། </string>
<string name="VerifyIdentityActivity_verified_exclamation">ར་སྤྲོད་བྱས་ཟིན།</string>
<string name="VerifyIdentityActivity_you_don_t_have_an_identity_key_exclamation">ཁྱེད་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="VerifyIdentityActivity_you_don_t_have_an_identity_key_exclamation">ཁྱེད་ལ་ངོ་རྟགས་ལྡེ་མིག་མི་འདུག</string>
<!--VerifyImportedIdentityActivity-->
<string name="VerifyImportedIdentityActivity_you_must_specify_a_name_for_this_contact_exclamation">འབྲེལ་བ་བྱ་ཡུལ་དེའི་མིང་ངེས་པར་དུ་ཁ་གསལ་སྟོན་དགོས།</string>
<string name="VerifyImportedIdentityActivity_save_identity_key_question">ངོས་འཛིན་ལྡེ་མིག་དེ་ཉར་ཚགས་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="VerifyImportedIdentityActivity_error_saving_identity_key_exclamation">ངོས་འཛིན་ལྡེ་མིག་ཉར་ཚགས་བྱེད་སྐབས་ནོར་འཁྲུལ་བྱུང་།</string>
<string name="VerifyImportedIdentityActivity_this_identity_key_or_an_identity_key_with_the_same_name_already_exists_please_edit_your_key_database">ངོས་འཛིན་ལྡེ་མིག་འདི་འམ། ཡང་ན་མིང་མཚུངས་ཅན་གྱི་ལྡེ་མིག་ཞིག་སྔོན་ནས་འདུག ལྡེ་མིག་གི་གཞི་གྲངས་དེ་རྩོམ་སྒྲིག་བྱེད།</string>
<string name="VerifyImportedIdentityActivity_scan_to_compare">འགྲན་ཆེད་ཞིབ་བརྟག་བྱེད།</string>
<string name="VerifyImportedIdentityActivity_get_scanned_to_compare">ལྡེ་མིག་འགྲན་ཆེད་ཞིབ་བརྟག་བྱེད།</string>
<string name="VerifyImportedIdentityActivity_save_identity_key_question">ངོ་རྟགས་ལྡེ་མིག་དེ་ཉར་ཚགས་བྱེད་རྒྱུ་ཡིན་ནམ།</string>
<string name="VerifyImportedIdentityActivity_error_saving_identity_key_exclamation">ངོ་རྟགས་ལྡེ་མིག་ཉར་ཚགས་བྱེད་སྐབས་ནོར་འཁྲུལ་བྱུང་།</string>
<string name="VerifyImportedIdentityActivity_this_identity_key_or_an_identity_key_with_the_same_name_already_exists_please_edit_your_key_database">ངོས་འཛིན་ལྡེ་མིག་འདི་འམ། ཡང་ན་མིང་མཚུངས་ཅན་གྱི་ལྡེ་མིག་ཞིག་སྔོན་ནས་འདུག ལྡེ་མིག་གི་གཞི་གྲངས་དེ་རྩོམ་སྒྲིག་བྱོས། </string>
<string name="VerifyImportedIdentityActivity_scan_to_compare">གཤིབ་སྡུར་ཆེད་ཞིབ་བརྟག་བྱོས། </string>
<string name="VerifyImportedIdentityActivity_get_scanned_to_compare">གཤིབ་སྡུར་སླད་ཞིབ་བརྟག་ཚར་བ་བྱོས། </string>
<string name="VerifyImportedIdentityActivity_not_verified_exclamation">ར་སྤྲོད་བྱས་མི་འདུག</string>
<string name="VerifyImportedIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">ཉེན་བརྡ། ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མི་མཚུངས</string>
<string name="VerifyImportedIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">ཉེན་བརྡ། ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མཚུངས་གི་མི་འདུག </string>
<string name="VerifyImportedIdentityActivity_the_scanned_key_matches_exclamation">ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མཚུངས།</string>
<string name="VerifyImportedIdentityActivity_verified_exclamation">ར་སྤྲོད་བྱས་ཟིན།</string>
<string name="VerifyImportedIdentityActivity_are_you_sure_that_you_would_like_to_mark_this_as_a_valid_identity_key_for_all_future_correspondence_with_s">ཁྱེད་ཀྱིས་འབྱུང་འགྱུར་གྱི་ཡིག་འབྲེལ་ཚང་མའི་མཉམ་དུ། ཁུངས་ཐུབ་ཀྱི་ངོ་རྟགས་འདིའི་ལྡེ་མིག་ལ་རྟགས་རྒྱག་འདོད་ཡོད་དམ། སྤྱིར་བཏང་ནས་ཁྱེད་ཀྱིས་མཛུབ་ཐེལ་ཁུངས་སྐྱེལ་བྱས་ཡོད་ན། ད་གཟོད་འདི་འདི་ལྟར་བྱེད་དགོས་རེད། </string>
<string name="VerifyImportedIdentityActivity_are_you_sure_that_you_would_like_to_mark_this_as_a_valid_identity_key_for_all_future_correspondence_with_s">ཁྱེད་ཀྱིས་འབྱུང་འགྱུར་གྱི་ཡིག་འབྲེལ་ཚང་མའི་མཉམ་དུ། ཁུངས་ཐུབ་ཀྱི་ངོ་རྟགས་ %s འདིའི་ལྡེ་མིག་ལ་རྟགས་རྒྱག་འདོད་ཡོད་དམ། སྤྱིར་བཏང་ནས་ཁྱེད་ཀྱིས་མཛུབ་ཐེལ་ཁུངས་སྐྱེལ་བྱས་ཡོད་ན། ད་གཟོད་འདི་ལྟར་བྱེད་དགོས་རེད། </string>
<!--VerifyKeysActivity-->
<string name="VerifyKeysActivity_mark_session_verified_question">ཁུངས་ཐུབ་ཀྱི་སྡེ་ཚན་ལ་རྟགས་བཀོད། </string>
<string name="VerifyKeysActivity_mark_session_verified_question">ཁུངས་ཐུབ་ཀྱི་སྡེ་ཚན་ལ་རྟགས་འཁོད། </string>
<string name="VerifyKeysActivity_are_you_sure_that_you_have_validated_these_fingerprints_and_would_like_to_mark_this_session_as_verified">ཁྱེད་ཀྱིས་མཛུབ་ཐེལ་འདི་ཚོ་ཁུངས་སྐྱེལ་བྱས་ཡོད་པ་མ་ཟད་སྡེ་ཚན་འདི་ལ་བདེན་དཔང་ཡིན་པའི་རྟགས་རྒྱག་གི་ཡིན་ནམ། </string>
<string name="VerifyKeysActivity_mark_verified">རྒྱབ་གཉེར་གྱི་རྟགས་བཀོད། </string>
<string name="VerifyKeysActivity_get_my_fingerprint_scanned">ངའི་མཛུབ་རིས་ཞིབ་བརྟག་བྱེད།</string>
<string name="VerifyKeysActivity_scan_their_fingerprint">གཞན་གྱི་མཛུབ་རིས་ཞིབ་བརྟག་བྱེད།</string>
<string name="VerifyKeysActivity_mark_verified">རྒྱབ་གཉེར་གྱི་རྟགས་འཁོད། </string>
<string name="VerifyKeysActivity_get_my_fingerprint_scanned">ངའི་མཛུབ་རིས་ཞིབ་བརྟག་བྱོས། </string>
<string name="VerifyKeysActivity_scan_their_fingerprint">ཁོང་ཚོའི་མཛུབ་རིས་ཞིབ་བརྟག་བྱོས། </string>
<string name="VerifyKeysActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully2">ཉེན་བརྡ། ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མི་མཚུངས། མཛུབ་རིས་ཡི་གེ་དེ་གཟབ་ནན་ངང་ཞིབ་བཤེར་བྱོས།</string>
<string name="VerifyKeysActivity_not_verified_exclamation">ར་སྤྲོད་བྱས་མི་འདུག</string>
<string name="VerifyKeysActivity_their_key_is_correct_it_is_also_necessary_to_get_your_fingerprint_scanned_as_well">གཞན་གྱི་ལྡེ་མིག་ཏག་ཏག་རེད། ཁྱེད་ཀྱི་མཛུབ་རིས་ཞིབ་བརྟག་བྱེད་དགོས་ངེས།</string>
<string name="VerifyKeysActivity_their_key_is_correct_it_is_also_necessary_to_get_your_fingerprint_scanned_as_well">ཁོང་ཚོའི་ལྡེ་མིག་ཏག་ཏག་རེད། ཁྱེད་ཀྱི་མཛུབ་རིས་ཞིབ་བརྟག་བྱེད་དགོས་ངེས།</string>
<string name="VerifyKeysActivity_verified_exclamation">ར་སྤྲོད་བྱས་ཟིན།</string>
<!--ViewIdentityActivity-->
<string name="ViewIdentityActivity_you_do_not_have_an_identity_key">ཁྱེད་ལ་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་མི་འདུག</string>
<string name="ViewIdentityActivity_you_do_not_have_an_identity_key">ཁྱེད་ལ་ངོ་རྟགས་ལྡེ་མིག་མི་འདུག</string>
<string name="ViewIdentityActivity_scan_to_compare">འགྲན་ཆེད་ཞིབ་བརྟག་བྱེད།</string>
<string name="ViewIdentityActivity_get_scanned_to_compare">ལྡེ་མིག་འགྲན་ཆེད་ཞིབ་བརྟག་བྱེད།</string>
<string name="ViewIdentityActivity_get_scanned_to_compare">གཤིབ་སྡུར་ཆེད་ཞིབ་བརྟག་བྱོས། </string>
<string name="ViewIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">ཉེན་བརྡ། ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མི་མཚུངས།</string>
<string name="ViewIdentityActivity_not_verified_exclamation">ར་སྤྲོད་བྱས་མི་འདུག</string>
<string name="ViewIdentityActivity_the_scanned_key_matches_exclamation">ཞིབ་བརྟག་བྱས་པའི་ལྡེ་མིག་དེ་མཚུངས།</string>
@@ -164,52 +175,63 @@
<!--KeyExchangeInitiator-->
<string name="KeyExchangeInitiator_initiate_despite_existing_request_question">ད་ཡོད་ཀྱི་རེ་སྐུལ་ལ་མ་ལྟོས་པར་འགོ་རྩོམ། </string>
<string name="KeyExchangeInitiator_youve_already_sent_a_session_initiation_request_to_this_recipient_are_you_sure">ཁྱེད་ཀྱིས་གཏོང་ས་དེར་རེ་སྐུལ་གྱི་སྡེ་ཚན་ཞིག་བཏང་ཚར་འདུག ཁྱེད་ཀྱིས་ཡང་བསྐྱར་གཅིག་གཏོང་འདོད་འདུག་གམ འདི་ཡིས་ཁྱེད་ཀྱི་རེ་སྐུལ་དང་པོ་དེ་རྩིས་མེད་གཏོང་གི་རེད། </string>
<string name="KeyExchangeInitiator_send">གཏོང་།</string>
<string name="KeyExchangeInitiator_send">ཐོངས། </string>
<!--MessageDisplayHelper-->
<string name="MessageDisplayHelper_bad_encrypted_message">གསང་སྡོམ་འཕྲིན་ཐུང་ཕན་མེད་ཞིག </string>
<string name="MessageDisplayHelper_decrypting_please_wait">གསང་སྡོམ་བཤིག་བཞིན་ཡོད། ཅུང་སྒུག་དང་།</string>
<string name="MessageDisplayHelper_message_encrypted_for_non_existing_session">རྙེད་རྒྱུ་མེད་པའི་སྡེ་ཚན་ཞིག་གི་ཆེད་དུ་འཕྲིན་ཐུང་གསང་སྡོམ་བྱས་ཡོད། </string>
<string name="MessageDisplayHelper_decryption_error_local_message_corrupted_mac_doesn_t_match_potential_tampering_question">གསང་གྲོལ་ནོར་སྐྱོན</string>
<string name="MessageDisplayHelper_decryption_error_local_message_corrupted_mac_doesn_t_match_potential_tampering_question">གསང་གྲོལ་ནོར་འཁྲུལ། གཞི་རིམ་འཕྲིན་ཐུང་འཕྲོ་བརླགས་བཏང་འདུག MAC མཚུངས་ཀྱི་མི་འདུག ཡར་རྒྱས་འདུག་གམ</string>
<!--MmsDatabase-->
<string name="MmsDatabase_connecting_to_mms_server">MMS ཞབས་ཞུའི་ཡོ་ཆས་ལ་མཐུད་བཞིན་པ། </string>
<string name="MmsDatabase_downloading_mms">MMS ཕབ་ལེན་བྱེད་བཞིན་ཡོད།</string>
<string name="MmsDatabase_mms_download_failed">MMS ཕབ་ལེན་བྱེད་མི་ཐུབ།</string>
<string name="MmsDatabase_mms_download_failed">MMS ཕབ་ལེན་བྱེད་ཐུབ་མ་སོང་། </string>
<string name="MmsDatabase_downloading">ཕབ་ལེན་བྱེད་བཞིན་པ། </string>
<string name="MmsDatabase_anonymous">མིང་མེད། </string>
<!--MmsMessageRecord-->
<string name="MmsMessageRecord_decrypting_mms_please_wait">MMS གསང་གྲོལ་བྱེད་བཞིན་ཡོད། ཅུང་སྒུག་རོགས། </string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">མཁོ་མེད་གསང་སྡོམ་ཅན་གྱི་MMSའཕྲིན་ཐུང་། </string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">སྡེ་ཚན་མི་མངོན་པ་དེའི་ཆེད་དུ་། MMS འཕྲིན་ཐུང་གསང་སྡོམ་བྱས་འདུག </string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">གནས་སྤོས།</string>
<string name="ApplicationMigrationService_migrating">གནས་སྤོར་བཞིན་ཡོད། </string>
<string name="ApplicationMigrationService_migrating_system_text_messages">འཕྲིན་ཐུང་མ་ལག་སྤོར་བཞིན་པ། </string>
<!--KeyCachingService-->
<string name="KeyCachingService_textsecure_passphrase_cached">ཡི་གེའི་ཉེན་སྲུང་གི་གསང་ཚིག་དེ་སྦས།</string>
<string name="KeyCachingService_passphrase_cached">གསང་ཚིག་དེ་སྦས།</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) འཕྲིན་ཐུང་གསར་པ་རྣམས།</string>
<string name="MessageNotifier_most_recent_from_s">%: ནས་ཉེ་ཆར་གྱི་དེ། </string>
<string name="MessageNotifier_most_recent_from_s">%s: ནས་ཉེ་ཆར་གྱི་དེ། </string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">འཕྲིན་ཐུང་བདེ་འཇགས་ཀྱི་བདེ་འཇགས་གཏམ་གླེང་ལ་རྒྱབ་སྐྱོར་བྱེད་མཁན་ཞིག་ནས་ཁྱེད་ལ་འཕྲིན་ཐུང་ཅིག་འབྱོར་འདུག ཁོང་གི་ལྷན་དུ་འབྲེལ་བ་བན་པོ་ཡོང་ཆེད་ལྡེ་མིག་བརྗེ་ལེན་རྒྱུ་ཡིན་ནམ།</string>
<string name="auto_initiate_activity__initiate_exchange">བརྗེ་ལེན་འགོ་བཙུགས།</string>
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">འཕྲིན་ཐུང་བདེ་འཇགས་ཀྱི་བདེ་འཇགས་གཏམ་གླེང་ལ་རྒྱབ་སྐྱོར་བྱེད་མཁན་ཞིག་ནས་ཁྱེད་ལ་འཕྲིན་ཐུང་ཅིག་འབྱོར་འདུག ཁོང་གི་ལྷན་དུ་འབྲེལ་བ་བརྟན་པོ་ཡོང་ཆེད་ལྡེ་མིག་བརྗེ་ལེན་རྒྱུ་ཡིན་ནམ།</string>
<string name="auto_initiate_activity__initiate_exchange">བརྗེ་ལེན་འགོ་ུགས། </string>
<!--change_passphrase_activity-->
<string name="change_passphrase_activity__old_passphrase">གསང་ཚིག་རྙིང་པ།</string>
<string name="change_passphrase_activity__new_passphrase">གསང་ིག་གསར་པ།</string>
<string name="change_passphrase_activity__repeat_new_passphrase">གསང་ིག་གསར་པ་བསྐྱར་དུ་ྲིས།</string>
<string name="change_passphrase_activity__new_passphrase">གསང་ིག་གསར་པ།</string>
<string name="change_passphrase_activity__repeat_new_passphrase">གསང་ིག་གསར་པ་བསྐྱར་དུ་ྲིས། </string>
<!--contact_selection_group_activity-->
<!--contact_selection_list_activity-->
<string name="contact_selection_group_activity__no_contacts">འབྲེལ་མིང་མི་འདུག</string>
<!--ContactSelectionListFragment-->
<string name="ContactSelectionlistFragment_select_for">ཆེད་དུ་འདེམས།</string>
<!--contact_selection_recent_activity-->
<string name="contact_selection_recent_activity__no_recent_calls">ཉེ་ཆར་ཁ་པར་གཏོང་བ་མི་འདུག</string>
<string name="contact_selection_recent_activity__no_recent_calls">ཉེ་ཆར་ཁ་པར་བཏང་བ་མི་འདུག </string>
<!--conversation_activity-->
<string name="conversation_activity__type_message">འཕྲིན་ཐུང་ྲིས།</string>
<string name="conversation_activity__send">གཏོང་།</string>
<string name="conversation_activity__type_message">འཕྲིན་ཐུང་ྲིས། </string>
<string name="conversation_activity__send">ཐོངས། </string>
<string name="conversation_activity__remove">ཕྱིར་ཕུད། </string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">ཕབ་ལེན་བྱོས། </string>
<string name="conversation_item_sent__downloading">ཕབ་ལེན་བྱེད་བཞིན་ཡོད། </string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">ཕབ་ལེན་བྱོས། </string>
<string name="conversation_item_received__downloading">ཕབ་ལེན་བྱེད་བཞིན་ཡོད། </string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">སྡེ་ཚན་སྒྲིགས། </string>
<!--create_passphrase_activity-->
<string name="create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase">རྒྱུན་ལྡན་ནས་ཁྱེད་ཀྱིས་གསང་སྡོམ་རེའུ་མིག་ལ་བེད་སྤྱོད་པའི་གསང་ཚིག་དེ་དང་། དེ་གསང་ཚིག་བརྟན་པོ་ཆགས་གི་རེད། </string>
<string name="create_passphrase_activity__passphrase">གསང་ིག :</string>
<string name="create_passphrase_activity__repeat">བསྐྱར་དུ། :</string>
<string name="create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase">རྒྱུན་ལྡན་ནས་ཁྱེད་ཀྱིས་གསང་སྡོམ་རེའུ་མིག་ལ་བེད་སྤྱོད་པའི་གསང་ཚིག་དེ་འདེམས་དང་། དེ་གསང་ཚིག་བརྟན་པོ་ཆགས་གི་རེད། </string>
<string name="create_passphrase_activity__passphrase">གསང་ིག :</string>
<string name="create_passphrase_activity__repeat">བསྐྱར་དུ་སྤེལ། :</string>
<!--receive_key_activity-->
<string name="receive_key_activity__session">གཏམ་གླེང་།</string>
<string name="receive_key_activity__identities">ངོ་རྟགས། </string>
@@ -221,42 +243,44 @@
<!--save_identity_activity-->
<string name="save_identity_activity__identity_name">ངོ་རྟགས་ཀྱི་མིང་། </string>
<!--verify_identity_activity-->
<string name="verify_identity_activity__their_identity_they_read">གཞན་གྱི་ངོས་འཛིན།:\\n(གཞན་གྱི་ཀློག)</string>
<string name="verify_identity_activity__your_identity_you_read">ཁྱེད་ཀྱི་ངོས་འཛིན།:\\n༼ཁྱེད་ཀྱི་ཀློག</string>
<string name="verify_identity_activity__their_identity_they_read">གཞན་གྱི་ངོ་རྟགས།(ཁོང་ཚོས་ཀློག)</string>
<string name="verify_identity_activity__your_identity_you_read">ཁྱེད་ཀྱི་ངོ་རྟགས། (ཁྱེད་ཀྱི་ཀློགས)</string>
<!--verify_import_identity_activity-->
<string name="verify_import_identity_activity__identity_name_n">ངོས་འཛིན་གྱི་མིང་།</string>
<string name="verify_import_identity_activity__imported_identity_n">ནང་འཇུག་བྱས་པའི་ངོས་འཛིན།:</string>
<string name="verify_import_identity_activity__identity_name_n">ངོ་རྟགས་ཀྱི་མིང་།</string>
<string name="verify_import_identity_activity__imported_identity_n">ནང་འཇུག་བྱས་པའི་ངོ་རྟགས།:</string>
<string name="verify_import_identity_activity__verified">ར་སྤྲོད་བྱས་ཟིན།</string>
<string name="verify_import_identity_activity__compare">བསྡུར </string>
<string name="verify_import_identity_activity__compare">ཞིབ་བསྡུར་བྱོས། </string>
<!--verify_keys_activity-->
<string name="verify_keys_activity__they_read_this">གཞན་གྱི་འདི་ཀློགས།</string>
<string name="verify_keys_activity__you_read_this">ཁྱེད་ཀྱི་འདི་ཀློགས:</string>
<string name="verify_keys_activity__you_read_this">ཁྱེད་ཀྱི་འདི་ཀློགས:</string>
<!--view_identity_activity-->
<string name="view_identity_activity__identity">ངོ་རྟགས། </string>
<string name="view_identity_activity__qr_code">QR ཨང་རྟགས། </string>
<!--AndroidManifest.xml-->
<string name="AndroidManifest__create_passphrase">གསང་ཚིག་གསར་བཟོ་བྱོས། </string>
<string name="AndroidManifest__enter_passphrase">གསང་ཡིག་འཇོག</string>
<string name="AndroidManifest__enter_passphrase">གསང་ཡིག་ནང་འཇུག་བྱོས། </string>
<string name="AndroidManifest__select_contacts">འབྲེལ་བ་བྱེད་ས་འདེམས། </string>
<string name="AndroidManifest__textsecure_detected">ཡི་གེའི་ཉེན་སྲུང་ནག་བསུབ་བཏང་ཚར། </string>
<string name="AndroidManifest__public_identity_key">སྤྱི་བའི་ངོ་རྟགས་ལྡེ་མིག </string>
<string name="AndroidManifest__change_passphrase">གསང་ཚིག་བརྗེ་བོ་རྒྱོབས།</string>
<string name="AndroidManifest__verify_session">གཏམ་གླེང་སྤྲོད་བྱོས།</string>
<string name="AndroidManifest__verify_session">གཏམ་གླེང་ར་སྤྲོད་བྱོས།</string>
<string name="AndroidManifest__verify_identity">ངོ་རྟགས་ར་སྤྲོད་བྱོས། </string>
<string name="AndroidManifest__save_identity">ངོ་རྟགས་སྲུང་</string>
<string name="AndroidManifest__save_identity">ངོ་རྟགས་ཉར་ཚགས་བྱོས། </string>
<string name="AndroidManifest__manage_identity_keys">ངོ་རྟགས་ཀྱི་ལྡེ་མིག་ལ་དོ་དམ་བྱོས། </string>
<string name="AndroidManifest__complete_key_exchange">ལྡེ་མིག་བརྗེ་ལེན་ལེགས་འགྲུབ་བྱོསས྄་</string>
<string name="AndroidManifest__complete_key_exchange">ལྡེ་མིག་བརྗེ་ལེན་ལེགས་འགྲུབ་བྱོས</string>
<string name="AndroidManifest__verify_imported_identity">ནང་འདྲེན་ངོ་རྟགས་ར་སྤྲོད་བྱོས། </string>
<!--preferences.xml-->
<string name="preferences__use_settings">སྒྲིག་བཟོ་བཀོལ།</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">ནང་ཡོང་འཕྲིན་ཐུང་ཚང་མར་བལྟ་བ་དང་ཉར་བ་ལཡི་གེའི་ཉེན་སྲུང་བེད་སྤྱོད་ཐོངས།</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__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>
<string name="preferences__choose_identity">ངོ་རྟགས་འདེམས། </string>
<string name="preferences__choose_your_contact_entry_from_the_contacts_list">འབྲེལ་ཐོ་ནས་ཁྱེད་ཀྱིས་འབྲེལ་བ་བྱེད་ས་དེ་གདམ།</string>
<string name="preferences__choose_your_contact_entry_from_the_contacts_list">འབྲེལ་ཐོ་ནས་ཁྱེད་ཀྱིས་འབྲེལ་བ་བྱེད་ས་དེ་གདེམས། </string>
<string name="preferences__encryption_settings">གསང་སྡོམ་སྒྲིག་བཟོ།</string>
<string name="preferences__change_passphrase">གསང་ཚིག་བརྗེ་བོ་རྒྱོབས།</string>
<string name="preferences__change_my_passphrase">ངའི་གསང་ཚིག་བརྗེ་བོ་རྒྱོབས།</string>
@@ -264,30 +288,34 @@
<string name="preferences__automatically_complete_key_exchanges_for_new_sessions_or_for_existing_sessions_with_the_same_identity_key">ལྡེ་མིག་གི་ངོ་རྟགས་གཅིག་པའི་ད་ཡོད་སྡེ་ཚན་ཞིག་གམ་ཡང་ན་སྡེ་ཚན་གསར་བ་ཞིག་གི་སླད་དུ། རང་བཞིན་ནས་ལྡེ་མིག་བརྗེ་ལེན་དེ་འགྲུབ་པ་བྱོས།</string>
<string name="preferences__include_a_whitespace_tag_at_the_end_of_every_non_encrypted_message">གསང་སྡོམ་མ་ཡིན་པའི་འཕྲིན་ཐུང་རེ་རེའི་རྗེས་སུ། དཀར་ངོས་ཀྱི་འཛེར་མ་དེ་སྦྱར།</string>
<string name="preferences__include_whitespace_tag">དཀར་ངོས་ཀྱི་འཛེར་མ་དེ་མཉམ་དུ་སྦྱར།</string>
<string name="preferences__sign_key_exchange_messages_with_identity_key">ཡོང་མཐུན་གྱི་ལྡེ་མིག་དེའི་མཉམ་དུ། འཕྲིན་ཐུང་བརྗེ་བསྒྱུར་གྱི་ལྡེ་མིག་དེར་སྲིབ་རྟགས་རྒྱོབས།</string>
<string name="preferences__sign_key_exchange">ལྡེ་མིག་བརྗེ་བསྒྱུར་དེར་སྲིབ་རྟགས་རྒྱོབས།</string>
<string name="preferences__forget_passphrase_from_memory_after_some_interval">དུས་བརྒལ་གྱི་དབར་ནས། དྲན་ཐོ་ནས་གསང་ཚིག་བརྗེད།</string>
<string name="preferences__sign_key_exchange_messages_with_identity_key">ངོ་རྟགས་ལྡེ་མིག་དེའི་མཉམ་དུ། འཕྲིན་ཐུང་བརྗེ་བསྒྱུར་གྱི་ལྡེ་མིག་དེར་རྟགས་རྒྱོབས།</string>
<string name="preferences__sign_key_exchange">ལྡེ་མིག་བརྗེ་བསྒྱུར་དེར་རྟགས་རྒྱོབས།</string>
<string name="preferences__forget_passphrase_from_memory_after_some_interval">དུས་བརྒལ་གྱི་དབར་ནས། དྲན་ཐོ་ནས་གསང་ཚིག་བརྗེད། </string>
<string name="preferences__timeout_passphrase">གསང་ཚིག་གི་དུས་ཚོད་ཡོལ་འདུག</string>
<string name="preferences__pref_timeout_interval_title">བར་སེང་མཚམས་འཇོག</string>
<string name="preferences__pref_timeout_interval_dialogtitle">གསང་ཚིག་དུས་ཚོད་བརྒལ་བ་འདེམས། </string>
<string name="preferences__pref_timeout_interval_title">བར་སེང་མཚམས་ཞོག </string>
<string name="preferences__the_amount_of_time_to_wait_before_forgetting_passphrase">དྲན་ཐོ་ནས་གསང་ཚིག་མ་བརྗེད་གོང་སྒུག་ཡུན་དུས་ཚོད།</string>
<string name="preferences__identity_key_settings">ངོ་རྟགས་ཀྱི་ལྡེ་མིག་སྒྲིག་བཟོ། </string>
<string name="preferences__view_my_identity_key">ངའི་ངོས་འཛིན་ལྡེ་མིག་ལྡེ་མིག་ལ་ལྟ</string>
<string name="preferences__export_my_identity_key">ངའི་ངོས་འཛིན་བྱེད་ཀྱི་ལྡེ་མིག་དེ་ཕྱི་འདན་བྱེད།</string>
<string name="preferences__view_my_identity_key">ངའི་ངོ་རྟགས་ལྡེ་མིག་ལ་ལྟོས། </string>
<string name="preferences__export_my_identity_key">ངའི་ངོ་རྟགས་ལྡེ་མིག་དེ་ཕྱི་འདྲེན་བྱོས། </string>
<string name="preferences__import_contacts_key">འབྲེལ་བ་བྱེད་སའི་ལྡེ་མིག་ནང་འདྲེན་བྱོས། </string>
<string name="preferences__import_an_identity_key_from_a_contact">འབྲེལ་བ་བྱེད་ས་ནས་ངོ་རྟགས་ལྡེ་མིག་ནང་འདྲེན་བྱོས། </string>
<string name="preferences__manage_identity_keys">ངོ་རྟགས་ཀྱི་ལྡེ་མིག་དོ་དམ་བྱོས། </string>
<string name="preferences__manage_identity_keys">ངོ་རྟགས་ལྡེ་མིག་དོ་དམ་བྱོས། </string>
<string name="preferences__manage_configured_identity_keys">སྡེབ་སྒྲིག་ཟིན་པའི་ངོ་རྟགས་ལྡེ་མིག་དོ་དམ་བྱོས། </string>
<string name="preferences__notification_settings">བརྡ་གཏོང་སྒྲིག་བཟོ།</string>
<string name="preferences__notifications">བརྡ་གཏོང་།</string>
<string name="preferences__led_color">LED ཡི་ཚོན།</string>
<string name="preferences__change_notification_led_color">LED ཡི་ཚོན་གྱི་བརྡ་གཏོང་བརྗེས།</string>
<string name="preferences__display_message_notifications_in_status_bar">འཕྲིན་ཐུང་གི་བརྡ་ཁྱབ་དེ་རྣམ་པའི་ཚན་བྱང་གི་ནང་དུ་བཀྲམས། </string>
<string name="preferences__led_color">LED ཡི་ཚོན་མདོག </string>
<string name="preferences__change_notification_led_color">LED ཡི་ཚོན་མདོག་གི་བརྡ་གཏོང་བརྗེས།</string>
<string name="preferences__pref_led_blink_title">LED ཡི་རྒྱན་རིས།</string>
<string name="preferences__change_notification_blink_pattern">LED རྒྱན་རིས་ཀྱི་བརྡ་གཏོང་བརྗེས།</string>
<string name="preferences__select_ringtone">དྲིལ་སྒྲ་གདམ།</string>
<string name="preferences__pref_led_blink_dialogtitle">LED ཡི་བཀོད་རིས་དེ་འདེམས། </string>
<string name="preferences__select_led_color">LED ཡི་ཚོན་མདོག་དེ་འདེམས། </string>
<string name="preferences__select_ringtone">དྲིལ་སྒྲ་འདེམས། </string>
<string name="preferences__vibrate"> འདར་བཅུག</string>
<string name="preferences__also_vibrate_when_notified">བརྡ་གཏོང་སྐབས་ཡང་འདར་བཅུག</string>
<string name="preferences__minutes">སྐར་མ།</string>
<string name="preferences__hours">ཆུ་ཚོད།</string>
<string name="preferences__hours">དུས་ཚོད།</string>
<string name="preferences__green">ལྗང་ཁུ།</string>
<string name="preferences__red">དམར་པོ།</string>
<string name="preferences__blue">སྔོན་པོ།</string>
@@ -296,58 +324,77 @@
<string name="preferences__magenta">རྒྱ་ཚོས་ཀྱི་ཚོས་གཞི།</string>
<string name="preferences__fast">མགྱོགས་པོ།</string>
<string name="preferences__normal">འཆར་ཅན།</string>
<string name="preferences__slow">དལ་བུ།</string>
<string name="preferences__custom">རང་གྲུབ།</string>
<string name="preferences__slow">དལ་བོ། </string>
<string name="preferences__custom">ཡུལ་སྲོལ། </string>
<string name="preferences__advanced_mms_access_point_names">རྒྱ་བསྐྱེད་པ། MMS Access Point ཡི་མིང་ཐོ་ཁག </string>
<string name="preferences__enable_fallback_mmsc">དེ་བསྐྱར་དུ་བྱེད་ནུས་ཅན་བཟོས། </string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">APN ཡི་མ་ལག་གི་བརྡ་ལན་དེ་མི་རྙེད་དུས། MMSC ཡི་བརྡ་ལན་སྡེབ་སྒྲིག་བྱས་བ་དེ་བེད་སྤྱོད་ཐོངས། </string>
<string name="preferences__mmsc_url_required">MMSC URL(དགོས་ངེས་ཅན) </string>
<string name="preferences__mms_proxy_host_optional">MMS Proxy Host (གདམ་ཀ་ཅན) </string>
<string name="preferences__mms_proxy_port_optional">MMS Proxy Port (གདམ་ཀ་ཅན) </string>
<string name="preferences__delivery_reports">སྙན་ཐོ་སྐྱེལ། </string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">ཁྱེད་ཀྱིས་གཏོང་བའི་SMSའཕྲིན་ཐུང་རེ་རེར་སྙན་ཐོ་སྐྱེལ་བའི་རེ་སྐུལ་བྱོས། </string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">ཁྱེད་ཀྱིས་གཏོང་བའི་MMSའཕྲིན་ཐུང་རེ་རེར་སྙན་ཐོ་སྐྱེལ་བའི་རེ་སྐུལ་བྱོས། </string>
<string name="preferences__mms_delivery_reports">MMS ཡི་སྙན་ཐོ་སྐྱེལ། </string>
<string name="preferences__sms_delivery_reports">SMS ཡི་སྙན་ཐོ་སྐྱེལ། </string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">གླེང་མོལ་གྱི་སྐུད་པའི་དམིགས་བསལ་རིང་ཚད་རྫོགས་མཚམས། རང་བཞིན་ནས་འཕྲིན་ཐུང་རྙིང་པ་དག་བསུབ་འགྲོ་གི་རེད། </string>
<string name="preferences__delete_old_messages">འཕྲིན་ཐུང་རྙིང་པ་གསུབ། </string>
<string name="preferences__storage">ཉར་ས། </string>
<string name="preferences__conversation_length_limit">གླེང་མོལ་གྱི་རིང་ཐུང་གི་ཚད། </string>
<string name="preferences__trim_all_threads_now">སྐུད་པ་ཚང་མ་གཙང་བཟོ་བྱོས། </string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">གླེང་མོལ་གྱི་སྐུད་པ་ཚང་མ་གཙང་སེལ་དང་གླེང་མོལ་གྱི་རིང་ཚད་ནུས་ལྡན་དུ་ཐོངས། </string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
<!--contact_selection_list-->
<string name="contact_selection_list__menu_select_all">ཚང་མ་འདེམས།</string>
<string name="contact_selection_list__menu_unselect_all">འདེམས་པ་ཡོངས་རྫོགས་མེད་པར་ཟོས།</string>
<string name="contact_selection_list__menu_unselect_all">ཚང་མ་མ་འདེམས། </string>
<!--contact_selection-->
<string name="contact_selection__menu_finished">མཇུག་རྫོགས་སོ།</string>
<!--conversation_button_context-->
<string name="conversation_button_context__menu_send_unencrypted">གསང་སྡོམ་མ་བྱེད་པའི་འཕྲིན་ཐུང་བསྐུར།</string>
<string name="conversation_button_context__menu_send_unencrypted">གསང་སྡོམ་མ་བྱས་བའི་འཕྲིན་ཐུང་བསྐུར།</string>
<!--conversation_callable-->
<string name="conversation_callable__menu_call">ཁ་པར་གཏོང་།</string>
<string name="conversation_callable__menu_call">ཁ་པར་ཐོངས། </string>
<!--conversation_context-->
<string name="conversation_context__menu_message_details">འཕྲིན་ཐུང་ཞིབ་ཆ།</string>
<string name="conversation_context__menu_copy_text">འཕྲིན་ཐུང་འདྲ་བཟོ་བྱེད།</string>
<string name="conversation_context__menu_delete_message">འཕྲིན་ཐུང་སུབ།</string>
<string name="conversation_context__menu_forward_message">འཕྲིན་ཐུང་བརྒྱུད་འགྲེམ་བྱེད།</string>
<string name="conversation_context__menu_message_details">འཕྲིན་ཐུང་གི་ཞིབ་ཆ།</string>
<string name="conversation_context__menu_copy_text">ཡི་གེ་འདྲ་བཤུས་བྱོས། </string>
<string name="conversation_context__menu_delete_message">འཕྲིན་ཐུང་སུབ།</string>
<string name="conversation_context__menu_forward_message">འཕྲིན་ཐུང་བརྒྱུད་འགྲེམ་བྱོས། </string>
<!--conversation_insecure-->
<string name="conversation_insecure__menu_start_secure_session">བདེ་འཇགས་གཏམ་གླེང་འགོ་རྩམ།</string>
<string name="conversation_insecure__menu_start_secure_session">བདེ་འཇགས་གཏམ་གླེང་འགོ་རྩམ། </string>
<!--conversation_list_batch-->
<string name="conversation_list_batch__menu_delete_selected">འདེམས་པ་རྣམས་སུབ།</string>
<string name="conversation_list_batch__menu_select_all">ཚང་མ་འདེམས།</string>
<string name="conversation_list_batch__menu_delete_selected">འདེམས་པ་རྣམས་སུབ།</string>
<string name="conversation_list_batch__menu_select_all">ཚང་མ་འདེམས། </string>
<!--conversation_list-->
<string name="conversation_list__menu_search">འཚོལ།</string>
<!--conversation_secure_verified-->
<!--conversation_secure_unverified-->
<string name="conversation_secure_verified__menu_security">ཉེན་སྲུང་།</string>
<string name="conversation_secure_verified__menu_verify_session">གཏམ་གླེང་ར་སྤྲོད་བྱེད།</string>
<string name="conversation_secure_verified__menu_verify_session">གཏམ་གླེང་ར་སྤྲོད་བྱོས། </string>
<string name="conversation_secure_verified__menu_verify_recipient">འཕྲིན་ཐུང་འབྱོར་ཡུལ་དེ་ར་སྤྲོད་བྱོས།</string>
<string name="conversation_secure_verified__menu_abort_secure_session">བདེ་འཇགས་གཏམ་གླེང་མཚམས་འཇོག</string>
<string name="conversation_secure_verified__menu_abort_secure_session">བདེ་འཇགས་གཏམ་གླེང་མཚམས་ོག </string>
<!--conversation-->
<string name="conversation__menu_add_attachment">ཟུར་སྣོན་འཇོག</string>
<string name="conversation__menu_delete_thread">འཕྲིན་ཐུང་སྐུད་རིམ་སུབ།</string>
<string name="conversation__menu_compare">བསྡུར། </string>
<string name="conversation__menu_add_attachment">ཟུར་སྣོན་ོག </string>
<string name="conversation__menu_delete_thread">འཕྲིན་ཐུང་སྐུད་རིམ་སུབ།</string>
<string name="conversation__menu_compare">ཞིབ་བསྡུར་བྱོས</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">འཕྲིན་ཐུང་ལེན་མཁན་གྱི་ཐོ། </string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">བསྡུར། </string>
<string name="key_scanning__menu_get_scanned_to_compare">ལྡེ་མིག་འགྲན་ཆེད་ཞིབ་བརྟག་བྱོས།</string>
<string name="key_scanning__menu_scan_to_compare">ལྡེ་མིག་འགྲན་ཆེད་ཞིབ་བརྟག་བྱོས།</string>
<string name="key_scanning__menu_compare">ཞིབ་བསྡུར་བྱོས</string>
<string name="key_scanning__menu_get_scanned_to_compare">ལྡེ་མིག་ཞིབ་བསྡུར་ཆེད་ཞིབ་བརྟག་བྱོས།</string>
<string name="key_scanning__menu_scan_to_compare">ཞིབ་བསྡུར་ཆེད་ཆེད་ཞིབ་བརྟག་བྱོས།</string>
<!--text_secure_locked-->
<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_settings">སྒྲིག་བཟོ།</string>
<string name="text_secure_normal__menu_import_export">ནང་འཇུག/ཕྱིར་འདོན།</string>
<string name="text_secure_normal__menu_import">ནང་འཇུག་བྱོས།</string>
<string name="text_secure_normal__menu_export">ཕྱི་འདན་བྱེད།</string>
<string name="text_secure_normal__menu_clear_passphrase">གསང་ིག་མེད་པ་བཟོས།</string>
<string name="text_secure_normal__menu_import">ནང་འདྲེན་བྱོས། </string>
<string name="text_secure_normal__menu_export">ཕྱི་འདྲེན་བྱོས། </string>
<string name="text_secure_normal__menu_clear_passphrase">གསང་ིག་མེད་པ་བཟོས།</string>
<!--verify_keys-->
<string name="verify_keys__menu_verified">ར་སྤྲོད་བྱས་ཟིན།</string>
<!--Misc. piggybacking-->
<string name="PlayStoreListing">ཡི་གེའི་ཉེན་སྲུང་ནི། བདེ་སྲུང་ཡར་ཐོན་ཅན་གྱི་འཕྲིན་ཐུང་ཉེར་སྤྱོད་མ་ལག་ཞིག་ཡིན་ཞིང་དེས་སོར་གནས་འཕྲིན་ཐུང་ཉེར་སྤྱོད་ཀྱི་ཚབ་ལྟ་བུ་བྱེད་ཀྱི་ཡོད། ཡི་གེའི་ཉེན་སྲུང་བེད་སྤྱོད་གཏོང་མཁན་ནང་ཕན་ཚུན་བར་གྱི་འཕྲིན་ཐུང་དག་གསང་སྡོམ་བྱས་ཡོད་པའི་མ་ཟད་འཕྲིན་ཡིག་ཚང་མ་མ་ལག་གི་གསང་སྡོམ་རེའུ་མིག་གི་ཁོངས་སུ་ཉར་ཡོད། གལ་སྲིད་ཁྱེད་རང་གི་ལག་འཁྱེར་ཁ་པར་བོར་བའམ་བརྐུས་བ་སོགས་བྱུང་ན། ཁྱེད་ཀྱི་འཕྲིན་ཐུང་དག་བདེ་འཇགས་ཡིན་ལ་ཡི་གེའི་ཉེན་སྲུང་བེད་སྤྱོད་གཏོང་མཁན་གཞན་དེ་ཡང་དྲ་ལམ་ནས་བྱ་ར་བྱེད་མི་ཐུབ། </string>
<!--EOF-->
</resources>

View File

@@ -27,6 +27,7 @@
<string name="ApplicationMigrationManager_copy">Kopieren</string>
<string name="ApplicationMigrationManager_dont_copy">Nicht kopieren</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">Momentan: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">Nicht gefunden!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">Es wurde kein gültiger Schlüssel zur Identifizierung für den ausgewählten Kontakt gefunden.</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">Sie haben keinen persönlichen Schlüssel!</string>
@@ -35,7 +36,11 @@
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">Bevor Sie Schlüssel importieren können, müssen Sie ihr Passwort eingeben...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">Bevor Sie Schlüssel verwalten können, müssen Sie ihr Passwort eingeben...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">Sie haben noch kein Passwort festgelegt!</string>
<string name="ApplicationPreferencesActivity_delete">Löschen</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Bild</string>
<string name="AttachmentTypeSelectorAdapter_video">Video</string>
<string name="AttachmentTypeSelectorAdapter_audio">Audio</string>
<!--ConversationItem-->
<string name="ConversationItem_message_size_d_kb">Größe der Nachricht: %d KB</string>
<string name="ConversationItem_expires_s">Läuft ab in: %s</string>
@@ -68,6 +73,10 @@
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">Die ausgewählte Audiodatei überschreitet leider die maximal mögliche Nachrichtengröße.</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">Der angegeben Empfänger ist keine gültige SMS oder E-Mail Adresse!</string>
<string name="ConversationActivity_message_is_empty_exclamation">Die Nachricht ist leer!</string>
<string name="ConversationActivity_forward_message_prefix">Weiterleiten</string>
<string name="ConversationActivity_group_conversation_recipients">Teilnehmer in Gruppengespräch</string>
<string name="ConversationActivity_group_conversation">Gruppengespräch</string>
<string name="ConversationActivity_d_recipients_in_group">%d Teilnehmer in Gruppe</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Nachrichtendetails</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Sender: %1$s Transport: %2$s Gesendet/Empfangen:%3$s</string>
@@ -177,6 +186,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">MMS wird entschlüsselt, bitte warten...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Falsch verschlüsselte MMS...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">MMS wurde für einen nicht existierende Sitzung verschlüsselt...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">Migration</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">SMS werden migriert</string>
@@ -185,8 +195,8 @@
<string name="KeyCachingService_passphrase_cached">Passwort gecached</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) Neue Nachrichten</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) Neue Nachrichten, letzte von: %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Letzte Nachricht von: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Sie haben eine Nachricht von jemand empfangen, der verschlüsselte Sitzungen über TextSecure unterstützt. Wollen Sie jetzt eine sicher Sitzung aufbauen?</string>
<string name="auto_initiate_activity__initiate_exchange">Austausch initieren</string>
@@ -205,6 +215,12 @@
<string name="conversation_activity__type_message">Nachricht schreiben</string>
<string name="conversation_activity__send">Senden</string>
<string name="conversation_activity__remove">Löschen</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Download</string>
<string name="conversation_item_sent__downloading">Herunterladen</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Download</string>
<string name="conversation_item_received__downloading">Herunterladen</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Mehrfachauswahl</string>
<!--create_passphrase_activity-->
@@ -340,6 +356,7 @@
<string name="conversation__menu_delete_thread">Thread löschen</string>
<string name="conversation__menu_compare">Vergleichen</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">Teilnehmerliste</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">Vergleichen</string>
<string name="key_scanning__menu_get_scanned_to_compare">Zum Vergleich eingelesen werden</string>
@@ -357,6 +374,4 @@
<string name="verify_keys__menu_verified">Verifiziert</string>
<!--Misc. piggybacking-->
<!--EOF-->
<string name="conversation_item_sent__download">Download</string>
<string name="conversation_item_received__download">Download</string>
</resources>

View File

@@ -1,7 +1,7 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">TextSecure</string>
<string name="yes">Si</string>
<string name="yes">Sí</string>
<string name="no">No</string>
<string name="delete">Borrar</string>
<!--ApplicationExportManager-->
@@ -20,22 +20,31 @@
<string name="ApplicationExportManager_import">Importar</string>
<string name="ApplicationExportManager_export">Exportar</string>
<!--ApplicationMigrationManager-->
<string name="ApplicationMigrationManager_migrating_database">MIgrando la base de datos</string>
<string name="ApplicationMigrationManager_migrating_database">Migrando la base de datos</string>
<string name="ApplicationMigrationManager_migrating_text_message_database">Migrando la base de datos de mensajes de texto...</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_question">¿Desea copiar la base de datos de mensajes de texto del sistema?</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_explanation">TextSecure usa una base de datos cifrada, separa de la base de datos predeterminada por el sistema. ¿Le gustaría copiar sus mensajes de texto existentes en la base cifrada de TextSecure? Su base de datos predeterminada en el sistema no será afectada. </string>
<string name="ApplicationMigrationManager_copy">Copiar</string>
<string name="ApplicationMigrationManager_dont_copy">No copiar</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">Actualmente: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">¡No encontrado!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">No se ha encontrado una clave de identidad válida en el contacto especificado.</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">¡Aún no tiene una clave de identidad!</string>
<string name="ApplicationPreferenceActivity_you_have_not_yet_defined_a_contact_for_yourself">No ha definido aún un contacto para usted mismo. Seleccione uno en el menú de Preferencias</string>
<string name="ApplicationPreferenceActivity_exported_to_contacts_database">Exportado a la base de datos de contactos</string>
<string name="ApplicationPreferenceActivity_exported_to_contacts_database">¡Exportado a la base de datos de contactos!</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">Necesita ingresar una frase de contraseña antes de importar claves...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">Necesita ingresar su frase de contraseña antes de gestionar sus claves...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">¡No ha elegido aún una frase de contraseña!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Límite de tamaño de la conversación</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">mensajes por conversación</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">¿Eliminar todos los mensajes viejos ahora?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">¿Estás seguro de que te gustaría recortar inmediatamente todos los hilos de conversación a los %s últimos mensajes?</string>
<string name="ApplicationPreferencesActivity_delete">Eliminar</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Fotografía</string>
<string name="AttachmentTypeSelectorAdapter_video">Vídeo</string>
<string name="AttachmentTypeSelectorAdapter_audio">Audio</string>
<!--ConversationItem-->
<string name="ConversationItem_message_size_d_kb">Tamaño del mensaje: %d KB</string>
<string name="ConversationItem_expires_s">Expira: %s</string>
@@ -68,9 +77,14 @@
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">Lo sentimos, el audio seleccionado excede las restricciones de tamaño del mensaje.</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">El destinatario no es un SMS o correo electrónico válido</string>
<string name="ConversationActivity_message_is_empty_exclamation">¡El mensaje está vacío!</string>
<string name="ConversationActivity_forward_message_prefix">FWD</string>
<string name="ConversationActivity_group_conversation_recipients">Receptores de Conversación en Grupo</string>
<string name="ConversationActivity_group_conversation">Conversación en Grupo</string>
<string name="ConversationActivity_d_recipients_in_group">%d receptores en grupo</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Detalles del mensaje</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Remitente: %1$s\nTransporte: %2$s\nEnviado/Recibido:%3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Emisor: %1$s⏎ Transporte: %2$s⏎ Enviado: %3$s⏎ Recibido:%4$s\n</string>
<string name="ConversationFragment_confirm_message_delete">Confirmar borrado del mensaje</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">¿Está seguro que desea borrar permanentemente este mensaje?</string>
<!--ConversationListAdapter-->
@@ -177,6 +191,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">Descifrando MMS, por favor espere...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Mensaje MMS mal cifrado</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">Mensaje MMS cifrado para sesión no existente...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">Migrando</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">Migrando los mensajes de texto del sistema</string>
@@ -185,8 +200,8 @@
<string name="KeyCachingService_passphrase_cached">Frase de contraseña en memoria caché</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) Nuevos mensajes</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) Nuevos mensajes, el más reciente de: %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Mensaje más reciente de: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Ha recibido un mensaje de alguien que tiene soporte para sesiones cifradas de TextSecure. ¿Desea iniciar una sesión segura?</string>
<string name="auto_initiate_activity__initiate_exchange">Iniciar intercambio</string>
@@ -205,6 +220,12 @@
<string name="conversation_activity__type_message">Escribir mensaje</string>
<string name="conversation_activity__send">Enviar</string>
<string name="conversation_activity__remove">Eliminar</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Descarga</string>
<string name="conversation_item_sent__downloading">Descargando</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Descarga</string>
<string name="conversation_item_received__downloading">Descargando</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Modo de selección múltiple</string>
<!--create_passphrase_activity-->
@@ -305,6 +326,23 @@
<string name="preferences__normal">Normal</string>
<string name="preferences__slow">Despacio</string>
<string name="preferences__custom">Personalizado</string>
<string name="preferences__advanced_mms_access_point_names">Avanzado: Nombres de Puntos de Acceso MMS</string>
<string name="preferences__enable_fallback_mmsc">Activar Fallback MMSC</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Usar la información MMSC configurada aquí cuando el sistema de información APN no está disponible.</string>
<string name="preferences__mmsc_url_required">MMSC URL (Requerido)</string>
<string name="preferences__mms_proxy_host_optional">MMS Proxy Host (Opcional)</string>
<string name="preferences__mms_proxy_port_optional">Puerto de Proxy MMS (Opcional)</string>
<string name="preferences__delivery_reports">Noticias de entrega</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Pide una noticia de entrega por cada mensaje SMS que envías</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Pide una noticia de entrega por cada mensaje MMS que envías</string>
<string name="preferences__mms_delivery_reports">Noticias de entrega de MMS</string>
<string name="preferences__sms_delivery_reports">Noticias de entrega de SMS</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Elimina automáticamente los mensajes más viejos una vez el hilo de conversación excede el límite de longitud</string>
<string name="preferences__delete_old_messages">Eliminar mensajes viejos</string>
<string name="preferences__storage">Almacenamiento</string>
<string name="preferences__conversation_length_limit">Límite de longitud de la conversación</string>
<string name="preferences__trim_all_threads_now">Recorta todos los hilos ahora</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Búsqueda a través de todos los hilos de conversación y fuerza los límites de longitud de la misma</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
@@ -340,6 +378,7 @@
<string name="conversation__menu_delete_thread">Borrar conversación</string>
<string name="conversation__menu_compare">Comparar</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">Lista de receptores</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">Comparar</string>
<string name="key_scanning__menu_get_scanned_to_compare">Escanear mi clave para comparar</string>
@@ -356,5 +395,6 @@
<!--verify_keys-->
<string name="verify_keys__menu_verified">Verificado</string>
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure es una aplicacion de mensajería de texto con énfasis en la seguridad que sirve de reemplazo a la aplicación de mensajería de texto por defecto. Los mensajes enviados a otros usuarios de TextSecure están cifrados al vuelo, y todos los mensajes de texto están cifrados en una base de datos en el dispositivo. Si tu teléfono se pierde o es robado, tus mensajes estarán seguros, y la comunicación con otros usuarios de TextSecure no será monitorizada en el aire.</string>
<!--EOF-->
</resources>

View File

@@ -27,6 +27,7 @@
<string name="ApplicationMigrationManager_copy">Copier</string>
<string name="ApplicationMigrationManager_dont_copy">Ne pas copier</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">Actuellement : %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">Introuvable !</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">Aucune clé didentité valide na été trouvée dans le contact indiqué.</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">Vous navez aucune clé didentité !</string>
@@ -35,6 +36,11 @@
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">Il est nécessaire davoir entré votre phrase de passe avant dimporter des clés…</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">Il est nécessaire davoir entré votre phrase de passe avant de gérer les clés…</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">Vous navez pas encore défini de phrase de passe !</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Limite de longueur de conversation</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">Messages par conversation</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Supprimer tous les anciens messages maintenant ?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Êtes-vous sur de vouloir réduire toutes les conversations aux %s messages les plus récents ?</string>
<string name="ApplicationPreferencesActivity_delete">Supprimer</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Image</string>
<string name="AttachmentTypeSelectorAdapter_video">Vidéo</string>
@@ -78,6 +84,7 @@
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Détails du message</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Expéditeur : %1$s\nTransport : %2$s\nEnvoyé / Reçu : %3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Expéditeur : %1$s\nTransport : %2$s\nEnvoyé : %3$s\nReçu : %4$s</string>
<string name="ConversationFragment_confirm_message_delete">Confirmer la suppression du message</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Êtes-vous sûr de vouloir supprimer définitivement ce message ?</string>
<!--ConversationListAdapter-->
@@ -184,6 +191,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">Déchiffrage du MMS, veuillez patienter…</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Message MMS mal chiffré…</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">Message MMS chiffré pour une session non-existante…</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">Migration</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">Migration des messages texte du système</string>
@@ -192,8 +200,8 @@
<string name="KeyCachingService_passphrase_cached">Phrase de passe en cache</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) Nouveaux messages</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) Nouveaux messages, le dernier est de : %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Dernier de : %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Vous avez reçu un message de quelqu\'un qui utilise les sessions chiffrées TextSecure. Voulez vous commencer une session chiffrée avec lui?</string>
<string name="auto_initiate_activity__initiate_exchange">Commencer l\'échange</string>
@@ -212,6 +220,12 @@
<string name="conversation_activity__type_message">Entrez le message</string>
<string name="conversation_activity__send">Envoyer</string>
<string name="conversation_activity__remove">Supprimer</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Télécharger</string>
<string name="conversation_item_sent__downloading">Téléchargement</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Télécharger</string>
<string name="conversation_item_received__downloading">Téléchargement</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Mode de sélection par lot</string>
<!--create_passphrase_activity-->
@@ -312,6 +326,23 @@
<string name="preferences__normal">Normal</string>
<string name="preferences__slow">Lent</string>
<string name="preferences__custom">Personnalisé</string>
<string name="preferences__advanced_mms_access_point_names">Avancé : Noms des Points dAccès MMS</string>
<string name="preferences__enable_fallback_mmsc">Activer le MMSC par défaut</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Utiliser les informations MMSC configurée ici lorsque les informations APN ne sont pas disponibles</string>
<string name="preferences__mmsc_url_required">URL MMSC (Obligatoire)</string>
<string name="preferences__mms_proxy_host_optional">Hôte Proxy MMS (Optionnel)</string>
<string name="preferences__mms_proxy_port_optional">Port Proxy MMS (Optionnel)</string>
<string name="preferences__delivery_reports">Accusés de réception</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Demander un accusé de réception pour chaque SMS envoyé</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Demander un accusé de réception pour chaque MMS envoyé</string>
<string name="preferences__mms_delivery_reports">Accusés de réception MMS</string>
<string name="preferences__sms_delivery_reports">Accusés de réception SMS</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Supprimer automatique les anciens messages lorsque la conversation dépasse un certaine taille d\'échange.</string>
<string name="preferences__delete_old_messages">Supprimer les anciens messages</string>
<string name="preferences__storage">Stockage</string>
<string name="preferences__conversation_length_limit">Limite de taille de conversation</string>
<string name="preferences__trim_all_threads_now">Réduire toutes les conversations maintenant</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Chercher à travers toutes les conversations et appliquer la limite de taille de conversation</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
@@ -366,6 +397,4 @@
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure est une application de messagerie texte avec une sécurité améliorée, qui constitue une alternative complète à lapplication de messagerie texte par défaut. Les messages à dautres utilisateurs de TextSecure sont chiffrés à la volée, et tous les messages sont stockés dans une base de données chiffrée sur lappareil. Si votre téléphone est perdu ou volé, vos messages seront en sécurité, et les communications avec dautres utilisateurs de TextSecure ne peuvent pas être espionnées à la volée.</string>
<!--EOF-->
<string name="conversation_item_sent__download">Télécharger</string>
<string name="conversation_item_received__download">Télécharger</string>
</resources>

View File

@@ -1,18 +1,18 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">TextSecur</string>
<string name="app_name">TextSecure</string>
<string name="yes"></string>
<string name="no">No</string>
<string name="delete">Cancella</string>
<!--ApplicationExportManager-->
<string name="ApplicationExportManager_import_database_and_settings_title">Importare il Database e le Impostazioni?</string>
<string name="ApplicationExportManager_import_database_and_settings_message">Importare il Database, le chiavi e le impostazioni di TextSecure dalla memoria SD?\nATTENZIONE: Questa operazione sovrascriverà ogni messaggio, chiave e impostazione esistente!</string>
<string name="ApplicationExportManager_importing_database_and_keys">Stò importando il Database e le chiavi</string>
<string name="ApplicationExportManager_importing_your_sms_database_keys_and_settings">Stò importando il database degli SMS, le chiavi e le impostazioni...</string>
<string name="ApplicationExportManager_importing_database_and_keys">Sto importando il Database e le chiavi</string>
<string name="ApplicationExportManager_importing_your_sms_database_keys_and_settings">Sto importando il database degli SMS, le chiavi e le impostazioni...</string>
<string name="ApplicationExportManager_export_database_question">Esportare il Database?</string>
<string name="ApplicationExportManager_export_textsecure_database_keys_and_settings_prompt">Esportare il Database, le chiavi e le impostazioni di TextSecure nella memoria SD?</string>
<string name="ApplicationExportManager_exporting_database_and_keys">Stò esportando il Database e le chiavi</string>
<string name="ApplicationExportManager_exporting_your_sms_database_keys_and_settings">Stò esportando il database degli SMS, le chiavi e le impostazioni...</string>
<string name="ApplicationExportManager_exporting_database_and_keys">Sto esportando il Database e le chiavi</string>
<string name="ApplicationExportManager_exporting_your_sms_database_keys_and_settings">Sto esportando il database degli SMS, le chiavi e le impostazioni...</string>
<string name="ApplicationExportManager_no_sd_card_found_exclamation">Nessuna memoria SD trovata!</string>
<string name="ApplicationExportManager_error_exporting_to_sd_exclamation">Errore durante l\'esportazione sulla memoria SD!</string>
<string name="ApplicationExportManager_import_successful_exclamation">Importazione completata!</string>
@@ -177,6 +177,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">Decifratura MMS in corso, attendere...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Cifratura MMS invalida..</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">Messaggio MMS cifrato per una sessione non esistente</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">Spostamento</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">Cambio sistema di messaggio di testo</string>
@@ -185,8 +186,8 @@
<string name="KeyCachingService_passphrase_cached">Password salvata in cache</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) Nuovi messaggi</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) Nuovi messaggio, il più recente da %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Più recente da: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Hai ricevuto un messaggio da qualcuno che supporta una sessione cifrata di TextSecure. Vuoi iniziare una sessione sicura?</string>
<string name="auto_initiate_activity__initiate_exchange">Inizio scambio delle chiavi</string>
@@ -205,6 +206,8 @@
<string name="conversation_activity__type_message">Inserisci il messaggio</string>
<string name="conversation_activity__send">Invia</string>
<string name="conversation_activity__remove">Elimina</string>
<!--conversation_item_sent-->
<!--conversation_item_received-->
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Modalità di selezione di gruppo</string>
<!--create_passphrase_activity-->
@@ -305,6 +308,7 @@
<string name="preferences__normal">Normale</string>
<string name="preferences__slow">Lento</string>
<string name="preferences__custom">Personalizzato</string>
<string name="preferences__mmsc_url_required">URL MMSC (Richiesto)</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->

View File

@@ -36,6 +36,11 @@
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">U moet uw wachtwoord opgegeven voor het importeren van sleutels...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">U moet uw wachtwoord opgegeven voor het beheren van sleutels...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">U heeft nog geen wachtwoord ingesteld!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Conversatielengte limiet</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">berichten per conversatie</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Nu alle oude berichten verwijderen?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Weet u zeker dat u bij alle conversaties alleen de laatste %s berichten behouden wilt?</string>
<string name="ApplicationPreferencesActivity_delete">Verwijderen</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Afbeelding</string>
<string name="AttachmentTypeSelectorAdapter_video">Video</string>
@@ -76,9 +81,11 @@
<string name="ConversationActivity_group_conversation_recipients">Groepsconversatie Ontvangers</string>
<string name="ConversationActivity_group_conversation">Groepsconversatie</string>
<string name="ConversationActivity_d_recipients_in_group">%d ontvangers in groep</string>
<string name="ConversationActivity_saving_draft">Concept opslaan...</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Bericht details</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Verzender: %1$s\nTransport: %2$s\nVerstuurd/Ontvangen:%3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Verzender: %1$s\nMedium: %2$s\nVerzonden: %3$s\nOntvangen:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">Bevestig Bericht Verwijdering</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Weet u zeker dat u dit bericht permanent wilt verwijderen?</string>
<!--ConversationListAdapter-->
@@ -87,6 +94,8 @@
<!--ConversationListFragment-->
<string name="ConversationListFragment_delete_threads_question">Meerdere conversaties verwijderen?</string>
<string name="ConversationListFragment_are_you_sure_you_wish_to_delete_all_selected_conversation_threads">Weet u zeker dat u ALLE geselecteerde conversaties wilt verwijderen?</string>
<string name="ConversationListFragment_deleting">Aan het verwijderen</string>
<string name="ConversationListFragment_deleting_selected_threads">Geselecteerde conversaties aan het verwijderen...</string>
<!--ConversationListItem-->
<string name="ConversationListItem_key_exchange_message">Sleuteluitwisselingsbericht...</string>
<!--KeyScanningActivity-->
@@ -185,6 +194,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">MMS ontsleutelen, een moment...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Verkeerd versleuteld MMS-bericht...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">MMS-bericht versleuteld voor niet bestaande sessie...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">Migreren</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">Migreren Systeem Tekstberichten</string>
@@ -193,8 +203,8 @@
<string name="KeyCachingService_passphrase_cached">Wachtwoord In Cache</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) Nieuwe berichten</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) Nieuwe berichten, meest recente van: %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Meest recente van: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">U heeft een bericht ontvangen van iemand die versleutelde TextSecure sessies ondersteunt. Wilt u een beveiligde sessie initiëren?</string>
<string name="auto_initiate_activity__initiate_exchange">Initieer Uitwisseling</string>
@@ -213,6 +223,12 @@
<string name="conversation_activity__type_message">Typ bericht</string>
<string name="conversation_activity__send">Verstuur</string>
<string name="conversation_activity__remove">Verwijder</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Download</string>
<string name="conversation_item_sent__downloading">Downloaden</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Download</string>
<string name="conversation_item_received__downloading">Downloaden</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Selecteer Meerdere Tegelijk</string>
<!--create_passphrase_activity-->
@@ -313,6 +329,23 @@
<string name="preferences__normal">Normaal</string>
<string name="preferences__slow">Langzaam</string>
<string name="preferences__custom">Aangepast</string>
<string name="preferences__advanced_mms_access_point_names">Geavanceerd: MMS Toegangspuntnamen</string>
<string name="preferences__enable_fallback_mmsc">Fallback MMSC Activeren</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Gebruik de hier ingestelde MMSC informatie wanneer de APN informatie van het systeem niet beschikbaar is.</string>
<string name="preferences__mmsc_url_required">MMSC URL (Vereist)</string>
<string name="preferences__mms_proxy_host_optional">MMS Proxy Host (Optioneel)</string>
<string name="preferences__mms_proxy_port_optional">MMS Proxy Poort (Optioneel)</string>
<string name="preferences__delivery_reports">Ontvangstbevestigingen</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Verzoek om een ontvangstbevestiging voor ieder verzonden SMS bericht</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Verzoek om een ontvangstbevestiging voor ieder verzonden MMS bericht</string>
<string name="preferences__mms_delivery_reports">MMS ontvangstbevestigingen</string>
<string name="preferences__sms_delivery_reports">SMS ontvangstbevestigingen</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Automatisch oudere berichten verwijderen wanneer een conversatie meer dan een bepaald aantal berichten bevat</string>
<string name="preferences__delete_old_messages">Verwijder oude berichten</string>
<string name="preferences__storage">Opslag</string>
<string name="preferences__conversation_length_limit">Conversatielengte limiet</string>
<string name="preferences__trim_all_threads_now">Oudere conversatieberichten nu verwijderen</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Alle conversaties scannen en de maximale conversatielengte toepassen</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
@@ -367,8 +400,4 @@
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure is een tekstberichtapplicatie met verbeterde beveiliging die dient als een volledige vervanger voor de standaard tekstberichtapplicatie. Berichten naar andere TextSecure gebruikers worden versleuteld en alle tekstberichten worden opgeslagen in een versleutelde database op uw telefoon. Wanneer uw telefoon kwijt of gestolen is, zijn uw berichten veilig en communicatie met andere TextSecure gebruikers kan niet afgeluisterd worden.</string>
<!--EOF-->
<string name="conversation_item_sent__download">Download</string>
<string name="conversation_item_sent__downloading">Downloaden</string>
<string name="conversation_item_received__download">Download</string>
<string name="conversation_item_received__downloading">Downloaden</string>
</resources>

View File

@@ -27,6 +27,7 @@
<string name="ApplicationMigrationManager_copy">Kopier</string>
<string name="ApplicationMigrationManager_dont_copy">Ikke kopier</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">Nå: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">Ikke funnet!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">Ingen gyldig ID-nøkkel ble funnet i den angitte kontakten.</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">Du har ingen ID-nøkkel!</string>
@@ -35,6 +36,11 @@
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">Du må taste inn passordet før du importerer nøkler...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">Du må taste inn passordet før du administrerer nøkler...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">Du har ikke definert et passord ennå!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Begrense samtalelengde</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">meldinger per samtale</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Slette alle gamle meldinger nå?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Er du sikker på at du ønsker å umiddelbart trimme alle samtaletrådene til de siste %s meldinger?</string>
<string name="ApplicationPreferencesActivity_delete">Slett</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Bilde</string>
<string name="AttachmentTypeSelectorAdapter_video">Video</string>
@@ -72,9 +78,14 @@
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">Mottakeren er ikke en gyldig SMS- eller e-postadresse!</string>
<string name="ConversationActivity_message_is_empty_exclamation">Meldingen er tom!</string>
<string name="ConversationActivity_forward_message_prefix">VS</string>
<string name="ConversationActivity_group_conversation_recipients">Mottakere i gruppen</string>
<string name="ConversationActivity_group_conversation">Gruppesamtale</string>
<string name="ConversationActivity_d_recipients_in_group">%d mottakere i gruppen</string>
<string name="ConversationActivity_saving_draft">Lagrer utkast...</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Meldingsdetaljer</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Sender: %1$s⏎ Transport: %2$s⏎ Sendt/Mottatt:%3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Avsender: %1$s⏎ Transport: %2$s⏎ Sendt: %3$s⏎ Mottatt:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">Bekreft sletting av melding</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Er du sikker på at du ønsker å slette denne meldingen?</string>
<!--ConversationListAdapter-->
@@ -83,6 +94,8 @@
<!--ConversationListFragment-->
<string name="ConversationListFragment_delete_threads_question">Slette trådene?</string>
<string name="ConversationListFragment_are_you_sure_you_wish_to_delete_all_selected_conversation_threads">Er du sikker på at du vil slette ALLE valgte samtaletråder?</string>
<string name="ConversationListFragment_deleting">Sletter</string>
<string name="ConversationListFragment_deleting_selected_threads">Sletter utvalgte tråder...</string>
<!--ConversationListItem-->
<string name="ConversationListItem_key_exchange_message">Nøkkelutvekslingsmelding...</string>
<!--KeyScanningActivity-->
@@ -181,6 +194,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">Dekrypterer MMS, vennligst vent...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Ugyldig kryptert MMS...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">MMS kryptert for en ikke-eksisterende sesjon...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">Migrerer</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">Migrerer systemets SMS meldinger</string>
@@ -189,8 +203,12 @@
<string name="KeyCachingService_passphrase_cached">Passord bufret</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) Nye meldinger</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) Nye meldinger, sist mottatt fra: %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Sist mottatt fra: %s</string>
<string name="MessageNotifier_key_exchange">Nøkkelutveksling...</string>
<string name="MessageNotifier_encrypted_message">Kryptert melding...</string>
<string name="MessageNotifier_corrupted_ciphertext">Korrumpert chiffertext</string>
<string name="MessageNotifier_no_subject">(Mangler emne)</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Du har mottatt en melding fra noen som støtter TextSecure krypterte sesjoner. Ønsker du å initiere en sikker sesjon?</string>
<string name="auto_initiate_activity__initiate_exchange">Start nøkkelutveksling</string>
@@ -209,6 +227,12 @@
<string name="conversation_activity__type_message">Skriv melding</string>
<string name="conversation_activity__send">Send</string>
<string name="conversation_activity__remove">Slett</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Last ned</string>
<string name="conversation_item_sent__downloading">Laster ned</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Last ned</string>
<string name="conversation_item_received__downloading">Laster ned</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Batch-valg modus</string>
<!--create_passphrase_activity-->
@@ -309,6 +333,23 @@
<string name="preferences__normal">Normal</string>
<string name="preferences__slow">Sakte</string>
<string name="preferences__custom">Tilpasset</string>
<string name="preferences__advanced_mms_access_point_names">Avansert: MMS Aksesspunkt navn (APN)</string>
<string name="preferences__enable_fallback_mmsc">Aktiver alternativ MMSC</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Benytt MMSC informasjonen konfigurert her når systemets APN informasjon er utilgjengelig.</string>
<string name="preferences__mmsc_url_required">MMSC URL (påkrevd)</string>
<string name="preferences__mms_proxy_host_optional">MMS proxy host (valgfritt)</string>
<string name="preferences__mms_proxy_port_optional">MMS proxy port (valgfritt)</string>
<string name="preferences__delivery_reports">Leveringsrapporter</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Be om en leveringsrapport for hver SMS-melding du sender</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Be om en leveringsrapport for hver MMS-melding du sender</string>
<string name="preferences__mms_delivery_reports">Leveringsrapport for MMS</string>
<string name="preferences__sms_delivery_reports">Leveringsrapport for SMS</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Slett eldre meldinger automatisk når en samtale tråd overskrider en spesifisert lengde</string>
<string name="preferences__delete_old_messages">Slett gamle meldinger</string>
<string name="preferences__storage">Lagring</string>
<string name="preferences__conversation_length_limit">Grense på samtalelengde</string>
<string name="preferences__trim_all_threads_now">Trimme alle samtaletråder nå</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Skanne gjennom alle samtaletrådene og håndheve grensen på samtalelengde </string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
@@ -344,6 +385,7 @@
<string name="conversation__menu_delete_thread">Slett tråd</string>
<string name="conversation__menu_compare">Sammenlign</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">Mottakerliste</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">Sammenlign</string>
<string name="key_scanning__menu_get_scanned_to_compare">Få den skannet for å sammenlikne</string>
@@ -362,6 +404,4 @@
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure er en tekstmeldingsapplikasjon hvor sikkerheten er forbedret. TextSecure fungerer som en full erstatning for mobilterminalens standard SMS/MMS applikasjon. Meldinger til andre TextSecure brukere sendes kryptert, og alle meldinger lagres i en kryptert database på mobilterminalen. Dersom telefonen blir stjålet eller mistet, vil meldingene være trygge, og kommunikasjon med andre TextSecure brukere kan ikke overvåkes.</string>
<!--EOF-->
<string name="conversation_item_sent__download">Last ned</string>
<string name="conversation_item_received__download">Last ned</string>
</resources>

View File

@@ -0,0 +1,412 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">TextSecure</string>
<string name="yes">Sim</string>
<string name="no">Não</string>
<string name="delete">Apagar</string>
<!--ApplicationExportManager-->
<string name="ApplicationExportManager_import_database_and_settings_title">Importar base de dados e configurações?</string>
<string name="ApplicationExportManager_import_database_and_settings_message">Importar a base de dados, chaves e configurações TextSecure a partir do Cartão SD?⏎ ⏎ AVISO: Isto vai sobreescrever as mensagens, chaves e configurações existentes!</string>
<string name="ApplicationExportManager_importing_database_and_keys">Importar base de dados e chaves</string>
<string name="ApplicationExportManager_importing_your_sms_database_keys_and_settings">A importar a base de dados de SMS, chaves e configurações...</string>
<string name="ApplicationExportManager_export_database_question">Exportar base de dados?</string>
<string name="ApplicationExportManager_export_textsecure_database_keys_and_settings_prompt">Exportar a base de dados, chaves e configurações TextSecure para o Cartão SD?</string>
<string name="ApplicationExportManager_exporting_database_and_keys">Exportar base de dados e chaves</string>
<string name="ApplicationExportManager_exporting_your_sms_database_keys_and_settings">A exportar a base de dados de SMS, chaves e configurações...</string>
<string name="ApplicationExportManager_no_sd_card_found_exclamation">Não foi encontrado um cartão SD!</string>
<string name="ApplicationExportManager_error_exporting_to_sd_exclamation">Erro na exportação para SD!</string>
<string name="ApplicationExportManager_import_successful_exclamation">Importação com sucesso!</string>
<string name="ApplicationExportManager_export_successful_exclamation">Exportação com sucesso!</string>
<string name="ApplicationExportManager_import">Importar</string>
<string name="ApplicationExportManager_export">Exportar</string>
<!--ApplicationMigrationManager-->
<string name="ApplicationMigrationManager_migrating_database">A migrar a base de dados</string>
<string name="ApplicationMigrationManager_migrating_text_message_database">A migrar a base de dados de mensagens...</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_question">Copiar a base de dados de mensagens do sistema?</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_explanation">Text secure usa uma base de dados cifrada que é independente da base de dados por omissão do sistema.\nGostaria de copiar as mensagens existentes para a base de dados cifrada do TextSecure?\nA base de dados por omissão não será afectada por esta operação.</string>
<string name="ApplicationMigrationManager_copy">Copiar</string>
<string name="ApplicationMigrationManager_dont_copy">Não copiar</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">Correntemente: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">Não encontrado!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">Não foi encontrada uma chave de identidade válida para o contacto especificado.</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">Não tem uma chave de identidade!</string>
<string name="ApplicationPreferenceActivity_you_have_not_yet_defined_a_contact_for_yourself">Ainda não definiu um contacto próprio. Selecione um no menu de Configurações.</string>
<string name="ApplicationPreferenceActivity_exported_to_contacts_database">Exportar para a base de dados de contactos!</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">Necessita de inserir a frase-chave antes de importar chaves...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">Necessita de inserir a frase-chave antes de gerir chaves...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">Ainda não definiu uma frase-chave!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Tamanho máximo de conversa</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">Mensagens por conversa</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Deletar todas mensagens antigas agora?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Tem certeza que deseja manter apenas as %s mensagens mais recentes?</string>
<string name="ApplicationPreferencesActivity_delete">Deletar</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Imagem</string>
<string name="AttachmentTypeSelectorAdapter_video">Vídeo</string>
<string name="AttachmentTypeSelectorAdapter_audio">Áudio</string>
<!--ConversationItem-->
<string name="ConversationItem_message_size_d_kb">Tamanho da mensagem: %d KB</string>
<string name="ConversationItem_expires_s">Expira: %s</string>
<string name="ConversationItem_error_sending_message">Erro no envio da mensagem</string>
<string name="ConversationItem_sending">A enviar...</string>
<string name="ConversationItem_saving_attachment">A guardar anexo</string>
<string name="ConversationItem_saving_attachment_to_sd_card">A guardar anexo para o cartão SD...</string>
<string name="ConversationItem_save_to_sd_card">Guardar para o cartão SD?</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_warning">Este ficheiro foi armazenado numa base de dados cifrada. A versão que gravar no cartão SD deixará de estar cifrada, pretende continuar?</string>
<string name="ConversationItem_error_while_saving_attachment_to_sd_card">Erro a gravar anexo para o cartão SD...</string>
<string name="ConversationItem_success_exclamation">Sucesso!</string>
<string name="ConversationItem_unable_to_write_to_sd_card_exclamation">Não é possível escrever no cartão SD...</string>
<string name="ConversationItem_view_secure_media_question">Ver ficheiro seguro?</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning">Este ficheiro foi armazenado numa base de dados cifrada. Infelizmente, para o visualizar com um visualizador externo, é necessário que os dados sejam decifrados temporariamente e escritos para o disco.\nTem a certeza que pretende fazer isto?</string>
<string name="ConversationItem_key_exchange_message">Mensagem de intercâmbio de chaves</string>
<string name="ConversationItem_received_and_processed_key_exchange_message">Mensagem de intercâmbio de chaves recebida e processada.</string>
<string name="ConversationItem_error_received_stale_key_exchange_message">Erro, foi recebida uma mensagem de intercâmbio de chaves desactualizada.</string>
<string name="ConversationItem_received_key_exchange_message_click_to_process">Foi recebida uma mensagem de intercâmbio de chaves, clique para a processar</string>
<!--ConversationActivity-->
<string name="ConversationActivity_initiate_secure_session_question">Iniciar sessão segura?</string>
<string name="ConversationActivity_initiate_secure_session_with_s_question">Iniciar sessão segura com %s?</string>
<string name="ConversationActivity_abort_secure_session_confirmation">Confirmação de término de sessão segura</string>
<string name="ConversationActivity_are_you_sure_that_you_want_to_abort_this_secure_session_question">Tem a certeza que pretende terminar esta sessão segura?</string>
<string name="ConversationActivity_delete_thread_confirmation">Confirme apagar a conversa</string>
<string name="ConversationActivity_are_you_sure_that_you_want_to_permanently_delete_this_conversation_question">Tem a certeza que pretende apagar permanentemente esta conversa?</string>
<string name="ConversationActivity_add_attachment">Adicionar anexo</string>
<string name="ConversationActivity_compose_message">Compor mensagem</string>
<string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">Lamento, ocorreu um erro no envio do seu anexo</string>
<string name="ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions">Lamento, o vídeo seleccionado excede as restrições de tamanho da mensagem</string>
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">Lamento, o áudio seleccionado excede as restrições de tamanho da mensagem</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">Destinatário não é um endereço SMS ou email válido!</string>
<string name="ConversationActivity_message_is_empty_exclamation">A mensagem está vazia!</string>
<string name="ConversationActivity_forward_message_prefix">FWD</string>
<string name="ConversationActivity_group_conversation_recipients">Agrupar destinatários de conversa</string>
<string name="ConversationActivity_group_conversation">Conversa em grupo</string>
<string name="ConversationActivity_d_recipients_in_group">%d destinatários no grupo</string>
<string name="ConversationActivity_saving_draft">Salvando rascunho...</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Detalhes da mensagem</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Remetente: %1$s⏎ Transporte: %2$s⏎ Enviadas/Recebidas:%3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Transmissor: %1$s\nTransporte: %2$s\nEnviado: %3$s\nRecebido:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">Confirme apagar a mensagem</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Tem a certeza que pretende apagar permanentemente esta mensagem?</string>
<!--ConversationListAdapter-->
<string name="ConversationListAdapter_encrypted_message_enter_passphrase">Mensagem cifrada, introduza frase-chave...</string>
<string name="ConversationListAdapter_key_exchange_message">Mensagem de intercâmbio de chaves...</string>
<!--ConversationListFragment-->
<string name="ConversationListFragment_delete_threads_question">Apagar conversas?</string>
<string name="ConversationListFragment_are_you_sure_you_wish_to_delete_all_selected_conversation_threads">Tem a certeza que pretende apagar TODAS as conversas seleccionadas?</string>
<string name="ConversationListFragment_deleting">Deletando</string>
<string name="ConversationListFragment_deleting_selected_threads">Deletando conversas selecionadas...</string>
<!--ConversationListItem-->
<string name="ConversationListItem_key_exchange_message">Mensagem de intercâmbio de chaves...</string>
<!--KeyScanningActivity-->
<string name="KeyScanningActivity_no_scanned_key_found_exclamation">Não foi encontrada nenhuma chave lida!</string>
<!--PassphraseChangeActivity-->
<string name="PassphraseChangeActivity_passphrases_dont_match_exclamation">As frases-chave não coincidem!</string>
<string name="PassphraseChangeActivity_incorrect_old_passphrase_exclamation">Frase-chave anterior incorrecta!</string>
<!--PassphraseCreateActivity-->
<string name="PassphraseCreateActivity_passphrases_dont_match_exclamation">As frases-chave não coincidem!</string>
<string name="PassphraseCreateActivity_generating_keypair">A gerar par-chave</string>
<string name="PassphraseCreateActivity_generating_a_local_encryption_keypair">A gerar um par-chave de cifra local...</string>
<!--PassphrasePromptActivity-->
<string name="PassphrasePromptActivity_invalid_passphrase_exclamation">Frase-chave inválida!</string>
<!--ReceiveKeyActivity-->
<string name="ReceiveKeyActivity_error_you_have_received_a_corrupted_public_key">ERRO:⏎ ⏎ Recebeu uma chave pública corrompida. Esta chave não pode ser processada, por favor reinicie uma sessão segura.</string>
<string name="ReceiveKeyActivity_error_you_have_received_a_public_key_from_an_unsupported_version_of_the_protocol">ERRO:⏎ ⏎ Recebeu uma chave pública de uma versão do protocolo não suportada. Esta chave não pode ser processada, por favor reinicie uma sessão segura.</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_does_not_include_an_identity_signature">Esta mensagem de intercâmbio de chaves não inclúi uma assinatura de identidade.</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_but_you_do_not_yet_trust_it">Esta mensagem de intercâmbio de chaves inclúi uma assinatura de identidade, mas ainda não confia nela.</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_which_you_trust_for_s">Esta mensagem de intercâmbio de chaves inclúi uma assinatura de identidade confiada para: %s</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_sent_to_start_your_current_encrypted_session_with_s">Esta é a chave que enviou para iniciar a sessão cifrada corrente com %s</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_received_to_start_your_current_encrypted_session_with_s">Esta é a chave que recebeu para iniciar a sessão cifrada corrente com %s</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_warning_you_already_have_an_encrypted_session">Recebeu uma mensagem de intercâmbio de chaves de %s.⏎ ⏎ AVISO: Já tem uma sessão cifrada com este contacto. Se aceitar esta mensagem de intercâmbio de chaves, a sessão existente vai ser destruída e terá de se re-autenticar. Deseja completar este intercâmbio de chaves?</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_previously_initiated">Recebeu uma mensagem de intercâmbio de chaves de %s. Já tem uma sessão cifrada prévia com este contacto e ao aceitar esta chave, completará o intercâmbio de chaves. Deseja completar este intercâmbio de chaves?</string>
<string name="ReceiveKeyActivity_you_have_initiated_a_key_exchange_message_with_s_but_have_not_yet_received_a_reply">Iniciou uma mensagem de intercâmbio de chaves com %s mas ainda não recebeu resposta.</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_no_existing_session">Recebeu uma mensagem de intercâmbio de chaves de %s. Não tem nenhuma sessão prévia estabelecida com este contacto, deseja completar este intercâmbio de chaves?</string>
<!--ReviewIdentitiesActivity-->
<string name="ReviewIdentitiesActivity_unable_to_view_corrupted_identity_key_exclamation">Não é possível visualizar a chave de identidade corrompida!</string>
<string name="ReviewIdentitiesActivity_delete_identity">Apagar identidade?</string>
<string name="ReviewIdentitiesActivity_delete_identity_are_you_sure_you_want_to_delete_this_identity_key">Tem a certeza que pretende apagar permanentemente esta chave de identidade?</string>
<string name="ReviewIdentitiesActivity_invalid_identity">Identidade inválida!</string>
<!--SaveIdentityActivity-->
<string name="SaveIdentityActivity_you_must_specify_a_name_for_this_identity_exclamation">Tem de especificar um nome para esta identidade!</string>
<string name="SaveIdentityActivity_identity_name_exists_exclamation">O nome de identidade já existe!</string>
<string name="SaveIdentityActivity_an_identity_key_with_the_specified_name_already_exists">Uma chave de identidade com o nome especificado já existe.</string>
<string name="SaveIdentityActivity_manage_identities">Gerir identidades</string>
<!--VerifyIdentityActivity-->
<string name="VerifyIdentityActivity_mark_identity_verified_question">Marcar identidades como verificadas?</string>
<string name="VerifyIdentityActivity_are_you_sure_you_have_validated_the_recipients_identity_fingerprint_and_would_like_to_mark_it_as_verified">Tem a certeza que validou a identidade dos destinatários e pretende marcá-la como verificada?</string>
<string name="VerifyIdentityActivity_mark_verified">Marcar como validade</string>
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">Não tem uma chave de identidade.</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">Destinatário não tem chave de identidade.</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key_exclamation">Destinatário não tem chave de identidade!</string>
<string name="VerifyIdentityActivity_scan_their_key_to_compare">Ler a chave do destinatário para comparação</string>
<string name="VerifyIdentityActivity_get_my_key_scanned">Ler a minha chave</string>
<string name="VerifyIdentityActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully">AVISO, a chave lida NÃO corresponde. Por favor verifique a impressão digital da chave cuidadosamente.</string>
<string name="VerifyIdentityActivity_not_verified_exclamation">NÃO verificado!</string>
<string name="VerifyIdentityActivity_their_key_is_correct_it_is_also_necessary_to_verify_your_key_with_them_as_well">A chave do destinatário está correcta. É igualmente necessário verificar a sua chave perante ele.</string>
<string name="VerifyIdentityActivity_verified_exclamation">Verificado!</string>
<string name="VerifyIdentityActivity_you_don_t_have_an_identity_key_exclamation">Não tem uma chave de identidade!</string>
<!--VerifyImportedIdentityActivity-->
<string name="VerifyImportedIdentityActivity_you_must_specify_a_name_for_this_contact_exclamation">Tem de especificar um nome para este contacto!</string>
<string name="VerifyImportedIdentityActivity_save_identity_key_question">Guardar chave de identidade?</string>
<string name="VerifyImportedIdentityActivity_error_saving_identity_key_exclamation">Erro ao gravar chave de identidade!</string>
<string name="VerifyImportedIdentityActivity_this_identity_key_or_an_identity_key_with_the_same_name_already_exists_please_edit_your_key_database">Esta chave de identidade ou uma chave com o mesmo nome já existe. Por favor edite a sua base de dados de chaves.</string>
<string name="VerifyImportedIdentityActivity_scan_to_compare">Leia o código QR para comparar</string>
<string name="VerifyImportedIdentityActivity_get_scanned_to_compare">Apresente o código QR para comparar</string>
<string name="VerifyImportedIdentityActivity_not_verified_exclamation">Não verificado!</string>
<string name="VerifyImportedIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">AVISO, a chave lida NÃO coincide!</string>
<string name="VerifyImportedIdentityActivity_the_scanned_key_matches_exclamation">A chave lida coincide!</string>
<string name="VerifyImportedIdentityActivity_verified_exclamation">Verificado!</string>
<string name="VerifyImportedIdentityActivity_are_you_sure_that_you_would_like_to_mark_this_as_a_valid_identity_key_for_all_future_correspondence_with_s">Tem a certeza que pretende marcar esta chave como válida para toda a futura correspondência com %s? Apenas deve fazer isto se verificou a impressão digital da chave.</string>
<!--VerifyKeysActivity-->
<string name="VerifyKeysActivity_mark_session_verified_question">Marcar sessão como verificada?</string>
<string name="VerifyKeysActivity_are_you_sure_that_you_have_validated_these_fingerprints_and_would_like_to_mark_this_session_as_verified">Tem a certeza que validou estas impressões digitais e pretende marcar esta sessão como verificada?</string>
<string name="VerifyKeysActivity_mark_verified">Marcar como verificada</string>
<string name="VerifyKeysActivity_get_my_fingerprint_scanned">Apresentar a minha impressão digital para leitura</string>
<string name="VerifyKeysActivity_scan_their_fingerprint">Ler a impressão digital do destinatário</string>
<string name="VerifyKeysActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully2">AVISO, a chave lida NÃO coincide. Por favor verifique a impressão digital cuidadosamente.</string>
<string name="VerifyKeysActivity_not_verified_exclamation">NÃO verificado!</string>
<string name="VerifyKeysActivity_their_key_is_correct_it_is_also_necessary_to_get_your_fingerprint_scanned_as_well">A chave está correcta. É igualmente necessário que o destinatário valide a sua impressão digital.</string>
<string name="VerifyKeysActivity_verified_exclamation">Verificado!</string>
<!--ViewIdentityActivity-->
<string name="ViewIdentityActivity_you_do_not_have_an_identity_key">Você não tem uma chave de identidade.</string>
<string name="ViewIdentityActivity_scan_to_compare">Leia o código QR para comparar</string>
<string name="ViewIdentityActivity_get_scanned_to_compare">Apresente o código QR para comparar</string>
<string name="ViewIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">AVISO, a chave lida NÃO coincide.</string>
<string name="ViewIdentityActivity_not_verified_exclamation">NÃO verificado!</string>
<string name="ViewIdentityActivity_the_scanned_key_matches_exclamation">A chave lida coincide!</string>
<string name="ViewIdentityActivity_verified_exclamation">Verificado!</string>
<!--KeyExchangeInitiator-->
<string name="KeyExchangeInitiator_initiate_despite_existing_request_question">Iniciar apesar de pedido existente?</string>
<string name="KeyExchangeInitiator_youve_already_sent_a_session_initiation_request_to_this_recipient_are_you_sure">Já enviou um pedido de início de sessão para este destinatário, tem a certeza que pretende enviar outro? Isto invalidará o pedido anterior.</string>
<string name="KeyExchangeInitiator_send">Enviar</string>
<!--MessageDisplayHelper-->
<string name="MessageDisplayHelper_bad_encrypted_message">Mensagem cifrada corrompida...</string>
<string name="MessageDisplayHelper_decrypting_please_wait">A decifrar, por favor aguarde...</string>
<string name="MessageDisplayHelper_message_encrypted_for_non_existing_session">Mensagem cifrada para sessão inexistente...</string>
<string name="MessageDisplayHelper_decryption_error_local_message_corrupted_mac_doesn_t_match_potential_tampering_question">Erro de decifração: Mensagem local corrompida, MAC não coincide. Potencial modificação?</string>
<!--MmsDatabase-->
<string name="MmsDatabase_connecting_to_mms_server">A ligar ao servidor de MMS...</string>
<string name="MmsDatabase_downloading_mms">A transferir MMS...</string>
<string name="MmsDatabase_mms_download_failed">Transferência de MMS falhou!</string>
<string name="MmsDatabase_downloading">A transferir...</string>
<string name="MmsDatabase_anonymous">Anónimo</string>
<!--MmsMessageRecord-->
<string name="MmsMessageRecord_decrypting_mms_please_wait">A decifrar MMS, por favor aguarde...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Mensagem MMS cifrada corrompida...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">Mensagem MMS cifrada para sessão inexistente...</string>
<!--MmsSender-->
<string name="MmsSender_currently_unable_to_send_your_mms_message">Mensagem MMS não pôde ser enviada agora. Será enviada quando o serviço estiver disponível.</string>
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">A migrar</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">A migrar mensagens de texto do sistema</string>
<!--KeyCachingService-->
<string name="KeyCachingService_textsecure_passphrase_cached">Frase-chave TextSecure guardada em memória cache</string>
<string name="KeyCachingService_passphrase_cached">Frase-chave guardada em cache</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) novas mensagens</string>
<string name="MessageNotifier_most_recent_from_s">Mais recente de: %s</string>
<string name="MessageNotifier_key_exchange">Troca de chave...</string>
<string name="MessageNotifier_encrypted_message">Mensagem criptografada...</string>
<string name="MessageNotifier_corrupted_ciphertext">Texto encriptado corrompido</string>
<string name="MessageNotifier_no_subject">(Sem Assunto)</string>
<string name="MessageNotifier_message_delivery_failed">Envio de mensagem falhou.</string>
<string name="MessageNotifier_failed_to_deliver_message">Falha ao enviar mensagem.</string>
<string name="MessageNotifier_error_delivering_message">Erro ao enviar mensagem.</string>
<!--SmsReceiver-->
<string name="SmsReceiver_currently_unable_to_send_your_sms_message">Mensagem SMS não pôde ser enviada agora. Será enviada quando o serviço estiver disponível.</string>
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Recebeu uma mensagem de alguém que suporta sessões cifradas TextSecure. Deseja iniciar uma sessão segura?</string>
<string name="auto_initiate_activity__initiate_exchange">Iniciar intercâmbio</string>
<!--change_passphrase_activity-->
<string name="change_passphrase_activity__old_passphrase">Frase-chave anterior:</string>
<string name="change_passphrase_activity__new_passphrase">Frase-chave nova:</string>
<string name="change_passphrase_activity__repeat_new_passphrase">Repetir a frase-chave nova:</string>
<!--contact_selection_group_activity-->
<!--contact_selection_list_activity-->
<string name="contact_selection_group_activity__no_contacts">Sem contactos.</string>
<!--ContactSelectionListFragment-->
<string name="ContactSelectionlistFragment_select_for">Seleccionar para</string>
<!--contact_selection_recent_activity-->
<string name="contact_selection_recent_activity__no_recent_calls">Sem chamadas recentes.</string>
<!--conversation_activity-->
<string name="conversation_activity__type_message">Digite a mensagem</string>
<string name="conversation_activity__send">Enviar</string>
<string name="conversation_activity__remove">Remover</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Transferir</string>
<string name="conversation_item_sent__downloading">A transferir</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Transferir</string>
<string name="conversation_item_received__downloading">A transferir</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Modo de selecção em grupo</string>
<!--create_passphrase_activity-->
<string name="create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase">Por favor escolha uma frase-chave que será usada para cifrar os seus dados localmente. Esta deve ser uma frase-chave forte.</string>
<string name="create_passphrase_activity__passphrase">Frase-chave:</string>
<string name="create_passphrase_activity__repeat">Repetir:</string>
<!--receive_key_activity-->
<string name="receive_key_activity__session">Sessão</string>
<string name="receive_key_activity__identities">Identidades</string>
<string name="receive_key_activity__complete_exchange">Completar intercâmbio</string>
<!--recipients_panel-->
<string name="recipients_panel__to">Para</string>
<!--review_identities-->
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">Não tem, presentemente, nenhuma chave de identidade na sua base de dados de confianças.</string>
<!--save_identity_activity-->
<string name="save_identity_activity__identity_name">Nome de identidade:</string>
<!--verify_identity_activity-->
<string name="verify_identity_activity__their_identity_they_read">A identidade do destinatário (ele lê):</string>
<string name="verify_identity_activity__your_identity_you_read">A sua identidade (você lê):</string>
<!--verify_import_identity_activity-->
<string name="verify_import_identity_activity__identity_name_n">Nome de identidade:\n\n</string>
<string name="verify_import_identity_activity__imported_identity_n">Identidade importada:\n\n</string>
<string name="verify_import_identity_activity__verified">Verificado!</string>
<string name="verify_import_identity_activity__compare">Comparar</string>
<!--verify_keys_activity-->
<string name="verify_keys_activity__they_read_this">O destinatário lê:</string>
<string name="verify_keys_activity__you_read_this">Você lê:</string>
<!--view_identity_activity-->
<string name="view_identity_activity__identity">Identidade:</string>
<string name="view_identity_activity__qr_code">Código QR</string>
<!--AndroidManifest.xml-->
<string name="AndroidManifest__create_passphrase">Criar frase-chave</string>
<string name="AndroidManifest__enter_passphrase">Introduzir frase-chave</string>
<string name="AndroidManifest__select_contacts">Seleccionar contactos</string>
<string name="AndroidManifest__textsecure_detected">TextSecure detectado</string>
<string name="AndroidManifest__public_identity_key">Chave de identidade pública</string>
<string name="AndroidManifest__change_passphrase">Mudar frase-chave</string>
<string name="AndroidManifest__verify_session">Verificar sessão</string>
<string name="AndroidManifest__verify_identity">Verificar identidade</string>
<string name="AndroidManifest__save_identity">Guardar identidade</string>
<string name="AndroidManifest__manage_identity_keys">Gerir chaves de identidade</string>
<string name="AndroidManifest__complete_key_exchange">Completar intercâmbio de chaves</string>
<string name="AndroidManifest__verify_imported_identity">Verificar identidade importada</string>
<!--preferences.xml-->
<string name="preferences__use_settings">Utilizar configurações</string>
<string name="preferences__pref_all_sms_title">Utilizar para todas as SMS</string>
<string name="preferences__pref_all_mms_title">Utilizar para todas as MMS</string>
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Utilizar TextSecure para visualizar e armazenar todas as mensagens de texto recebidas</string>
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Utilizar TextSecure para visualizar e armazenar todas as mensagens multimédia recebidas</string>
<string name="preferences__input_settings">Configurações de introdução</string>
<string name="preferences__pref_enter_sends_title">Tecla de enter envia</string>
<string name="preferences__pressing_the_enter_key_will_send_text_messages">Pressionar a tecla Enter envia mensagem de texto</string>
<string name="preferences__display_settings">Configurações de apresentação</string>
<string name="preferences__choose_identity">Escolher identidade</string>
<string name="preferences__choose_your_contact_entry_from_the_contacts_list">Escolha o seu próprio contacto da lista de contactos.</string>
<string name="preferences__encryption_settings">Configurações de cifra</string>
<string name="preferences__change_passphrase">Mudar frase-chave</string>
<string name="preferences__change_my_passphrase">Mudar a minha frase-chave</string>
<string name="preferences__complete_key_exchanges">Completar intercâmbios de chaves</string>
<string name="preferences__automatically_complete_key_exchanges_for_new_sessions_or_for_existing_sessions_with_the_same_identity_key">Completar automaticamente intercâmbios de chaves para novas sessões ou para sessões existentes com a mesma chave de identidade</string>
<string name="preferences__include_a_whitespace_tag_at_the_end_of_every_non_encrypted_message">Incluir uma etiqueta de caracteres invisíveis no fim de todas as mensagens não cifradas</string>
<string name="preferences__include_whitespace_tag">Incluir etiqueta de caracteres invisíveis</string>
<string name="preferences__sign_key_exchange_messages_with_identity_key">Assinar mensagens de intercâmbio de chaves com a chave de identidade</string>
<string name="preferences__sign_key_exchange">Assinar intercâmbio de chaves</string>
<string name="preferences__forget_passphrase_from_memory_after_some_interval">Esquecer a frase-chave após algum intervalo</string>
<string name="preferences__timeout_passphrase">Expirar frase-chave</string>
<string name="preferences__pref_timeout_interval_dialogtitle">Seleccionar tempo de expiração da frase-chave</string>
<string name="preferences__pref_timeout_interval_title">Intervalo de expiração</string>
<string name="preferences__the_amount_of_time_to_wait_before_forgetting_passphrase">Tempo a esperar antes de remover a frase-chave da memória</string>
<string name="preferences__identity_key_settings">Configurações de chave de identidade</string>
<string name="preferences__view_my_identity_key">Visualizar a minha chave de identidade</string>
<string name="preferences__export_my_identity_key">Exportar a minha chave de identidade</string>
<string name="preferences__import_contacts_key">Importar a chave do contacto</string>
<string name="preferences__import_an_identity_key_from_a_contact">Importar uma chave de identidade a partir de um contacto</string>
<string name="preferences__manage_identity_keys">Gerir chaves de identidade</string>
<string name="preferences__manage_configured_identity_keys">Gerir chaves de identidade configuradas</string>
<string name="preferences__notification_settings">Configurações de notificação</string>
<string name="preferences__notifications">Notificações</string>
<string name="preferences__display_message_notifications_in_status_bar">Apresentar notificações de mensagens na barra de estado</string>
<string name="preferences__led_color">Cor do LED</string>
<string name="preferences__change_notification_led_color">Mudar a cor do LED de notificação</string>
<string name="preferences__pref_led_blink_title">Padrão de pisca do LED</string>
<string name="preferences__change_notification_blink_pattern">Alterar padrão de pisca do LED</string>
<string name="preferences__pref_led_blink_dialogtitle">Seleccionar padrão de pisca do LED</string>
<string name="preferences__select_led_color">Seleccionar cor do LED</string>
<string name="preferences__select_ringtone">Seleccionar toque</string>
<string name="preferences__vibrate">Vibrar</string>
<string name="preferences__also_vibrate_when_notified">Vibrar também quando notificado</string>
<string name="preferences__minutes">minutos</string>
<string name="preferences__hours">horas</string>
<string name="preferences__green">Verde</string>
<string name="preferences__red">Vermelho</string>
<string name="preferences__blue">Azul</string>
<string name="preferences__orange">Laranja</string>
<string name="preferences__cyan">Cíano</string>
<string name="preferences__magenta">Magenta</string>
<string name="preferences__fast">Rápido</string>
<string name="preferences__normal">Normal</string>
<string name="preferences__slow">Lento</string>
<string name="preferences__custom">Personalizado</string>
<string name="preferences__advanced_mms_access_point_names">Avançado: APN de MMS</string>
<string name="preferences__enable_fallback_mmsc">Habilitar MMSC de reseva</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Usar esta informação de MMSC quando APN do sistema estiver indisponível.</string>
<string name="preferences__mmsc_url_required">URL do MMSC (Necessário)</string>
<string name="preferences__mms_proxy_host_optional">Proxy MMS (Opcional)</string>
<string name="preferences__mms_proxy_port_optional">Porta do Proxy MMS (Opcional)</string>
<string name="preferences__delivery_reports">Avisos de entrega</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Pedir um aviso de entrega para cada SMS enviado</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Pedir um aviso de entrega para cada MMS enviado</string>
<string name="preferences__mms_delivery_reports">Avisos de entrega de MMS</string>
<string name="preferences__sms_delivery_reports">Avisos de entrega de SMS</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Automaticamente deletar mensagens mais antigas quando uma conversa exceder o tamanho especificado</string>
<string name="preferences__delete_old_messages">Deletar mensagens antigas</string>
<string name="preferences__storage">Armazenamento</string>
<string name="preferences__conversation_length_limit">Tamanho máximo de conversa</string>
<string name="preferences__trim_all_threads_now">Apagar todas conversas agora</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Verificar todas conversas e aplicar tamanho máximo de conversa</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
<!--contact_selection_list-->
<string name="contact_selection_list__menu_select_all">Seleccionar tudo</string>
<string name="contact_selection_list__menu_unselect_all">Desseleccionar tudo</string>
<!--contact_selection-->
<string name="contact_selection__menu_finished">Terminado</string>
<!--conversation_button_context-->
<string name="conversation_button_context__menu_send_unencrypted">Enviar não cifrado</string>
<!--conversation_callable-->
<string name="conversation_callable__menu_call">Chamar</string>
<!--conversation_context-->
<string name="conversation_context__menu_message_details">Detalhes da mensagem</string>
<string name="conversation_context__menu_copy_text">Copiar texto</string>
<string name="conversation_context__menu_delete_message">Apagar mensagem</string>
<string name="conversation_context__menu_forward_message">Reencaminhar mensagem</string>
<!--conversation_insecure-->
<string name="conversation_insecure__menu_start_secure_session">Iniciar sessão segura</string>
<!--conversation_list_batch-->
<string name="conversation_list_batch__menu_delete_selected">Apagar seleccionado</string>
<string name="conversation_list_batch__menu_select_all">Seleccionar tudo</string>
<!--conversation_list-->
<string name="conversation_list__menu_search">Procurar</string>
<!--conversation_secure_verified-->
<!--conversation_secure_unverified-->
<string name="conversation_secure_verified__menu_security">Segurança</string>
<string name="conversation_secure_verified__menu_verify_session">Verificar sessão</string>
<string name="conversation_secure_verified__menu_verify_recipient">Verificar destinatário</string>
<string name="conversation_secure_verified__menu_abort_secure_session">Abortar sessão segura</string>
<!--conversation-->
<string name="conversation__menu_add_attachment">Adicionar anexo</string>
<string name="conversation__menu_delete_thread">Apagar conversa</string>
<string name="conversation__menu_compare">Comparar</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">Lista de destinatários</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">Comparar</string>
<string name="key_scanning__menu_get_scanned_to_compare">Apresente o código QR para comparar</string>
<string name="key_scanning__menu_scan_to_compare">Leia o código QR para comparar</string>
<!--text_secure_locked-->
<string name="text_secure_locked__menu_unlock">Desbloquear</string>
<!--text_secure_normal-->
<string name="text_secure_normal__menu_new_message">Mensagem nova</string>
<string name="text_secure_normal__menu_settings">Configurações</string>
<string name="text_secure_normal__menu_import_export">Importar/Exportar</string>
<string name="text_secure_normal__menu_import">Importar</string>
<string name="text_secure_normal__menu_export">Exportar</string>
<string name="text_secure_normal__menu_clear_passphrase">Limpar frase-chave</string>
<!--verify_keys-->
<string name="verify_keys__menu_verified">Verificado</string>
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure é umaaplicação de mensagens seguras que funciona como uma alternativa integral à aplicação de mensagens por omissão. Mensagens para outros utilizadores TextSecure são cifradas em trânsito e todas as mensagens são armazenadas numa base de dados cifrada no dispositivo. Se o seu telemóvel for perdido ou roubado, as suas mensages estarão seguras e comunicações com outros utilizadores TextSecure não podem ser monitorizadas em trânsito.</string>
<!--EOF-->
</resources>

View File

@@ -27,6 +27,7 @@
<string name="ApplicationMigrationManager_copy">Copiar</string>
<string name="ApplicationMigrationManager_dont_copy">Não copiar</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">Correntemente: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">Não encontrado!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">Não foi encontrada uma chave de identidade válida para o contacto especificado.</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">Não tem uma chave de identidade!</string>
@@ -35,6 +36,11 @@
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">Necessita de inserir a frase-chave antes de importar chaves...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">Necessita de inserir a frase-chave antes de gerir chaves...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">Ainda não definiu uma frase-chave!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Tamanho máximo de conversa</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">Mensagens por conversa</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Deletar todas mensagens antigas agora?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Tem certeza que deseja manter apenas as %s mensagens mais recentes?</string>
<string name="ApplicationPreferencesActivity_delete">Deletar</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Imagem</string>
<string name="AttachmentTypeSelectorAdapter_video">Vídeo</string>
@@ -72,9 +78,13 @@
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">Destinatário não é um endereço SMS ou email válido!</string>
<string name="ConversationActivity_message_is_empty_exclamation">A mensagem está vazia!</string>
<string name="ConversationActivity_forward_message_prefix">FWD</string>
<string name="ConversationActivity_group_conversation_recipients">Agrupar destinatários de conversa</string>
<string name="ConversationActivity_group_conversation">Conversa em grupo</string>
<string name="ConversationActivity_d_recipients_in_group">%d destinatários no grupo</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Detalhes da mensagem</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Remetente: %1$s⏎ Transporte: %2$s⏎ Enviadas/Recebidas:%3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Transmissor: %1$s\nTransporte: %2$s\nEnviado: %3$s\nRecebido:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">Confirme apagar a mensagem</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Tem a certeza que pretende apagar permanentemente esta mensagem?</string>
<!--ConversationListAdapter-->
@@ -181,6 +191,7 @@
<string name="MmsMessageRecord_decrypting_mms_please_wait">A decifrar MMS, por favor aguarde...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Mensagem MMS cifrada corrompida...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">Mensagem MMS cifrada para sessão inexistente...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">A migrar</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">A migrar mensagens de texto do sistema</string>
@@ -189,8 +200,8 @@
<string name="KeyCachingService_passphrase_cached">Frase-chave guardada em cache</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) novas mensagens</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) novas mensagens, a mais recente de: %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Mais recente de: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Recebeu uma mensagem de alguém que suporta sessões cifradas TextSecure. Deseja iniciar uma sessão segura?</string>
<string name="auto_initiate_activity__initiate_exchange">Iniciar intercâmbio</string>
@@ -209,6 +220,12 @@
<string name="conversation_activity__type_message">Digite a mensagem</string>
<string name="conversation_activity__send">Enviar</string>
<string name="conversation_activity__remove">Remover</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Transferir</string>
<string name="conversation_item_sent__downloading">A transferir</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Transferir</string>
<string name="conversation_item_received__downloading">A transferir</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Modo de selecção em grupo</string>
<!--create_passphrase_activity-->
@@ -309,6 +326,23 @@
<string name="preferences__normal">Normal</string>
<string name="preferences__slow">Lento</string>
<string name="preferences__custom">Personalizado</string>
<string name="preferences__advanced_mms_access_point_names">Avançado: APN de MMS</string>
<string name="preferences__enable_fallback_mmsc">Habilitar MMSC de reseva</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Usar esta informação de MMSC quando APN do sistema estiver indisponível.</string>
<string name="preferences__mmsc_url_required">URL do MMSC (Necessário)</string>
<string name="preferences__mms_proxy_host_optional">Proxy MMS (Opcional)</string>
<string name="preferences__mms_proxy_port_optional">Porta do Proxy MMS (Opcional)</string>
<string name="preferences__delivery_reports">Avisos de entrega</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Pedir um aviso de entrega para cada SMS enviado</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Pedir um aviso de entrega para cada MMS enviado</string>
<string name="preferences__mms_delivery_reports">Avisos de entrega de MMS</string>
<string name="preferences__sms_delivery_reports">Avisos de entrega de SMS</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Automaticamente deletar mensagens mais antigas quando uma conversa exceder o tamanho especificado</string>
<string name="preferences__delete_old_messages">Deletar mensagens antigas</string>
<string name="preferences__storage">Armazenamento</string>
<string name="preferences__conversation_length_limit">Tamanho máximo de conversa</string>
<string name="preferences__trim_all_threads_now">Apagar todas conversas agora</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Verificar todas conversas e aplicar tamanho máximo de conversa</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
@@ -344,6 +378,7 @@
<string name="conversation__menu_delete_thread">Apagar conversa</string>
<string name="conversation__menu_compare">Comparar</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">Lista de destinatários</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">Comparar</string>
<string name="key_scanning__menu_get_scanned_to_compare">Apresente o código QR para comparar</string>
@@ -360,5 +395,6 @@
<!--verify_keys-->
<string name="verify_keys__menu_verified">Verificado</string>
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure é umaaplicação de mensagens seguras que funciona como uma alternativa integral à aplicação de mensagens por omissão. Mensagens para outros utilizadores TextSecure são cifradas em trânsito e todas as mensagens são armazenadas numa base de dados cifrada no dispositivo. Se o seu telemóvel for perdido ou roubado, as suas mensages estarão seguras e comunicações com outros utilizadores TextSecure não podem ser monitorizadas em trânsito.</string>
<!--EOF-->
</resources>

400
res/values-sk/strings.xml Normal file
View File

@@ -0,0 +1,400 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">TextSecure</string>
<string name="yes">Áno</string>
<string name="no">Nie</string>
<string name="delete">Zmaž</string>
<!--ApplicationExportManager-->
<string name="ApplicationExportManager_import_database_and_settings_title">Importovať databázu a nastavenia?</string>
<string name="ApplicationExportManager_import_database_and_settings_message">Importovať TextSecure databázu, kľúče a nastavenia z SD karty?\n\nPOZOR: Akcia zničí existujúce správy, kľúče a nastavenia!</string>
<string name="ApplicationExportManager_importing_database_and_keys">Importujem databázu a kľúče</string>
<string name="ApplicationExportManager_importing_your_sms_database_keys_and_settings">Importujem SMS databázu, kľúče a nastavenia...</string>
<string name="ApplicationExportManager_export_database_question">Exportovať databázu?</string>
<string name="ApplicationExportManager_export_textsecure_database_keys_and_settings_prompt">Exportovať TextSecure databázu, kľúče a nastavenia na SD kartu?</string>
<string name="ApplicationExportManager_exporting_database_and_keys">Exportujem databázu a kľúče</string>
<string name="ApplicationExportManager_exporting_your_sms_database_keys_and_settings">Exportujem SMS databázu, kľúče a nastavenia...</string>
<string name="ApplicationExportManager_no_sd_card_found_exclamation">Nenašiel som SD kartu!</string>
<string name="ApplicationExportManager_error_exporting_to_sd_exclamation">Chyba exportu na SD!</string>
<string name="ApplicationExportManager_import_successful_exclamation">Import úspešný!</string>
<string name="ApplicationExportManager_export_successful_exclamation">Export úspešný!</string>
<string name="ApplicationExportManager_import">Import</string>
<string name="ApplicationExportManager_export">Export</string>
<!--ApplicationMigrationManager-->
<string name="ApplicationMigrationManager_migrating_database">Presúvam databázu</string>
<string name="ApplicationMigrationManager_migrating_text_message_database">Presúvam databázu textových správ...</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_question">Kopírovať systémovú databázu textových správ?</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_explanation">TextSecure používa kryptovanú databázu, nezávislú od štandardnej systémovej databázy. Želáte si prekopírovať správy zo štandardnej systémovej databázy do kryptovanej TextSecure databázy? Štandardná systémová databáza správ zostane nezmenená.</string>
<string name="ApplicationMigrationManager_copy">Kopírovať</string>
<string name="ApplicationMigrationManager_dont_copy">Nekopírovať</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">Aktuálne: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">Nenájdené!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">V špecifikovanom kontakte nebol nájdený platný kľúč totožnosti.</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">Nemáte kľúč totožnosti!</string>
<string name="ApplicationPreferenceActivity_you_have_not_yet_defined_a_contact_for_yourself">Zatiaľ ste si nenastavili svoj kontakt. Vyberte si v Nastaveniach!</string>
<string name="ApplicationPreferenceActivity_exported_to_contacts_database">Exportované do databázy kontaktov!</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">Pred importovaním kľúčov musíte vložiť heslo...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">Pred spravovaním kľúčov musíte vložiť heslo...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">Ešte ste si nenastavili heslo!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Obmedzenie dĺžky konverzácie</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">správ na konverzáciu</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Zmazať všetky staré správy?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Ste si istí, že chcete skrátiť všetky vlákna konverzácií na %s najnovších správ?</string>
<string name="ApplicationPreferencesActivity_delete">Zmaž</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">Obrázok</string>
<string name="AttachmentTypeSelectorAdapter_video">Video</string>
<string name="AttachmentTypeSelectorAdapter_audio">Audio</string>
<!--ConversationItem-->
<string name="ConversationItem_message_size_d_kb">Vwľkosť správy: %d KB</string>
<string name="ConversationItem_expires_s">Vyprší: %s</string>
<string name="ConversationItem_error_sending_message">Chyba pri posielaní správy</string>
<string name="ConversationItem_sending">Posielam...</string>
<string name="ConversationItem_saving_attachment">Ukladám prílohu</string>
<string name="ConversationItem_saving_attachment_to_sd_card">Ukladám prílohu na SD kartu...</string>
<string name="ConversationItem_save_to_sd_card">Uložiť na SD kartu?</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_warning">Tieto údaje sú uložené v kryptovanej databáze. Verzia ktorú ukladáte na SD kartu nebude kryptovaná. Chcete pokračovať?</string>
<string name="ConversationItem_error_while_saving_attachment_to_sd_card">Chyba počas ukladania na SD kartu!</string>
<string name="ConversationItem_success_exclamation">Hotovo!</string>
<string name="ConversationItem_unable_to_write_to_sd_card_exclamation">Nemôžem zapisovať na SD kartu!</string>
<string name="ConversationItem_view_secure_media_question">Zobraziť kryptované údaje?</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning">Tieto údaje sú uložené v kryptovanej databáze. Pre zobrazenie externým prehliadačom budú údaje dekryptované a dočasne zapísané na disk. Ste si istí že chcete pokračovať?</string>
<string name="ConversationItem_key_exchange_message">Správa vzájomnej výmeny kľúčov</string>
<string name="ConversationItem_received_and_processed_key_exchange_message">Prijal som správu vzájomnej výmeny kľúčov a spracovávam ju.</string>
<string name="ConversationItem_error_received_stale_key_exchange_message">Chyba, prijal som zaseknutú správu vzájomnej výmeny kľúčov.</string>
<string name="ConversationItem_received_key_exchange_message_click_to_process">Prijal som správu vzájomnej výmeny kľúčov, kliknite pre spracovanie.</string>
<!--ConversationActivity-->
<string name="ConversationActivity_initiate_secure_session_question">Vytvoriť bezpečnú session?</string>
<string name="ConversationActivity_initiate_secure_session_with_s_question">Vytvoriť bezpečnú session s %s?</string>
<string name="ConversationActivity_abort_secure_session_confirmation">Potvrdenie zrušenia bezpečnej session</string>
<string name="ConversationActivity_are_you_sure_that_you_want_to_abort_this_secure_session_question">Ste si istí, že chcete zrušiť bezpečnú session?</string>
<string name="ConversationActivity_delete_thread_confirmation">Potvrdenie zmazania vlákna</string>
<string name="ConversationActivity_are_you_sure_that_you_want_to_permanently_delete_this_conversation_question">Ste si istí, že chcete natrvalo zmazať túto konveráciu?</string>
<string name="ConversationActivity_add_attachment">Pridať prílohu</string>
<string name="ConversationActivity_compose_message">Vytvoriť správu</string>
<string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">Nastala chyba pri vytváraní prílohy.</string>
<string name="ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions">Vybrané video prekračuje veľkostné obmedzenia správy.</string>
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">Vybrané audio prekračuje veľkostné obmedzenia správy.</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">Príjemca nie je platná SMS alebo emailová adresa!</string>
<string name="ConversationActivity_message_is_empty_exclamation">Správa je prázdna!</string>
<string name="ConversationActivity_forward_message_prefix">FWD</string>
<string name="ConversationActivity_group_conversation_recipients">Príjemcovia skupinovej správy</string>
<string name="ConversationActivity_group_conversation">Skupinová konverzácia</string>
<string name="ConversationActivity_d_recipients_in_group">%d príjemcov v skupine</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">Detaily správy</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Odosielateľ: %1$s\nTransport: %2$s\nOdoslané/Prijaté:%3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Sender: %1$s\nTransport: %2$s\nOdoslané: %3$s\nPrijaté:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">Potvrď zmazanie správy</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Ste si istí že chcete úplne zmazať túto správu?</string>
<!--ConversationListAdapter-->
<string name="ConversationListAdapter_encrypted_message_enter_passphrase">Kryptovaná správa, zadajte heslo...</string>
<string name="ConversationListAdapter_key_exchange_message">Správa vzájomnej výmeny kľúčov...</string>
<!--ConversationListFragment-->
<string name="ConversationListFragment_delete_threads_question">Zmazať vlákna?</string>
<string name="ConversationListFragment_are_you_sure_you_wish_to_delete_all_selected_conversation_threads">Ste si istí, že chcete zmazať VŠETKY označené vlákna konverzácií?</string>
<!--ConversationListItem-->
<string name="ConversationListItem_key_exchange_message">Správa vzájomnej výmeny kľúčov...</string>
<!--KeyScanningActivity-->
<string name="KeyScanningActivity_no_scanned_key_found_exclamation">Nenašiel som scannovaný kľúč!</string>
<!--PassphraseChangeActivity-->
<string name="PassphraseChangeActivity_passphrases_dont_match_exclamation">Heslá sa nezhodujú!</string>
<string name="PassphraseChangeActivity_incorrect_old_passphrase_exclamation">Nesprávne staré heslo!</string>
<!--PassphraseCreateActivity-->
<string name="PassphraseCreateActivity_passphrases_dont_match_exclamation">Heslá sa nezhodujú!</string>
<string name="PassphraseCreateActivity_generating_keypair">Generujem pár kľúčov</string>
<string name="PassphraseCreateActivity_generating_a_local_encryption_keypair">Generujem pár kľúčov na lokálne kryptovanie...</string>
<!--PassphrasePromptActivity-->
<string name="PassphrasePromptActivity_invalid_passphrase_exclamation">Nesprávne heslo!</string>
<!--ReceiveKeyActivity-->
<string name="ReceiveKeyActivity_error_you_have_received_a_corrupted_public_key">CHYBA:\n\nDostali ste poškodený verejný kľúč. Tento kľúč sa nedá použiť. Spustite prosím znova vytvorenie bezpečnej session.</string>
<string name="ReceiveKeyActivity_error_you_have_received_a_public_key_from_an_unsupported_version_of_the_protocol">CHYBA:\n\nDostali ste verejný kľúč nepodporovanej verzie protokolu. Tento kľúč sa nedá použiť. Spustite prosím znova vytvorenie bezpečnej session.</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_does_not_include_an_identity_signature">Táto správa vzájomnej výmeny kľúčov neobsahuje podpis totožnosti.</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_but_you_do_not_yet_trust_it">Táto správa vzájomnej výmeny kľúčov obsahuje podpis totožnosti, ale zatiaľ mu nedôverujete.</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_which_you_trust_for_s">Táto správa vzájomnej výmeny kľúčov obsahuje podpis totožnosti ktorej dôverujete pre: %s</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_sent_to_start_your_current_encrypted_session_with_s">Toto je kľúč ktorý ste poslali na začatie aktuálnej bezpečnej session s %s</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_received_to_start_your_current_encrypted_session_with_s">Toto je kľúč ktorý ste prijali na začatie aktuálnej bezpečnej session s %s</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_warning_you_already_have_an_encrypted_session">Od %s ste prijali správu vzájomnej výmeny kľúčov.\n\nPOZOR: S týmto kontaktom už máte bezpečnú session. Ak akceptujete túto správu vzájomnej výmeny kľúčov, zrušíte tým aktuálnu bezpečnú session a budete sa musieť znova autentifikovať. Chcete naozaj dokončiť túto vzájomnú výmenu kľúčov?</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_previously_initiated">Od %s ste prijali správu vzájomnej výmeny kľúčov. Predtým ste spustili vytvorenie bezpečnej session s týmto kontaktom a akceptovaním tohto kľúča dokončíte vzájomnú výmenu kľúčov. Chcete dokončiť vzájomnú výmenu kľúčov?</string>
<string name="ReceiveKeyActivity_you_have_initiated_a_key_exchange_message_with_s_but_have_not_yet_received_a_reply">Spustili ste vzájomnú výmenu kľúčov s %s, ale zatiaľ ste nedostali odpoveď.</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_no_existing_session">Od %s ste dostali správu vzájomnej výmeny kľúčov. S týmto kontaktom zatiaľ nemáte vytvorenú session, chcete dokončiť túto vzájomnú výmenu kľúčov?</string>
<!--ReviewIdentitiesActivity-->
<string name="ReviewIdentitiesActivity_unable_to_view_corrupted_identity_key_exclamation">Nie je možné zobraziť poškodený kľúč totožnosti!</string>
<string name="ReviewIdentitiesActivity_delete_identity">Zmazať totožnosť?</string>
<string name="ReviewIdentitiesActivity_delete_identity_are_you_sure_you_want_to_delete_this_identity_key">Ste si istí že chcete úplne zmazať tento kľúč totožnosti?</string>
<string name="ReviewIdentitiesActivity_invalid_identity">Neplatná totožnosť!</string>
<!--SaveIdentityActivity-->
<string name="SaveIdentityActivity_you_must_specify_a_name_for_this_identity_exclamation">Musíte špecifikovať meno tejto totožnosti!</string>
<string name="SaveIdentityActivity_identity_name_exists_exclamation">Meno totožnosti už existuje!</string>
<string name="SaveIdentityActivity_an_identity_key_with_the_specified_name_already_exists">Kľúč totožnosti so špecifikovaným menom už existuje.</string>
<string name="SaveIdentityActivity_manage_identities">Správa totožností</string>
<!--VerifyIdentityActivity-->
<string name="VerifyIdentityActivity_mark_identity_verified_question">Označiť totožnosť ako verifikovanú?</string>
<string name="VerifyIdentityActivity_are_you_sure_you_have_validated_the_recipients_identity_fingerprint_and_would_like_to_mark_it_as_verified">Ste si istí že ste overili odtlačok príjemcovej totožnosti a chcete totožnosť označiť ako verifikovanú?</string>
<string name="VerifyIdentityActivity_mark_verified">Označ ako verifikovaná</string>
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">Nemáte kľúč totožnosti.</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">Príjemca nemá kľúč totožnosti.</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key_exclamation">Príjemca nemá kľúč totožnosti!</string>
<string name="VerifyIdentityActivity_scan_their_key_to_compare">Pre porovnanie scannuj ich kľúč</string>
<string name="VerifyIdentityActivity_get_my_key_scanned">Daj scannovať môj kľúč</string>
<string name="VerifyIdentityActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully">POZOR, scannovaný kľúč sa NEZHODUJE! Dôsledne skontrolujte text odtlačku.</string>
<string name="VerifyIdentityActivity_not_verified_exclamation">NIE JE verifikované!</string>
<string name="VerifyIdentityActivity_their_key_is_correct_it_is_also_necessary_to_verify_your_key_with_them_as_well">Ich kľúč je v poriadku. Je tiež potrebné dať druhej strane verifikovať váš kľúč.</string>
<string name="VerifyIdentityActivity_verified_exclamation">Verifikované!</string>
<string name="VerifyIdentityActivity_you_don_t_have_an_identity_key_exclamation">Nemáte kľúč totožnosti!</string>
<!--VerifyImportedIdentityActivity-->
<string name="VerifyImportedIdentityActivity_you_must_specify_a_name_for_this_contact_exclamation">Musíte zadať meno kontaktu!</string>
<string name="VerifyImportedIdentityActivity_save_identity_key_question">Uložiť kľúč totožnosti?</string>
<string name="VerifyImportedIdentityActivity_error_saving_identity_key_exclamation">Chyba pri ukladaní kľúča totožnosti!</string>
<string name="VerifyImportedIdentityActivity_this_identity_key_or_an_identity_key_with_the_same_name_already_exists_please_edit_your_key_database">Tento kľúč totožnosti alebo kľúč totožnosti s rovnakým menom už existuje. Preverte prosím Vašu databázu kľúčov.</string>
<string name="VerifyImportedIdentityActivity_scan_to_compare">Zoscannuj pre porovnanie</string>
<string name="VerifyImportedIdentityActivity_get_scanned_to_compare">Daj zoscannovať pre porovnanie</string>
<string name="VerifyImportedIdentityActivity_not_verified_exclamation">NIE JE verifikované!</string>
<string name="VerifyImportedIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">POZOR, scannovaný kľúč sa NEZHODUJE!</string>
<string name="VerifyImportedIdentityActivity_the_scanned_key_matches_exclamation">Scannované kľúče sú zhodné!</string>
<string name="VerifyImportedIdentityActivity_verified_exclamation">Verifikované!</string>
<string name="VerifyImportedIdentityActivity_are_you_sure_that_you_would_like_to_mark_this_as_a_valid_identity_key_for_all_future_correspondence_with_s">Ste si istí, že to chcete označiť ako platný kľúč totožnosti pre budúcu korešpondenciu s %s? Mali by ste to spraviť, len ak ste už verifikovali odtlačok.</string>
<!--VerifyKeysActivity-->
<string name="VerifyKeysActivity_mark_session_verified_question">Označiť session verifikovanou?</string>
<string name="VerifyKeysActivity_are_you_sure_that_you_have_validated_these_fingerprints_and_would_like_to_mark_this_session_as_verified">Ste si istí že ste porovnali tieto odtlačky a chcete označiť session za verifikovanú?</string>
<string name="VerifyKeysActivity_mark_verified">Označ verifikovanou</string>
<string name="VerifyKeysActivity_get_my_fingerprint_scanned">Daj scannovať môj odtlačok</string>
<string name="VerifyKeysActivity_scan_their_fingerprint">Scannuj ich odtlačok </string>
<string name="VerifyKeysActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully2">POZOR, scannovaný kľúč sa NEZHODUJE! Dôsledne skontrolujte text odtlačku.</string>
<string name="VerifyKeysActivity_not_verified_exclamation">NIE JE verifikované!</string>
<string name="VerifyKeysActivity_their_key_is_correct_it_is_also_necessary_to_get_your_fingerprint_scanned_as_well">Ich kľúč je správny. Je tiež potrebné dať druhej strane scannovať Váš odtlačok.</string>
<string name="VerifyKeysActivity_verified_exclamation">Verifikovaný!</string>
<!--ViewIdentityActivity-->
<string name="ViewIdentityActivity_you_do_not_have_an_identity_key">Nemáte kľúč totožnosti.</string>
<string name="ViewIdentityActivity_scan_to_compare">Zoscannuj pre porovnanie</string>
<string name="ViewIdentityActivity_get_scanned_to_compare">Daj scannovať na porovnanie</string>
<string name="ViewIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">POZOR, scannovaný kľúč sa NEZHODUJE!</string>
<string name="ViewIdentityActivity_not_verified_exclamation">NIE JE verifikované!</string>
<string name="ViewIdentityActivity_the_scanned_key_matches_exclamation">Scannovaný kľúč sa zhoduje!</string>
<string name="ViewIdentityActivity_verified_exclamation">Verifikované!</string>
<!--KeyExchangeInitiator-->
<string name="KeyExchangeInitiator_initiate_despite_existing_request_question">Žiadať napriek už existujúcej požiadavke?</string>
<string name="KeyExchangeInitiator_youve_already_sent_a_session_initiation_request_to_this_recipient_are_you_sure">Tomuto príjemcovi ste už poslali požiadavku na vytvorenie bezpečnej session. Poslanie ďalšej požiadavky zneplatní predchádzajúcu požiadavku. Ste si istí že chcete poslať ďalšiu požiadavku?</string>
<string name="KeyExchangeInitiator_send">Odošli</string>
<!--MessageDisplayHelper-->
<string name="MessageDisplayHelper_bad_encrypted_message">Nesprávne kryptovaná správa...</string>
<string name="MessageDisplayHelper_decrypting_please_wait">Prosím čakaj, dekryptujem...</string>
<string name="MessageDisplayHelper_message_encrypted_for_non_existing_session">Správa zakryptovaná pre neexistujúcu session...</string>
<string name="MessageDisplayHelper_decryption_error_local_message_corrupted_mac_doesn_t_match_potential_tampering_question">Chyba dekryptovania: lokálna správa poškodená, nesúhlasí MAC. Podozrenie z odpočúvania?</string>
<!--MmsDatabase-->
<string name="MmsDatabase_connecting_to_mms_server">Pripájam sa na MMS server...</string>
<string name="MmsDatabase_downloading_mms">Sťahujem MMS...</string>
<string name="MmsDatabase_mms_download_failed">Sťahovanie MMS zlyhalo!</string>
<string name="MmsDatabase_downloading">Sťahujem...</string>
<string name="MmsDatabase_anonymous">Anonymný</string>
<!--MmsMessageRecord-->
<string name="MmsMessageRecord_decrypting_mms_please_wait">Prosím čakaj, dekryptujem MMS...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">Nesprávne kryptovaná MMS správa...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">MMS správa zakryptovaná pre neexistujúcu session...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">Presúvam</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">Presúvam systémové textové správy</string>
<!--KeyCachingService-->
<string name="KeyCachingService_textsecure_passphrase_cached">Heslo pre TextSecure kešované</string>
<string name="KeyCachingService_passphrase_cached">Heslo kešované</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) nových správ</string>
<string name="MessageNotifier_most_recent_from_s">Najnovšia od: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">Dostali ste správu od klienta podporujúceho TextSecure bezpečnú session. Želáte si vytvoriť bezpečnú session?</string>
<string name="auto_initiate_activity__initiate_exchange">Začni vzájomnú výmenu</string>
<!--change_passphrase_activity-->
<string name="change_passphrase_activity__old_passphrase">Staré heslo:</string>
<string name="change_passphrase_activity__new_passphrase">Nové heslo:</string>
<string name="change_passphrase_activity__repeat_new_passphrase">Zopakujte nové heslo:</string>
<!--contact_selection_group_activity-->
<!--contact_selection_list_activity-->
<string name="contact_selection_group_activity__no_contacts">Žiadne kontakty.</string>
<!--ContactSelectionListFragment-->
<string name="ContactSelectionlistFragment_select_for">Vyber pre</string>
<!--contact_selection_recent_activity-->
<string name="contact_selection_recent_activity__no_recent_calls">Žiadne nedávne hovory.</string>
<!--conversation_activity-->
<string name="conversation_activity__type_message">Napísať správu</string>
<string name="conversation_activity__send">Odoslať</string>
<string name="conversation_activity__remove">Odstrániť</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">Stiahnuť</string>
<string name="conversation_item_sent__downloading">Sťahujem</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">Stiahnuť</string>
<string name="conversation_item_received__downloading">Sťahujem</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">Mód hromadného výberu</string>
<!--create_passphrase_activity-->
<string name="create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase">Vyberte si prosím heslo ktoré bude použité na lokálne kryptovanie údajov. Malo by to byť silné heslo.</string>
<string name="create_passphrase_activity__passphrase">Heslo:</string>
<string name="create_passphrase_activity__repeat">Zopakujte:</string>
<!--receive_key_activity-->
<string name="receive_key_activity__session">Session</string>
<string name="receive_key_activity__identities">Identifikácie</string>
<string name="receive_key_activity__complete_exchange">Dokončiť výmenu</string>
<!--recipients_panel-->
<string name="recipients_panel__to">Pre</string>
<!--review_identities-->
<string name="review_identities__you_don_t_currently_have_any_identity_keys_in_your_trust_database">V databáze nemáte žiadne kľúče totožnosti.</string>
<!--save_identity_activity-->
<string name="save_identity_activity__identity_name">Meno totožnosti:</string>
<!--verify_identity_activity-->
<string name="verify_identity_activity__their_identity_they_read">Ich totožnosť (oni vidia):</string>
<string name="verify_identity_activity__your_identity_you_read">Vaša totožnosť (vy vidíte):</string>
<!--verify_import_identity_activity-->
<string name="verify_import_identity_activity__identity_name_n">Meno totožnosti:\\n</string>
<string name="verify_import_identity_activity__imported_identity_n">Importovaná totožnosť:\\n</string>
<string name="verify_import_identity_activity__verified">Verifikované!</string>
<string name="verify_import_identity_activity__compare">Porovnaj</string>
<!--verify_keys_activity-->
<string name="verify_keys_activity__they_read_this">Oni vidia:</string>
<string name="verify_keys_activity__you_read_this">Vy vidíte:</string>
<!--view_identity_activity-->
<string name="view_identity_activity__identity">Totožnosť:</string>
<string name="view_identity_activity__qr_code">QR kód</string>
<!--AndroidManifest.xml-->
<string name="AndroidManifest__create_passphrase">Vytvor heslo</string>
<string name="AndroidManifest__enter_passphrase">Zadaj heslo</string>
<string name="AndroidManifest__select_contacts">Vyber kontakty</string>
<string name="AndroidManifest__textsecure_detected">Detekovaný TextSecure</string>
<string name="AndroidManifest__public_identity_key">Kľúč verejnej totožnosti</string>
<string name="AndroidManifest__change_passphrase">Zmeň heslo</string>
<string name="AndroidManifest__verify_session">Verifikuj session</string>
<string name="AndroidManifest__verify_identity">Verifikuj totožnosť</string>
<string name="AndroidManifest__save_identity">Ulož totožnosť</string>
<string name="AndroidManifest__manage_identity_keys">Spravuj kľúče totožnosti</string>
<string name="AndroidManifest__complete_key_exchange">Vzájomná výmena kľúčov</string>
<string name="AndroidManifest__verify_imported_identity">Over importované totožnosti</string>
<!--preferences.xml-->
<string name="preferences__use_settings">Použi nastavenia</string>
<string name="preferences__pref_all_sms_title">Použi pre všetky SMS</string>
<string name="preferences__pref_all_mms_title">Použi pre všetky MMS</string>
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_text_messages">Použi TextSecure na zobrazenie a uloženie všetkých prichádzajúcich správ</string>
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">Použi TextSecure na zobrazenie a uloženie všetkých prichádzajúcich miltimediálnych správ</string>
<string name="preferences__input_settings">Nastavenia vstupu</string>
<string name="preferences__pref_enter_sends_title">Enter odošle</string>
<string name="preferences__pressing_the_enter_key_will_send_text_messages">Stlačením Enter odošlete textové správy</string>
<string name="preferences__display_settings">Nastavenia zobrazenia</string>
<string name="preferences__choose_identity">Vyberte totožnosť</string>
<string name="preferences__choose_your_contact_entry_from_the_contacts_list">Vyberte Váš kontakt zo zoznamu kontaktov.</string>
<string name="preferences__encryption_settings">Nastavenia kryptovania</string>
<string name="preferences__change_passphrase">Zmeniť heslo</string>
<string name="preferences__change_my_passphrase">Zmeniť moje heslo</string>
<string name="preferences__complete_key_exchanges">Kompletné vzájomné výmeny kľúčov</string>
<string name="preferences__automatically_complete_key_exchanges_for_new_sessions_or_for_existing_sessions_with_the_same_identity_key">Automaticky dokonči vzájomné výmeny kľúčov pre nové session alebo pre existujúce session s rovnakým kľúčom totožnosti.</string>
<string name="preferences__include_a_whitespace_tag_at_the_end_of_every_non_encrypted_message">Na koniec každej nezakryptovanej správy vlož prázdny znak</string>
<string name="preferences__include_whitespace_tag">Vlož prázdny znak</string>
<string name="preferences__sign_key_exchange_messages_with_identity_key">Podpíš správy vzájomnej výmeny kľúča kľúčom totožnosti</string>
<string name="preferences__sign_key_exchange">Výmena podpisových kľúčov</string>
<string name="preferences__forget_passphrase_from_memory_after_some_interval">Po určitom čase zmaž heslo z pamäte</string>
<string name="preferences__timeout_passphrase">Doba zapamätania hesla</string>
<string name="preferences__pref_timeout_interval_dialogtitle">Vyber dobu zapamätania hesla</string>
<string name="preferences__pref_timeout_interval_title">Interval vypršania</string>
<string name="preferences__the_amount_of_time_to_wait_before_forgetting_passphrase">Časový interval po ktorom sa vymaže heslo z pamäte</string>
<string name="preferences__identity_key_settings">Nastavenia kľúča totožnosti</string>
<string name="preferences__view_my_identity_key">Zobraz môj kľúč totožnosti</string>
<string name="preferences__export_my_identity_key">Exportuj môj kľúč totožnosti</string>
<string name="preferences__import_contacts_key">Importuj kľúč kontaktu</string>
<string name="preferences__import_an_identity_key_from_a_contact">Importuj kľúč totožnosti z kontaktu</string>
<string name="preferences__manage_identity_keys">Spravuj kľúče totožnosti</string>
<string name="preferences__manage_configured_identity_keys">Spravuj konfigurované kľúče totožnosti</string>
<string name="preferences__notification_settings">Nastavenia hlásení</string>
<string name="preferences__notifications">Hlásenia</string>
<string name="preferences__display_message_notifications_in_status_bar">Zobrazuj hlásenia o správach v stavovom riadku</string>
<string name="preferences__led_color">Farba LED</string>
<string name="preferences__change_notification_led_color">Zmeň farbu LED hlásení</string>
<string name="preferences__pref_led_blink_title">Vzor blikania LED</string>
<string name="preferences__change_notification_blink_pattern">Zmena vzoru blikania LED hlásení</string>
<string name="preferences__pref_led_blink_dialogtitle">Vyberte vzor blikania LED</string>
<string name="preferences__select_led_color">Vyber farbu LED</string>
<string name="preferences__select_ringtone">Vyber zvonenie</string>
<string name="preferences__vibrate">Vybruj</string>
<string name="preferences__also_vibrate_when_notified">Počas hlásenia tiež vibruj</string>
<string name="preferences__minutes">minúty</string>
<string name="preferences__hours">hodiny</string>
<string name="preferences__green">Zelená</string>
<string name="preferences__red">Červená</string>
<string name="preferences__blue">Modrá</string>
<string name="preferences__orange">Oranžová</string>
<string name="preferences__cyan">Azúrová (cyan)</string>
<string name="preferences__magenta">Purpurová (magenta)</string>
<string name="preferences__fast">Rýchlo</string>
<string name="preferences__normal">Normálne</string>
<string name="preferences__slow">Pomaly</string>
<string name="preferences__custom">Vlastné</string>
<string name="preferences__advanced_mms_access_point_names">Pokročilé: mená prístupových bodov MMS</string>
<string name="preferences__enable_fallback_mmsc">Povoľ fallback MMSC</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Použi nastavenia MMSC ak nie sú dostupné APN informácie.</string>
<string name="preferences__mmsc_url_required">MMSC URL (nutné)</string>
<string name="preferences__mms_proxy_host_optional">MMS Proxy (voliteľné)</string>
<string name="preferences__mms_proxy_port_optional">MMS Proxy port (voliteľné)</string>
<string name="preferences__delivery_reports">Správy o doručení</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Pre každú odoslanú SMS vyžiadaj správu o doručení</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Pre každú odoslanú MMS vyžiadaj správu o doručení</string>
<string name="preferences__mms_delivery_reports">Správy o doručení MMS</string>
<string name="preferences__sms_delivery_reports">Správy o doručení SMS</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Ak dĺžka vlákna presiahne nastavenú hranicu, automaticky zmaž najstaršie správy.</string>
<string name="preferences__delete_old_messages">Zmaž staré správy</string>
<string name="preferences__storage">Úložisko</string>
<string name="preferences__conversation_length_limit">Obmedzenie dĺžky konverzácie</string>
<string name="preferences__trim_all_threads_now">Skráť všetky vlákna</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Skontroluj dĺžku vlákna všetkých konverzácií.</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
<!--contact_selection_list-->
<string name="contact_selection_list__menu_select_all">Označ všetko</string>
<string name="contact_selection_list__menu_unselect_all">Odznač všetko</string>
<!--contact_selection-->
<string name="contact_selection__menu_finished">Hotovo</string>
<!--conversation_button_context-->
<string name="conversation_button_context__menu_send_unencrypted">Pošli nekryptované</string>
<!--conversation_callable-->
<string name="conversation_callable__menu_call">Volaj</string>
<!--conversation_context-->
<string name="conversation_context__menu_message_details">Podrobnosti správy</string>
<string name="conversation_context__menu_copy_text">Kopíruj text</string>
<string name="conversation_context__menu_delete_message">Zmaž správu</string>
<string name="conversation_context__menu_forward_message">Prepošli správu</string>
<!--conversation_insecure-->
<string name="conversation_insecure__menu_start_secure_session">Začni bezpečnú session</string>
<!--conversation_list_batch-->
<string name="conversation_list_batch__menu_delete_selected">Zmaž vybrané</string>
<string name="conversation_list_batch__menu_select_all">Označ všetko</string>
<!--conversation_list-->
<string name="conversation_list__menu_search">Hľadaj</string>
<!--conversation_secure_verified-->
<!--conversation_secure_unverified-->
<string name="conversation_secure_verified__menu_security">Bezpečnosť</string>
<string name="conversation_secure_verified__menu_verify_session">Over session</string>
<string name="conversation_secure_verified__menu_verify_recipient">Over príjemcu</string>
<string name="conversation_secure_verified__menu_abort_secure_session">Zruš bezpečnú session</string>
<!--conversation-->
<string name="conversation__menu_add_attachment">Pridaj prílohu</string>
<string name="conversation__menu_delete_thread">Zmaž vlákno</string>
<string name="conversation__menu_compare">Porovnaj</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">Zoznam príjemcov</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">Porovnaj</string>
<string name="key_scanning__menu_get_scanned_to_compare">Nascannuj pre porovnanie</string>
<string name="key_scanning__menu_scan_to_compare">Zoscannuj pre porovnanie</string>
<!--text_secure_locked-->
<string name="text_secure_locked__menu_unlock">Odomkni</string>
<!--text_secure_normal-->
<string name="text_secure_normal__menu_new_message">Nová správa</string>
<string name="text_secure_normal__menu_settings">Nastavenia</string>
<string name="text_secure_normal__menu_import_export">Import / Export</string>
<string name="text_secure_normal__menu_import">Import</string>
<string name="text_secure_normal__menu_export">Export</string>
<string name="text_secure_normal__menu_clear_passphrase">Vymaž heslo</string>
<!--verify_keys-->
<string name="verify_keys__menu_verified">Overené</string>
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure je aplikácia na bezpečné posielanie textových správ, plne nahradzujúca štandardnú aplikáciu pre prácu so správami. Správy ostatným užívateľom TextSecure sú posielané kryptované. Všetky textové správy sú uložené v kryptovanej databáze Vášho zariadenia. V prípade straty alebo krádeže Vášho zariadenia zostávajú Vaše správy zabezpečené a komunikácia s ostatnými užívateľmi TextSecure nemôže byť počas prenosu monitorovaná.</string>
<!--EOF-->
</resources>

View File

@@ -0,0 +1,377 @@
<?xml version='1.0' encoding='UTF-8'?>
<resources>
<string name="app_name">TextSecure</string>
<string name="yes"></string>
<string name="no"></string>
<string name="delete">删除</string>
<!--ApplicationExportManager-->
<string name="ApplicationExportManager_import_database_and_settings_title">导入数据库和设置?</string>
<string name="ApplicationExportManager_import_database_and_settings_message">从 SD 卡导入 TextSecure 数据库、密钥和设置? 警告: 这将覆盖所有现有消息、密钥和设置!</string>
<string name="ApplicationExportManager_importing_database_and_keys">正在导入数据库和密钥</string>
<string name="ApplicationExportManager_importing_your_sms_database_keys_and_settings">正在导入您的短信数据库、密钥和设置...</string>
<string name="ApplicationExportManager_export_database_question">导出数据库?</string>
<string name="ApplicationExportManager_export_textsecure_database_keys_and_settings_prompt">导出 TextSecure 数据库、密钥和设置到 SD 卡?</string>
<string name="ApplicationExportManager_exporting_database_and_keys">正在导出数据库和密钥</string>
<string name="ApplicationExportManager_exporting_your_sms_database_keys_and_settings">正在导出您的短信数据库、密钥和设置...</string>
<string name="ApplicationExportManager_no_sd_card_found_exclamation">未找到 SD 卡!</string>
<string name="ApplicationExportManager_error_exporting_to_sd_exclamation">导出到 SD 时出现错误!</string>
<string name="ApplicationExportManager_import_successful_exclamation">导入成功!</string>
<string name="ApplicationExportManager_export_successful_exclamation">导出成功!</string>
<string name="ApplicationExportManager_import">导入</string>
<string name="ApplicationExportManager_export">导出</string>
<!--ApplicationMigrationManager-->
<string name="ApplicationMigrationManager_migrating_database">正在迁移数据库</string>
<string name="ApplicationMigrationManager_migrating_text_message_database">正在迁移文本消息数据库...</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_question">复制系统文本消息数据库?</string>
<string name="ApplicationMigrationManager_copy_system_text_message_database_explanation">TextSecure 使用与默认系统数据库分离的加密数据库。 是否要复制现有文本消息到 TextSecure 的加密数据库? 您的默认系统数据库将不受影响。</string>
<string name="ApplicationMigrationManager_copy">复制</string>
<string name="ApplicationMigrationManager_dont_copy">不复制</string>
<!--ApplicationPreferencesActivity-->
<string name="ApplicationPreferencesActivity_currently_s">当前: %s</string>
<string name="ApplicationPreferenceActivity_not_found_exclamation">未找到!</string>
<string name="ApplicationPreferenceActivity_no_valid_identity_key_was_found_in_the_specified_contact">指定联系人未找到有效身份密钥。</string>
<string name="ApplicationPreferenceActivity_you_don_t_have_an_identity_key_exclamation">您没有身份密钥!</string>
<string name="ApplicationPreferenceActivity_you_have_not_yet_defined_a_contact_for_yourself">您还未为自己定义联系人! 在“设置”菜单中设置一个。</string>
<string name="ApplicationPreferenceActivity_exported_to_contacts_database">导出到联系人数据库!</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">您需要在导入密钥之前输入密码...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">您需要在管理密钥之前输入密码...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">您还未设置密码!</string>
<!--AttachmentTypeSelectorAdapter-->
<string name="AttachmentTypeSelectorAdapter_picture">图片</string>
<string name="AttachmentTypeSelectorAdapter_video">视频</string>
<string name="AttachmentTypeSelectorAdapter_audio">音频</string>
<!--ConversationItem-->
<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>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_warning">该媒体已存储在加密数据库中。 您保存到 SD 卡的版本将不再加密,是否继续?</string>
<string name="ConversationItem_error_while_saving_attachment_to_sd_card">将附件保存到 SD 卡时出现错误!</string>
<string name="ConversationItem_success_exclamation">成功!</string>
<string name="ConversationItem_unable_to_write_to_sd_card_exclamation">无法写入 SD 卡!</string>
<string name="ConversationItem_view_secure_media_question">查看安全媒体?</string>
<string name="ConversationItem_this_media_has_been_stored_in_an_encrypted_database_external_viewer_warning">该媒体已存储在加密数据库中。 很遗憾,当前要使用外部内容查看器查看需要将数据临时解密并写入磁盘。 是否确定要这么做?</string>
<string name="ConversationItem_key_exchange_message">密钥交换信息</string>
<string name="ConversationItem_received_and_processed_key_exchange_message">已接收和处理过的密钥交换信息</string>
<string name="ConversationItem_error_received_stale_key_exchange_message">错误,收到旧密钥交换消息。</string>
<string name="ConversationItem_received_key_exchange_message_click_to_process">已收到并处理完密钥交换消息。</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>
<string name="ConversationActivity_compose_message">编辑邮件</string>
<string name="ConversationActivity_sorry_there_was_an_error_setting_your_attachment">很抱歉,设置附件时出现错误。</string>
<string name="ConversationActivity_sorry_the_selected_video_exceeds_message_size_restrictions">很抱歉,选定视频超过邮件大小限制。</string>
<string name="ConversationActivity_sorry_the_selected_audio_exceeds_message_size_restrictions">很抱歉,选定音频超过邮件大小限制。</string>
<string name="ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation">收件人不是有效的短信或电子邮件地址!</string>
<string name="ConversationActivity_message_is_empty_exclamation">邮件为空!</string>
<string name="ConversationActivity_forward_message_prefix">转发</string>
<string name="ConversationActivity_group_conversation_recipients">多人对话接收人</string>
<string name="ConversationActivity_group_conversation">对人对话</string>
<string name="ConversationActivity_d_recipients_in_group">小组中有 %d 个接收人</string>
<!--ConversationFragment-->
<string name="ConversationFragment_message_details">邮件详细信息</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">发件人: %1$s\n传输 %2$s\n已发送/接收:%3$s</string>
<string name="ConversationFragment_confirm_message_delete">确认邮件删除</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">是否确定要永久删除此邮件?</string>
<!--ConversationListAdapter-->
<string name="ConversationListAdapter_encrypted_message_enter_passphrase">加密邮件,输入密码... </string>
<string name="ConversationListAdapter_key_exchange_message">密钥交换消息...</string>
<!--ConversationListFragment-->
<string name="ConversationListFragment_delete_threads_question">删除线程?</string>
<string name="ConversationListFragment_are_you_sure_you_wish_to_delete_all_selected_conversation_threads">是否确定要删除所有选定对话线程?</string>
<!--ConversationListItem-->
<string name="ConversationListItem_key_exchange_message">密钥交换消息...</string>
<!--KeyScanningActivity-->
<string name="KeyScanningActivity_no_scanned_key_found_exclamation">未找到已扫描密钥!</string>
<!--PassphraseChangeActivity-->
<string name="PassphraseChangeActivity_passphrases_dont_match_exclamation">密码不匹配!</string>
<string name="PassphraseChangeActivity_incorrect_old_passphrase_exclamation">旧密码不正确!</string>
<!--PassphraseCreateActivity-->
<string name="PassphraseCreateActivity_passphrases_dont_match_exclamation">密码不匹配!</string>
<string name="PassphraseCreateActivity_generating_keypair">正在生成密钥对</string>
<string name="PassphraseCreateActivity_generating_a_local_encryption_keypair">正在生成本地加密密钥对...</string>
<!--PassphrasePromptActivity-->
<string name="PassphrasePromptActivity_invalid_passphrase_exclamation">无效密码!</string>
<!--ReceiveKeyActivity-->
<string name="ReceiveKeyActivity_error_you_have_received_a_corrupted_public_key">错误:\n\n您收到的公钥已损坏。 该密钥无法处理,请重新启动安全会话。</string>
<string name="ReceiveKeyActivity_error_you_have_received_a_public_key_from_an_unsupported_version_of_the_protocol">错误:\n\n您收到来自不支持协议版本的公钥。 该密钥无法处理,请重新启动安全会话。</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_does_not_include_an_identity_signature">该密钥交换消息不包括身份签名。</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_but_you_do_not_yet_trust_it">该密钥交换消息包含身份签名,但是您还未信任它。</string>
<string name="ReceiveKeyActivity_this_key_exchange_message_includes_an_identity_signature_which_you_trust_for_s">该密钥交换消息包含您信任的身份签名: %s</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_sent_to_start_your_current_encrypted_session_with_s">这是您发送以启动与 %s 的当前加密会话的密钥</string>
<string name="ReceiveKeyActivity_this_is_the_key_that_you_received_to_start_your_current_encrypted_session_with_s">这是您收到以启动与 %s 的当前加密会话的密钥</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_warning_you_already_have_an_encrypted_session">您收到来自 %s 的密钥交换消息。\n\n警告 您正与该联系人进行加密会话。 如果选择接收该密钥交换消息,这将中断现有会话并对您进行重新身份验证。 是否要完成此密钥交换?</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_previously_initiated">您收到来自 %s 的密钥交换消息。您之前已启动与该联系人的会话,访问该密钥后,您将完成密钥交换。 是否要完成此密钥交换?</string>
<string name="ReceiveKeyActivity_you_have_initiated_a_key_exchange_message_with_s_but_have_not_yet_received_a_reply">您已经收到来自 %s 的密钥交换消息。您与该联系人当前没有会话,是否要完成该密钥交换?</string>
<string name="ReceiveKeyActivity_you_have_received_a_key_exchange_message_from_s_you_have_no_existing_session">您已经收到来自 %s 的密钥交换消息。您与该联系人当前没有会话,是否要完成该密钥交换?</string>
<!--ReviewIdentitiesActivity-->
<string name="ReviewIdentitiesActivity_unable_to_view_corrupted_identity_key_exclamation">无法查看已损坏的身份密钥!</string>
<string name="ReviewIdentitiesActivity_delete_identity">删除身份?</string>
<string name="ReviewIdentitiesActivity_delete_identity_are_you_sure_you_want_to_delete_this_identity_key">是否确定要永久删除此身份密钥?</string>
<string name="ReviewIdentitiesActivity_invalid_identity">无效身份!</string>
<!--SaveIdentityActivity-->
<string name="SaveIdentityActivity_you_must_specify_a_name_for_this_identity_exclamation">您必须为此身份指定一个名称!</string>
<string name="SaveIdentityActivity_identity_name_exists_exclamation">身份名称已存在!</string>
<string name="SaveIdentityActivity_an_identity_key_with_the_specified_name_already_exists">带指定名称的身份密钥已经存在。</string>
<string name="SaveIdentityActivity_manage_identities">管理身份</string>
<!--VerifyIdentityActivity-->
<string name="VerifyIdentityActivity_mark_identity_verified_question">标记身份为已验证?</string>
<string name="VerifyIdentityActivity_are_you_sure_you_have_validated_the_recipients_identity_fingerprint_and_would_like_to_mark_it_as_verified">是否确定已验证收件人的身份指纹并标记为已验证?</string>
<string name="VerifyIdentityActivity_mark_verified">标记为已验证</string>
<string name="VerifyIdentityActivity_you_do_not_have_an_identity_key">您没有身份密钥。</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key">收件人没有身份密钥。</string>
<string name="VerifyIdentityActivity_recipient_has_no_identity_key_exclamation">收件人没有身份密钥!</string>
<string name="VerifyIdentityActivity_scan_their_key_to_compare">扫描他们的密钥以进行比较</string>
<string name="VerifyIdentityActivity_get_my_key_scanned">扫描我的密钥</string>
<string name="VerifyIdentityActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully">警告,扫描的密钥不匹配! 请仔细检查指纹文本。</string>
<string name="VerifyIdentityActivity_not_verified_exclamation">未验证!</string>
<string name="VerifyIdentityActivity_their_key_is_correct_it_is_also_necessary_to_verify_your_key_with_them_as_well">他们的密钥正确。 还需要他们验证您的密钥。</string>
<string name="VerifyIdentityActivity_verified_exclamation">已验证!</string>
<string name="VerifyIdentityActivity_you_don_t_have_an_identity_key_exclamation">您没有身份密钥!</string>
<!--VerifyImportedIdentityActivity-->
<string name="VerifyImportedIdentityActivity_you_must_specify_a_name_for_this_contact_exclamation">您必须为此联系人指定一个名称!</string>
<string name="VerifyImportedIdentityActivity_save_identity_key_question">保存身份密钥?</string>
<string name="VerifyImportedIdentityActivity_error_saving_identity_key_exclamation">保存身份密钥时出现错误!</string>
<string name="VerifyImportedIdentityActivity_this_identity_key_or_an_identity_key_with_the_same_name_already_exists_please_edit_your_key_database">此身份密钥或具有相同名称的身份密钥已经存在。 请编辑您的密钥数据库。</string>
<string name="VerifyImportedIdentityActivity_scan_to_compare">扫描进行比较</string>
<string name="VerifyImportedIdentityActivity_get_scanned_to_compare">进行扫描以比较</string>
<string name="VerifyImportedIdentityActivity_not_verified_exclamation">未验证!</string>
<string name="VerifyImportedIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">警告,扫描的密钥不匹配!</string>
<string name="VerifyImportedIdentityActivity_the_scanned_key_matches_exclamation">扫描的密钥匹配!</string>
<string name="VerifyImportedIdentityActivity_verified_exclamation">已验证!</string>
<string name="VerifyImportedIdentityActivity_are_you_sure_that_you_would_like_to_mark_this_as_a_valid_identity_key_for_all_future_correspondence_with_s">是否确定要标记这为有效身份密钥,以用于与 %s 的所有未来通信? 您应仅在实际验证过指纹后这么做。</string>
<!--VerifyKeysActivity-->
<string name="VerifyKeysActivity_mark_session_verified_question">标记会话已验证?</string>
<string name="VerifyKeysActivity_are_you_sure_that_you_have_validated_these_fingerprints_and_would_like_to_mark_this_session_as_verified">是否确定要验证这些指纹并标记此会话为已验证?</string>
<string name="VerifyKeysActivity_mark_verified">标记为已验证</string>
<string name="VerifyKeysActivity_get_my_fingerprint_scanned">扫描我的指纹</string>
<string name="VerifyKeysActivity_scan_their_fingerprint">扫描他们的指纹</string>
<string name="VerifyKeysActivity_warning_the_scanned_key_does_not_match_please_check_the_fingerprint_text_carefully2">警告,扫描的密钥不匹配! 请仔细检查指纹文本。</string>
<string name="VerifyKeysActivity_not_verified_exclamation">未验证!</string>
<string name="VerifyKeysActivity_their_key_is_correct_it_is_also_necessary_to_get_your_fingerprint_scanned_as_well">他们的密钥正确。 还需要扫描您的指纹。</string>
<string name="VerifyKeysActivity_verified_exclamation">已验证!</string>
<!--ViewIdentityActivity-->
<string name="ViewIdentityActivity_you_do_not_have_an_identity_key">您没有身份密钥。</string>
<string name="ViewIdentityActivity_scan_to_compare">扫描进行比较</string>
<string name="ViewIdentityActivity_get_scanned_to_compare">进行扫描以进行比较</string>
<string name="ViewIdentityActivity_warning_the_scanned_key_does_not_match_exclamation">警告,扫描的密钥不匹配!</string>
<string name="ViewIdentityActivity_not_verified_exclamation">未验证!</string>
<string name="ViewIdentityActivity_the_scanned_key_matches_exclamation">扫描的密钥匹配!</string>
<string name="ViewIdentityActivity_verified_exclamation">已验证!</string>
<!--KeyExchangeInitiator-->
<string name="KeyExchangeInitiator_initiate_despite_existing_request_question">启动 Despite Existing Request</string>
<string name="KeyExchangeInitiator_youve_already_sent_a_session_initiation_request_to_this_recipient_are_you_sure">您已经发送会话启动请求到此收件人,是否确定要再次发送? 这将使第一个请求无效。</string>
<string name="KeyExchangeInitiator_send">发送</string>
<!--MessageDisplayHelper-->
<string name="MessageDisplayHelper_bad_encrypted_message">加密消息错误...</string>
<string name="MessageDisplayHelper_decrypting_please_wait">正在解密,请稍后...</string>
<string name="MessageDisplayHelper_message_encrypted_for_non_existing_session">已为不存在的会话加密消息...</string>
<string name="MessageDisplayHelper_decryption_error_local_message_corrupted_mac_doesn_t_match_potential_tampering_question">解密错误: 本地消息已损坏MAC 不匹配。 潜在篡改?</string>
<!--MmsDatabase-->
<string name="MmsDatabase_connecting_to_mms_server">正在连接到彩信服务器...</string>
<string name="MmsDatabase_downloading_mms">正在下载彩信...</string>
<string name="MmsDatabase_mms_download_failed">彩信下载失败!</string>
<string name="MmsDatabase_downloading">正在下载...</string>
<string name="MmsDatabase_anonymous">匿名</string>
<!--MmsMessageRecord-->
<string name="MmsMessageRecord_decrypting_mms_please_wait">正在解密彩信,请稍后...</string>
<string name="MmsMessageRecord_bad_encrypted_mms_message">加密彩信消息错误...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">已为不存在的会话加密彩信消息...</string>
<!--MmsSender-->
<!--ApplicationMigrationService-->
<string name="ApplicationMigrationService_migrating">正在迁移</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">正在迁移系统文本消息</string>
<!--KeyCachingService-->
<string name="KeyCachingService_textsecure_passphrase_cached">TextSecure 密码已缓存</string>
<string name="KeyCachingService_passphrase_cached">密码已缓存</string>
<!--MessageNotifier-->
<string name="MessageNotifier_d_new_messages">(%d) 新邮件</string>
<string name="MessageNotifier_most_recent_from_s">最新邮件来自: %s</string>
<!--SmsReceiver-->
<!--auto_initiate_activity-->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">您收到一条支持 TextSecure 加密会话的人发送的消息。 是否要启动安全会话?</string>
<string name="auto_initiate_activity__initiate_exchange">启动交换</string>
<!--change_passphrase_activity-->
<string name="change_passphrase_activity__old_passphrase">旧密码:</string>
<string name="change_passphrase_activity__new_passphrase">新密码:</string>
<string name="change_passphrase_activity__repeat_new_passphrase">重复新密码:</string>
<!--contact_selection_group_activity-->
<!--contact_selection_list_activity-->
<string name="contact_selection_group_activity__no_contacts">没有联系人。</string>
<!--ContactSelectionListFragment-->
<string name="ContactSelectionlistFragment_select_for">选择</string>
<!--contact_selection_recent_activity-->
<string name="contact_selection_recent_activity__no_recent_calls">没有最近呼叫。</string>
<!--conversation_activity-->
<string name="conversation_activity__type_message">输入消息</string>
<string name="conversation_activity__send">发送</string>
<string name="conversation_activity__remove">删除</string>
<!--conversation_item_sent-->
<string name="conversation_item_sent__download">下载</string>
<string name="conversation_item_sent__downloading">正在下载</string>
<!--conversation_item_received-->
<string name="conversation_item_received__download">下载</string>
<string name="conversation_item_received__downloading">正在下载</string>
<!--conversation_fragment_cab-->
<string name="conversation_fragment_cab__batch_selection_mode">批量选择模式</string>
<!--create_passphrase_activity-->
<string name="create_passphrase_activity__please_choose_a_passphrase_that_will_be_used_to_locally_encrypt_your_data_this_should_be_a_strong_passphrase">请选择用于本地加密数据的密码。 这应是强密码。</string>
<string name="create_passphrase_activity__passphrase">密码:</string>
<string name="create_passphrase_activity__repeat">重复:</string>
<!--receive_key_activity-->
<string name="receive_key_activity__session">会话</string>
<string name="receive_key_activity__identities">身份</string>
<string name="receive_key_activity__complete_exchange">完成交换</string>
<!--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>
<!--save_identity_activity-->
<string name="save_identity_activity__identity_name">身份名称:</string>
<!--verify_identity_activity-->
<string name="verify_identity_activity__their_identity_they_read">他们的身份(他们读取):</string>
<string name="verify_identity_activity__your_identity_you_read">您的身份(您读取):</string>
<!--verify_import_identity_activity-->
<string name="verify_import_identity_activity__identity_name_n">身份名称:\</string>
<string name="verify_import_identity_activity__imported_identity_n">导入的身份:\</string>
<string name="verify_import_identity_activity__verified">已验证!</string>
<string name="verify_import_identity_activity__compare">比较</string>
<!--verify_keys_activity-->
<string name="verify_keys_activity__they_read_this">他们读取这个:</string>
<string name="verify_keys_activity__you_read_this">您读取这个:</string>
<!--view_identity_activity-->
<string name="view_identity_activity__identity">身份:</string>
<string name="view_identity_activity__qr_code">QR 代码</string>
<!--AndroidManifest.xml-->
<string name="AndroidManifest__create_passphrase">创建密码</string>
<string name="AndroidManifest__enter_passphrase">输入密码</string>
<string name="AndroidManifest__select_contacts">选择联系人</string>
<string name="AndroidManifest__textsecure_detected">检测到 TextSecure</string>
<string name="AndroidManifest__public_identity_key">身份公钥</string>
<string name="AndroidManifest__change_passphrase">更改密码</string>
<string name="AndroidManifest__verify_session">验证会话</string>
<string name="AndroidManifest__verify_identity">验证身份</string>
<string name="AndroidManifest__save_identity">保存身份</string>
<string name="AndroidManifest__manage_identity_keys">管理身份密钥</string>
<string name="AndroidManifest__complete_key_exchange">完成密钥交换</string>
<string name="AndroidManifest__verify_imported_identity">验证已导入身份</string>
<!--preferences.xml-->
<string name="preferences__use_settings">使用设置</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">使用 TextSecure 查看和存储所有入站文本消息</string>
<string name="preferences__use_textsecure_for_viewing_and_storing_all_incoming_multimedia_messages">使用 TextSecure 查看和存储所有入站多媒体消息</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">按下 enter 键将发送文本消息</string>
<string name="preferences__display_settings">显示设置</string>
<string name="preferences__choose_identity">选择身份</string>
<string name="preferences__choose_your_contact_entry_from_the_contacts_list">从联系人列表选择您的联系人条目。</string>
<string name="preferences__encryption_settings">加密设置</string>
<string name="preferences__change_passphrase">更改密码</string>
<string name="preferences__change_my_passphrase">更改我的密码</string>
<string name="preferences__complete_key_exchanges">完成密钥交换</string>
<string name="preferences__automatically_complete_key_exchanges_for_new_sessions_or_for_existing_sessions_with_the_same_identity_key">使用同一身份密钥自动完成新会话或现有会话的密钥交换</string>
<string name="preferences__include_a_whitespace_tag_at_the_end_of_every_non_encrypted_message">在每个非加密消息末尾包含空白标签</string>
<string name="preferences__include_whitespace_tag">包含空白标签</string>
<string name="preferences__sign_key_exchange_messages_with_identity_key">使用身份密钥签名密钥交换消息</string>
<string name="preferences__sign_key_exchange">签名密钥交换</string>
<string name="preferences__forget_passphrase_from_memory_after_some_interval">在一段时间后忘记内存中的密码</string>
<string name="preferences__timeout_passphrase">超时密码</string>
<string name="preferences__pref_timeout_interval_dialogtitle">选择密码超时</string>
<string name="preferences__pref_timeout_interval_title">超时间隔</string>
<string name="preferences__the_amount_of_time_to_wait_before_forgetting_passphrase">忘记内存中的密码前等待的时间长度</string>
<string name="preferences__identity_key_settings">身份密钥设置</string>
<string name="preferences__view_my_identity_key">查看我的身份密钥</string>
<string name="preferences__export_my_identity_key">导出我的身份密钥</string>
<string name="preferences__import_contacts_key">导入联系人密钥</string>
<string name="preferences__import_an_identity_key_from_a_contact">从联系人导入身份密钥</string>
<string name="preferences__manage_identity_keys">管理身份密钥</string>
<string name="preferences__manage_configured_identity_keys">管理配置的身份密钥</string>
<string name="preferences__notification_settings">通知设置</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__change_notification_led_color">更改通知 LED 颜色</string>
<string name="preferences__pref_led_blink_title">LED 闪烁模式</string>
<string name="preferences__change_notification_blink_pattern">更改通知 LED 闪烁模式</string>
<string name="preferences__pref_led_blink_dialogtitle">选择 LED 闪烁模式</string>
<string name="preferences__select_led_color">选择 LED 颜色</string>
<string name="preferences__select_ringtone">选择铃声</string>
<string name="preferences__vibrate">振动</string>
<string name="preferences__also_vibrate_when_notified">通知时同时振动</string>
<string name="preferences__minutes">分钟</string>
<string name="preferences__hours">小时</string>
<string name="preferences__green">绿色</string>
<string name="preferences__red">红色</string>
<string name="preferences__blue">蓝色</string>
<string name="preferences__orange">橙色</string>
<string name="preferences__cyan">青色</string>
<string name="preferences__magenta">洋红色</string>
<string name="preferences__fast">快速</string>
<string name="preferences__normal">正常</string>
<string name="preferences__slow">缓慢</string>
<string name="preferences__custom">自定义</string>
<!--****************************************-->
<!--menus-->
<!--****************************************-->
<!--contact_selection_list-->
<string name="contact_selection_list__menu_select_all">选择所有</string>
<string name="contact_selection_list__menu_unselect_all">取消选择所有</string>
<!--contact_selection-->
<string name="contact_selection__menu_finished">已完成</string>
<!--conversation_button_context-->
<string name="conversation_button_context__menu_send_unencrypted">发送未加密</string>
<!--conversation_callable-->
<string name="conversation_callable__menu_call">呼叫</string>
<!--conversation_context-->
<string name="conversation_context__menu_message_details">邮件详细信息</string>
<string name="conversation_context__menu_copy_text">复制文本</string>
<string name="conversation_context__menu_delete_message">删除邮件</string>
<string name="conversation_context__menu_forward_message">转发邮件</string>
<!--conversation_insecure-->
<string name="conversation_insecure__menu_start_secure_session">启动安全会话</string>
<!--conversation_list_batch-->
<string name="conversation_list_batch__menu_delete_selected">删除选定项</string>
<string name="conversation_list_batch__menu_select_all">选择所有</string>
<!--conversation_list-->
<string name="conversation_list__menu_search">搜索</string>
<!--conversation_secure_verified-->
<!--conversation_secure_unverified-->
<string name="conversation_secure_verified__menu_security">安全性</string>
<string name="conversation_secure_verified__menu_verify_session">验证会话</string>
<string name="conversation_secure_verified__menu_verify_recipient">验证收件人</string>
<string name="conversation_secure_verified__menu_abort_secure_session">中止安全会话</string>
<!--conversation-->
<string name="conversation__menu_add_attachment">添加附件</string>
<string name="conversation__menu_delete_thread">删除线程</string>
<string name="conversation__menu_compare">比较</string>
<!--conversation_group_options-->
<string name="convesation_group_options__recipients_list">接收人列表</string>
<!--key_scanning-->
<string name="key_scanning__menu_compare">比较</string>
<string name="key_scanning__menu_get_scanned_to_compare">进行扫描以进行比较</string>
<string name="key_scanning__menu_scan_to_compare">扫描进行比较</string>
<!--text_secure_locked-->
<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_settings">设置</string>
<string name="text_secure_normal__menu_import_export">导入/导出</string>
<string name="text_secure_normal__menu_import">导入</string>
<string name="text_secure_normal__menu_export">导出</string>
<string name="text_secure_normal__menu_clear_passphrase">清除密码</string>
<!--verify_keys-->
<string name="verify_keys__menu_verified">已验证</string>
<!--Misc. piggybacking-->
<string name="PlayStoreListing">TextSecure 是一个安全增强文本消息应用程序,用于完全替代默认文本消息应用程序。 无线发送给其他 TextSecure 用户的消息都经过加密,同时所有文本消息都存储在设备的加密数据库中。 如果电话丢失或被盗,您的消息将保持安全,同时无法空中监控与其他 TextSecure 用户的无线通信。</string>
<!--EOF-->
</resources>

View File

@@ -2,4 +2,7 @@
<resources>
<drawable name="white_background">#ffffffff</drawable>
<drawable name="text_color_black">#ff000000</drawable>
<color name="unread_bgcolor">#ffffffff</color>
<color name="read_bgcolor">#ffeeeeee</color>
</resources>

View File

@@ -39,7 +39,12 @@
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_importing_keys">You need to have entered your passphrase before importing keys...</string>
<string name="ApplicationPreferenceActivity_you_need_to_have_entered_your_passphrase_before_managing_keys">You need to have entered your passphrase before managing keys...</string>
<string name="ApplicationPreferenceActivity_you_havent_set_a_passphrase_yet">You haven\'t set a passphrase yet!</string>
<string name="ApplicationPreferencesActivity_conversation_length_limit">Conversation length limit</string>
<string name="ApplicationPreferencesActivity_messages_per_conversation">messages per conversation</string>
<string name="ApplicationPreferencesActivity_delete_all_old_messages_now">Delete all old messages now?</string>
<string name="ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages">Are you sure you would like to immediately trim all conversation threads to the %s most recent messages?</string>
<string name="ApplicationPreferencesActivity_delete">Delete</string>
<!-- AttachmentTypeSelectorAdapter -->
<string name="AttachmentTypeSelectorAdapter_picture">Picture</string>
@@ -83,10 +88,12 @@
<string name="ConversationActivity_group_conversation_recipients">Group Conversation Recipients</string>
<string name="ConversationActivity_group_conversation">Group Conversation</string>
<string name="ConversationActivity_d_recipients_in_group">%d recipients in group</string>
<string name="ConversationActivity_saving_draft">Saving draft...</string>
<!-- ConversationFragment -->
<string name="ConversationFragment_message_details">Message details</string>
<string name="ConversationFragment_sender_s_transport_s_sent_received_s">Sender: %1$s\nTransport: %2$s\nSent/Received:%3$s</string>
<string name="ConversationFragment_sender_s_transport_s_sent_s_received_s">Sender: %1$s\nTransport: %2$s\nSent: %3$s\nReceived:%4$s</string>
<string name="ConversationFragment_confirm_message_delete">Confirm Message Delete</string>
<string name="ConversationFragment_are_you_sure_you_want_to_permanently_delete_this_message">Are you sure that you want to permanently delete this message?</string>
@@ -97,6 +104,8 @@
<!-- ConversationListFragment -->
<string name="ConversationListFragment_delete_threads_question">Delete threads?</string>
<string name="ConversationListFragment_are_you_sure_you_wish_to_delete_all_selected_conversation_threads">Are you sure you wish to delete ALL selected conversation threads?</string>
<string name="ConversationListFragment_deleting">Deleting</string>
<string name="ConversationListFragment_deleting_selected_threads">Deleting selected threads...</string>
<!-- ConversationListItem -->
<string name="ConversationListItem_key_exchange_message">Key exchange message...</string>
@@ -199,7 +208,7 @@
<string name="MessageDisplayHelper_decrypting_please_wait">Decrypting, please wait...</string>
<string name="MessageDisplayHelper_message_encrypted_for_non_existing_session">Message encrypted for non-existing session...</string>
<string name="MessageDisplayHelper_decryption_error_local_message_corrupted_mac_doesn_t_match_potential_tampering_question">Decryption error: local message corrupted, MAC doesn\'t match. Potential tampering?</string>
<!-- MmsDatabase -->
<string name="MmsDatabase_connecting_to_mms_server">Connecting to MMS server...</string>
<string name="MmsDatabase_downloading_mms">Downloading MMS...</string>
@@ -212,6 +221,9 @@
<string name="MmsMessageRecord_bad_encrypted_mms_message">Bad encrypted MMS message...</string>
<string name="MmsMessageRecord_mms_message_encrypted_for_non_existing_session">MMS message encrypted for non-existing session...</string>
<!-- MmsSender -->
<string name="MmsSender_currently_unable_to_send_your_mms_message">Currently unable to send your MMS message. It will be sent once service becomes available.</string>
<!-- ApplicationMigrationService -->
<string name="ApplicationMigrationService_migrating">Migrating</string>
<string name="ApplicationMigrationService_migrating_system_text_messages">Migrating System Text Messages</string>
@@ -221,9 +233,18 @@
<string name="KeyCachingService_passphrase_cached">Passphrase Cached</string>
<!-- MessageNotifier -->
<string name="MessageNotifier_d_new_messages">(%d) New messages</string>
<string name="MessageNotifier_d_new_messages_most_recent_from_s">(%1$d) New messages, most recent from: %2$s</string>
<string name="MessageNotifier_most_recent_from_s">Most recent from: %s</string>
<string name="MessageNotifier_d_new_messages">%d new messages</string>
<string name="MessageNotifier_most_recent_from_s">Most recent from: %s</string>
<string name="MessageNotifier_key_exchange">Key exchange...</string>
<string name="MessageNotifier_encrypted_message">Encrypted message...</string>
<string name="MessageNotifier_corrupted_ciphertext">Corrupted ciphertext</string>
<string name="MessageNotifier_no_subject">(No Subject)</string>
<string name="MessageNotifier_message_delivery_failed">Message delivery failed.</string>
<string name="MessageNotifier_failed_to_deliver_message">Failed to deliver message.</string>
<string name="MessageNotifier_error_delivering_message">Error delivering message.</string>
<!-- SmsReceiver -->
<string name="SmsReceiver_currently_unable_to_send_your_sms_message">Currently unable to send your SMS message. It will be sent once service becomes available.</string>
<!-- auto_initiate_activity -->
<string name="auto_initiate_activity__you_have_received_a_message_from_someone_who_supports_textsecure_encrypted_sessions_would_you_like_to_initiate_a_secure_session">You have received a message from someone who supports TextSecure encrypted sessions. Would you like to initiate a secure session?</string>
@@ -249,6 +270,14 @@
<string name="conversation_activity__send">Send</string>
<string name="conversation_activity__remove">Remove</string>
<!-- conversation_item_sent -->
<string name="conversation_item_sent__download">Download</string>
<string name="conversation_item_sent__downloading">Downloading</string>
<!-- conversation_item_received -->
<string name="conversation_item_received__download">Download</string>
<string name="conversation_item_received__downloading">Downloading</string>
<!-- conversation_fragment_cab -->
<string name="conversation_fragment_cab__batch_selection_mode">Batch Selection Mode</string>
@@ -362,6 +391,24 @@
<string name="preferences__normal">Normal</string>
<string name="preferences__slow">Slow</string>
<string name="preferences__custom">Custom</string>
<string name="preferences__advanced_mms_access_point_names">Advanced: MMS Access Point Names</string>
<string name="preferences__enable_fallback_mmsc">Enable Fallback MMSC</string>
<string name="preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable">Use MMSC information configured here when system APN information is unavailable.</string>
<string name="preferences__mmsc_url_required">MMSC URL (Required)</string>
<string name="preferences__mms_proxy_host_optional">MMS Proxy Host (Optional)</string>
<string name="preferences__mms_proxy_port_optional">MMS Proxy Port (Optional)</string>
<string name="preferences__delivery_reports">Delivery Reports</string>
<string name="preferences__request_a_delivery_report_for_each_sms_message_you_send">Request a delivery report for each SMS message you send</string>
<string name="preferences__request_a_delivery_report_for_each_mms_message_you_send">Request a delivery report for each MMS message you send</string>
<string name="preferences__mms_delivery_reports">MMS delivery reports</string>
<string name="preferences__sms_delivery_reports">SMS delivery reports</string>
<string name="preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length">Automatically delete older messages once a conversation thread exceeds a specified length</string>
<string name="preferences__delete_old_messages">Delete old messages</string>
<string name="preferences__storage">Storage</string>
<string name="preferences__conversation_length_limit">Conversation length limit</string>
<string name="preferences__trim_all_threads_now">Trim all threads now</string>
<string name="preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits">Scan through all conversation threads and enforce conversation length limits</string>
<!-- **************************************** -->
<!-- menus -->
@@ -434,9 +481,5 @@
<string name="PlayStoreListing">TextSecure is a security enhanced text messaging application that serves as a full replacement for the default text messaging application. Messages to other TextSecure users are encrypted over the air, and all text messages are stored in an encrypted database on the device. If your phone is lost or stolen, your messages will be safe, and communication with other TextSecure users can\'t be monitored over the air.</string>
<!-- EOF -->
<string name="conversation_item_sent__download">Download</string>
<string name="conversation_item_sent__downloading">Downloading</string>
<string name="conversation_item_received__download">Download</string>
<string name="conversation_item_received__downloading">Downloading</string>
</resources>

View File

@@ -1,22 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
/*
* Copyright (C) 2007-2008 Esmertec AG.
* Copyright (C) 2007-2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
-->
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
<PreferenceCategory android:title="@string/preferences__use_settings">
<CheckBoxPreference android:defaultValue="true"
@@ -31,6 +14,38 @@
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences__delivery_reports">
<CheckBoxPreference android:defaultValue="false"
android:key="pref_delivery_report_sms"
android:summary="@string/preferences__request_a_delivery_report_for_each_sms_message_you_send"
android:title="@string/preferences__sms_delivery_reports" />
<CheckBoxPreference android:defaultValue="false"
android:key="pref_delivery_report_mms"
android:summary="@string/preferences__request_a_delivery_report_for_each_mms_message_you_send"
android:enabled="false"
android:title="@string/preferences__mms_delivery_reports" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences__storage">
<CheckBoxPreference android:defaultValue="false"
android:key="pref_trim_threads"
android:summary="@string/preferences__automatically_delete_older_messages_once_a_conversation_thread_exceeds_a_specified_length"
android:title="@string/preferences__delete_old_messages" />
<EditTextPreference android:defaultValue="500"
android:key="pref_trim_length"
android:title="@string/preferences__conversation_length_limit"
android:inputType="number"
android:dependency="pref_trim_threads" />
<Preference android:key="pref_trim_now"
android:title="@string/preferences__trim_all_threads_now"
android:summary="@string/preferences__scan_through_all_conversation_threads_and_enforce_conversation_length_limits"
android:dependency="pref_trim_threads" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences__input_settings">
<CheckBoxPreference android:defaultValue="false"
android:key="pref_enter_sends"
@@ -140,4 +155,25 @@
android:title="@string/preferences__vibrate"
android:summary="@string/preferences__also_vibrate_when_notified" />
</PreferenceCategory>
<PreferenceCategory android:title="@string/preferences__advanced_mms_access_point_names">
<CheckBoxPreference android:key="pref_use_local_apns"
android:defaultValue="false"
android:title="@string/preferences__enable_fallback_mmsc"
android:summary="@string/preferences__use_mmsc_information_configured_here_when_system_apn_information_is_unavailable"/>
<EditTextPreference android:key="pref_apn_mmsc_host"
android:title="@string/preferences__mmsc_url_required"
android:dependency="pref_use_local_apns" />
<EditTextPreference android:key="pref_apn_mms_proxy"
android:title="@string/preferences__mms_proxy_host_optional"
android:dependency="pref_use_local_apns" />
<EditTextPreference android:key="pref_apn_mms_proxy_port"
android:title="@string/preferences__mms_proxy_port_optional"
android:dependency="pref_use_local_apns"
android:inputType="number" />
</PreferenceCategory>
</PreferenceScreen>

View File

@@ -17,18 +17,19 @@
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.preference.EditTextPreference;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.provider.ContactsContract;
import android.util.Log;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.contacts.ContactAccessor;
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
import org.thoughtcrime.securesms.crypto.IdentityKey;
@@ -37,6 +38,9 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.util.Dialogs;
import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.thoughtcrime.securesms.util.Trimmer;
import com.actionbarsherlock.view.MenuItem;
import java.util.List;
@@ -47,7 +51,7 @@ import java.util.List;
*
*/
public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
public class ApplicationPreferencesActivity extends PassphraseRequiredSherlockPreferenceActivity {
private static final int PICK_IDENTITY_CONTACT = 1;
private static final int IMPORT_IDENTITY_ID = 2;
@@ -74,6 +78,18 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
private static final String MANAGE_IDENTITIES_PREF = "pref_manage_identity";
private static final String CHANGE_PASSPHRASE_PREF = "pref_change_passphrase";
public static final String USE_LOCAL_MMS_APNS_PREF = "pref_use_local_apns";
public static final String MMSC_HOST_PREF = "pref_apn_mmsc_host";
public static final String MMSC_PROXY_HOST_PREF = "pref_apn_mms_proxy";
public static final String MMSC_PROXY_PORT_PREF = "pref_apn_mms_proxy_port";
public static final String SMS_DELIVERY_REPORT_PREF = "pref_delivery_report_sms";
public static final String MMS_DELIVERY_REPORT_PREF = "pref_delivery_report_mms";
public static final String THREAD_TRIM_ENABLED = "pref_trim_threads";
public static final String THREAD_TRIM_LENGTH = "pref_trim_length";
public static final String THREAD_TRIM_NOW = "pref_trim_now";
@Override
protected void onCreate(Bundle icicle) {
super.onCreate(icicle);
@@ -83,6 +99,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
addPreferencesFromResource(R.xml.preferences);
initializeIdentitySelection();
initializeEditTextSummaries();
this.findPreference(VIEW_MY_IDENTITY_PREF)
.setOnPreferenceClickListener(new ViewMyIdentityClickListener());
@@ -94,22 +111,16 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
.setOnPreferenceClickListener(new ManageIdentitiesClickListener());
this.findPreference(CHANGE_PASSPHRASE_PREF)
.setOnPreferenceClickListener(new ChangePassphraseClickListener());
this.findPreference(THREAD_TRIM_NOW)
.setOnPreferenceClickListener(new TrimNowClickListener());
this.findPreference(THREAD_TRIM_LENGTH)
.setOnPreferenceChangeListener(new TrimLengthValidationListener());
}
@Override
public void onStart() {
super.onStart();
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_START_EVENT);
startService(intent);
}
@Override
public void onStop() {
super.onStop();
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT);
startService(intent);
public void onDestroy() {
MemoryCleaner.clean((MasterSecret)getIntent().getParcelableExtra("master_secret"));
super.onDestroy();
}
@Override
@@ -124,12 +135,6 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
}
@Override
public void onDestroy() {
MemoryCleaner.clean((MasterSecret)getIntent().getParcelableExtra("master_secret"));
super.onDestroy();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
@@ -139,6 +144,28 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
return false;
}
private void initializeEditTextSummary(final EditTextPreference preference) {
if (preference.getText() == null) {
preference.setSummary("Not set");
} else {
preference.setSummary(preference.getText());
}
preference.setOnPreferenceChangeListener(new Preference.OnPreferenceChangeListener() {
@Override
public boolean onPreferenceChange(Preference pref, Object newValue) {
preference.setSummary(newValue == null ? "Not set" : ((String)newValue));
return true;
}
});
}
private void initializeEditTextSummaries() {
initializeEditTextSummary((EditTextPreference)this.findPreference(MMSC_HOST_PREF));
initializeEditTextSummary((EditTextPreference)this.findPreference(MMSC_PROXY_HOST_PREF));
initializeEditTextSummary((EditTextPreference)this.findPreference(MMSC_PROXY_PORT_PREF));
}
private void initializeIdentitySelection() {
ContactIdentityManager identity = ContactIdentityManager.getInstance(this);
@@ -193,6 +220,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
private class IdentityPreferenceClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent intent = new Intent(Intent.ACTION_PICK);
intent.setType(ContactsContract.Contacts.CONTENT_TYPE);
@@ -202,6 +230,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
private class ViewMyIdentityClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
Intent viewIdentityIntent = new Intent(ApplicationPreferencesActivity.this, ViewIdentityActivity.class);
viewIdentityIntent.putExtra("identity_key", IdentityKeyUtil.getIdentityKey(ApplicationPreferencesActivity.this));
@@ -212,6 +241,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
private class ExportMyIdentityClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
if (!IdentityKeyUtil.hasIdentityKey(ApplicationPreferencesActivity.this)) {
Toast.makeText(ApplicationPreferencesActivity.this,
@@ -243,6 +273,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
private class ImportContactIdentityClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
MasterSecret masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret");
@@ -261,6 +292,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
private class ManageIdentitiesClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
MasterSecret masterSecret = (MasterSecret)getIntent().getParcelableExtra("master_secret");
@@ -279,6 +311,7 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
private class ChangePassphraseClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
SharedPreferences settings = getSharedPreferences(KeyCachingService.PREFERENCES_NAME, 0);
@@ -294,4 +327,59 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
}
}
private class TrimNowClickListener implements Preference.OnPreferenceClickListener {
@Override
public boolean onPreferenceClick(Preference preference) {
final int threadLengthLimit = Integer.parseInt(PreferenceManager.getDefaultSharedPreferences(ApplicationPreferencesActivity.this)
.getString(THREAD_TRIM_LENGTH, "500"));
AlertDialog.Builder builder = new AlertDialog.Builder(ApplicationPreferencesActivity.this);
builder.setTitle(R.string.ApplicationPreferencesActivity_delete_all_old_messages_now);
builder.setMessage(String.format(getString(R.string.ApplicationPreferencesActivity_are_you_sure_you_would_like_to_immediately_trim_all_conversation_threads_to_the_s_most_recent_messages),
threadLengthLimit));
builder.setPositiveButton(R.string.ApplicationPreferencesActivity_delete,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Trimmer.trimAllThreads(ApplicationPreferencesActivity.this, threadLengthLimit);
}
});
builder.setNegativeButton(android.R.string.cancel, null);
builder.show();
return true;
}
}
private class TrimLengthValidationListener implements Preference.OnPreferenceChangeListener {
public TrimLengthValidationListener() {
EditTextPreference preference = (EditTextPreference)findPreference(THREAD_TRIM_LENGTH);
preference.setSummary(preference.getText() + " " + getString(R.string.ApplicationPreferencesActivity_messages_per_conversation));
}
@Override
public boolean onPreferenceChange(Preference preference, Object newValue) {
if (newValue == null || ((String)newValue).trim().length() == 0) {
return false;
}
try {
Integer.parseInt((String)newValue);
} catch (NumberFormatException nfe) {
Log.w("ApplicationPreferencesActivity", nfe);
return false;
}
if (Integer.parseInt((String)newValue) < 1) {
return false;
}
preference.setSummary((String)newValue + " " +
getString(R.string.ApplicationPreferencesActivity_messages_per_conversation));
return true;
}
}
}

View File

@@ -24,8 +24,6 @@ import android.util.Log;
import android.view.View;
import android.widget.Button;
import com.actionbarsherlock.app.SherlockActivity;
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.LocalKeyRecord;
@@ -42,7 +40,7 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
* @author Moxie Marlinspike
*
*/
public class AutoInitiateActivity extends SherlockActivity {
public class AutoInitiateActivity extends PassphraseRequiredSherlockActivity {
private long threadId;
private Recipient recipient;
@@ -77,12 +75,14 @@ public class AutoInitiateActivity extends SherlockActivity {
}
private class OkListener implements View.OnClickListener {
@Override
public void onClick(View v) {
initiateKeyExchange();
}
}
private class CancelListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Log.w("AutoInitiateActivity", "Exempting threadID: " + threadId);
exemptThread(AutoInitiateActivity.this, threadId);
@@ -114,5 +114,4 @@ public class AutoInitiateActivity extends SherlockActivity {
(new RemoteKeyRecord(context,recipient).getCurrentRemoteKey() == null) &&
(new LocalKeyRecord(context, masterSecret, recipient).getCurrentKeyPair() == null);
}
}

View File

@@ -22,17 +22,15 @@ import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import org.thoughtcrime.securesms.recipients.Recipients;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.ActionBar.Tab;
import com.actionbarsherlock.app.ActionBar.TabListener;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService;
/**
* Activity container for selecting a list of contacts. Provides a tab frame for
* contact, group, and "recent contact" activity tabs. Used by ComposeMessageActivity
@@ -41,7 +39,7 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
* @author Moxie Marlinspike
*
*/
public class ContactSelectionActivity extends SherlockFragmentActivity {
public class ContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity {
private ContactSelectionListFragment contactsFragment;
private ContactSelectionGroupsFragment groupsFragment;
@@ -64,18 +62,6 @@ public class ContactSelectionActivity extends SherlockFragmentActivity {
setupRecentTab();
}
@Override
protected void onStart() {
super.onStart();
registerPassphraseActivityStarted();
}
@Override
protected void onStop() {
super.onStop();
registerPassphraseActivityStopped();
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = this.getSupportMenuInflater();
@@ -160,19 +146,4 @@ public class ContactSelectionActivity extends SherlockFragmentActivity {
recentTab.setIcon(R.drawable.ic_tab_recent);
this.getSupportActionBar().addTab(recentTab);
}
private void registerPassphraseActivityStarted() {
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_START_EVENT);
startService(intent);
}
private void registerPassphraseActivityStopped() {
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT);
startService(intent);
}
}

View File

@@ -95,7 +95,7 @@ public class ContactSelectionGroupsFragment extends SherlockListFragment
for (ContactData contactData : contactDataList) {
for (NumberData numberData : contactData.numbers) {
recipientList.add(new Recipient(contactData.name, numberData.number, null));
recipientList.add(new Recipient(contactData.name, numberData.number, null, null));
}
}
}

View File

@@ -101,7 +101,7 @@ public class ContactSelectionListFragment extends SherlockListFragment
for (ContactData contactData : selectedContacts.values()) {
for (NumberData numberData : contactData.numbers) {
recipientList.add(new Recipient(contactData.name, numberData.number, null));
recipientList.add(new Recipient(contactData.name, numberData.number, null, null));
}
}

View File

@@ -89,7 +89,7 @@ public class ContactSelectionRecentFragment extends SherlockListFragment
for (ContactData contactData : selectedContacts.values()) {
for (NumberData numberData : contactData.numbers) {
recipientList.add(new Recipient(contactData.name, numberData.number, null));
recipientList.add(new Recipient(contactData.name, numberData.number, null, null));
}
}

View File

@@ -24,6 +24,8 @@ import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.telephony.PhoneNumberUtils;
@@ -35,6 +37,7 @@ import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnKeyListener;
import android.view.WindowManager;
import android.view.inputmethod.EditorInfo;
import android.widget.Button;
import android.widget.EditText;
@@ -42,36 +45,40 @@ import android.widget.ImageButton;
import android.widget.TextView;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.components.RecipientsPanel;
import org.thoughtcrime.securesms.crypto.AuthenticityCalculator;
import org.thoughtcrime.securesms.crypto.KeyExchangeInitiator;
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.KeyUtil;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.DraftDatabase;
import org.thoughtcrime.securesms.database.DraftDatabase.Draft;
import org.thoughtcrime.securesms.mms.AttachmentManager;
import org.thoughtcrime.securesms.mms.AttachmentTypeSelectorAdapter;
import org.thoughtcrime.securesms.mms.MediaTooLargeException;
import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.protocol.Tag;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.MessageNotifier;
import org.thoughtcrime.securesms.sms.MessageSender;
import org.thoughtcrime.securesms.util.CharacterCalculator;
import org.thoughtcrime.securesms.util.EncryptedCharacterCalculator;
import org.thoughtcrime.securesms.util.InvalidMessageException;
import org.thoughtcrime.securesms.util.MemoryCleaner;
import org.thoughtcrime.securesms.util.Util;
import ws.com.google.android.mms.MmsException;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
@@ -83,14 +90,21 @@ import java.util.List;
* @author Moxie Marlinspike
*
*/
public class ConversationActivity extends SherlockFragmentActivity
public class ConversationActivity extends PassphraseRequiredSherlockFragmentActivity
implements ConversationFragment.ConversationFragmentListener
{
private static final int PICK_CONTACT = 1;
private static final int PICK_IMAGE = 2;
private static final int PICK_VIDEO = 3;
private static final int PICK_AUDIO = 4;
public static final String RECIPIENTS_EXTRA = "recipients";
public static final String THREAD_ID_EXTRA = "thread_id";
public static final String MASTER_SECRET_EXTRA = "master_secret";
public static final String DRAFT_TEXT_EXTRA = "draft_text";
public static final String DRAFT_IMAGE_EXTRA = "draft_image";
public static final String DRAFT_AUDIO_EXTRA = "draft_audio";
private static final int PICK_CONTACT = 1;
private static final int PICK_IMAGE = 2;
private static final int PICK_VIDEO = 3;
private static final int PICK_AUDIO = 4;
private MasterSecret masterSecret;
private RecipientsPanel recipientsPanel;
@@ -101,7 +115,6 @@ public class ConversationActivity extends SherlockFragmentActivity
private AttachmentTypeSelectorAdapter attachmentAdapter;
private AttachmentManager attachmentManager;
private BroadcastReceiver killActivityReceiver;
private BroadcastReceiver securityUpdateReceiver;
private Recipients recipients;
@@ -119,15 +132,25 @@ public class ConversationActivity extends SherlockFragmentActivity
initializeReceivers();
initializeResources();
initializeDraft();
initializeTitleBar();
}
@Override
protected void onPause() {
super.onPause();
MessageNotifier.setVisibleThread(-1L);
}
@Override
protected void onResume() {
super.onResume();
initializeSecurity();
initializeTitleBar();
calculateCharactersRemaining();
MessageNotifier.setVisibleThread(threadId);
markThreadAsRead();
}
@Override
@@ -136,21 +159,12 @@ public class ConversationActivity extends SherlockFragmentActivity
if (!isExistingConversation())
initializeRecipientsInput();
registerPassphraseActivityStarted();
}
@Override
protected void onStop() {
super.onStop();
registerPassphraseActivityStopped();
}
@Override
protected void onDestroy() {
unregisterReceiver(killActivityReceiver);
unregisterReceiver(securityUpdateReceiver);
saveDraft();
MemoryCleaner.clean(masterSecret);
super.onDestroy();
}
@@ -394,6 +408,45 @@ public class ConversationActivity extends SherlockFragmentActivity
this.invalidateOptionsMenu();
}
private void initializeDraft() {
String draftText = getIntent().getStringExtra(DRAFT_TEXT_EXTRA);
Uri draftImage = getIntent().getParcelableExtra(DRAFT_IMAGE_EXTRA);
Uri draftAudio = getIntent().getParcelableExtra(DRAFT_AUDIO_EXTRA);
if (draftText != null) composeText.setText(draftText);
if (draftImage != null) addAttachmentImage(draftImage);
if (draftAudio != null) addAttachmentAudio(draftAudio);
if (draftText == null && draftImage == null && draftAudio == null) {
initializeDraftFromDatabase();
}
}
private void initializeDraftFromDatabase() {
new AsyncTask<Void, Void, List<Draft>>() {
@Override
protected List<Draft> doInBackground(Void... params) {
MasterCipher masterCipher = new MasterCipher(masterSecret);
DraftDatabase draftDatabase = DatabaseFactory.getDraftDatabase(ConversationActivity.this);
List<Draft> results = draftDatabase.getDrafts(masterCipher, threadId);
draftDatabase.clearDrafts(threadId);
return results;
}
@Override
protected void onPostExecute(List<Draft> drafts) {
for (Draft draft : drafts) {
if (draft.getType().equals(Draft.TEXT)) composeText.setText(draft.getValue());
else if (draft.getType().equals(Draft.IMAGE)) addAttachmentImage(Uri.parse(draft.getValue()));
else if (draft.getType().equals(Draft.AUDIO)) addAttachmentAudio(Uri.parse(draft.getValue()));
else if (draft.getType().equals(Draft.VIDEO)) addAttachmentVideo(Uri.parse(draft.getValue()));
}
}
}.execute();
}
private void initializeSecurity() {
if (isSingleConversation() &&
KeyUtil.isSessionFor(this, getRecipients().getPrimaryRecipient()))
@@ -435,8 +488,12 @@ public class ConversationActivity extends SherlockFragmentActivity
registerForContextMenu(sendButton);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
if (getIntent().getStringExtra("forwarded_message") != null)
composeText.setText(R.string.ConversationActivity_forward_message_prefix+": " + getIntent().getStringExtra("forwarded_message"));
composeText.setText(getString(R.string.ConversationActivity_forward_message_prefix)+": " + getIntent().getStringExtra("forwarded_message"));
}
private void initializeRecipientsInput() {
@@ -449,13 +506,6 @@ public class ConversationActivity extends SherlockFragmentActivity
private void initializeReceivers() {
killActivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
securityUpdateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
@@ -470,10 +520,6 @@ public class ConversationActivity extends SherlockFragmentActivity
}
};
registerReceiver(killActivityReceiver,
new IntentFilter(KeyCachingService.PASSPHRASE_EXPIRED_EVENT),
KeyCachingService.KEY_PERMISSION, null);
registerReceiver(securityUpdateReceiver,
new IntentFilter(KeyExchangeProcessor.SECURITY_UPDATE_EVENT),
KeyCachingService.KEY_PERMISSION, null);
@@ -537,6 +583,52 @@ public class ConversationActivity extends SherlockFragmentActivity
}
}
private List<Draft> getDraftsForCurrentState() {
List<Draft> drafts = new LinkedList<Draft>();
if (!Util.isEmpty(composeText)) {
drafts.add(new Draft(Draft.TEXT, composeText.getText().toString()));
}
for (Slide slide : attachmentManager.getSlideDeck().getSlides()) {
if (slide.hasImage()) drafts.add(new Draft(Draft.IMAGE, slide.getUri().toString()));
else if (slide.hasAudio()) drafts.add(new Draft(Draft.AUDIO, slide.getUri().toString()));
else if (slide.hasVideo()) drafts.add(new Draft(Draft.VIDEO, slide.getUri().toString()));
}
return drafts;
}
private void saveDraft() {
if (this.threadId <= 0 || this.recipients == null || this.recipients.isEmpty())
return;
final List<Draft> drafts = getDraftsForCurrentState();
if (drafts.size() <= 0)
return;
final long thisThreadId = this.threadId;
final MasterSecret thisMasterSecret = this.masterSecret.parcelClone();
new AsyncTask<Void, Void, Void>() {
@Override
protected void onPreExecute() {
Toast.makeText(ConversationActivity.this,
R.string.ConversationActivity_saving_draft,
Toast.LENGTH_SHORT).show();
}
@Override
protected Void doInBackground(Void... params) {
MasterCipher masterCipher = new MasterCipher(thisMasterSecret);
DatabaseFactory.getDraftDatabase(ConversationActivity.this).insertDrafts(masterCipher, thisThreadId, drafts);
MemoryCleaner.clean(thisMasterSecret);
return null;
}
}.execute();
}
private void calculateCharactersRemaining() {
int charactersSpent = composeText.getText().length();
CharacterCalculator.CharacterState characterState = characterCalculator.calculateCharacters(charactersSpent);
@@ -583,6 +675,17 @@ public class ConversationActivity extends SherlockFragmentActivity
return rawText;
}
private void markThreadAsRead() {
new AsyncTask<Long, Void, Void>() {
@Override
protected Void doInBackground(Long... params) {
DatabaseFactory.getThreadDatabase(ConversationActivity.this).setRead(params[0]);
MessageNotifier.updateNotification(ConversationActivity.this, masterSecret);
return null;
}
}.execute(threadId);
}
private void sendComplete(Recipients recipients, long threadId) {
attachmentManager.clear();
recipientsPanel.disable();
@@ -627,7 +730,6 @@ public class ConversationActivity extends SherlockFragmentActivity
}
sendComplete(recipients, allocatedThreadId);
MessageNotifier.updateNotification(ConversationActivity.this, false);
} catch (RecipientFormattingException ex) {
Toast.makeText(ConversationActivity.this,
R.string.ConversationActivity_recipient_is_not_a_valid_sms_or_email_address_exclamation,
@@ -642,21 +744,10 @@ public class ConversationActivity extends SherlockFragmentActivity
}
}
private void registerPassphraseActivityStarted() {
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_START_EVENT);
startService(intent);
}
private void registerPassphraseActivityStopped() {
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT);
startService(intent);
}
// Listeners
private class AddRecipientButtonListener implements OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(ConversationActivity.this, ContactSelectionActivity.class);
startActivityForResult(intent, PICK_CONTACT);
@@ -664,12 +755,14 @@ public class ConversationActivity extends SherlockFragmentActivity
};
private class AttachmentTypeListener implements DialogInterface.OnClickListener {
@Override
public void onClick(DialogInterface dialog, int which) {
addAttachment(attachmentAdapter.buttonToCommand(which));
}
}
private class RecipientsPanelChangeListener implements RecipientsPanel.RecipientsPanelChangedListener {
@Override
public void onRecipientsPanelUpdate(Recipients recipients) {
initializeSecurity();
initializeTitleBar();
@@ -678,10 +771,12 @@ public class ConversationActivity extends SherlockFragmentActivity
}
private class SendButtonListener implements OnClickListener, TextView.OnEditorActionListener {
@Override
public void onClick(View v) {
sendMessage(false);
}
@Override
public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
if (actionId == EditorInfo.IME_ACTION_SEND) {
sendButton.performClick();
@@ -693,15 +788,19 @@ public class ConversationActivity extends SherlockFragmentActivity
};
private class OnTextChangedListener implements TextWatcher {
@Override
public void afterTextChanged(Editable s) {
calculateCharactersRemaining();
}
@Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {}
@Override
public void onTextChanged(CharSequence s, int start, int before,int count) {}
}
private class ComposeKeyPressedListener implements OnKeyListener {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_DOWN) {
if (keyCode == KeyEvent.KEYCODE_ENTER) {

View File

@@ -21,11 +21,11 @@ import android.database.Cursor;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CursorAdapter;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MessageDisplayHelper;
@@ -43,7 +43,6 @@ import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.MessageNotifier;
import org.thoughtcrime.securesms.util.InvalidMessageException;
import ws.com.google.android.mms.MmsException;
@@ -65,7 +64,6 @@ public class ConversationAdapter extends CursorAdapter {
private static final int MAX_CACHE_SIZE = 40;
private final TouchListener touchListener = new TouchListener();
private final LinkedHashMap<String,MessageRecord> messageRecordCache;
private final Handler failedIconClickHandler;
private final long threadId;
@@ -75,8 +73,6 @@ public class ConversationAdapter extends CursorAdapter {
private final MasterCipher masterCipher;
private final LayoutInflater inflater;
private boolean dataChanged;
public ConversationAdapter(Recipients recipients, long threadId, Context context,
MasterSecret masterSecret, Handler failedIconClickHandler)
{
@@ -86,14 +82,10 @@ public class ConversationAdapter extends CursorAdapter {
this.threadId = threadId;
this.masterSecret = masterSecret;
this.masterCipher = new MasterCipher(masterSecret);
this.dataChanged = false;
this.failedIconClickHandler = failedIconClickHandler;
this.messageRecordCache = initializeCache();
this.inflater = (LayoutInflater)context
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
MessageNotifier.updateNotification(context, false);
}
@Override
@@ -104,8 +96,6 @@ public class ConversationAdapter extends CursorAdapter {
MessageRecord messageRecord = getMessageRecord(id, cursor, type);
item.set(masterSecret, messageRecord, failedIconClickHandler);
view.setOnTouchListener(touchListener);
}
@Override
@@ -143,7 +133,8 @@ public class ConversationAdapter extends CursorAdapter {
private MediaMmsMessageRecord getMediaMmsMessageRecord(long messageId, Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
long date = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.DATE));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_RECEIVED));
long box = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.MESSAGE_BOX));
Recipient recipient = getIndividualRecipientFor(null);
GroupData groupData = null;
@@ -175,13 +166,15 @@ public class ConversationAdapter extends CursorAdapter {
}
return new MediaMmsMessageRecord(context, id, recipients, recipient,
date, threadId, slideDeck, box, groupData);
dateSent, dateReceived, threadId,
slideDeck, box, groupData);
}
private NotificationMmsMessageRecord getNotificationMmsMessageRecord(long messageId, Cursor cursor) {
Recipient recipient = getIndividualRecipientFor(null);
long id = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
long date = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.DATE));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_SENT));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_RECEIVED));
NotificationInd notification;
@@ -192,7 +185,8 @@ public class ConversationAdapter extends CursorAdapter {
notification = new NotificationInd(new PduHeaders());
}
return new NotificationMmsMessageRecord(id, recipients, recipient, date, threadId,
return new NotificationMmsMessageRecord(id, recipients, recipient,
dateSent, dateReceived, threadId,
notification.getContentLocation(),
notification.getMessageSize(),
notification.getExpiry(),
@@ -201,8 +195,10 @@ public class ConversationAdapter extends CursorAdapter {
}
private SmsMessageRecord getSmsMessageRecord(long messageId, Cursor cursor) {
long date = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.DATE));
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_RECEIVED));
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_SENT));
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS));
String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY));
String address = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
Recipient recipient = getIndividualRecipientFor(address);
@@ -220,8 +216,8 @@ public class ConversationAdapter extends CursorAdapter {
}
SmsMessageRecord messageRecord = new SmsMessageRecord(context, messageId, recipients,
recipient, date, type, threadId,
groupData);
recipient, dateSent, dateReceived,
type, threadId, status, groupData);
if (body == null) {
body = "";
@@ -265,38 +261,30 @@ public class ConversationAdapter extends CursorAdapter {
Recipient recipient;
try {
if (address == null) recipient = recipients.getPrimaryRecipient();
else recipient = RecipientFactory.getRecipientsFromString(context, address).getPrimaryRecipient();
if (address == null) recipient = recipients.getPrimaryRecipient();
else recipient = RecipientFactory.getRecipientsFromString(context, address, false).getPrimaryRecipient();
if (recipient == null) recipient = new Recipient("Unknown", "Unknown", null,
ContactPhotoFactory.getDefaultContactPhoto(context));
} catch (RecipientFormattingException e) {
Log.w("ConversationAdapter", e);
recipient = new Recipient("Unknown", "Unknown", null);
recipient = new Recipient("Unknown", "Unknown", null,
ContactPhotoFactory.getDefaultContactPhoto(context));
}
return recipient;
}
@Override
protected void onContentChanged() {
super.onContentChanged();
messageRecordCache.clear();
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
this.dataChanged = true;
}
public void close() {
this.getCursor().close();
}
private class TouchListener implements View.OnTouchListener {
public boolean onTouch(View v, MotionEvent event) {
if (ConversationAdapter.this.dataChanged) {
ConversationAdapter.this.dataChanged = false;
MessageNotifier.updateNotification(context, false);
}
return false;
}
}
private LinkedHashMap<String,MessageRecord> initializeCache() {
return new LinkedHashMap<String,MessageRecord>() {
@Override

View File

@@ -123,19 +123,30 @@ public class ConversationFragment extends SherlockListFragment
}
private void handleDisplayDetails(MessageRecord message) {
String sender = message.getRecipients().getPrimaryRecipient().getNumber();
String transport = message.isMms() ? "mms" : "sms";
long date = message.getDate();
String sender = message.getRecipients().getPrimaryRecipient().getNumber();
String transport = message.isMms() ? "mms" : "sms";
long dateReceived = message.getDateReceived();
long dateSent = message.getDateSent();
SimpleDateFormat dateFormatter = new SimpleDateFormat("EEE MMM d, yyyy 'at' hh:mm:ss a zzz");
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(R.string.ConversationFragment_message_details);
builder.setIcon(android.R.drawable.ic_dialog_info);
builder.setCancelable(false);
builder.setMessage(String.format(getSherlockActivity()
.getString(R.string.ConversationFragment_sender_s_transport_s_sent_received_s),
sender, transport.toUpperCase(),
dateFormatter.format(new Date(date))));
if (dateReceived == dateSent || message.isOutgoing()) {
builder.setMessage(String.format(getSherlockActivity()
.getString(R.string.ConversationFragment_sender_s_transport_s_sent_received_s),
sender, transport.toUpperCase(),
dateFormatter.format(new Date(dateSent))));
} else {
builder.setMessage(String.format(getSherlockActivity()
.getString(R.string.ConversationFragment_sender_s_transport_s_sent_s_received_s),
sender, transport.toUpperCase(),
dateFormatter.format(new Date(dateSent)),
dateFormatter.format(new Date(dateReceived))));
}
builder.setPositiveButton(android.R.string.ok, null);
builder.show();
}

View File

@@ -42,6 +42,7 @@ import android.widget.TextView;
import android.widget.Toast;
import org.thoughtcrime.securesms.contacts.ContactIdentityManager;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.model.MediaMmsMessageRecord;
@@ -52,7 +53,6 @@ import org.thoughtcrime.securesms.mms.Slide;
import org.thoughtcrime.securesms.mms.SlideDeck;
import org.thoughtcrime.securesms.protocol.Tag;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.service.SendReceiveService;
import java.io.File;
@@ -84,6 +84,7 @@ public class ConversationItem extends LinearLayout {
private ImageView failedImage;
private ImageView keyImage;
private ImageView contactPhoto;
private ImageView deliveredImage;
private ImageView mmsThumbnail;
private Button mmsDownloadButton;
@@ -118,6 +119,7 @@ public class ConversationItem extends LinearLayout {
this.mmsDownloadButton = (Button) findViewById(R.id.mms_download_button);
this.mmsDownloadingLabel = (TextView) findViewById(R.id.mms_label_downloading);
this.contactPhoto = (ImageView)findViewById(R.id.contact_photo);
this.deliveredImage = (ImageView)findViewById(R.id.delivered_indicator);
setOnClickListener(clickListener);
this.failedImage.setOnClickListener(failedIconClickListener);
@@ -182,6 +184,7 @@ public class ConversationItem extends LinearLayout {
failedImage.setVisibility(messageRecord.isFailed() ? View.VISIBLE : View.GONE);
secureImage.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
keyImage.setVisibility(messageRecord.isKeyExchange() ? View.VISIBLE : View.GONE);
deliveredImage.setVisibility(!messageRecord.isKeyExchange() && messageRecord.isDelivered() ? View.VISIBLE : View.GONE);
mmsThumbnail.setVisibility(View.GONE);
mmsDownloadButton.setVisibility(View.GONE);
@@ -190,7 +193,9 @@ public class ConversationItem extends LinearLayout {
if (messageRecord.isFailed()) dateText.setText(R.string.ConversationItem_error_sending_message);
else if (messageRecord.isPending()) dateText.setText(R.string.ConversationItem_sending);
else dateText.setText(DateUtils.getRelativeTimeSpanString(getContext(),
messageRecord.getDate(),
(messageRecord.isOutgoing() ?
messageRecord.getDateSent() :
messageRecord.getDateReceived()),
false));
}
@@ -283,20 +288,9 @@ public class ConversationItem extends LinearLayout {
}
}
private void setContactPhotoForUserIdentity() {
Uri selfIdentityContact = ContactIdentityManager.getInstance(context).getSelfIdentityUri();
if (selfIdentityContact!= null) {
Recipient recipient = RecipientFactory.getRecipientForUri(context, selfIdentityContact);
if (recipient != null) {
contactPhoto.setImageBitmap(recipient.getContactPhoto());
return;
}
} else {
contactPhoto.setImageDrawable(context.getResources().getDrawable(R.drawable.ic_contact_picture));
}
Uri uri = ContactIdentityManager.getInstance(context).getSelfIdentityUri();
contactPhoto.setImageBitmap(ContactPhotoFactory.getLocalUserContactPhoto(context, uri));
contactPhoto.setVisibility(View.VISIBLE);
}

View File

@@ -1,24 +1,16 @@
package org.thoughtcrime.securesms;
import android.app.NotificationManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.database.ContentObserver;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.os.Parcelable;
import android.provider.ContactsContract;
import android.util.Log;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import android.view.WindowManager;
import org.thoughtcrime.securesms.ApplicationExportManager.ApplicationExportListener;
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
@@ -33,18 +25,21 @@ import org.thoughtcrime.securesms.service.KeyCachingService;
import org.thoughtcrime.securesms.service.SendReceiveService;
import org.thoughtcrime.securesms.util.MemoryCleaner;
public class ConversationListActivity extends SherlockFragmentActivity
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
public class ConversationListActivity extends PassphraseRequiredSherlockFragmentActivity
implements ConversationListFragment.ConversationSelectedListener
{
private ConversationListFragment fragment;
private MasterSecret masterSecret;
private BroadcastReceiver killActivityReceiver;
private BroadcastReceiver newKeyReceiver;
private ApplicationMigrationManager migrationManager;
private boolean havePromptedForPassphrase = false;
private boolean isVisible = false;
@Override
public void onCreate(Bundle icicle) {
@@ -52,7 +47,6 @@ public class ConversationListActivity extends SherlockFragmentActivity
setContentView(R.layout.conversation_list_activity);
getSupportActionBar().setTitle("TextSecure");
initializeKillReceiver();
initializeSenderReceiverService();
initializeResources();
initializeContactUpdatesReceiver();
@@ -61,51 +55,71 @@ public class ConversationListActivity extends SherlockFragmentActivity
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
Log.w("SecureSMS", "Got onNewIntent...");
createConversationIfNecessary(intent);
this.setIntent(intent);
}
@Override
public void onResume() {
super.onResume();
isVisible = true;
}
@Override
public void onPause() {
super.onPause();
if (newKeyReceiver != null) {
Log.w("ConversationListActivity", "Unregistering receiver...");
unregisterReceiver(newKeyReceiver);
newKeyReceiver = null;
}
}
@Override
public void onResume() {
super.onResume();
Log.w("ConversationListActivity", "onResume called...");
clearNotifications();
initializeKeyCachingServiceRegistration();
}
@Override
public void onStart() {
super.onStart();
registerPassphraseActivityStarted();
isVisible = false;
}
@Override
public void onStop() {
super.onStop();
havePromptedForPassphrase = false;
registerPassphraseActivityStopped();
}
@Override
public void onDestroy() {
Log.w("SecureSMS", "onDestroy...");
unregisterReceiver(killActivityReceiver);
Log.w("ConversationListActivity", "onDestroy...");
MemoryCleaner.clean(masterSecret);
super.onDestroy();
}
@Override
public void onMasterSecretCleared() {
this.masterSecret = null;
this.fragment.setMasterSecret(null);
this.invalidateOptionsMenu();
if (!havePromptedForPassphrase && isVisible) {
promptForPassphrase();
}
}
@Override
public void onNewMasterSecret(MasterSecret masterSecret) {
this.masterSecret = masterSecret;
if (masterSecret != null) {
if (!IdentityKeyUtil.hasIdentityKey(this)) {
new Thread(new IdentityKeyInitializer()).start();
}
if (!MasterSecretUtil.hasAsymmericMasterSecret(this)) {
new Thread(new AsymmetricMasteSecretInitializer()).start();
}
if (!isDatabaseMigrated()) initializeDatabaseMigration();
else DecryptingQueue.schedulePendingDecrypts(this, masterSecret);
}
this.fragment.setMasterSecret(masterSecret);
this.invalidateOptionsMenu();
this.havePromptedForPassphrase = false;
createConversationIfNecessary(this.getIntent());
}
@Override
public boolean onPrepareOptionsMenu(Menu menu) {
Log.w("ConversationListActivity", "onPrepareOptionsMenu...");
@@ -124,12 +138,12 @@ public class ConversationListActivity extends SherlockFragmentActivity
super.onOptionsItemSelected(item);
switch (item.getItemId()) {
case R.id.menu_new_message: createConversation(-1, null); return true;
case R.id.menu_unlock: promptForPassphrase(); return true;
case R.id.menu_settings: handleDisplaySettings(); return true;
case R.id.menu_export: handleExportDatabase(); return true;
case R.id.menu_import: handleImportDatabase(); return true;
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
case R.id.menu_new_message: createConversation(-1, null, null, null, null); return true;
case R.id.menu_unlock: promptForPassphrase(); return true;
case R.id.menu_settings: handleDisplaySettings(); return true;
case R.id.menu_export: handleExportDatabase(); return true;
case R.id.menu_import: handleImportDatabase(); return true;
case R.id.menu_clear_passphrase: handleClearPassphrase(); return true;
}
return false;
@@ -137,21 +151,25 @@ public class ConversationListActivity extends SherlockFragmentActivity
@Override
public void onCreateConversation(long threadId, Recipients recipients) {
createConversation(threadId, recipients);
createConversation(threadId, recipients, null, null, null);
}
private void createConversation(long threadId, Recipients recipients) {
private void createConversation(long threadId, Recipients recipients,
String text, Uri imageUri, Uri audioUri)
{
if (this.masterSecret == null) {
promptForPassphrase();
return;
}
Log.w("ConversationListActivity", "Creating conversation: " + threadId);
Intent intent = new Intent(this, ConversationActivity.class);
intent.putExtra("recipients", recipients);
intent.putExtra("thread_id", threadId);
intent.putExtra("master_secret", masterSecret);
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients);
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
intent.putExtra(ConversationActivity.DRAFT_TEXT_EXTRA, text);
intent.putExtra(ConversationActivity.DRAFT_IMAGE_EXTRA, imageUri);
intent.putExtra(ConversationActivity.DRAFT_AUDIO_EXTRA, audioUri);
startActivity(intent);
}
@@ -182,11 +200,8 @@ public class ConversationListActivity extends SherlockFragmentActivity
ApplicationExportListener listener = new ApplicationExportManager.ApplicationExportListener() {
@Override
public void onPrepareForImport() {
initializeWithMasterSecret(null);
Intent clearKeyIntent = new Intent(KeyCachingService.CLEAR_KEY_ACTION, null,
ConversationListActivity.this, KeyCachingService.class);
startService(clearKeyIntent);
onMasterSecretCleared();
handleClearPassphrase();
}
};
@@ -195,49 +210,9 @@ public class ConversationListActivity extends SherlockFragmentActivity
}
private void handleClearPassphrase() {
Intent keyService = new Intent(this, KeyCachingService.class);
keyService.setAction(KeyCachingService.CLEAR_KEY_ACTION);
startService(keyService);
this.masterSecret = null;
fragment.setMasterSecret(null);
promptForPassphrase();
}
private void initializeWithMasterSecret(MasterSecret masterSecret) {
this.masterSecret = masterSecret;
if (masterSecret != null) {
if (!IdentityKeyUtil.hasIdentityKey(this)) {
new Thread(new IdentityKeyInitializer()).start();
}
if (!MasterSecretUtil.hasAsymmericMasterSecret(this)) {
new Thread(new AsymmetricMasteSecretInitializer()).start();
}
if (!isDatabaseMigrated()) initializeDatabaseMigration();
else DecryptingQueue.schedulePendingDecrypts(this, masterSecret);
}
this.fragment.setMasterSecret(masterSecret);
this.invalidateOptionsMenu();
createConversationIfNecessary(this.getIntent());
}
private void initializeKillReceiver() {
this.killActivityReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
finish();
}
};
registerReceiver(this.killActivityReceiver,
new IntentFilter(KeyCachingService.PASSPHRASE_EXPIRED_EVENT),
KeyCachingService.KEY_PERMISSION, null);
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.CLEAR_KEY_ACTION);
startService(intent);
}
private void initializeContactUpdatesReceiver() {
@@ -281,24 +256,12 @@ public class ConversationListActivity extends SherlockFragmentActivity
}
}
private void initializeKeyCachingServiceRegistration() {
Log.w("ConversationListActivity", "Checking caching service...");
this.newKeyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
Log.w("ConversationListActivity", "Got a key broadcast...");
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret"));
}
};
IntentFilter filter = new IntentFilter(KeyCachingService.NEW_KEY_EVENT);
registerReceiver(newKeyReceiver, filter, KeyCachingService.KEY_PERMISSION, null);
Intent bindIntent = new Intent(this, KeyCachingService.class);
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void initializeResources() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE);
}
this.fragment = (ConversationListFragment)this.getSupportFragmentManager()
.findFragmentById(R.id.fragment_content);
}
@@ -308,84 +271,55 @@ public class ConversationListActivity extends SherlockFragmentActivity
.getBoolean("migrated", false);
}
private void clearNotifications() {
NotificationManager manager =
(NotificationManager)getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(KeyCachingService.NOTIFICATION_ID);
}
private void createConversationIfNecessary(Intent intent) {
Log.w("ConversationListActivity", "createConversationIfNecessary called");
long thread = intent.getLongExtra("thread_id", -1L);
String type = intent.getType();
Recipients recipients = null;
String draftText = null;
Uri draftImage = null;
Uri draftAudio = null;
if (intent.getAction() != null && intent.getAction().equals("android.intent.action.SENDTO")) {
Log.w("ConversationListActivity", "Intent has sendto action...");
if (Intent.ACTION_SENDTO.equals(intent.getAction())) {
try {
recipients = RecipientFactory.getRecipientsFromString(this, intent.getData().getSchemeSpecificPart());
recipients = RecipientFactory.getRecipientsFromString(this, intent.getData().getSchemeSpecificPart(), false);
thread = DatabaseFactory.getThreadDatabase(this).getThreadIdIfExistsFor(recipients);
} catch (RecipientFormattingException rfe) {
recipients = null;
}
} else if (Intent.ACTION_SEND.equals(intent.getAction())) {
if ("text/plain".equals(type)) {
draftText = intent.getStringExtra(Intent.EXTRA_TEXT);
} else if (type.startsWith("image/")) {
draftImage = intent.getParcelableExtra(Intent.EXTRA_STREAM);
} else if (type.startsWith("audio/")) {
draftAudio = intent.getParcelableExtra(Intent.EXTRA_STREAM);
}
} else {
recipients = intent.getParcelableExtra("recipients");
}
if (recipients != null) {
Log.w("ConversationListActivity", "Creating conversation: " + thread + " , " + recipients);
createConversation(thread, recipients);
if (recipients != null || Intent.ACTION_SEND.equals(intent.getAction())) {
createConversation(thread, recipients, draftText, draftImage, draftAudio);
intent.putExtra("thread_id", -1L);
intent.putExtra("recipients", (Parcelable)null);
intent.putExtra(Intent.EXTRA_TEXT, (String)null);
intent.putExtra(Intent.EXTRA_STREAM, (Parcelable)null);
intent.setAction(null);
}
}
private void registerPassphraseActivityStarted() {
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_START_EVENT);
startService(intent);
}
private void registerPassphraseActivityStopped() {
Intent intent = new Intent(this, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT);
startService(intent);
}
private ServiceConnection serviceConnection = new ServiceConnection() {
public void onServiceConnected(ComponentName className, IBinder service) {
KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
MasterSecret masterSecret = keyCachingService.getMasterSecret();
initializeWithMasterSecret(masterSecret);
if (masterSecret == null && !havePromptedForPassphrase)
promptForPassphrase();
Intent cachingIntent = new Intent(ConversationListActivity.this, KeyCachingService.class);
startService(cachingIntent);
try {
ConversationListActivity.this.unbindService(this);
} catch (IllegalArgumentException iae) {
Log.w("SecureSMS", iae);
}
}
public void onServiceDisconnected(ComponentName name) {}
};
private class IdentityKeyInitializer implements Runnable {
@Override
public void run() {
IdentityKeyUtil.generateIdentityKeys(ConversationListActivity.this, masterSecret);
}
}
private class AsymmetricMasteSecretInitializer implements Runnable {
@Override
public void run() {
MasterSecretUtil.generateAsymmetricMasterSecret(ConversationListActivity.this, masterSecret);
}
}
}

View File

@@ -18,8 +18,10 @@ package org.thoughtcrime.securesms;
import android.content.Context;
import android.database.Cursor;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.CursorAdapter;
import org.thoughtcrime.securesms.database.DatabaseFactory;
@@ -37,9 +39,10 @@ import java.util.Set;
*
* @author Moxie Marlinspike
*/
public class ConversationListAdapter extends CursorAdapter {
public class ConversationListAdapter extends CursorAdapter implements AbsListView.RecyclerListener {
private final Context context;
private final LayoutInflater inflater;
private final Set<Long> batchSet = Collections.synchronizedSet(new HashSet<Long>());
private boolean batchMode = false;
@@ -47,21 +50,19 @@ public class ConversationListAdapter extends CursorAdapter {
public ConversationListAdapter(Context context, Cursor cursor) {
super(context, cursor);
this.context = context;
this.inflater = LayoutInflater.from(context);
}
@Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
ConversationListItem view = new ConversationListItem(context, batchSet);
bindView(view, context, cursor);
return view;
return inflater.inflate(R.layout.conversation_list_item_view, parent, false);
}
@Override
public void bindView(View view, Context context, Cursor cursor) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID));
String recipientId = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.RECIPIENT_IDS));
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientId);
Recipients recipients = RecipientFactory.getRecipientsForIds(context, recipientId, true);
long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE));
long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT));
@@ -70,7 +71,7 @@ public class ConversationListAdapter extends CursorAdapter {
ThreadRecord thread = new ThreadRecord(context, recipients, date, count, read == 1, threadId);
setBody(cursor, thread);
((ConversationListItem)view).set(thread, batchMode);
((ConversationListItem)view).set(thread, batchSet, batchMode);
}
protected void filterBody(ThreadRecord thread, String body) {
@@ -114,4 +115,9 @@ public class ConversationListAdapter extends CursorAdapter {
this.notifyDataSetChanged();
}
@Override
public void onMovedToScrapHeap(View view) {
((ConversationListItem)view).unbind();
}
}

View File

@@ -19,8 +19,10 @@ package org.thoughtcrime.securesms;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.DialogInterface;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.LoaderManager;
@@ -32,18 +34,18 @@ import android.widget.AdapterView;
import android.widget.CursorAdapter;
import android.widget.ListView;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.recipients.Recipients;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.loaders.ConversationListLoader;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.MessageNotifier;
import java.util.Set;
@@ -73,8 +75,8 @@ public class ConversationListFragment extends SherlockListFragment
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
this.listener = (ConversationSelectedListener)activity;
super.onAttach(activity);
this.listener = (ConversationSelectedListener)activity;
}
@Override
@@ -100,12 +102,14 @@ public class ConversationListFragment extends SherlockListFragment
}
public void setMasterSecret(MasterSecret masterSecret) {
this.masterSecret = masterSecret;
initializeListAdapter();
if (this.masterSecret != masterSecret) {
this.masterSecret = masterSecret;
initializeListAdapter();
}
}
@SuppressLint({ "NewApi", "NewApi" })
private void initializeSearch(android.widget.SearchView searchView) {
private void initializeSearch(android.widget.SearchView searchView) {
searchView.setOnQueryTextListener(new android.widget.SearchView.OnQueryTextListener() {
@Override
public boolean onQueryTextSubmit(String query) {
@@ -143,6 +147,7 @@ private void initializeSearch(android.widget.SearchView searchView) {
this.setListAdapter(new DecryptingConversationListAdapter(getActivity(), null, masterSecret));
}
getListView().setRecyclerListener((ConversationListAdapter)getListAdapter());
getLoaderManager().restartLoader(0, null, this);
}
@@ -154,14 +159,35 @@ private void initializeSearch(android.widget.SearchView searchView) {
alert.setCancelable(true);
alert.setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Set<Long> selectedConversations = ((ConversationListAdapter)getListAdapter())
final Set<Long> selectedConversations = ((ConversationListAdapter)getListAdapter())
.getBatchSelections();
if (!selectedConversations.isEmpty()) {
DatabaseFactory.getThreadDatabase(getActivity())
.deleteConversations(selectedConversations);
MessageNotifier.updateNotification(getActivity(), false);
new AsyncTask<Void, Void, Void>() {
private ProgressDialog dialog;
@Override
protected void onPreExecute() {
dialog = ProgressDialog.show(getActivity(),
getSherlockActivity().getString(R.string.ConversationListFragment_deleting),
getSherlockActivity().getString(R.string.ConversationListFragment_deleting_selected_threads),
true, false);
}
@Override
protected Void doInBackground(Void... params) {
DatabaseFactory.getThreadDatabase(getActivity()).deleteConversations(selectedConversations);
MessageNotifier.updateNotification(getActivity(), masterSecret);
return null;
}
@Override
protected void onPostExecute(Void result) {
dialog.dismiss();
}
}.execute();
}
}
});

View File

@@ -17,23 +17,30 @@
package org.thoughtcrime.securesms;
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.graphics.Typeface;
import android.net.Uri;
import android.os.Build;
import android.os.Handler;
import android.provider.Contacts.Intents;
import android.provider.ContactsContract.QuickContact;
import android.text.Spannable;
import android.text.SpannableStringBuilder;
import android.text.format.DateUtils;
import android.text.style.ForegroundColorSpan;
import android.text.style.StyleSpan;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.CheckBox;
import android.widget.CompoundButton;
import android.widget.ImageView;
import android.widget.QuickContactBadge;
import android.widget.RelativeLayout;
import android.widget.TextView;
import org.thoughtcrime.securesms.database.model.ThreadRecord;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.Set;
@@ -45,49 +52,59 @@ import java.util.Set;
* @author Moxie Marlinspike
*/
public class ConversationListItem extends RelativeLayout {
public class ConversationListItem extends RelativeLayout
implements Recipient.RecipientModifiedListener
{
private Context context;
private Set<Long> selectedThreads;
private Recipients recipients;
private long threadId;
private boolean first;
private TextView subjectView;
private TextView fromView;
private TextView dateView;
private CheckBox checkbox;
private QuickContactBadge contactPhoto;
private long count;
private boolean read;
public ConversationListItem(Context context, boolean first) {
this(context, (Set<Long>)null);
private ImageView contactPhotoImage;
private QuickContactBadge contactPhotoBadge;
this.first = true;
contactPhoto.setVisibility(View.GONE);
}
private final Handler handler = new Handler();
public ConversationListItem(Context context, Set<Long> selectedThreads) {
public ConversationListItem(Context context) {
super(context);
LayoutInflater li = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
li.inflate(R.layout.conversation_list_item_view, this, true);
this.selectedThreads = selectedThreads;
this.subjectView = (TextView)findViewById(R.id.subject);
this.fromView = (TextView)findViewById(R.id.from);
this.dateView = (TextView)findViewById(R.id.date);
this.contactPhoto = (QuickContactBadge)findViewById(R.id.contact_photo);
this.checkbox = (CheckBox)findViewById(R.id.checkbox);
intializeListeners();
this.context = context;
}
public ConversationListItem(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
}
public void set(ThreadRecord thread, boolean batchMode) {
this.recipients = thread.getRecipients();
this.threadId = thread.getThreadId();
this.fromView.setText(formatFrom(recipients, thread.getCount(), thread.isRead()));
@Override
protected void onFinishInflate() {
this.subjectView = (TextView)findViewById(R.id.subject);
this.fromView = (TextView)findViewById(R.id.from);
this.dateView = (TextView)findViewById(R.id.date);
this.checkbox = (CheckBox)findViewById(R.id.checkbox);
this.contactPhotoBadge = (QuickContactBadge)findViewById(R.id.contact_photo_badge);
this.contactPhotoImage = (ImageView)findViewById(R.id.contact_photo_image);
initializeContactWidgetVisibility();
intializeListeners();
}
public void set(ThreadRecord thread, Set<Long> selectedThreads, boolean batchMode) {
this.selectedThreads = selectedThreads;
this.recipients = thread.getRecipients();
this.threadId = thread.getThreadId();
this.count = thread.getCount();
this.read = thread.isRead();
this.recipients.addListener(this);
this.fromView.setText(formatFrom(recipients, count, read));
if (thread.isKeyExchange())
this.subjectView.setText(R.string.ConversationListItem_key_exchange_message,
@@ -96,7 +113,9 @@ public class ConversationListItem extends RelativeLayout {
this.subjectView.setText(thread.getBody(), TextView.BufferType.SPANNABLE);
if (thread.getEmphasis())
((Spannable)this.subjectView.getText()).setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0, this.subjectView.getText().length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
((Spannable)this.subjectView.getText()).setSpan(new StyleSpan(android.graphics.Typeface.ITALIC), 0,
this.subjectView.getText().length(),
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
if (thread.getDate() > 0)
this.dateView.setText(DateUtils.getRelativeTimeSpanString(getContext(), thread.getDate(), false));
@@ -104,27 +123,75 @@ public class ConversationListItem extends RelativeLayout {
if (selectedThreads != null)
this.checkbox.setChecked(selectedThreads.contains(threadId));
if (!first) {
if (batchMode) checkbox.setVisibility(View.VISIBLE);
else checkbox.setVisibility(View.GONE);
if (batchMode) checkbox.setVisibility(View.VISIBLE);
else checkbox.setVisibility(View.GONE);
contactPhoto.setImageBitmap(this.recipients.getPrimaryRecipient().getContactPhoto());
contactPhoto.assignContactFromPhone(this.recipients.getPrimaryRecipient().getNumber(), true);
contactPhoto.setVisibility(View.VISIBLE);
}
setBackground(read, batchMode);
setContactPhoto(this.recipients.getPrimaryRecipient());
}
public void unbind() {
this.recipients.removeListener(this);
}
private void intializeListeners() {
checkbox.setOnCheckedChangeListener(new CheckedChangedListener());
}
private void initializeContactWidgetVisibility() {
if (isBadgeEnabled()) {
contactPhotoBadge.setVisibility(View.VISIBLE);
contactPhotoImage.setVisibility(View.GONE);
} else {
contactPhotoBadge.setVisibility(View.GONE);
contactPhotoImage.setVisibility(View.VISIBLE);
}
}
private void setContactPhoto(final Recipient recipient) {
if (recipient == null) return;
if (isBadgeEnabled()) {
contactPhotoBadge.setImageBitmap(recipient.getContactPhoto());
contactPhotoBadge.assignContactFromPhone(recipient.getNumber(), true);
} else {
contactPhotoImage.setImageBitmap(recipient.getContactPhoto());
contactPhotoImage.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (recipient.getContactUri() != null) {
QuickContact.showQuickContact(context, contactPhotoImage, recipient.getContactUri(), QuickContact.MODE_LARGE, null);
} else {
Intent intent = new Intent(Intents.SHOW_OR_CREATE_CONTACT, Uri.fromParts("tel", recipient.getNumber(), null));
context.startActivity(intent);
}
}
});
}
}
private void setBackground(boolean read, boolean batch) {
if (batch && checkbox.isChecked()) {
setBackgroundResource(R.drawable.list_selected_holo_light);
} else if (read) {
setBackgroundResource(R.drawable.conversation_list_item_background_read);
} else {
setBackgroundResource(R.drawable.conversation_list_item_background_unread);
}
}
private boolean isBadgeEnabled() {
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB;
}
private CharSequence formatFrom(Recipients from, long count, boolean read) {
SpannableStringBuilder builder = new SpannableStringBuilder(from.toShortString());
String fromString = from.toShortString();
SpannableStringBuilder builder = new SpannableStringBuilder(fromString);
if (count > 0) {
builder.append(" " + count);
builder.setSpan(new ForegroundColorSpan(Color.parseColor("#66333333")),
from.toShortString().length(), builder.length(),
fromString.length(), builder.length(),
Spannable.SPAN_INCLUSIVE_EXCLUSIVE);
}
@@ -145,9 +212,23 @@ public class ConversationListItem extends RelativeLayout {
}
private class CheckedChangedListener implements CompoundButton.OnCheckedChangeListener {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (isChecked) selectedThreads.add(threadId);
else selectedThreads.remove(threadId);
setBackground(read, true);
}
}
@Override
public void onModified(Recipient recipient) {
handler.post(new Runnable() {
@Override
public void run() {
ConversationListItem.this.fromView.setText(formatFrom(recipients, count, read));
setContactPhoto(ConversationListItem.this.recipients.getPrimaryRecipient());
}
});
}
}

View File

@@ -19,23 +19,22 @@ package org.thoughtcrime.securesms;
import android.content.Intent;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockActivity;
import org.thoughtcrime.securesms.crypto.SerializableKey;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Dialogs;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import com.google.zxing.integration.android.IntentIntegrator;
import com.google.zxing.integration.android.IntentResult;
import org.thoughtcrime.securesms.crypto.SerializableKey;
import org.thoughtcrime.securesms.util.Base64;
import org.thoughtcrime.securesms.util.Dialogs;
/**
* Activity for initiating/receiving key QR code scans.
*
* @author Moxie Marlinspike
*/
public abstract class KeyScanningActivity extends SherlockActivity {
public abstract class KeyScanningActivity extends PassphraseRequiredSherlockActivity {
@Override
public boolean onPrepareOptionsMenu(Menu menu) {

View File

@@ -17,11 +17,9 @@
package org.thoughtcrime.securesms;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
@@ -61,8 +59,19 @@ public class PassphraseCreateActivity extends PassphraseActivity {
this.okButton = (Button) findViewById(R.id.ok_button);
this.cancelButton = (Button) findViewById(R.id.cancel_button);
this.okButton.setOnClickListener(new OkButtonClickListener());
this.cancelButton.setOnClickListener(new CancelButtonClickListener());
this.okButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
verifyAndSavePassphrases();
}
});
this.cancelButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
finish();
}
});
}
private void verifyAndSavePassphrases() {
@@ -76,28 +85,18 @@ public class PassphraseCreateActivity extends PassphraseActivity {
this.passphraseEdit.setText("");
this.passphraseRepeatEdit.setText("");
} else {
MasterSecret masterSecret = MasterSecretUtil.generateMasterSecret(this, passphrase);
MemoryCleaner.clean(passphrase); // We do this, but the edit boxes are basically impossible to clean up.
// We do this, but the edit boxes are basically impossible to clean up.
MemoryCleaner.clean(passphraseRepeat);
new AsymmetricSecretGenerator(masterSecret).generate();
new SecretGenerator().execute(passphrase);
}
}
private class AsymmetricSecretGenerator extends Handler implements Runnable {
private class SecretGenerator extends AsyncTask<String, Void, Void> {
private ProgressDialog progressDialog;
private MasterSecret masterSecret;
private MasterSecret masterSecret;
public AsymmetricSecretGenerator(MasterSecret masterSecret) {
this.masterSecret = masterSecret;
}
public void run() {
MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret);
IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this, masterSecret);
this.obtainMessage().sendToTarget();
}
public void generate() {
@Override
protected void onPreExecute() {
progressDialog = new ProgressDialog(PassphraseCreateActivity.this);
progressDialog.setTitle(R.string.PassphraseCreateActivity_generating_keypair);
progressDialog.setMessage(getString(R.string.PassphraseCreateActivity_generating_a_local_encryption_keypair));
@@ -105,28 +104,30 @@ public class PassphraseCreateActivity extends PassphraseActivity {
progressDialog.setIndeterminate(true);
progressDialog.setProgressStyle(ProgressDialog.STYLE_SPINNER);
progressDialog.show();
new Thread(this).start();
}
@Override
public void handleMessage(Message message) {
protected Void doInBackground(String... params) {
String passphrase = params[0];
masterSecret = MasterSecretUtil.generateMasterSecret(PassphraseCreateActivity.this,
passphrase);
// We do this, but the edit boxes are basically impossible to clean up.
MemoryCleaner.clean(passphrase);
MasterSecretUtil.generateAsymmetricMasterSecret(PassphraseCreateActivity.this, masterSecret);
IdentityKeyUtil.generateIdentityKeys(PassphraseCreateActivity.this, masterSecret);
return null;
}
@Override
protected void onPostExecute(Void param) {
progressDialog.dismiss();
setMasterSecret(masterSecret);
}
}
private class CancelButtonClickListener implements OnClickListener {
public void onClick(View v) {
finish();
}
}
private class OkButtonClickListener implements OnClickListener {
public void onClick(View v) {
verifyAndSavePassphrases();
}
}
@Override
protected void cleanup() {
this.passphraseEdit = null;

View File

@@ -0,0 +1,8 @@
package org.thoughtcrime.securesms;
import org.thoughtcrime.securesms.crypto.MasterSecret;
public interface PassphraseRequiredActivity {
public void onMasterSecretCleared();
public void onNewMasterSecret(MasterSecret masterSecret);
}

View File

@@ -0,0 +1,128 @@
package org.thoughtcrime.securesms;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.os.IBinder;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.service.KeyCachingService;
public class PassphraseRequiredMixin {
private KeyCachingServiceConnection serviceConnection;
private BroadcastReceiver clearKeyReceiver;
private BroadcastReceiver newKeyReceiver;
public void onCreate(Context context, PassphraseRequiredActivity activity) {
initializeClearKeyReceiver(context, activity);
}
public void onResume(Context context, PassphraseRequiredActivity activity) {
initializeNewKeyReceiver(context, activity);
initializeServiceConnection(context, activity);
KeyCachingService.registerPassphraseActivityStarted(context);
}
public void onPause(Context context, PassphraseRequiredActivity activity) {
removeNewKeyReceiver(context);
removeServiceConnection(context);
KeyCachingService.registerPassphraseActivityStopped(context);
}
public void onDestroy(Context context, PassphraseRequiredActivity activity) {
removeClearKeyReceiver(context);
}
private void initializeClearKeyReceiver(Context context, final PassphraseRequiredActivity activity) {
this.clearKeyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
activity.onMasterSecretCleared();
}
};
IntentFilter filter = new IntentFilter(KeyCachingService.CLEAR_KEY_EVENT);
context.registerReceiver(clearKeyReceiver, filter, KeyCachingService.KEY_PERMISSION, null);
}
private void initializeNewKeyReceiver(Context context, final PassphraseRequiredActivity activity) {
this.newKeyReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
activity.onNewMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret"));
}
};
IntentFilter filter = new IntentFilter(KeyCachingService.NEW_KEY_EVENT);
context.registerReceiver(newKeyReceiver, filter, KeyCachingService.KEY_PERMISSION, null);
}
private void initializeServiceConnection(Context context, PassphraseRequiredActivity activity) {
Intent cachingIntent = new Intent(context, KeyCachingService.class);
context.startService(cachingIntent);
this.serviceConnection = new KeyCachingServiceConnection(activity);
Intent bindIntent = new Intent(context, KeyCachingService.class);
context.bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void removeClearKeyReceiver(Context context) {
if (clearKeyReceiver != null) {
context.unregisterReceiver(clearKeyReceiver);
clearKeyReceiver = null;
}
}
private void removeNewKeyReceiver(Context context) {
if (newKeyReceiver != null) {
context.unregisterReceiver(newKeyReceiver);
newKeyReceiver = null;
}
}
private void removeServiceConnection(Context context) {
if (this.serviceConnection != null && this.serviceConnection.isBound()) {
context.unbindService(this.serviceConnection);
}
}
private static class KeyCachingServiceConnection implements ServiceConnection {
private final PassphraseRequiredActivity activity;
private boolean isBound;
public KeyCachingServiceConnection(PassphraseRequiredActivity activity) {
this.activity = activity;
this.isBound = false;
}
@Override
public void onServiceConnected(ComponentName name, IBinder service) {
KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
MasterSecret masterSecret = keyCachingService.getMasterSecret();
this.isBound = true;
if (masterSecret == null) {
activity.onMasterSecretCleared();
} else {
activity.onNewMasterSecret(masterSecret);
}
}
@Override
public void onServiceDisconnected(ComponentName name) {
this.isBound = false;
}
public boolean isBound() {
return this.isBound;
}
}
}

View File

@@ -0,0 +1,45 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import com.actionbarsherlock.app.SherlockActivity;
public class PassphraseRequiredSherlockActivity extends SherlockActivity implements PassphraseRequiredActivity {
private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
delegate.onCreate(this, this);
}
@Override
protected void onResume() {
super.onResume();
delegate.onResume(this, this);
}
@Override
protected void onPause() {
super.onPause();
delegate.onPause(this, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
delegate.onDestroy(this, this);
}
@Override
public void onMasterSecretCleared() {
finish();
}
@Override
public void onNewMasterSecret(MasterSecret masterSecret) {}
}

View File

@@ -0,0 +1,44 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import com.actionbarsherlock.app.SherlockFragmentActivity;
public class PassphraseRequiredSherlockFragmentActivity extends SherlockFragmentActivity implements PassphraseRequiredActivity {
private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
delegate.onCreate(this, this);
}
@Override
protected void onResume() {
super.onResume();
delegate.onResume(this, this);
}
@Override
protected void onPause() {
super.onPause();
delegate.onPause(this, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
delegate.onDestroy(this, this);
}
@Override
public void onMasterSecretCleared() {
finish();
}
@Override
public void onNewMasterSecret(MasterSecret masterSecret) {}
}

View File

@@ -0,0 +1,45 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import com.actionbarsherlock.app.SherlockListActivity;
public class PassphraseRequiredSherlockListActivity extends SherlockListActivity implements PassphraseRequiredActivity {
private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
delegate.onCreate(this, this);
}
@Override
protected void onResume() {
super.onResume();
delegate.onResume(this, this);
}
@Override
protected void onPause() {
super.onPause();
delegate.onPause(this, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
delegate.onDestroy(this, this);
}
@Override
public void onMasterSecretCleared() {
finish();
}
@Override
public void onNewMasterSecret(MasterSecret masterSecret) {}
}

View File

@@ -0,0 +1,48 @@
package org.thoughtcrime.securesms;
import android.os.Bundle;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
public abstract class PassphraseRequiredSherlockPreferenceActivity
extends SherlockPreferenceActivity
implements PassphraseRequiredActivity
{
private final PassphraseRequiredMixin delegate = new PassphraseRequiredMixin();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
delegate.onCreate(this, this);
}
@Override
protected void onResume() {
super.onResume();
delegate.onResume(this, this);
}
@Override
protected void onPause() {
super.onPause();
delegate.onPause(this, this);
}
@Override
protected void onDestroy() {
super.onDestroy();
delegate.onDestroy(this, this);
}
@Override
public void onMasterSecretCleared() {
finish();
}
@Override
public void onNewMasterSecret(MasterSecret masterSecret) {}
}

View File

@@ -16,7 +16,6 @@
*/
package org.thoughtcrime.securesms;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
@@ -40,7 +39,7 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
* @author Moxie Marlinspike
*/
public class ReceiveKeyActivity extends Activity {
public class ReceiveKeyActivity extends PassphraseRequiredSherlockActivity {
private TextView descriptionText;
private TextView signatureText;
@@ -171,6 +170,7 @@ public class ReceiveKeyActivity extends Activity {
}
private class VerifyIdentityListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(ReceiveKeyActivity.this, VerifyIdentityActivity.class);
intent.putExtra("recipient", recipient);
@@ -181,6 +181,7 @@ public class ReceiveKeyActivity extends Activity {
}
private class VerifySessionListener implements View.OnClickListener {
@Override
public void onClick(View v) {
Intent intent = new Intent(ReceiveKeyActivity.this, VerifyKeysActivity.class);
intent.putExtra("recipient", recipient);
@@ -191,6 +192,7 @@ public class ReceiveKeyActivity extends Activity {
}
private class OkListener implements View.OnClickListener {
@Override
public void onClick(View v) {
keyExchangeProcessor.processKeyExchangeMessage(keyExchangeMessage, threadId);
finish();
@@ -198,6 +200,7 @@ public class ReceiveKeyActivity extends Activity {
}
private class CancelListener implements View.OnClickListener {
@Override
public void onClick(View v) {
ReceiveKeyActivity.this.finish();
}

View File

@@ -25,8 +25,6 @@ import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockActivity;
import org.thoughtcrime.securesms.crypto.IdentityKey;
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
import org.thoughtcrime.securesms.crypto.MasterSecret;
@@ -39,7 +37,7 @@ import org.thoughtcrime.securesms.util.MemoryCleaner;
*
* @author Moxie Marlinspike
*/
public class SaveIdentityActivity extends SherlockActivity {
public class SaveIdentityActivity extends PassphraseRequiredSherlockActivity {
private MasterSecret masterSecret;
private IdentityKey identityKey;
@@ -83,6 +81,7 @@ public class SaveIdentityActivity extends SherlockActivity {
}
private class OkListener implements View.OnClickListener {
@Override
public void onClick(View v) {
if (identityName.getText() == null || identityName.getText().toString().trim().length() == 0) {
Toast.makeText(SaveIdentityActivity.this,
@@ -99,6 +98,7 @@ public class SaveIdentityActivity extends SherlockActivity {
builder.setMessage(R.string.SaveIdentityActivity_an_identity_key_with_the_specified_name_already_exists);
builder.setPositiveButton(R.string.SaveIdentityActivity_manage_identities,
new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
Intent intent = new Intent(SaveIdentityActivity.this, ReviewIdentitiesActivity.class);
intent.putExtra("master_secret", masterSecret);
@@ -115,9 +115,9 @@ public class SaveIdentityActivity extends SherlockActivity {
}
private class CancelListener implements View.OnClickListener {
@Override
public void onClick(View v) {
finish();
}
}
}

View File

@@ -81,7 +81,7 @@ public class RecipientsPanel extends RelativeLayout {
public Recipients getRecipients() throws RecipientFormattingException {
String rawText = recipientsText.getText().toString();
Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText);
Recipients recipients = RecipientFactory.getRecipientsFromString(getContext(), rawText, false);
if (recipients.isEmpty())
throw new RecipientFormattingException("Recipient List Is Empty!");

View File

@@ -0,0 +1,73 @@
package org.thoughtcrime.securesms.contacts;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.util.LRUCache;
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
public class ContactPhotoFactory {
private static final Object defaultPhotoLock = new Object();
private static Bitmap defaultContactPhoto;
private static final Map<Uri,Bitmap> localUserContactPhotoCache =
Collections.synchronizedMap(new LRUCache<Uri,Bitmap>(2));
private static final String[] CONTENT_URI_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.LOOKUP_KEY
};
public static Bitmap getDefaultContactPhoto(Context context) {
synchronized (defaultPhotoLock) {
if (defaultContactPhoto == null)
defaultContactPhoto = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_contact_picture);
return defaultContactPhoto;
}
}
public static Bitmap getLocalUserContactPhoto(Context context, Uri uri) {
if (uri == null) return getDefaultContactPhoto(context);
Bitmap contactPhoto = localUserContactPhotoCache.get(uri);
if (contactPhoto == null) {
Cursor cursor = context.getContentResolver().query(uri, CONTENT_URI_PROJECTION,
null, null, null);
if (cursor != null && cursor.moveToFirst()) {
contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI,
cursor.getLong(0) + ""));
} else {
contactPhoto = getDefaultContactPhoto(context);
}
localUserContactPhotoCache.put(uri, contactPhoto);
}
return contactPhoto;
}
public static void clearCache() {
localUserContactPhotoCache.clear();
}
private static Bitmap getContactPhoto(Context context, Uri uri) {
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
if (inputStream == null) return ContactPhotoFactory.getDefaultContactPhoto(context);
else return BitmapFactory.decodeStream(inputStream);
}
}

View File

@@ -17,15 +17,6 @@
package org.thoughtcrime.securesms.contacts;
import java.util.ArrayList;
import java.util.List;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.RecipientsFormatter;
import android.content.Context;
import android.telephony.PhoneNumberUtils;
import android.text.Annotation;
@@ -39,11 +30,20 @@ import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.MultiAutoCompleteTextView;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.recipients.RecipientsFormatter;
import java.util.ArrayList;
import java.util.List;
/**
* Provide UI for editing the recipients of multi-media messages.
*/
@@ -52,7 +52,7 @@ public class RecipientsEditor extends MultiAutoCompleteTextView {
private final RecipientsEditorTokenizer mTokenizer;
private char mLastSeparator = ',';
private Context mContext;
public RecipientsEditor(Context context, AttributeSet attrs) {
super(context, attrs, android.R.attr.autoCompleteTextViewStyle);
mContext = context;
@@ -131,7 +131,7 @@ public class RecipientsEditor extends MultiAutoCompleteTextView {
public Recipients constructContactsFromInput() {
Recipients r = null;
try {
r = RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString() );
r = RecipientFactory.getRecipientsFromString(mContext, mTokenizer.getRawString(), false);
} catch (RecipientFormattingException e) {
Log.w( "RecipientsEditor", e);
}

View File

@@ -1,6 +1,6 @@
/**
/**
* Copyright (C) 2011 Whisper Systems
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -10,21 +10,22 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.crypto;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.EncryptingMmsDatabase;
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.mms.TextTransport;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.protocol.Prefix;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
@@ -38,13 +39,14 @@ import ws.com.google.android.mms.ContentType;
import ws.com.google.android.mms.MmsException;
import ws.com.google.android.mms.pdu.MultimediaMessagePdu;
import ws.com.google.android.mms.pdu.PduParser;
import android.content.Context;
import android.database.Cursor;
import android.util.Log;
import java.io.IOException;
import java.util.LinkedList;
import java.util.List;
/**
* A work queue for processing a number of encryption operations.
*
*
* @author Moxie Marlinspike
*/
@@ -52,12 +54,12 @@ public class DecryptingQueue {
private static List<Runnable> workQueue = new LinkedList<Runnable>();
private static Thread workerThread;
static {
workerThread = new WorkerThread(workQueue, "Async Decryption Thread");
workerThread.start();
}
}
public static void scheduleDecryption(Context context, MasterSecret masterSecret, long messageId, long threadId, MultimediaMessagePdu mms) {
MmsDecryptionItem runnable = new MmsDecryptionItem(context, masterSecret, messageId, threadId, mms);
synchronized (workQueue) {
@@ -65,7 +67,7 @@ public class DecryptingQueue {
workQueue.notifyAll();
}
}
public static void scheduleDecryption(Context context, MasterSecret masterSecret, long messageId, String originator, String body) {
DecryptionWorkItem runnable = new DecryptionWorkItem(context, masterSecret, messageId, body, originator);
synchronized (workQueue) {
@@ -73,16 +75,16 @@ public class DecryptingQueue {
workQueue.notifyAll();
}
}
public static void schedulePendingDecrypts(Context context, MasterSecret masterSecret) {
Cursor cursor = null;
Log.w("DecryptingQueue", "Processing pending decrypts...");
try {
cursor = DatabaseFactory.getSmsDatabase(context).getDecryptInProgressMessages();
if (cursor == null || cursor.getCount() == 0 || !cursor.moveToFirst())
return;
do {
scheduleDecryptFromCursor(context, masterSecret, cursor);
} while (cursor.moveToNext());
@@ -94,12 +96,12 @@ public class DecryptingQueue {
public static void scheduleRogueMessages(Context context, MasterSecret masterSecret, Recipient recipient) {
Cursor cursor = null;
try {
cursor = DatabaseFactory.getSmsDatabase(context).getEncryptedRogueMessages(recipient);
if (cursor == null || cursor.getCount() == 0 || !cursor.moveToFirst())
return;
do {
DatabaseFactory.getSmsDatabase(context).markAsDecrypting(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
scheduleDecryptFromCursor(context, masterSecret, cursor);
@@ -107,9 +109,9 @@ public class DecryptingQueue {
} finally {
if (cursor != null)
cursor.close();
}
}
}
private static void scheduleDecryptFromCursor(Context context, MasterSecret masterSecret, Cursor cursor) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.ID));
String originator = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
@@ -117,14 +119,14 @@ public class DecryptingQueue {
scheduleDecryption(context, masterSecret, id, originator, body);
}
private static class MmsDecryptionItem implements Runnable {
private long messageId;
private long threadId;
private Context context;
private MasterSecret masterSecret;
private MultimediaMessagePdu pdu;
public MmsDecryptionItem(Context context, MasterSecret masterSecret, long messageId, long threadId, MultimediaMessagePdu pdu) {
this.context = context;
this.masterSecret = masterSecret;
@@ -132,7 +134,7 @@ public class DecryptingQueue {
this.threadId = threadId;
this.pdu = pdu;
}
private byte[] getEncryptedData() {
for (int i=0;i<pdu.getBody().getPartsNum();i++) {
Log.w("DecryptingQueue", "Content type (" + i + "): " + new String(pdu.getBody().getPart(i).getContentType()));
@@ -140,16 +142,17 @@ public class DecryptingQueue {
return pdu.getBody().getPart(i).getData();
}
}
return null;
}
@Override
public void run() {
EncryptingMmsDatabase database = DatabaseFactory.getEncryptingMmsDatabase(context, masterSecret);
try {
String messageFrom = pdu.getFrom().getString();
Recipients recipients = RecipientFactory.getRecipientsFromString(context, messageFrom);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, messageFrom, false);
Recipient recipient = recipients.getPrimaryRecipient();
byte[] ciphertextPduBytes = getEncryptedData();
@@ -164,42 +167,41 @@ public class DecryptingQueue {
database.markAsNoSession(messageId, threadId);
return;
}
byte[] plaintextPduBytes;
synchronized (SessionCipher.CIPHER_LOCK) {
Log.w("DecryptingQueue", "Decrypting: " + Hex.toString(ciphertextPduBytes));
SessionCipher cipher = new SessionCipher(context, masterSecret, recipient, new TextTransport());
SessionCipher cipher = new SessionCipher(context, masterSecret, recipient, new TextTransport());
plaintextPduBytes = cipher.decryptMessage(ciphertextPduBytes);
}
MultimediaMessagePdu plaintextPdu = (MultimediaMessagePdu)new PduParser(plaintextPduBytes).parse();
Log.w("DecryptingQueue", "Successfully decrypted MMS!");
database.insertSecureDecryptedMessageReceived(plaintextPdu, threadId);
database.delete(messageId);
database.delete(messageId);
} catch (RecipientFormattingException rfe) {
Log.w("DecryptingQueue", rfe);
database.markAsDecryptFailed(messageId, threadId);
} catch (InvalidMessageException ime) {
Log.w("DecryptingQueue", ime);
database.markAsDecryptFailed(messageId, threadId);
database.markAsDecryptFailed(messageId, threadId);
} catch (MmsException mme) {
Log.w("DecryptingQueue", mme);
database.markAsDecryptFailed(messageId, threadId);
database.markAsDecryptFailed(messageId, threadId);
}
}
}
private static class DecryptionWorkItem implements Runnable {
private long messageId;
private Context context;
private MasterSecret masterSecret;
private String body;
private String originator;
public DecryptionWorkItem(Context context, MasterSecret masterSecret, long messageId, String body, String originator) {
this.context = context;
this.messageId = messageId;
@@ -207,7 +209,7 @@ public class DecryptingQueue {
this.body = body;
this.originator = originator;
}
private void handleRemoteAsymmetricEncrypt() {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
String plaintextBody;
@@ -215,17 +217,17 @@ public class DecryptingQueue {
synchronized (SessionCipher.CIPHER_LOCK) {
try {
Log.w("DecryptingQueue", "Parsing recipient for originator: " + originator);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, originator);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, originator, false);
Recipient recipient = recipients.getPrimaryRecipient();
Log.w("DecryptingQueue", "Parsed Recipient: " + recipient.getNumber());
if (!KeyUtil.isSessionFor(context, recipient)) {
Log.w("DecryptingQueue", "No such recipient session...");
database.markAsNoSession(messageId);
return;
}
SessionCipher cipher = new SessionCipher(context, masterSecret, recipient, new SmsTransportDetails());
SessionCipher cipher = new SessionCipher(context, masterSecret, recipient, new SmsTransportDetails());
plaintextBody = new String(cipher.decryptMessage(body.getBytes()));
} catch (InvalidMessageException e) {
Log.w("DecryptionQueue", e);
@@ -239,12 +241,13 @@ public class DecryptingQueue {
}
database.updateSecureMessageBody(masterSecret, messageId, plaintextBody);
MessageNotifier.updateNotification(context, masterSecret);
}
private void handleLocalAsymmetricEncrypt() {
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
String plaintextBody;
try {
AsymmetricMasterCipher asymmetricMasterCipher = new AsymmetricMasterCipher(MasterSecretUtil.getAsymmetricMasterSecret(context, masterSecret));
String encryptedBody = body.substring(Prefix.ASYMMETRIC_LOCAL_ENCRYPT.length());
@@ -258,15 +261,17 @@ public class DecryptingQueue {
database.markAsDecryptFailed(messageId);
return;
}
database.updateMessageBody(masterSecret, messageId, plaintextBody);
MessageNotifier.updateNotification(context, masterSecret);
}
public void run() {
@Override
public void run() {
if (body.startsWith(Prefix.ASYMMETRIC_ENCRYPT)) handleRemoteAsymmetricEncrypt();
else if (body.startsWith(Prefix.ASYMMETRIC_LOCAL_ENCRYPT)) handleLocalAsymmetricEncrypt();
}
}
}

View File

@@ -1,6 +1,6 @@
/**
/**
* Copyright (C) 2011 Whisper Systems
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -10,89 +10,111 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.crypto;
import javax.crypto.spec.SecretKeySpec;
import android.os.Parcel;
import android.os.Parcelable;
import org.bouncycastle.util.Arrays;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import javax.crypto.spec.SecretKeySpec;
/**
* When a user first initializes TextSecure, a few secrets
* are generated. These are:
*
*
* 1) A 128bit symmetric encryption key.
* 2) A 160bit symmetric MAC key.
* 3) An ECC keypair.
*
*
* The first two, along with the ECC keypair's private key, are
* then encrypted on disk using PBE.
*
*
* This class represents 1 and 2.
*
*
* @author Moxie Marlinspike
*/
public class MasterSecret implements Parcelable {
private final SecretKeySpec encryptionKey;
private final SecretKeySpec macKey;
public static final Parcelable.Creator<MasterSecret> CREATOR = new Parcelable.Creator<MasterSecret>() {
@Override
public MasterSecret createFromParcel(Parcel in) {
return new MasterSecret(in);
}
@Override
public MasterSecret[] newArray(int size) {
return new MasterSecret[size];
}
};
public MasterSecret(SecretKeySpec encryptionKey, SecretKeySpec macKey) {
this.encryptionKey = encryptionKey;
this.macKey = macKey;
}
private MasterSecret(Parcel in) {
byte[] encryptionKeyBytes = new byte[in.readInt()];
in.readByteArray(encryptionKeyBytes);
byte[] macKeyBytes = new byte[in.readInt()];
in.readByteArray(macKeyBytes);
this.encryptionKey = new SecretKeySpec(encryptionKeyBytes, "AES");
this.macKey = new SecretKeySpec(macKeyBytes, "HmacSHA1");
// SecretKeySpec does an internal copy in its constructor.
Arrays.fill(encryptionKeyBytes, (byte)0x00);
Arrays.fill(macKeyBytes, (byte)0x00);
}
public SecretKeySpec getEncryptionKey() {
return this.encryptionKey;
}
public SecretKeySpec getMacKey() {
return this.macKey;
}
@Override
public void writeToParcel(Parcel out, int flags) {
out.writeInt(encryptionKey.getEncoded().length);
out.writeByteArray(encryptionKey.getEncoded());
out.writeInt(macKey.getEncoded().length);
out.writeByteArray(macKey.getEncoded());
}
@Override
public int describeContents() {
return 0;
}
public MasterSecret parcelClone() {
Parcel thisParcel = Parcel.obtain();
Parcel thatParcel = Parcel.obtain();
byte[] bytes = null;
thisParcel.writeValue(this);
bytes = thisParcel.marshall();
thatParcel.unmarshall(bytes, 0, bytes.length);
thatParcel.setDataPosition(0);
MasterSecret that = (MasterSecret)thatParcel.readValue(MasterSecret.class.getClassLoader());
thisParcel.recycle();
thatParcel.recycle();
return that;
}
}

View File

@@ -22,6 +22,7 @@ import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteDatabase.CursorFactory;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import java.util.Collections;
import java.util.HashMap;
@@ -44,8 +45,9 @@ public class CanonicalAddressDatabase {
private static CanonicalAddressDatabase instance;
private final DatabaseHelper databaseHelper;
private final HashMap<String,Long> addressCache = new HashMap<String,Long>();
private final Map<String,String> idCache = Collections.synchronizedMap(new HashMap<String,String>());
private final Map<String,Long> addressCache = Collections.synchronizedMap(new HashMap<String,Long>());
private final Map<String,String> idCache = Collections.synchronizedMap(new HashMap<String,String>());
public static CanonicalAddressDatabase getInstance(Context context) {
synchronized (lock) {
@@ -58,18 +60,45 @@ public class CanonicalAddressDatabase {
private CanonicalAddressDatabase(Context context) {
databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
fillCache();
}
private void fillCache() {
Cursor cursor = null;
try {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
cursor = db.query(TABLE, null, null, null, null, null, null);
while (cursor != null && cursor.moveToNext()) {
long id = cursor.getLong(cursor.getColumnIndexOrThrow(ID_COLUMN));
String address = cursor.getString(cursor.getColumnIndexOrThrow(ADDRESS_COLUMN));
if (address == null || address.trim().length() == 0)
address = "Anonymous";
idCache.put(id+"", address);
addressCache.put(address, id);
}
} finally {
if (cursor != null)
cursor.close();
}
}
public String getAddressFromId(String id) {
if (id == null || id.trim().equals("")) return "Anonymous";
String cachedAddress = idCache.get(id);
if (cachedAddress != null)
return cachedAddress;
Cursor cursor = null;
try {
Log.w("CanonicalAddressDatabase", "Hitting DB on query [ID].");
SQLiteDatabase db = databaseHelper.getReadableDatabase();
cursor = db.query(TABLE, null, ID_COLUMN + " = ?", new String[] {id+""}, null, null, null);
@@ -127,7 +156,7 @@ public class CanonicalAddressDatabase {
private long getCanonicalAddressFromDatabase(String address) {
Cursor cursor = null;
try {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String[] selectionArguments = new String[] {address};
cursor = db.query(TABLE, ID_PROJECTION, SELECTION, selectionArguments, null, null, null);

View File

@@ -27,7 +27,9 @@ public class DatabaseFactory {
private static final int INTRODUCED_IDENTITIES_VERSION = 2;
private static final int INTRODUCED_INDEXES_VERSION = 3;
private static final int DATABASE_VERSION = 3;
private static final int INTRODUCED_DATE_SENT_VERSION = 4;
private static final int INTRODUCED_DRAFTS_VERSION = 5;
private static final int DATABASE_VERSION = 5;
private static final String DATABASE_NAME = "messages.db";
private static final Object lock = new Object();
@@ -47,6 +49,7 @@ public class DatabaseFactory {
private final MmsAddressDatabase mmsAddress;
private final MmsSmsDatabase mmsSmsDatabase;
private final IdentityDatabase identityDatabase;
private final DraftDatabase draftDatabase;
public static DatabaseFactory getInstance(Context context) {
synchronized (lock) {
@@ -115,6 +118,10 @@ public class DatabaseFactory {
return getInstance(context).identityDatabase;
}
public static DraftDatabase getDraftDatabase(Context context) {
return getInstance(context).draftDatabase;
}
private DatabaseFactory(Context context) {
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
this.sms = new SmsDatabase(context, databaseHelper);
@@ -126,6 +133,7 @@ public class DatabaseFactory {
this.mmsAddress = new MmsAddressDatabase(context, databaseHelper);
this.mmsSmsDatabase = new MmsSmsDatabase(context, databaseHelper);
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
this.draftDatabase = new DraftDatabase(context, databaseHelper);
}
public void close() {
@@ -148,12 +156,14 @@ public class DatabaseFactory {
db.execSQL(ThreadDatabase.CREATE_TABLE);
db.execSQL(MmsAddressDatabase.CREATE_TABLE);
db.execSQL(IdentityDatabase.CREATE_TABLE);
db.execSQL(DraftDatabase.CREATE_TABLE);
executeStatements(db, SmsDatabase.CREATE_INDEXS);
executeStatements(db, MmsDatabase.CREATE_INDEXS);
executeStatements(db, PartDatabase.CREATE_INDEXS);
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
executeStatements(db, DraftDatabase.CREATE_INDEXS);
// db.execSQL(CanonicalAddress.CREATE_TABLE);
}
@@ -171,6 +181,29 @@ public class DatabaseFactory {
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
}
if (oldVersion < INTRODUCED_DATE_SENT_VERSION) {
db.beginTransaction();
db.execSQL("ALTER TABLE " + SmsDatabase.TABLE_NAME +
" ADD COLUMN " + SmsDatabase.DATE_SENT + " INTEGER;");
db.execSQL("UPDATE " + SmsDatabase.TABLE_NAME +
" SET " + SmsDatabase.DATE_SENT + " = " + SmsDatabase.DATE_RECEIVED + ";");
db.execSQL("ALTER TABLE " + MmsDatabase.TABLE_NAME +
" ADD COLUMN " + MmsDatabase.DATE_RECEIVED + " INTEGER;");
db.execSQL("UPDATE " + MmsDatabase.TABLE_NAME +
" SET " + MmsDatabase.DATE_RECEIVED + " = " + MmsDatabase.DATE_SENT + ";");
db.setTransactionSuccessful();
db.endTransaction();
}
if (oldVersion < INTRODUCED_DRAFTS_VERSION) {
db.beginTransaction();
db.execSQL(DraftDatabase.CREATE_TABLE);
executeStatements(db, DraftDatabase.CREATE_INDEXS);
db.setTransactionSuccessful();
db.endTransaction();
}
}
private void executeStatements(SQLiteDatabase db, String[] statements) {

View File

@@ -0,0 +1,102 @@
package org.thoughtcrime.securesms.database;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.util.InvalidMessageException;
import java.util.LinkedList;
import java.util.List;
public class DraftDatabase extends Database {
private static final String TABLE_NAME = "drafts";
public static final String ID = "_id";
public static final String THREAD_ID = "thread_id";
public static final String DRAFT_TYPE = "type";
public static final String DRAFT_VALUE = "value";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
THREAD_ID + " INTEGER, " + DRAFT_TYPE + " TEXT, " + DRAFT_VALUE + " TEXT);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS draft_thread_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
};
public DraftDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
}
public void insertDrafts(MasterCipher masterCipher, long threadId, List<Draft> drafts) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
for (Draft draft : drafts) {
ContentValues values = new ContentValues(3);
values.put(THREAD_ID, threadId);
values.put(DRAFT_TYPE, masterCipher.encryptBody(draft.getType()));
values.put(DRAFT_VALUE, masterCipher.encryptBody(draft.getValue()));
db.insert(TABLE_NAME, null, values);
}
}
public void clearDrafts(long threadId) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""});
}
public List<Draft> getDrafts(MasterCipher masterCipher, long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
List<Draft> results = new LinkedList<Draft>();
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, null, THREAD_ID + " = ?", new String[] {threadId+""}, null, null, null);
while (cursor != null && cursor.moveToNext()) {
try {
String encryptedType = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_TYPE));
String encryptedValue = cursor.getString(cursor.getColumnIndexOrThrow(DRAFT_VALUE));
results.add(new Draft(masterCipher.decryptBody(encryptedType),
masterCipher.decryptBody(encryptedValue)));
} catch (InvalidMessageException ime) {
Log.w("DraftDatabase", ime);
}
}
return results;
} finally {
if (cursor != null)
cursor.close();
}
}
public static class Draft {
public static final String TEXT = "text";
public static final String IMAGE = "image";
public static final String VIDEO = "video";
public static final String AUDIO = "audio";
private final String type;
private final String value;
public Draft(String type, String value) {
this.type = type;
this.value = value;
}
public String getType() {
return type;
}
public String getValue() {
return value;
}
}
}

View File

@@ -28,6 +28,7 @@ import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Trimmer;
import ws.com.google.android.mms.InvalidHeaderValueException;
import ws.com.google.android.mms.MmsException;
@@ -49,7 +50,8 @@ public class MmsDatabase extends Database {
public static final String TABLE_NAME = "mms";
public static final String ID = "_id";
private static final String THREAD_ID = "thread_id";
public static final String DATE = "date";
public static final String DATE_SENT = "date";
public static final String DATE_RECEIVED = "date_received";
public static final String MESSAGE_BOX = "msg_box";
private static final String READ = "read";
private static final String MESSAGE_ID = "m_id";
@@ -78,7 +80,7 @@ public class MmsDatabase extends Database {
private static final String DELIVERY_REPORT = "d_rpt";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
THREAD_ID + " INTEGER, " + DATE + " INTEGER, " + MESSAGE_BOX + " INTEGER, " +
THREAD_ID + " INTEGER, " + DATE_SENT + " INTEGER, " + DATE_RECEIVED + " INTEGER, " + MESSAGE_BOX + " INTEGER, " +
READ + " INTEGER DEFAULT 0, " + MESSAGE_ID + " TEXT, " + SUBJECT + " TEXT, " +
SUBJECT_CHARSET + " INTEGER, " + CONTENT_TYPE + " TEXT, " + CONTENT_LOCATION + " TEXT, " +
EXPIRY + " INTEGER, " + MESSAGE_CLASS + " TEXT, " + MESSAGE_TYPE + " INTEGER, " +
@@ -140,7 +142,7 @@ public class MmsDatabase extends Database {
try {
EncodedStringValue encodedString = headers.getEncodedStringValue(PduHeaders.FROM);
String fromString = new String(encodedString.getTextString(), CharacterSets.MIMENAME_ISO_8859_1);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, fromString);
Recipients recipients = RecipientFactory.getRecipientsFromString(context, fromString, false);
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
@@ -297,6 +299,10 @@ public class MmsDatabase extends Database {
contentValues.put(THREAD_ID, threadId);
contentValues.put(CONTENT_LOCATION, contentLocation);
contentValues.put(STATUS, Types.DOWNLOAD_INITIALIZED);
contentValues.put(DATE_RECEIVED, System.currentTimeMillis() / 1000);
if (!contentValues.containsKey(DATE_SENT))
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
long messageId = insertMediaMessage(retrieved, contentValues);
return messageId;
@@ -327,8 +333,10 @@ public class MmsDatabase extends Database {
contentValues.put(MESSAGE_BOX, Types.MESSAGE_BOX_INBOX);
contentValues.put(THREAD_ID, threadId);
contentValues.put(STATUS, Types.DOWNLOAD_INITIALIZED);
if (!contentValues.containsKey(DATE))
contentValues.put(DATE, System.currentTimeMillis() / 1000);
contentValues.put(DATE_RECEIVED, System.currentTimeMillis() / 1000);
if (!contentValues.containsKey(DATE_SENT))
contentValues.put(DATE_SENT, contentValues.getAsLong(DATE_RECEIVED));
long messageId = db.insert(TABLE_NAME, null, contentValues);
addressDatabase.insertAddressesForId(messageId, headers);
@@ -336,6 +344,7 @@ public class MmsDatabase extends Database {
notifyConversationListeners(threadId);
DatabaseFactory.getThreadDatabase(context).update(threadId);
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
Trimmer.trimThread(context, threadId);
return messageId;
} catch (RecipientFormattingException rfe) {
@@ -353,9 +362,11 @@ public class MmsDatabase extends Database {
contentValues.put(THREAD_ID, threadId);
contentValues.put(READ, 1);
contentValues.put(DATE_RECEIVED, contentValues.getAsLong(DATE_SENT));
long messageId = insertMediaMessage(sendRequest, contentValues);
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
Trimmer.trimThread(context, threadId);
return messageId;
}
@@ -419,6 +430,35 @@ public class MmsDatabase extends Database {
}
}
/*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) {
date = date / 1000;
Cursor cursor = null;
try {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
String where = THREAD_ID + " = ? AND (CASE " + MESSAGE_BOX;
for (int outgoingType : Types.OUTGOING_MAILBOX_TYPES) {
where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date;
}
where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)");
Log.w("MmsDatabase", "Executing trim query: " + where);
cursor = db.query(TABLE_NAME, new String[] {ID}, where, new String[] {threadId+""}, null, null, null);
while (cursor != null && cursor.moveToNext()) {
Log.w("MmsDatabase", "Trimming: " + cursor.getLong(0));
delete(cursor.getLong(0));
}
} finally {
if (cursor != null)
cursor.close();
}
}
public void deleteAllThreads() {
DatabaseFactory.getPartDatabase(context).deleteAllParts();
DatabaseFactory.getMmsAddressDatabase(context).deleteAllAddresses();
@@ -428,10 +468,11 @@ public class MmsDatabase extends Database {
}
public Cursor getCarrierMmsInformation(String apn) {
Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current");
String selection = (apn == null || apn.trim().length() == 0) ? null : String.format("apn = '%s'", apn.trim());
Uri uri = Uri.withAppendedPath(Uri.parse("content://telephony/carriers"), "current");
String selection = (apn == null || apn.trim().length() == 0) ? null : "apn = ?";
String[] selectionArgs = (apn == null || apn.trim().length() == 0) ? null : new String[] {apn.trim()};
return context.getContentResolver().query(uri, null, selection, null, null);
return context.getContentResolver().query(uri, null, selection, selectionArgs, null);
}
private PduHeaders getHeadersForId(long messageId) throws MmsException {
@@ -479,7 +520,7 @@ public class MmsDatabase extends Database {
phb.addOctet(REPORT_ALLOWED, PduHeaders.REPORT_ALLOWED);
phb.addOctet(RETRIEVE_STATUS, PduHeaders.RETRIEVE_STATUS);
phb.addOctet(STATUS, PduHeaders.STATUS);
phb.addLong(DATE, PduHeaders.DATE);
phb.addLong(DATE_SENT, PduHeaders.DATE);
phb.addLong(DELIVERY_TIME, PduHeaders.DELIVERY_TIME);
phb.addLong(EXPIRY, PduHeaders.EXPIRY);
phb.addLong(MESSAGE_SIZE, PduHeaders.MESSAGE_SIZE);
@@ -509,7 +550,7 @@ public class MmsDatabase extends Database {
cvb.add(REPORT_ALLOWED, headers.getOctet(PduHeaders.REPORT_ALLOWED));
cvb.add(RETRIEVE_STATUS, headers.getOctet(PduHeaders.RETRIEVE_STATUS));
cvb.add(STATUS, headers.getOctet(PduHeaders.STATUS));
cvb.add(DATE, headers.getLongInteger(PduHeaders.DATE));
cvb.add(DATE_SENT, headers.getLongInteger(PduHeaders.DATE));
cvb.add(DELIVERY_TIME, headers.getLongInteger(PduHeaders.DELIVERY_TIME));
cvb.add(EXPIRY, headers.getLongInteger(PduHeaders.EXPIRY));
cvb.add(MESSAGE_SIZE, headers.getLongInteger(PduHeaders.MESSAGE_SIZE));
@@ -544,12 +585,23 @@ public class MmsDatabase extends Database {
public static final int DOWNLOAD_SOFT_FAILURE = 4;
public static final int DOWNLOAD_HARD_FAILURE = 5;
public static final int[] OUTGOING_MAILBOX_TYPES = {Types.MESSAGE_BOX_OUTBOX,
Types.MESSAGE_BOX_SENT,
Types.MESSAGE_BOX_SECURE_OUTBOX,
Types.MESSAGE_BOX_SENT_FAILED,
Types.MESSAGE_BOX_SECURE_SENT};
public static boolean isSecureMmsBox(long mailbox) {
return mailbox == Types.MESSAGE_BOX_SECURE_OUTBOX || mailbox == Types.MESSAGE_BOX_SECURE_SENT || mailbox == Types.MESSAGE_BOX_SECURE_INBOX;
}
public static boolean isOutgoingMmsBox(long mailbox) {
return mailbox == Types.MESSAGE_BOX_OUTBOX || mailbox == Types.MESSAGE_BOX_SENT || mailbox == Types.MESSAGE_BOX_SECURE_OUTBOX || mailbox == Types.MESSAGE_BOX_SENT_FAILED || mailbox == Types.MESSAGE_BOX_SECURE_SENT;
for (int outgoingMailboxType : OUTGOING_MAILBOX_TYPES) {
if (mailbox == outgoingMailboxType)
return true;
}
return false;
}
public static boolean isPendingMmsBox(long mailbox) {

View File

@@ -35,6 +35,9 @@ public class MmsSmsDatabase extends Database {
public static final String MMS_GROUP_SENT_COUNT = "mms_group_sent_count";
public static final String MMS_GROUP_SEND_FAILED_COUNT = "mms_group_sent_failed_count";
public static final String DATE_SENT = "date_sent";
public static final String DATE_RECEIVED = "date_received";
public MmsSmsDatabase(Context context, SQLiteOpenHelper databaseHelper) {
super(context, databaseHelper);
}
@@ -75,10 +78,10 @@ public class MmsSmsDatabase extends Database {
"WHEN " + SmsDatabase.Types.FAILED_TYPE + " THEN 1 " +
"ELSE 0 END)";
String[] projection = {"_id", "body", "type", "address", "subject", "normalized_date AS date", "m_type", "msg_box", "transport_type", "COUNT(_id) AS group_size", mmsGroupSentCount + " AS mms_group_sent_count", mmsGroupSentFailedCount + " AS mms_group_sent_failed_count", smsGroupSentCount + " AS sms_group_sent_count", smsGroupSentFailedCount + " AS sms_group_sent_failed_count", smsCaseSecurity + " AS sms_collate", mmsCaseSecurity + " AS mms_collate"};
String order = "normalized_date ASC";
String[] projection = {"_id", "body", "type", "address", "subject", "status", "normalized_date_sent AS date_sent", "normalized_date_received AS date_received", "m_type", "msg_box", "transport_type", "COUNT(_id) AS group_size", mmsGroupSentCount + " AS mms_group_sent_count", mmsGroupSentFailedCount + " AS mms_group_sent_failed_count", smsGroupSentCount + " AS sms_group_sent_count", smsGroupSentFailedCount + " AS sms_group_sent_failed_count", smsCaseSecurity + " AS sms_collate", mmsCaseSecurity + " AS mms_collate"};
String order = "normalized_date_received ASC";
String selection = "thread_id = " + threadId;
String groupBy = "normalized_date / 1000, sms_collate, mms_collate";
String groupBy = "normalized_date_sent / 1000, sms_collate, mms_collate";
Cursor cursor = queryTables(projection, selection, order, groupBy, null);
setNotifyConverationListeners(cursor, threadId);
@@ -87,8 +90,11 @@ public class MmsSmsDatabase extends Database {
}
public Cursor getConversation(long threadId) {
String[] projection = {"_id", "body", "type", "address", "subject", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
String order = "normalized_date ASC";
String[] projection = {"_id", "body", "type", "address", "subject",
"normalized_date_sent AS date_sent",
"normalized_date_received AS date_received",
"m_type", "msg_box", "status", "transport_type"};
String order = "normalized_date_received ASC";
String selection = "thread_id = " + threadId;
Cursor cursor = queryTables(projection, selection, order, null, null);
@@ -98,8 +104,11 @@ public class MmsSmsDatabase extends Database {
}
public Cursor getConversationSnippet(long threadId) {
String[] projection = {"_id", "body", "type", "address", "subject", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
String order = "normalized_date DESC";
String[] projection = {"_id", "body", "type", "address", "subject",
"normalized_date_sent AS date_sent",
"normalized_date_received AS date_received",
"m_type", "msg_box", "transport_type"};
String order = "normalized_date_received DESC";
String selection = "thread_id = " + threadId;
Cursor cursor = queryTables(projection, selection, order, null, "1");
@@ -107,8 +116,11 @@ public class MmsSmsDatabase extends Database {
}
public Cursor getUnread() {
String[] projection = {"_id", "body", "read", "type", "address", "subject", "thread_id", "normalized_date AS date", "m_type", "msg_box", "transport_type"};
String order = "normalized_date ASC";
String[] projection = {"_id", "body", "read", "type", "address", "subject", "thread_id",
"normalized_date_sent AS date_sent",
"normalized_date_received AS date_received",
"m_type", "msg_box", "transport_type"};
String order = "normalized_date_received ASC";
String selection = "read = 0";
Cursor cursor = queryTables(projection, selection, order, null, null);
@@ -123,8 +135,8 @@ public class MmsSmsDatabase extends Database {
}
private Cursor queryTables(String[] projection, String selection, String order, String groupBy, String limit) {
String[] mmsProjection = {"date * 1000 AS normalized_date", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
String[] smsProjection = {"date * 1 AS normalized_date", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
String[] mmsProjection = {"date * 1000 AS normalized_date_sent", "date_received * 1000 AS normalized_date_received", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "status", "transport_type"};
String[] smsProjection = {"date_sent * 1 AS normalized_date_sent", "date * 1 AS normalized_date_received", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "status", "transport_type"};
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
@@ -140,6 +152,7 @@ public class MmsSmsDatabase extends Database {
mmsColumnsPresent.add("m_type");
mmsColumnsPresent.add("msg_box");
mmsColumnsPresent.add("date");
mmsColumnsPresent.add("date_received");
mmsColumnsPresent.add("read");
mmsColumnsPresent.add("thread_id");
@@ -149,12 +162,14 @@ public class MmsSmsDatabase extends Database {
smsColumnsPresent.add("type");
smsColumnsPresent.add("address");
smsColumnsPresent.add("subject");
smsColumnsPresent.add("date_sent");
smsColumnsPresent.add("date");
smsColumnsPresent.add("read");
smsColumnsPresent.add("thread_id");
smsColumnsPresent.add("status");
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery("transport_type", mmsProjection, mmsColumnsPresent, 0, "mms", selection, null, null, null);
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery("transport_type", smsProjection, smsColumnsPresent, 0, "sms", selection, null, null, null);
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery("transport_type", mmsProjection, mmsColumnsPresent, 2, "mms", selection, null, null, null);
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery("transport_type", smsProjection, smsColumnsPresent, 2, "sms", selection, null, null, null);
SQLiteQueryBuilder unionQueryBuilder = new SQLiteQueryBuilder();
String unionQuery = unionQueryBuilder.buildUnionQuery(new String[] {smsSubQuery, mmsSubQuery}, order, null);

View File

@@ -27,6 +27,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Trimmer;
import java.util.ArrayList;
import java.util.List;
@@ -45,7 +46,8 @@ public class SmsDatabase extends Database {
public static final String THREAD_ID = "thread_id";
public static final String ADDRESS = "address";
public static final String PERSON = "person";
public static final String DATE = "date";
public static final String DATE_RECEIVED = "date";
public static final String DATE_SENT = "date_sent";
public static final String PROTOCOL = "protocol";
public static final String READ = "read";
public static final String STATUS = "status";
@@ -56,10 +58,10 @@ public class SmsDatabase extends Database {
public static final String SERVICE_CENTER = "service_center";
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " integer PRIMARY KEY, " +
THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + PERSON + " INTEGER, " + DATE + " INTEGER, " +
PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT -1," +
TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " + SUBJECT + " TEXT, " + BODY + " TEXT, " +
SERVICE_CENTER + " TEXT);";
THREAD_ID + " INTEGER, " + ADDRESS + " TEXT, " + PERSON + " INTEGER, " + DATE_RECEIVED + " INTEGER, " +
DATE_SENT + " INTEGER, " + PROTOCOL + " INTEGER, " + READ + " INTEGER DEFAULT 0, " +
STATUS + " INTEGER DEFAULT -1," + TYPE + " INTEGER, " + REPLY_PATH_PRESENT + " INTEGER, " +
SUBJECT + " TEXT, " + BODY + " TEXT, " + SERVICE_CENTER + " TEXT);";
public static final String[] CREATE_INDEXS = {
"CREATE INDEX IF NOT EXISTS sms_thread_id_index ON " + TABLE_NAME + " (" + THREAD_ID + ");",
@@ -82,16 +84,17 @@ public class SmsDatabase extends Database {
notifyConversationListeners(getThreadIdForMessage(id));
}
private long insertMessageReceived(SmsMessage message, String body, long type) {
private long insertMessageReceived(SmsMessage message, String body, long type, long timeSent) {
List<Recipient> recipientList = new ArrayList<Recipient>(1);
recipientList.add(new Recipient(null, message.getDisplayOriginatingAddress(), null));
recipientList.add(new Recipient(null, message.getDisplayOriginatingAddress(), null, null));
Recipients recipients = new Recipients(recipientList);
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
ContentValues values = new ContentValues(6);
values.put(ADDRESS, message.getDisplayOriginatingAddress());
values.put(DATE, Long.valueOf(System.currentTimeMillis()));
values.put(DATE_RECEIVED, Long.valueOf(System.currentTimeMillis()));
values.put(DATE_SENT, timeSent);
values.put(PROTOCOL, message.getProtocolIdentifier());
values.put(READ, Integer.valueOf(0));
@@ -110,6 +113,8 @@ public class SmsDatabase extends Database {
DatabaseFactory.getThreadDatabase(context).setUnread(threadId);
DatabaseFactory.getThreadDatabase(context).update(threadId);
notifyConversationListeners(threadId);
Trimmer.trimThread(context, threadId);
return messageId;
}
@@ -168,6 +173,16 @@ public class SmsDatabase extends Database {
updateType(id, Types.SENT_TYPE);
}
public void markStatus(long id, int status) {
Log.w("MessageDatabase", "Updating ID: " + id + " to status: " + status);
ContentValues contentValues = new ContentValues();
contentValues.put(STATUS, status);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {id+""});
notifyConversationListeners(getThreadIdForMessage(id));
}
public void markAsSentFailed(long id) {
updateType(id, Types.FAILED_TYPE);
}
@@ -198,11 +213,12 @@ public class SmsDatabase extends Database {
}
public long insertSecureMessageReceived(SmsMessage message, String body) {
return insertMessageReceived(message, body, Types.DECRYPT_IN_PROGRESS_TYPE);
return insertMessageReceived(message, body, Types.DECRYPT_IN_PROGRESS_TYPE,
message.getTimestampMillis());
}
public long insertMessageReceived(SmsMessage message, String body) {
return insertMessageReceived(message, body, Types.INBOX_TYPE);
return insertMessageReceived(message, body, Types.INBOX_TYPE, message.getTimestampMillis());
}
public long insertMessageSent(String address, long threadId, String body, long date, long type) {
@@ -211,16 +227,18 @@ public class SmsDatabase extends Database {
contentValues.put(ADDRESS, address);
contentValues.put(THREAD_ID, threadId);
contentValues.put(BODY, body);
contentValues.put(DATE, date);
contentValues.put(DATE_RECEIVED, date);
contentValues.put(DATE_SENT, date);
contentValues.put(READ, 1);
contentValues.put(TYPE, type);
SQLiteDatabase db = databaseHelper.getWritableDatabase();
long messageId = db.insert(TABLE_NAME, ADDRESS, contentValues);
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
DatabaseFactory.getThreadDatabase(context).update(threadId);
notifyConversationListeners(threadId);
Trimmer.trimThread(context, threadId);
return messageId;
}
@@ -262,6 +280,18 @@ public class SmsDatabase extends Database {
db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""});
}
/*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
String where = THREAD_ID + " = ? AND (CASE " + TYPE;
for (int outgoingType : Types.OUTGOING_MESSAGE_TYPES) {
where += " WHEN " + outgoingType + " THEN " + DATE_SENT + " < " + date;
}
where += (" ELSE " + DATE_RECEIVED + " < " + date + " END)");
db.delete(TABLE_NAME, where, new String[] {threadId+""});
}
/*package*/ void deleteThreads(Set<Long> threadIds) {
SQLiteDatabase db = databaseHelper.getWritableDatabase();
@@ -297,8 +327,27 @@ public class SmsDatabase extends Database {
}
/*package*/ SQLiteStatement createInsertStatement(SQLiteDatabase database) {
return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " + PERSON + ", " + DATE + ", " + PROTOCOL + ", " + READ + ", " + STATUS + ", " + TYPE + ", " + REPLY_PATH_PRESENT + ", " + SUBJECT + ", " + BODY + ", " + SERVICE_CENTER + ", THREAD_ID) " +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
return database.compileStatement("INSERT INTO " + TABLE_NAME + " (" + ADDRESS + ", " +
PERSON + ", " +
DATE_SENT + ", " +
DATE_RECEIVED + ", " +
PROTOCOL + ", " +
READ + ", " +
STATUS + ", " +
TYPE + ", " +
REPLY_PATH_PRESENT + ", " +
SUBJECT + ", " +
BODY + ", " +
SERVICE_CENTER +
", THREAD_ID) " +
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
}
public static class Status {
public static final int STATUS_NONE = -1;
public static final int STATUS_COMPLETE = 0;
public static final int STATUS_PENDING = 32;
public static final int STATUS_FAILED = 64;
}
public static class Types {
@@ -315,12 +364,21 @@ public class SmsDatabase extends Database {
public static final int DECRYPT_IN_PROGRESS_TYPE = 47; // Messages are in the process of being asymmetricaly decrypted.
public static final int NO_SESSION_TYPE = 48; // Messages were received with async encryption but there is no session yet.
public static final int[] OUTGOING_MESSAGE_TYPES = {SENT_TYPE, SENT_PENDING, ENCRYPTING_TYPE,
ENCRYPTED_OUTBOX_TYPE, SECURE_SENT_TYPE,
FAILED_TYPE};
public static boolean isFailedMessageType(long type) {
return type == FAILED_TYPE;
}
public static boolean isOutgoingMessageType(long type) {
return type == SENT_TYPE || type == SENT_PENDING || type == ENCRYPTING_TYPE || type == ENCRYPTED_OUTBOX_TYPE || type == SECURE_SENT_TYPE || type == FAILED_TYPE;
for (int outgoingType : OUTGOING_MESSAGE_TYPES) {
if (type == outgoingType)
return true;
}
return false;
}
public static boolean isPendingMessageType(long type) {

View File

@@ -77,17 +77,18 @@ public class SmsMigrator {
{
addStringToStatement(statement, cursor, 1, SmsDatabase.ADDRESS);
addIntToStatement(statement, cursor, 2, SmsDatabase.PERSON);
addIntToStatement(statement, cursor, 3, SmsDatabase.DATE);
addIntToStatement(statement, cursor, 4, SmsDatabase.PROTOCOL);
addIntToStatement(statement, cursor, 5, SmsDatabase.READ);
addIntToStatement(statement, cursor, 6, SmsDatabase.STATUS);
addIntToStatement(statement, cursor, 7, SmsDatabase.TYPE);
addIntToStatement(statement, cursor, 8, SmsDatabase.REPLY_PATH_PRESENT);
addStringToStatement(statement, cursor, 9, SmsDatabase.SUBJECT);
addEncryptedStringToStatement(context, statement, cursor, masterSecret, 10, SmsDatabase.BODY);
addStringToStatement(statement, cursor, 11, SmsDatabase.SERVICE_CENTER);
addIntToStatement(statement, cursor, 3, SmsDatabase.DATE_RECEIVED);
addIntToStatement(statement, cursor, 4, SmsDatabase.DATE_RECEIVED);
addIntToStatement(statement, cursor, 5, SmsDatabase.PROTOCOL);
addIntToStatement(statement, cursor, 6, SmsDatabase.READ);
addIntToStatement(statement, cursor, 7, SmsDatabase.STATUS);
addIntToStatement(statement, cursor, 8, SmsDatabase.TYPE);
addIntToStatement(statement, cursor, 9, SmsDatabase.REPLY_PATH_PRESENT);
addStringToStatement(statement, cursor, 10, SmsDatabase.SUBJECT);
addEncryptedStringToStatement(context, statement, cursor, masterSecret, 11, SmsDatabase.BODY);
addStringToStatement(statement, cursor, 12, SmsDatabase.SERVICE_CENTER);
statement.bindLong(12, threadId);
statement.bindLong(13, threadId);
}
private static String getTheirCanonicalAddress(Context context, String theirRecipientId) {
@@ -128,7 +129,7 @@ public class SmsMigrator {
try {
if (sb.length() == 0) return null;
else return RecipientFactory.getRecipientsFromString(context, sb.toString());
else return RecipientFactory.getRecipientsFromString(context, sb.toString(), true);
} catch (RecipientFormattingException rfe) {
Log.w("SmsMigrator", rfe);
return null;

View File

@@ -21,8 +21,10 @@ import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.util.Log;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.Arrays;
@@ -143,6 +145,56 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners();
}
public void trimAllThreads(int length, ProgressListener listener) {
Cursor cursor = null;
int threadCount = 0;
int complete = 0;
try {
cursor = this.getConversationList();
if (cursor != null)
threadCount = cursor.getCount();
while (cursor != null && cursor.moveToNext()) {
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ID));
trimThread(threadId, length);
listener.onProgress(++complete, threadCount);
}
} finally {
if (cursor != null)
cursor.close();
}
}
public void trimThread(long threadId, int length) {
Log.w("ThreadDatabase", "Trimming thread: " + threadId + " to: " + length);
Cursor cursor = null;
try {
cursor = DatabaseFactory.getMmsSmsDatabase(context).getConversation(threadId);
if (cursor != null && cursor.getCount() > length) {
Log.w("ThreadDatabase", "Cursor count is greater than length!");
cursor.moveToPosition(cursor.getCount() - length);
long lastTweetDate = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_RECEIVED));
Log.w("ThreadDatabase", "Cut off tweet date: " + lastTweetDate);
DatabaseFactory.getSmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
DatabaseFactory.getMmsDatabase(context).deleteMessagesInThreadBeforeDate(threadId, lastTweetDate);
update(threadId);
notifyConversationListeners(threadId);
}
} finally {
if (cursor != null)
cursor.close();
}
}
public void setRead(long threadId) {
ContentValues contentValues = new ContentValues(1);
contentValues.put(READ, 1);
@@ -262,6 +314,25 @@ public class ThreadDatabase extends Database {
}
}
public Recipients getRecipientsForThreadId(Context context, long threadId) {
SQLiteDatabase db = databaseHelper.getReadableDatabase();
Cursor cursor = null;
try {
cursor = db.query(TABLE_NAME, null, ID + " = ?", new String[] {threadId+""}, null, null, null);
if (cursor != null && cursor.moveToFirst()) {
String recipientIds = cursor.getString(cursor.getColumnIndexOrThrow(RECIPIENT_IDS));
return RecipientFactory.getRecipientsForIds(context, recipientIds, false);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
public void update(long threadId) {
MmsSmsDatabase mmsSmsDatabase = DatabaseFactory.getMmsSmsDatabase(context);
long count = mmsSmsDatabase.getConversationCount(threadId);
@@ -279,7 +350,7 @@ public class ThreadDatabase extends Database {
if (cursor != null && cursor.moveToFirst()) {
updateThread(threadId, count,
cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY)),
cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.DATE)));
cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_RECEIVED)));
} else {
deleteThread(threadId);
}
@@ -290,4 +361,8 @@ public class ThreadDatabase extends Database {
notifyConversationListListeners();
}
public static interface ProgressListener {
public void onProgress(int complete, int total);
}
}

View File

@@ -30,7 +30,8 @@ import org.thoughtcrime.securesms.recipients.Recipients;
public abstract class DisplayRecord {
private final Recipients recipients;
private final long date;
private final long dateSent;
private final long dateReceived;
private final long threadId;
private String body;
@@ -39,11 +40,12 @@ public abstract class DisplayRecord {
protected boolean processedKeyExchange;
protected boolean staleKeyExchange;
public DisplayRecord(Recipients recipients, long date, long threadId) {
this.threadId = threadId;
this.recipients = recipients;
this.date = date;
this.emphasis = false;
public DisplayRecord(Recipients recipients, long dateSent, long dateReceived, long threadId) {
this.threadId = threadId;
this.recipients = recipients;
this.dateSent = dateSent;
this.dateReceived = dateReceived;
this.emphasis = false;
}
public void setEmphasis(boolean emphasis) {
@@ -81,8 +83,12 @@ public abstract class DisplayRecord {
return recipients;
}
public long getDate() {
return date;
public long getDateSent() {
return dateSent;
}
public long getDateReceived() {
return dateReceived;
}
public long getThreadId() {

View File

@@ -42,10 +42,12 @@ public class MediaMmsMessageRecord extends MessageRecord {
private final long mailbox;
public MediaMmsMessageRecord(Context context, long id, Recipients recipients,
Recipient individualRecipient, long date, long threadId,
SlideDeck slideDeck, long mailbox, GroupData groupData)
Recipient individualRecipient, long dateSent, long dateReceived,
long threadId, SlideDeck slideDeck, long mailbox,
GroupData groupData)
{
super(id, recipients, individualRecipient, date, threadId, groupData);
super(id, recipients, individualRecipient, dateSent, dateReceived,
threadId, DELIVERY_STATUS_NONE, groupData);
this.slideDeck = slideDeck;
this.mailbox = mailbox;

View File

@@ -29,18 +29,26 @@ import org.thoughtcrime.securesms.recipients.Recipients;
*/
public abstract class MessageRecord extends DisplayRecord {
public static final int DELIVERY_STATUS_NONE = 0;
public static final int DELIVERY_STATUS_RECEIVED = 1;
public static final int DELIVERY_STATUS_PENDING = 2;
public static final int DELIVERY_STATUS_FAILED = 3;
private final Recipient individualRecipient;
private final long id;
private final int deliveryStatus;
private final GroupData groupData;
public MessageRecord(long id, Recipients recipients,
Recipient individualRecipient,
long date, long threadId,
long dateSent, long dateReceived,
long threadId, int deliveryStatus,
GroupData groupData)
{
super(recipients, date, threadId);
super(recipients, dateSent, dateReceived, threadId);
this.id = id;
this.individualRecipient = individualRecipient;
this.deliveryStatus = deliveryStatus;
this.groupData = groupData;
}
@@ -58,6 +66,14 @@ public abstract class MessageRecord extends DisplayRecord {
return id;
}
public int getDeliveryStatus() {
return deliveryStatus;
}
public boolean isDelivered() {
return getDeliveryStatus() == DELIVERY_STATUS_RECEIVED;
}
public boolean isStaleKeyExchange() {
return this.staleKeyExchange;
}

View File

@@ -37,11 +37,13 @@ public class NotificationMmsMessageRecord extends MessageRecord {
private final byte[] transactionId;
public NotificationMmsMessageRecord(long id, Recipients recipients, Recipient individualRecipient,
long date, long threadId, byte[] contentLocation,
long messageSize, long expiry,
long dateSent, long dateReceived, long threadId,
byte[] contentLocation, long messageSize, long expiry,
int status, byte[] transactionId)
{
super(id, recipients, individualRecipient, date, threadId, null);
super(id, recipients, individualRecipient, dateSent, dateReceived,
threadId, DELIVERY_STATUS_NONE, null);
this.contentLocation = contentLocation;
this.messageSize = messageSize;
this.expiry = expiry;
@@ -60,7 +62,6 @@ public class NotificationMmsMessageRecord extends MessageRecord {
return this.status;
}
public byte[] getContentLocation() {
return contentLocation;
}

View File

@@ -40,12 +40,14 @@ public class SmsMessageRecord extends MessageRecord {
public SmsMessageRecord(Context context, long id,
Recipients recipients,
Recipient individualRecipient,
long date, long type, long threadId,
GroupData groupData)
long dateSent, long dateReceived,
long type, long threadId,
int status, GroupData groupData)
{
super(id, recipients, individualRecipient, date, threadId, groupData);
this.context = context.getApplicationContext();
this.type = type;
super(id, recipients, individualRecipient, dateSent, dateReceived,
threadId, getGenericDeliveryStatus(status), groupData);
this.context = context.getApplicationContext();
this.type = type;
}
public long getType() {
@@ -73,7 +75,8 @@ public class SmsMessageRecord extends MessageRecord {
@Override
public boolean isFailed() {
return SmsDatabase.Types.isFailedMessageType(getType());
return SmsDatabase.Types.isFailedMessageType(getType()) ||
getDeliveryStatus() == DELIVERY_STATUS_FAILED;
}
@Override
@@ -96,4 +99,15 @@ public class SmsMessageRecord extends MessageRecord {
return false;
}
private static int getGenericDeliveryStatus(int status) {
if (status == SmsDatabase.Status.STATUS_NONE) {
return MessageRecord.DELIVERY_STATUS_NONE;
} else if (status >= SmsDatabase.Status.STATUS_FAILED) {
return MessageRecord.DELIVERY_STATUS_FAILED;
} else if (status >= SmsDatabase.Status.STATUS_PENDING) {
return MessageRecord.DELIVERY_STATUS_PENDING;
} else {
return MessageRecord.DELIVERY_STATUS_RECEIVED;
}
}
}

View File

@@ -38,7 +38,7 @@ public class ThreadRecord extends DisplayRecord {
long date, long count,
boolean read, long threadId)
{
super(recipients, date, threadId);
super(recipients, date, date, threadId);
this.context = context.getApplicationContext();
this.count = count;
this.read = read;
@@ -68,4 +68,8 @@ public class ThreadRecord extends DisplayRecord {
return read;
}
public long getDate() {
return getDateReceived();
}
}

View File

@@ -0,0 +1,19 @@
package org.thoughtcrime.securesms.mms;
public class ApnUnavailableException extends Exception {
public ApnUnavailableException() {
}
public ApnUnavailableException(String detailMessage) {
super(detailMessage);
}
public ApnUnavailableException(Throwable throwable) {
super(throwable);
}
public ApnUnavailableException(String detailMessage, Throwable throwable) {
super(detailMessage, throwable);
}
}

View File

@@ -1,6 +1,6 @@
/**
/**
* Copyright (C) 2011 Whisper Systems
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -10,100 +10,67 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.mms;
import java.io.IOException;
import org.thoughtcrime.securesms.R;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import org.thoughtcrime.securesms.R;
import java.io.IOException;
public class AttachmentManager {
private final Context context;
private final View attachmentView;
private final ImageView thumbnail;
// private final Button viewButton;
// private final Button editButton;
private final Button removeButton;
private final SlideDeck slideDeck;
public AttachmentManager(Activity view) {
this.attachmentView = (View)view.findViewById(R.id.attachment_editor);
this.thumbnail = (ImageView)view.findViewById(R.id.attachment_thumbnail);
// this.viewButton = (Button)view.findViewById(R.id.view_image_button);
// this.editButton = (Button)view.findViewById(R.id.replace_image_button);
this.removeButton = (Button)view.findViewById(R.id.remove_image_button);
this.slideDeck = new SlideDeck();
this.context = view;
this.context = view;
this.removeButton.setOnClickListener(new RemoveButtonListener());
}
public void clear() {
slideDeck.clear();
attachmentView.setVisibility(View.GONE);
}
public void setImage(Uri image) throws IOException {
Log.w("AttachmentManager" , "Setting image: " + image);
ImageSlide slide = new ImageSlide(context, image);
slideDeck.addSlide(slide);
thumbnail.setImageBitmap(slide.getThumbnail());
attachmentView.setVisibility(View.VISIBLE);
}
public void setVideo(Uri video) throws IOException, MediaTooLargeException {
VideoSlide slide = new VideoSlide(context, video);
slideDeck.addSlide(slide);
thumbnail.setImageBitmap(slide.getThumbnail());
attachmentView.setVisibility(View.VISIBLE);
}
public void setAudio(Uri audio)throws IOException, MediaTooLargeException {
AudioSlide slide = new AudioSlide(context, audio);
slideDeck.addSlide(slide);
thumbnail.setImageBitmap(slide.getThumbnail());
attachmentView.setVisibility(View.VISIBLE);
}
// public Bitmap constructThumbnailFromVideo(Uri uri) {
// MediaMetadataRetriever retriever = new MediaMetadataRetriever();
// try {
// retriever.setMode(MediaMetadataRetriever.MODE_CAPTURE_FRAME_ONLY);
// retriever.setDataSource(context, uri);
// return retriever.captureFrame();
// } catch (RuntimeException re) {
// Log.w("AttachmentManager", re);
// } finally {
// try {
// retriever.release();
// } catch (RuntimeException ex) {
// }
// }
// return null;
// }
// private void displayMessageSizeException() {
// AlertDialog.Builder builder = new AlertDialog.Builder(context);
// builder.setIcon(R.drawable.ic_sms_mms_not_delivered);
// builder.setTitle("Message size limit reached.");
// builder.setMessage("Sorry, this attachment is too large for your message.");
// builder.setPositiveButton("Ok", null);
// builder.show();
// }
public boolean isAttachmentPresent() {
return attachmentView.getVisibility() == View.VISIBLE;
@@ -112,35 +79,27 @@ public class AttachmentManager {
public SlideDeck getSlideDeck() {
return slideDeck;
}
// public static void selectAudio(Activity activity, int requestCode) {
// Intent intent = new Intent(RingtoneManager.ACTION_RINGTONE_PICKER);
// intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_DEFAULT, false);
// intent.putExtra(RingtoneManager.EXTRA_RINGTONE_SHOW_SILENT, false);
// intent.putExtra(RingtoneManager.EXTRA_RINGTONE_INCLUDE_DRM, false);
// intent.putExtra(RingtoneManager.EXTRA_RINGTONE_TITLE, "Select audio");
// activity.startActivityForResult(intent, requestCode);
// }
public static void selectVideo(Activity activity, int requestCode) {
selectMediaType(activity, "video/*", requestCode);
}
public static void selectImage(Activity activity, int requestCode) {
selectMediaType(activity, "image/*", requestCode);
}
public static void selectAudio(Activity activity, int requestCode) {
selectMediaType(activity, "audio/*", requestCode);
}
private static void selectMediaType(Activity activity, String type, int requestCode) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType(type);
activity.startActivityForResult(intent, requestCode);
}
private class RemoveButtonListener implements View.OnClickListener {
@Override
public void onClick(View v) {
clear();
}

View File

@@ -17,9 +17,13 @@
package org.thoughtcrime.securesms.mms;
import android.content.Context;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.database.sqlite.SQLiteException;
import android.net.ConnectivityManager;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.util.Log;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
@@ -37,26 +41,47 @@ import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.params.HttpProtocolParams;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.service.MmsDownloader;
import org.thoughtcrime.securesms.util.Conversions;
import ws.com.google.android.mms.MmsException;
import java.io.DataInputStream;
import java.io.IOException;
import java.net.InetAddress;
public class MmsCommunication {
protected static MmsConnectionParameters getMmsConnectionParameters(Context context, String apn)
throws MmsException
protected static MmsConnectionParameters getLocallyConfiguredMmsConnectionParameters(Context context)
throws ApnUnavailableException
{
Cursor cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(apn);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (preferences.getBoolean(ApplicationPreferencesActivity.USE_LOCAL_MMS_APNS_PREF, false)) {
String mmsc = preferences.getString(ApplicationPreferencesActivity.MMSC_HOST_PREF, null);
if (mmsc == null || !mmsc.startsWith("http"))
throw new ApnUnavailableException("Malformed locally configured MMSC: " + mmsc);
String proxy = preferences.getString(ApplicationPreferencesActivity.MMSC_PROXY_HOST_PREF, null);
String port = preferences.getString(ApplicationPreferencesActivity.MMSC_PROXY_PORT_PREF, null);
return new MmsConnectionParameters(mmsc, proxy, port);
}
throw new ApnUnavailableException("No locally configured parameters available");
}
protected static MmsConnectionParameters getMmsConnectionParameters(Context context, String apn)
throws ApnUnavailableException
{
Cursor cursor = null;
try {
cursor = DatabaseFactory.getMmsDatabase(context).getCarrierMmsInformation(apn);
if (cursor == null || !cursor.moveToFirst())
throw new MmsException("No carrier MMS information available.");
return getLocallyConfiguredMmsConnectionParameters(context);
do {
String mmsc = cursor.getString(cursor.getColumnIndexOrThrow("mmsc"));
@@ -68,7 +93,13 @@ public class MmsCommunication {
} while (cursor.moveToNext());
throw new MmsException("No carrier MMS information available.");
return getLocallyConfiguredMmsConnectionParameters(context);
} catch (SQLiteException sqe) {
Log.w("MmsCommunication", sqe);
return getLocallyConfiguredMmsConnectionParameters(context);
} catch (SecurityException se) {
Log.w("MmsCommunication", se);
return getLocallyConfiguredMmsConnectionParameters(context);
} finally {
if (cursor != null)
cursor.close();
@@ -150,6 +181,9 @@ public class MmsCommunication {
}
public String getProxy() {
if (!hasProxy())
return null;
return proxy;
}

View File

@@ -25,8 +25,6 @@ import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
import ws.com.google.android.mms.MmsException;
import java.io.IOException;
public class MmsDownloadHelper extends MmsCommunication {
@@ -50,14 +48,16 @@ public class MmsDownloadHelper extends MmsCommunication {
}
public static byte[] retrieveMms(Context context, String url, String apn) throws IOException {
try {
MmsConnectionParameters connectionParameters = getMmsConnectionParameters(context, apn);
MmsConnectionParameters connectionParameters;
checkRouteToHost(context, connectionParameters, url);
return makeRequest(connectionParameters, url);
} catch (MmsException me) {
Log.w("MmsDownloader", me);
throw new IOException("Problem configuring MmsConnectionParameters.");
try {
connectionParameters = getMmsConnectionParameters(context, apn);
} catch (ApnUnavailableException aue) {
Log.w("MmsDownloadHelper", aue);
connectionParameters = new MmsConnectionParameters(null, null, null);
}
checkRouteToHost(context, connectionParameters, url);
return makeRequest(connectionParameters, url);
}
}

View File

@@ -26,8 +26,6 @@ import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.ByteArrayEntity;
import ws.com.google.android.mms.MmsException;
import java.io.IOException;
public class MmsSendHelper extends MmsCommunication {
@@ -59,8 +57,8 @@ public class MmsSendHelper extends MmsCommunication {
MmsConnectionParameters parameters = getMmsConnectionParameters(context, apn);
checkRouteToHost(context, parameters, parameters.getMmsc());
return makePost(parameters, mms);
} catch (MmsException me) {
Log.w("MmsSender", me);
} catch (ApnUnavailableException aue) {
Log.w("MmsSender", aue);
throw new IOException("Failed to get MMSC information...");
}
}

View File

@@ -0,0 +1,351 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.notifications;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.BigTextStyle;
import android.support.v4.app.NotificationCompat.InboxStyle;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MessageDisplayHelper;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.protocol.Prefix;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.InvalidMessageException;
import org.thoughtcrime.securesms.util.Util;
import java.io.IOException;
import java.util.List;
/**
* Handles posting system notifications for new messages.
*
*
* @author Moxie Marlinspike
*/
public class MessageNotifier {
public static final int NOTIFICATION_ID = 1338;
private volatile static long visibleThread = -1;
public static void setVisibleThread(long threadId) {
visibleThread = threadId;
}
public static void notifyMessageDeliveryFailed(Context context, Recipients recipients, long threadId) {
if (visibleThread == threadId) {
sendInThreadNotification(context);
} else {
Intent intent = new Intent(context, ConversationListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
intent.putExtra("recipients", recipients);
intent.putExtra("thread_id", threadId);
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.drawable.icon_notification);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_list_alert_sms_failed));
builder.setContentTitle(context.getString(R.string.MessageNotifier_message_delivery_failed));
builder.setContentText(context.getString(R.string.MessageNotifier_failed_to_deliver_message));
builder.setTicker(context.getString(R.string.MessageNotifier_error_delivering_message));
builder.setContentIntent(PendingIntent.getActivity(context, 0, intent, 0));
builder.setAutoCancel(true);
setNotificationAlarms(context, builder, true);
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify((int)threadId, builder.build());
}
}
public static void updateNotification(Context context, MasterSecret masterSecret) {
updateNotification(context, masterSecret, false);
}
public static void updateNotification(Context context, MasterSecret masterSecret, long threadId) {
if (visibleThread == threadId) {
DatabaseFactory.getThreadDatabase(context).setRead(threadId);
sendInThreadNotification(context);
} else {
updateNotification(context, masterSecret, true);
}
}
private static void updateNotification(Context context, MasterSecret masterSecret, boolean signal) {
Cursor cursor = null;
try {
cursor = DatabaseFactory.getMmsSmsDatabase(context).getUnread();
if (cursor == null || cursor.isAfterLast()) {
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
.cancel(NOTIFICATION_ID);
return;
}
NotificationState notificationState = constructNotificationState(context, masterSecret, cursor);
if (notificationState.hasMultipleThreads()) {
sendMultipleThreadNotification(context, notificationState, signal);
} else {
sendSingleThreadNotification(context, notificationState, signal);
}
} finally {
if (cursor != null)
cursor.close();
}
}
private static void sendSingleThreadNotification(Context context,
NotificationState notificationState,
boolean signal)
{
List<NotificationItem> notifications = notificationState.getNotifications();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
Recipients recipients = notifications.get(0).getRecipients();
builder.setSmallIcon(R.drawable.icon_notification);
builder.setLargeIcon(recipients.getPrimaryRecipient().getContactPhoto());
builder.setContentTitle(recipients.getPrimaryRecipient().toShortString());
builder.setContentText(notifications.get(0).getText());
builder.setContentIntent(notifications.get(0).getPendingIntent(context));
SpannableStringBuilder content = new SpannableStringBuilder();
for (NotificationItem item : notifications) {
content.append(item.getBigStyleSummary());
content.append('\n');
}
builder.setStyle(new BigTextStyle().bigText(content));
setNotificationAlarms(context, builder, signal);
if (signal) {
builder.setTicker(notifications.get(0).getTickerText());
}
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(NOTIFICATION_ID, builder.build());
}
private static void sendMultipleThreadNotification(Context context,
NotificationState notificationState,
boolean signal)
{
List<NotificationItem> notifications = notificationState.getNotifications();
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.drawable.icon_notification);
builder.setLargeIcon(BitmapFactory.decodeResource(context.getResources(),
R.drawable.icon_notification));
builder.setContentTitle(String.format(context.getString(R.string.MessageNotifier_d_new_messages),
notificationState.getMessageCount()));
builder.setContentText(String.format(context.getString(R.string.MessageNotifier_most_recent_from_s),
notifications.get(0).getRecipientName()));
builder.setContentIntent(PendingIntent.getActivity(context, 0, new Intent(context, ConversationListActivity.class), 0));
InboxStyle style = new InboxStyle();
for (NotificationItem item : notifications) {
style.addLine(item.getTickerText());
}
builder.setStyle(style);
setNotificationAlarms(context, builder, signal);
if (signal) {
builder.setTicker(notifications.get(0).getTickerText());
}
((NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE))
.notify(NOTIFICATION_ID, builder.build());
}
private static void sendInThreadNotification(Context context) {
try {
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
String ringtone = sp.getString(ApplicationPreferencesActivity.RINGTONE_PREF, null);
if (ringtone == null)
return;
Uri uri = Uri.parse(ringtone);
MediaPlayer player = new MediaPlayer();
player.setAudioStreamType(AudioManager.STREAM_NOTIFICATION);
player.setDataSource(context, uri);
player.setLooping(false);
player.setVolume(0.25f, 0.25f);
player.prepare();
final AudioManager audioManager = ((AudioManager)context.getSystemService(Context.AUDIO_SERVICE));
audioManager.requestAudioFocus(null, AudioManager.STREAM_NOTIFICATION,
AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK);
player.setOnCompletionListener(new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer mp) {
audioManager.abandonAudioFocus(null);
}
});
player.start();
} catch (IOException ioe) {
Log.w("MessageNotifier", ioe);
}
}
private static NotificationState constructNotificationState(Context context,
MasterSecret masterSecret,
Cursor cursor)
{
NotificationState notificationState = new NotificationState();
while (cursor.moveToNext()) {
Recipients recipients = getRecipients(context, cursor);
long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
CharSequence body = getBody(context, masterSecret, cursor);
Uri image = null;
notificationState.addNotification(new NotificationItem(recipients, threadId, body, image));
}
return notificationState;
}
private static CharSequence getBody(Context context, MasterSecret masterSecret, Cursor cursor) {
String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY));
if (body == null) {
return context.getString(R.string.MessageNotifier_no_subject);
}
if (masterSecret != null) {
try {
body = MessageDisplayHelper.getDecryptedMessageBody(new MasterCipher(masterSecret), body);
} catch (InvalidMessageException e) {
Log.w("MessageNotifier", e);
return Util.getItalicizedString(context.getString(R.string.MessageNotifier_corrupted_ciphertext));
}
}
if (body.startsWith(Prefix.SYMMETRIC_ENCRYPT) ||
body.startsWith(Prefix.ASYMMETRIC_ENCRYPT) ||
body.startsWith(Prefix.ASYMMETRIC_LOCAL_ENCRYPT))
{
return Util.getItalicizedString(context.getString(R.string.MessageNotifier_encrypted_message));
} else if (body.startsWith(Prefix.KEY_EXCHANGE) ||
body.startsWith(Prefix.PROCESSED_KEY_EXCHANGE))
{
return Util.getItalicizedString(context.getString(R.string.MessageNotifier_key_exchange));
}
return body;
}
private static Recipients getSmsRecipient(Context context, Cursor cursor)
throws RecipientFormattingException
{
String address = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
return RecipientFactory.getRecipientsFromString(context, address, false);
}
private static Recipients getMmsRecipient(Context context, Cursor cursor)
throws RecipientFormattingException
{
long messageId = cursor.getLong(cursor.getColumnIndexOrThrow(MmsDatabase.ID));
String address = DatabaseFactory.getMmsDatabase(context).getMessageRecipient(messageId);
return RecipientFactory.getRecipientsFromString(context, address, false);
}
private static Recipients getRecipients(Context context, Cursor cursor) {
String type = cursor.getString(cursor.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
try {
if (type.equals("sms")) {
return getSmsRecipient(context, cursor);
} else {
return getMmsRecipient(context, cursor);
}
} catch (RecipientFormattingException e) {
Log.w("MessageNotifier", e);
return new Recipients(new Recipient("Unknown", "Unknown", null,
ContactPhotoFactory.getDefaultContactPhoto(context)));
}
}
private static void setNotificationAlarms(Context context,
NotificationCompat.Builder builder,
boolean signal)
{
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
String ringtone = sp.getString(ApplicationPreferencesActivity.RINGTONE_PREF, null);
boolean vibrate = sp.getBoolean(ApplicationPreferencesActivity.VIBRATE_PREF, true);
String ledColor = sp.getString(ApplicationPreferencesActivity.LED_COLOR_PREF, "green");
String ledBlinkPattern = sp.getString(ApplicationPreferencesActivity.LED_BLINK_PREF, "500,2000");
String ledBlinkPatternCustom = sp.getString(ApplicationPreferencesActivity.LED_BLINK_PREF_CUSTOM, "500,2000");
String[] blinkPatternArray = parseBlinkPattern(ledBlinkPattern, ledBlinkPatternCustom);
builder.setSound(TextUtils.isEmpty(ringtone) || !signal ? null : Uri.parse(ringtone));
if (signal && vibrate)
builder.setDefaults(Notification.DEFAULT_VIBRATE);
builder.setLights(Color.parseColor(ledColor), Integer.parseInt(blinkPatternArray[0]),
Integer.parseInt(blinkPatternArray[1]));
}
private static String[] parseBlinkPattern(String blinkPattern, String blinkPatternCustom) {
if (blinkPattern.equals("custom"))
blinkPattern = blinkPatternCustom;
return blinkPattern.split(",");
}
}

View File

@@ -0,0 +1,78 @@
package org.thoughtcrime.securesms.notifications;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.text.SpannableStringBuilder;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Util;
public class NotificationItem {
private final Recipients recipients;
private final long threadId;
private final CharSequence text;
private final Uri image;
public NotificationItem(Recipients recipients, long threadId, CharSequence text, Uri image) {
this.recipients = recipients;
this.text = text;
this.image = image;
this.threadId = threadId;
}
public Recipients getRecipients() {
return recipients;
}
public String getRecipientName() {
return recipients.getPrimaryRecipient().toShortString();
}
public CharSequence getText() {
return text;
}
public Uri getImage() {
return image;
}
public boolean hasImage() {
return image != null;
}
public long getThreadId() {
return threadId;
}
public CharSequence getBigStyleSummary() {
return (text == null) ? "" : text;
}
public CharSequence getTickerText() {
SpannableStringBuilder builder = new SpannableStringBuilder();
builder.append(Util.getBoldedString(getRecipientName()));
builder.append(": ");
builder.append(getText());
return builder;
}
public PendingIntent getPendingIntent(Context context) {
Intent intent = new Intent(context, ConversationListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
if (recipients.getPrimaryRecipient() != null) {
intent.putExtra("recipients", recipients);
intent.putExtra("thread_id", threadId);
}
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
return PendingIntent.getActivity(context, 0, intent, 0);
}
}

View File

@@ -0,0 +1,39 @@
package org.thoughtcrime.securesms.notifications;
import android.graphics.Bitmap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
public class NotificationState {
private final LinkedList<NotificationItem> notifications = new LinkedList<NotificationItem>();
private final Set<Long> threads = new HashSet<Long>();
private int notificationCount = 0;
public void addNotification(NotificationItem item) {
notifications.addFirst(item);
threads.add(item.getThreadId());
notificationCount++;
}
public boolean hasMultipleThreads() {
return threads.size() > 1;
}
public int getMessageCount() {
return notificationCount;
}
public List<NotificationItem> getNotifications() {
return notifications;
}
public Bitmap getContactPhoto() {
return notifications.get(0).getRecipients().getPrimaryRecipient().getContactPhoto();
}
}

View File

@@ -1,6 +1,6 @@
/**
/**
* Copyright (C) 2011 Whisper Systems
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -10,17 +10,16 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.preferences;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Color;
import android.os.Build;
import android.preference.DialogPreference;
import android.util.AttributeSet;
import android.view.View;
@@ -29,9 +28,12 @@ import android.widget.SeekBar;
import android.widget.Spinner;
import android.widget.TextView;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
/**
* Dialog preference for encryption passphrase timeout.
*
*
* @author Moxie Marlinspike
*/
@@ -40,14 +42,14 @@ public class PassphraseTimeoutPreference extends DialogPreference {
private Spinner scaleSpinner;
private SeekBar seekBar;
private TextView timeoutText;
public PassphraseTimeoutPreference(Context context, AttributeSet attrs) {
super(context, attrs);
this.setDialogLayoutResource(R.layout.passphrase_timeout_dialog);
this.setPositiveButtonText("Ok");
this.setNegativeButtonText("Cancel");
this.setPositiveButtonText(android.R.string.ok);
this.setNegativeButtonText(android.R.string.cancel);
}
@Override
protected View onCreateDialogView() {
View dialog = super.onCreateDialogView();
@@ -55,32 +57,39 @@ public class PassphraseTimeoutPreference extends DialogPreference {
this.seekBar = (SeekBar)dialog.findViewById(R.id.seekbar);
this.timeoutText = (TextView)dialog.findViewById(R.id.timeout_text);
// Can't figure out how to style a DialogPreference.
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
this.timeoutText.setTextColor(Color.parseColor("#cccccc"));
} else {
this.timeoutText.setTextColor(Color.parseColor("#000000"));
}
initializeDefaults();
initializeListeners();
return dialog;
}
@Override
public void onClick(DialogInterface dialog, int which) {
if (which == DialogInterface.BUTTON_POSITIVE) {
int interval;
if (scaleSpinner.getSelectedItemPosition() == 0) {
interval = Math.max(seekBar.getProgress(), 1);
} else {
interval = Math.max(seekBar.getProgress(), 1) * 60;
}
this.getSharedPreferences().edit().putInt(ApplicationPreferencesActivity.PASSPHRASE_TIMEOUT_INTERVAL_PREF, interval).commit();
}
super.onClick(dialog, which);
}
private void initializeDefaults() {
int timeout = this.getSharedPreferences().getInt(ApplicationPreferencesActivity.PASSPHRASE_TIMEOUT_INTERVAL_PREF, 60 * 5);
if (timeout > 60) {
scaleSpinner.setSelection(1);
seekBar.setMax(24);
@@ -96,10 +105,11 @@ public class PassphraseTimeoutPreference extends DialogPreference {
private void initializeListeners() {
this.seekBar.setOnSeekBarChangeListener(new SeekbarChangedListener());
this.scaleSpinner.setOnItemSelectedListener(new ScaleSelectedListener());
this.scaleSpinner.setOnItemSelectedListener(new ScaleSelectedListener());
}
private class ScaleSelectedListener implements AdapterView.OnItemSelectedListener {
@Override
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long selected) {
if (selected == 0) {
seekBar.setMax(60);
@@ -108,25 +118,29 @@ public class PassphraseTimeoutPreference extends DialogPreference {
}
}
@Override
public void onNothingSelected(AdapterView<?> arg0) {
}
}
}
private class SeekbarChangedListener implements SeekBar.OnSeekBarChangeListener {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (progress < 1)
if (progress < 1)
progress = 1;
timeoutText.setText(progress +"");
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
}
}

View File

@@ -1,102 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.recipients;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import java.io.InputStream;
public class NewRecipientProvider extends RecipientProvider {
private static final String[] CALLER_ID_PROJECTION = new String[] {
PhoneLookup.DISPLAY_NAME,
PhoneLookup.LOOKUP_KEY,
PhoneLookup._ID,
};
private static final String[] CONTENT_URI_PROJECTION = new String[] {
ContactsContract.Contacts._ID,
ContactsContract.Contacts.DISPLAY_NAME,
ContactsContract.Contacts.LOOKUP_KEY
};
@Override
public Recipient getRecipient(Context context, Uri uri) {
Cursor cursor = context.getContentResolver().query(uri, CONTENT_URI_PROJECTION, null, null, null);
try {
if (cursor.moveToFirst()) {
long rowId = cursor.getLong(0);
Uri contactUri = Contacts.getLookupUri(rowId, cursor.getString(2));
Bitmap contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI,
rowId+""));
String displayName = cursor.getString(1);
cursor.close();
cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, new String[] {ContactsContract.CommonDataKinds.Phone.NUMBER}, ContactsContract.CommonDataKinds.Phone.CONTACT_ID +" = ?", new String[] {rowId+""}, null);
if (cursor.moveToFirst())
return new Recipient(displayName, cursor.getString(0), contactUri, contactPhoto);
else
return new Recipient(displayName, null, contactUri, contactPhoto);
}
} finally {
cursor.close();
}
return null;
}
@Override
public Recipient getRecipient(Context context, String number) {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION, null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1));
Bitmap contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI,
cursor.getLong(2)+""));
Recipient recipient = new Recipient(cursor.getString(0), number, contactUri, contactPhoto);
return recipient;
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private Bitmap getContactPhoto(Context context, Uri uri) {
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
if (inputStream == null)
return getDefaultContactPhoto(context);
else
return BitmapFactory.decodeStream(inputStream);
}
}

View File

@@ -20,6 +20,13 @@ import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Parcel;
import android.os.Parcelable;
import android.util.Log;
import org.thoughtcrime.securesms.recipients.RecipientProvider.RecipientDetails;
import org.thoughtcrime.securesms.util.FutureTaskListener;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import java.util.HashSet;
public class Recipient implements Parcelable {
@@ -33,35 +40,65 @@ public class Recipient implements Parcelable {
}
};
private final String name;
private final String number;
private Uri contactUri;
private Bitmap contactPhoto;
private final HashSet<RecipientModifiedListener> listeners = new HashSet<RecipientModifiedListener>();
public Recipient(String name, String number, Uri contactUri, Bitmap contactPhoto) {
this(name, number, contactPhoto);
this.contactUri = contactUri;
private String name;
private Bitmap contactPhoto;
private Uri contactUri;
public Recipient(String number, Bitmap contactPhoto,
ListenableFutureTask<RecipientDetails> future)
{
this.number = number;
this.contactPhoto = contactPhoto;
future.setListener(new FutureTaskListener<RecipientDetails>() {
@Override
public void onSuccess(RecipientDetails result) {
if (result != null) {
HashSet<RecipientModifiedListener> localListeners;
synchronized (Recipient.this) {
Recipient.this.name = result.name;
Recipient.this.contactUri = result.contactUri;
Recipient.this.contactPhoto = result.avatar;
localListeners = (HashSet<RecipientModifiedListener>)listeners.clone();
listeners.clear();
}
for (RecipientModifiedListener listener : localListeners)
listener.onModified(Recipient.this);
}
}
@Override
public void onFailure(Throwable error) {
Log.w("Recipient", error);
}
});
}
public Recipient(String name, String number, Bitmap contactPhoto) {
this.name = name;
public Recipient(String name, String number, Uri contactUri, Bitmap contactPhoto) {
this.number = number;
this.contactUri = contactUri;
this.name = name;
this.contactPhoto = contactPhoto;
}
public Recipient(Parcel in) {
this.name = in.readString();
this.number = in.readString();
this.contactUri = in.readParcelable(null);
this.contactPhoto = in.readParcelable(null);
this.name = in.readString();
this.contactUri = (Uri)in.readParcelable(null);
this.contactPhoto = (Bitmap)in.readParcelable(null);
}
public Uri getContactUri() {
public synchronized Uri getContactUri() {
return this.contactUri;
}
public String getName() {
return name;
public synchronized String getName() {
return this.name;
}
public String getNumber() {
@@ -72,20 +109,43 @@ public class Recipient implements Parcelable {
return 0;
}
public void writeToParcel(Parcel dest, int flags) {
dest.writeString(name);
// public void updateAsynchronousContent(RecipientDetails result) {
// if (result != null) {
// Recipient.this.name.set(result.name);
// Recipient.this.contactUri.set(result.contactUri);
// Recipient.this.contactPhoto.set(result.avatar);
//
// synchronized(this) {
// if (listener == null) asynchronousUpdateComplete = true;
// else listener.onModified(Recipient.this);
// }
// }
// }
public synchronized void addListener(RecipientModifiedListener listener) {
listeners.add(listener);
}
public synchronized void removeListener(RecipientModifiedListener listener) {
listeners.remove(listener);
}
public synchronized void writeToParcel(Parcel dest, int flags) {
dest.writeString(number);
dest.writeString(name);
dest.writeParcelable(contactUri, 0);
dest.writeParcelable(contactPhoto, 0);
}
public String toShortString() {
public synchronized String toShortString() {
return (name == null ? number : name);
}
public Bitmap getContactPhoto() {
public synchronized Bitmap getContactPhoto() {
return contactPhoto;
}
public static interface RecipientModifiedListener {
public void onModified(Recipient recipient);
}
}

View File

@@ -17,56 +17,29 @@
package org.thoughtcrime.securesms.recipients;
import android.content.Context;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.util.NumberUtil;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.LinkedList;
import java.util.List;
import java.util.StringTokenizer;
public class RecipientFactory {
private static final Map<String,Recipient> recipientCache = Collections.synchronizedMap(new LRUHashMap<String,Recipient>());
private static final Map<String,Recipient> recipientIdCache = Collections.synchronizedMap(new LRUHashMap<String,Recipient>());
private static final Map<Uri,Recipient> recipientUriCache = Collections.synchronizedMap(new HashMap<Uri,Recipient>());
private static final RecipientProvider provider = new RecipientProvider();
private static final RecipientProvider provider = new NewRecipientProvider();
public static Recipients getRecipientsForIds(Context context, String recipientIds, boolean asynchronous) {
if (recipientIds == null || recipientIds.trim().length() == 0)
return new Recipients(new LinkedList<Recipient>());
public static RecipientProvider getRecipientProvider() {
return provider;
}
public static Recipient getRecipientForUri(Context context, Uri uri) {
Recipient recipient = recipientUriCache.get(uri);
if (recipient == null)
recipient = getRecipientFromProvider(context, uri);
return recipient;
}
public static Recipients getRecipientsForIds(Context context, String recipientIds) {
ArrayList<Recipient> results = new ArrayList<Recipient>();
StringTokenizer tokenizer = new StringTokenizer(recipientIds.trim(), " ");
List<Recipient> results = new LinkedList<Recipient>();
StringTokenizer tokenizer = new StringTokenizer(recipientIds.trim(), " ");
while (tokenizer.hasMoreTokens()) {
String recipientId = tokenizer.nextToken();
Recipient recipient = recipientIdCache.get(recipientId);
if (recipient == null)
recipient = getRecipientFromProviderId(context, recipientId);
if (recipient == null)
recipient = getNullIdRecipient(context, recipientId);
Recipient recipient = getRecipientFromProviderId(context, recipientId, asynchronous);
results.add(recipient);
}
@@ -74,24 +47,18 @@ public class RecipientFactory {
return new Recipients(results);
}
private static Recipient getRecipientForNumber(Context context, String number) {
Recipient recipient = recipientCache.get(number);
if (recipient == null)
recipient = getRecipientFromProvider(context, number);
if (recipient == null)
recipient = getNullRecipient(context, number);
return recipient;
private static Recipient getRecipientForNumber(Context context, String number, boolean asynchronous) {
return provider.getRecipient(context, number, asynchronous);
}
public static Recipients getRecipientsFromString(Context context, String rawText) throws RecipientFormattingException {
ArrayList<Recipient> results = new ArrayList<Recipient>();
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
public static Recipients getRecipientsFromString(Context context, String rawText, boolean asynchronous)
throws RecipientFormattingException
{
List<Recipient> results = new LinkedList<Recipient>();
StringTokenizer tokenizer = new StringTokenizer(rawText, ",");
while (tokenizer.hasMoreTokens()) {
Recipient recipient = parseRecipient(context, tokenizer.nextToken());
Recipient recipient = parseRecipient(context, tokenizer.nextToken(), asynchronous);
if( recipient != null )
results.add(recipient);
}
@@ -99,50 +66,23 @@ public class RecipientFactory {
return new Recipients(results);
}
private static Recipient getNullIdRecipient(Context context, String recipientId) {
String address = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId);
Recipient recipient = getNullRecipient(context, address);
recipientIdCache.put(recipientId, recipient);
return recipient;
private static Recipient getRecipientFromProviderId(Context context, String recipientId, boolean asynchronous) {
String number = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId);
return getRecipientForNumber(context, number, asynchronous);
}
private static Recipient getNullRecipient(Context context, String number) {
Recipient nullRecipient = new Recipient(null, number, BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_contact_picture));
recipientCache.put(number, nullRecipient);
return nullRecipient;
private static boolean hasBracketedNumber(String recipient) {
int openBracketIndex = recipient.indexOf('<');
return (openBracketIndex != -1) &&
(recipient.indexOf('>', openBracketIndex) != -1);
}
private static Recipient getRecipientFromProviderId(Context context, String recipientId) {
Log.w("RecipientFactory", "Hitting recipient provider [ID].");
String address = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId);
Recipient recipient = getRecipientFromProvider(context, address);
recipientIdCache.put(recipientId, recipient);
return recipient;
}
private static Recipient getRecipientFromProvider(Context context, Uri uri) {
Recipient recipient = provider.getRecipient(context, uri);
if (recipient != null)
recipientUriCache.put(uri, recipient);
return recipient;
}
private static Recipient getRecipientFromProvider(Context context, String number) {
Recipient recipient = provider.getRecipient(context, number);
if (recipient != null)
recipientCache.put(number, recipient);
return recipient;
}
private static String parseBracketedNumber(String recipient) throws RecipientFormattingException {
private static String parseBracketedNumber(String recipient)
throws RecipientFormattingException
{
int begin = recipient.indexOf('<');
int end = recipient.indexOf('>');
int end = recipient.indexOf('>', begin);
String value = recipient.substring(begin + 1, end);
if (NumberUtil.isValidSmsOrEmail(value))
@@ -151,32 +91,26 @@ public class RecipientFactory {
throw new RecipientFormattingException("Bracketed value: " + value + " is not valid.");
}
private static Recipient parseRecipient(Context context, String recipient) throws RecipientFormattingException {
private static Recipient parseRecipient(Context context, String recipient, boolean asynchronous)
throws RecipientFormattingException
{
recipient = recipient.trim();
if( recipient.length() == 0 )
return null;
if ((recipient.indexOf('<') != -1) && (recipient.indexOf('>') != -1))
return getRecipientForNumber(context, parseBracketedNumber(recipient));
if (hasBracketedNumber(recipient))
return getRecipientForNumber(context, parseBracketedNumber(recipient), asynchronous);
if (NumberUtil.isValidSmsOrEmail(recipient))
return getRecipientForNumber(context, recipient);
return getRecipientForNumber(context, recipient, asynchronous);
throw new RecipientFormattingException("Recipient: " + recipient + " is badly formatted.");
}
public static void clearCache() {
recipientCache.clear();
recipientIdCache.clear();
recipientUriCache.clear();
ContactPhotoFactory.clearCache();
provider.clearCache();
}
private static class LRUHashMap<K,V> extends LinkedHashMap<K,V> {
private static final int MAX_SIZE = 1000;
@Override
protected boolean removeEldestEntry (Map.Entry<K,V> eldest) {
return size() > MAX_SIZE;
}
}
}

View File

@@ -17,25 +17,148 @@
package org.thoughtcrime.securesms.recipients;
import android.content.Context;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.provider.ContactsContract.PhoneLookup;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
import org.thoughtcrime.securesms.util.LRUCache;
import org.thoughtcrime.securesms.util.ListenableFutureTask;
import org.thoughtcrime.securesms.util.Util;
public abstract class RecipientProvider {
import java.io.InputStream;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
private static Bitmap defaultContactPhoto;
public class RecipientProvider {
public abstract Recipient getRecipient(Context context, String number);
public abstract Recipient getRecipient(Context context, Uri uri);
private static final Map<String,Recipient> recipientCache = Collections.synchronizedMap(new LRUCache<String,Recipient>(1000));
// private static final ExecutorService asyncRecipientResolver = Executors.newSingleThreadExecutor();
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
public Bitmap getDefaultContactPhoto(Context context) {
synchronized (this) {
if (defaultContactPhoto == null)
defaultContactPhoto = BitmapFactory.decodeResource(context.getResources(), R.drawable.ic_contact_picture);
private static final String[] CALLER_ID_PROJECTION = new String[] {
PhoneLookup.DISPLAY_NAME,
PhoneLookup.LOOKUP_KEY,
PhoneLookup._ID,
};
public Recipient getRecipient(Context context, String number, boolean asynchronous) {
Recipient cachedRecipient = recipientCache.get(number);
if (cachedRecipient != null) return cachedRecipient;
else if (asynchronous) return getAsynchronousRecipient(context, number);
else return getSynchronousRecipient(context, number);
}
private Recipient getSynchronousRecipient(Context context, String number) {
Log.w("RecipientProvider", "Cache miss [SYNC]!");
RecipientDetails details = getRecipientDetails(context, number);
Recipient recipient;
if (details != null) {
recipient = new Recipient(details.name, number, details.contactUri, details.avatar);
} else {
recipient = new Recipient(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(context));
}
return defaultContactPhoto;
recipientCache.put(number, recipient);
return recipient;
}
private Recipient getAsynchronousRecipient(final Context context, final String number) {
Log.w("RecipientProvider", "Cache miss [ASYNC]!");
// Recipient recipient = new Recipient(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(context));
// recipientCache.put(number, recipient);
//
// new AsyncTask<Recipient, Void, RecipientDetails>() {
// private Recipient recipient;
//
// @Override
// protected RecipientDetails doInBackground(Recipient... recipient) {
// this.recipient = recipient[0];
// return getRecipientDetails(context, number);
// }
//
// @Override
// protected void onPostExecute(RecipientDetails result) {
// recipient.updateAsynchronousContent(result);
// }
// }.execute(recipient);
//
// return recipient;
// ListenableFutureTask<RecipientDetails> future = new ListenableFutureTask<RecipientDetails>(new Callable<RecipientDetails>() {
Callable<RecipientDetails> task = new Callable<RecipientDetails>() {
@Override
public RecipientDetails call() throws Exception {
// Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
return getRecipientDetails(context, number);
}
};
ListenableFutureTask<RecipientDetails> future = new ListenableFutureTask<RecipientDetails>(task, null);
asyncRecipientResolver.submit(future);
Recipient recipient = new Recipient(number, ContactPhotoFactory.getDefaultContactPhoto(context), future);
recipientCache.put(number, recipient);
return recipient;
//// return new Recipient(null, number, ContactPhotoFactory.getDefaultContactPhoto(context));
}
public void clearCache() {
recipientCache.clear();
}
private RecipientDetails getRecipientDetails(Context context, String number) {
Uri uri = Uri.withAppendedPath(PhoneLookup.CONTENT_FILTER_URI, Uri.encode(number));
Cursor cursor = context.getContentResolver().query(uri, CALLER_ID_PROJECTION,
null, null, null);
try {
if (cursor != null && cursor.moveToFirst()) {
Uri contactUri = Contacts.getLookupUri(cursor.getLong(2), cursor.getString(1));
Bitmap contactPhoto = getContactPhoto(context, Uri.withAppendedPath(Contacts.CONTENT_URI,
cursor.getLong(2)+""));
return new RecipientDetails(cursor.getString(0), contactUri, contactPhoto);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
private Bitmap getContactPhoto(Context context, Uri uri) {
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
if (inputStream == null)
return ContactPhotoFactory.getDefaultContactPhoto(context);
else
return BitmapFactory.decodeStream(inputStream);
}
public static class RecipientDetails {
public final String name;
public final Bitmap avatar;
public final Uri contactUri;
public RecipientDetails(String name, Uri contactUri, Bitmap avatar) {
this.name = name;
this.avatar = avatar;
this.contactUri = contactUri;
}
}
}

View File

@@ -21,6 +21,7 @@ import android.os.Parcel;
import android.os.Parcelable;
import org.thoughtcrime.securesms.crypto.KeyUtil;
import org.thoughtcrime.securesms.recipients.Recipient.RecipientModifiedListener;
import org.thoughtcrime.securesms.util.NumberUtil;
import java.util.ArrayList;
@@ -68,6 +69,18 @@ public class Recipients implements Parcelable {
return this;
}
public void addListener(RecipientModifiedListener listener) {
for (Recipient recipient : recipients) {
recipient.addListener(listener);
}
}
public void removeListener(RecipientModifiedListener listener) {
for (Recipient recipient : recipients) {
recipient.removeListener(listener);
}
}
public boolean isEmailRecipient() {
for (Recipient recipient : recipients) {
if (NumberUtil.isValidEmail(recipient.getNumber()))

View File

@@ -20,6 +20,7 @@ import android.app.AlarmManager;
import android.app.Notification;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Binder;
@@ -34,6 +35,7 @@ import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
/**
* Small service that stays running to keep a key cached in memory.
@@ -43,12 +45,12 @@ import org.thoughtcrime.securesms.crypto.MasterSecret;
public class KeyCachingService extends Service {
public static final int NOTIFICATION_ID = 1337;
public static final int SERVICE_RUNNING_ID = 4141;
public static final int SERVICE_RUNNING_ID = 4141;
public static final String KEY_PERMISSION = "org.thoughtcrime.securesms.ACCESS_SECRETS";
public static final String NEW_KEY_EVENT = "org.thoughtcrime.securesms.service.action.NEW_KEY_EVENT";
public static final String PASSPHRASE_EXPIRED_EVENT = "org.thoughtcrime.securesms.service.action.PASSPHRASE_EXPIRED_EVENT";
public static final String CLEAR_KEY_EVENT = "org.thoughtcrime.securesms.service.action.CLEAR_KEY_EVENT";
private static final String PASSPHRASE_EXPIRED_EVENT = "org.thoughtcrime.securesms.service.action.PASSPHRASE_EXPIRED_EVENT";
public static final String CLEAR_KEY_ACTION = "org.thoughtcrime.securesms.service.action.CLEAR_KEY";
public static final String ACTIVITY_START_EVENT = "org.thoughtcrime.securesms.service.action.ACTIVITY_START_EVENT";
public static final String ACTIVITY_STOP_EVENT = "org.thoughtcrime.securesms.service.action.ACTIVITY_STOP_EVENT";
@@ -67,12 +69,19 @@ public class KeyCachingService extends Service {
return masterSecret;
}
public synchronized void setMasterSecret(MasterSecret masterSecret) {
public synchronized void setMasterSecret(final MasterSecret masterSecret) {
this.masterSecret = masterSecret;
foregroundService();
broadcastNewSecret();
startTimeoutIfAppropriate();
new Thread() {
@Override
public void run() {
MessageNotifier.updateNotification(KeyCachingService.this, masterSecret);
}
}.start();
}
@Override
@@ -86,17 +95,20 @@ public class KeyCachingService extends Service {
else if (intent.getAction() != null && intent.getAction().equals(ACTIVITY_STOP_EVENT))
handleActivityStopped();
else if (intent.getAction() != null && intent.getAction().equals(PASSPHRASE_EXPIRED_EVENT))
handlePassphraseExpired();
handleClearKey();
}
@Override
public void onCreate() {
super.onCreate();
pending = PendingIntent.getService(this, 0, new Intent(PASSPHRASE_EXPIRED_EVENT, null, this, KeyCachingService.class), 0);
}
@Override
public void onDestroy() {
Log.e("kcs", "KCS Is Being Destroyed!");
super.onDestroy();
Log.w("KeyCachingService", "KCS Is Being Destroyed!");
handleClearKey();
}
private void handleActivityStarted() {
@@ -117,14 +129,18 @@ public class KeyCachingService extends Service {
private void handleClearKey() {
this.masterSecret = null;
stopForeground(true);
}
private void handlePassphraseExpired() {
handleClearKey();
Intent intent = new Intent(PASSPHRASE_EXPIRED_EVENT);
Intent intent = new Intent(CLEAR_KEY_EVENT);
intent.setPackage(getApplicationContext().getPackageName());
sendBroadcast(intent, KEY_PERMISSION);
new Thread() {
@Override
public void run() {
MessageNotifier.updateNotification(KeyCachingService.this, null);
}
}.start();
}
private void startTimeoutIfAppropriate() {
@@ -180,6 +196,7 @@ public class KeyCachingService extends Service {
private void broadcastNewSecret() {
Log.w("service", "Broadcasting new secret...");
Intent intent = new Intent(NEW_KEY_EVENT);
intent.putExtra("master_secret", masterSecret);
intent.setPackage(getApplicationContext().getPackageName());
@@ -198,4 +215,16 @@ public class KeyCachingService extends Service {
return KeyCachingService.this;
}
}
public static void registerPassphraseActivityStarted(Context activity) {
Intent intent = new Intent(activity, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_START_EVENT);
activity.startService(intent);
}
public static void registerPassphraseActivityStopped(Context activity) {
Intent intent = new Intent(activity, KeyCachingService.class);
intent.setAction(KeyCachingService.ACTIVITY_STOP_EVENT);
activity.startService(intent);
}
}

View File

@@ -1,217 +0,0 @@
/**
* Copyright (C) 2011 Whisper Systems
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.service;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.support.v4.app.NotificationCompat;
import android.text.TextUtils;
import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.ConversationListActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.database.MmsSmsDatabase;
import org.thoughtcrime.securesms.database.SmsDatabase;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.RecipientFactory;
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
import org.thoughtcrime.securesms.recipients.Recipients;
import java.util.LinkedList;
/**
* Handles posting system notifications for new messages.
*
*
* @author Moxie Marlinspike
*/
public class MessageNotifier {
public static final int NOTIFICATION_ID = 1338;
private static Bitmap buildContactPhoto(Recipients recipients) {
Recipient recipient = recipients.getPrimaryRecipient();
if (recipient == null) {
return null;
} else {
return recipient.getContactPhoto();
}
}
private static String buildTickerMessage(Context context, int count, Recipients recipients) {
Recipient recipient = recipients.getPrimaryRecipient();
if (recipient == null) {
return String.format(context.getString(R.string.MessageNotifier_d_new_messages), count);
} else {
return String.format(context.getString(R.string.MessageNotifier_d_new_messages_most_recent_from_s), count,
recipient.getName() == null ? recipient.getNumber() : recipient.getName());
}
}
private static String buildTitleMessage(Context context, int count) {
return String.format(context.getString(R.string.MessageNotifier_d_new_messages), count);
}
private static String buildSubtitleMessage(Context context, Recipients recipients) {
Recipient recipient = recipients.getPrimaryRecipient();
if (recipient != null) {
return String.format(context.getString(R.string.MessageNotifier_most_recent_from_s),
(recipient.getName() == null ? recipient.getNumber() : recipient.getName()));
}
return null;
}
private static Recipients getSmsRecipient(Context context, Cursor c) throws RecipientFormattingException {
String address = c.getString(c.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
return RecipientFactory.getRecipientsFromString(context, address);
}
private static Recipients getMmsRecipient(Context context, Cursor c) throws RecipientFormattingException {
long messageId = c.getLong(c.getColumnIndexOrThrow(MmsDatabase.ID));
String address = DatabaseFactory.getMmsDatabase(context).getMessageRecipient(messageId);
return RecipientFactory.getRecipientsFromString(context, address);
}
private static Recipients getMostRecentRecipients(Context context, Cursor c) {
if (c != null && c.moveToLast()) {
try {
String type = c.getString(c.getColumnIndexOrThrow(MmsSmsDatabase.TRANSPORT));
if (type.equals("sms"))
return getSmsRecipient(context, c);
else
return getMmsRecipient(context, c);
} catch (RecipientFormattingException e) {
return new Recipients(new LinkedList<Recipient>());
}
}
return null;
}
private static PendingIntent buildPendingIntent(Context context, Cursor c, Recipients recipients) {
Intent intent = new Intent(context, ConversationListActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_SINGLE_TOP);
Log.w("SMSNotifier", "Building pending intent...");
if (c != null && c.getCount() == 1) {
Log.w("SMSNotifier", "Adding extras...");
c.moveToLast();
long threadId = c.getLong(c.getColumnIndexOrThrow(SmsDatabase.THREAD_ID));
Log.w("SmsNotifier", "Adding thread_id to pending intent: " + threadId);
if (recipients.getPrimaryRecipient() != null) {
intent.putExtra("recipients", recipients);
intent.putExtra("thread_id", threadId);
}
intent.setData((Uri.parse("custom://"+System.currentTimeMillis())));
}
return PendingIntent.getActivity(context, 0, intent, 0);
}
private static void sendNotification(Context context, NotificationManager manager,
PendingIntent launchIntent, Bitmap contactPhoto,
String ticker, String title,
String subtitle, boolean signal)
{
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(context);
if (!sp.getBoolean(ApplicationPreferencesActivity.NOTIFICATION_PREF, true)) return;
String ringtone = sp.getString(ApplicationPreferencesActivity.RINGTONE_PREF, null);
boolean vibrate = sp.getBoolean(ApplicationPreferencesActivity.VIBRATE_PREF, true);
String ledColor = sp.getString(ApplicationPreferencesActivity.LED_COLOR_PREF, "green");
String ledBlinkPattern = sp.getString(ApplicationPreferencesActivity.LED_BLINK_PREF, "500,2000");
String ledBlinkPatternCustom = sp.getString(ApplicationPreferencesActivity.LED_BLINK_PREF_CUSTOM, "500,2000");
String[] blinkPatternArray = parseBlinkPattern(ledBlinkPattern, ledBlinkPatternCustom);
NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
builder.setSmallIcon(R.drawable.icon_notification);
builder.setLargeIcon(contactPhoto);
builder.setTicker(ticker);
builder.setContentTitle(title);
builder.setContentText(subtitle);
builder.setContentIntent(launchIntent);
builder.setSound(TextUtils.isEmpty(ringtone) || !signal ? null : Uri.parse(ringtone));
if (signal && vibrate)
builder.setDefaults(Notification.DEFAULT_VIBRATE);
builder.setLights(Color.parseColor(ledColor), Integer.parseInt(blinkPatternArray[0]), Integer.parseInt(blinkPatternArray[1]));
manager.notify(NOTIFICATION_ID, builder.build());
}
private static void flashNotification(Context context, NotificationManager manager) {
sendNotification(context, manager, buildPendingIntent(context, null, null),
null, "(1) New Messages", "(1) New Messages", null, true);
}
public static void updateNotification(Context context, boolean signal) {
NotificationManager manager = (NotificationManager)context.getSystemService(Context.NOTIFICATION_SERVICE);
manager.cancel(NOTIFICATION_ID);
Cursor c = null;
try {
c = DatabaseFactory.getMmsSmsDatabase(context).getUnread();
if ((c == null && signal) || (!c.moveToFirst() && signal)) {flashNotification(context, manager); return;}
else if (c == null || !c.moveToFirst()) return;
Recipients recipients = getMostRecentRecipients(context, c);
String ticker = buildTickerMessage(context, c.getCount(), recipients);
String title = buildTitleMessage(context, c.getCount());
String subtitle = buildSubtitleMessage(context, recipients);
PendingIntent launchIntent = buildPendingIntent(context, c, recipients);
Bitmap contactPhoto = buildContactPhoto(recipients);
sendNotification(context, manager, launchIntent, contactPhoto,
ticker, title, subtitle, signal);
} finally {
if (c != null)
c.close();
}
}
private static String[] parseBlinkPattern(String blinkPattern, String blinkPatternCustom) {
if (blinkPattern.equals("custom"))
blinkPattern = blinkPatternCustom;
return blinkPattern.split(",");
}
}

View File

@@ -71,20 +71,20 @@ public class MmsDownloader extends MmscProcessor {
getApnInformation());
RetrieveConf retrieved = (RetrieveConf)new PduParser(pdu).parse();
if (retrieved == null)
throw new IOException("Bad retrieved PDU");
for (int i=0;i<retrieved.getBody().getPartsNum();i++) {
Log.w("MmsDownloader", "Sent MMS part of content-type: " + new String(retrieved.getBody().getPart(i).getContentType()));
Log.w("MmsDownloader", "Sent MMS part of content-type: " +
new String(retrieved.getBody().getPart(i).getContentType()));
}
if (retrieved == null)
throw new IOException("Bad retrieved PDU");
if (retrieved.getSubject() != null && WirePrefix.isEncryptedMmsSubject(retrieved.getSubject().getString())) {
long messageId = mmsDatabase.insertSecureMessageReceived(retrieved, item.getContentLocation(), item.getThreadId());
if (item.getMasterSecret() != null)
DecryptingQueue.scheduleDecryption(context, item.getMasterSecret(), messageId, item.getThreadId(), retrieved);
} else {
mmsDatabase.insertMessageReceived(retrieved, item.getContentLocation(), item.getThreadId());
long messageId = mmsDatabase.insertSecureMessageReceived(retrieved, item.getContentLocation(), item.getThreadId());
if (item.getMasterSecret() != null)
DecryptingQueue.scheduleDecryption(context, item.getMasterSecret(), messageId, item.getThreadId(), retrieved);
} else {
mmsDatabase.insertMessageReceived(retrieved, item.getContentLocation(), item.getThreadId());
}
mmsDatabase.delete(item.getMessageId());
@@ -93,7 +93,7 @@ public class MmsDownloader extends MmscProcessor {
// MmsSendHelper.sendMms(context, new PduComposer(context, notifyResponse).make());
if (this.pendingMessages.isEmpty())
finishConnectivity();
finishConnectivity();
} catch (IOException e) {
DatabaseFactory.getMmsDatabase(context).markDownloadState(item.getMessageId(), MmsDatabase.Types.DOWNLOAD_SOFT_FAILURE);
@@ -109,10 +109,10 @@ public class MmsDownloader extends MmscProcessor {
protected void handleConnectivityChange() {
if (!isConnected()) {
if (!isConnectivityPossible() && !pendingMessages.isEmpty()) {
DatabaseFactory.getMmsDatabase(context).markDownloadState(pendingMessages.remove().getMessageId(), MmsDatabase.Types.DOWNLOAD_NO_CONNECTIVITY);
toastHandler.makeToast("No connectivity available for MMS download, try again later...");
Log.w("MmsDownloadService", "Unable to download MMS, please try again later.");
finishConnectivity();
DatabaseFactory.getMmsDatabase(context).markDownloadState(pendingMessages.remove().getMessageId(), MmsDatabase.Types.DOWNLOAD_NO_CONNECTIVITY);
toastHandler.makeToast("No connectivity available for MMS download, try again later...");
Log.w("MmsDownloadService", "Unable to download MMS, please try again later.");
finishConnectivity();
}
return;
@@ -125,10 +125,10 @@ public class MmsDownloader extends MmscProcessor {
public void process(MasterSecret masterSecret, Intent intent) {
if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_ACTION)) {
DownloadItem item = new DownloadItem(intent.getLongExtra("message_id", -1),
intent.getLongExtra("thread_id", -1),
intent.getStringExtra("content_location"),
intent.getByteArrayExtra("transaction_id"),
masterSecret);
intent.getLongExtra("thread_id", -1),
intent.getStringExtra("content_location"),
intent.getByteArrayExtra("transaction_id"),
masterSecret);
handleDownloadMms(item);
} else if (intent.getAction().equals(SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION)) {
@@ -174,9 +174,7 @@ public class MmsDownloader extends MmscProcessor {
}
@Override
protected String getConnectivityAction() {
protected String getConnectivityAction() {
return SendReceiveService.DOWNLOAD_MMS_CONNECTIVITY_ACTION;
}
}

View File

@@ -23,6 +23,7 @@ import android.util.Log;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import ws.com.google.android.mms.pdu.GenericPdu;
import ws.com.google.android.mms.pdu.NotificationInd;
@@ -37,12 +38,12 @@ public class MmsReceiver {
this.context = context;
}
private void scheduleDownload(NotificationInd pdu, long messageId) {
private void scheduleDownload(NotificationInd pdu, long messageId, long threadId) {
Intent intent = new Intent(SendReceiveService.DOWNLOAD_MMS_ACTION, null, context, SendReceiveService.class);
intent.putExtra("content_location", new String(pdu.getContentLocation()));
intent.putExtra("message_id", messageId);
intent.putExtra("transaction_id", pdu.getTransactionId());
intent.putExtra("thread_id", DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId));
intent.putExtra("thread_id", threadId);
context.startService(intent);
}
@@ -61,8 +62,11 @@ public class MmsReceiver {
database = DatabaseFactory.getMmsDatabase(context);
long messageId = database.insertMessageReceived((NotificationInd)pdu);
MessageNotifier.updateNotification(context, true);
scheduleDownload((NotificationInd)pdu, messageId);
long threadId = database.getThreadIdForMessage(messageId);
MessageNotifier.updateNotification(context, masterSecret, threadId);
scheduleDownload((NotificationInd)pdu, messageId, threadId);
Log.w("MmsReceiverService", "Inserted received notification...");
}
}

View File

@@ -18,17 +18,21 @@ package org.thoughtcrime.securesms.service;
import android.content.Context;
import android.content.Intent;
import android.os.Handler;
import android.telephony.TelephonyManager;
import android.util.Log;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SessionCipher;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.database.MmsDatabase;
import org.thoughtcrime.securesms.mms.MmsSendHelper;
import org.thoughtcrime.securesms.mms.TextTransport;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.util.Hex;
import ws.com.google.android.mms.ContentType;
@@ -49,9 +53,11 @@ import java.util.LinkedList;
public class MmsSender extends MmscProcessor {
private final LinkedList<SendReq[]> pendingMessages = new LinkedList<SendReq[]>();
private final Handler toastHandler;
public MmsSender(Context context) {
public MmsSender(Context context, Handler toastHandler) {
super(context);
this.toastHandler = toastHandler;
}
public void process(MasterSecret masterSecret, Intent intent) {
@@ -69,8 +75,8 @@ public class MmsSender extends MmscProcessor {
sendRequests[0] = database.getSendRequest(messageId);
}
if (sendRequests.length > 0)
handleSendMms(sendRequests);
if (sendRequests != null && sendRequests.length > 0)
handleSendMms(sendRequests, messageId != -1);
} catch (MmsException me) {
Log.w("MmsSender", me);
@@ -90,10 +96,15 @@ public class MmsSender extends MmscProcessor {
else finishConnectivity();
}
private void handleSendMms(SendReq[] sendRequests) {
private void handleSendMms(SendReq[] sendRequests, boolean targeted) {
if (!isConnectivityPossible()) {
for (int i=0;i<sendRequests.length;i++)
DatabaseFactory.getMmsDatabase(context).markAsSentFailed(sendRequests[i].getDatabaseMessageId());
if (targeted) {
toastHandler
.obtainMessage(0, context.getString(R.string.MmsSender_currently_unable_to_send_your_mms_message))
.sendToTarget();
}
// for (int i=0;i<sendRequests.length;i++)
// DatabaseFactory.getMmsDatabase(context).markAsSentFailed(sendRequests[i].getDatabaseMessageId());
} else {
pendingMessages.add(sendRequests);
issueConnectivityRequest();
@@ -108,7 +119,7 @@ public class MmsSender extends MmscProcessor {
private byte[] getEncryptedPdu(MasterSecret masterSecret, String recipient, byte[] pduBytes) {
synchronized (SessionCipher.CIPHER_LOCK) {
SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, recipient, null), new TextTransport());
SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, recipient, null, null), new TextTransport());
return cipher.encryptMessage(pduBytes);
}
}
@@ -146,17 +157,23 @@ public class MmsSender extends MmscProcessor {
Log.w("MmsSender", "Sent MMS part of content-type: " + new String(pdu.getBody().getPart(i).getContentType()));
}
long threadId = DatabaseFactory.getMmsDatabase(context).getThreadIdForMessage(messageId);
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(context, threadId);
if (conf == null) {
db.markAsSentFailed(messageId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
Log.w("MmsSender", "No M-Send.conf received in response to send.");
return;
} else if (conf.getResponseStatus() != PduHeaders.RESPONSE_STATUS_OK) {
Log.w("MmsSender", "Got bad response: " + conf.getResponseStatus());
db.updateResponseStatus(messageId, conf.getResponseStatus());
db.markAsSentFailed(messageId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
return;
} else if (isInconsistentResponse(pdu, conf)) {
db.markAsSentFailed(messageId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
Log.w("MmsSender", "Got a response for the wrong transaction?");
return;
} else {

View File

@@ -47,6 +47,7 @@ public class SendReceiveService extends Service {
public static final String SEND_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_SMS_ACTION";
public static final String SENT_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SENT_SMS_ACTION";
public static final String DELIVERED_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DELIVERED_SMS_ACTION";
public static final String RECEIVE_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_SMS_ACTION";
public static final String SEND_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_ACTION";
public static final String SEND_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION";
@@ -69,13 +70,16 @@ public class SendReceiveService extends Service {
private MmsDownloader mmsDownloader;
private MasterSecret masterSecret;
private NewKeyReceiver receiver;
private boolean hasSecret;
private NewKeyReceiver newKeyReceiver;
private ClearKeyReceiver clearKeyReceiver;
private List<Runnable> workQueue;
private List<Runnable> pendingSecretList;
private Thread workerThread;
@Override
public void onCreate() {
public void onCreate() {
initializeHandlers();
initializeProcessors();
initializeAddressCanonicalization();
@@ -93,6 +97,8 @@ public class SendReceiveService extends Service {
scheduleIntent(RECEIVE_SMS, intent);
else if (intent.getAction().equals(SENT_SMS_ACTION))
scheduleIntent(RECEIVE_SMS, intent);
else if (intent.getAction().equals(DELIVERED_SMS_ACTION))
scheduleIntent(RECEIVE_SMS, intent);
else if (intent.getAction().equals(SEND_MMS_ACTION) || intent.getAction().equals(SEND_MMS_CONNECTIVITY_ACTION))
scheduleSecretRequiredIntent(SEND_MMS, intent);
else if (intent.getAction().equals(RECEIVE_MMS_ACTION))
@@ -108,15 +114,27 @@ public class SendReceiveService extends Service {
return null;
}
@Override
public void onDestroy() {
Log.w("SendReceiveService", "onDestroy()...");
super.onDestroy();
if (newKeyReceiver != null)
unregisterReceiver(newKeyReceiver);
if (clearKeyReceiver != null)
unregisterReceiver(clearKeyReceiver);
}
private void initializeHandlers() {
toastHandler = new ToastHandler();
}
private void initializeProcessors() {
smsReceiver = new SmsReceiver(this);
smsReceiver = new SmsReceiver(this, toastHandler);
smsSender = new SmsSender(this);
mmsReceiver = new MmsReceiver(this);
mmsSender = new MmsSender(this);
mmsSender = new MmsSender(this, toastHandler);
mmsDownloader = new MmsDownloader(this, toastHandler);
}
@@ -129,20 +147,27 @@ public class SendReceiveService extends Service {
}
private void initializeMasterSecret() {
receiver = new NewKeyReceiver();
IntentFilter filter = new IntentFilter(KeyCachingService.NEW_KEY_EVENT);
registerReceiver(receiver, filter, KeyCachingService.KEY_PERMISSION, null);
hasSecret = false;
newKeyReceiver = new NewKeyReceiver();
clearKeyReceiver = new ClearKeyReceiver();
IntentFilter newKeyFilter = new IntentFilter(KeyCachingService.NEW_KEY_EVENT);
registerReceiver(newKeyReceiver, newKeyFilter, KeyCachingService.KEY_PERMISSION, null);
IntentFilter clearKeyFilter = new IntentFilter(KeyCachingService.CLEAR_KEY_EVENT);
registerReceiver(clearKeyReceiver, clearKeyFilter, KeyCachingService.KEY_PERMISSION, null);
Intent bindIntent = new Intent(this, KeyCachingService.class);
bindService(bindIntent, serviceConnection, Context.BIND_AUTO_CREATE);
}
private void initializeWithMasterSecret(MasterSecret masterSecret) {
Log.w("SendReceiveService", "SendReceive service got master secret: " + masterSecret);
Log.w("SendReceiveService", "SendReceive service got master secret.");
if (masterSecret != null) {
synchronized (workQueue) {
this.masterSecret = masterSecret;
this.hasSecret = true;
Iterator<Runnable> iterator = pendingSecretList.iterator();
while (iterator.hasNext())
@@ -170,7 +195,7 @@ public class SendReceiveService extends Service {
Runnable work = new SendReceiveWorkItem(intent, what);
synchronized (workQueue) {
if (masterSecret != null) {
if (hasSecret) {
workQueue.add(work);
workQueue.notifyAll();
} else {
@@ -180,7 +205,6 @@ public class SendReceiveService extends Service {
}
private class SendReceiveWorkItem implements Runnable {
private final Intent intent;
private final int what;
@@ -189,10 +213,11 @@ public class SendReceiveService extends Service {
this.what = what;
}
@Override
public void run() {
switch (what) {
case RECEIVE_SMS: smsReceiver.process(masterSecret, intent); return;
case SEND_SMS: smsSender.process(masterSecret, intent); return;
case RECEIVE_SMS: smsReceiver.process(masterSecret, intent); return;
case SEND_SMS: smsSender.process(masterSecret, intent); return;
case RECEIVE_MMS: mmsReceiver.process(masterSecret, intent); return;
case SEND_MMS: mmsSender.process(masterSecret, intent); return;
case DOWNLOAD_MMS: mmsDownloader.process(masterSecret, intent); return;
@@ -207,12 +232,13 @@ public class SendReceiveService extends Service {
this.sendMessage(message);
}
@Override
public void handleMessage(Message message) {
public void handleMessage(Message message) {
Toast.makeText(SendReceiveService.this, (String)message.obj, Toast.LENGTH_LONG).show();
}
}
private ServiceConnection serviceConnection = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName className, IBinder service) {
KeyCachingService keyCachingService = ((KeyCachingService.KeyCachingBinder)service).getService();
MasterSecret masterSecret = keyCachingService.getMasterSecret();
@@ -222,6 +248,7 @@ public class SendReceiveService extends Service {
SendReceiveService.this.unbindService(this);
}
@Override
public void onServiceDisconnected(ComponentName name) {}
};
@@ -231,6 +258,48 @@ public class SendReceiveService extends Service {
Log.w("SendReceiveService", "Got a MasterSecret broadcast...");
initializeWithMasterSecret((MasterSecret)intent.getParcelableExtra("master_secret"));
}
};
}
/**
* This class receives broadcast notifications to clear the MasterSecret.
*
* We don't want to clear it immediately, since there are potentially jobs
* in the work queue which require the master secret. Instead, we reset a
* flag so that new incoming jobs will be evaluated as if no mastersecret is
* present.
*
* Then, we add a job to the end of the queue which actually clears the masterSecret
* value. That way all jobs before this moment will be processed correctly, and all
* jobs after this moment will be evaluated as if no mastersecret is present (and potentially
* held).
*
* When we go to actually clear the mastersecret, we ensure that the flag is still false.
* This allows a new mastersecret broadcast to come in correctly without us clobbering it.
*
*/
private class ClearKeyReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
Log.w("SendReceiveService", "Got a clear mastersecret broadcast...");
synchronized (workQueue) {
SendReceiveService.this.hasSecret = false;
workQueue.add(new Runnable() {
@Override
public void run() {
Log.w("SendReceiveService", "Running clear key work item...");
synchronized (workQueue) {
if (!SendReceiveService.this.hasSecret) {
Log.w("SendReceiveService", "Actually clearing key...");
SendReceiveService.this.masterSecret = null;
}
}
}
});
workQueue.notifyAll();
}
}
};
}

View File

@@ -1,6 +1,6 @@
/**
/**
* Copyright (C) 2011 Whisper Systems
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
@@ -10,14 +10,12 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package org.thoughtcrime.securesms.service;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
@@ -26,66 +24,74 @@ import android.preference.PreferenceManager;
import android.telephony.SmsMessage;
import android.util.Log;
import org.thoughtcrime.securesms.protocol.WirePrefix;
public class SmsListener extends BroadcastReceiver {
private static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
private boolean isExemption(SmsMessage message, String messageBody) {
// ignore OTP messages from Sparebank1 (Norwegian bank)
// ignore CLASS0 ("flash") messages
if (message.getMessageClass() == SmsMessage.MessageClass.CLASS_0)
return true;
// ignore OTP messages from Sparebank1 (Norwegian bank)
if (messageBody.startsWith("Sparebank1://otp?")) {
return (true);
}
return true;
}
// Sprint Visual Voicemail
return
message.getOriginatingAddress().length() < 7 &&
(messageBody.startsWith("//ANDROID:") || messageBody.startsWith("//Android:") ||
return
message.getOriginatingAddress().length() < 7 &&
(messageBody.startsWith("//ANDROID:") || messageBody.startsWith("//Android:") ||
messageBody.startsWith("//android:") || messageBody.startsWith("//BREW:"));
}
private SmsMessage getSmsMessageFromIntent(Intent intent) {
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[])bundle.get("pdus");
if (pdus == null || pdus.length == 0)
return null;
return SmsMessage.createFromPdu((byte[])pdus[0]);
}
private String getSmsMessageBodyFromIntent(Intent intent) {
Bundle bundle = intent.getExtras();
Object[] pdus = (Object[])bundle.get("pdus");
StringBuilder bodyBuilder = new StringBuilder();
if (pdus == null)
return null;
for (int i=0;i<pdus.length;i++)
bodyBuilder.append(SmsMessage.createFromPdu((byte[])pdus[i]).getDisplayMessageBody());
return bodyBuilder.toString();
}
private boolean isRelevent(Context context, Intent intent) {
SmsMessage message = getSmsMessageFromIntent(intent);
String messageBody = getSmsMessageBodyFromIntent(intent);
if (message == null && messageBody == null)
return false;
if (isExemption(message, messageBody))
return false;
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("pref_all_sms", true))
return true;
return true;
return WirePrefix.isEncryptedMessage(messageBody) || WirePrefix.isKeyExchange(messageBody);
}
@Override
public void onReceive(Context context, Intent intent) {
public void onReceive(Context context, Intent intent) {
Log.w("SMSListener", "Got SMS broadcast...");
if (intent.getAction().equals(SMS_RECEIVED_ACTION) && isRelevent(context, intent)) {
intent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
intent.putExtra("ResultCode", this.getResultCode());
@@ -97,6 +103,10 @@ public class SmsListener extends BroadcastReceiver {
intent.putExtra("ResultCode", this.getResultCode());
intent.setClass(context, SendReceiveService.class);
context.startService(intent);
} else if (intent.getAction().equals(SendReceiveService.DELIVERED_SMS_ACTION)) {
intent.putExtra("ResultCode", this.getResultCode());
intent.setClass(context, SendReceiveService.class);
context.startService(intent);
}
}
}

View File

@@ -21,10 +21,12 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.telephony.SmsManager;
import android.telephony.SmsMessage;
import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.R;
import org.thoughtcrime.securesms.crypto.DecryptingQueue;
import org.thoughtcrime.securesms.crypto.InvalidKeyException;
import org.thoughtcrime.securesms.crypto.InvalidVersionException;
@@ -33,9 +35,12 @@ import org.thoughtcrime.securesms.crypto.KeyExchangeProcessor;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.MasterSecretUtil;
import org.thoughtcrime.securesms.database.DatabaseFactory;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
import org.thoughtcrime.securesms.protocol.Prefix;
import org.thoughtcrime.securesms.protocol.WirePrefix;
import org.thoughtcrime.securesms.recipients.Recipient;
import org.thoughtcrime.securesms.recipients.Recipients;
import org.thoughtcrime.securesms.service.SendReceiveService.ToastHandler;
import org.thoughtcrime.securesms.sms.MultipartMessageHandler;
public class SmsReceiver {
@@ -43,9 +48,11 @@ public class SmsReceiver {
private MultipartMessageHandler multipartMessageHandler = new MultipartMessageHandler();
private final Context context;
private final ToastHandler toastHandler;
public SmsReceiver(Context context) {
this.context = context;
public SmsReceiver(Context context, ToastHandler toastHandler) {
this.context = context;
this.toastHandler = toastHandler;
}
private String assembleSecureMessageFragments(String sender, String messageBody) {
@@ -86,11 +93,13 @@ public class SmsReceiver {
}
}
private void storeSecureMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
private long storeSecureMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
long messageId = DatabaseFactory.getSmsDatabase(context).insertSecureMessageReceived(message, messageBody);
Log.w("SmsReceiver", "Inserted secure message received: " + messageId);
if (masterSecret != null)
DecryptingQueue.scheduleDecryption(context, masterSecret, messageId, message.getDisplayOriginatingAddress(), messageBody);
return messageId;
}
private long storeStandardMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
@@ -99,10 +108,10 @@ public class SmsReceiver {
else return DatabaseFactory.getSmsDatabase(context).insertMessageReceived(message, messageBody);
}
private void storeKeyExchangeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
private long storeKeyExchangeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean(ApplicationPreferencesActivity.AUTO_KEY_EXCHANGE_PREF, true)) {
try {
Recipient recipient = new Recipient(null, message.getDisplayOriginatingAddress(), null);
Recipient recipient = new Recipient(null, message.getDisplayOriginatingAddress(), null, null);
KeyExchangeMessage keyExchangeMessage = new KeyExchangeMessage(messageBody);
KeyExchangeProcessor processor = new KeyExchangeProcessor(context, masterSecret, recipient);
@@ -118,7 +127,7 @@ public class SmsReceiver {
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
processor.processKeyExchangeMessage(keyExchangeMessage, threadId);
return;
return messageId;
}
} catch (InvalidVersionException e) {
Log.w("SmsReceiver", e);
@@ -127,19 +136,17 @@ public class SmsReceiver {
}
}
storeStandardMessage(masterSecret, message, messageBody);
return storeStandardMessage(masterSecret, message, messageBody);
}
private boolean storeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
private long storeMessage(MasterSecret masterSecret, SmsMessage message, String messageBody) {
if (messageBody.startsWith(Prefix.ASYMMETRIC_ENCRYPT)) {
storeSecureMessage(masterSecret, message, messageBody);
return storeSecureMessage(masterSecret, message, messageBody);
} else if (messageBody.startsWith(Prefix.KEY_EXCHANGE)) {
storeKeyExchangeMessage(masterSecret, message, messageBody);
return storeKeyExchangeMessage(masterSecret, message, messageBody);
} else {
storeStandardMessage(masterSecret, message, messageBody);
return storeStandardMessage(masterSecret, message, messageBody);
}
return true;
}
private SmsMessage[] parseMessages(Bundle bundle) {
@@ -158,22 +165,48 @@ public class SmsReceiver {
String message = assembleMessageFragments(messages);
if (message != null) {
storeMessage(masterSecret, messages[0], message);
MessageNotifier.updateNotification(context, true);
long messageId = storeMessage(masterSecret, messages[0], message);
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
MessageNotifier.updateNotification(context, masterSecret, threadId);
}
}
private void handleSentMessage(Intent intent) {
long messageId = intent.getLongExtra("message_id", -1);
long type = intent.getLongExtra("type", -1);
int result = intent.getIntExtra("ResultCode", -31337);
Log.w("SMSReceiverService", "Intent resultcode: " + intent.getIntExtra("ResultCode", 42));
Log.w("SMSReceiverService", "Intent resultcode: " + result);
Log.w("SMSReceiverService", "Running sent callback: " + messageId + "," + type);
if (intent.getIntExtra("ResultCode", -31337) == Activity.RESULT_OK)
if (result == Activity.RESULT_OK) {
DatabaseFactory.getSmsDatabase(context).markAsSent(messageId, type);
else
} else if (result == SmsManager.RESULT_ERROR_NO_SERVICE || result == SmsManager.RESULT_ERROR_RADIO_OFF) {
toastHandler
.obtainMessage(0, context.getString(R.string.SmsReceiver_currently_unable_to_send_your_sms_message))
.sendToTarget();
} else {
long threadId = DatabaseFactory.getSmsDatabase(context).getThreadIdForMessage(messageId);
Recipients recipients = DatabaseFactory.getThreadDatabase(context).getRecipientsForThreadId(context, threadId);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
MessageNotifier.notifyMessageDeliveryFailed(context, recipients, threadId);
}
}
private void handleDeliveredMessage(Intent intent) {
long messageId = intent.getLongExtra("message_id", -1);
long type = intent.getLongExtra("type", -1);
byte[] pdu = intent.getByteArrayExtra("pdu");
String format = intent.getStringExtra("format");
SmsMessage message = SmsMessage.createFromPdu(pdu);
if (message == null) {
return;
}
DatabaseFactory.getSmsDatabase(context).markStatus(messageId, message.getStatus());
}
public void process(MasterSecret masterSecret, Intent intent) {
@@ -181,5 +214,7 @@ public class SmsReceiver {
handleReceiveMessage(masterSecret, intent);
else if (intent.getAction().equals(SendReceiveService.SENT_SMS_ACTION))
handleSentMessage(intent);
else if (intent.getAction().equals(SendReceiveService.DELIVERED_SMS_ACTION))
handleDeliveredMessage(intent);
}
}

View File

@@ -21,9 +21,11 @@ import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.telephony.SmsManager;
import android.util.Log;
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
import org.thoughtcrime.securesms.crypto.MasterCipher;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.crypto.SessionCipher;
@@ -111,15 +113,36 @@ public class SmsSender {
return sentIntents;
}
private ArrayList<PendingIntent> constructDeliveredIntents(long messageId, long type, ArrayList<String> messages) {
if (!PreferenceManager.getDefaultSharedPreferences(context)
.getBoolean(ApplicationPreferencesActivity.SMS_DELIVERY_REPORT_PREF, false))
{
return null;
}
ArrayList<PendingIntent> deliveredIntents = new ArrayList<PendingIntent>(messages.size());
for (int i=0;i<messages.size();i++) {
Intent pending = new Intent(SendReceiveService.DELIVERED_SMS_ACTION, Uri.parse("custom://" + messageId + System.currentTimeMillis()), context, SmsListener.class);
pending.putExtra("type", type);
pending.putExtra("message_id", messageId);
deliveredIntents.add(PendingIntent.getBroadcast(context, 0, pending, 0));
}
return deliveredIntents;
}
private void deliverGSMTransportTextMessage(String recipient, String text, long messageId, long type) {
ArrayList<String> messages = SmsManager.getDefault().divideMessage(text);
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
ArrayList<String> messages = SmsManager.getDefault().divideMessage(text);
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
ArrayList<PendingIntent> deliveredIntents = constructDeliveredIntents(messageId, type, messages);
// XXX moxie@thoughtcrime.org 1/7/11 -- There's apparently a bug where for some unknown recipients
// and messages, this will throw an NPE. I have no idea why, so I'm just catching it and marking
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
// the app.
try {
SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, null);
SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents);
} catch (NullPointerException npe) {
Log.w("SmsSender", npe);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
@@ -142,15 +165,18 @@ public class SmsSender {
return;
}
ArrayList<String> messages = multipartMessageHandler.divideMessage(recipient, text, prefix);
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
ArrayList<String> messages = multipartMessageHandler.divideMessage(recipient, text, prefix);
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
ArrayList<PendingIntent> deliveredIntents = constructDeliveredIntents(messageId, type, messages);
for (int i=0;i<messages.size();i++) {
// XXX moxie@thoughtcrime.org 1/7/11 -- There's apparently a bug where for some unknown recipients
// and messages, this will throw an NPE. I have no idea why, so I'm just catching it and marking
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
// the app.
try {
SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i), null);
SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i),
deliveredIntents == null ? null : deliveredIntents.get(i));
} catch (NullPointerException npe) {
Log.w("SmsSender", npe);
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
@@ -175,7 +201,7 @@ public class SmsSender {
private String getAsymmetricEncrypt(MasterSecret masterSecret, String body, String address) {
synchronized (SessionCipher.CIPHER_LOCK) {
SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, address, null), new SmsTransportDetails());
SessionCipher cipher = new SessionCipher(context, masterSecret, new Recipient(null, address, null, null), new SmsTransportDetails());
return new String(cipher.encryptMessage(body.getBytes()));
}
}

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